diff --git a/Zennysoft.Game.Abstractions/Entity/IStackable.cs b/Zennysoft.Game.Abstractions/Entity/IStackable.cs new file mode 100644 index 00000000..e70cab9c --- /dev/null +++ b/Zennysoft.Game.Abstractions/Entity/IStackable.cs @@ -0,0 +1,8 @@ +namespace Zennysoft.Game.Abstractions; + +public interface IStackable +{ + int Count { get; } + + void SetCount(int count); +} diff --git a/Zennysoft.Game.Ma.Implementation/UI/InGameUI/state/States/InGameUI.State.InventoryOpen.cs b/Zennysoft.Game.Ma.Implementation/UI/InGameUI/state/States/InGameUI.State.InventoryOpen.cs index ccff79d1..9dadb647 100644 --- a/Zennysoft.Game.Ma.Implementation/UI/InGameUI/state/States/InGameUI.State.InventoryOpen.cs +++ b/Zennysoft.Game.Ma.Implementation/UI/InGameUI/state/States/InGameUI.State.InventoryOpen.cs @@ -7,7 +7,7 @@ public partial class InGameUILogic public partial record State { [Meta] - public partial record InventoryOpen : Active, IGet + public partial record InventoryOpen : State, IGet { public InventoryOpen() { diff --git a/Zennysoft.Game.Ma.Implementation/UI/InGameUI/state/States/InGameUI.State.MinimapOpen.cs b/Zennysoft.Game.Ma.Implementation/UI/InGameUI/state/States/InGameUI.State.MinimapOpen.cs index 46375b7e..5dc70509 100644 --- a/Zennysoft.Game.Ma.Implementation/UI/InGameUI/state/States/InGameUI.State.MinimapOpen.cs +++ b/Zennysoft.Game.Ma.Implementation/UI/InGameUI/state/States/InGameUI.State.MinimapOpen.cs @@ -7,7 +7,7 @@ public partial class InGameUILogic public partial record State { [Meta] - public partial record MinimapOpen : Active, IGet + public partial record MinimapOpen : State, IGet { public MinimapOpen() { diff --git a/Zennysoft.Game.Ma/project.godot b/Zennysoft.Game.Ma/project.godot index 184f1ac4..38b90f89 100644 --- a/Zennysoft.Game.Ma/project.godot +++ b/Zennysoft.Game.Ma/project.godot @@ -29,8 +29,8 @@ general/balloon_path="res://src/ui/dialogue/Balloon.tscn" [display] -window/size/viewport_width=1280 -window/size/viewport_height=960 +window/size/viewport_width=1920 +window/size/viewport_height=1080 [dotnet] diff --git a/Zennysoft.Game.Ma/src/audio/InGameAudio.cs b/Zennysoft.Game.Ma/src/audio/InGameAudio.cs index 8db09312..fdee1386 100644 --- a/Zennysoft.Game.Ma/src/audio/InGameAudio.cs +++ b/Zennysoft.Game.Ma/src/audio/InGameAudio.cs @@ -57,86 +57,86 @@ public partial class InGameAudio : Node public void Setup() { - InGameAudioLogic = new InGameAudioLogic(); + InGameAudioLogic = new InGameAudioLogic(); } public void OnResolved() { - InGameAudioLogic.Set(AppRepo); - InGameAudioLogic.Set(GameEventDepot); - InGameAudioLogic.Set(Player); - InGameAudioLogic.Set(GameRepo); + InGameAudioLogic.Set(AppRepo); + InGameAudioLogic.Set(GameEventDepot); + InGameAudioLogic.Set(Player); + InGameAudioLogic.Set(GameRepo); - InGameAudioBinding = InGameAudioLogic.Bind(); + InGameAudioBinding = InGameAudioLogic.Bind(); - InGameAudioBinding - .Handle((in InGameAudioLogic.Output.PlayOverworldMusic _) => StartOverworldMusic()) - .Handle((in InGameAudioLogic.Output.PlayDungeonThemeAMusic _) => StartDungeonThemeA()) - .Handle((in InGameAudioLogic.Output.PlayMenuScrollSound _) => PlayMenuScrollSound()) - .Handle((in InGameAudioLogic.Output.PlayEquipSound _) => PlayEquipSound()) - .Handle((in InGameAudioLogic.Output.PlayMenuBackSound _) => PlayMenuBackSound()) - .Handle((in InGameAudioLogic.Output.PlayInventorySortedSound _) => PlayInventorySortedSound()) - .Handle((in InGameAudioLogic.Output.PlayHealingItemSound _) => PlayHealingItemSound()) - .Handle((in InGameAudioLogic.Output.PlayTeleportSound _) => PlayTeleportSound()) - .Handle((in InGameAudioLogic.Output.PlayPlayerAttackSound _) => { PlayerAttackSFX.Stop(); PlayerAttackSFX.Play(); }) - .Handle((in InGameAudioLogic.Output.PlayPlayerAttackWallSound _) => { PlayerAttackWallSFX.Stop(); PlayerAttackWallSFX.Play(); }) - .Handle((in InGameAudioLogic.Output.PlayPlayerAttackEnemySound _) => { PlayerAttackEnemySFX.Stop(); PlayerAttackEnemySFX.Play(); }); + InGameAudioBinding + .Handle((in InGameAudioLogic.Output.PlayOverworldMusic _) => StartOverworldMusic()) + .Handle((in InGameAudioLogic.Output.PlayDungeonThemeAMusic _) => StartDungeonThemeA()) + .Handle((in InGameAudioLogic.Output.PlayMenuScrollSound _) => PlayMenuScrollSound()) + .Handle((in InGameAudioLogic.Output.PlayEquipSound _) => PlayEquipSound()) + .Handle((in InGameAudioLogic.Output.PlayMenuBackSound _) => PlayMenuBackSound()) + .Handle((in InGameAudioLogic.Output.PlayInventorySortedSound _) => PlayInventorySortedSound()) + .Handle((in InGameAudioLogic.Output.PlayHealingItemSound _) => PlayHealingItemSound()) + .Handle((in InGameAudioLogic.Output.PlayTeleportSound _) => PlayTeleportSound()) + .Handle((in InGameAudioLogic.Output.PlayPlayerAttackSound _) => { PlayerAttackSFX.Stop(); PlayerAttackSFX.Play(); }) + .Handle((in InGameAudioLogic.Output.PlayPlayerAttackWallSound _) => { PlayerAttackWallSFX.Stop(); PlayerAttackWallSFX.Play(); }) + .Handle((in InGameAudioLogic.Output.PlayPlayerAttackEnemySound _) => { PlayerAttackEnemySFX.Stop(); PlayerAttackEnemySFX.Play(); }); - InGameAudioLogic.Start(); + InGameAudioLogic.Start(); } public void OnExitTree() { - InGameAudioLogic.Stop(); - InGameAudioBinding.Dispose(); + InGameAudioLogic.Stop(); + InGameAudioBinding.Dispose(); } private void StartOverworldMusic() { - OverworldBgm.Stop(); - OverworldBgm.FadeIn(); + OverworldBgm.Stop(); + OverworldBgm.FadeIn(); } private void StartDungeonThemeA() { - OverworldBgm.FadeOut(); - DungeonThemeABgm.Stop(); - DungeonThemeABgm.FadeIn(); + OverworldBgm.FadeOut(); + DungeonThemeABgm.Stop(); + DungeonThemeABgm.FadeIn(); } private void PlayMenuScrollSound() { - MenuScrollSFX.Stop(); - MenuScrollSFX.Play(); + MenuScrollSFX.Stop(); + MenuScrollSFX.Play(); } private void PlayEquipSound() { - EquipSFX.Stop(); - EquipSFX.Play(); + EquipSFX.Stop(); + EquipSFX.Play(); } private void PlayMenuBackSound() { - MenuBackSFX.Stop(); - MenuBackSFX.Play(); + MenuBackSFX.Stop(); + MenuBackSFX.Play(); } private void PlayInventorySortedSound() { - InventorySortedSFX.Stop(); - InventorySortedSFX.Play(); + InventorySortedSFX.Stop(); + InventorySortedSFX.Play(); } private void PlayHealingItemSound() { - HealingItemSFX.Stop(); - HealingItemSFX.Play(); + HealingItemSFX.Stop(); + HealingItemSFX.Play(); } private void PlayTeleportSound() { - TeleportSFX.Stop(); - TeleportSFX.Play(); + TeleportSFX.Stop(); + TeleportSFX.Play(); } } diff --git a/Zennysoft.Game.Ma/src/audio/InGameAudio.tscn b/Zennysoft.Game.Ma/src/audio/InGameAudio.tscn index 394afc34..797300ce 100644 --- a/Zennysoft.Game.Ma/src/audio/InGameAudio.tscn +++ b/Zennysoft.Game.Ma/src/audio/InGameAudio.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=15 format=3 uid="uid://b16ejcwanod72"] +[gd_scene load_steps=16 format=3 uid="uid://b16ejcwanod72"] [ext_resource type="Script" uid="uid://2mnouyn1jcqs" path="res://src/audio/InGameAudio.cs" id="1_gpmcr"] [ext_resource type="AudioStream" uid="uid://dfu0fksb6slhx" path="res://src/audio/music/droney.mp3" id="2_8hfyr"] @@ -11,6 +11,7 @@ [ext_resource type="AudioStream" uid="uid://d1mlduwauechv" path="res://src/audio/sfx/PlayerAttackSFX.wav" id="7_wtvpb"] [ext_resource type="AudioStream" uid="uid://r1tryiit38i8" path="res://src/audio/sfx/MenuBackSFX.ogg" id="8_1xcgo"] [ext_resource type="AudioStream" uid="uid://bjj61s8q2gwb8" path="res://src/audio/sfx/EquipSFX.ogg" id="8_kwybb"] +[ext_resource type="AudioStream" uid="uid://4u8f1tpgs08b" path="res://src/audio/sfx/PlayerHitEnemySFX.wav" id="9_hertr"] [ext_resource type="AudioStream" uid="uid://myx4s8lmarc2" path="res://src/audio/sfx/HealSFX.ogg" id="10_3lcw5"] [ext_resource type="AudioStream" uid="uid://dci08kmwsu6k1" path="res://src/audio/sfx/ExitSFX.ogg" id="11_offhc"] [ext_resource type="AudioStream" uid="uid://d3sn7c614uj2n" path="res://src/audio/sfx/SortSFX.ogg" id="12_wprjr"] @@ -70,6 +71,11 @@ unique_name_in_owner = true stream = ExtResource("7_8vh2f") volume_db = -5.0 +[node name="PlayerAttackEnemySFX" type="AudioStreamPlayer" parent="SFX"] +unique_name_in_owner = true +stream = ExtResource("9_hertr") +volume_db = -5.0 + [node name="MenuScrollSFX" type="AudioStreamPlayer" parent="SFX"] unique_name_in_owner = true stream = ExtResource("7_777nl") diff --git a/Zennysoft.Game.Ma/src/audio/sfx/PlayerHitEnemySFX.wav b/Zennysoft.Game.Ma/src/audio/sfx/PlayerHitEnemySFX.wav new file mode 100644 index 00000000..d4d1e3a6 Binary files /dev/null and b/Zennysoft.Game.Ma/src/audio/sfx/PlayerHitEnemySFX.wav differ diff --git a/Zennysoft.Game.Ma/src/audio/sfx/PlayerHitEnemySFX.wav.import b/Zennysoft.Game.Ma/src/audio/sfx/PlayerHitEnemySFX.wav.import new file mode 100644 index 00000000..a6940cb1 --- /dev/null +++ b/Zennysoft.Game.Ma/src/audio/sfx/PlayerHitEnemySFX.wav.import @@ -0,0 +1,24 @@ +[remap] + +importer="wav" +type="AudioStreamWAV" +uid="uid://4u8f1tpgs08b" +path="res://.godot/imported/PlayerHitEnemySFX.wav-7235df5e0b579a1c2793d138bb462425.sample" + +[deps] + +source_file="res://src/audio/sfx/PlayerHitEnemySFX.wav" +dest_files=["res://.godot/imported/PlayerHitEnemySFX.wav-7235df5e0b579a1c2793d138bb462425.sample"] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop_mode=0 +edit/loop_begin=0 +edit/loop_end=-1 +compress/mode=2 diff --git a/Zennysoft.Game.Ma/src/game/Game.cs b/Zennysoft.Game.Ma/src/game/Game.cs index 4d730a40..68dc8120 100644 --- a/Zennysoft.Game.Ma/src/game/Game.cs +++ b/Zennysoft.Game.Ma/src/game/Game.cs @@ -320,8 +320,12 @@ public partial class Game : Node3D, IGame Player.HealVT(throwableItem.HealVTAmount); } - await ToSignal(GetTree().CreateTimer(1f), "timeout"); - GameRepo.RemoveItemFromInventory(item); + await ToSignal(GetTree().CreateTimer(0.3f), "timeout"); + + if (item is IStackable stackableItem && stackableItem.Count > 1) + stackableItem.SetCount(stackableItem.Count - 1); + else + GameRepo.RemoveItemFromInventory(item); } public void DropItem(InventoryItem item) diff --git a/Zennysoft.Game.Ma/src/inventory_menu/InventoryLabelSettings.tres b/Zennysoft.Game.Ma/src/inventory_menu/InventoryLabelSettings.tres index 7ff5f0e9..35ce0fd4 100644 --- a/Zennysoft.Game.Ma/src/inventory_menu/InventoryLabelSettings.tres +++ b/Zennysoft.Game.Ma/src/inventory_menu/InventoryLabelSettings.tres @@ -1,6 +1,7 @@ [gd_resource type="LabelSettings" load_steps=2 format=3 uid="uid://bl5xpqyq8vjtv"] -[sub_resource type="SystemFont" id="SystemFont_1ibjc"] +[ext_resource type="FontFile" uid="uid://dit3vylt7hmmx" path="res://src/ui/fonts/FT88-Regular.ttf" id="1_1lnq2"] [resource] -font = SubResource("SystemFont_1ibjc") +font = ExtResource("1_1lnq2") +font_size = 30 diff --git a/Zennysoft.Game.Ma/src/inventory_menu/ItemSlot.cs b/Zennysoft.Game.Ma/src/inventory_menu/ItemSlot.cs index 941c6b7d..16d24680 100644 --- a/Zennysoft.Game.Ma/src/inventory_menu/ItemSlot.cs +++ b/Zennysoft.Game.Ma/src/inventory_menu/ItemSlot.cs @@ -2,6 +2,7 @@ using Chickensoft.AutoInject; using Chickensoft.GodotNodeInterfaces; using Chickensoft.Introspection; using Godot; +using Zennysoft.Game.Abstractions; using Zennysoft.Ma.Adapter; namespace Zennysoft.Game.Ma; @@ -32,6 +33,8 @@ public partial class ItemSlot : HBoxContainer, IItemSlot [Node] public Label ItemName { get; set; } = default!; + [Node] public Label ItemCount { get; set; } = default!; + private static LabelSettings ItemFont => GD.Load("res://src/ui/label_settings/MainTextRegular.tres"); private static LabelSettings SelectedItemFont => GD.Load("res://src/ui/label_settings/MainTextFontItalicized.tres"); private static LabelSettings EquippedItemFont => GD.Load("res://src/ui/label_settings/MainTextFontEquipped.tres"); @@ -45,6 +48,12 @@ public partial class ItemSlot : HBoxContainer, IItemSlot Player.EquippedWeapon.Sync += EquipableItem_Sync; Player.EquippedArmor.Sync += EquipableItem_Sync; Player.EquippedAccessory.Sync += EquipableItem_Sync; + + if (Item is IStackable stackableItem) + { + ItemCount.Text = $"{stackableItem.Count:D2}"; + ItemCount.Visible = true; + } } private void EquipableItem_Sync(EquipableItem obj) diff --git a/Zennysoft.Game.Ma/src/inventory_menu/ItemSlot.tscn b/Zennysoft.Game.Ma/src/inventory_menu/ItemSlot.tscn index 68eb14f5..b4a4714c 100644 --- a/Zennysoft.Game.Ma/src/inventory_menu/ItemSlot.tscn +++ b/Zennysoft.Game.Ma/src/inventory_menu/ItemSlot.tscn @@ -1,9 +1,10 @@ -[gd_scene load_steps=6 format=3 uid="uid://c005nd0m2eim"] +[gd_scene load_steps=7 format=3 uid="uid://c005nd0m2eim"] -[ext_resource type="Script" path="res://src/inventory_menu/ItemSlot.cs" id="1_yttxt"] +[ext_resource type="Script" uid="uid://cglxk7v8hpesn" path="res://src/inventory_menu/ItemSlot.cs" id="1_yttxt"] [ext_resource type="Texture2D" uid="uid://0r1dws4ajhdx" path="res://src/items/accessory/textures/MASK 01.PNG" id="2_7kdbd"] -[ext_resource type="Script" path="res://src/inventory_menu/ItemLabel.cs" id="3_xlgl0"] +[ext_resource type="Script" uid="uid://b0rrpkpsfdga8" path="res://src/inventory_menu/ItemLabel.cs" id="3_xlgl0"] [ext_resource type="FontFile" uid="uid://bohbd123672ea" path="res://src/ui/fonts/FT88-Italic.ttf" id="4_vcxwm"] +[ext_resource type="LabelSettings" uid="uid://bl5xpqyq8vjtv" path="res://src/inventory_menu/InventoryLabelSettings.tres" id="5_a7hko"] [sub_resource type="LabelSettings" id="LabelSettings_lgjx0"] font = ExtResource("4_vcxwm") @@ -39,3 +40,10 @@ label_settings = SubResource("LabelSettings_lgjx0") vertical_alignment = 1 autowrap_mode = 2 script = ExtResource("3_xlgl0") + +[node name="ItemCount" type="Label" parent="."] +unique_name_in_owner = true +visible = false +layout_mode = 2 +text = "x99" +label_settings = ExtResource("5_a7hko") diff --git a/Zennysoft.Game.Ma/src/items/Inventory.cs b/Zennysoft.Game.Ma/src/items/Inventory.cs index 90ebd58b..ededc0be 100644 --- a/Zennysoft.Game.Ma/src/items/Inventory.cs +++ b/Zennysoft.Game.Ma/src/items/Inventory.cs @@ -4,6 +4,7 @@ using Chickensoft.Serialization; using Godot; using System.Collections.Generic; using System.Linq; +using Zennysoft.Game.Abstractions; using Zennysoft.Ma.Adapter; namespace Zennysoft.Game.Ma; @@ -52,6 +53,16 @@ public partial class Inventory : Node, IInventory var consumables = listToSort.Where(x => x is ConsumableItem).OrderBy(x => x as ConsumableItem, new ConsumableComparer()); var throwables = listToSort.Where(x => x is ThrowableItem).OrderBy(x => x as ThrowableItem, new ThrowableComparer()); Items = [.. equippedItems, .. weapons, .. armor, .. accessories, .. consumables, .. throwables]; + + var stackableItems = Items.OfType(); + var itemsToStack = stackableItems.GroupBy(x => ((InventoryItem)x).ItemName).Where(x => x.Count() > 1); + foreach (var itemStack in itemsToStack) + { + var firstItem = itemStack.First(); + firstItem.SetCount(itemStack.Count()); + var itemsToRemove = itemStack.Except([firstItem]).Cast(); + Items = [.. Items.Except(itemsToRemove)]; + } } public class WeaponComparer : IComparer diff --git a/Zennysoft.Game.Ma/src/items/effect/EffectItem.cs b/Zennysoft.Game.Ma/src/items/effect/EffectItem.cs index 04dabe24..932cbee8 100644 --- a/Zennysoft.Game.Ma/src/items/effect/EffectItem.cs +++ b/Zennysoft.Game.Ma/src/items/effect/EffectItem.cs @@ -35,5 +35,6 @@ public partial class EffectItem : InventoryItem [Export] [Save("effect_item_stats")] public EffectItemStats Stats { get; set; } = new EffectItemStats(); + public override Texture2D GetTexture() => Stats.Texture; } diff --git a/Zennysoft.Game.Ma/src/items/throwable/ThrowableItem.cs b/Zennysoft.Game.Ma/src/items/throwable/ThrowableItem.cs index 82e7c8e6..3fed3443 100644 --- a/Zennysoft.Game.Ma/src/items/throwable/ThrowableItem.cs +++ b/Zennysoft.Game.Ma/src/items/throwable/ThrowableItem.cs @@ -2,12 +2,13 @@ using Chickensoft.AutoInject; using Chickensoft.Introspection; using Chickensoft.Serialization; using Godot; +using Zennysoft.Game.Abstractions; using Zennysoft.Ma.Adapter; namespace Zennysoft.Game.Ma; [Meta(typeof(IAutoNode)), Id("throwable_item")] -public partial class ThrowableItem : InventoryItem +public partial class ThrowableItem : InventoryItem, IStackable { public override void _Notification(int what) => this.Notify(what); @@ -40,10 +41,14 @@ public partial class ThrowableItem : InventoryItem public void SetDescription(string description) => Stats.Description = description; - public int Count { get; } + [Save("throwable_item_count")] + public int Count { get; private set; } = 1; [Export] [Save("throwable_item_stats")] public ThrowableItemStats Stats { get; set; } + public override Texture2D GetTexture() => Stats.Texture; + + public void SetCount(int count) => Count = count; } diff --git a/Zennysoft.Game.Ma/src/map/dungeon/code/Floor0.cs b/Zennysoft.Game.Ma/src/map/dungeon/code/Floor0.cs index 187f0ea1..b8e19fc5 100644 --- a/Zennysoft.Game.Ma/src/map/dungeon/code/Floor0.cs +++ b/Zennysoft.Game.Ma/src/map/dungeon/code/Floor0.cs @@ -1,4 +1,4 @@ -using Chickensoft.AutoInject; +using Chickensoft.AutoInject; using Chickensoft.Introspection; using Godot; using System.Collections.Immutable; @@ -22,9 +22,9 @@ public partial class Floor0 : Node3D, IDungeonFloor public override void _Ready() { - Show(); - Exit.AreaEntered += Exit_AreaEntered; - FloorIsLoaded = true; + Show(); + Exit.AreaEntered += Exit_AreaEntered; + FloorIsLoaded = true; } private void Exit_AreaEntered(Area3D area) => ExitReached(); diff --git a/Zennysoft.Game.Ma/src/map/dungeon/floors/Floor00.tscn b/Zennysoft.Game.Ma/src/map/dungeon/floors/Floor00.tscn index d9d2e249..3c87e650 100644 --- a/Zennysoft.Game.Ma/src/map/dungeon/floors/Floor00.tscn +++ b/Zennysoft.Game.Ma/src/map/dungeon/floors/Floor00.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=63 format=4 uid="uid://dl6h1djc27ddl"] +[gd_scene load_steps=64 format=4 uid="uid://dl6h1djc27ddl"] [ext_resource type="Script" uid="uid://c1nhqlem1ew3m" path="res://src/map/dungeon/code/Floor0.cs" id="1_db2o3"] [ext_resource type="Texture2D" uid="uid://b27ksiyfefb33" path="res://src/map/dungeon/models/Set A/02. Altar/02_ALTAR_FLOOR_ZER0_VER_outside_desert.png" id="2_xh2ej"] @@ -17,6 +17,7 @@ [ext_resource type="Texture2D" uid="uid://cururtxtgylxf" path="res://src/map/dungeon/models/Set A/02. Altar/02_ALTAR_FLOOR_ZER0_VER_COLUMN.jpg" id="15_ojbcg"] [ext_resource type="PackedScene" uid="uid://1fl6s352e2ej" path="res://src/items/throwable/ThrowableItem.tscn" id="16_db2o3"] [ext_resource type="Resource" uid="uid://qqg0gdcb8fwg" path="res://src/items/throwable/resources/SpellSignKnowledge.tres" id="17_ntxe5"] +[ext_resource type="Resource" uid="uid://bph8c6by4s047" path="res://src/items/throwable/resources/GeomanticDice.tres" id="18_ntxe5"] [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_3ubi4"] shading_mode = 0 @@ -902,3 +903,19 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -9.00384, 1.80761, 11.3571) [node name="ThrowableItem" parent="." instance=ExtResource("16_db2o3")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3.78811, -3.32789, 0) Stats = ExtResource("17_ntxe5") + +[node name="ThrowableItem2" parent="." instance=ExtResource("16_db2o3")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3.78811, -3.32789, 0) +Stats = ExtResource("17_ntxe5") + +[node name="ThrowableItem3" parent="." instance=ExtResource("16_db2o3")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3.78811, -3.32789, 0) +Stats = ExtResource("17_ntxe5") + +[node name="ThrowableItem4" parent="." instance=ExtResource("16_db2o3")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3.78811, -3.32789, 0) +Stats = ExtResource("17_ntxe5") + +[node name="DifferentThrowable" parent="." instance=ExtResource("16_db2o3")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -4.2442, -2.60258, -2.96088) +Stats = ExtResource("18_ntxe5")