public static CreateItemResult TryDropToCharacter( IReadOnlyDropItemsList dropItemsList, ICharacter character, bool sendNoFreeSpaceNotification, double probabilityMultiplier, DropItemContext context) { if (character == null) { return(new CreateItemResult() { IsEverythingCreated = false }); } var itemsService = Api.Server.Items; var result = dropItemsList.Execute( (protoItem, count) => itemsService.CreateItem(character, protoItem, count), context, probabilityMultiplier); if (sendNoFreeSpaceNotification && !result.IsEverythingCreated) { NotificationSystem.ServerSendNotificationNoSpaceInInventory(character); } return(result); }
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 override void SharedOnActionCompletedInternal(BottleRefillAction state, ICharacter character) { var itemBottle = state.ItemEmptyBottle; var requiredWaterProtoTile = state.WaterProtoTileToRefill; if (requiredWaterProtoTile == null) { throw new Exception("Impossible"); } if (IsClient) { return; } var isNeedToReduceItemCount = true; if (itemBottle.Count == 1) { // destroy last item isNeedToReduceItemCount = false; Server.Items.SetCount(itemBottle, count: 0, byCharacter: character); } // spawn filled bottle item var result = requiredWaterProtoTile is TileWaterSea ? Server.Items.CreateItem <ItemBottleWaterSalty>(character) : Server.Items.CreateItem <ItemBottleWaterStale>(character); if (!result.IsEverythingCreated) { result.Rollback(); NotificationSystem.ServerSendNotificationNoSpaceInInventory(character); return; } if (isNeedToReduceItemCount) { // reduce item count Server.Items.SetCount(itemBottle, itemBottle.Count - 1, byCharacter: character); } var itemsChangedCount = NotificationSystem.SharedGetItemsChangedCount(result); this.CallClient(character, _ => _.ClientRemote_ActionCompleted(itemsChangedCount)); }
/// <summary> /// Executed when a weapon must reload (after the reloading duration is completed). /// </summary> private static void SharedProcessWeaponReload( ICharacter character, WeaponState weaponState, out bool isAmmoTypeChanged) { var weaponReloadingState = weaponState.WeaponReloadingState; // remove weapon reloading state weaponState.WeaponReloadingState = null; var itemWeapon = weaponReloadingState.Item; var itemWeaponProto = (IProtoItemWeapon)itemWeapon.ProtoGameObject; var itemWeaponPrivateState = itemWeapon.GetPrivateState <WeaponPrivateState>(); var weaponAmmoCount = (int)itemWeaponPrivateState.AmmoCount; var weaponAmmoCapacity = itemWeaponProto.AmmoCapacity; isAmmoTypeChanged = false; var selectedProtoItemAmmo = weaponReloadingState.ProtoItemAmmo; var currentProtoItemAmmo = itemWeaponPrivateState.CurrentProtoItemAmmo; if (weaponAmmoCount > 0) { if (selectedProtoItemAmmo != currentProtoItemAmmo && weaponAmmoCount > 0) { // unload current ammo if (IsServer) { var targetContainers = SharedGetTargetContainersForCharacterAmmo(character, isForAmmoUnloading: true); var result = Server.Items.CreateItem( protoItem: currentProtoItemAmmo, new AggregatedItemsContainers(targetContainers), count: (ushort)weaponAmmoCount); if (!result.IsEverythingCreated) { // cannot unload current ammo - no space, try to unload to the ground result.Rollback(); var tile = Api.Server.World.GetTile(character.TilePosition); var groundContainer = ObjectGroundItemsContainer .ServerTryGetOrCreateGroundContainerAtTileOrNeighbors(character, tile); if (groundContainer is null) { // cannot unload current ammo to the ground - no free space around character NotificationSystem.ServerSendNotificationNoSpaceInInventory(character); return; } result = Server.Items.CreateItem( container: groundContainer, protoItem: currentProtoItemAmmo, count: (ushort)weaponAmmoCount); if (!result.IsEverythingCreated) { // cannot unload current ammo to the ground - no space in ground containers near the character result.Rollback(); NotificationSystem.ServerSendNotificationNoSpaceInInventory(character); return; } // notify player that there were not enough space in inventory so the items were dropped to the ground NotificationSystem.ServerSendNotificationNoSpaceInInventoryItemsDroppedToGround( character, result.ItemAmounts.First().Key?.ProtoItem); } } Logger.Info( $"Weapon ammo unloaded: {itemWeapon} -> {weaponAmmoCount} {currentProtoItemAmmo})", character); weaponAmmoCount = 0; itemWeaponPrivateState.SetAmmoCount(0); } else // if the same ammo type is loaded if (weaponAmmoCount == weaponAmmoCapacity) { // already completely loaded Logger.Info( $"Weapon reloading cancelled: {itemWeapon} - no reloading is required ({weaponAmmoCount}/{weaponAmmoCapacity} {selectedProtoItemAmmo})", character); return; } } else // if ammoCount == 0 if (selectedProtoItemAmmo is null && currentProtoItemAmmo is null) { Logger.Info( $"Weapon reloading cancelled: {itemWeapon} - already unloaded ({weaponAmmoCount}/{weaponAmmoCapacity})", character); return; } if (selectedProtoItemAmmo != null) { var selectedAmmoGroup = SharedGetCompatibleAmmoGroups(character, itemWeaponProto) .FirstOrDefault(g => g.Key == selectedProtoItemAmmo); if (selectedAmmoGroup is null) { Logger.Warning( $"Weapon reloading impossible: {itemWeapon} - no ammo of the required type ({selectedProtoItemAmmo})", character); return; } var ammoItems = SharedSelectAmmoItemsFromGroup(selectedAmmoGroup, ammoCountNeed: weaponAmmoCapacity - weaponAmmoCount); foreach (var request in ammoItems) { var itemAmmo = request.Item; Api.Assert(itemAmmo.ProtoItem == selectedProtoItemAmmo, "Sanity check"); int ammoToSubstract; var itemAmmoCount = itemAmmo.Count; if (itemAmmoCount == 0) { continue; } if (request.Count != itemAmmoCount) { if (request.Count < itemAmmoCount) { itemAmmoCount = request.Count; } else if (IsServer) { Logger.Warning( $"Trying to take more ammo to reload than player have: {itemAmmo} requested {request.Count}. Will reload as much as possible only.", character); } } if (weaponAmmoCount + itemAmmoCount >= weaponAmmoCapacity) { // there are more than enough ammo in that item stack to fully refill the weapon ammoToSubstract = weaponAmmoCapacity - weaponAmmoCount; weaponAmmoCount = weaponAmmoCapacity; } else { // consume full item stack ammoToSubstract = itemAmmoCount; weaponAmmoCount += itemAmmoCount; } // reduce ammo item count if (IsServer) { Server.Items.SetCount( itemAmmo, itemAmmo.Count - ammoToSubstract, byCharacter: character); } if (weaponAmmoCount == weaponAmmoCapacity) { // the weapon is fully reloaded, no need to subtract ammo from the next ammo items break; } } } if (currentProtoItemAmmo != selectedProtoItemAmmo) { // another ammo type selected itemWeaponPrivateState.CurrentProtoItemAmmo = selectedProtoItemAmmo; // reset weapon cache (it will be re-calculated on next fire processing) weaponState.WeaponCache = null; isAmmoTypeChanged = true; } if (weaponAmmoCount < 0 || weaponAmmoCount > weaponAmmoCapacity) { Logger.Error( "Something is completely wrong during reloading! Result ammo count is: " + weaponAmmoCount); weaponAmmoCount = 0; } itemWeaponPrivateState.SetAmmoCount((ushort)weaponAmmoCount); if (weaponAmmoCount == 0) { // weapon unloaded - and the log entry about this is already written (see above) return; } Logger.Info( $"Weapon reloaded: {itemWeapon} - ammo {weaponAmmoCount}/{weaponAmmoCapacity} {selectedProtoItemAmmo?.ToString() ?? "<no ammo>"}", character); if (IsServer) { ServerNotifyAboutReloading(character, weaponState, isFinished: true); } else { weaponState.ProtoWeapon.SoundPresetWeapon .PlaySound(WeaponSound.ReloadFinished, character, SoundConstants.VolumeWeapon); } }
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); }