Refactor stats

This commit is contained in:
2025-10-22 16:24:07 -07:00
parent 6ec45c4805
commit f0c4e65783
77 changed files with 565 additions and 372 deletions

View File

@@ -0,0 +1,63 @@
using Chickensoft.Collections;
using System;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma;
public class AttackComponent : IAttackComponent
{
public IAutoProp<int> CurrentAttack => _currentAttack;
public IAutoProp<int> MaximumAttack => _maximumAttack;
public IAutoProp<int> BonusAttack => _bonusAttack;
public int TotalAttack => CurrentAttack.Value + BonusAttack.Value;
private readonly AutoProp<int> _currentAttack;
private readonly AutoProp<int> _maximumAttack;
private readonly AutoProp<int> _bonusAttack;
public AttackComponent(int attackValue)
{
_maximumAttack = new AutoProp<int>(attackValue);
_currentAttack = new AutoProp<int>(attackValue);
_bonusAttack = new AutoProp<int>(0);
}
public void Restore(int restoreAmount)
{
var cappedAmount = Math.Min(restoreAmount + _currentAttack.Value, _maximumAttack.Value);
_currentAttack.OnNext(cappedAmount);
}
public void Reduce(int reduceAmount)
{
var cappedAmount = Math.Max(_currentAttack.Value - reduceAmount, 0);
_currentAttack.OnNext(cappedAmount);
}
public void SetAttack(int attack)
{
var cappedAmount = Math.Min(attack, _maximumAttack.Value);
_currentAttack.OnNext(cappedAmount);
}
public void RaiseMaximumAttack(int raiseAmount)
{
_maximumAttack.OnNext(raiseAmount);
Restore(raiseAmount);
}
public void RaiseBonusAttack(int raiseAmount)
{
_bonusAttack.OnNext(_bonusAttack.Value + raiseAmount);
}
public void ResetBonusAttack()
{
_bonusAttack.OnNext(0);
}
}

View File

@@ -0,0 +1 @@
uid://ctyq0v5nsmyv4

View File

@@ -0,0 +1,63 @@
using Chickensoft.Collections;
using System;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma;
public class DefenseComponent : IDefenseComponent
{
public IAutoProp<int> CurrentDefense => _currentDefense;
public IAutoProp<int> MaximumDefense => _maximumDefense;
public IAutoProp<int> BonusDefense => _bonusDefense;
private readonly AutoProp<int> _currentDefense;
private readonly AutoProp<int> _maximumDefense;
private readonly AutoProp<int> _bonusDefense;
public int TotalDefense => CurrentDefense.Value + BonusDefense.Value;
public DefenseComponent(int defenseValue)
{
_maximumDefense = new AutoProp<int>(defenseValue);
_currentDefense = new AutoProp<int>(defenseValue);
_bonusDefense = new AutoProp<int>(0);
}
public void Restore(int restoreAmount)
{
var cappedAmount = Math.Min(restoreAmount + _currentDefense.Value, _maximumDefense.Value);
_currentDefense.OnNext(cappedAmount);
}
public void Reduce(int reduceAmount)
{
var cappedAmount = Math.Max(_currentDefense.Value - reduceAmount, 0);
_currentDefense.OnNext(cappedAmount);
}
public void SetDefense(int attack)
{
var cappedAmount = Math.Min(attack, _maximumDefense.Value);
_currentDefense.OnNext(cappedAmount);
}
public void RaiseMaximumDefense(int raiseAmount)
{
_maximumDefense.OnNext(raiseAmount);
Restore(raiseAmount);
}
public void RaiseBonusDefense(int raiseAmount)
{
_bonusDefense.OnNext(_bonusDefense.Value + raiseAmount);
}
public void ResetBonusDefense()
{
_bonusDefense.OnNext(0);
}
}

View File

@@ -0,0 +1 @@
uid://c0ynebyuj4jwe

View File

@@ -0,0 +1,45 @@
using Chickensoft.Collections;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma;
public class EquipmentComponent : IEquipmentComponent
{
public IAutoProp<EquipableItem> EquippedWeapon => _equippedWeapon;
public IAutoProp<EquipableItem> EquippedArmor => _equippedArmor;
public IAutoProp<EquipableItem> EquippedAccessory => _equippedAccessory;
public AutoProp<EquipableItem> _equippedWeapon;
public AutoProp<EquipableItem> _equippedArmor;
public AutoProp<EquipableItem> _equippedAccessory;
public EquipmentComponent()
{
_equippedWeapon = new AutoProp<EquipableItem>(new Weapon());
_equippedArmor = new AutoProp<EquipableItem>(new Armor());
_equippedAccessory = new AutoProp<EquipableItem>(new Accessory());
}
public void Equip(EquipableItem equipable)
{
if (equipable is Weapon weapon)
_equippedWeapon.OnNext(weapon);
if (equipable is Armor armor)
_equippedArmor.OnNext(armor);
if (equipable is Accessory accessory)
_equippedAccessory.OnNext(accessory);
}
public void Unequip(EquipableItem equipable)
{
if (equipable is Weapon weapon)
_equippedWeapon.OnNext(new Weapon());
if (equipable is Armor armor)
_equippedArmor.OnNext(new Armor());
if (equipable is Accessory accessory)
_equippedAccessory.OnNext(new Accessory());
}
}

View File

@@ -0,0 +1 @@
uid://bntwm4big7gnm

View File

@@ -0,0 +1,56 @@
using Chickensoft.Collections;
using System;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma;
public class ExperiencePointsComponent : IExperiencePointsComponent
{
public IAutoProp<int> CurrentExp => _currentExp;
public IAutoProp<int> ExpToNextLevel => _expToNextLevel;
public IAutoProp<double> ExpGainRate => _expGainRate;
public IAutoProp<int> Level => _level;
private readonly AutoProp<int> _currentExp;
private readonly AutoProp<int> _expToNextLevel;
private readonly AutoProp<double> _expGainRate;
private readonly AutoProp<int> _level;
public ExperiencePointsComponent()
{
var firstLevelExpRequirement = ExpToNextLevelCalculation(1);
_expToNextLevel = new AutoProp<int>(firstLevelExpRequirement);
_currentExp = new AutoProp<int>(0);
_expGainRate = new AutoProp<double>(1.0);
_level = new AutoProp<int>(1);
}
public void Gain(int baseExpGain)
{
var modifiedExpGain = baseExpGain * _expGainRate.Value;
var newCurrentExpTotal = modifiedExpGain + _currentExp.Value;
while (modifiedExpGain + _currentExp.Value >= _expToNextLevel.Value)
LevelUp();
var cappedAmount = Math.Min(baseExpGain + _currentExp.Value, _expToNextLevel.Value);
_currentExp.OnNext(cappedAmount);
}
public void LevelUp()
{
_level.OnNext(_level.Value + 1);
var expToNextLevel = ExpToNextLevelCalculation(_level.Value);
_currentExp.OnNext(_currentExp.Value - _expToNextLevel.Value);
_expToNextLevel.OnNext(expToNextLevel);
}
private int ExpToNextLevelCalculation(int nextLevel)
{
return (int)(6.5 * nextLevel + 4.5 * Math.Pow(nextLevel, 2) + Math.Pow(nextLevel, 3));
}
}

View File

@@ -0,0 +1 @@
uid://dyxfogbt1th04

View File

@@ -0,0 +1,59 @@
using Chickensoft.Collections;
using Chickensoft.Serialization;
using System;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma;
public class HealthComponent : IHealthComponent
{
[Save("current_hp")]
public IAutoProp<int> CurrentHP => _currentHP;
[Save("maximum_hp")]
public IAutoProp<int> MaximumHP => _maximumHP;
private readonly AutoProp<int> _currentHP;
private readonly AutoProp<int> _maximumHP;
public event Action? HealthReachedZero;
public event Action? DamageTaken;
public bool AtFullHealth => CurrentHP.Value == MaximumHP.Value;
public HealthComponent(int initialHP)
{
_maximumHP = new AutoProp<int>(initialHP);
_currentHP = new AutoProp<int>(initialHP);
}
public void Heal(int healAmount)
{
var cappedAmount = Math.Min(healAmount + _currentHP.Value, _maximumHP.Value);
_currentHP.OnNext(cappedAmount);
}
public void Damage(int damageAmount)
{
var cappedAmount = Math.Max(_currentHP.Value - damageAmount, 0);
_currentHP.OnNext(cappedAmount);
if (cappedAmount == 0)
HealthReachedZero?.Invoke();
else
DamageTaken?.Invoke();
}
public void SetHealth(int health)
{
var cappedAmount = Math.Min(health, _maximumHP.Value);
_currentHP.OnNext(cappedAmount);
}
public void RaiseMaximumHP(int raiseAmount)
{
_maximumHP.OnNext(raiseAmount);
Heal(raiseAmount);
}
}

View File

@@ -0,0 +1 @@
uid://c2e62jpounk1h

View File

@@ -0,0 +1,18 @@
using Chickensoft.Collections;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma;
public class LuckComponent : ILuckComponent
{
public IAutoProp<int> Luck => _luck;
private AutoProp<int> _luck;
public LuckComponent(int initialLuck)
{
_luck = new AutoProp<int>(initialLuck);
}
public void SetLuck(int value) => _luck.OnNext(value);
}

View File

@@ -0,0 +1 @@
uid://dfrj2surolauj

View File

@@ -0,0 +1,48 @@
using Chickensoft.Collections;
using System;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma;
public class VTComponent : IVTComponent
{
public IAutoProp<int> CurrentVT => _currentVT;
public IAutoProp<int> MaximumVT => _maximumVT;
private readonly AutoProp<int> _currentVT;
private readonly AutoProp<int> _maximumVT;
public bool AtFullVT => CurrentVT.Value == MaximumVT.Value;
public VTComponent(int initialVT)
{
_maximumVT = new AutoProp<int>(initialVT);
_currentVT = new AutoProp<int>(initialVT);
}
public void Restore(int restoreAmount)
{
var cappedAmount = Math.Min(restoreAmount + _currentVT.Value, _maximumVT.Value);
_currentVT.OnNext(cappedAmount);
}
public void Reduce(int reduceAmount)
{
var cappedAmount = Math.Max(_currentVT.Value - reduceAmount, 0);
_currentVT.OnNext(cappedAmount);
}
public void SetVT(int vt)
{
var cappedAmount = Math.Min(vt, _maximumVT.Value);
_currentVT.OnNext(cappedAmount);
}
public void RaiseMaximumVT(int raiseAmount)
{
_maximumVT.OnNext(raiseAmount);
Restore(raiseAmount);
}
}

View File

@@ -0,0 +1 @@
uid://b0tagp4amvy2v

View File

@@ -1,9 +1,9 @@
using Chickensoft.AutoInject;
using Chickensoft.Introspection;
using Godot;
using System;
using System.Collections.Immutable;
using System.Linq;
using Zennysoft.Game.Implementation.Components;
using Zennysoft.Ma.Adapter;
using Zennysoft.Ma.Adapter.Entity;
@@ -26,11 +26,11 @@ public abstract partial class Enemy : CharacterBody3D, IEnemy, IProvide<IEnemyLo
[Dependency] protected IPlayer _player => this.DependOn(() => GetParent().GetChildren().OfType<IPlayer>().Single());
#endregion
public HealthComponent HealthComponent { get; private set; }
public IHealthComponent HealthComponent { get; private set; }
public AttackComponent AttackComponent { get; private set; }
public IAttackComponent AttackComponent { get; private set; }
public DefenseComponent DefenseComponent { get; private set; }
public IDefenseComponent DefenseComponent { get; private set; }
public virtual IEnemyModelView EnemyModelView { get; set; } = default!;
@@ -118,36 +118,6 @@ public abstract partial class Enemy : CharacterBody3D, IEnemy, IProvide<IEnemyLo
}
public void SetTarget(Vector3 targetPosition) => TargetPosition = targetPosition;
public virtual void SetEnemyPosition(Vector3 newPosition)
{
GlobalPosition = newPosition;
if (this is IHavePatrolBehavior patrolEnemy)
patrolEnemy.PatrolBehavior.HomePosition = GlobalPosition;
_enemyLogic.Input(new EnemyLogic.Input.Reset());
}
public void LookAtTarget(Vector3 targetPosition)
{
var lookDirection = GlobalPosition - targetPosition;
if (lookDirection != GlobalPosition)
LookAt(new Vector3(lookDirection.X, GlobalPosition.Y, lookDirection.Z), Vector3.Up);
}
public virtual void TakeDamage(int damage)
{
GD.Print($"Enemy Hit for {damage} damage.");
HealthComponent.Damage(damage);
}
private void EnemyModelView_HitPlayer(object sender, System.EventArgs e)
{
_player.TakeDamage(new Damage(AttackComponent.TotalAttack, ElementType.None, false, false, false));
}
public virtual void TakeHit()
{
_enemyLogic.Input(new EnemyLogic.Input.Alert());
@@ -181,4 +151,37 @@ public abstract partial class Enemy : CharacterBody3D, IEnemy, IProvide<IEnemyLo
_enemyLogic.Stop();
EnemyBinding.Dispose();
}
public virtual void MoveEnemyToNewRoom(IDungeonRoom newRoom)
{
if (newRoom is MonsterRoom monsterRoom)
{
var spawnPoints = monsterRoom.EnemySpawnPoints.GetChildren().OfType<Marker3D>().ToList();
var spawnPointsGodotCollection = new Godot.Collections.Array<Marker3D>(spawnPoints);
var randomSpawnPoint = spawnPointsGodotCollection.PickRandom();
GlobalPosition = randomSpawnPoint.GlobalPosition;
if (this is IHavePatrolBehavior patrolEnemy)
patrolEnemy.PatrolBehavior.HomePosition = GlobalPosition;
_enemyLogic.Input(new EnemyLogic.Input.Reset());
}
throw new NotImplementedException($"Only {nameof(MonsterRoom)} types are currently supported.");
}
protected void LookAtTarget(Vector3 targetPosition)
{
var lookDirection = GlobalPosition - targetPosition;
if (lookDirection != GlobalPosition)
LookAt(new Vector3(lookDirection.X, GlobalPosition.Y, lookDirection.Z), Vector3.Up);
}
protected void SetTarget(Vector3 targetPosition) => TargetPosition = targetPosition;
private void EnemyModelView_HitPlayer(object sender, System.EventArgs e)
{
_player.TakeDamage(new Damage(AttackComponent.TotalAttack, ElementType.None, false, false, false));
}
}

View File

@@ -18,7 +18,7 @@ public abstract partial class Enemy2D : Enemy
[Node] private EnemyModelView2D _enemyModelView { get; set; } = default!;
public void OnReady()
public void OnEnterTree()
{
LineOfSight.BodyEntered += LineOfSight_BodyEntered;
}

View File

@@ -23,12 +23,16 @@ public partial class FollowBehavior : Node3D, IBehavior
private NavigationAgent3D _navigationAgent;
private Timer _thinkTimer;
public void Init(NavigationAgent3D navigationAgent)
public FollowBehavior()
{
_navigationAgent = navigationAgent;
_thinkTimer = new Timer() { WaitTime = _thinkTime };
_thinkTimer.Timeout += OnTimeout;
AddChild(_thinkTimer);
}
public void Init(NavigationAgent3D navigationAgent)
{
_navigationAgent = navigationAgent;
SetPhysicsProcess(false);
}

View File

@@ -28,11 +28,15 @@ public partial class PatrolBehavior : Node3D, IBehavior
[Signal] public delegate void OnVelocityComputedEventHandler(Vector3 safeVelocity);
public void OnReady()
public PatrolBehavior()
{
_patrolTimer = new Timer() { WaitTime = _patrolTime };
_patrolTimer.Timeout += PatrolTimer_Timeout;
AddChild(_patrolTimer);
}
public void OnReady()
{
SetPhysicsProcess(false);
}

View File

@@ -16,7 +16,7 @@ public partial class Sproingy : Enemy2D, IHavePatrolBehavior, IHaveEngagePlayerB
[Node] public Area3D PlayerDetector { get; set; } = default!;
public new void OnReady()
public void OnReady()
{
FollowBehavior.Init(NavigationAgent);
PatrolBehavior.Init(NavigationAgent);

View File

@@ -81,6 +81,8 @@ shape = SubResource("CylinderShape3D_drfkj")
[node name="EnemyModelView" parent="Visual" instance=ExtResource("4_o3b7p")]
unique_name_in_owner = true
_upperThreshold = 0.5
_lowerThreshold = -0.5
[node name="Components" type="Node3D" parent="."]

View File

@@ -33,46 +33,22 @@ shape = SubResource("CapsuleShape3D_7uhtm")
unique_name_in_owner = true
[node name="Skeleton3D" parent="EnemyModelView/Armature" index="0"]
bones/0/position = Vector3(-0.260271, -1.05324, -1.96773)
bones/0/rotation = Quaternion(0.0915277, -0.692111, -0.0341586, 0.715149)
bones/1/rotation = Quaternion(0.0828172, 0.0642671, -0.39627, 0.91213)
bones/2/rotation = Quaternion(-0.137837, 0.137086, 0.403643, 0.894025)
bones/3/rotation = Quaternion(-0.00338816, 0.00852271, 0.0152662, 0.999842)
bones/4/rotation = Quaternion(0.037164, 0.133882, 0.101977, 0.985036)
bones/5/rotation = Quaternion(-0.0397875, -0.0104688, 0.0235613, 0.998875)
bones/0/position = Vector3(-0.260254, -1.05135, -1.96786)
bones/6/rotation = Quaternion(-0.0474983, -0.294201, -0.744151, 0.597854)
bones/7/rotation = Quaternion(0.0788712, -0.0306685, -0.220772, 0.971647)
bones/8/rotation = Quaternion(-0.127286, 0.0273856, -0.425308, 0.895635)
bones/9/rotation = Quaternion(-0.0931654, 0.0493592, -0.752794, 0.649757)
bones/10/rotation = Quaternion(0.0429966, 0.0102923, 0.363547, 0.930526)
bones/11/rotation = Quaternion(-0.779773, -0.0573165, 0.0817155, 0.618054)
bones/12/rotation = Quaternion(-0.607818, -0.670503, -0.284916, 0.31592)
bones/13/rotation = Quaternion(-0.255941, 0.586097, -0.127235, 0.758153)
bones/14/rotation = Quaternion(-0.513517, -0.227335, -0.228787, 0.795157)
bones/15/rotation = Quaternion(-0.215465, 0.745342, 0.613525, -0.147065)
bones/11/rotation = Quaternion(-0.779814, -0.0573517, 0.0816353, 0.61801)
bones/15/rotation = Quaternion(-0.21544, 0.745303, 0.613567, -0.147118)
bones/16/rotation = Quaternion(-0.486067, -0.16412, -0.362283, 0.778174)
bones/17/rotation = Quaternion(-0.0553629, -0.0361614, 0.62832, 0.77514)
bones/18/rotation = Quaternion(-0.119289, 0.0998131, -0.0173011, 0.987678)
bones/19/position = Vector3(-0.381043, -1.19992, -1.71791)
bones/19/rotation = Quaternion(0.627663, 0.29282, 0.545153, -0.472338)
bones/20/rotation = Quaternion(-0.327815, -0.422723, -0.300673, 0.789581)
bones/21/rotation = Quaternion(-0.0604945, 0.00129838, 0.489705, 0.869786)
bones/22/rotation = Quaternion(0.156218, 0.0483037, -0.624744, 0.763516)
bones/23/rotation = Quaternion(0.123936, -0.00678731, -0.347765, 0.92933)
bones/24/rotation = Quaternion(0.427621, 0.561851, 0.530083, 0.469549)
bones/25/position = Vector3(4.82744, -12.3397, 0.183847)
bones/25/rotation = Quaternion(-0.400051, 0.463947, -0.598439, 0.516317)
bones/26/position = Vector3(-0.0233502, -1.11395, -2.01916)
bones/26/rotation = Quaternion(0.608697, -0.3155, -0.575514, -0.445793)
bones/27/rotation = Quaternion(-0.202236, 0.424675, 0.137941, 0.871622)
bones/28/rotation = Quaternion(-0.0627838, -0.00116445, -0.50126, 0.863015)
bones/29/rotation = Quaternion(0.150998, -0.0515735, 0.668372, 0.726511)
bones/31/position = Vector3(-7.29038, -6.72226, -0.133983)
bones/31/rotation = Quaternion(-0.453784, 0.542292, 0.542291, -0.453784)
bones/32/rotation = Quaternion(0.456756, 0.539878, -0.539587, -0.456893)
bones/19/position = Vector3(-0.379519, -1.19848, -1.72293)
bones/19/rotation = Quaternion(0.627358, 0.293207, 0.545673, -0.471903)
bones/20/rotation = Quaternion(-0.327492, -0.423093, -0.300135, 0.789722)
bones/21/rotation = Quaternion(-0.0604877, 0.00129843, 0.48965, 0.869818)
bones/26/position = Vector3(-0.0278308, -1.11395, -2.01914)
bones/27/rotation = Quaternion(-0.202309, 0.424634, 0.137996, 0.871616)
bones/28/rotation = Quaternion(-0.0627943, -0.00116438, -0.501344, 0.862966)
[node name="BoneAttachment3D" parent="EnemyModelView/Armature/Skeleton3D" index="0"]
transform = Transform3D(-0.266252, -0.0359368, -0.963233, -0.333724, -0.934064, 0.127095, -0.904288, 0.355294, 0.236703, -1.68948, 8.20049, 4.9569)
transform = Transform3D(-0.266252, -0.0359368, -0.963233, -0.333724, -0.934064, 0.127095, -0.904288, 0.355294, 0.236703, -1.68946, 8.20239, 4.95677)
[node name="Collision" type="Area3D" parent="."]
collision_layer = 2048

View File

@@ -11,7 +11,6 @@ using Zennysoft.Game.Abstractions;
using Zennysoft.Ma.Adapter;
using System.IO;
using System.Threading.Tasks;
using Zennysoft.Game.Implementation.Components;
[Meta(typeof(IAutoNode))]
public partial class Game : Node3D, IGame
@@ -95,7 +94,8 @@ public partial class Game : Node3D, IGame
PlayerData = new PlayerData()
{
Inventory = _player.Inventory
Inventory = (Inventory)_player.Inventory,
HealthComponent = (HealthComponent)_player.HealthComponent,
},
MapData = new MapData()
{
@@ -119,7 +119,7 @@ public partial class Game : Node3D, IGame
public void OnResolved()
{
var saveFileManager = _container.GetInstance<IMaSaveFileManager<GameData>>();
var saveFileManager = _container.GetInstance<IMaSaveFileManager>();
SaveFile = new SaveFile<GameData>(
root: GameChunk,
onSave: saveFileManager.Save,
@@ -127,7 +127,7 @@ public partial class Game : Node3D, IGame
{
try
{
var gameData = await saveFileManager.Load();
var gameData = await saveFileManager.Load() as GameData;
return gameData;
}
catch (FileNotFoundException)
@@ -138,7 +138,6 @@ public partial class Game : Node3D, IGame
return null;
}
);
GameBinding = GameState.Bind();
GameBinding
.Handle((in GameState.Output.InitializeGame _) =>
@@ -200,22 +199,22 @@ public partial class Game : Node3D, IGame
.Handle((in GameState.Output.LoadNextFloor _) =>
{
FloorClearMenu.FadeOut();
_map.LoadFloor();
if (_player.EquippedWeapon.Value.ItemTag == ItemTag.BreaksOnChange)
Task.Run(() => _map.LoadFloor());
if (_player.EquipmentComponent.EquippedWeapon.Value.ItemTag == ItemTag.BreaksOnChange)
{
var itemToDestroy = _player.EquippedWeapon.Value;
var itemToDestroy = _player.EquipmentComponent.EquippedWeapon.Value;
_player.Unequip(itemToDestroy);
_player.Inventory.Remove(itemToDestroy);
}
if (_player.EquippedArmor.Value.ItemTag == ItemTag.BreaksOnChange)
if (_player.EquipmentComponent.EquippedArmor.Value.ItemTag == ItemTag.BreaksOnChange)
{
var itemToDestroy = _player.EquippedArmor.Value;
var itemToDestroy = _player.EquipmentComponent.EquippedArmor.Value;
_player.Unequip(itemToDestroy);
_player.Inventory.Remove(itemToDestroy);
}
if (_player.EquippedAccessory.Value.ItemTag == ItemTag.BreaksOnChange)
if (_player.EquipmentComponent.EquippedAccessory.Value.ItemTag == ItemTag.BreaksOnChange)
{
var itemToDestroy = _player.EquippedAccessory.Value;
var itemToDestroy = _player.EquipmentComponent.EquippedAccessory.Value;
_player.Unequip(itemToDestroy);
_player.Inventory.Remove(itemToDestroy);
}
@@ -245,6 +244,7 @@ public partial class Game : Node3D, IGame
_effectService = new EffectService(this, _player, _map);
}
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
public void OnReady()
{
InitializeGame();
@@ -254,22 +254,22 @@ public partial class Game : Node3D, IGame
InGameUI.PlayerInfoUI.Activate();
_player.Activate();
}
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
private void FloorClearMenu_SaveAndExit()
{
//SaveFile.Save();
_player.Deactivate();
GameState.Input(new GameState.Input.ReturnToMainMenu());
InGameUI.Hide();
}
private void FloorClearMenu_GoToNextFloor() => GameState.Input(new GameState.Input.LoadNextFloor());
public void LoadExistingGame() => SaveFile.Load().ContinueWith((_) => CallDeferred(nameof(FinishedLoadingSaveFile)));
public void InitializeGame()
{
_map.InitializeMapData();
_player.InitializePlayerState();
}
public void FloorExitReached()

View File

@@ -0,0 +1,43 @@
using Chickensoft.Introspection;
using Chickensoft.Serialization;
using Zennysoft.Game.Ma;
namespace Zennysoft.Ma.Adapter;
[Meta, Id("game_data")]
public partial record GameData
{
[Save("player_data")]
public required PlayerData PlayerData { get; init; }
[Save("map_data")]
public required MapData MapData { get; init; }
[Save("rescued_items")]
public required RescuedItemDatabase RescuedItems { get; init; }
}
public partial record PlayerData
{
[Save("inventory")]
public required Inventory Inventory { get; init; }
[Save("health_component")]
public required HealthComponent HealthComponent { get; init; }
[Save("vt_component")]
public VTComponent VTComponent { get; init; }
[Save("attack_component")]
public AttackComponent AttackComponent { get; init; }
[Save("defense_component")]
public DefenseComponent DefenseComponent { get; init; }
[Save("experience_points_component")]
public ExperiencePointsComponent ExperiencePointsComponent { get; init; }
[Save("luck_component")]
public LuckComponent LuckComponent { get; init; }
[Save("equipment_component")]
public EquipmentComponent EquipmentComponent { get; init; }
}
public partial record MapData
{
}

View File

@@ -4,8 +4,8 @@ namespace Zennysoft.Game.Ma;
using Chickensoft.AutoInject;
using Chickensoft.GodotNodeInterfaces;
using Chickensoft.SaveFileBuilder;
using Godot;
using System.Threading.Tasks;
using Zennysoft.Game.Implementation;
using Zennysoft.Ma.Adapter;
public interface IGame : IProvide<IGame>, IProvide<IGameRepo>, IProvide<IPlayer>, IProvide<IMap>, IProvide<ISaveChunk<GameData>>, INode3D

View File

@@ -3,6 +3,7 @@ using System.Linq;
using System;
using Zennysoft.Ma.Adapter;
using Zennysoft.Ma.Adapter.Entity;
using Zennysoft.Game.Implementation;
namespace Zennysoft.Game.Ma;
@@ -37,12 +38,7 @@ public class EffectService
var enemyList = validRooms.SelectMany(x => x.GetEnemiesInCurrentRoom());
foreach (var enemy in enemyList)
{
var spawnPoints = currentMonsterRoom.EnemySpawnPoints.GetChildren().OfType<Marker3D>().ToList();
var spawnPointsGodotCollection = new Godot.Collections.Array<Marker3D>(spawnPoints);
var randomSpawnPoint = spawnPointsGodotCollection.PickRandom();
enemy.SetEnemyPosition(randomSpawnPoint.GlobalPosition);
}
enemy.MoveEnemyToNewRoom(currentMonsterRoom);
}
public void KillHalfEnemiesInRoom()
@@ -54,8 +50,8 @@ public class EffectService
var currentMonsterRoom = (MonsterRoom)currentRoom;
var enemyList = currentMonsterRoom.GetEnemiesInCurrentRoom().ToList();
var enemiesToKill = enemyList.Count / 2;
//for (var i = 0; i < enemiesToKill; i++)
// enemyList[i].Die();
for (var i = 0; i < enemiesToKill; i++)
enemyList[i].Die();
}
public void TurnAllEnemiesInRoomIntoHealingItem()
@@ -68,8 +64,8 @@ public class EffectService
var currentEnemies = currentRoom.EnemiesInRoom;
foreach (var enemy in currentEnemies)
{
//enemy.Die();
//DropHealingItem(enemy.GetEnemyGlobalPosition());
enemy.Die();
DropHealingItem(enemy.GlobalPosition);
}
}
@@ -95,9 +91,7 @@ public class EffectService
if (currentRoom is not MonsterRoom)
return;
var currentEnemies = currentRoom.EnemiesInRoom;
//foreach (var enemy in currentEnemies)
// enemy.SetCurrentHP(enemy.GetMaximumHP());
currentRoom.EnemiesInRoom.ForEach(e => e.HealthComponent.SetHealth(e.HealthComponent.MaximumHP.Value));
_player.HealthComponent.SetHealth(_player.HealthComponent.MaximumHP.Value);
}
@@ -110,8 +104,12 @@ public class EffectService
var currentEnemies = currentRoom.EnemiesInRoom;
var hpToAbsorb = 0.0;
//foreach (var enemy in currentEnemies)
// hpToAbsorb += enemy.CurrentHP.Value * 0.05;
foreach (var enemy in currentEnemies)
{
var absorbAmount = enemy.HealthComponent.MaximumHP.Value * 0.05;
enemy.HealthComponent.Damage((int)absorbAmount);
hpToAbsorb += absorbAmount;
}
_player.HealthComponent.Heal((int)hpToAbsorb);
GD.Print("HP to absorb: " + hpToAbsorb);
}
@@ -127,7 +125,7 @@ public class EffectService
foreach (var enemy in currentEnemies)
{
var damageDealt = DamageCalculator.CalculateDamage(new Damage(20, elementType, false, false, false), 10, new ElementalResistanceSet(0, 0, 0, 0, 0));
enemy.TakeDamage(damageDealt);
enemy.HealthComponent.Damage(damageDealt);
}
}
@@ -152,28 +150,25 @@ public class EffectService
public void RaiseCurrentWeaponAttack()
{
if (string.IsNullOrEmpty(_player.EquippedWeapon.Value.ItemName))
if (string.IsNullOrEmpty(_player.EquipmentComponent.EquippedWeapon.Value.ItemName))
return;
var currentWeapon = (Weapon)_player.EquippedWeapon.Value;
var currentWeapon = (Weapon)_player.EquipmentComponent.EquippedWeapon.Value;
currentWeapon.IncreaseWeaponAttack(1);
//_player.ModifyBonusAttack(1);
_player.AttackComponent.RaiseBonusAttack(1);
}
public void RaiseCurrentArmorDefense()
{
if (string.IsNullOrEmpty(_player.EquippedArmor.Value.ItemName))
if (string.IsNullOrEmpty(_player.EquipmentComponent.EquippedArmor.Value.ItemName))
return;
var currentArmor = (Armor)_player.EquippedArmor.Value;
var currentArmor = (Armor)_player.EquipmentComponent.EquippedArmor.Value;
currentArmor.IncreaseArmorDefense(1);
_player.DefenseComponent.RaiseBonusDefense(1);
}
public void RaiseLevel()
{
_player.LevelUp();
}
public void RaiseLevel() => _player.LevelUp();
public void TeleportToRandomRoom(IEnemy enemy)
{
@@ -189,11 +184,8 @@ public class EffectService
var roomsGodotCollection = new Godot.Collections.Array<MonsterRoom>(validRooms);
var randomRoom = roomsGodotCollection.PickRandom();
var spawnPoints = randomRoom.EnemySpawnPoints.GetChildren().OfType<Marker3D>().ToList();
var spawnPointsGodotCollection = new Godot.Collections.Array<Marker3D>(spawnPoints);
var randomSpawnPoint = spawnPointsGodotCollection.PickRandom();
enemy.SetEnemyPosition(randomSpawnPoint.GlobalPosition);
enemy.MoveEnemyToNewRoom(randomRoom);
}
public void TeleportToRandomRoom(IPlayer player)

View File

@@ -5,6 +5,7 @@ using Godot;
using System.Collections.Generic;
using System.Linq;
using Zennysoft.Game.Abstractions;
using Zennysoft.Game.Implementation;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma;

View File

@@ -1,6 +1,7 @@
using Chickensoft.Introspection;
using Chickensoft.Serialization;
using Godot;
using Zennysoft.Game.Implementation;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma;

View File

@@ -1,6 +1,7 @@
using Chickensoft.Introspection;
using Chickensoft.Serialization;
using Godot;
using Zennysoft.Game.Implementation;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma;

View File

@@ -2,6 +2,7 @@
using Chickensoft.GodotNodeInterfaces;
using Chickensoft.Introspection;
using Godot;
using Zennysoft.Game.Implementation;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma;

View File

@@ -1,6 +1,7 @@
using Chickensoft.Introspection;
using Chickensoft.Serialization;
using Godot;
using Zennysoft.Game.Implementation;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma;

View File

@@ -2,7 +2,6 @@ using Chickensoft.AutoInject;
using Chickensoft.Introspection;
using Godot;
using Zennysoft.Game.Abstractions;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma;

View File

@@ -3,6 +3,7 @@ using Chickensoft.Introspection;
using Chickensoft.Serialization;
using Godot;
using Zennysoft.Game.Abstractions;
using Zennysoft.Game.Implementation;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma;

View File

@@ -1,6 +1,7 @@
using Chickensoft.Introspection;
using Chickensoft.Serialization;
using Godot;
using Zennysoft.Game.Implementation;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma;

View File

@@ -1,6 +1,7 @@
using Chickensoft.AutoInject;
using Chickensoft.Introspection;
using Godot;
using Zennysoft.Game.Implementation;
using Zennysoft.Ma.Adapter;
using Zennysoft.Ma.Adapter.Entity;
@@ -108,22 +109,22 @@ public partial class ThrownItem : RigidBody3D
{
switch (throwableItem.ThrowableItemTag)
{
//case ThrowableItemTag.LowerTargetTo1HP:
// enemy.TakeDamage(enemy.CurrentHP.Value - 1, ignoreDefense: true, ignoreElementalResistance: true);
// break;
case ThrowableItemTag.LowerTargetTo1HP:
enemy.HealthComponent.SetHealth(1);
break;
case ThrowableItemTag.TeleportToRandomLocation:
_effectService.TeleportToRandomRoom(enemy);
break;
default:
var damageDealt = DamageCalculator.CalculateDamage(new Damage(throwableItem.ThrowDamage, throwableItem.ElementType, false, false, false), 10, new ElementalResistanceSet(0, 0, 0, 0, 0));
enemy.TakeDamage(damageDealt);
enemy.HealthComponent.Damage(damageDealt);
break;
}
}
else
{
var damageDealt = DamageCalculator.CalculateDamage(new Damage(ItemThatIsThrown.ThrowDamage, ElementType.None, false, false, false), 10, new ElementalResistanceSet(0, 0, 0, 0, 0));
enemy.TakeDamage(damageDealt);
enemy.HealthComponent.Damage(damageDealt);
}
}
}

View File

@@ -1,6 +1,7 @@
using Chickensoft.Introspection;
using Chickensoft.Serialization;
using Godot;
using Zennysoft.Game.Implementation;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma;

View File

@@ -3,9 +3,7 @@ using Chickensoft.Collections;
using Chickensoft.GodotNodeInterfaces;
using Chickensoft.SaveFileBuilder;
using Godot;
using System.Collections.Immutable;
using System.Threading.Tasks;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma;

View File

@@ -2,6 +2,7 @@ using Chickensoft.AutoInject;
using Chickensoft.Introspection;
using Godot;
using System.Linq;
using Zennysoft.Game.Implementation;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma;

View File

@@ -1,12 +1,10 @@
using Chickensoft.AutoInject;
using Chickensoft.Collections;
using Chickensoft.GodotNodeInterfaces;
using Chickensoft.Introspection;
using Chickensoft.SaveFileBuilder;
using Godot;
using SimpleInjector;
using System;
using Zennysoft.Game.Implementation.Components;
using Zennysoft.Ma.Adapter;
using Zennysoft.Ma.Adapter.Entity;
@@ -27,17 +25,19 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<ISaveChunk<Play
public ISaveChunk<PlayerData> PlayerChunk { get; set; } = default!;
#endregion
public HealthComponent HealthComponent { get; private set; }
public IHealthComponent HealthComponent { get; private set; }
public VTComponent VTComponent { get; private set; }
public IVTComponent VTComponent { get; private set; }
public AttackComponent AttackComponent { get; private set; }
public IAttackComponent AttackComponent { get; private set; }
public DefenseComponent DefenseComponent { get; private set; }
public IDefenseComponent DefenseComponent { get; private set; }
public ExperiencePointsComponent ExperiencePointsComponent { get; private set; }
public IExperiencePointsComponent ExperiencePointsComponent { get; private set; }
public LuckComponent LuckComponent { get; private set; }
public ILuckComponent LuckComponent { get; private set; }
public IEquipmentComponent EquipmentComponent { get; private set; }
public Vector3 CurrentPosition => GlobalPosition;
@@ -45,15 +45,6 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<ISaveChunk<Play
public IInventory Inventory { get; private set; } = default!;
public AutoProp<EquipableItem> EquippedWeapon => _equippedWeapon;
private AutoProp<EquipableItem> _equippedWeapon { get; set; } = new AutoProp<EquipableItem>(new Weapon());
public AutoProp<EquipableItem> EquippedArmor => _equippedArmor;
private AutoProp<EquipableItem> _equippedArmor { get; set; } = new AutoProp<EquipableItem>(new Armor());
public AutoProp<EquipableItem> EquippedAccessory => _equippedAccessory;
private AutoProp<EquipableItem> _equippedAccessory { get; set; } = new AutoProp<EquipableItem>(new Accessory());
private PlayerLogic.Settings Settings { get; set; } = default!;
private IPlayerLogic PlayerLogic { get; set; } = default!;
@@ -119,14 +110,16 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<ISaveChunk<Play
private Vector3 _knockbackDirection = Vector3.Zero;
#region Initialization
public void InitializePlayerState()
public Player()
{
Inventory = new Inventory();
SetProcessInput(false);
SetPhysicsProcess(false);
HealthTimer.WaitTime = _healthTimerWaitTime;
HealthTimer.Timeout += OnHealthTimerTimeout;
HealthComponent = new HealthComponent(InitialHP);
VTComponent = new VTComponent(InitialVT);
AttackComponent = new AttackComponent(InitialAttack);
DefenseComponent = new DefenseComponent(InitialDefense);
ExperiencePointsComponent = new ExperiencePointsComponent();
LuckComponent = new LuckComponent(InitialLuck);
EquipmentComponent = new EquipmentComponent();
}
public void Setup()
@@ -138,9 +131,6 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<ISaveChunk<Play
PlayerLogic.Set(this as IPlayer);
PlayerLogic.Set(Settings);
PlayerLogic.Set(_gameRepo);
Hitbox.AreaEntered += Hitbox_AreaEntered;
CollisionDetector.AreaEntered += CollisionDetector_AreaEntered;
}
public void OnResolved()
@@ -150,11 +140,13 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<ISaveChunk<Play
PlayerChunk = new SaveChunk<PlayerData>(
onSave: (chunk) => new PlayerData()
{
Inventory = Inventory
Inventory = (Inventory)Inventory,
HealthComponent = (HealthComponent)HealthComponent
},
onLoad: (chunk, data) =>
{
Inventory = data.Inventory;
HealthComponent = data.HealthComponent;
}
);
@@ -173,22 +165,18 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<ISaveChunk<Play
PlayerLogic.Start();
this.Provide();
SetProcessInput(false);
SetPhysicsProcess(false);
}
public void OnReady()
{
Hitbox.AreaEntered += Hitbox_AreaEntered;
CollisionDetector.AreaEntered += CollisionDetector_AreaEntered;
SwordSlashAnimation.Position = GetViewport().GetVisibleRect().Size / 2;
HealthComponent = new HealthComponent(InitialHP);
HealthComponent.HealthReachedZero += Die;
VTComponent = new VTComponent(InitialVT);
AttackComponent = new AttackComponent(InitialAttack);
DefenseComponent = new DefenseComponent(InitialDefense);
ExperiencePointsComponent = new ExperiencePointsComponent();
LuckComponent = new LuckComponent(InitialLuck);
HealthTimer.WaitTime = _healthTimerWaitTime;
HealthTimer.Timeout += OnHealthTimerTimeout;
SetProcessInput(false);
SetPhysicsProcess(false);
}
#endregion
@@ -206,20 +194,7 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<ISaveChunk<Play
SetHealthTimerStatus(false);
}
public void Attack()
{
if (PlayerIsHittingGeometry())
{
AnimationPlayer.Play("hit_wall");
_gameRepo.OnPlayerAttackedWall();
}
else
{
PlayAttackAnimation();
}
}
public void SetHealthTimerStatus(bool isActive)
private void SetHealthTimerStatus(bool isActive)
{
if (isActive)
HealthTimer.Start();
@@ -227,22 +202,6 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<ISaveChunk<Play
HealthTimer.Stop();
}
public void Move(float delta)
{
var rawInput = GlobalInputVector;
var strafeLeftInput = LeftStrafeInputVector;
var strafeRightInput = RightStrafeInputVector;
var transform = Transform;
transform.Basis = new Basis(Vector3.Up, Settings.RotationSpeed * -rawInput.X * delta) * transform.Basis;
var moveDirection = new Vector3(strafeRightInput - strafeLeftInput, 0, rawInput.Z).Normalized();
var velocity = Basis * moveDirection * Settings.MoveSpeed * Settings.Acceleration;
_knockbackStrength *= 0.9f;
Transform = Transform with { Basis = transform.Basis };
Velocity = velocity + (_knockbackDirection * _knockbackStrength);
MoveAndSlide();
}
public void TeleportPlayer(Transform3D newTransform)
{
Transform = newTransform;
@@ -250,7 +209,7 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<ISaveChunk<Play
public void TakeDamage(Damage damage)
{
var damageReceived = DamageCalculator.CalculateDamage(damage, DefenseComponent.TotalDefense, ((Armor)_equippedArmor.Value).Stats.ElementalResistanceSet);
var damageReceived = DamageCalculator.CalculateDamage(damage, DefenseComponent.TotalDefense, ((Armor)EquipmentComponent.EquippedArmor.Value).Stats.ElementalResistanceSet);
HealthComponent.Damage(damageReceived);
}
@@ -306,21 +265,21 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<ISaveChunk<Play
if (equipable is Weapon weapon)
{
Unequip(_equippedWeapon.Value);
Unequip(EquipmentComponent.EquippedWeapon.Value);
weapon.IsEquipped = true;
_equippedWeapon.OnNext(weapon);
EquipmentComponent.Equip(weapon);
}
else if (equipable is Armor armor)
{
Unequip(_equippedArmor.Value);
Unequip(EquipmentComponent.EquippedArmor.Value);
armor.IsEquipped = true;
_equippedArmor.OnNext(armor);
EquipmentComponent.Equip(armor);
}
else if (equipable is Accessory accessory)
{
Unequip(_equippedAccessory.Value);
Unequip(EquipmentComponent.EquippedAccessory.Value);
accessory.IsEquipped = true;
_equippedAccessory.OnNext(accessory);
EquipmentComponent.Equip(accessory);
}
else
throw new NotImplementedException("Item type is not supported.");
@@ -331,17 +290,17 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<ISaveChunk<Play
if (equipable is Weapon weapon)
{
weapon.IsEquipped = false;
_equippedWeapon.OnNext(new Weapon());
EquipmentComponent.Unequip(weapon);
}
else if (equipable is Armor armor)
{
armor.IsEquipped = false;
_equippedArmor.OnNext(new Armor());
EquipmentComponent.Unequip(armor);
}
else if (equipable is Accessory accessory)
{
accessory.IsEquipped = false;
_equippedAccessory.OnNext(new Accessory());
EquipmentComponent.Unequip(accessory);
}
else
throw new NotImplementedException("Item type is not supported.");
@@ -365,6 +324,19 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<ISaveChunk<Play
private static float RightStrafeInputVector => Input.GetActionStrength(GameInputs.StrafeRight);
private void Attack()
{
if (PlayerIsHittingGeometry())
{
AnimationPlayer.Play("hit_wall");
_gameRepo.OnPlayerAttackedWall();
}
else
{
PlayAttackAnimation();
}
}
private void ThrowItem()
{
var itemScene = GD.Load<PackedScene>("res://src/items/throwable/ThrowableItem.tscn");
@@ -376,7 +348,7 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<ISaveChunk<Play
private void PlayAttackAnimation()
{
var attackSpeed = ((Weapon)EquippedWeapon.Value).AttackSpeed;
var attackSpeed = ((Weapon)EquipmentComponent.EquippedWeapon.Value).AttackSpeed;
AnimationPlayer.SetSpeedScale((float)attackSpeed);
AnimationPlayer.Play("attack");
_gameRepo.OnPlayerAttack();
@@ -388,13 +360,29 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<ISaveChunk<Play
PlayerBinding.Dispose();
}
private void Move(float delta)
{
var rawInput = GlobalInputVector;
var strafeLeftInput = LeftStrafeInputVector;
var strafeRightInput = RightStrafeInputVector;
var transform = Transform;
transform.Basis = new Basis(Vector3.Up, Settings.RotationSpeed * -rawInput.X * delta) * transform.Basis;
var moveDirection = new Vector3(strafeRightInput - strafeLeftInput, 0, rawInput.Z).Normalized();
var velocity = Basis * moveDirection * Settings.MoveSpeed * Settings.Acceleration;
_knockbackStrength *= 0.9f;
Transform = Transform with { Basis = transform.Basis };
Velocity = velocity + (_knockbackDirection * _knockbackStrength);
MoveAndSlide();
}
private void OnPlayerPositionUpdated(Vector3 globalPosition) => GlobalPosition = globalPosition;
private void OnHealthTimerTimeout()
{
if (VTComponent.CurrentVT.Value > 0)
{
if (((Accessory)EquippedAccessory.Value).AccessoryTag == AccessoryTag.HalfVTConsumption)
if (((Accessory)EquipmentComponent.EquippedAccessory.Value).AccessoryTag == AccessoryTag.HalfVTConsumption)
reduceOnTick = !reduceOnTick;
HealthComponent.Heal(1);
@@ -415,21 +403,21 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<ISaveChunk<Play
private void HitEnemy(IEnemy enemy)
{
var ignoreElementalResistance = ((Weapon)EquippedWeapon.Value).WeaponTag == WeaponTag.IgnoreAffinity;
var ignoreElementalResistance = ((Weapon)EquipmentComponent.EquippedWeapon.Value).WeaponTag == WeaponTag.IgnoreAffinity;
var isCriticalHit = BattleExtensions.IsCriticalHit(LuckComponent.Luck.Value);
var element = ((Weapon)EquippedWeapon.Value).WeaponElement;
var element = ((Weapon)EquipmentComponent.EquippedWeapon.Value).WeaponElement;
var baseAttack = new Damage(
(int)(AttackComponent.TotalAttack * ((Weapon)EquippedWeapon.Value).ElementalDamageBonus),
(int)(AttackComponent.TotalAttack * ((Weapon)EquipmentComponent.EquippedWeapon.Value).ElementalDamageBonus),
element,
false,
false,
ignoreElementalResistance);
var damageDealt = DamageCalculator.CalculateDamage(baseAttack, enemy.DefenseComponent.TotalDefense, new ElementalResistanceSet(0, 0, 0, 0, 0));
enemy.TakeDamage(damageDealt);
enemy.HealthComponent.Damage(damageDealt);
if (((Weapon)EquippedWeapon.Value).WeaponTag == WeaponTag.Knockback && enemy is IKnockbackable knockbackable)
if (((Weapon)EquipmentComponent.EquippedWeapon.Value).WeaponTag == WeaponTag.Knockback && enemy is IKnockbackable knockbackable)
knockbackable.Knockback(0.3f, -CurrentBasis.Z.Normalized());
if (((Weapon)EquippedWeapon.Value).WeaponTag == WeaponTag.SelfDamage)
if (((Weapon)EquipmentComponent.EquippedWeapon.Value).WeaponTag == WeaponTag.SelfDamage)
TakeDamage(new Damage(5, ElementType.None, false, true, true));
_gameRepo.OnPlayerAttackedEnemy();

View File

@@ -1,19 +0,0 @@
using Chickensoft.Collections;
using Zennysoft.Game.Implementation.Components;
namespace Zennysoft.Game.Ma;
public interface ICharacterStats
{
public HealthComponent HP { get; }
public IAutoProp<int> CurrentAttack { get; }
public IAutoProp<int> CurrentDefense { get; }
public IAutoProp<int> MaxAttack { get; }
public IAutoProp<int> MaxDefense { get; }
public IAutoProp<double> Luck { get; }
}

View File

@@ -0,0 +1,18 @@
using Chickensoft.GodotNodeInterfaces;
using Zennysoft.Game.Implementation;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma;
public interface IItemSlot : IHBoxContainer
{
public InventoryItem Item { get; set; }
public void SetItemStyle();
public void SetSelectedItemStyle();
public void SetEquippedItemStyle();
public void SetEquippedSelectedItemStyle();
}

View File

@@ -0,0 +1 @@
uid://cyit2s4ahwfqf

View File

@@ -4,6 +4,7 @@ using Chickensoft.Introspection;
using Godot;
using System.Linq;
using System.Threading.Tasks;
using Zennysoft.Game.Implementation;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma;
@@ -344,7 +345,7 @@ public partial class InventoryMenu : Control, IInventoryMenu
if (equipableItem.IsEquipped)
{
ItemEffectLabel.Text = $"{itemSlot.Item.GetType()} unequipped.";
Player.Unequip(equipableItem);
Player.EquipmentComponent.Unequip(equipableItem);
itemSlot.SetSelectedItemStyle();
if (equipableItem.ItemTag == ItemTag.BreaksOnChange)
Player.Inventory.Remove(equipableItem);
@@ -352,7 +353,7 @@ public partial class InventoryMenu : Control, IInventoryMenu
else
{
ItemEffectLabel.Text = $"{itemSlot.Item.GetType()} equipped.";
Player.Equip(equipableItem);
Player.EquipmentComponent.Equip(equipableItem);
itemSlot.SetEquippedSelectedItemStyle();
}

View File

@@ -1,25 +1,12 @@
using Chickensoft.AutoInject;
using Chickensoft.GodotNodeInterfaces;
using Chickensoft.Introspection;
using Godot;
using Zennysoft.Game.Abstractions;
using Zennysoft.Game.Implementation;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma;
public interface IItemSlot : IHBoxContainer
{
public InventoryItem Item { get; set; }
public void SetItemStyle();
public void SetSelectedItemStyle();
public void SetEquippedItemStyle();
public void SetEquippedSelectedItemStyle();
}
[Meta(typeof(IAutoNode))]
public partial class ItemSlot : HBoxContainer, IItemSlot
{
@@ -27,8 +14,6 @@ public partial class ItemSlot : HBoxContainer, IItemSlot
[Dependency] public IPlayer Player => this.DependOn<IPlayer>();
//[Node] public Label EquipBonus { get; set; } = default!;
[Node] public TextureRect ItemTexture { get; set; } = default!;
[Node] public Label ItemName { get; set; } = default!;
@@ -36,7 +21,9 @@ public partial class ItemSlot : HBoxContainer, IItemSlot
[Node] public Label ItemCount { get; set; } = default!;
private static LabelSettings ItemFont => GD.Load<LabelSettings>("res://src/ui/label_settings/MainTextBold.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");
@@ -45,9 +32,9 @@ public partial class ItemSlot : HBoxContainer, IItemSlot
{
ItemName.Text = Item.ItemName;
ItemTexture.Texture = Item.GetTexture();
Player.EquippedWeapon.Sync += EquipableItem_Sync;
Player.EquippedArmor.Sync += EquipableItem_Sync;
Player.EquippedAccessory.Sync += EquipableItem_Sync;
Player.EquipmentComponent.EquippedWeapon.Sync += EquipableItem_Sync;
Player.EquipmentComponent.EquippedArmor.Sync += EquipableItem_Sync;
Player.EquipmentComponent.EquippedAccessory.Sync += EquipableItem_Sync;
if (Item is IStackable stackableItem)
{

View File

@@ -4,6 +4,7 @@ using Godot;
using System;
using System.Collections.Immutable;
using System.Linq;
using Zennysoft.Game.Implementation;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma;