Split equipment status from Inventory, fix equip/unequip bonuses being reflected correctly in code and inventory menu

This commit is contained in:
2025-02-19 16:04:40 -08:00
parent f71412d946
commit 5404f8e7b0
16 changed files with 200 additions and 170 deletions

View File

@@ -18,9 +18,9 @@ public partial class InGameAudioLogic
gameEventDepot.DungeonAThemeAreaEntered += OnDungeonAThemeEntered; gameEventDepot.DungeonAThemeAreaEntered += OnDungeonAThemeEntered;
gameEventDepot.MenuScrolled += OnMenuScrolled; gameEventDepot.MenuScrolled += OnMenuScrolled;
gameEventDepot.MenuBackedOut += OnMenuBackedOut; gameEventDepot.MenuBackedOut += OnMenuBackedOut;
player.Inventory.EquippedWeapon.Changed += OnEquippedItem; player.EquippedWeapon.Changed += OnEquippedItem;
player.Inventory.EquippedArmor.Changed += OnEquippedItem; player.EquippedArmor.Changed += OnEquippedItem;
player.Inventory.EquippedAccessory.Changed += OnEquippedItem; player.EquippedAccessory.Changed += OnEquippedItem;
gameEventDepot.InventorySorted += OnInventorySorted; gameEventDepot.InventorySorted += OnInventorySorted;
gameEventDepot.HealingItemConsumed += OnHealingItemConsumed; gameEventDepot.HealingItemConsumed += OnHealingItemConsumed;
gameEventDepot.RestorativePickedUp += OnRestorativePickedUp; gameEventDepot.RestorativePickedUp += OnRestorativePickedUp;
@@ -34,9 +34,9 @@ public partial class InGameAudioLogic
gameEventDepot.DungeonAThemeAreaEntered -= OnDungeonAThemeEntered; gameEventDepot.DungeonAThemeAreaEntered -= OnDungeonAThemeEntered;
gameEventDepot.MenuScrolled -= OnMenuScrolled; gameEventDepot.MenuScrolled -= OnMenuScrolled;
gameEventDepot.MenuBackedOut -= OnMenuBackedOut; gameEventDepot.MenuBackedOut -= OnMenuBackedOut;
player.Inventory.EquippedWeapon.Changed -= OnEquippedItem; player.EquippedWeapon.Changed -= OnEquippedItem;
player.Inventory.EquippedArmor.Changed -= OnEquippedItem; player.EquippedArmor.Changed -= OnEquippedItem;
player.Inventory.EquippedAccessory.Changed -= OnEquippedItem; player.EquippedAccessory.Changed -= OnEquippedItem;
gameEventDepot.InventorySorted -= OnInventorySorted; gameEventDepot.InventorySorted -= OnInventorySorted;
gameEventDepot.TeleportEntered -= OnTeleportEntered; gameEventDepot.TeleportEntered -= OnTeleportEntered;
}); });

View File

@@ -307,7 +307,7 @@ tracks/0/loop_wrap = true
tracks/0/keys = { tracks/0/keys = {
"times": PackedFloat32Array(0), "times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1), "transitions": PackedFloat32Array(1),
"update": 0, "update": 1,
"values": [&"idle_back_walk"] "values": [&"idle_back_walk"]
} }
tracks/1/type = "value" tracks/1/type = "value"
@@ -319,7 +319,7 @@ tracks/1/loop_wrap = true
tracks/1/keys = { tracks/1/keys = {
"times": PackedFloat32Array(0, 0.0833333, 0.166667, 0.25, 0.333333, 0.416667, 0.5, 0.583333, 0.666666, 0.75, 0.833333, 0.916666, 1, 1.08333, 1.16667), "times": PackedFloat32Array(0, 0.0833333, 0.166667, 0.25, 0.333333, 0.416667, 0.5, 0.583333, 0.666666, 0.75, 0.833333, 0.916666, 1, 1.08333, 1.16667),
"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1), "transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1),
"update": 0, "update": 1,
"values": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14] "values": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
} }

View File

@@ -223,12 +223,12 @@ public partial class Game : Node3D, IGame
private void FloorClearMenu_TransitionCompleted() private void FloorClearMenu_TransitionCompleted()
{ {
GameRepo.Resume(); GameRepo.Resume();
if (Player.Inventory.EquippedWeapon.Value.ItemTags.Contains(ItemTag.BreaksOnChange)) if (Player.EquippedWeapon.Value.ItemTags.Contains(ItemTag.BreaksOnChange))
Player.Inventory.Unequip(Player.Inventory.EquippedWeapon.Value); Player.Unequip(Player.EquippedWeapon.Value);
if (Player.Inventory.EquippedArmor.Value.ItemTags.Contains(ItemTag.BreaksOnChange)) if (Player.EquippedArmor.Value.ItemTags.Contains(ItemTag.BreaksOnChange))
Player.Inventory.Unequip(Player.Inventory.EquippedArmor.Value); Player.Unequip(Player.EquippedArmor.Value);
if (Player.Inventory.EquippedAccessory.Value.ItemTags.Contains(ItemTag.BreaksOnChange)) if (Player.EquippedAccessory.Value.ItemTags.Contains(ItemTag.BreaksOnChange))
Player.Inventory.Unequip(Player.Inventory.EquippedAccessory.Value); Player.Unequip(Player.EquippedAccessory.Value);
} }
private void FloorClearMenu_GoToNextFloor() private void FloorClearMenu_GoToNextFloor()

View File

@@ -9,7 +9,7 @@ namespace GameJamDungeon;
public interface IInventoryMenu : IControl public interface IInventoryMenu : IControl
{ {
public Task RedrawInventory(); public Task RefreshInventoryScreen();
public Task ShowMessage(string message); public Task ShowMessage(string message);
@@ -21,7 +21,7 @@ public partial class InventoryMenu : Control, IInventoryMenu
{ {
public override void _Notification(int what) => this.Notify(what); public override void _Notification(int what) => this.Notify(what);
[Dependency] public IGameRepo GameRepo => this.DependOn<IGameRepo>(); [Dependency] private IGameRepo _gameRepo => this.DependOn<IGameRepo>();
[Dependency] public IGame Game => this.DependOn<IGame>(); [Dependency] public IGame Game => this.DependOn<IGame>();
@@ -91,9 +91,9 @@ public partial class InventoryMenu : Control, IInventoryMenu
Player.Stats.CurrentExp.Sync += CurrentExp_Sync; Player.Stats.CurrentExp.Sync += CurrentExp_Sync;
Player.Stats.ExpToNextLevel.Sync += ExpToNextLevel_Sync; Player.Stats.ExpToNextLevel.Sync += ExpToNextLevel_Sync;
Player.Stats.CurrentLevel.Sync += CurrentLevel_Sync; Player.Stats.CurrentLevel.Sync += CurrentLevel_Sync;
Player.Inventory.EquippedWeapon.Sync += EquippedWeapon_Sync; Player.EquippedWeapon.Sync += EquippedWeapon_Sync;
Player.Inventory.EquippedArmor.Sync += EquippedArmor_Sync; Player.EquippedArmor.Sync += EquippedArmor_Sync;
Player.Inventory.EquippedAccessory.Sync += EquippedAccessory_Sync; Player.EquippedAccessory.Sync += EquippedAccessory_Sync;
SetProcessInput(false); SetProcessInput(false);
} }
@@ -105,7 +105,7 @@ public partial class InventoryMenu : Control, IInventoryMenu
await ShowInventoryInfo(); await ShowInventoryInfo();
ItemEffectLabel.Text = message; ItemEffectLabel.Text = message;
await ToSignal(GetTree().CreateTimer(1f), "timeout"); await ToSignal(GetTree().CreateTimer(1f), "timeout");
await RedrawInventory(); await RefreshInventoryScreen();
SetProcessInput(true); SetProcessInput(true);
} }
@@ -150,7 +150,7 @@ public partial class InventoryMenu : Control, IInventoryMenu
private void CurrentHP_Sync(int obj) => HPValue.Text = $"{obj}/{Player.Stats.MaximumHP.Value}"; private void CurrentHP_Sync(int obj) => HPValue.Text = $"{obj}/{Player.Stats.MaximumHP.Value}";
public async Task RedrawInventory() public async Task RefreshInventoryScreen()
{ {
await ClearItems(); await ClearItems();
PopulateInventory(); PopulateInventory();
@@ -223,7 +223,7 @@ public partial class InventoryMenu : Control, IInventoryMenu
{ {
inventory.Sort(); inventory.Sort();
GameEventDepot.OnInventorySorted(); GameEventDepot.OnInventorySorted();
RedrawInventory(); RefreshInventoryScreen();
} }
} }
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
@@ -245,7 +245,7 @@ public partial class InventoryMenu : Control, IInventoryMenu
private void PopulatePlayerInfo() private void PopulatePlayerInfo()
{ {
FloorLabel.Text = $"Floor {GameRepo.CurrentFloor:D2}"; FloorLabel.Text = $"Floor {_gameRepo.CurrentFloor:D2}";
if (ItemSlots.Any()) if (ItemSlots.Any())
{ {
@@ -268,12 +268,12 @@ public partial class InventoryMenu : Control, IInventoryMenu
if (currentItem is IEquipableItem equipable) if (currentItem is IEquipableItem equipable)
{ {
var isEquipped = Player.Inventory.IsEquipped(equipable); ;
UseButton.Text = isEquipped ? "Unequip" : "Equip"; UseButton.Text = equipable.IsEquipped ? "Unequip" : "Equip";
ThrowButton.Disabled = isEquipped ? true : false; ThrowButton.Disabled = equipable.IsEquipped ? true : false;
ThrowButton.FocusMode = isEquipped ? FocusModeEnum.None : FocusModeEnum.All; ThrowButton.FocusMode = equipable.IsEquipped ? FocusModeEnum.None : FocusModeEnum.All;
DropButton.Disabled = isEquipped ? true : false; DropButton.Disabled = equipable.IsEquipped ? true : false;
DropButton.FocusMode = isEquipped ? FocusModeEnum.None : FocusModeEnum.All; DropButton.FocusMode = equipable.IsEquipped ? FocusModeEnum.None : FocusModeEnum.All;
} }
else else
{ {
@@ -306,7 +306,7 @@ public partial class InventoryMenu : Control, IInventoryMenu
await ToSignal(GetTree().CreateTimer(0.1f), "timeout"); await ToSignal(GetTree().CreateTimer(0.1f), "timeout");
_currentIndex = 0; _currentIndex = 0;
_currentPageNumber = pageToChangeTo; _currentPageNumber = pageToChangeTo;
await RedrawInventory(); await RefreshInventoryScreen();
GameEventDepot.OnMenuScrolled(); GameEventDepot.OnMenuScrolled();
} }
@@ -339,14 +339,14 @@ public partial class InventoryMenu : Control, IInventoryMenu
itemSlot.Item = item; itemSlot.Item = item;
ItemsPage.AddChildEx(itemSlot); ItemsPage.AddChildEx(itemSlot);
if (itemSlot.Item is IEquipableItem equipable && Player.Inventory.IsEquipped(equipable)) if (itemSlot.Item is IEquipableItem equipable && equipable.IsEquipped)
itemSlot.SetEquippedItemStyle(); itemSlot.SetEquippedItemStyle();
} }
if (ItemSlots.Any()) if (ItemSlots.Any())
{ {
ItemSlots.ElementAt(_currentIndex).SetSelectedItemStyle(); ItemSlots.ElementAt(_currentIndex).SetSelectedItemStyle();
if (ItemSlots.ElementAt(_currentIndex).Item is IEquipableItem equipable && Player.Inventory.IsEquipped(equipable)) if (ItemSlots.ElementAt(_currentIndex).Item is IEquipableItem equipable && equipable.IsEquipped)
ItemSlots.ElementAt(_currentIndex).SetEquippedSelectedItemStyle(); ItemSlots.ElementAt(_currentIndex).SetEquippedSelectedItemStyle();
} }
} }
@@ -355,7 +355,7 @@ public partial class InventoryMenu : Control, IInventoryMenu
{ {
await ToSignal(GetTree().CreateTimer(0.1f), "timeout"); await ToSignal(GetTree().CreateTimer(0.1f), "timeout");
itemSlot.SetItemStyle(); itemSlot.SetItemStyle();
if (itemSlot.Item is IEquipableItem equipable && Player.Inventory.IsEquipped(equipable)) if (itemSlot.Item is IEquipableItem equipable && equipable.IsEquipped)
itemSlot.SetEquippedItemStyle(); itemSlot.SetEquippedItemStyle();
} }
@@ -372,16 +372,16 @@ public partial class InventoryMenu : Control, IInventoryMenu
var itemSlot = ItemSlots[_currentIndex]; var itemSlot = ItemSlots[_currentIndex];
if (itemSlot.Item is IEquipableItem equipableItem) if (itemSlot.Item is IEquipableItem equipableItem)
{ {
if (Player.Inventory.IsEquipped(equipableItem)) if (equipableItem.IsEquipped)
{ {
ItemEffectLabel.Text = $"{itemSlot.Item.GetType()} unequipped."; ItemEffectLabel.Text = $"{itemSlot.Item.GetType()} unequipped.";
Player.Inventory.Unequip(equipableItem); Player.Unequip(equipableItem);
itemSlot.SetSelectedItemStyle(); itemSlot.SetSelectedItemStyle();
} }
else else
{ {
ItemEffectLabel.Text = $"{itemSlot.Item.GetType()} equipped."; ItemEffectLabel.Text = $"{itemSlot.Item.GetType()} equipped.";
Player.Inventory.Equip(equipableItem); Player.Equip(equipableItem);
itemSlot.SetEquippedSelectedItemStyle(); itemSlot.SetEquippedSelectedItemStyle();
} }
@@ -446,7 +446,7 @@ public partial class InventoryMenu : Control, IInventoryMenu
SetProcessInput(false); SetProcessInput(false);
await HideUserActionPrompt(); await HideUserActionPrompt();
await ShowInventoryInfo(); await ShowInventoryInfo();
await RedrawInventory(); await RefreshInventoryScreen();
await ToSignal(GetTree().CreateTimer(1f), "timeout"); await ToSignal(GetTree().CreateTimer(1f), "timeout");
SetProcessInput(true); SetProcessInput(true);
} }

View File

@@ -42,9 +42,9 @@ public partial class ItemSlot : HBoxContainer, IItemSlot
ItemName.Text = Item.ItemName; ItemName.Text = Item.ItemName;
//EquipBonus.Text = "..."; //EquipBonus.Text = "...";
ItemTexture.Texture = Item.GetTexture(); ItemTexture.Texture = Item.GetTexture();
Player.Inventory.EquippedWeapon.Sync += EquippedWeapon_Sync; Player.EquippedWeapon.Sync += EquippedWeapon_Sync;
Player.Inventory.EquippedArmor.Sync += EquippedArmor_Sync; Player.EquippedArmor.Sync += EquippedArmor_Sync;
Player.Inventory.EquippedAccessory.Sync += EquippedAccessory_Sync; Player.EquippedAccessory.Sync += EquippedAccessory_Sync;
} }
private void EquippedWeapon_Sync(Weapon obj) private void EquippedWeapon_Sync(Weapon obj)
@@ -90,7 +90,7 @@ public partial class ItemSlot : HBoxContainer, IItemSlot
} }
public void SetSelectedItemStyle() public void SetSelectedItemStyle()
{ {
if (Item is IEquipableItem equipableItem && Player.Inventory.IsEquipped(equipableItem)) if (Item is IEquipableItem equipableItem && equipableItem.IsEquipped)
{ {
ItemName.LabelSettings = SelectedEquippedItemFont; ItemName.LabelSettings = SelectedEquippedItemFont;
//EquipBonus.LabelSettings = EquippedItemFont; //EquipBonus.LabelSettings = EquippedItemFont;

View File

@@ -5,4 +5,6 @@ namespace GameJamDungeon;
public interface IEquipableItem : IInventoryItem public interface IEquipableItem : IInventoryItem
{ {
public ImmutableList<ItemTag> ItemTags { get; } public ImmutableList<ItemTag> ItemTags { get; }
public bool IsEquipped { get; }
} }

View File

@@ -1,8 +1,6 @@
using Chickensoft.AutoInject; using Chickensoft.AutoInject;
using Chickensoft.Collections;
using Chickensoft.GodotNodeInterfaces; using Chickensoft.GodotNodeInterfaces;
using Godot; using Godot;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -11,22 +9,11 @@ namespace GameJamDungeon;
public interface IInventory : INode public interface IInventory : INode
{ {
public List<IInventoryItem> Items { get; } public List<IInventoryItem> Items { get; }
public IAutoProp<Weapon> EquippedWeapon { get; }
public IAutoProp<Armor> EquippedArmor { get; }
public IAutoProp<Accessory> EquippedAccessory { get; }
public bool TryAdd(IInventoryItem inventoryItem); public bool TryAdd(IInventoryItem inventoryItem);
public void Remove(IInventoryItem inventoryItem); public void Remove(IInventoryItem inventoryItem);
public void Equip(IEquipableItem equipable);
public void Unequip(IEquipableItem equipable);
public bool IsEquipped(IEquipableItem equipable);
public void Sort(); public void Sort();
event Inventory.InventoryAtCapacityEventHandler InventoryAtCapacity; event Inventory.InventoryAtCapacityEventHandler InventoryAtCapacity;
@@ -51,38 +38,8 @@ public partial class Inventory : Node, IInventory
Items = []; Items = [];
} }
public void Setup()
{
EquippedWeapon.Changed += EquippedWeapon_Changed;
EquippedArmor.Changed += EquippedArmor_Changed;
EquippedAccessory.Changed += EquippedAccessory_Changed;
}
private void EquippedAccessory_Changed(Accessory obj)
{
}
private void EquippedArmor_Changed(Armor obj)
{
throw new NotImplementedException();
}
private void EquippedWeapon_Changed(Weapon obj)
{
throw new NotImplementedException();
}
public List<IInventoryItem> Items { get; private set; } public List<IInventoryItem> Items { get; private set; }
public IAutoProp<Weapon> EquippedWeapon => _equippedWeapon;
private AutoProp<Weapon> _equippedWeapon { get; set; } = new AutoProp<Weapon>(new Weapon());
public IAutoProp<Armor> EquippedArmor => _equippedArmor;
private AutoProp<Armor> _equippedArmor { get; set; } = new AutoProp<Armor>(new Armor());
public IAutoProp<Accessory> EquippedAccessory => _equippedAccessory;
private AutoProp<Accessory> _equippedAccessory { get; set; } = new AutoProp<Accessory>(new Accessory());
public bool TryAdd(IInventoryItem inventoryItem) public bool TryAdd(IInventoryItem inventoryItem)
{ {
if (Items.Count >= _maxInventorySize) if (Items.Count >= _maxInventorySize)
@@ -98,50 +55,12 @@ public partial class Inventory : Node, IInventory
public void Remove(IInventoryItem inventoryItem) => Items.Remove(inventoryItem); public void Remove(IInventoryItem inventoryItem) => Items.Remove(inventoryItem);
public void Equip(IEquipableItem equipable)
{
if (equipable is Weapon weapon)
_equippedWeapon.OnNext(weapon);
else if (equipable is Armor armor)
_equippedArmor.OnNext(armor);
else if (equipable is Accessory accessory)
_equippedAccessory.OnNext(accessory);
else
throw new NotImplementedException("Item type is not supported.");
}
public void Unequip(IEquipableItem equipable)
{
if (equipable is Weapon)
_equippedWeapon.OnNext(new Weapon());
else if (equipable is Armor)
_equippedArmor.OnNext(new Armor());
else if (equipable is Accessory)
_equippedAccessory.OnNext(new Accessory());
else
throw new NotImplementedException("Item type is not supported.");
if (equipable.ItemTags.Contains(ItemTag.BreaksOnChange))
Remove(equipable);
}
public bool IsEquipped(IEquipableItem equipable)
{
if (equipable is Weapon weapon)
return _equippedWeapon.Value.Equals(weapon);
else if (equipable is Armor armor)
return _equippedArmor.Value.Equals(armor);
else if (equipable is Accessory accessory)
return _equippedAccessory.Value.Equals(accessory);
else
throw new NotImplementedException("Item type is not supported.");
}
public void Sort() public void Sort()
{ {
var equippedWeapon = Items.OfType<Weapon>().Where(IsEquipped); var equippedWeapon = Items.OfType<Weapon>().Where(x => x.IsEquipped);
var equippedArmor = Items.OfType<Armor>().Where(IsEquipped); var equippedArmor = Items.OfType<Armor>().Where(x => x.IsEquipped);
var equippedAccessory = Items.OfType<Accessory>().Where(IsEquipped); var equippedAccessory = Items.OfType<Accessory>().Where(x => x.IsEquipped);
var equippedItems = new List<IInventoryItem>(); var equippedItems = new List<IInventoryItem>();
equippedItems.AddRange(equippedWeapon); equippedItems.AddRange(equippedWeapon);
equippedItems.AddRange(equippedArmor); equippedItems.AddRange(equippedArmor);

View File

@@ -41,10 +41,16 @@ public partial class Accessory : Node3D, IEquipableItem
public double LuckUp => _accessoryStats.LuckUp; public double LuckUp => _accessoryStats.LuckUp;
public int ATKUp => _accessoryStats.ATKUp;
public int DEFUp => _accessoryStats.DEFUp;
public ImmutableList<AccessoryTag> AccessoryTags => [.. _accessoryStats.AccessoryTags]; public ImmutableList<AccessoryTag> AccessoryTags => [.. _accessoryStats.AccessoryTags];
public ImmutableList<ItemTag> ItemTags => [.. _accessoryStats.ItemTags]; public ImmutableList<ItemTag> ItemTags => [.. _accessoryStats.ItemTags];
public bool IsEquipped { get; set; }
public void OnReady() public void OnReady()
{ {
Pickup.BodyEntered += OnEntered; Pickup.BodyEntered += OnEntered;

View File

@@ -1,6 +1,6 @@
[gd_scene load_steps=3 format=3 uid="uid://b07srt3lckt4e"] [gd_scene load_steps=3 format=3 uid="uid://b07srt3lckt4e"]
[ext_resource type="Script" path="res://src/items/accessory/Accessory.cs" id="1_ikyk2"] [ext_resource type="Script" uid="uid://2xddsc0pjykd" path="res://src/items/accessory/Accessory.cs" id="1_ikyk2"]
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_1ceef"] [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_1ceef"]
radius = 0.470016 radius = 0.470016

View File

@@ -1,17 +1,22 @@
[gd_resource type="Resource" script_class="AccessoryStats" load_steps=3 format=3 uid="uid://cvkwmart5y51r"] [gd_resource type="Resource" script_class="AccessoryStats" load_steps=3 format=3 uid="uid://cvkwmart5y51r"]
[ext_resource type="Texture2D" uid="uid://hjyk3j24o48b" path="res://src/items/accessory/textures/MASK 03.PNG" id="1_q42cv"] [ext_resource type="Texture2D" uid="uid://hjyk3j24o48b" path="res://src/items/accessory/textures/MASK 03.PNG" id="1_q42cv"]
[ext_resource type="Script" path="res://src/items/accessory/AccessoryStats.cs" id="1_xqaot"] [ext_resource type="Script" uid="uid://b8arlmivk68b" path="res://src/items/accessory/AccessoryStats.cs" id="1_xqaot"]
[resource] [resource]
script = ExtResource("1_xqaot") script = ExtResource("1_xqaot")
ATKUp = 0 ATKUp = 0
DEFUp = 0 DEFUp = 0
LUCKUp = 0.15 LuckUp = 0.1
MaxHPUp = 0 MaxHPUp = 0
MaxVTUp = 0 MaxVTUp = 0
AccessoryTags = [] AccessoryTags = Array[int]([])
Name = "Mask of the Goddess of Avarice" Name = "Mask of the Goddess of Avarice"
Description = "Raises Luck" Description = "Raises Luck"
Texture = ExtResource("1_q42cv") Texture = ExtResource("1_q42cv")
SpawnRate = 0.1 SpawnRate = 0.1
ThrowSpeed = 12.0
HealHPAmount = 0
HealVTAmount = 0
ThrowDamage = 5
ItemTags = Array[int]([])

View File

@@ -37,6 +37,8 @@ public partial class Armor : Node3D, IEquipableItem
public ImmutableList<ItemTag> ItemTags => [.. _armorStats.ItemTags]; public ImmutableList<ItemTag> ItemTags => [.. _armorStats.ItemTags];
public bool IsEquipped { get; set; }
public void OnReady() public void OnReady()
{ {
Pickup.BodyEntered += OnEntered; Pickup.BodyEntered += OnEntered;

View File

@@ -51,6 +51,8 @@ public partial class Weapon : Node3D, IInventoryItem, IEquipableItem
public double ElementalDamageBonus => _weaponStats.ElementalDamageBonus; public double ElementalDamageBonus => _weaponStats.ElementalDamageBonus;
public bool IsEquipped { get; set; }
public void OnReady() public void OnReady()
{ {
Pickup.BodyEntered += OnEntered; Pickup.BodyEntered += OnEntered;
@@ -60,9 +62,12 @@ public partial class Weapon : Node3D, IInventoryItem, IEquipableItem
public void SetItemStats(InventoryItemStats inventoryItemStats) public void SetItemStats(InventoryItemStats inventoryItemStats)
{ {
_weaponStats = (WeaponStats)inventoryItemStats; _weaponStats = (WeaponStats)inventoryItemStats;
if (inventoryItemStats.Texture != null)
{
var texture = ResourceLoader.Load(inventoryItemStats.Texture.ResourcePath) as Texture2D; var texture = ResourceLoader.Load(inventoryItemStats.Texture.ResourcePath) as Texture2D;
Sprite.Texture = texture; Sprite.Texture = texture;
} }
}
public void Throw() public void Throw()
{ {

View File

@@ -1,16 +1,9 @@
[gd_scene load_steps=18 format=3 uid="uid://bc1sp6xwe0j65"] [gd_scene load_steps=11 format=3 uid="uid://bc1sp6xwe0j65"]
[ext_resource type="Script" uid="uid://dwt6302nsf4vq" path="res://src/map/dungeon/code/DungeonFloor.cs" id="1_0ecnn"] [ext_resource type="Script" uid="uid://dwt6302nsf4vq" path="res://src/map/dungeon/code/DungeonFloor.cs" id="1_0ecnn"]
[ext_resource type="Script" uid="uid://b1x125h0tya2w" path="res://addons/SimpleDungeons/DungeonGenerator3D.gd" id="2_cxmwa"] [ext_resource type="Script" uid="uid://b1x125h0tya2w" path="res://addons/SimpleDungeons/DungeonGenerator3D.gd" id="2_cxmwa"]
[ext_resource type="PackedScene" uid="uid://dpec2lbt83dhe" path="res://src/map/dungeon/scenes/Set A/03. Antechamber A.tscn" id="3_gkkr3"] [ext_resource type="PackedScene" uid="uid://dpec2lbt83dhe" path="res://src/map/dungeon/scenes/Set A/03. Antechamber A.tscn" id="3_gkkr3"]
[ext_resource type="PackedScene" uid="uid://i781lbf2wb22" path="res://src/map/dungeon/scenes/Set A/04. Antechamber B.tscn" id="4_n610c"]
[ext_resource type="PackedScene" uid="uid://cam640h4euewx" path="res://src/map/dungeon/scenes/Set A/05. Pit Room A.tscn" id="5_n8ctv"]
[ext_resource type="PackedScene" uid="uid://b7111krf365x0" path="res://src/map/dungeon/scenes/Set A/06. Balcony Room A.tscn" id="6_epmod"]
[ext_resource type="PackedScene" uid="uid://vdhl32je6hq2" path="res://src/map/dungeon/scenes/Set A/07. Statue Room.tscn" id="7_lrk4l"]
[ext_resource type="PackedScene" uid="uid://b82dx66mgs2d7" path="res://src/map/dungeon/scenes/Set A/08. BasinRoom.tscn" id="8_5rblf"] [ext_resource type="PackedScene" uid="uid://b82dx66mgs2d7" path="res://src/map/dungeon/scenes/Set A/08. BasinRoom.tscn" id="8_5rblf"]
[ext_resource type="PackedScene" uid="uid://c1qicmrcg6q6x" path="res://src/map/dungeon/scenes/Set A/09. Column Room.tscn" id="9_qx5t8"]
[ext_resource type="PackedScene" uid="uid://dhm2lyfkrjugf" path="res://src/map/dungeon/scenes/Set A/11. Long Room.tscn" id="10_bjb1j"]
[ext_resource type="PackedScene" uid="uid://dfpyfpnya0f4u" path="res://src/map/dungeon/scenes/Set A/13. Water Room.tscn" id="11_kl0fr"]
[ext_resource type="PackedScene" uid="uid://dbvr8ewajja6a" path="res://src/enemy/EnemyDatabase.tscn" id="12_aw26s"] [ext_resource type="PackedScene" uid="uid://dbvr8ewajja6a" path="res://src/enemy/EnemyDatabase.tscn" id="12_aw26s"]
[ext_resource type="PackedScene" uid="uid://cihbmyo0ltq4m" path="res://src/map/dungeon/scenes/Set A/19. Floor Exit A.tscn" id="12_n02rw"] [ext_resource type="PackedScene" uid="uid://cihbmyo0ltq4m" path="res://src/map/dungeon/scenes/Set A/19. Floor Exit A.tscn" id="12_n02rw"]
[ext_resource type="PackedScene" uid="uid://bksq62muhk3h5" path="res://src/enemy/enemy_types/1. sproingy/Sproingy.tscn" id="13_kwaga"] [ext_resource type="PackedScene" uid="uid://bksq62muhk3h5" path="res://src/enemy/enemy_types/1. sproingy/Sproingy.tscn" id="13_kwaga"]
@@ -31,9 +24,9 @@ navigation_mesh = SubResource("NavigationMesh_4d8mx")
[node name="DungeonGenerator" type="Node3D" parent="NavigationRegion3D"] [node name="DungeonGenerator" type="Node3D" parent="NavigationRegion3D"]
unique_name_in_owner = true unique_name_in_owner = true
script = ExtResource("2_cxmwa") script = ExtResource("2_cxmwa")
room_scenes = Array[PackedScene]([ExtResource("3_gkkr3"), ExtResource("4_n610c"), ExtResource("5_n8ctv"), ExtResource("6_epmod"), ExtResource("7_lrk4l"), ExtResource("8_5rblf"), ExtResource("9_qx5t8"), ExtResource("10_bjb1j"), ExtResource("11_kl0fr"), ExtResource("12_n02rw")]) room_scenes = Array[PackedScene]([ExtResource("3_gkkr3"), ExtResource("8_5rblf"), ExtResource("12_n02rw")])
corridor_room_scene = ExtResource("13_ofywd") corridor_room_scene = ExtResource("13_ofywd")
dungeon_size = Vector3i(40, 1, 40) dungeon_size = Vector3i(20, 1, 20)
voxel_scale = Vector3(4, 4, 4) voxel_scale = Vector3(4, 4, 4)
generate_on_ready = false generate_on_ready = false
heuristic_scale = 3.0 heuristic_scale = 3.0

View File

@@ -1,4 +1,5 @@
using Godot; using Chickensoft.Collections;
using Godot;
namespace GameJamDungeon; namespace GameJamDungeon;
@@ -32,11 +33,15 @@ public interface IPlayer : IKillable
public void RaiseVT(int amount); public void RaiseVT(int amount);
public void RaiseBonusAttack(int amount); public void ModifyBonusAttack(int amount);
public void RaiseBonusDefense(int amount); public void ModifyBonusDefense(int amount);
public void RaiseBonusLuck(int amount); public void ModifyMaximumHP(int amount);
public void ModifyMaximumVT(int amount);
public void ModifyBonusLuck(double amount);
public IInventory Inventory { get; } public IInventory Inventory { get; }
@@ -45,4 +50,14 @@ public interface IPlayer : IKillable
public Vector3 CurrentPosition { get; } public Vector3 CurrentPosition { get; }
public Basis CurrentBasis { get; } public Basis CurrentBasis { get; }
public IAutoProp<Weapon> EquippedWeapon { get; }
public IAutoProp<Armor> EquippedArmor { get; }
public IAutoProp<Accessory> EquippedAccessory { get; }
public void Equip(IEquipableItem equipable);
public void Unequip(IEquipableItem equipable);
} }

View File

@@ -1,9 +1,11 @@
using Chickensoft.AutoInject; using Chickensoft.AutoInject;
using Chickensoft.Collections;
using Chickensoft.GodotNodeInterfaces; using Chickensoft.GodotNodeInterfaces;
using Chickensoft.Introspection; using Chickensoft.Introspection;
using Chickensoft.SaveFileBuilder; using Chickensoft.SaveFileBuilder;
using Godot; using Godot;
using Godot.Collections; using Godot.Collections;
using System;
namespace GameJamDungeon; namespace GameJamDungeon;
@@ -25,6 +27,15 @@ public partial class Player : CharacterBody3D, IPlayer
public IInventory Inventory { get; private set; } = default!; public IInventory Inventory { get; private set; } = default!;
public IAutoProp<Weapon> EquippedWeapon => _equippedWeapon;
private AutoProp<Weapon> _equippedWeapon { get; set; } = new AutoProp<Weapon>(new Weapon());
public IAutoProp<Armor> EquippedArmor => _equippedArmor;
private AutoProp<Armor> _equippedArmor { get; set; } = new AutoProp<Armor>(new Armor());
public IAutoProp<Accessory> EquippedAccessory => _equippedAccessory;
private AutoProp<Accessory> _equippedAccessory { get; set; } = new AutoProp<Accessory>(new Accessory());
private PlayerLogic.Settings Settings { get; set; } = default!; private PlayerLogic.Settings Settings { get; set; } = default!;
private PlayerLogic PlayerLogic { get; set; } = default!; private PlayerLogic PlayerLogic { get; set; } = default!;
@@ -129,13 +140,15 @@ public partial class Player : CharacterBody3D, IPlayer
Inventory.TryAdd(defaultWeapon); Inventory.TryAdd(defaultWeapon);
Inventory.TryAdd(defaultArmor); Inventory.TryAdd(defaultArmor);
Inventory.Equip(defaultWeapon); EquippedWeapon.Sync += EquippedWeapon_Sync;
Inventory.Equip(defaultArmor); EquippedArmor.Sync += EquippedArmor_Sync;
EquippedAccessory.Sync += EquippedAccessory_Sync;
Inventory.EquippedAccessory.Sync += EquippedAccessory_Sync;
Stats.CurrentHP.Sync += CurrentHP_Sync; Stats.CurrentHP.Sync += CurrentHP_Sync;
Stats.CurrentExp.Sync += CurrentEXP_Sync; Stats.CurrentExp.Sync += CurrentEXP_Sync;
Equip(defaultWeapon);
Equip(defaultArmor);
HealthTimer.WaitTime = _healthTimerWaitTime; HealthTimer.WaitTime = _healthTimerWaitTime;
} }
@@ -146,7 +159,7 @@ public partial class Player : CharacterBody3D, IPlayer
PlayerBinding PlayerBinding
.Handle((in PlayerLogic.Output.Animations.Attack output) => .Handle((in PlayerLogic.Output.Animations.Attack output) =>
{ {
var attackSpeed = Inventory.EquippedWeapon.Value.AttackSpeed; var attackSpeed = EquippedWeapon.Value.AttackSpeed;
AnimationPlayer.SetSpeedScale((float)attackSpeed); AnimationPlayer.SetSpeedScale((float)attackSpeed);
AnimationPlayer.Play("attack"); AnimationPlayer.Play("attack");
@@ -219,19 +232,29 @@ public partial class Player : CharacterBody3D, IPlayer
Game.AnnounceMessageOnInventoryScreen($"{raiseString}VT Restored."); Game.AnnounceMessageOnInventoryScreen($"{raiseString}VT Restored.");
} }
public void RaiseBonusAttack(int amount) public void ModifyBonusAttack(int amount)
{ {
Stats.SetBonusAttack(Stats.BonusAttack.Value + amount); Stats.SetBonusAttack(Stats.BonusAttack.Value + amount);
} }
public void RaiseBonusDefense(int amount) public void ModifyBonusDefense(int amount)
{ {
Stats.SetBonusDefense(Stats.BonusDefense.Value + amount); Stats.SetBonusDefense(Stats.BonusDefense.Value + amount);
} }
public void RaiseBonusLuck(int amount) public void ModifyMaximumHP(int amount)
{ {
Stats.SetLuck(amount); Stats.SetMaximumHP(Stats.MaximumHP.Value + amount);
}
public void ModifyMaximumVT(int amount)
{
Stats.SetMaximumVT(Stats.MaximumVT.Value + amount);
}
public void ModifyBonusLuck(double amount)
{
Stats.SetLuck(Stats.Luck.Value + amount);
} }
public void Move(float delta) public void Move(float delta)
@@ -310,6 +333,61 @@ public partial class Player : CharacterBody3D, IPlayer
PlayerLogic.Input(new PlayerLogic.Input.Moved(GlobalPosition, GlobalTransform)); PlayerLogic.Input(new PlayerLogic.Input.Moved(GlobalPosition, GlobalTransform));
} }
public void Equip(IEquipableItem equipable)
{
if (equipable is Weapon weapon)
{
Unequip(_equippedWeapon.Value);
weapon.IsEquipped = true;
_equippedWeapon.OnNext(weapon);
}
else if (equipable is Armor armor)
{
Unequip(_equippedArmor.Value);
armor.IsEquipped = true;
_equippedArmor.OnNext(armor);
}
else if (equipable is Accessory accessory)
{
Unequip(_equippedAccessory.Value);
accessory.IsEquipped = true;
_equippedAccessory.OnNext(accessory);
}
else
throw new NotImplementedException("Item type is not supported.");
}
public void Unequip(IEquipableItem equipable)
{
if (equipable is Weapon weapon)
{
weapon.IsEquipped = false;
ModifyBonusAttack(-weapon.Damage);
_equippedWeapon.OnNext(new Weapon());
}
else if (equipable is Armor armor)
{
armor.IsEquipped = false;
ModifyBonusDefense(-armor.Defense);
_equippedArmor.OnNext(new Armor());
}
else if (equipable is Accessory accessory)
{
accessory.IsEquipped = false;
ModifyMaximumHP(-accessory.MaxHPUp);
ModifyMaximumVT(-accessory.MaxVTUp);
ModifyBonusAttack(-accessory.ATKUp);
ModifyBonusDefense(-accessory.DEFUp);
ModifyBonusLuck(-accessory.LuckUp);
_equippedAccessory.OnNext(new Accessory());
}
else
throw new NotImplementedException("Item type is not supported.");
if (equipable.ItemTags.Contains(ItemTag.BreaksOnChange))
Inventory.Remove(equipable);
}
private static Vector3 GlobalInputVector private static Vector3 GlobalInputVector
{ {
get get
@@ -371,7 +449,7 @@ public partial class Player : CharacterBody3D, IPlayer
if (Stats.CurrentVT.Value > 0) if (Stats.CurrentVT.Value > 0)
{ {
if (Inventory.EquippedAccessory.Value.AccessoryTags.Contains(AccessoryTag.HalfVTConsumption)) if (EquippedAccessory.Value.AccessoryTags.Contains(AccessoryTag.HalfVTConsumption))
{ {
reduceOnTick = !reduceOnTick; reduceOnTick = !reduceOnTick;
} }
@@ -383,18 +461,23 @@ public partial class Player : CharacterBody3D, IPlayer
Stats.SetCurrentHP(Stats.CurrentHP.Value - 1); Stats.SetCurrentHP(Stats.CurrentHP.Value - 1);
} }
private void EquippedAccessory_Sync(Accessory accessory) private void EquippedWeapon_Sync(Weapon obj)
{ {
Stats.SetMaximumHP(Stats.MaximumHP.Value + accessory.MaxHPUp); ModifyBonusAttack(obj.Damage);
Stats.SetMaximumVT(Stats.MaximumVT.Value + accessory.MaxVTUp);
Stats.SetLuck(Stats.Luck.Value + accessory.LuckUp);
} }
private void Inventory_AccessoryUnequipped(Accessory accessory) private void EquippedArmor_Sync(Armor obj)
{ {
Stats.SetMaximumHP(Stats.MaximumHP.Value - accessory.MaxHPUp); ModifyBonusDefense(obj.Defense);
Stats.SetMaximumVT(Stats.MaximumVT.Value - accessory.MaxVTUp); }
Stats.SetLuck(Stats.Luck.Value - accessory.LuckUp);
private void EquippedAccessory_Sync(Accessory accessory)
{
ModifyMaximumHP(accessory.MaxHPUp);
ModifyMaximumVT(accessory.MaxVTUp);
ModifyBonusAttack(accessory.ATKUp);
ModifyBonusDefense(accessory.DEFUp);
ModifyBonusLuck(accessory.LuckUp);
} }
private void CurrentHP_Sync(int newHealth) private void CurrentHP_Sync(int newHealth)
@@ -411,7 +494,7 @@ public partial class Player : CharacterBody3D, IPlayer
private double CalculateDefenseResistance(double incomingDamage) private double CalculateDefenseResistance(double incomingDamage)
{ {
return Mathf.Max(incomingDamage - Stats.CurrentDefense.Value, 0.0); return Mathf.Max(incomingDamage - Stats.CurrentDefense.Value - Stats.BonusDefense.Value, 0.0);
} }
private void Hitbox_AreaEntered(Area3D area) private void Hitbox_AreaEntered(Area3D area)
@@ -423,19 +506,19 @@ public partial class Player : CharacterBody3D, IPlayer
private void HitEnemy(IEnemy enemy) private void HitEnemy(IEnemy enemy)
{ {
var attackValue = PlayerStatResource.CurrentAttack + Inventory.EquippedWeapon.Value.Damage; var attackValue = Stats.CurrentAttack.Value + Stats.BonusAttack.Value;
var ignoreElementalResistance = Inventory.EquippedWeapon.Value.WeaponTags.Contains(WeaponTag.IgnoreAffinity); var ignoreElementalResistance = EquippedWeapon.Value.WeaponTags.Contains(WeaponTag.IgnoreAffinity);
var isCriticalHit = BattleExtensions.IsCriticalHit(Stats.Luck.Value); var isCriticalHit = BattleExtensions.IsCriticalHit(Stats.Luck.Value);
var element = Inventory.EquippedWeapon.Value.WeaponElement; var element = EquippedWeapon.Value.WeaponElement;
enemy.TakeDamage( enemy.TakeDamage(
attackValue * Inventory.EquippedWeapon.Value.ElementalDamageBonus, attackValue * EquippedWeapon.Value.ElementalDamageBonus,
element, element,
isCriticalHit, isCriticalHit,
false, false,
ignoreElementalResistance); ignoreElementalResistance);
if (Inventory.EquippedWeapon.Value.WeaponTags.Contains(WeaponTag.Knockback)) if (EquippedWeapon.Value.WeaponTags.Contains(WeaponTag.Knockback))
enemy.Knockback(0.3f, -CurrentBasis.Z.Normalized()); enemy.Knockback(0.3f, -CurrentBasis.Z.Normalized());
} }
} }

View File

@@ -56,7 +56,7 @@ public partial class InGameUI : Control, IInGameUI
public void ShowInventoryScreen() public void ShowInventoryScreen()
{ {
InventoryMenu.RedrawInventory(); InventoryMenu.RefreshInventoryScreen();
InventoryMenu.Show(); InventoryMenu.Show();
} }