diff --git a/TShockAPI/Configuration/ServerSideConfig.cs b/TShockAPI/Configuration/ServerSideConfig.cs index 7008af9b..8e777303 100644 --- a/TShockAPI/Configuration/ServerSideConfig.cs +++ b/TShockAPI/Configuration/ServerSideConfig.cs @@ -72,6 +72,10 @@ namespace TShockAPI.Configuration /// [Description("Warns players and the console if a player has the tshock.ignore.ssc permission with data in the SSC table.")] public bool WarnPlayersAboutBypassPermission = true; + + /// If set to true, items given to players will be inserted directly into their inventory. Requires SSC. Otherwise, items given to players will spawn as dropped items. + [Description("If set to true, items given to players will be inserted directly into their inventory. Requires SSC. Otherwise, items given to players will spawn as dropped items.")] + public bool GiveItemsDirectly = false; } /// diff --git a/TShockAPI/Configuration/TShockConfig.cs b/TShockAPI/Configuration/TShockConfig.cs index 21538129..e1781500 100644 --- a/TShockAPI/Configuration/TShockConfig.cs +++ b/TShockAPI/Configuration/TShockConfig.cs @@ -160,7 +160,7 @@ namespace TShockAPI.Configuration /// Disables tombstone dropping during death for all players. [Description("Disables tombstone dropping during death for all players.")] public bool DisableTombstones = true; - + /// /// Disables Skeletron Prime Bombs from spawning, useful for preventing unwanted world destruction on for the worthy seed world. /// diff --git a/TShockAPI/TSPlayer.cs b/TShockAPI/TSPlayer.cs index 73820eaa..39b10d8f 100644 --- a/TShockAPI/TSPlayer.cs +++ b/TShockAPI/TSPlayer.cs @@ -1461,7 +1461,7 @@ namespace TShockAPI } /// - /// Sends a rectangle of tiles at a location with the given length and width. + /// Sends a rectangle of tiles at a location with the given length and width. /// /// The x coordinate the rectangle will begin at /// The y coordinate the rectangle will begin at @@ -1508,6 +1508,115 @@ namespace TShockAPI /// The item stack. /// The item prefix. public virtual void GiveItem(int type, int stack, int prefix = 0) + { + if (TShock.ServerSideCharacterConfig.Settings.GiveItemsDirectly) + GiveItemDirectly(type, stack, prefix); + else + GiveItemByDrop(type, stack, prefix); + } + + private Item EmptySentinelItem = new Item(); + + private bool Depleted(Item item) + => item.type == 0 || item.stack == 0; + + private void GiveItemDirectly(int type, int stack, int prefix) + { + if (ItemID.Sets.IsAPickup[type] || !Main.ServerSideCharacter || this.IsDisabledForSSC) + { + GiveItemByDrop(type, stack, prefix); + return; + } + + var item = new Item(); + item.netDefaults(type); + item.stack = stack; + item.prefix = (byte)prefix; + + if (item.IsACoin) + for (int slot = -4; slot < 50; slot++) + if (Depleted(item = GiveItemDirectly_FillIntoOccupiedSlot(item, slot < 0 ? slot + 54 : slot))) + return; + + if (item.FitsAmmoSlot()) + if (Depleted(item = GiveItem_FillAmmo(item))) + return; + + for (int slot = 0; slot < 50; slot++) + if (Depleted(item = GiveItemDirectly_FillIntoOccupiedSlot(item, slot))) + return; + + if (!item.IsACoin && item.useStyle != 0) + for (int slot = 0; slot < 10; slot++) + if (Depleted(item = GiveItemDirectly_FillEmptyInventorySlot(item, slot))) + return; + + int lastSlot = item.IsACoin ? 54 : 50; + for (int slot = lastSlot - 1; slot >= 0; slot--) + if (Depleted(item = GiveItemDirectly_FillEmptyInventorySlot(item, slot))) + return; + + // oh no, i can't give... guess i gotta spill it on the floor + GiveItemByDrop(type, stack, prefix); + } + + private void SendItemSlotPacketFor(int slot) + { + int prefix = this.TPlayer.inventory[slot].prefix; + NetMessage.SendData(5, this.Index, -1, null, this.Index, slot, prefix, 0f, 0, 0, 0); + } + + private Item GiveItem_FillAmmo(Item item) + { + var inv = this.TPlayer.inventory; + + for (int i = 54; i < 58; i++) + if (Depleted(item = GiveItemDirectly_FillIntoOccupiedSlot(item, i))) + return EmptySentinelItem; + + if (!item.CanFillEmptyAmmoSlot()) + return item; + + for (int i = 54; i < 58; i++) + if (GiveItemDirectly_FillEmptyInventorySlot(item, i) == EmptySentinelItem) + return EmptySentinelItem; + + return item; + } + + private Item GiveItemDirectly_FillIntoOccupiedSlot(Item item, int slot) + { + var inv = this.TPlayer.inventory; + if (inv[slot].type <= 0 || inv[slot].stack >= inv[slot].maxStack || !item.IsTheSameAs(inv[slot])) + return item; + + if (item.stack + inv[slot].stack <= inv[slot].maxStack) + { + inv[slot].stack += item.stack; + SendItemSlotPacketFor(slot); + return EmptySentinelItem; + } + + var newItem = item.DeepClone(); + newItem.stack -= inv[slot].maxStack - inv[slot].stack; + inv[slot].stack = inv[slot].maxStack; + SendItemSlotPacketFor(slot); + + return newItem; + } + + private Item GiveItemDirectly_FillEmptyInventorySlot(Item item, int slot) + { + var inv = this.TPlayer.inventory; + if (inv[slot].type != 0) + return item; + + inv[slot] = item; + SendItemSlotPacketFor(slot); + return EmptySentinelItem; + } + + private void GiveItemByDrop(int type, int stack, int prefix) { int itemIndex = Item.NewItem(new EntitySource_DebugCommand(), (int)X, (int)Y, TPlayer.width, TPlayer.height, type, stack, true, prefix, true); Main.item[itemIndex].playerIndexTheItemIsReservedFor = this.Index; diff --git a/TerrariaServerAPI b/TerrariaServerAPI index e87215aa..c6b0fd87 160000 --- a/TerrariaServerAPI +++ b/TerrariaServerAPI @@ -1 +1 @@ -Subproject commit e87215aa183dfce43458341f975647cdee5d1710 +Subproject commit c6b0fd879e0d68c5125776ecde89ff3d3f730023 diff --git a/docs/changelog.md b/docs/changelog.md index 3d2733fe..9807b85b 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -70,6 +70,7 @@ Use past tense when adding new entries; sign your name off when you add or chang * Fix players being kicked after using the Flamethrower to apply the `OnFire3` debuff for `1200` ticks. (@BashGuy10) * Fix being kicked for using the new sponge types on liquid. (@BashGuy10) * Allow flask buffs to be applied on town npc due to the Flymeal. Add a permission could skip the buff detection. (@KawaiiYuyu) +* Add ability for items given to players to be inserted directly into their inventory instead of spawned as an item drop (@pontaoski) ## TShock 4.5.18 * Fixed `TSPlayer.GiveItem` not working if the player is in lava. (@PotatoCider) diff --git a/docs/ssc-config.md b/docs/ssc-config.md index 8d96a3ea..4e1dbf2e 100644 --- a/docs/ssc-config.md +++ b/docs/ssc-config.md @@ -3,6 +3,11 @@ Enable server side characters, causing client data to be saved on the server ins * **Field type**: `Boolean` * **Default**: `False` +## GiveItemsDirectly +If set to true, items given to players will be inserted directly into their inventory. Requires SSC. Otherwise, items given to players will spawn as dropped items. +* **Field type**: `Boolean` +* **Default**: `False` + ## LogonDiscardThreshold Time, in milliseconds, to disallow discarding items after logging in when ServerSideCharacters is ON. * **Field type**: `Int32`