public bool ServerOnHackingStage(IStaticWorldObject worldObject, ICharacter character) { var publicState = GetPublicState(worldObject); publicState.HackingProgressPercent += 100 / this.HackingStagesNumber; if (publicState.HackingProgressPercent < 100) { return(true); } Server.World.DestroyObject(worldObject); var lootDroplist = this.LootDroplist; var dropItemContext = new DropItemContext(character, worldObject); var dropItemResult = lootDroplist.TryDropToCharacterOrGround( character, character.TilePosition, dropItemContext, out _, probabilityMultiplier: this.ServerGetDropListProbabilityMultiplier(worldObject)); if (dropItemResult.TotalCreatedCount > 0) { NotificationSystem.ServerSendItemsNotification(character, dropItemResult); var skillExperienceToAdd = SkillSearching.ExperienceAddWhenSearching * SearchSkillExperienceMultiplier; character.ServerAddSkillExperience <SkillSearching>(skillExperienceToAdd); } ServerObjectUseObserver.NotifyObjectUsed(character, worldObject); this.ServerOnHacked(character, worldObject); return(true); }
private bool ServerRemote_UseItem(IItem item) { var character = ServerRemoteContext.Character; this.ServerValidateItemForRemoteCall(item, character); var teleportObjects = Server.World .GetStaticWorldObjectsOfProto <TObjectTeleport>() .ToList(); var knownTeleportPositions = TeleportsSystem.ServerGetDiscoveredTeleports(character); teleportObjects.RemoveAll(t => knownTeleportPositions.Contains(t.TilePosition)); if (teleportObjects.Count == 0) { // no teleports to reveal return(false); } // reveal a single random teleport TeleportsSystem.ServerAddTeleportToDiscoveredList(character, teleportObjects.TakeByRandom()); Server.Items.SetCount(item, item.Count - 1); NotificationSystem.ServerSendItemsNotification(character, protoItem: this, deltaCount: -1); Logger.Important("Discovered a teleport with " + this.ShortId, character); return(true); }
public bool ServerGather(IStaticWorldObject worldObject, ICharacter character) { var lootDroplist = GetPublicState(worldObject).ProtoCharacterMob .LootDroplist; var dropItemContext = new DropItemContext(character, worldObject); CreateItemResult dropItemResult; var attemptRemains = 200; do { dropItemResult = lootDroplist.TryDropToCharacterOrGround(character, character.TilePosition, dropItemContext, out _); } // ensure that at least something is spawned... // perhaps that's not a good idea, but we have an attempts limit while (dropItemResult.TotalCreatedCount == 0 && --attemptRemains > 0); // probably the attempts limit exceeded and nothing is spawned // we don't consider this as an issue as the probability of this is too rare Logger.Info(this + " was gathered", character); NotificationSystem.ServerSendItemsNotification(character, dropItemResult); character.ServerAddSkillExperience <SkillHunting>(SkillHunting.ExperienceForGather); Server.World.DestroyObject(worldObject); return(true); }
protected override bool ServerTryGatherByCharacter(ICharacter who, IStaticWorldObject vegetationObject) { var publicState = GetPublicState(vegetationObject); if (!publicState.IsSpoiled) { if (!base.ServerTryGatherByCharacter(who, vegetationObject)) { return(false); } } else // spoiled plant { var result = this.SpoiledGatherDroplist.TryDropToCharacterOrGround( who, who.TilePosition, new DropItemContext(who, vegetationObject), out var groundItemsContainer); if (result.TotalCreatedCount == 0) { result.Rollback(); return(false); } Logger.Info(vegetationObject + " was gathered when spoiled", who); NotificationSystem.ServerSendItemsNotification( who, result, exceptItemsContainer: groundItemsContainer); } // reset grown harvest state this.ServerClearHarvestState(vegetationObject, who); return(true); }
public static void ServerSpawnEmptyBottle(ICharacter character, ushort count = 1) { var createItemResult = Server.Items.CreateItem <ItemBottleEmpty>(character, count); // notify the owner about the spawned empty bottle NotificationSystem.ServerSendItemsNotification(character, createItemResult); }
private void ServerRemote_Cut(IItem item) { var character = ServerRemoteContext.Character; this.ServerValidateItemForRemoteCall(item, character); var createItemResult = this.DropItemsList.TryDropToCharacter( character, new DropItemContext(character), probabilityMultiplier: RateResourcesGatherBasic.SharedValue); if (!createItemResult.IsEverythingCreated) { createItemResult.Rollback(); return; } Logger.Important(character + " cut " + item); this.ServerNotifyItemUsed(character, item); // decrease item count Server.Items.SetCount(item, (ushort)(item.Count - 1)); NotificationSystem.ServerSendItemsNotification(character, createItemResult); }
public static void ServerSpawnEmptyBottle(ICharacter character, ushort count = 1) { var createItemResult = Server.Items.CreateItem <ItemBottleEmpty>(character, count); if (createItemResult.IsEverythingCreated) { // notify the owner about the spawned empty bottle NotificationSystem.ServerSendItemsNotification(character, createItemResult); return; } createItemResult.Rollback(); var groundContainer = ObjectGroundItemsContainer.ServerTryGetOrCreateGroundContainerAtTileOrNeighbors(character, character.Tile); if (groundContainer is null) { return; } createItemResult = Server.Items.CreateItem <ItemBottleEmpty>(groundContainer, count); if (createItemResult.IsEverythingCreated) { // notify the owner about the spawned empty bottle NotificationSystem.ServerSendNotificationNoSpaceInInventoryItemsDroppedToGround( character, createItemResult.ItemAmounts.FirstOrDefault().Key?.ProtoItem); return; } // BUG: cannot spawn an empty bottle either to player or to the ground. It's a rare case, but still possible. // It's better to return a false result and cancel the action such as drinking of the water. NotificationSystem.ServerSendNotificationNoSpaceInInventory(character); }
protected virtual bool ServerTryGatherByCharacter(ICharacter who, IStaticWorldObject vegetationObject) { var result = this.GatherDroplist.TryDropToCharacterOrGround( who, who.TilePosition, new DropItemContext(who, vegetationObject), out var groundItemsContainer, probabilityMultiplier: RateResourcesGatherBasic.SharedValue); if (result.TotalCreatedCount == 0) { return(false); } // even if at least one item is gathered it should pass // otherwise we will have an issue with berries and other stuff which cannot be rollback easily Logger.Info(vegetationObject + " was gathered", who); result.RemoveEntriesNotOwnedByCharacter(who); NotificationSystem.ServerSendItemsNotification( who, result, exceptItemsContainer: groundItemsContainer); return(true); }
private void ServerOnMineralStageMined( WeaponFinalCache weaponCache, IStaticWorldObject mineralObject, int damageStage) { var byCharacter = weaponCache.Character; var byWeaponProto = weaponCache.ProtoWeapon; Logger.Info( $"{mineralObject} current damage stage changed to {damageStage}. Dropping items for that stage", byCharacter); try { var dropItemsList = this.DropItemsConfig.GetForStage(damageStage); var dropItemContext = new DropItemContext(byCharacter, mineralObject, byWeaponProto, weaponCache.ProtoExplosive); var objectDrone = weaponCache.Drone; if (objectDrone != null) { // drop resources into the internal storage of the drone var storageItemsContainer = ((IProtoDrone)objectDrone.ProtoGameObject) .ServerGetStorageItemsContainer(objectDrone); dropItemsList.TryDropToContainer(storageItemsContainer, dropItemContext); } else if (byWeaponProto is IProtoItemWeaponMelee) { var result = dropItemsList.TryDropToCharacterOrGround(byCharacter, mineralObject.TilePosition, dropItemContext, groundContainer: out _); if (result.TotalCreatedCount > 0) { NotificationSystem.ServerSendItemsNotification(byCharacter, result); } } else { // not a melee weapon or cannot drop to the character inventory - drop on the ground only dropItemsList.TryDropToGround(mineralObject.TilePosition, dropItemContext, out _); } } finally { if (byWeaponProto is IProtoItemToolMining || weaponCache.ProtoExplosive is ObjectBombMining) { // add experience proportional to the mineral structure points (effectively - for the time spent on mining) var exp = SkillProspecting.ExperienceAddPerStructurePoint; exp *= this.StructurePointsMax / this.DamageStagesCount; byCharacter?.ServerAddSkillExperience <SkillProspecting>(exp); } } }
protected override void ServerOnStaticObjectDestroyedByCharacter( ICharacter byCharacter, IProtoItemWeapon byWeaponProto, IStaticWorldObject targetObject) { base.ServerOnStaticObjectDestroyedByCharacter(byCharacter, byWeaponProto, targetObject); // drop chance and gained experience depends on the vegetation growth stage var growthProgressFraction = this.GrowthStagesCount > 0 ? GetPublicState(targetObject).GrowthStage / (double)this.GrowthStagesCount : 1; growthProgressFraction = MathHelper.Clamp(growthProgressFraction, 0.1, 1); try { var dropItemContext = new DropItemContext(byCharacter, targetObject); if (byWeaponProto is IProtoItemWeaponMelee) { // a melee weapon - try drop items to character var result = this.DroplistOnDestroy.TryDropToCharacter( byCharacter, dropItemContext, probabilityMultiplier: growthProgressFraction); if (result.IsEverythingCreated) { NotificationSystem.ServerSendItemsNotification(byCharacter, result); return; } result.Rollback(); } // not a melee weapon or cannot drop to character - drop on the ground only this.DroplistOnDestroy.TryDropToGround( targetObject.TilePosition, dropItemContext, probabilityMultiplier: growthProgressFraction, groundContainer: out _); } finally { if (byWeaponProto is IProtoItemToolWoodcutting) { // add experience proportional to the tree structure points (effectively - for the time spent on woodcutting) var exp = SkillWoodcutting.ExperienceAddPerStructurePoint; exp *= this.StructurePointsMax * growthProgressFraction; byCharacter?.ServerAddSkillExperience <SkillWoodcutting>(exp); } } }
protected virtual bool ServerTryGatherByCharacter(ICharacter who, IStaticWorldObject vegetationObject) { var result = this.GatherDroplist.TryDropToCharacter(who, new DropItemContext(who, vegetationObject)); if (result.IsEverythingCreated) { Logger.Info(vegetationObject + " was gathered", who); NotificationSystem.ServerSendItemsNotification(who, result); return(true); } result.Rollback(); return(false); }
public bool ServerGather(IStaticWorldObject worldObject, ICharacter character) { //MOD PublicState publicState = GetPublicState(worldObject); var lootDroplist = publicState.ProtoCharacterMob.LootDroplist; var dropItemContext = new DropItemContext(character, worldObject); CreateItemResult dropItemResultFinal = new CreateItemResult(); var level = publicState.Level; for (int i = 0; i < level; i++) { CreateItemResult dropItemResult; var attemptRemains = 200; do { dropItemResult = lootDroplist.TryDropToCharacterOrGround(character, character.TilePosition, dropItemContext, out _); dropItemResultFinal.MergeWith(dropItemResult); } // ensure that at least something is spawned... // perhaps that's not a good idea, but we have an attempts limit while (dropItemResult.TotalCreatedCount == 0 && --attemptRemains > 0); } if (!dropItemResultFinal.IsEverythingCreated) { Logger.Warning("Not all loot items were provided by " + worldObject + " - there is not enough space in inventory and around the character"); } // probably the attempts limit exceeded and nothing is spawned // we don't consider this as an issue as the probability of this is too rare Logger.Info(worldObject + " was gathered", character); Server.World.DestroyObject(worldObject); NotificationSystem.ServerSendItemsNotification(character, dropItemResultFinal); character.ServerAddSkillExperience <SkillHunting>(SkillHunting.ExperienceForGather * level); return(true); }
protected override void ServerOnUse(ICharacter character, PlayerCharacterCurrentStats currentStats) { var createItemResult = this.droplist.TryDropToCharacterOrGround(character, character.TilePosition, new DropItemContext(character), out _); if (!createItemResult.IsEverythingCreated) { createItemResult.Rollback(); throw new Exception("Not enough space"); } NotificationSystem.ServerSendItemsNotification(character, createItemResult); }
private void ServerOnDamageStageIncreased( [CanBeNull] ICharacter byCharacter, IProtoItemWeapon byWeaponProto, IStaticWorldObject mineralObject, int damageStage) { Logger.Info( $"{mineralObject} current damage stage changed to {damageStage}. Dropping items for that stage", byCharacter); try { var dropItemsList = this.DropItemsConfig.GetForStage(damageStage); var dropItemContext = new DropItemContext(byCharacter, mineralObject, byWeaponProto); if (byWeaponProto is IProtoItemWeaponMelee) { var result = dropItemsList.TryDropToCharacter(byCharacter, dropItemContext); if (result.IsEverythingCreated) { NotificationSystem.ServerSendItemsNotification( byCharacter, result); return; } result.Rollback(); } // not a melee weapon or cannot drop to the character inventory - drop on the ground only dropItemsList.TryDropToGround(mineralObject.TilePosition, dropItemContext, out _); } finally { if (byWeaponProto is IProtoItemToolMining) { // add experience proportional to the mineral structure points (effectively - for the time spent on mining) var exp = SkillMining.ExperienceAddPerStructurePoint; exp *= this.StructurePointsMax / DamageStagesCount; byCharacter?.ServerAddSkillExperience <SkillMining>(exp); } } }
public static bool ServerTryDeductBait(ICharacter character, IProtoItemFishingBait protoItemBait) { var itemBait = SharedFindBaitItem(character, protoItemBait); if (itemBait is null) { return(false); } // found a bait item of the required type, deduct the amount Server.Items.SetCount(itemBait, itemBait.Count - 1, byCharacter: character); NotificationSystem.ServerSendItemsNotification(character, protoItemBait, deltaCount: -1); return(true); }
private static bool ServerTryCreateOutputItems(CraftingQueue craftingQueue, CraftingQueueItem queueItem) { if (craftingQueue.IsContainerOutputFull && craftingQueue.ContainerOutputLastStateHash == craftingQueue.ContainerOutput.StateHash) { return(false); } var recipe = queueItem.Recipe; var result = recipe.OutputItems.TrySpawnToContainer(craftingQueue.ContainerOutput); if (!result.IsEverythingCreated) { // cannot create items at specified container result.Rollback(); craftingQueue.IsContainerOutputFull = true; craftingQueue.ContainerOutputLastStateHash = craftingQueue.ContainerOutput.StateHash; return(false); } // something is created, assume crafting success craftingQueue.IsContainerOutputFull = false; craftingQueue.ContainerOutputLastStateHash = craftingQueue.ContainerOutput.StateHash; if (recipe is Recipe.RecipeForManufacturing recipeManufacturing) { recipeManufacturing.ServerOnManufacturingCompleted( ((ManufacturingCraftingQueue)craftingQueue).WorldObject, craftingQueue); } if (craftingQueue is CharacterCraftingQueue characterCraftingQueue) { NotificationSystem.ServerSendItemsNotification( characterCraftingQueue.Character, result); } return(true); }
/// <summary> /// Please note - no validations are done! /// If the item is missing it will be not destroyed and passed. /// </summary> public static void ServerDestroyItems( ICharacter character, IReadOnlyList <ProtoItemWithCount> requiredItems) { var serverItemsService = Api.Server.Items; var itemsChangedCount = new Dictionary <IProtoItem, int>(); foreach (var requiredItem in requiredItems) { serverItemsService.DestroyItemsOfType( character, requiredItem.ProtoItem, requiredItem.Count, out var destroyedCount); if (destroyedCount > 0) { itemsChangedCount[requiredItem.ProtoItem] = -(int)destroyedCount; } } NotificationSystem.ServerSendItemsNotification(character, itemsChangedCount); }
public void ServerDestroyRequiredItems(ICharacter character) { if (CreativeModeSystem.SharedIsInCreativeMode(character)) { Api.Logger.Important(character + " is in the admin mode - free construction is allowed."); return; } // assume all the validation has been done before this action var serverItemsService = Api.Server.Items; var itemsChangedCount = new Dictionary <IProtoItem, int>(); foreach (var requiredItem in this.RequiredItems) { serverItemsService.DestroyItemsOfType( character, requiredItem.ProtoItem, requiredItem.Count, out _); itemsChangedCount[requiredItem.ProtoItem] = -requiredItem.Count; } NotificationSystem.ServerSendItemsNotification(character, itemsChangedCount); }
private void ServerGatheringSystemGatherHandler(ICharacter character, IStaticWorldObject worldObject) { if (!(worldObject.ProtoStaticWorldObject is ObjectCorpse)) { return; } // corpse gathered! // find the device and vial var itemDevice = character.SharedGetPlayerContainerEquipment() .GetItemsOfProto(this) .FirstOrDefault(); if (itemDevice == null) { // don't have an equipped device return; } var protoItemVialEmpty = GetProtoEntity <ItemVialEmpty>(); // require at least one vial if (!character.ContainsItemsOfType(protoItemVialEmpty, requiredCount: 1)) { // don't have an empty vial this.CallClient(character, _ => _.ClientRemote_NotEnoughEmptyVials()); return; } var protoMob = (IProtoCharacterCore)worldObject.GetPublicState <ObjectCorpse.PublicState>() .ProtoCharacterMob; var healthMax = protoMob.StatDefaultHealthMax; var maxVialsToUse = (ushort)MathHelper.Clamp(Math.Floor(healthMax / 100.0), min: 1, max: 10); // destroy empty vial Server.Items.DestroyItemsOfType( character, protoItemVialEmpty, maxVialsToUse, out var destroyedEmptyVialsCount); if (destroyedEmptyVialsCount == 0) { // cannot destroy any empty vial (should be impossible) return; } // spawn biomaterial vials var createItemResult = Server.Items.CreateItem <ItemVialBiomaterial>(character, count: destroyedEmptyVialsCount); var itemChangedCount = NotificationSystem.SharedGetItemsChangedCount(createItemResult); itemChangedCount.Add(protoItemVialEmpty, -(int)destroyedEmptyVialsCount); NotificationSystem.ServerSendItemsNotification(character, itemChangedCount); ItemDurabilitySystem.ServerModifyDurability(itemDevice, -DurabilityDecreasePerUse); Logger.Info("Biomaterial collected successfully with Biomaterial collector", character); }
private static void ServerOnDroneReturnedToPlayer(IDynamicWorldObject worldObject) { var privateState = GetPrivateState(worldObject); var character = privateState.CharacterOwner; CharacterDroneControlSystem.ServerDespawnDrone(worldObject, isReturnedToPlayer: true); var storageContainer = privateState.StorageItemsContainer; if (storageContainer.OccupiedSlotsCount == 0) { return; } // drop storage container contents to player // but first, move the drone item to its original slot (if possible) var characterContainerInventory = character.SharedGetPlayerContainerInventory(); var characterContainerHotbar = character.SharedGetPlayerContainerHotbar(); var itemInFirstSlot = storageContainer.GetItemAtSlot(0); if (itemInFirstSlot is not null) { // item in the first slot is the drone's associated item // it could be destroyed in case the drone's HP dropped <= 1 Server.Items.MoveOrSwapItem(itemInFirstSlot, privateState.IsStartedFromHotbarContainer ? characterContainerHotbar : characterContainerInventory, slotId: privateState.StartedFromSlotIndex, movedCount: out _); } var result = Server.Items.TryMoveAllItems(storageContainer, characterContainerInventory); try { if (storageContainer.OccupiedSlotsCount == 0) { // all items moved from drone to player return; } // try move remaining items to hotbar var resultToHotbar = Server.Items.TryMoveAllItems(storageContainer, characterContainerHotbar); result.MergeWith(resultToHotbar, areAllItemsMoved: resultToHotbar.AreAllItemMoved); if (storageContainer.OccupiedSlotsCount == 0) { // all items moved from drone to player return; } } finally { if (result.MovedItems.Count > 0) { // notify player about the received items NotificationSystem.ServerSendItemsNotification( character, result.MovedItems .GroupBy(p => p.Key.ProtoItem) .Where(p => !(p.Key is TItemDrone)) .ToDictionary(p => p.Key, p => p.Sum(v => v.Value))); } } // try to drop the remaining items on the ground var groundContainer = ObjectGroundItemsContainer .ServerTryGetOrCreateGroundContainerAtTileOrNeighbors(character, character.Tile); if (groundContainer is not null) { var result2 = Server.Items.TryMoveAllItems(storageContainer, groundContainer); if (result2.MovedItems.Count > 0) { var protoItemForIcon = result2.MovedItems.First().Key.ProtoItem; NotificationSystem.ServerSendNotificationNoSpaceInInventoryItemsDroppedToGround( character, protoItemForIcon); // ensure that these items could be lifted only by their owner in PvE WorldObjectClaimSystem.ServerTryClaim(groundContainer.OwnerAsStaticObject, character, WorldObjectClaimDuration.DroppedGoods); } } if (storageContainer.OccupiedSlotsCount == 0) { return; } Logger.Error("Not all items dropped on the ground from the drone storage: " + worldObject + " slots occupied: " + storageContainer.OccupiedSlotsCount); }
private void ServerRemote_Uninstall(byte slotId) { var character = ServerRemoteContext.Character; var worldObject = InteractionCheckerSystem.GetCurrentInteraction(character); this.VerifyGameObject((IStaticWorldObject)worldObject); var itemToUninstall = character.SharedGetPlayerContainerEquipment() .GetItemAtSlot(slotId); if (itemToUninstall == null) { throw new Exception("No implant installed"); } var itemToUninstallProto = (IProtoItemEquipmentImplant)itemToUninstall.ProtoItem; var biomaterialRequiredAmount = CreativeModeSystem.SharedIsInCreativeMode(character) ? (ushort)0 : itemToUninstallProto.BiomaterialAmountRequiredToUninstall; if (!character.ContainsItemsOfType(ProtoItemBiomaterialVial.Value, requiredCount: biomaterialRequiredAmount) && !CreativeModeSystem.SharedIsInCreativeMode(character)) { throw new Exception("Not enough biomaterial vials"); } // move to inventory if (!Server.Items.MoveOrSwapItem(itemToUninstall, character.SharedGetPlayerContainerInventory(), out _)) { NotificationSystem.ServerSendNotificationNoSpaceInInventory(character); return; } if (biomaterialRequiredAmount > 0) { // destroy vials Server.Items.DestroyItemsOfType(character, ProtoItemBiomaterialVial.Value, countToDestroy: biomaterialRequiredAmount, out _); NotificationSystem.ServerSendItemsNotification( character, ProtoItemBiomaterialVial.Value, -biomaterialRequiredAmount); } if (itemToUninstallProto is ItemImplantBroken) { // broken implant destroys on uninstall Server.Items.DestroyItem(itemToUninstall); NotificationSystem.ServerSendItemsNotification( character, itemToUninstallProto, -1); } Logger.Info("Implant uninstalled: " + itemToUninstall); }
private void ServerRemote_Install(IItem itemToInstall, byte slotId) { var character = ServerRemoteContext.Character; var worldObject = InteractionCheckerSystem.GetCurrentInteraction(character); this.VerifyGameObject((IStaticWorldObject)worldObject); var containerEquipment = character.SharedGetPlayerContainerEquipment(); var currentInstalledItem = containerEquipment.GetItemAtSlot(slotId); if (currentInstalledItem != null) { if (currentInstalledItem == itemToInstall) { Logger.Info("The implant is already installed"); return; } throw new Exception("Please uninstall installed implant"); } if (itemToInstall.Container.OwnerAsCharacter != character || itemToInstall.Container == containerEquipment) { throw new Exception("The item to install must be in character containers (except equipment)"); } if (!containerEquipment.ProtoItemsContainer.CanAddItem( new CanAddItemContext(containerEquipment, itemToInstall, slotId, byCharacter: null, isExploratoryCheck: false))) { throw new Exception("Cannot install implant item there"); } var itemToInstallProto = (IProtoItemEquipmentImplant)itemToInstall.ProtoItem; if (itemToInstallProto is ItemImplantBroken) { throw new Exception("Cannot install broken implant"); } foreach (var equippedItem in containerEquipment.Items) { if (equippedItem.ProtoItem == itemToInstallProto) { throw new Exception("Another implant of this type is already installed: " + itemToInstallProto); } } var biomaterialRequiredAmount = CreativeModeSystem.SharedIsInCreativeMode(character) ? (ushort)0 : itemToInstallProto.BiomaterialAmountRequiredToInstall; if (!character.ContainsItemsOfType(ProtoItemBiomaterialVial.Value, requiredCount: biomaterialRequiredAmount)) { throw new Exception("Not enough biomaterial vials"); } if (!Server.Items.MoveOrSwapItem(itemToInstall, containerEquipment, out _, slotId: slotId)) { throw new Exception("Unknown error - cannot move implant item to the player equipment"); } if (biomaterialRequiredAmount > 0) { // destroy vials Server.Items.DestroyItemsOfType(character, ProtoItemBiomaterialVial.Value, countToDestroy: biomaterialRequiredAmount, out _); NotificationSystem.ServerSendItemsNotification( character, ProtoItemBiomaterialVial.Value, -biomaterialRequiredAmount); } Logger.Info("Implant installed: " + itemToInstall); }
private void ServerGatheringSystemGatherHandler(ICharacter character, IStaticWorldObject worldObject) { if (!(worldObject.ProtoStaticWorldObject is ObjectCorpse)) { return; } // corpse looted // find the device and vial var itemDevice = character.SharedGetPlayerContainerEquipment() .GetItemsOfProto(this) .FirstOrDefault(); if (itemDevice is null) { // don't have an equipped device return; } var protoItemVialEmpty = GetProtoEntity <ItemVialEmpty>(); // require at least one vial if (!character.ContainsItemsOfType(protoItemVialEmpty, requiredCount: 1)) { // don't have an empty vial this.CallClient(character, _ => _.ClientRemote_NotEnoughEmptyVials()); return; } var protoMob = worldObject.GetPublicState <ObjectCorpse.PublicState>() .ProtoCharacterMob; var healthMax = protoMob.StatDefaultHealthMax; var maxVialsToUse = (ushort)MathHelper.Clamp( Math.Floor(protoMob.BiomaterialValueMultiplier * healthMax / 100.0), min: 1, max: 10); // destroy empty vial Server.Items.DestroyItemsOfType( character, protoItemVialEmpty, maxVialsToUse, out var destroyedEmptyVialsCount); if (destroyedEmptyVialsCount == 0) { // cannot destroy any empty vial (should be impossible) return; } // spawn biomaterial vials var protoItemVialBiomaterial = Api.GetProtoEntity <ItemVialBiomaterial>(); var createItemResult = Server.Items.CreateItem(protoItemVialBiomaterial, character, count: destroyedEmptyVialsCount); if (!createItemResult.IsEverythingCreated) { createItemResult.Rollback(); var groundItemsContainer = ObjectGroundItemsContainer.ServerTryGetOrCreateGroundContainerAtTileOrNeighbors(character, character.Tile); if (groundItemsContainer is null) { Logger.Error("Not enough space around the player to drop a full vial"); // restore items Server.Items.CreateItem(protoItemVialEmpty, character, destroyedEmptyVialsCount); return; } createItemResult = Server.Items.CreateItem(protoItemVialBiomaterial, groundItemsContainer, count: destroyedEmptyVialsCount); if (!createItemResult.IsEverythingCreated) { createItemResult.Rollback(); Logger.Error("Not enough space around the player to drop a full vial"); // restore items Server.Items.CreateItem(protoItemVialEmpty, character, destroyedEmptyVialsCount); return; } NotificationSystem.ServerSendNotificationNoSpaceInInventoryItemsDroppedToGround(character, protoItemVialBiomaterial); } var itemChangedCount = NotificationSystem.SharedGetItemsChangedCount(createItemResult); itemChangedCount.Add(protoItemVialEmpty, -(int)destroyedEmptyVialsCount); NotificationSystem.ServerSendItemsNotification(character, itemChangedCount); ItemDurabilitySystem.ServerModifyDurability(itemDevice, -DurabilityDecreasePerUse); Logger.Info("Biomaterial collected successfully with Biomaterial collector", character); }
private static int ServerTryConsumeWaterBottles( ICharacter character, int waterAmount, int waterCapacity, List <IItem> bottlesToConsume) { var serverItemsService = Server.Items; var totalUsedBottlesCount = 0; var maxBottlesToConsume = MaxBottlesToConsumePerRefill; foreach (var itemBottle in bottlesToConsume) { // check if character owns this item if (!character.ProtoCharacter .SharedEnumerateAllContainers(character, includeEquipmentContainer: false) .Any(c => c.Items.Contains(itemBottle))) { throw new Exception("The character doesn't own " + itemBottle + " - cannot use it to reload"); } int itemBottleCountToSubstract; var itemBottleCount = itemBottle.Count; if (itemBottleCount == 0) { continue; } if (itemBottleCount > maxBottlesToConsume) { itemBottleCount = (ushort)maxBottlesToConsume; } if (waterAmount + itemBottleCount * BottleWaterAmount >= waterCapacity) { // there are more than enough item count in that item stack to fully refill the watering can itemBottleCountToSubstract = (int)Math.Ceiling((waterCapacity - waterAmount) / (double)BottleWaterAmount); waterAmount = waterCapacity; } else { // consume full item stack itemBottleCountToSubstract = itemBottleCount; waterAmount += itemBottleCount * BottleWaterAmount; } if (itemBottleCountToSubstract > 0) { maxBottlesToConsume -= itemBottleCountToSubstract; totalUsedBottlesCount += itemBottleCountToSubstract; // reduce item count var itemBottleNewCount = itemBottle.Count - itemBottleCountToSubstract; serverItemsService.SetCount( itemBottle, itemBottleNewCount, byCharacter: character, isSendingUpdatesToPlayer: true); // spawn empty bottles ItemBottleEmpty.ServerSpawnEmptyBottle(character, (ushort)itemBottleCountToSubstract); } if (waterAmount >= waterCapacity) { // fully refilled break; } if (maxBottlesToConsume <= 0) { // amount of bottles to consume exceeded break; } } NotificationSystem.ServerSendItemsNotification( character, new Dictionary <IProtoItem, int>() { { GetProtoEntity <ItemBottleWater>(), -totalUsedBottlesCount } }); return(waterAmount); }
private static double ServerTryConsumeFuelItems( ItemFuelRefillActionState state, ICharacter character, double fuelAmount, double fuelCapacity) { var maxItemsToConsumePerRefill = MaxItemsToConsumePerRefill; var consumedItems = new Dictionary <IProtoItem, int>(); foreach (var itemToConsume in state.ItemsToConsumeForRefill) { // check if character owns this item if (!character.ProtoCharacter .SharedEnumerateAllContainers(character, includeEquipmentContainer: false) .Any(c => c.Items.Contains(itemToConsume))) { throw new Exception("The character doesn't own " + itemToConsume + " - cannot use it to reload"); } int itemToConsumeCountToSubstract; var itemToConsumeCount = itemToConsume.Count; if (itemToConsumeCount == 0) { continue; } if (itemToConsumeCount > maxItemsToConsumePerRefill) { itemToConsumeCount = (ushort)maxItemsToConsumePerRefill; } var fuelAmountPerItem = ((IProtoItemFuel)itemToConsume.ProtoItem).FuelAmount; if (fuelAmount + itemToConsumeCount * fuelAmountPerItem >= fuelCapacity) { // there are more than enough item count in that item stack to fully refill itemToConsumeCountToSubstract = (int)Math.Ceiling((fuelCapacity - fuelAmount) / fuelAmountPerItem); fuelAmount = fuelCapacity; } else { // consume full item stack itemToConsumeCountToSubstract = itemToConsumeCount; fuelAmount += itemToConsumeCount * fuelAmountPerItem; } if (itemToConsumeCountToSubstract > 0) { maxItemsToConsumePerRefill -= itemToConsumeCountToSubstract; // reduce item count var itemToConsumeNewCount = itemToConsume.Count - itemToConsumeCountToSubstract; Server.Items.SetCount( itemToConsume, itemToConsumeNewCount, byCharacter: character); // ReSharper disable once PossibleNullReferenceException consumedItems.Add(itemToConsume.ProtoItem, -itemToConsumeCountToSubstract); } if (fuelAmount >= fuelCapacity) { // fully refilled break; } if (maxItemsToConsumePerRefill <= 0) { // amount of bottles to consume exceeded break; } } NotificationSystem.ServerSendItemsNotification(character, consumedItems); return(fuelAmount); }
public bool ServerGather(IStaticWorldObject worldObject, ICharacter character) { var privateState = GetPrivateState(worldObject); if (privateState.IsDropListSpawned) { // this loot container was already search - drop list was already spawned return(true); } // spawn items accordingly to the droplist privateState.IsDropListSpawned = true; var skillExperienceToAdd = SkillSearching.ExperienceAddWhenSearching * this.SearchingSkillExperienceMultiplier; var lootDroplist = this.ServerGetLootDroplist(worldObject); var dropItemContext = new DropItemContext(character, worldObject); CreateItemResult dropItemResult; if (this.IsAutoTakeAll) { // try to simply pickup the content dropItemResult = lootDroplist.TryDropToCharacter(character, dropItemContext, sendNoFreeSpaceNotification: false); if (dropItemResult.IsEverythingCreated && dropItemResult.TotalCreatedCount > 0) { NotificationSystem.ServerSendItemsNotification(character, dropItemResult); Server.World.DestroyObject(worldObject); // destroy object after success pickup character.ServerAddSkillExperience <SkillSearching>(skillExperienceToAdd); ServerLootEventHelper.OnLootReceived(character, worldObject); return(true); } dropItemResult.Rollback(); } // create a container and drop items there var attemptRemains = 100; var itemsContainer = privateState.ItemsContainer; do { dropItemResult = lootDroplist.TryDropToContainer(itemsContainer, dropItemContext); } // ensure that at least something is spawned... // perhaps that's not a good idea, but we have an attempts limit while (dropItemResult.TotalCreatedCount == 0 && --attemptRemains > 0); Server.Items.SetSlotsCount(itemsContainer, itemsContainer.OccupiedSlotsCount); character.ServerAddSkillExperience <SkillSearching>(skillExperienceToAdd); ServerLootEventHelper.OnLootReceived(character, worldObject); Server.World.EnterPrivateScope(character, worldObject); // register private scope exit on interaction cancel InteractionCheckerSystem.SharedRegister( character, worldObject, finishAction: isAbort => { if (worldObject.IsDestroyed) { return; } Server.World.ExitPrivateScope(character, worldObject); if (isAbort) { // notify client this.CallClient(character, _ => _.ClientRemote_FinishInteraction(worldObject)); } if (this.IsAutoDestroyWhenLooted) { // container was closed - destroy it Server.World.DestroyObject(worldObject); } }); Logger.Important($"Started object interaction with {worldObject} for {character}"); this.CallClient(character, _ => _.ClientRemote_OnContainerOpened(worldObject)); return(true); }
protected override void ServerOnStaticObjectDestroyedByCharacter( ICharacter byCharacter, WeaponFinalCache weaponCache, IStaticWorldObject targetObject) { base.ServerOnStaticObjectDestroyedByCharacter(byCharacter, weaponCache, targetObject); // drop chance and gained experience depends on the vegetation growth stage var growthProgressFraction = this.SharedGetGrowthProgress(targetObject); growthProgressFraction = MathHelper.Clamp(growthProgressFraction, 0.1, 1); try { var dropItemsList = this.DroplistOnDestroy; var dropItemContext = new DropItemContext(byCharacter, targetObject, weaponCache.ProtoWeapon, weaponCache.ProtoExplosive); var objectDrone = weaponCache.Drone; if (objectDrone is not null) { // drop resources into the internal storage of the drone var storageItemsContainer = ((IProtoDrone)objectDrone.ProtoGameObject) .ServerGetStorageItemsContainer(objectDrone); dropItemsList.TryDropToContainer(storageItemsContainer, dropItemContext, probabilityMultiplier: growthProgressFraction); } else if (weaponCache.ProtoWeapon is IProtoItemWeaponMelee) { // a melee weapon - try drop items to character var result = dropItemsList.TryDropToCharacterOrGround( byCharacter, targetObject.TilePosition, dropItemContext, groundContainer: out _, probabilityMultiplier: growthProgressFraction); if (result.TotalCreatedCount > 0) { NotificationSystem.ServerSendItemsNotification(byCharacter, result); } } else { // not a melee weapon or cannot drop to character - drop on the ground only dropItemsList.TryDropToGround( targetObject.TilePosition, dropItemContext, probabilityMultiplier: growthProgressFraction, groundContainer: out _); } } finally { if (weaponCache.ProtoWeapon is IProtoItemToolWoodcutting) { // add experience proportional to the vegetation structure points // (effectively - for the time spent on woodcutting) var exp = SkillLumbering.ExperienceAddPerStructurePoint; exp *= this.StructurePointsMax * growthProgressFraction; byCharacter?.ServerAddSkillExperience <SkillLumbering>(exp); } } }