Fix up equipping inventory items, fill out the rest of the inventory labels

This commit is contained in:
2024-09-09 23:05:44 -07:00
parent 4986cca661
commit b5798e7bc0
17 changed files with 340 additions and 108 deletions

View File

@@ -181,9 +181,9 @@ public partial class Enemy : CharacterBody3D, IEnemy, IProvide<IEnemyLogic>
var rng = new RandomNumberGenerator(); var rng = new RandomNumberGenerator();
rng.Randomize(); rng.Randomize();
var roll = rng.Randf(); var roll = rng.Randf();
if (roll <= GameRepo.EquippedWeapon.WeaponInfo.Luck) if (roll <= GameRepo.EquippedWeapon.Value.WeaponInfo.Luck)
isCriticalHit = true; isCriticalHit = true;
var damage = DamageCalculator.CalculatePlayerDamage(hitBox.Damage, hitBox.GetParent<IPlayer>().PlayerStatInfo, EnemyStatInfo, GameRepo.EquippedWeapon.WeaponInfo, isCriticalHit); var damage = DamageCalculator.CalculatePlayerDamage(hitBox.Damage, hitBox.GetParent<IPlayer>().PlayerStatInfo, EnemyStatInfo, GameRepo.EquippedWeapon.Value.WeaponInfo, isCriticalHit);
GD.Print($"Enemy Hit for {damage} damage."); GD.Print($"Enemy Hit for {damage} damage.");
EnemyLogic.Input(new EnemyLogic.Input.HitByPlayer(damage)); EnemyLogic.Input(new EnemyLogic.Input.HitByPlayer(damage));
} }

View File

@@ -5,14 +5,23 @@ namespace GameJamDungeon
[GlobalClass] [GlobalClass]
public partial class EnemyStatInfo : Resource, ICharacterStats public partial class EnemyStatInfo : Resource, ICharacterStats
{ {
[Export]
public double CurrentHP { get; set; }
[Export] [Export]
public double MaximumHP { get; set; } public double MaximumHP { get; set; }
[Export] [Export]
public int BaseAttack { get; set; } public int CurrentAttack { get; set; }
[Export] [Export]
public int BaseDefense { get; set; } public int CurrentDefense { get; set; }
[Export]
public int MaxAttack { get; set; }
[Export]
public int MaxDefense { get; set; }
[Export] [Export]
public float Luck { get; set; } = 0.05f; public float Luck { get; set; } = 0.05f;

View File

@@ -91,9 +91,9 @@ public partial class Game : Node3D, IGame
var currentFloor = Floors.ElementAt(_currentFloor); var currentFloor = Floors.ElementAt(_currentFloor);
currentFloor.CallDeferred(MethodName.QueueFree, []); currentFloor.CallDeferred(MethodName.QueueFree, []);
if (GameRepo.EquippedWeapon.WeaponInfo != null && GameRepo.EquippedWeapon.WeaponInfo.WeaponTags.Contains(WeaponTag.BreaksOnChange)) if (GameRepo.EquippedWeapon.Value.WeaponInfo != null && GameRepo.EquippedWeapon.Value.WeaponInfo.WeaponTags.Contains(WeaponTag.BreaksOnChange))
{ {
GameRepo.InventoryItems.Value.Remove(GameRepo.EquippedWeapon); GameRepo.InventoryItems.Value.Remove(GameRepo.EquippedWeapon.Value);
GameRepo.OnWeaponEquipped(new Weapon()); GameRepo.OnWeaponEquipped(new Weapon());
} }

View File

@@ -23,25 +23,29 @@ public interface IGameRepo : IDisposable
void SetPlayerGlobalPosition(Vector3 playerGlobalPosition); void SetPlayerGlobalPosition(Vector3 playerGlobalPosition);
void SetPlayerStatInfo(PlayerStatInfo playerStatInfo);
public void OnWeaponEquipped(Weapon equippedItem); public void OnWeaponEquipped(Weapon equippedItem);
public void OnArmorEquipped(Armor equippedItem); public void OnArmorEquipped(Armor equippedItem);
public void OnAccessoryEquipped(Accessory equippedItem); public void OnAccessoryEquipped(Accessory equippedItem);
public Weapon EquippedWeapon { get; } public AutoProp<Weapon> EquippedWeapon { get; }
public Armor EquippedArmor { get; } public AutoProp<Armor> EquippedArmor { get; }
public Accessory EquippedAccessory { get; } public AutoProp<Accessory> EquippedAccessory { get; }
public AutoProp<int> HPBarValue { get; }
public AutoProp<int> VTBarValue { get; }
bool IsWithinDialogueSpace { get; set; }
public int MaxItemSize { get; } public int MaxItemSize { get; }
public AutoProp<PlayerStatInfo> PlayerStatInfo { get; }
public bool IsItemEquipped(IEquipable item);
public void EquipItem(IEquipable item);
public void UnequipItem(IEquipable item);
} }
public class GameRepo : IGameRepo public class GameRepo : IGameRepo
@@ -61,20 +65,16 @@ public class GameRepo : IGameRepo
public IAutoProp<bool> IsPaused => _isPaused; public IAutoProp<bool> IsPaused => _isPaused;
private readonly AutoProp<bool> _isPaused; private readonly AutoProp<bool> _isPaused;
private Weapon _equippedWeapon; private AutoProp<Weapon> _equippedWeapon;
public Weapon EquippedWeapon => _equippedWeapon; public AutoProp<Weapon> EquippedWeapon => _equippedWeapon;
private Armor _equippedArmor; private AutoProp<Armor> _equippedArmor;
public Armor EquippedArmor => _equippedArmor; public AutoProp<Armor> EquippedArmor => _equippedArmor;
private Accessory _equippedAccessory; private AutoProp<Accessory> _equippedAccessory;
public Accessory EquippedAccessory => _equippedAccessory; public AutoProp<Accessory> EquippedAccessory => _equippedAccessory;
public AutoProp<int> HPBarValue { get; }
public AutoProp<int> VTBarValue { get; }
public bool IsWithinDialogueSpace { get; set; } public bool IsWithinDialogueSpace { get; set; }
@@ -82,16 +82,20 @@ public class GameRepo : IGameRepo
private bool _disposedValue; private bool _disposedValue;
private AutoProp<PlayerStatInfo> _playerStatInfo;
public AutoProp<PlayerStatInfo> PlayerStatInfo => _playerStatInfo;
public GameRepo() public GameRepo()
{ {
_inventoryItems = new AutoProp<List<IInventoryItem>>([]); _inventoryItems = new AutoProp<List<IInventoryItem>>([]);
_isInventoryScreenOpened = new AutoProp<bool>(false); _isInventoryScreenOpened = new AutoProp<bool>(false);
_isPaused = new AutoProp<bool>(false); _isPaused = new AutoProp<bool>(false);
_playerGlobalPosition = new AutoProp<Vector3>(Vector3.Zero); _playerGlobalPosition = new AutoProp<Vector3>(Vector3.Zero);
_equippedWeapon = new Weapon(); _equippedWeapon = new AutoProp<Weapon>(new Weapon());
HPBarValue = new AutoProp<int>(0); _equippedArmor = new AutoProp<Armor>(new Armor());
VTBarValue = new AutoProp<int>(0); _equippedAccessory = new AutoProp<Accessory>(new Accessory());
IsWithinDialogueSpace = false; _playerStatInfo = new AutoProp<PlayerStatInfo>(new PlayerStatInfo());
} }
public void Pause() public void Pause()
@@ -108,19 +112,21 @@ public class GameRepo : IGameRepo
public void SetPlayerGlobalPosition(Vector3 playerGlobalPosition) => _playerGlobalPosition.OnNext(playerGlobalPosition); public void SetPlayerGlobalPosition(Vector3 playerGlobalPosition) => _playerGlobalPosition.OnNext(playerGlobalPosition);
public void SetPlayerStatInfo(PlayerStatInfo playerStatInfo) => _playerStatInfo.OnNext(playerStatInfo);
public void OnWeaponEquipped(Weapon equippedItem) public void OnWeaponEquipped(Weapon equippedItem)
{ {
_equippedWeapon = equippedItem; _equippedWeapon.OnNext(equippedItem);
} }
public void OnArmorEquipped(Armor equippedItem) public void OnArmorEquipped(Armor equippedItem)
{ {
_equippedArmor = equippedItem; _equippedArmor.OnNext(equippedItem);
} }
public void OnAccessoryEquipped(Accessory equippedItem) public void OnAccessoryEquipped(Accessory equippedItem)
{ {
_equippedAccessory = equippedItem; _equippedAccessory.OnNext(equippedItem);
} }
public void OnGameEnded() public void OnGameEnded()
@@ -129,6 +135,42 @@ public class GameRepo : IGameRepo
Ended?.Invoke(); Ended?.Invoke();
} }
public bool IsItemEquipped(IEquipable item)
{
if (item is Weapon)
return EquippedWeapon.Value == item;
if (item is Armor)
return EquippedArmor.Value == item;
if (item is Accessory)
return EquippedAccessory.Value == item;
return false;
}
public void EquipItem(IEquipable item)
{
if (item is Weapon weapon)
OnWeaponEquipped(weapon);
if (item is Armor armor)
OnArmorEquipped(armor);
if (item is Accessory accessory)
OnAccessoryEquipped(accessory);
}
public void UnequipItem(IEquipable item)
{
if (item == EquippedWeapon.Value)
OnWeaponEquipped(new Weapon());
if (item == EquippedArmor.Value)
OnArmorEquipped(new Armor());
if (item == EquippedAccessory.Value)
OnAccessoryEquipped(new Accessory());
}
protected void Dispose(bool disposing) protected void Dispose(bool disposing)
{ {
if (!_disposedValue) if (!_disposedValue)

View File

@@ -54,15 +54,65 @@ public partial class InventoryMenu : Control, IInventoryMenu
private int _currentIndex = 0; private int _currentIndex = 0;
private ItemSlot[] ItemSlots => ItemsPage.GetChildren().OfType<ItemSlot>().ToArray(); private IItemSlot[] ItemSlots => ItemsPage.GetChildren().OfType<IItemSlot>().ToArray();
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 EquippedItemFont => GD.Load<LabelSettings>("res://src/ui/label_settings/MainTextFontEquipped.tres");
private static LabelSettings SelectedEquippedItemFont => GD.Load<LabelSettings>("res://src/ui/label_settings/MainTextFontSelectedEquipped.tres");
public void PopulateItems() public void PopulateItems()
{
PopulatePlayerInfo();
PopulateInventory();
}
public void PopulatePlayerInfo()
{
FloorLabel.Text = $"Floor 0";
var currentLevel = GameRepo.PlayerStatInfo.Value.CurrentLevel;
CurrentLevelLabel.Text = $"Level {currentLevel:D2}";
var currentHP = GameRepo.PlayerStatInfo.Value.CurrentHP;
var maxHP = GameRepo.PlayerStatInfo.Value.MaximumHP;
HPValue.Text = $"{currentHP}/{maxHP}";
var currentVT = GameRepo.PlayerStatInfo.Value.CurrentVT;
var maxVT = GameRepo.PlayerStatInfo.Value.MaximumVT;
VTValue.Text = $"{currentVT}/{maxVT}";
var currentAttack = GameRepo.PlayerStatInfo.Value.CurrentAttack;
var maxAttack = GameRepo.PlayerStatInfo.Value.MaxAttack;
ATKValue.Text = $"{currentAttack}/{maxAttack}";
var currentDefense = GameRepo.PlayerStatInfo.Value.CurrentDefense;
var maxDefense = GameRepo.PlayerStatInfo.Value.MaxDefense;
DEFValue.Text = $"{currentDefense}/{maxDefense}";
var atkBonus = GameRepo.PlayerStatInfo.Value.BonusAttack;
var defBonus = GameRepo.PlayerStatInfo.Value.BonusDefense;
ATKBonusLabel.Text = atkBonus != 0 ? $"{atkBonus}" : "...";
DEFBonusLabel.Text = defBonus != 0 ? $"{defBonus}" : "...";
// TODO: Change font style when EXP Bonus effect is active
var currentExp = GameRepo.PlayerStatInfo.Value.CurrentEXP;
var expToNextLevel = GameRepo.PlayerStatInfo.Value.EXPToNextLevel;
EXPValue.Text = $"{currentExp}/{expToNextLevel}";
if (ItemSlots.Any())
{
ItemDescriptionTitle.Text = $"{ItemSlots.ElementAtOrDefault(_currentIndex).Item.Info.Name}";
ItemEffectLabel.Text = $"{ItemSlots.ElementAtOrDefault(_currentIndex).Item.Info.Description}";
}
else
{
ItemDescriptionTitle.Text = string.Empty;
ItemEffectLabel.Text = string.Empty;
}
}
private void PopulateInventory()
{ {
var inventory = GameRepo.InventoryItems.Value; var inventory = GameRepo.InventoryItems.Value;
var numberOfItemsToDisplay = _currentPageNumber == InventoryPageNumber.FirstPage ? Mathf.Min(inventory.Count, _itemsPerPage) : Mathf.Min(inventory.Count - _itemsPerPage, _itemsPerPage); var numberOfItemsToDisplay = _currentPageNumber == InventoryPageNumber.FirstPage ? Mathf.Min(inventory.Count, _itemsPerPage) : Mathf.Min(inventory.Count - _itemsPerPage, _itemsPerPage);
@@ -88,15 +138,19 @@ public partial class InventoryMenu : Control, IInventoryMenu
var item = inventory.ElementAt(i + indexToStart); var item = inventory.ElementAt(i + indexToStart);
var itemScene = GD.Load<PackedScene>(ITEM_SLOT_SCENE); var itemScene = GD.Load<PackedScene>(ITEM_SLOT_SCENE);
var itemSlot = itemScene.Instantiate<IItemSlot>(); var itemSlot = itemScene.Instantiate<IItemSlot>();
itemSlot.Item = item;
ItemsPage.AddChildEx(itemSlot); ItemsPage.AddChildEx(itemSlot);
itemSlot.ItemName.Text = item.Info.Name; if (itemSlot.Item is IEquipable equipable && GameRepo.IsItemEquipped(equipable))
itemSlot.ItemTexture.Texture = item.Info.Texture; itemSlot.SetEquippedItemStyle();
itemSlot.EquipBonus.Text = "...";
} }
if (ItemSlots.Any()) if (ItemSlots.Any())
ItemSlots.ElementAt(_currentIndex).ItemName.LabelSettings = SelectedItemFont; {
ItemSlots.ElementAt(_currentIndex).SetSelectedItemStyle();
if (ItemSlots.ElementAt(_currentIndex).Item is IEquipable equipable && GameRepo.IsItemEquipped(equipable))
ItemSlots.ElementAt(_currentIndex).SetEquippedSelectedItemStyle();
}
} }
public override void _UnhandledInput(InputEvent @event) public override void _UnhandledInput(InputEvent @event)
@@ -120,31 +174,53 @@ public partial class InventoryMenu : Control, IInventoryMenu
if (ItemSlots.Any() && Input.IsActionJustPressed(GameInputs.UiDown)) if (ItemSlots.Any() && Input.IsActionJustPressed(GameInputs.UiDown))
{ {
if (!inventory.Any() || _currentIndex + 1 >= _itemsPerPage) if (!inventory.Any())
return; return;
ItemSlots.ElementAt(_currentIndex).ItemName.LabelSettings = ItemFont; var oldItem = ItemSlots.ElementAt(_currentIndex);
_currentIndex++; ItemSlots.ElementAt(_currentIndex).SetItemStyle();
ItemSlots.ElementAt(_currentIndex).ItemName.LabelSettings = SelectedItemFont; var index = new[] { _currentIndex + 1, _itemsPerPage - 1, ItemSlots.Count() - 1 };
_currentIndex = index.Min();
var newItem = ItemSlots.ElementAt(_currentIndex);
newItem.SetSelectedItemStyle();
if (oldItem.Item is IEquipable equipable && GameRepo.IsItemEquipped(equipable))
oldItem.SetEquippedItemStyle();
if (newItem.Item is IEquipable newEquipable && GameRepo.IsItemEquipped(newEquipable))
newItem.SetEquippedSelectedItemStyle();
} }
if (ItemSlots.Any() && Input.IsActionJustPressed(GameInputs.UiUp)) if (ItemSlots.Any() && Input.IsActionJustPressed(GameInputs.UiUp))
{ {
if (!inventory.Any() || _currentIndex <= 0) if (!inventory.Any())
return; return;
ItemSlots.ElementAt(_currentIndex).ItemName.LabelSettings = ItemFont; var oldItem = ItemSlots.ElementAt(_currentIndex);
_currentIndex--; ItemSlots.ElementAt(_currentIndex).SetItemStyle();
ItemSlots.ElementAt(_currentIndex).ItemName.LabelSettings = SelectedItemFont; var index = new[] { _currentIndex - 1, 0 };
_currentIndex = index.Max();
var newItem = ItemSlots.ElementAt(_currentIndex);
newItem.SetSelectedItemStyle();
if (oldItem.Item is IEquipable equipable && GameRepo.IsItemEquipped(equipable))
oldItem.SetEquippedItemStyle();
if (newItem.Item is IEquipable newEquipable && GameRepo.IsItemEquipped(newEquipable))
newItem.SetEquippedSelectedItemStyle();
} }
if (ItemSlots.Any() && Input.IsActionJustPressed(GameInputs.UiAccept)) if (ItemSlots.Any() && Input.IsActionJustPressed(GameInputs.UiAccept))
{ {
if (_currentPageNumber == InventoryPageNumber.FirstPage) var itemSlot = ItemSlots[_currentIndex];
if (itemSlot.Item is IEquipable equipableItem)
{ {
var item = inventory.ElementAt(_currentIndex); if (GameRepo.IsItemEquipped(equipableItem))
if (item is IEquippable) {
ItemSlots.ElementAt(_currentIndex).ItemName.LabelSettings = EquippedItemFont; GameRepo.UnequipItem(equipableItem);
itemSlot.SetSelectedItemStyle();
}
else
{
GameRepo.EquipItem(equipableItem);
itemSlot.SetEquippedSelectedItemStyle();
}
} }
} }
} }
@@ -153,7 +229,7 @@ public partial class InventoryMenu : Control, IInventoryMenu
{ {
foreach (var item in ItemSlots) foreach (var item in ItemSlots)
{ {
ItemsPage.RemoveChild(item); ItemsPage.RemoveChildEx(item);
} }
} }
} }

View File

@@ -114,7 +114,7 @@ layout_mode = 2
[node name="PlayerInfo" type="VBoxContainer" parent="InventoryInfo/HBoxContainer"] [node name="PlayerInfo" type="VBoxContainer" parent="InventoryInfo/HBoxContainer"]
layout_mode = 2 layout_mode = 2
theme_override_constants/separation = 40 theme_override_constants/separation = 20
[node name="FloorBox" type="HBoxContainer" parent="InventoryInfo/HBoxContainer/PlayerInfo"] [node name="FloorBox" type="HBoxContainer" parent="InventoryInfo/HBoxContainer/PlayerInfo"]
layout_mode = 2 layout_mode = 2

View File

@@ -1,15 +1,20 @@
using Chickensoft.AutoInject; using Chickensoft.AutoInject;
using Chickensoft.GodotNodeInterfaces; using Chickensoft.GodotNodeInterfaces;
using Chickensoft.Introspection; using Chickensoft.Introspection;
using GameJamDungeon;
using Godot; using Godot;
public interface IItemSlot : IHBoxContainer public interface IItemSlot : IHBoxContainer
{ {
public Label EquipBonus { get; } public IInventoryItem Item { get; set; }
public TextureRect ItemTexture { get; } public void SetItemStyle();
public Label ItemName { get; } public void SetSelectedItemStyle();
public void SetEquippedItemStyle();
public void SetEquippedSelectedItemStyle();
} }
[Meta(typeof(IAutoNode))] [Meta(typeof(IAutoNode))]
@@ -17,9 +22,70 @@ public partial class ItemSlot : HBoxContainer, IItemSlot
{ {
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>();
[Node] public Label EquipBonus { get; set; } = default!; [Node] public Label EquipBonus { get; set; } = default!;
[Node] public TextureRect ItemTexture { get; set; } = default!; [Node] public TextureRect ItemTexture { get; set; } = default!;
[Node] public Label ItemName { get; set; } = default!; [Node] public Label ItemName { get; set; } = default!;
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 EquippedItemFont => GD.Load<LabelSettings>("res://src/ui/label_settings/MainTextFontEquipped.tres");
private static LabelSettings SelectedEquippedItemFont => GD.Load<LabelSettings>("res://src/ui/label_settings/MainTextFontSelectedEquipped.tres");
public void OnReady()
{
ItemName.Text = Item.Info.Name;
EquipBonus.Text = "...";
ItemTexture.Texture = Item.Info.Texture;
GameRepo.EquippedWeapon.Sync += EquippedWeapon_Sync;
GameRepo.EquippedArmor.Sync += EquippedArmor_Sync;
GameRepo.EquippedAccessory.Sync += EquippedAccessory_Sync;
}
private void EquippedWeapon_Sync(Weapon obj)
{
if (Item is Weapon unequippedItem && unequippedItem != obj)
SetItemStyle();
}
private void EquippedArmor_Sync(Armor obj)
{
if (Item is Armor unequippedItem && unequippedItem != obj)
SetItemStyle();
}
private void EquippedAccessory_Sync(Accessory obj)
{
if (Item is Accessory unequippedItem && unequippedItem != obj)
SetItemStyle();
}
public void SetItemStyle()
{
ItemName.LabelSettings = ItemFont;
EquipBonus.LabelSettings = ItemFont;
}
public void SetSelectedItemStyle()
{
ItemName.LabelSettings = SelectedItemFont;
EquipBonus.LabelSettings = SelectedItemFont;
}
public void SetEquippedItemStyle()
{
ItemName.LabelSettings = EquippedItemFont;
EquipBonus.LabelSettings = EquippedItemFont;
}
public void SetEquippedSelectedItemStyle()
{
ItemName.LabelSettings = SelectedEquippedItemFont;
EquipBonus.LabelSettings = SelectedEquippedItemFont;
}
public IInventoryItem Item { get; set; } = default!;
} }

View File

@@ -1,4 +1,4 @@
namespace GameJamDungeon namespace GameJamDungeon
{ {
public interface IEquippable; public interface IEquipable;
} }

View File

@@ -5,7 +5,7 @@ using Godot;
using System.Linq; using System.Linq;
[Meta(typeof(IAutoNode))] [Meta(typeof(IAutoNode))]
public partial class Accessory : Node3D, IInventoryItem, IEquippable public partial class Accessory : Node3D, IInventoryItem, IEquipable
{ {
public override void _Notification(int what) => this.Notify(what); public override void _Notification(int what) => this.Notify(what);

View File

@@ -5,7 +5,7 @@ using Godot;
using System.Linq; using System.Linq;
[Meta(typeof(IAutoNode))] [Meta(typeof(IAutoNode))]
public partial class Armor : Node3D, IInventoryItem, IEquippable public partial class Armor : Node3D, IInventoryItem, IEquipable
{ {
public override void _Notification(int what) => this.Notify(what); public override void _Notification(int what) => this.Notify(what);

View File

@@ -5,7 +5,7 @@ using Godot;
using System.Linq; using System.Linq;
[Meta(typeof(IAutoNode))] [Meta(typeof(IAutoNode))]
public partial class Weapon : Node3D, IInventoryItem, IEquippable public partial class Weapon : Node3D, IInventoryItem, IEquipable
{ {
public Weapon() public Weapon()
{ {

View File

@@ -98,6 +98,8 @@ namespace GameJamDungeon
GameRepo.SetPlayerGlobalPosition(GlobalPosition); GameRepo.SetPlayerGlobalPosition(GlobalPosition);
GameRepo.PlayerGlobalPosition.Sync += OnPlayerPositionUpdated; GameRepo.PlayerGlobalPosition.Sync += OnPlayerPositionUpdated;
GameRepo.SetPlayerStatInfo(PlayerStatInfo);
_currentHP = new AutoProp<double>(PlayerStatInfo.MaximumHP); _currentHP = new AutoProp<double>(PlayerStatInfo.MaximumHP);
_currentVT = new AutoProp<int>(PlayerStatInfo.MaximumVT); _currentVT = new AutoProp<int>(PlayerStatInfo.MaximumVT);
_currentHP.Sync += OnHPChanged; _currentHP.Sync += OnHPChanged;
@@ -119,7 +121,7 @@ namespace GameJamDungeon
}) })
.Handle((in PlayerLogic.Output.Animations.Attack output) => .Handle((in PlayerLogic.Output.Animations.Attack output) =>
{ {
var weaponInfo = GameRepo.EquippedWeapon.WeaponInfo; var weaponInfo = GameRepo.EquippedWeapon.Value.WeaponInfo;
var attackSpeed = (float)weaponInfo.AttackSpeed; var attackSpeed = (float)weaponInfo.AttackSpeed;
AnimationPlayer.SetSpeedScale(attackSpeed); AnimationPlayer.SetSpeedScale(attackSpeed);
AnimationPlayer.Play("attack"); AnimationPlayer.Play("attack");
@@ -160,7 +162,7 @@ namespace GameJamDungeon
var roll = rng.Randf(); var roll = rng.Randf();
if (roll <= enemy.EnemyStatInfo.Luck) if (roll <= enemy.EnemyStatInfo.Luck)
isCriticalHit = true; isCriticalHit = true;
var damage = DamageCalculator.CalculateEnemyDamage(hitBox.Damage, PlayerStatInfo, enemy.EnemyStatInfo, GameRepo.EquippedArmor.ArmorInfo, isCriticalHit); var damage = DamageCalculator.CalculateEnemyDamage(hitBox.Damage, PlayerStatInfo, enemy.EnemyStatInfo, GameRepo.EquippedArmor.Value.ArmorInfo, isCriticalHit);
_currentHP.OnNext(_currentHP.Value - damage); _currentHP.OnNext(_currentHP.Value - damage);
GD.Print($"Player hit for {damage} damage."); GD.Print($"Player hit for {damage} damage.");
} }

View File

@@ -15,10 +15,19 @@
[sub_resource type="Resource" id="Resource_up0v1"] [sub_resource type="Resource" id="Resource_up0v1"]
script = ExtResource("2_n88di") script = ExtResource("2_n88di")
CurrentHP = 100.0
MaximumHP = 100.0 MaximumHP = 100.0
CurrentVT = 90
MaximumVT = 90 MaximumVT = 90
BaseAttack = 10 CurrentLevel = 1
BaseDefense = 5 CurrentEXP = 0
EXPToNextLevel = 100
CurrentAttack = 12
MaxAttack = 12
CurrentDefense = 8
MaxDefense = 8
BonusAttack = 0
BonusDefense = 0
[sub_resource type="BoxShape3D" id="BoxShape3D_wedu3"] [sub_resource type="BoxShape3D" id="BoxShape3D_wedu3"]

View File

@@ -1,31 +1,7 @@
using Chickensoft.Collections; namespace GameJamDungeon
using Chickensoft.Serialization;
using Godot;
using System.Collections.Generic;
namespace GameJamDungeon
{ {
public partial record PlayerData public partial record PlayerData
{ {
[Save("global_transform")] // TODO: Implement save system
public required Transform3D GlobalTransform { get; init; }
[Save("state_machine")]
public required PlayerLogic StateMachine { get; init; }
[Save("PlayerEquippedSword")]
public required IAutoProp<WeaponInfo> EquippedWeapon { get; set; }
[Save("PlayerInventory")]
public required InventoryItemInfo[] Inventory { get; set; } = new InventoryItemInfo[5];
[Save("PlayerStats")]
public required PlayerStatInfo PlayerStats { get; set; }
[Save("CurrentHP")]
public required int CurrentHP { get; set; }
[Save("CurrentVT")]
public required int CurrentVT { get; set; }
} }
} }

View File

@@ -1,4 +1,5 @@
using Godot; using Chickensoft.Collections;
using Godot;
namespace GameJamDungeon namespace GameJamDungeon
{ {
@@ -6,15 +7,62 @@ namespace GameJamDungeon
public partial class PlayerStatInfo : Resource, ICharacterStats public partial class PlayerStatInfo : Resource, ICharacterStats
{ {
[Export] [Export]
public double MaximumHP { get; set; } public double CurrentHP { get => _currentHP.Value; set => _currentHP.OnNext(value); }
[Export] [Export]
public int MaximumVT { get; set; } public double MaximumHP { get => _maximumHP.Value; set => _maximumHP.OnNext(value); }
[Export] [Export]
public int BaseAttack { get; set; } public int CurrentVT { get => _currentVT.Value; set => _currentVT.OnNext(value); }
[Export] [Export]
public int BaseDefense { get; set; } public int MaximumVT { get => _maximumVT.Value; set => _maximumVT.OnNext(value); }
[Export]
public int CurrentLevel { get => _currentLevel.Value; set => _currentLevel.OnNext(value); }
[Export]
public int CurrentEXP { get => _currentExp.Value; set => _currentExp.OnNext(value); }
[Export]
public int EXPToNextLevel { get => _expToNextLevel.Value; set => _expToNextLevel.OnNext(value); }
[Export]
public int CurrentAttack { get => _currentAttack.Value; set => _currentAttack.OnNext(value); }
[Export]
public int MaxAttack { get => _maxAttack.Value; set => _maxAttack.OnNext(value); }
[Export]
public int CurrentDefense { get => _currentDefense.Value; set => _currentDefense.OnNext(value); }
[Export]
public int MaxDefense { get => _maxDefense.Value; set => _maxDefense.OnNext(value); }
[Export]
public int BonusAttack { get => _bonusAttack.Value; set => _bonusAttack.OnNext(value); }
[Export]
public int BonusDefense { get => _bonusDefense.Value; set => _bonusDefense.OnNext(value); }
// AutoProp backing data
private readonly AutoProp<double> _currentHP = new AutoProp<double>(0);
private readonly AutoProp<double> _maximumHP = new AutoProp<double>(0);
private readonly AutoProp<int> _currentVT = new AutoProp<int>(0);
private readonly AutoProp<int> _maximumVT = new AutoProp<int>(0);
private readonly AutoProp<int> _currentExp = new AutoProp<int>(0);
private readonly AutoProp<int> _expToNextLevel = new AutoProp<int>(0);
private readonly AutoProp<int> _currentLevel = new AutoProp<int>(0);
private readonly AutoProp<int> _currentAttack = new AutoProp<int>(0);
private readonly AutoProp<int> _currentDefense = new AutoProp<int>(0);
private readonly AutoProp<int> _maxAttack = new AutoProp<int>(0);
private readonly AutoProp<int> _maxDefense = new AutoProp<int>(0);
private readonly AutoProp<int> _bonusAttack = new AutoProp<int>(0);
private readonly AutoProp<int> _bonusDefense = new AutoProp<int>(0);
} }
} }

View File

@@ -6,7 +6,7 @@ namespace GameJamDungeon
{ {
public static double CalculatePlayerDamage(int attackDamage, PlayerStatInfo playerStatInfo, EnemyStatInfo enemyStatInfo, WeaponInfo weapon, bool isCriticalHit) public static double CalculatePlayerDamage(int attackDamage, PlayerStatInfo playerStatInfo, EnemyStatInfo enemyStatInfo, WeaponInfo weapon, bool isCriticalHit)
{ {
var baseDamage = attackDamage + playerStatInfo.BaseAttack; var baseDamage = attackDamage + playerStatInfo.CurrentAttack;
var hydricResistance = enemyStatInfo.HydricResistance; var hydricResistance = enemyStatInfo.HydricResistance;
var igneousResistance = enemyStatInfo.IgneousResistance; var igneousResistance = enemyStatInfo.IgneousResistance;
var telluricResistance = enemyStatInfo.TelluricResistance; var telluricResistance = enemyStatInfo.TelluricResistance;
@@ -28,7 +28,7 @@ namespace GameJamDungeon
var elementDDamage = (weapon.AeolicDamageBonus > 0 ? weapon.AeolicDamageBonus - aeolicResistance : 0) / 100; var elementDDamage = (weapon.AeolicDamageBonus > 0 ? weapon.AeolicDamageBonus - aeolicResistance : 0) / 100;
var elementEDamage = (weapon.FerrumDamageBonus > 0 ? weapon.FerrumDamageBonus - ferrumResistance : 0) / 100; var elementEDamage = (weapon.FerrumDamageBonus > 0 ? weapon.FerrumDamageBonus - ferrumResistance : 0) / 100;
var elementalBonusDamage = baseDamage + (baseDamage * elementADamage) + (baseDamage * elementBDamage) + (baseDamage * elementCDamage) + (baseDamage * elementDDamage) + (baseDamage * elementEDamage); var elementalBonusDamage = baseDamage + (baseDamage * elementADamage) + (baseDamage * elementBDamage) + (baseDamage * elementCDamage) + (baseDamage * elementDDamage) + (baseDamage * elementEDamage);
var calculatedDamage = elementalBonusDamage - enemyStatInfo.BaseDefense; var calculatedDamage = elementalBonusDamage - enemyStatInfo.CurrentDefense;
if (isCriticalHit) if (isCriticalHit)
calculatedDamage *= 2; calculatedDamage *= 2;
@@ -38,14 +38,14 @@ namespace GameJamDungeon
public static double CalculateEnemyDamage(int attackDamage, PlayerStatInfo playerStatInfo, EnemyStatInfo enemyStatInfo, ArmorInfo armor, bool isCriticalHit) public static double CalculateEnemyDamage(int attackDamage, PlayerStatInfo playerStatInfo, EnemyStatInfo enemyStatInfo, ArmorInfo armor, bool isCriticalHit)
{ {
var baseDamage = attackDamage + enemyStatInfo.BaseAttack; var baseDamage = attackDamage + enemyStatInfo.CurrentAttack;
var elementADamage = (enemyStatInfo.BaseHydricDamageBonus > 0 ? enemyStatInfo.BaseHydricDamageBonus - armor.HydricResistance : 0) / 100; var elementADamage = (enemyStatInfo.BaseHydricDamageBonus > 0 ? enemyStatInfo.BaseHydricDamageBonus - armor.HydricResistance : 0) / 100;
var elementBDamage = (enemyStatInfo.IgneousDamageBonus > 0 ? enemyStatInfo.IgneousDamageBonus - armor.IgneousResistance : 0) / 100; var elementBDamage = (enemyStatInfo.IgneousDamageBonus > 0 ? enemyStatInfo.IgneousDamageBonus - armor.IgneousResistance : 0) / 100;
var elementCDamage = (enemyStatInfo.TelluricDamageBonus > 0 ? enemyStatInfo.TelluricDamageBonus - armor.TelluricResistance : 0) / 100; var elementCDamage = (enemyStatInfo.TelluricDamageBonus > 0 ? enemyStatInfo.TelluricDamageBonus - armor.TelluricResistance : 0) / 100;
var elementDDamage = (enemyStatInfo.AeolicDamageBonus > 0 ? enemyStatInfo.AeolicDamageBonus - armor.AeolicResistance : 0) / 100; var elementDDamage = (enemyStatInfo.AeolicDamageBonus > 0 ? enemyStatInfo.AeolicDamageBonus - armor.AeolicResistance : 0) / 100;
var elementEDamage = (enemyStatInfo.FerrumDamageBonus > 0 ? enemyStatInfo.FerrumDamageBonus - armor.FerrumResistance : 0) / 100; var elementEDamage = (enemyStatInfo.FerrumDamageBonus > 0 ? enemyStatInfo.FerrumDamageBonus - armor.FerrumResistance : 0) / 100;
var elementalBonusDamage = baseDamage + (baseDamage * elementADamage) + (baseDamage * elementBDamage) + (baseDamage * elementCDamage) + (baseDamage * elementDDamage) + (baseDamage * elementEDamage); var elementalBonusDamage = baseDamage + (baseDamage * elementADamage) + (baseDamage * elementBDamage) + (baseDamage * elementCDamage) + (baseDamage * elementDDamage) + (baseDamage * elementEDamage);
var calculatedDamage = elementalBonusDamage - playerStatInfo.BaseDefense - (armor != null ? armor.Defense : 0); var calculatedDamage = elementalBonusDamage - playerStatInfo.CurrentDefense - (armor != null ? armor.Defense : 0);
if (isCriticalHit) if (isCriticalHit)
calculatedDamage *= 2; calculatedDamage *= 2;

View File

@@ -1,13 +1,17 @@
using Godot; namespace GameJamDungeon
namespace GameJamDungeon
{ {
public interface ICharacterStats public interface ICharacterStats
{ {
public double CurrentHP { get; }
public double MaximumHP { get; } public double MaximumHP { get; }
public int BaseAttack { get; } public int CurrentAttack { get; }
public int BaseDefense { get; } public int CurrentDefense { get; }
public int MaxAttack { get; }
public int MaxDefense { get; }
} }
} }