Add stackable items

This commit is contained in:
2025-03-11 16:00:46 -07:00
parent 192f2eb316
commit f060229b5e
17 changed files with 151 additions and 57 deletions

View File

@@ -0,0 +1,8 @@
namespace Zennysoft.Game.Abstractions;
public interface IStackable
{
int Count { get; }
void SetCount(int count);
}

View File

@@ -7,7 +7,7 @@ public partial class InGameUILogic
public partial record State public partial record State
{ {
[Meta] [Meta]
public partial record InventoryOpen : Active, IGet<Input.HideInventory> public partial record InventoryOpen : State, IGet<Input.HideInventory>
{ {
public InventoryOpen() public InventoryOpen()
{ {

View File

@@ -7,7 +7,7 @@ public partial class InGameUILogic
public partial record State public partial record State
{ {
[Meta] [Meta]
public partial record MinimapOpen : Active, IGet<Input.HideMinimap> public partial record MinimapOpen : State, IGet<Input.HideMinimap>
{ {
public MinimapOpen() public MinimapOpen()
{ {

View File

@@ -29,8 +29,8 @@ general/balloon_path="res://src/ui/dialogue/Balloon.tscn"
[display] [display]
window/size/viewport_width=1280 window/size/viewport_width=1920
window/size/viewport_height=960 window/size/viewport_height=1080
[dotnet] [dotnet]

View File

@@ -57,86 +57,86 @@ public partial class InGameAudio : Node
public void Setup() public void Setup()
{ {
InGameAudioLogic = new InGameAudioLogic(); InGameAudioLogic = new InGameAudioLogic();
} }
public void OnResolved() public void OnResolved()
{ {
InGameAudioLogic.Set(AppRepo); InGameAudioLogic.Set(AppRepo);
InGameAudioLogic.Set(GameEventDepot); InGameAudioLogic.Set(GameEventDepot);
InGameAudioLogic.Set(Player); InGameAudioLogic.Set(Player);
InGameAudioLogic.Set(GameRepo); InGameAudioLogic.Set(GameRepo);
InGameAudioBinding = InGameAudioLogic.Bind(); InGameAudioBinding = InGameAudioLogic.Bind();
InGameAudioBinding InGameAudioBinding
.Handle((in InGameAudioLogic.Output.PlayOverworldMusic _) => StartOverworldMusic()) .Handle((in InGameAudioLogic.Output.PlayOverworldMusic _) => StartOverworldMusic())
.Handle((in InGameAudioLogic.Output.PlayDungeonThemeAMusic _) => StartDungeonThemeA()) .Handle((in InGameAudioLogic.Output.PlayDungeonThemeAMusic _) => StartDungeonThemeA())
.Handle((in InGameAudioLogic.Output.PlayMenuScrollSound _) => PlayMenuScrollSound()) .Handle((in InGameAudioLogic.Output.PlayMenuScrollSound _) => PlayMenuScrollSound())
.Handle((in InGameAudioLogic.Output.PlayEquipSound _) => PlayEquipSound()) .Handle((in InGameAudioLogic.Output.PlayEquipSound _) => PlayEquipSound())
.Handle((in InGameAudioLogic.Output.PlayMenuBackSound _) => PlayMenuBackSound()) .Handle((in InGameAudioLogic.Output.PlayMenuBackSound _) => PlayMenuBackSound())
.Handle((in InGameAudioLogic.Output.PlayInventorySortedSound _) => PlayInventorySortedSound()) .Handle((in InGameAudioLogic.Output.PlayInventorySortedSound _) => PlayInventorySortedSound())
.Handle((in InGameAudioLogic.Output.PlayHealingItemSound _) => PlayHealingItemSound()) .Handle((in InGameAudioLogic.Output.PlayHealingItemSound _) => PlayHealingItemSound())
.Handle((in InGameAudioLogic.Output.PlayTeleportSound _) => PlayTeleportSound()) .Handle((in InGameAudioLogic.Output.PlayTeleportSound _) => PlayTeleportSound())
.Handle((in InGameAudioLogic.Output.PlayPlayerAttackSound _) => { PlayerAttackSFX.Stop(); PlayerAttackSFX.Play(); }) .Handle((in InGameAudioLogic.Output.PlayPlayerAttackSound _) => { PlayerAttackSFX.Stop(); PlayerAttackSFX.Play(); })
.Handle((in InGameAudioLogic.Output.PlayPlayerAttackWallSound _) => { PlayerAttackWallSFX.Stop(); PlayerAttackWallSFX.Play(); }) .Handle((in InGameAudioLogic.Output.PlayPlayerAttackWallSound _) => { PlayerAttackWallSFX.Stop(); PlayerAttackWallSFX.Play(); })
.Handle((in InGameAudioLogic.Output.PlayPlayerAttackEnemySound _) => { PlayerAttackEnemySFX.Stop(); PlayerAttackEnemySFX.Play(); }); .Handle((in InGameAudioLogic.Output.PlayPlayerAttackEnemySound _) => { PlayerAttackEnemySFX.Stop(); PlayerAttackEnemySFX.Play(); });
InGameAudioLogic.Start(); InGameAudioLogic.Start();
} }
public void OnExitTree() public void OnExitTree()
{ {
InGameAudioLogic.Stop(); InGameAudioLogic.Stop();
InGameAudioBinding.Dispose(); InGameAudioBinding.Dispose();
} }
private void StartOverworldMusic() private void StartOverworldMusic()
{ {
OverworldBgm.Stop(); OverworldBgm.Stop();
OverworldBgm.FadeIn(); OverworldBgm.FadeIn();
} }
private void StartDungeonThemeA() private void StartDungeonThemeA()
{ {
OverworldBgm.FadeOut(); OverworldBgm.FadeOut();
DungeonThemeABgm.Stop(); DungeonThemeABgm.Stop();
DungeonThemeABgm.FadeIn(); DungeonThemeABgm.FadeIn();
} }
private void PlayMenuScrollSound() private void PlayMenuScrollSound()
{ {
MenuScrollSFX.Stop(); MenuScrollSFX.Stop();
MenuScrollSFX.Play(); MenuScrollSFX.Play();
} }
private void PlayEquipSound() private void PlayEquipSound()
{ {
EquipSFX.Stop(); EquipSFX.Stop();
EquipSFX.Play(); EquipSFX.Play();
} }
private void PlayMenuBackSound() private void PlayMenuBackSound()
{ {
MenuBackSFX.Stop(); MenuBackSFX.Stop();
MenuBackSFX.Play(); MenuBackSFX.Play();
} }
private void PlayInventorySortedSound() private void PlayInventorySortedSound()
{ {
InventorySortedSFX.Stop(); InventorySortedSFX.Stop();
InventorySortedSFX.Play(); InventorySortedSFX.Play();
} }
private void PlayHealingItemSound() private void PlayHealingItemSound()
{ {
HealingItemSFX.Stop(); HealingItemSFX.Stop();
HealingItemSFX.Play(); HealingItemSFX.Play();
} }
private void PlayTeleportSound() private void PlayTeleportSound()
{ {
TeleportSFX.Stop(); TeleportSFX.Stop();
TeleportSFX.Play(); TeleportSFX.Play();
} }
} }

View File

@@ -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="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"] [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://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://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://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://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://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"] [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") stream = ExtResource("7_8vh2f")
volume_db = -5.0 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"] [node name="MenuScrollSFX" type="AudioStreamPlayer" parent="SFX"]
unique_name_in_owner = true unique_name_in_owner = true
stream = ExtResource("7_777nl") stream = ExtResource("7_777nl")

Binary file not shown.

View File

@@ -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

View File

@@ -320,8 +320,12 @@ public partial class Game : Node3D, IGame
Player.HealVT(throwableItem.HealVTAmount); Player.HealVT(throwableItem.HealVTAmount);
} }
await ToSignal(GetTree().CreateTimer(1f), "timeout"); await ToSignal(GetTree().CreateTimer(0.3f), "timeout");
GameRepo.RemoveItemFromInventory(item);
if (item is IStackable stackableItem && stackableItem.Count > 1)
stackableItem.SetCount(stackableItem.Count - 1);
else
GameRepo.RemoveItemFromInventory(item);
} }
public void DropItem(InventoryItem item) public void DropItem(InventoryItem item)

View File

@@ -1,6 +1,7 @@
[gd_resource type="LabelSettings" load_steps=2 format=3 uid="uid://bl5xpqyq8vjtv"] [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] [resource]
font = SubResource("SystemFont_1ibjc") font = ExtResource("1_1lnq2")
font_size = 30

View File

@@ -2,6 +2,7 @@ using Chickensoft.AutoInject;
using Chickensoft.GodotNodeInterfaces; using Chickensoft.GodotNodeInterfaces;
using Chickensoft.Introspection; using Chickensoft.Introspection;
using Godot; using Godot;
using Zennysoft.Game.Abstractions;
using Zennysoft.Ma.Adapter; using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma; namespace Zennysoft.Game.Ma;
@@ -32,6 +33,8 @@ public partial class ItemSlot : HBoxContainer, IItemSlot
[Node] public Label ItemName { get; set; } = default!; [Node] public Label ItemName { get; set; } = default!;
[Node] public Label ItemCount { get; set; } = default!;
private static LabelSettings ItemFont => GD.Load<LabelSettings>("res://src/ui/label_settings/MainTextRegular.tres"); private static LabelSettings ItemFont => GD.Load<LabelSettings>("res://src/ui/label_settings/MainTextRegular.tres");
private static LabelSettings SelectedItemFont => GD.Load<LabelSettings>("res://src/ui/label_settings/MainTextFontItalicized.tres"); private static LabelSettings SelectedItemFont => GD.Load<LabelSettings>("res://src/ui/label_settings/MainTextFontItalicized.tres");
private static LabelSettings EquippedItemFont => GD.Load<LabelSettings>("res://src/ui/label_settings/MainTextFontEquipped.tres"); private static LabelSettings EquippedItemFont => GD.Load<LabelSettings>("res://src/ui/label_settings/MainTextFontEquipped.tres");
@@ -45,6 +48,12 @@ public partial class ItemSlot : HBoxContainer, IItemSlot
Player.EquippedWeapon.Sync += EquipableItem_Sync; Player.EquippedWeapon.Sync += EquipableItem_Sync;
Player.EquippedArmor.Sync += EquipableItem_Sync; Player.EquippedArmor.Sync += EquipableItem_Sync;
Player.EquippedAccessory.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) private void EquipableItem_Sync(EquipableItem obj)

View File

@@ -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="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="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"] [sub_resource type="LabelSettings" id="LabelSettings_lgjx0"]
font = ExtResource("4_vcxwm") font = ExtResource("4_vcxwm")
@@ -39,3 +40,10 @@ label_settings = SubResource("LabelSettings_lgjx0")
vertical_alignment = 1 vertical_alignment = 1
autowrap_mode = 2 autowrap_mode = 2
script = ExtResource("3_xlgl0") 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")

View File

@@ -4,6 +4,7 @@ using Chickensoft.Serialization;
using Godot; using Godot;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Zennysoft.Game.Abstractions;
using Zennysoft.Ma.Adapter; using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma; 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 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()); var throwables = listToSort.Where(x => x is ThrowableItem).OrderBy(x => x as ThrowableItem, new ThrowableComparer());
Items = [.. equippedItems, .. weapons, .. armor, .. accessories, .. consumables, .. throwables]; Items = [.. equippedItems, .. weapons, .. armor, .. accessories, .. consumables, .. throwables];
var stackableItems = Items.OfType<IStackable>();
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<InventoryItem>();
Items = [.. Items.Except(itemsToRemove)];
}
} }
public class WeaponComparer : IComparer<Weapon> public class WeaponComparer : IComparer<Weapon>

View File

@@ -35,5 +35,6 @@ public partial class EffectItem : InventoryItem
[Export] [Export]
[Save("effect_item_stats")] [Save("effect_item_stats")]
public EffectItemStats Stats { get; set; } = new EffectItemStats(); public EffectItemStats Stats { get; set; } = new EffectItemStats();
public override Texture2D GetTexture() => Stats.Texture; public override Texture2D GetTexture() => Stats.Texture;
} }

View File

@@ -2,12 +2,13 @@ using Chickensoft.AutoInject;
using Chickensoft.Introspection; using Chickensoft.Introspection;
using Chickensoft.Serialization; using Chickensoft.Serialization;
using Godot; using Godot;
using Zennysoft.Game.Abstractions;
using Zennysoft.Ma.Adapter; using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma; namespace Zennysoft.Game.Ma;
[Meta(typeof(IAutoNode)), Id("throwable_item")] [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); 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 void SetDescription(string description) => Stats.Description = description;
public int Count { get; } [Save("throwable_item_count")]
public int Count { get; private set; } = 1;
[Export] [Export]
[Save("throwable_item_stats")] [Save("throwable_item_stats")]
public ThrowableItemStats Stats { get; set; } public ThrowableItemStats Stats { get; set; }
public override Texture2D GetTexture() => Stats.Texture; public override Texture2D GetTexture() => Stats.Texture;
public void SetCount(int count) => Count = count;
} }

View File

@@ -1,4 +1,4 @@
using Chickensoft.AutoInject; using Chickensoft.AutoInject;
using Chickensoft.Introspection; using Chickensoft.Introspection;
using Godot; using Godot;
using System.Collections.Immutable; using System.Collections.Immutable;
@@ -22,9 +22,9 @@ public partial class Floor0 : Node3D, IDungeonFloor
public override void _Ready() public override void _Ready()
{ {
Show(); Show();
Exit.AreaEntered += Exit_AreaEntered; Exit.AreaEntered += Exit_AreaEntered;
FloorIsLoaded = true; FloorIsLoaded = true;
} }
private void Exit_AreaEntered(Area3D area) => ExitReached(); private void Exit_AreaEntered(Area3D area) => ExitReached();

View File

@@ -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="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"] [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="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="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://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"] [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_3ubi4"]
shading_mode = 0 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")] [node name="ThrowableItem" parent="." instance=ExtResource("16_db2o3")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3.78811, -3.32789, 0) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3.78811, -3.32789, 0)
Stats = ExtResource("17_ntxe5") 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")