private static double ServerGetLowestEquipmentDurabilityFraction(IEnumerable <IItem> items) { var lowestDurabilityFraction = 1.0; foreach (var item in items) { if (!(item.ProtoItem is IProtoItemEquipment protoItemEquipment)) { // not an equipment continue; } switch (protoItemEquipment.EquipmentType) { case EquipmentType.FullBody: case EquipmentType.Head: case EquipmentType.Chest: case EquipmentType.Legs: // consider these equipment item types for the durability check break; default: // don't consider implants, devices and other (future?) slots continue; } var durabilityFraction = ItemDurabilitySystem.SharedGetDurabilityFraction(item); if (durabilityFraction < lowestDurabilityFraction) { lowestDurabilityFraction = durabilityFraction; } } return(lowestDurabilityFraction); }
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 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 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); }
/// <summary> /// When the reactor completely shut downs, it will destroy all modules /// that have remaining durability below 1%. /// </summary> private static void ServerOnReactorShutdown(ObjectGeneratorPragmiumReactorPrivateState reactorPrivateState) { foreach (var item in reactorPrivateState.ItemsContainer.Items.ToList()) { if (item.ProtoGameObject is ProtoItemReactorModule && ItemDurabilitySystem.SharedGetDurabilityPercent(item) < 1) { ItemDurabilitySystem.ServerBreakItem(item); } } }
public static double SharedCalculateResultDurabilityFraction( IItem inputItem1, IItem inputItem2, ICharacter character) { var result = ItemDurabilitySystem.SharedGetDurabilityPercent(inputItem1) + ItemDurabilitySystem.SharedGetDurabilityPercent(inputItem2) + SkillMaintenance.SharedGetCurrentBonusPercent(character); result = MathHelper.Clamp(result, 0, 100); return(result / 100.0); }
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); } }
private void Refresh() { if (this.IsDisposed) { return; } var character = ClientCurrentCharacterHelper.Character; if (ClientCurrentCharacterFinalStatsHelper.FinalStatsCache.IsDirty) { // refresh will be called again automatically when the new final stats cache is assigned return; } this.PercentSkillText = SkillMaintenance.SharedGetCurrentBonusPercent(character) + "%"; var inputItem1 = this.containerInput.GetItemAtSlot(0); this.PercentInput1Text = GetDurabilityPercentText(inputItem1); var inputItem2 = this.containerInput.GetItemAtSlot(1); this.PercentInput2Text = GetDurabilityPercentText(inputItem2); var outputItem = this.containerOutput.GetItemAtSlot(0); if (outputItem != null) { this.PercentOutputText = string.Empty; return; } else { if (inputItem1 == null || inputItem2 == null) { this.PercentOutputText = "?"; } else { var resultPercent = ObjectTinkerTable.SharedCalculateResultDurabilityFraction(inputItem1, inputItem2, character); this.PercentOutputText = (byte)(100 * resultPercent) + "%"; } } string GetDurabilityPercentText(IItem item) => item == null ? string.Empty : ItemDurabilitySystem.SharedGetDurabilityPercent(item) + "%"; }
private static bool SharedTryFindItemsOfType( IItemsContainerProvider containers, IProtoItem requiredProtoItem, uint count, out List <IItem> result, double minQualityFraction) { var countToFindRemains = (int)count; result = new List <IItem>(); foreach (var container in containers.ItemsContainers) { foreach (var item in container.Items) { if (item.ProtoItem != requiredProtoItem) { continue; } if (requiredProtoItem is IProtoItemWithDurability && ItemDurabilitySystem.SharedGetDurabilityFraction(item) < minQualityFraction) { // not enough durability continue; } if (requiredProtoItem is IProtoItemWithFreshness && ItemFreshnessSystem.SharedGetFreshnessFraction(item) < minQualityFraction) { // not enough durability continue; } result.Add(item); countToFindRemains -= item.Count; if (countToFindRemains <= 0) { break; } } if (countToFindRemains <= 0) { break; } } return(countToFindRemains <= 0); }
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); } }
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); } }
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); } }
public static IItem ClientSelectNextDrone(List <IItem> exceptItems) { IItem selectedItem = null; var selectedItemOrder = int.MinValue; var privateState = ClientCurrentCharacterHelper.PrivateState; // find a drone item with the highest order number foreach (var item in privateState.ContainerHotbar.Items) { if (IsValidDroneItem(item, out var order) && order >= selectedItemOrder) { selectedItem = item; selectedItemOrder = order; } } foreach (var item in privateState.ContainerInventory.Items) { if (IsValidDroneItem(item, out var order) && order >= selectedItemOrder) { selectedItem = item; selectedItemOrder = order; } } return(selectedItem); bool IsValidDroneItem(IItem item, out int order) { if (item.ProtoItem is IProtoItemDrone protoItemDrone && ItemDurabilitySystem.SharedGetDurabilityValue(item) > 0 && !exceptItems.Contains(item)) { order = protoItemDrone.SelectionOrder; return(true); } order = int.MinValue; return(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); } }
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); }
private void TrySendDrone() { var targetsList = Api.Client.World.GetStaticWorldObjectsOfProto <IProtoStaticWorldObject>() .Where(IsValidObject) .OrderBy(o => CurrentCharacter.Position.DistanceTo(o.TilePosition.ToVector2D())) .ToList(); if (targetsList.Count == 0) { return; } int targetN = 0; int droneControlLimit = ((IProtoItemDroneControl)SelectedItem.ProtoGameObject).MaxDronesToControl; int droneNumberToSend = Math.Min( droneControlLimit - CurrentCharacter.SharedGetCurrentControlledDronesNumber(), targetsList.Count); using var tempExceptDrones = Api.Shared.GetTempList <IItem>(); for (var index = 0; index < droneNumberToSend; index++) { IItem itemDrone; do { itemDrone = CharacterDroneControlSystem.ClientSelectNextDrone(tempExceptDrones.AsList()); if (itemDrone is null) { return; } tempExceptDrones.Add(itemDrone); } while (ItemDurabilitySystem.SharedGetDurabilityFraction(itemDrone) < DroneDurabilityThreshold); if (!CharacterDroneControlSystem.ClientTryStartDrone(itemDrone, targetsList[targetN].TilePosition, showErrorNotification: false)) { return; } targetN++; } }
// 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); }
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); } }
private void CheckItemDurability([NotNull] IItem item) { double itemDurabilityFraction = ItemDurabilitySystem.SharedGetDurabilityFraction(item); if (IsUnequipEnabled && itemDurabilityFraction <= UnequipThreshold) { if (TryToMoveItem( item: item, toContainer: CurrentCharacter.SharedGetPlayerContainerInventory())) { if (item != null) { itemAlerts.Remove(item); } return; } } if (!itemAlerts.ContainsKey(item)) { itemAlerts.Add(item, new AlertDetails()); } if (IsAlertNotificationEnabled && itemDurabilityFraction <= AlertThreshold && (itemAlerts[item].Durability - itemDurabilityFraction >= AlertStep || (AlertTimeout > 0 && itemAlerts[item].Time + AlertTimeout < Api.Client.Core.ClientRealTime))) { itemAlerts[item].Durability = itemDurabilityFraction; itemAlerts[item].Time = Api.Client.Core.ClientRealTime; NotificationSystem.ClientShowNotification( title: "Item durability low!", color: NotificationColor.Bad, icon: item.ProtoItem.Icon); } }