protected override void SharedOnActionCompletedInternal(WateringActionState state, ICharacter character) { var worldObject = (IStaticWorldObject)state.TargetWorldObject; var protoPlant = (IProtoObjectPlant)worldObject.ProtoWorldObject; var itemWateringCan = state.ItemWateringCan; var protoWateringCan = (IProtoItemToolWateringCan)itemWateringCan.ProtoItem; if (IsServer) { protoPlant.ServerOnWatered(character, worldObject, wateringDuration: protoWateringCan.WateringDuration.TotalSeconds); } protoWateringCan.SharedOnWatered(itemWateringCan, worldObject); if (IsServer) { character.ServerAddSkillExperience <SkillFarming>(SkillFarming.ExperienceForWatering); // notify tool was used ServerItemUseObserver.NotifyItemUsed(character, itemWateringCan); // reduce watering can durability ItemDurabilitySystem.ServerModifyDurability(itemWateringCan, delta: -1); } }
protected override void ServerOnDegradeWeapon( ICharacter character, IItem weaponItem, IProtoItemWeapon protoWeapon, List <WeaponHitData> hitObjects) { if (hitObjects.Count == 0) { // no objects were hit return; } var decrease = this.DurabilityDecreasePerAction; foreach (var hit in hitObjects) { var protoObject = hit.WorldObject.ProtoWorldObject; if (protoObject is IProtoObjectWall || protoObject is IProtoObjectDoor || protoObject is IProtoObjectTradingStation) { // hit wall, door or station decrease *= 5; break; } } ItemDurabilitySystem.ServerModifyDurability( weaponItem, delta: -decrease); }
protected override void ServerUpdate(ServerUpdateData data) { var item = data.GameObject; if (!(item.Container?.ProtoItemsContainer is ItemsContainerGeneratorSolar)) { // the panel is not installed return; } var worldObjectGenerator = item.Container.OwnerAsStaticObject; if (worldObjectGenerator == null || worldObjectGenerator.IsDestroyed) { return; } // The panel is installed. // Decrease durability proportionally to the generation rate // (degrade only during the daytime and only if active). var rate = ProtoObjectGeneratorSolar.SharedGetElectricityProductionRate(worldObjectGenerator); if (rate <= 0) { return; } var decrease = this.DurabilityDecreasePerMinuteWhenInstalled * (data.DeltaTime / 60.0); decrease *= rate; ItemDurabilitySystem.ServerModifyDurability(item, delta: -(int)decrease); }
protected override void ServerUpdate(ServerUpdateData data) { var publicState = data.PublicState; if (!publicState.IsActive) { return; } var item = data.GameObject; ItemDurabilitySystem.ServerModifyDurability(item, -this.DurabilityDecreasePerSecond * data.DeltaTime, roundUp: true); // check if item is in a hotbar selected slot, if not - make it not active var itemOwnerCharacter = item.Container?.OwnerAsCharacter; if (itemOwnerCharacter == null || !itemOwnerCharacter.ServerIsOnline || item != itemOwnerCharacter.SharedGetPlayerSelectedHotbarItem()) { Logger.Info(item + " is not in the hotbar selected slot or player is offline - make it inactive"); publicState.IsActive = false; return; } ServerItemUseObserver.NotifyItemUsed(itemOwnerCharacter, item); this.ItemFuelConfig.SharedTryConsumeFuel(item, data.PrivateState, data.DeltaTime, out var isFuelRanOut); if (isFuelRanOut) { publicState.IsActive = false; } }
private void ServerRemote_OnActionCompletedInternal(IItem itemVehicleRemote, ICharacter character, Vector2D mousePosition) { if (!(itemVehicleRemote.ProtoItem is IProtoItemVehicleRemoteControl protoRemote)) { return; } if (!CharacterEnergySystem.SharedHasEnergyCharge(character, protoRemote.EngeryUse)) { return; } List <GarageVehicleEntry> list = VehicleGarageSystem.ServerGetCharacterVehicles(character, false); TakeVehicleResult result = TakeVehicleResult.Unknown; if (list.Count > 0) { var privateState = itemVehicleRemote.GetPrivateState <ItemVehicleRemoteControlPrivateState>(); result = this.ServerTakeVehicle(privateState, character, mousePosition); } if (result == TakeVehicleResult.Success) { CharacterEnergySystem.ServerDeductEnergyCharge(character, protoRemote.EngeryUse); ItemDurabilitySystem.ServerModifyDurability(itemVehicleRemote, delta: -1); } }
private static void ServerOnBatteryItemCharged(IItem item, double energyAdded) { if (energyAdded <= 0) { return; } var container = item.Container; // reduce durability proportionally to the added charge ItemDurabilitySystem.ServerModifyDurability(item, -(int)Math.Ceiling(energyAdded)); if (!item.IsDestroyed) { return; } // item was destroyed during recharging var character = container?.OwnerAsCharacter; if (character == null || character.SharedGetPlayerContainerEquipment() != container) { return; } // redistribute remaining energy to other energy bank devices var energyRemains = SharedGetPrivateState(item).EnergyCharge; ServerAddEnergyChargeInternal(character, energyRemains, invokeServerOnBatteryItemCharged: false); }
private void ServerApplyItemsDecay( ObjectGeneratorPragmiumReactorPrivateState reactorPrivateState, double deltaTime) { var activationProgress = reactorPrivateState.ActivationProgressPercents / 100.0; if (activationProgress <= 0) { reactorPrivateState.ServerAccumulatedDecayDuration = 0; return; } using var tempItemsList = Api.Shared.WrapInTempList(reactorPrivateState.ItemsContainer.Items); if (tempItemsList.Count == 0) { return; } reactorPrivateState.ServerAccumulatedDecayDuration += deltaTime; if (reactorPrivateState.ServerAccumulatedDecayDuration < ItemDecayIntervalDuration) { return; } // apply accumulated decay reactorPrivateState.ServerAccumulatedDecayDuration -= ItemDecayIntervalDuration; var decayMultiplier = activationProgress * ItemDecayIntervalDuration; var fuelDecayMultiplier = 100.0 / reactorPrivateState.Stats.FuelLifetimePercent; foreach (var item in tempItemsList.AsList()) { var protoGameObject = item.ProtoGameObject; switch (protoGameObject) { case ItemReactorFuelRod protoFuelRod: { var decayAmount = decayMultiplier * protoFuelRod.DurabilityMax / protoFuelRod.LifetimeDuration; decayAmount *= fuelDecayMultiplier; ItemDurabilitySystem.ServerModifyDurability(item, -(int)decayAmount); break; } case ProtoItemReactorModule protoItemModule: { var decayAmount = decayMultiplier * protoItemModule.DurabilityMax / protoItemModule.LifetimeDuration; ItemDurabilitySystem.ServerModifyDurability(item, -(int)decayAmount); break; } } } }
public static void ServerDespawnDrone(IDynamicWorldObject objectDrone, bool isReturnedToPlayer) { var privateState = objectDrone.GetPrivateState <DronePrivateState>(); var publicState = objectDrone.GetPublicState <DronePublicState>(); publicState.ResetTargetPosition(); if (privateState.IsDespawned) { return; } var droneItem = privateState.AssociatedItem; var protoItemDrone = (IProtoItemDrone)droneItem.ProtoItem; var characterOwner = privateState.CharacterOwner; var world = Server.World; var protoDrone = protoItemDrone.ProtoDrone; protoDrone.ServerOnDroneDroppedOrReturned(objectDrone, characterOwner, isReturnedToPlayer); // recreate physics (as despawned drone doesn't have any physics) privateState.IsDespawned = true; world.StopPhysicsBody(objectDrone.PhysicsBody); objectDrone.ProtoWorldObject.SharedCreatePhysics(objectDrone); world.SetPosition(objectDrone, ServerCharacterDeathMechanic.ServerGetGraveyardPosition().ToVector2D()); privateState.CharacterOwner = null; ServerOnDroneControlRemoved(characterOwner, objectDrone); var currentDurability = (int)(objectDrone.GetPublicState <DronePublicState>().StructurePointsCurrent / protoItemDrone.DurabilityToStructurePointsConversionCoefficient); if (currentDurability <= 1) { currentDurability = 0; } var deltaDurabilility = (int)(ItemDurabilitySystem.SharedGetDurabilityValue(droneItem) - currentDurability); if (deltaDurabilility <= 0) { return; } ItemDurabilitySystem.ServerModifyDurability(droneItem, -deltaDurabilility); if (droneItem.IsDestroyed) { // drone item degraded to 100%, notify the player ItemDurabilitySystem.Instance.CallClient(characterOwner, _ => _.ClientRemote_ItemBroke(droneItem.ProtoItem)); } }
private static void ServerOnEnergyUsed(IItem item, double energyAmountUsed) { if (energyAmountUsed <= 0) { return; } // reduce durability proportionally to the removed charge ItemDurabilitySystem.ServerModifyDurability(item, -(int)Math.Ceiling(energyAmountUsed)); }
private void SharedOnStageCompleted() { if (Api.IsServer) { // notify tool was used ServerItemUseObserver.NotifyItemUsed(this.Character, this.ItemVehicleRepairKit); // reduce tool durability ItemDurabilitySystem.ServerModifyDurability(this.ItemVehicleRepairKit, delta: -1); } else // if client { Api.Client.Audio.PlayOneShot(VehicleSystem.SoundResourceVehicleRepair); } this.currentStageDurationSeconds = this.CalculateStageDurationSeconds(this.Character, isFirstStage: false); this.currentStageTimeRemainsSeconds += this.currentStageDurationSeconds; var currentStructurePoints = this.VehiclePublicState.StructurePointsCurrent; var newStructurePoints = currentStructurePoints + this.stageStructureAddValue; if (Api.IsServer) { ((IProtoVehicle)this.Vehicle.ProtoGameObject) .ServerOnRepair(this.Vehicle, this.Character); } if (currentStructurePoints < this.structurePointsMax) { // repairing is still possible - more stages are available this.UpdateProgress(); if (Api.IsServer && this.ItemVehicleRepairKit.IsDestroyed) { // tool was destroyed (durability 0) this.AbortAction(); return; } return; } // repairing is absolutely completed if (Api.IsServer) { // TODO: add skill experience for repair // this.CharacterPrivateState.Skills.ServerAddSkillExperience<SkillVehicles>( // SkillVehicles.ExperienceAddWhenRepairFinished); } this.SetCompleted(isCancelled: false); VehicleRepairKitSystem.SharedActionCompleted(this.Character, this); }
public override void ServerOnItemDamaged(IItem item, double damageApplied) { var owner = item.Container.OwnerAsCharacter; if (owner != null) { damageApplied *= owner.SharedGetFinalStatMultiplier(StatName.ImplantDegradationFromDamageMultiplier); } ItemDurabilitySystem.ServerModifyDurability(item, delta: -(int)damageApplied); }
private void ServerRemote_RelocateStructure(IStaticWorldObject objectStructure, Vector2Ushort toPosition) { if (objectStructure.TilePosition == toPosition) { // relocation not required return; } var character = ServerRemoteContext.Character; if (!SharedValidateCanCharacterRelocateStructure(character, objectStructure, toPosition, errorMessage: out _, logErrors: true)) { return; } var fromPosition = objectStructure.TilePosition; Api.SafeInvoke( () => ServerStructureBeforeRelocating?.Invoke(character, fromPosition, objectStructure)); Server.World.SetPosition(objectStructure, toPosition); try { // ensure the structure is reinitialized (has its physics rebuilt, etc) objectStructure.ServerInitialize(); } catch (Exception ex) { Logger.Exception(ex); } ConstructionPlacementSystem.Instance.ServerNotifyOnStructurePlacedOrRelocated(objectStructure, character); Api.SafeInvoke( () => ServerStructureRelocated?.Invoke(character, fromPosition, objectStructure)); // let's deduct the tool durability if (CreativeModeSystem.SharedIsInCreativeMode(character)) { return; } // the item in hotbar is definitely a construction tool as it was validated above var itemConstructionTool = character.SharedGetPlayerSelectedHotbarItem(); ItemDurabilitySystem.ServerModifyDurability( itemConstructionTool, delta: -((IProtoObjectStructure)objectStructure.ProtoGameObject).RelocationToolDurabilityCost); }
private static void ServerOnEnergyUsed(IItem item, double energyAmountUsed) { if (energyAmountUsed <= 0) { return; } // reduce durability proportionally to the removed charge, durability as int is dropping to fast if (RandomHelper.Next(20) == 1) { ItemDurabilitySystem.ServerModifyDurability(item, -energyAmountUsed, true); } }
public override void ServerOnCharacterDeath(IItem item, bool isEquipped, out bool shouldDrop) { // implants never drop on death shouldDrop = false; if (!isEquipped) { return; } // durability reduced on death var fraction = MathHelper.Clamp(this.DurabilityFractionReduceOnDeath, 0, 1); var durabilityDelta = (ushort)(this.DurabilityMax * fraction); ItemDurabilitySystem.ServerModifyDurability(item, delta: -durabilityDelta); }
public virtual void ServerOnCharacterDeath(IItem item, bool isEquipped, out bool shouldDrop) { shouldDrop = true; if (!isEquipped) { return; } if (item.ProtoItem is IProtoItemWithDurablity protoItemWithDurablity) { // -10% of durability reduced on death const double durabilityFractionReduceOnDeath = 0.1; var durabilityDelta = (ushort)(protoItemWithDurablity.DurabilityMax * durabilityFractionReduceOnDeath); ItemDurabilitySystem.ServerModifyDurability(item, delta: -durabilityDelta); } }
public virtual void ServerOnCharacterDeath(IItem item, bool isEquipped, out bool shouldDrop) { shouldDrop = true; if (!isEquipped) { return; } if (item.ProtoItem is IProtoItemWithDurablity protoItemWithDurablity) { // -10% of durability reduced on death var durabilityDelta = protoItemWithDurablity.DurabilityMax * DurabilityFractionReduceOnDeath; ItemDurabilitySystem.ServerModifyDurability(item, delta: -durabilityDelta, roundUp: false); } }
protected sealed override void ServerUpdate(ServerUpdateData data) { base.ServerUpdate(data); if (this.durabilityDecreasePerServerUpdate <= 0) { // non-degradeable return; } if (data.DeltaTime <= 0) { // not processed return; } // try to degrade durability over time and give experience for cybernetic affinity skill var item = data.GameObject; var owner = item.Container?.OwnerAsCharacter; if (owner is null || !owner.ServerIsOnline || owner.SharedGetPlayerContainerEquipment() != item.Container) { // player offline or not an equipped item return; } if (CharacterIdleSystem.ServerIsIdlePlayer(owner)) { return; } var durabilityDecrease = this.durabilityDecreasePerServerUpdate; durabilityDecrease *= owner.SharedGetFinalStatMultiplier(StatName.ImplantDegradationSpeedMultiplier); ItemDurabilitySystem.ServerModifyDurability(item, -durabilityDecrease, roundUp: false); owner.ServerAddSkillExperience <SkillCyberneticAffinity>( data.DeltaTime * SkillCyberneticAffinity.ExperienceAddedPerImplantPerSecond); if (!item.IsDestroyed) { this.ServerUpdateInstalledImplant(data); } }
private void ServerGatheringSystemGatherHandler(ICharacter character, IStaticWorldObject worldObject) { if (!(worldObject.ProtoStaticWorldObject is ObjectCorpse)) { return; } // corpse looted var itemDevice = character.SharedGetPlayerContainerEquipment() .GetItemsOfProto(this) .FirstOrDefault(); if (itemDevice == null) { // don't have an equipped device return; } ItemDurabilitySystem.ServerModifyDurability(itemDevice, -DurabilityDecreasePerUse); }
public override void ServerOnCharacterDeath(IItem item, bool isEquipped, out bool shouldDrop) { if (!isEquipped) { // not installed implants always drop on death shouldDrop = true; return; } // installed implants never drop on death shouldDrop = false; // reduce implant's durability on death var fraction = MathHelper.Clamp(this.DurabilityFractionReduceOnDeath, 0, 1); var durabilityDelta = this.DurabilityMax * fraction; ItemDurabilitySystem.ServerModifyDurability(item, delta: -durabilityDelta, roundUp: false); }
public virtual void ServerOnCharacterDeath(IItem item, bool isEquipped, out bool shouldDrop) { // only unequipped items will drop unless full loot system enabled shouldDrop = ItemConstants.ServerPvpIsFullLootEnabled || !isEquipped; if (!isEquipped) { return; } if (item.ProtoItem is IProtoItemWithDurability protoItemWithDurability) { // reduce equipped item's durability on death var durabilityDelta = protoItemWithDurability.DurabilityMax * DurabilityFractionReduceOnDeath; ItemDurabilitySystem.ServerModifyDurability(item, delta: -durabilityDelta, roundUp: false); } }
// please note that requiredEnergyAmount is <= ushort.MaxValue private static bool ServerDeductEnergyChargeInternal(IDynamicWorldObject vehicle, uint requiredEnergyAmount) { if (requiredEnergyAmount <= 0) { return(true); } using var tempItemsList = SharedGetTempListFuelItemsForVehicle(vehicle); if (tempItemsList.Count == 0) { // there are no battery packs equipped return(false); } // deduct energy foreach (var item in tempItemsList.AsList()) { var privateState = SharedGetPrivateState(item); var charge = privateState.DurabilityCurrent; if (charge <= 0) { // no charge there continue; } if (charge >= requiredEnergyAmount) { // there are more than enough charge available ItemDurabilitySystem.ServerModifyDurability(item, -(int)requiredEnergyAmount); return(true); } // use all the remaining charge in this item requiredEnergyAmount -= charge; ItemDurabilitySystem.ServerModifyDurability(item, -(int)charge); } // probably consumed but not enough energy (remaining requiredEnergyAmount > 0) return(false); }
private void ServerRemote_RelocateStructure(IStaticWorldObject objectStructure, Vector2Ushort toPosition) { if (objectStructure.TilePosition == toPosition) { // relocation not required return; } var character = ServerRemoteContext.Character; if (!SharedValidateCanCharacterRelocateStructure(character, objectStructure, toPosition, logErrors: true)) { return; } Api.SafeInvoke( () => ServerStructureBeforeRelocating?.Invoke(character, objectStructure)); Server.World.SetPosition(objectStructure, toPosition); ConstructionPlacementSystem.Instance.ServerNotifyOnStructurePlacedOrRelocated(objectStructure, character); Api.SafeInvoke( () => ServerStructureRelocated?.Invoke(character, objectStructure)); // let's deduct the tool durability if (CreativeModeSystem.SharedIsInCreativeMode(character)) { return; } // the item in hotbar is definitely a construction tool as it was validated above var itemConstructionTool = character.SharedGetPlayerSelectedHotbarItem(); ItemDurabilitySystem.ServerModifyDurability(itemConstructionTool, delta: -ToolDurabilityCostForStructureRelocation); }
public static bool ConditionHasDeviceEquipped(DropItemContext context) { // Please note: checking this condition will also automatically deduct the device's durability. if (!context.HasCharacter) { return(false); } // find the device var itemDevice = context.Character.SharedGetPlayerContainerEquipment() .GetItemsOfProto(protoItemKeiniteCollector) .FirstOrDefault(); if (itemDevice is null) { // don't have an equipped device return(false); } ItemDurabilitySystem.ServerModifyDurability(itemDevice, -DurabilityDecreasePerUse); return(true); }
protected sealed override void ServerUpdate(ServerUpdateData data) { base.ServerUpdate(data); if (this.durabilityDecreasePerServerUpdate <= 0) { // non-degradeable return; } if (data.DeltaTime <= 0) { // not processed return; } // try to degrade durability over time var item = data.GameObject; var owner = item.Container?.OwnerAsCharacter; if (owner == null || !owner.IsOnline || owner.SharedGetPlayerContainerEquipment() != item.Container) { // player offline or not an equipped item return; } var durabilityDecrease = this.durabilityDecreasePerServerUpdate; durabilityDecrease *= owner.SharedGetFinalStatMultiplier(StatName.ImplantDegradationSpeedMultiplier); ItemDurabilitySystem.ServerModifyDurability(item, -(int)Math.Floor(durabilityDecrease)); if (!item.IsDestroyed) { this.ServerUpdateInstalledImplant(data); } }
protected override void ServerOnDegradeWeapon( ICharacter character, IItem weaponItem, IProtoItemWeapon protoWeapon, IReadOnlyList <IWorldObject> hitObjects) { if (hitObjects.Count == 0) { // no objects were hit return; } var decrease = this.DurabilityDecreasePerAction; foreach (var hitObject in hitObjects) { var protoObject = hitObject.ProtoWorldObject; if (protoObject is IProtoObjectWall || protoObject is IProtoObjectDoor || protoObject is IProtoObjectTradingStation) { if (LandClaimSystem.SharedIsObjectInsideAnyArea((IStaticWorldObject)hitObject)) { // hit a wall, door or station inside a land claim area - take a durability penalty decrease = (ushort)Math.Min( Math.Ceiling(decrease * this.DurabilityDecreaseMultiplierWhenHittingBuildings), ushort.MaxValue); break; } } } ItemDurabilitySystem.ServerModifyDurability( weaponItem, delta: -decrease); }
public virtual void ServerOnItemDamaged(IItem item, double damageApplied) { ItemDurabilitySystem.ServerModifyDurability(item, delta: -(int)damageApplied); }
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 void SharedOnStageCompleted() { if (Api.IsServer && this.ItemCrowbarTool is not null) { // notify tool was used ServerItemUseObserver.NotifyItemUsed(this.Character, this.ItemCrowbarTool); // reduce tool durability ItemDurabilitySystem.ServerModifyDurability(this.ItemCrowbarTool, delta: -1); } this.currentStageDurationSeconds = this.CalculateStageDurationSeconds(this.Character, isFirstStage: false); this.currentStageTimeRemainsSeconds += this.currentStageDurationSeconds; var oldStructurePoints = this.ObjectPublicState.StructurePointsCurrent; var newStructurePoints = Math.Max( 0, oldStructurePoints - this.stageStructureRemoveValue); this.protoStructure.SharedOnDeconstructionStage( this.WorldObject, this.Character, oldStructurePoints, newStructurePoints); if (Api.IsClient || // client will simply always deconstruct until finished newStructurePoints > 0) { // deconstruction progressed if (Api.IsServer) { Logger.Important( $"Deconstruction progressed: {this.WorldObject} structure points: {newStructurePoints}/{this.structurePointsMax}; by {this.Character}"); } this.UpdateProgress(); if (Api.IsServer && this.ItemCrowbarTool is not null && this.ItemCrowbarTool.IsDestroyed) { // tool was destroyed (durability 0) this.AbortAction(); return; } return; } // deconstruction is completed if (Api.IsServer) { if (!(this.protoStructure is ProtoObjectConstructionSite)) { this.CharacterPrivateState.Skills.ServerAddSkillExperience <SkillBuilding>( SkillBuilding.ExperienceAddWhenDeconstructionFinished); } Logger.Important( $"Deconstruction completed: {this.WorldObject} structure points: {newStructurePoints}/{this.structurePointsMax}; by {this.Character}"); this.ObjectPublicState.StructurePointsCurrent = newStructurePoints; } this.SetCompleted(isCancelled: false); DeconstructionSystem.SharedActionCompleted(this.Character, this); }
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 void SharedOnStageCompleted() { if (!this.ValidateRequiredItemsAvailable()) { // don't have required items - cannot do building/repairing action this.AbortAction(); return; } if (Api.IsServer) { // items are removing only on the Server-side this.Config.ServerDestroyRequiredItems(this.Character); // notify tool was used ServerItemUseObserver.NotifyItemUsed(this.Character, this.ItemConstructionTool); // reduce tool durability ItemDurabilitySystem.ServerModifyDurability(this.ItemConstructionTool, delta: -1); } this.currentStageDurationSeconds = this.CalculateStageDurationSeconds(this.Character, isFirstStage: false); this.currentStageTimeRemainsSeconds += this.currentStageDurationSeconds; var currentStructurePoints = this.ObjectPublicState.StructurePointsCurrent; var newStructurePoints = currentStructurePoints + this.stageStructureAddValue; // Please note: as we're using floating number (StructurePointsCurrent) to track the construction progress // it might cause some inaccuracy. In order to avoid this inaccuracy we're adding some tolerance. // The tolerance is also needed to handle the case when the blueprint was damaged only slightly. var completionTolerance = this.stageStructureAddValue / 2.0; // Please note: client will simply always construct until finished // Server will keep building stages until the completion tolerance reached. if ((Api.IsClient && currentStructurePoints < this.structurePointsMax) || (Api.IsServer && newStructurePoints + completionTolerance < this.structurePointsMax)) { // repairing/building is still possible - more stages are available if (Api.IsServer) { this.ObjectPublicState.StructurePointsCurrent = (float)newStructurePoints; Api.Logger.Important( $"Building/repairing progressed: {this.WorldObject} structure points: {newStructurePoints}/{this.structurePointsMax}; by {this.Character}"); if (this.IsRepair) { ((IProtoObjectStructure)this.WorldObject.ProtoStaticWorldObject) .ServerOnRepairStageFinished(this.WorldObject, this.Character); } } this.UpdateProgress(); if (!this.ValidateRequiredItemsAvailable()) { // don't have enough required items - cannot continue building/repairing action this.AbortAction(); return; } if (Api.IsServer && this.ItemConstructionTool.IsDestroyed) { // tool was destroyed (durability 0) this.AbortAction(); return; } return; } // repairing/building is completed if (Api.IsServer) { newStructurePoints = this.structurePointsMax; Api.Logger.Important( $"Building/repairing completed: {this.WorldObject} structure points: {newStructurePoints}/{this.structurePointsMax}; by {this.Character}"); this.ObjectPublicState.StructurePointsCurrent = (float)newStructurePoints; if (this.IsRepair) { ((IProtoObjectStructure)this.WorldObject.ProtoStaticWorldObject) .ServerOnRepairStageFinished(this.WorldObject, this.Character); } if (this.ObjectPublicState is ConstructionSitePublicState constructionSiteState) { constructionSiteState.LastBuildActionDoneByCharacter = this.Character; var constructionProto = ProtoObjectConstructionSite.SharedGetConstructionProto(this.WorldObject); // add skill experience for building this.CharacterPrivateState.Skills.ServerAddSkillExperience <SkillBuilding>( SkillBuilding.ExperienceAddWhenBuildingFinished * constructionProto.BuildingSkillExperienceMultiplier); } else { // add skill experience for repair this.CharacterPrivateState.Skills.ServerAddSkillExperience <SkillBuilding>( SkillBuilding.ExperienceAddWhenRepairFinished * ((IProtoObjectStructure)this.WorldObject.ProtoGameObject).BuildingSkillExperienceMultiplier); } } this.SetCompleted(isCancelled: false); ConstructionSystem.SharedActionCompleted(this.Character, this); }