Figured out the math for EXP per level

This commit is contained in:
2025-10-01 01:46:27 -07:00
parent 6ee1788337
commit 3bcb747473
2 changed files with 343 additions and 357 deletions

View File

@@ -8,8 +8,11 @@
<ItemGroup> <ItemGroup>
<Compile Remove="Game\state\states\**" /> <Compile Remove="Game\state\states\**" />
<Compile Remove="Map\**" />
<EmbeddedResource Remove="Game\state\states\**" /> <EmbeddedResource Remove="Game\state\states\**" />
<EmbeddedResource Remove="Map\**" />
<None Remove="Game\state\states\**" /> <None Remove="Game\state\states\**" />
<None Remove="Map\**" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -28,8 +31,4 @@
<ProjectReference Include="..\Zennysoft.Game.Godot.Implementation\Zennysoft.Game.Implementation.csproj" /> <ProjectReference Include="..\Zennysoft.Game.Godot.Implementation\Zennysoft.Game.Implementation.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="Map\" />
</ItemGroup>
</Project> </Project>

View File

@@ -4,7 +4,6 @@ using Chickensoft.GodotNodeInterfaces;
using Chickensoft.Introspection; using Chickensoft.Introspection;
using Chickensoft.SaveFileBuilder; using Chickensoft.SaveFileBuilder;
using Godot; using Godot;
using Godot.Collections;
using SimpleInjector; using SimpleInjector;
using System; using System;
using Zennysoft.Ma.Adapter; using Zennysoft.Ma.Adapter;
@@ -89,564 +88,552 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<ISaveChunk<Play
private float _knockbackStrength = 0.0f; private float _knockbackStrength = 0.0f;
private Vector3 _knockbackDirection = Vector3.Zero; private Vector3 _knockbackDirection = Vector3.Zero;
private Dictionary<int, int> _expToNextLevel;
private DamageCalculator _damageCalculator; private DamageCalculator _damageCalculator;
#region Initialization #region Initialization
public void InitializePlayerState() public void InitializePlayerState()
{ {
Inventory = new Inventory(); Inventory = new Inventory();
Stats = InitializePlayerStats(); Stats = InitializePlayerStats();
SetProcessInput(false); SetProcessInput(false);
SetPhysicsProcess(false); SetPhysicsProcess(false);
EquippedWeapon.Changed += EquippedWeapon_Sync; EquippedWeapon.Changed += EquippedWeapon_Sync;
EquippedArmor.Changed += EquippedArmor_Sync; EquippedArmor.Changed += EquippedArmor_Sync;
EquippedAccessory.Changed += EquippedAccessory_Sync; EquippedAccessory.Changed += EquippedAccessory_Sync;
Stats.CurrentHP.Changed += CurrentHP_Sync; Stats.CurrentHP.Changed += CurrentHP_Sync;
Stats.CurrentExp.Changed += CurrentEXP_Sync; Stats.CurrentExp.Changed += CurrentEXP_Sync;
HealthTimer.WaitTime = _healthTimerWaitTime; HealthTimer.WaitTime = _healthTimerWaitTime;
HealthTimer.Timeout += OnHealthTimerTimeout; HealthTimer.Timeout += OnHealthTimerTimeout;
} }
public void Setup() public void Setup()
{ {
var container = new SimpleInjector.Container(); var container = new SimpleInjector.Container();
container.Register<IPlayerLogic, PlayerLogic>(Lifestyle.Singleton); container.Register<IPlayerLogic, PlayerLogic>(Lifestyle.Singleton);
//container.Verify(); //container.Verify();
PlayerLogic = container.GetInstance<IPlayerLogic>(); PlayerLogic = container.GetInstance<IPlayerLogic>();
PlayerLogic.Set(this as IPlayer); PlayerLogic.Set(this as IPlayer);
PlayerLogic.Set(Settings); PlayerLogic.Set(Settings);
PlayerLogic.Set(Stats); PlayerLogic.Set(Stats);
PlayerLogic.Set(_gameRepo); PlayerLogic.Set(_gameRepo);
_expToNextLevel = new Dictionary<int, int> _damageCalculator = new DamageCalculator();
{
{ 2, 12 },
{ 3, 39 },
{ 4, 87 },
{ 5, 162 },
{ 6, 270 },
{ 7, 417 },
{ 8, 609 }
};
_damageCalculator = new DamageCalculator();
Hitbox.AreaEntered += Hitbox_AreaEntered; Hitbox.AreaEntered += Hitbox_AreaEntered;
CollisionDetector.AreaEntered += CollisionDetector_AreaEntered; CollisionDetector.AreaEntered += CollisionDetector_AreaEntered;
AnimationPlayer.AnimationFinished += OnAnimationFinished; AnimationPlayer.AnimationFinished += OnAnimationFinished;
} }
public void OnResolved() public void OnResolved()
{ {
Settings = new PlayerLogic.Settings() { RotationSpeed = _playerStatResource.RotationSpeed, MoveSpeed = _playerStatResource.MoveSpeed, Acceleration = _playerStatResource.Acceleration }; Settings = new PlayerLogic.Settings() { RotationSpeed = _playerStatResource.RotationSpeed, MoveSpeed = _playerStatResource.MoveSpeed, Acceleration = _playerStatResource.Acceleration };
PlayerChunk = new SaveChunk<PlayerData>( PlayerChunk = new SaveChunk<PlayerData>(
onSave: (chunk) => new PlayerData() onSave: (chunk) => new PlayerData()
{ {
PlayerStats = Stats, PlayerStats = Stats,
Inventory = Inventory Inventory = Inventory
}, },
onLoad: (chunk, data) => onLoad: (chunk, data) =>
{ {
Stats = new PlayerStats( Stats = new PlayerStats(
data.PlayerStats.CurrentHP, data.PlayerStats.CurrentHP,
data.PlayerStats.MaximumHP, data.PlayerStats.MaximumHP,
data.PlayerStats.CurrentVT, data.PlayerStats.CurrentVT,
data.PlayerStats.MaximumVT, data.PlayerStats.MaximumVT,
data.PlayerStats.CurrentAttack, data.PlayerStats.CurrentAttack,
data.PlayerStats.BonusAttack, data.PlayerStats.BonusAttack,
data.PlayerStats.MaxAttack, data.PlayerStats.MaxAttack,
data.PlayerStats.CurrentDefense, data.PlayerStats.CurrentDefense,
data.PlayerStats.BonusDefense, data.PlayerStats.BonusDefense,
data.PlayerStats.MaxDefense, data.PlayerStats.MaxDefense,
data.PlayerStats.CurrentExp, data.PlayerStats.CurrentExp,
data.PlayerStats.CurrentLevel, data.PlayerStats.CurrentLevel,
data.PlayerStats.ExpToNextLevel, data.PlayerStats.ExpToNextLevel,
data.PlayerStats.Luck); data.PlayerStats.Luck);
Inventory = data.Inventory; Inventory = data.Inventory;
} }
); );
PlayerBinding = PlayerLogic.Bind(); PlayerBinding = PlayerLogic.Bind();
PlayerBinding PlayerBinding
.Handle((in PlayerLogic.Output.Animations.Attack output) => .Handle((in PlayerLogic.Output.Animations.Attack output) =>
{ {
if (PlayerIsHittingGeometry()) if (PlayerIsHittingGeometry())
{ {
AnimationPlayer.Play("hit_wall"); AnimationPlayer.Play("hit_wall");
_gameRepo.OnPlayerAttackedWall(); _gameRepo.OnPlayerAttackedWall();
} }
else else
{ {
PlayAttackAnimation(); PlayAttackAnimation();
} }
}) })
.Handle((in PlayerLogic.Output.ThrowItem output) => .Handle((in PlayerLogic.Output.ThrowItem output) =>
{ {
}) })
.Handle((in PlayerLogic.Output.Move output) => .Handle((in PlayerLogic.Output.Move output) =>
{ {
Move(output.delta); Move(output.delta);
}); });
GameChunk.AddChunk(PlayerChunk); GameChunk.AddChunk(PlayerChunk);
PlayerLogic.Start(); PlayerLogic.Start();
this.Provide(); this.Provide();
SetProcessInput(false); SetProcessInput(false);
SetPhysicsProcess(false); SetPhysicsProcess(false);
} }
public void OnReady() public void OnReady()
{ {
SwordSlashAnimation.Position = GetViewport().GetVisibleRect().Size / 2; SwordSlashAnimation.Position = GetViewport().GetVisibleRect().Size / 2;
} }
#endregion #endregion
public void Activate() public void Activate()
{ {
SetProcessInput(true); SetProcessInput(true);
SetPhysicsProcess(true); SetPhysicsProcess(true);
HealthTimer.Start(); HealthTimer.Start();
} }
public void Deactivate() public void Deactivate()
{ {
SetProcessInput(false); SetProcessInput(false);
SetPhysicsProcess(false); SetPhysicsProcess(false);
HealthTimer.Stop(); HealthTimer.Stop();
} }
public void Attack() public void Attack()
{ {
PlayerLogic.Input(new PlayerLogic.Input.Attack()); PlayerLogic.Input(new PlayerLogic.Input.Attack());
} }
public void RaiseHP(int amountToRaise) public void RaiseHP(int amountToRaise)
{ {
Stats.SetMaximumHP(Stats.MaximumHP.Value + amountToRaise); Stats.SetMaximumHP(Stats.MaximumHP.Value + amountToRaise);
Stats.SetCurrentHP(Stats.MaximumHP.Value); Stats.SetCurrentHP(Stats.MaximumHP.Value);
_gameRepo.AnnounceMessageInInventory($"{amountToRaise}MAXHP Up."); _gameRepo.AnnounceMessageInInventory($"{amountToRaise}MAXHP Up.");
} }
public void HealHP(int amountToRestore) public void HealHP(int amountToRestore)
{ {
Stats.SetCurrentHP(Stats.CurrentHP.Value + amountToRestore); Stats.SetCurrentHP(Stats.CurrentHP.Value + amountToRestore);
var raiseString = amountToRestore == 1000 ? "MAX" : $"{amountToRestore}"; var raiseString = amountToRestore == 1000 ? "MAX" : $"{amountToRestore}";
_gameRepo.AnnounceMessageInInventory($"{raiseString}HP Restored."); _gameRepo.AnnounceMessageInInventory($"{raiseString}HP Restored.");
} }
public void RaiseVT(int amountToRaise) public void RaiseVT(int amountToRaise)
{ {
if (Stats.CurrentVT == Stats.MaximumVT) if (Stats.CurrentVT == Stats.MaximumVT)
{ {
Stats.SetMaximumVT(Stats.MaximumVT.Value + amountToRaise); Stats.SetMaximumVT(Stats.MaximumVT.Value + amountToRaise);
Stats.SetCurrentVT(Stats.MaximumVT.Value); Stats.SetCurrentVT(Stats.MaximumVT.Value);
_gameRepo.AnnounceMessageInInventory($"{amountToRaise}MAXVT Up."); _gameRepo.AnnounceMessageInInventory($"{amountToRaise}MAXVT Up.");
} }
} }
public void HealVT(int amountToRestore) public void HealVT(int amountToRestore)
{ {
Stats.SetCurrentVT(Stats.CurrentVT.Value + amountToRestore); Stats.SetCurrentVT(Stats.CurrentVT.Value + amountToRestore);
var raiseString = amountToRestore == 1000 ? "MAX" : $"{amountToRestore}"; var raiseString = amountToRestore == 1000 ? "MAX" : $"{amountToRestore}";
_gameRepo.AnnounceMessageInInventory($"{raiseString}VT Restored."); _gameRepo.AnnounceMessageInInventory($"{raiseString}VT Restored.");
} }
public void ModifyBonusAttack(int amount) public void ModifyBonusAttack(int amount)
{ {
Stats.SetBonusAttack(Stats.BonusAttack.Value + amount); Stats.SetBonusAttack(Stats.BonusAttack.Value + amount);
} }
public void ModifyBonusDefense(int amount) public void ModifyBonusDefense(int amount)
{ {
Stats.SetBonusDefense(Stats.BonusDefense.Value + amount); Stats.SetBonusDefense(Stats.BonusDefense.Value + amount);
} }
public void ModifyMaximumHP(int amount) public void ModifyMaximumHP(int amount)
{ {
Stats.SetMaximumHP(Stats.MaximumHP.Value + amount); Stats.SetMaximumHP(Stats.MaximumHP.Value + amount);
} }
public void ModifyMaximumVT(int amount) public void ModifyMaximumVT(int amount)
{ {
Stats.SetMaximumVT(Stats.MaximumVT.Value + amount); Stats.SetMaximumVT(Stats.MaximumVT.Value + amount);
} }
public void ModifyBonusLuck(double amount) public void ModifyBonusLuck(double amount)
{ {
Stats.SetLuck(Stats.Luck.Value + amount); Stats.SetLuck(Stats.Luck.Value + amount);
} }
public void Move(float delta) public void Move(float delta)
{ {
var rawInput = GlobalInputVector; var rawInput = GlobalInputVector;
var strafeLeftInput = LeftStrafeInputVector; var strafeLeftInput = LeftStrafeInputVector;
var strafeRightInput = RightStrafeInputVector; var strafeRightInput = RightStrafeInputVector;
var transform = Transform; var transform = Transform;
transform.Basis = new Basis(Vector3.Up, Settings.RotationSpeed * -rawInput.X * delta) * transform.Basis; transform.Basis = new Basis(Vector3.Up, Settings.RotationSpeed * -rawInput.X * delta) * transform.Basis;
var moveDirection = new Vector3(strafeRightInput - strafeLeftInput, 0, rawInput.Z).Normalized(); var moveDirection = new Vector3(strafeRightInput - strafeLeftInput, 0, rawInput.Z).Normalized();
var velocity = Basis * moveDirection * Settings.MoveSpeed * Settings.Acceleration; var velocity = Basis * moveDirection * Settings.MoveSpeed * Settings.Acceleration;
_knockbackStrength *= 0.9f; _knockbackStrength *= 0.9f;
Transform = Transform with { Basis = transform.Basis }; Transform = Transform with { Basis = transform.Basis };
Velocity = velocity + (_knockbackDirection * _knockbackStrength); Velocity = velocity + (_knockbackDirection * _knockbackStrength);
MoveAndSlide(); MoveAndSlide();
} }
public void TeleportPlayer(Transform3D newTransform) public void TeleportPlayer(Transform3D newTransform)
{ {
Transform = newTransform; Transform = newTransform;
} }
public void TakeDamage(double damage, ElementType elementType, bool isCriticalHit = false) public void TakeDamage(double damage, ElementType elementType, bool isCriticalHit = false)
{ {
if (Stats.CurrentHP.Value > 0) if (Stats.CurrentHP.Value > 0)
{ {
_damageCalculator.CalculateDamage(damage, elementType, Stats.CurrentDefense.Value + Stats.BonusDefense.Value, ((Armor)_equippedArmor.Value).Stats.ElementalResistanceSet); _damageCalculator.CalculateDamage(damage, elementType, Stats.CurrentDefense.Value + Stats.BonusDefense.Value, ((Armor)_equippedArmor.Value).Stats.ElementalResistanceSet);
Stats.SetCurrentHP(Stats.CurrentHP.Value - (int)damage); Stats.SetCurrentHP(Stats.CurrentHP.Value - (int)damage);
} }
} }
public void Knockback(float impulse) public void Knockback(float impulse)
{ {
_knockbackStrength = impulse; _knockbackStrength = impulse;
_knockbackDirection = GlobalBasis.Z.Normalized(); _knockbackDirection = GlobalBasis.Z.Normalized();
} }
public void GainExp(double expGained) public void GainExp(double expGained)
{ {
Stats.SetCurrentExp(Stats.CurrentExp.Value + expGained); Stats.SetCurrentExp(Stats.CurrentExp.Value + expGained);
} }
public void LevelUp() public void LevelUp()
{ {
var rng = new RandomNumberGenerator(); var rng = new RandomNumberGenerator();
rng.Randomize(); rng.Randomize();
var hpIncrease = rng.RandiRange(3, 6); var hpIncrease = rng.RandiRange(3, 6);
Stats.SetMaximumHP(Stats.MaximumHP.Value + hpIncrease); Stats.SetMaximumHP(Stats.MaximumHP.Value + hpIncrease);
var nextLevel = Stats.CurrentLevel.Value + 1; var nextLevel = Stats.CurrentLevel.Value + 1;
var expToNextLevel = _expToNextLevel[nextLevel]; var expToNextLevel = (int)(6.5 * nextLevel + 4.5 * Mathf.Pow(nextLevel, 2) + Mathf.Pow(nextLevel, 3));
var newCurrentExp = Mathf.Max(Stats.CurrentExp.Value - Stats.ExpToNextLevel.Value, 0); var newCurrentExp = Mathf.Max(Stats.CurrentExp.Value - Stats.ExpToNextLevel.Value, 0);
Stats.SetCurrentLevel(nextLevel); Stats.SetCurrentLevel(nextLevel);
Stats.SetExpToNextLevel(expToNextLevel); Stats.SetExpToNextLevel(expToNextLevel);
Stats.SetCurrentExp(newCurrentExp); Stats.SetCurrentExp(newCurrentExp);
} }
public void Die() public void Die()
{ {
EquippedWeapon.Sync -= EquippedWeapon_Sync; EquippedWeapon.Sync -= EquippedWeapon_Sync;
EquippedArmor.Sync -= EquippedArmor_Sync; EquippedArmor.Sync -= EquippedArmor_Sync;
EquippedAccessory.Sync -= EquippedAccessory_Sync; EquippedAccessory.Sync -= EquippedAccessory_Sync;
Stats.CurrentHP.Sync -= CurrentHP_Sync; Stats.CurrentHP.Sync -= CurrentHP_Sync;
Stats.CurrentExp.Sync -= CurrentEXP_Sync; Stats.CurrentExp.Sync -= CurrentEXP_Sync;
HealthTimer.WaitTime = _healthTimerWaitTime; HealthTimer.WaitTime = _healthTimerWaitTime;
HealthTimer.Timeout -= OnHealthTimerTimeout; HealthTimer.Timeout -= OnHealthTimerTimeout;
SwordSlashAnimation.Stop(); SwordSlashAnimation.Stop();
SetProcessInput(false); SetProcessInput(false);
SetPhysicsProcess(false); SetPhysicsProcess(false);
//Hitbox.AreaEntered -= Hitbox_AreaEntered; //Hitbox.AreaEntered -= Hitbox_AreaEntered;
//CollisionDetector.AreaEntered -= CollisionDetector_AreaEntered; //CollisionDetector.AreaEntered -= CollisionDetector_AreaEntered;
//AnimationPlayer.AnimationFinished -= OnAnimationFinished; //AnimationPlayer.AnimationFinished -= OnAnimationFinished;
Game.GameOver(); Game.GameOver();
} }
public override void _Input(InputEvent @event) public override void _Input(InputEvent @event)
{ {
if (@event.IsActionPressed(GameInputs.Attack)) if (@event.IsActionPressed(GameInputs.Attack))
Attack(); Attack();
if (@event.IsActionPressed(GameInputs.Sprint)) if (@event.IsActionPressed(GameInputs.Sprint))
Settings.MoveSpeed *= 2; Settings.MoveSpeed *= 2;
if (@event.IsActionReleased(GameInputs.Sprint)) if (@event.IsActionReleased(GameInputs.Sprint))
Settings.MoveSpeed /= 2; Settings.MoveSpeed /= 2;
} }
public void OnPhysicsProcess(double delta) public void OnPhysicsProcess(double delta)
{ {
PlayerLogic.Input(new PlayerLogic.Input.PhysicsTick(delta)); PlayerLogic.Input(new PlayerLogic.Input.PhysicsTick(delta));
PlayerLogic.Input(new PlayerLogic.Input.Moved(GlobalPosition, GlobalTransform)); PlayerLogic.Input(new PlayerLogic.Input.Moved(GlobalPosition, GlobalTransform));
} }
public void Equip(EquipableItem equipable) public void Equip(EquipableItem equipable)
{ {
if (equipable.ItemTag == ItemTag.MysteryItem) if (equipable.ItemTag == ItemTag.MysteryItem)
{ {
var rerolledItem = Game.RerollItem(equipable) as EquipableItem; var rerolledItem = Game.RerollItem(equipable) as EquipableItem;
Equip(rerolledItem); Equip(rerolledItem);
return; return;
} }
if (equipable is Weapon weapon) if (equipable is Weapon weapon)
{ {
Unequip(_equippedWeapon.Value); Unequip(_equippedWeapon.Value);
weapon.IsEquipped = true; weapon.IsEquipped = true;
_equippedWeapon.OnNext(weapon); _equippedWeapon.OnNext(weapon);
} }
else if (equipable is Armor armor) else if (equipable is Armor armor)
{ {
Unequip(_equippedArmor.Value); Unequip(_equippedArmor.Value);
armor.IsEquipped = true; armor.IsEquipped = true;
_equippedArmor.OnNext(armor); _equippedArmor.OnNext(armor);
} }
else if (equipable is Accessory accessory) else if (equipable is Accessory accessory)
{ {
Unequip(_equippedAccessory.Value); Unequip(_equippedAccessory.Value);
accessory.IsEquipped = true; accessory.IsEquipped = true;
_equippedAccessory.OnNext(accessory); _equippedAccessory.OnNext(accessory);
} }
else else
throw new NotImplementedException("Item type is not supported."); throw new NotImplementedException("Item type is not supported.");
} }
public void Unequip(EquipableItem equipable) public void Unequip(EquipableItem equipable)
{ {
if (equipable is Weapon weapon) if (equipable is Weapon weapon)
{ {
weapon.IsEquipped = false; weapon.IsEquipped = false;
ModifyBonusAttack(-weapon.Damage); ModifyBonusAttack(-weapon.Damage);
_equippedWeapon.OnNext(new Weapon()); _equippedWeapon.OnNext(new Weapon());
} }
else if (equipable is Armor armor) else if (equipable is Armor armor)
{ {
armor.IsEquipped = false; armor.IsEquipped = false;
ModifyBonusDefense(-armor.Defense); ModifyBonusDefense(-armor.Defense);
_equippedArmor.OnNext(new Armor()); _equippedArmor.OnNext(new Armor());
} }
else if (equipable is Accessory accessory) else if (equipable is Accessory accessory)
{ {
accessory.IsEquipped = false; accessory.IsEquipped = false;
ModifyMaximumHP(-accessory.MaxHPUp); ModifyMaximumHP(-accessory.MaxHPUp);
ModifyMaximumVT(-accessory.MaxVTUp); ModifyMaximumVT(-accessory.MaxVTUp);
ModifyBonusAttack(-accessory.ATKUp); ModifyBonusAttack(-accessory.ATKUp);
ModifyBonusDefense(-accessory.DEFUp); ModifyBonusDefense(-accessory.DEFUp);
ModifyBonusLuck(-accessory.LuckUp); ModifyBonusLuck(-accessory.LuckUp);
_equippedAccessory.OnNext(new Accessory()); _equippedAccessory.OnNext(new Accessory());
} }
else else
throw new NotImplementedException("Item type is not supported."); throw new NotImplementedException("Item type is not supported.");
} }
private static Vector3 GlobalInputVector private static Vector3 GlobalInputVector
{ {
get get
{ {
var rawInput = Input.GetVector(GameInputs.MoveLeft, GameInputs.MoveRight, GameInputs.MoveUp, GameInputs.MoveDown); var rawInput = Input.GetVector(GameInputs.MoveLeft, GameInputs.MoveRight, GameInputs.MoveUp, GameInputs.MoveDown);
var input = new Vector3 var input = new Vector3
{ {
X = rawInput.X, X = rawInput.X,
Z = rawInput.Y Z = rawInput.Y
}; };
return input with { Y = 0f }; return input with { Y = 0f };
} }
} }
private static float LeftStrafeInputVector private static float LeftStrafeInputVector
{ {
get get
{ {
return Input.GetActionStrength(GameInputs.StrafeLeft); return Input.GetActionStrength(GameInputs.StrafeLeft);
} }
} }
private static float RightStrafeInputVector private static float RightStrafeInputVector
{ {
get get
{ {
return Input.GetActionStrength(GameInputs.StrafeRight); return Input.GetActionStrength(GameInputs.StrafeRight);
} }
} }
private void ThrowItem() private void ThrowItem()
{ {
var itemScene = GD.Load<PackedScene>("res://src/items/throwable/ThrowableItem.tscn"); var itemScene = GD.Load<PackedScene>("res://src/items/throwable/ThrowableItem.tscn");
var throwItem = itemScene.Instantiate<ThrowableItem>(); var throwItem = itemScene.Instantiate<ThrowableItem>();
GetTree().Root.AddChildEx(throwItem); GetTree().Root.AddChildEx(throwItem);
throwItem.GlobalPosition = CurrentPosition + new Vector3(0, 3.5f, 0); throwItem.GlobalPosition = CurrentPosition + new Vector3(0, 3.5f, 0);
throwItem.GlobalRotation = GlobalRotation; throwItem.GlobalRotation = GlobalRotation;
} }
private void PlayAttackAnimation() private void PlayAttackAnimation()
{ {
var attackSpeed = ((Weapon)EquippedWeapon.Value).AttackSpeed; var attackSpeed = ((Weapon)EquippedWeapon.Value).AttackSpeed;
AnimationPlayer.SetSpeedScale((float)attackSpeed); AnimationPlayer.SetSpeedScale((float)attackSpeed);
if (EquippedWeapon.Value.ItemName == "Atonement") if (EquippedWeapon.Value.ItemName == "Atonement")
AnimationPlayer.Play("atonement_attack"); AnimationPlayer.Play("atonement_attack");
else else
AnimationPlayer.Play("attack"); AnimationPlayer.Play("attack");
_gameRepo.OnPlayerAttack(); _gameRepo.OnPlayerAttack();
} }
private void OnAnimationFinished(StringName animation) private void OnAnimationFinished(StringName animation)
{ {
PlayerLogic.Input(new PlayerLogic.Input.AttackAnimationFinished()); PlayerLogic.Input(new PlayerLogic.Input.AttackAnimationFinished());
} }
private void OnExitTree() private void OnExitTree()
{ {
PlayerLogic.Stop(); PlayerLogic.Stop();
PlayerBinding.Dispose(); PlayerBinding.Dispose();
AnimationPlayer.AnimationFinished -= OnAnimationFinished; AnimationPlayer.AnimationFinished -= OnAnimationFinished;
} }
private void OnPlayerPositionUpdated(Vector3 globalPosition) => GlobalPosition = globalPosition; private void OnPlayerPositionUpdated(Vector3 globalPosition) => GlobalPosition = globalPosition;
private void OnHealthTimerTimeout() private void OnHealthTimerTimeout()
{ {
if (Stats.CurrentHP.Value <= 0) if (Stats.CurrentHP.Value <= 0)
return; return;
if (Stats.CurrentVT.Value > 0) if (Stats.CurrentVT.Value > 0)
{ {
if (((Accessory)EquippedAccessory.Value).AccessoryTag == AccessoryTag.HalfVTConsumption) if (((Accessory)EquippedAccessory.Value).AccessoryTag == AccessoryTag.HalfVTConsumption)
{ {
reduceOnTick = !reduceOnTick; reduceOnTick = !reduceOnTick;
} }
Stats.SetCurrentHP(Stats.CurrentHP.Value + 1); Stats.SetCurrentHP(Stats.CurrentHP.Value + 1);
if (reduceOnTick) if (reduceOnTick)
Stats.SetCurrentVT(Stats.CurrentVT.Value - 1); Stats.SetCurrentVT(Stats.CurrentVT.Value - 1);
} }
else else
Stats.SetCurrentHP(Stats.CurrentHP.Value - 1); Stats.SetCurrentHP(Stats.CurrentHP.Value - 1);
} }
private void EquippedWeapon_Sync(EquipableItem obj) private void EquippedWeapon_Sync(EquipableItem obj)
{ {
ModifyBonusAttack(((Weapon)obj).Damage); ModifyBonusAttack(((Weapon)obj).Damage);
} }
private void EquippedArmor_Sync(EquipableItem obj) private void EquippedArmor_Sync(EquipableItem obj)
{ {
ModifyBonusDefense(((Armor)obj).Defense); ModifyBonusDefense(((Armor)obj).Defense);
} }
private void EquippedAccessory_Sync(EquipableItem accessory) private void EquippedAccessory_Sync(EquipableItem accessory)
{ {
ModifyMaximumHP(((Accessory)accessory).MaxHPUp); ModifyMaximumHP(((Accessory)accessory).MaxHPUp);
ModifyMaximumVT(((Accessory)accessory).MaxVTUp); ModifyMaximumVT(((Accessory)accessory).MaxVTUp);
ModifyBonusAttack(((Accessory)accessory).ATKUp); ModifyBonusAttack(((Accessory)accessory).ATKUp);
ModifyBonusDefense(((Accessory)accessory).DEFUp); ModifyBonusDefense(((Accessory)accessory).DEFUp);
ModifyBonusLuck(((Accessory)accessory).LuckUp); ModifyBonusLuck(((Accessory)accessory).LuckUp);
} }
private void CurrentHP_Sync(int newHealth) private void CurrentHP_Sync(int newHealth)
{ {
if (newHealth <= 0) if (newHealth <= 0)
Die(); Die();
} }
private void CurrentEXP_Sync(double newExp) private void CurrentEXP_Sync(double newExp)
{ {
if (Stats.CurrentExp.Value >= Stats.ExpToNextLevel.Value) if (Stats.CurrentExp.Value >= Stats.ExpToNextLevel.Value)
LevelUp(); LevelUp();
} }
private void Hitbox_AreaEntered(Area3D area) private void Hitbox_AreaEntered(Area3D area)
{ {
var target = area.GetOwner(); var target = area.GetOwner();
if (target is IEnemy enemy) if (target is IEnemy enemy)
HitEnemy(enemy); HitEnemy(enemy);
} }
private void HitEnemy(IEnemy enemy) private void HitEnemy(IEnemy enemy)
{ {
var attackValue = Stats.CurrentAttack.Value + Stats.BonusAttack.Value; var attackValue = Stats.CurrentAttack.Value + Stats.BonusAttack.Value;
var ignoreElementalResistance = ((Weapon)EquippedWeapon.Value).WeaponTag == WeaponTag.IgnoreAffinity; var ignoreElementalResistance = ((Weapon)EquippedWeapon.Value).WeaponTag == WeaponTag.IgnoreAffinity;
var isCriticalHit = BattleExtensions.IsCriticalHit(Stats.Luck.Value); var isCriticalHit = BattleExtensions.IsCriticalHit(Stats.Luck.Value);
var element = ((Weapon)EquippedWeapon.Value).WeaponElement; var element = ((Weapon)EquippedWeapon.Value).WeaponElement;
enemy.TakeDamage( enemy.TakeDamage(
attackValue * ((Weapon)EquippedWeapon.Value).ElementalDamageBonus, attackValue * ((Weapon)EquippedWeapon.Value).ElementalDamageBonus,
element, element,
isCriticalHit, isCriticalHit,
false, false,
ignoreElementalResistance); ignoreElementalResistance);
if (((Weapon)EquippedWeapon.Value).WeaponTag == WeaponTag.Knockback) if (((Weapon)EquippedWeapon.Value).WeaponTag == WeaponTag.Knockback)
enemy.Knockback(0.3f, -CurrentBasis.Z.Normalized()); enemy.Knockback(0.3f, -CurrentBasis.Z.Normalized());
_gameRepo.OnPlayerAttackedEnemy(); _gameRepo.OnPlayerAttackedEnemy();
} }
private void CollisionDetector_AreaEntered(Area3D area) private void CollisionDetector_AreaEntered(Area3D area)
{ {
if (area.GetParent() is InventoryItem inventoryItem) if (area.GetParent() is InventoryItem inventoryItem)
{ {
var isAdded = Inventory.TryAdd(inventoryItem); var isAdded = Inventory.TryAdd(inventoryItem);
if (isAdded) if (isAdded)
{ {
_gameRepo.AnnounceMessageOnMainScreen($"{inventoryItem.ItemName} picked up."); _gameRepo.AnnounceMessageOnMainScreen($"{inventoryItem.ItemName} picked up.");
inventoryItem.QueueFree(); inventoryItem.QueueFree();
} }
else else
_gameRepo.AnnounceMessageOnMainScreen($"Could not pick up {inventoryItem.ItemName}."); _gameRepo.AnnounceMessageOnMainScreen($"Could not pick up {inventoryItem.ItemName}.");
} }
if (area.GetParent() is DroppedItem droppedItem) if (area.GetParent() is DroppedItem droppedItem)
{ {
var isAdded = Inventory.TryAdd(droppedItem.Item); var isAdded = Inventory.TryAdd(droppedItem.Item);
if (isAdded) if (isAdded)
{ {
_gameRepo.AnnounceMessageOnMainScreen($"{droppedItem.Item.ItemName} picked up."); _gameRepo.AnnounceMessageOnMainScreen($"{droppedItem.Item.ItemName} picked up.");
droppedItem.QueueFree(); droppedItem.QueueFree();
} }
else else
_gameRepo.AnnounceMessageOnMainScreen($"Could not pick up {droppedItem.Item.ItemName}."); _gameRepo.AnnounceMessageOnMainScreen($"Could not pick up {droppedItem.Item.ItemName}.");
} }
if (area.GetParent() is ThrownItem thrownItem) if (area.GetParent() is ThrownItem thrownItem)
{ {
var isAdded = Inventory.TryAdd(thrownItem.ItemThatIsThrown); var isAdded = Inventory.TryAdd(thrownItem.ItemThatIsThrown);
if (isAdded) if (isAdded)
{ {
_gameRepo.AnnounceMessageOnMainScreen($"{thrownItem.ItemThatIsThrown.ItemName} picked up."); _gameRepo.AnnounceMessageOnMainScreen($"{thrownItem.ItemThatIsThrown.ItemName} picked up.");
thrownItem.QueueFree(); thrownItem.QueueFree();
} }
else else
_gameRepo.AnnounceMessageOnMainScreen($"Could not pick up {thrownItem.ItemThatIsThrown.ItemName}."); _gameRepo.AnnounceMessageOnMainScreen($"Could not pick up {thrownItem.ItemThatIsThrown.ItemName}.");
} }
if (area.GetParent() is Restorative restorative) if (area.GetParent() is Restorative restorative)
{ {
_gameRepo.OnRestorativePickedUp(restorative); _gameRepo.OnRestorativePickedUp(restorative);
restorative.QueueFree(); restorative.QueueFree();
} }
} }
private PlayerStats InitializePlayerStats() private PlayerStats InitializePlayerStats()
{ {
var playerStats = new PlayerStats( var playerStats = new PlayerStats(
currentHP: new AutoProp<int>(_playerStatResource.CurrentHP), currentHP: new AutoProp<int>(_playerStatResource.CurrentHP),
maximumHP: new AutoProp<int>(_playerStatResource.MaximumHP), maximumHP: new AutoProp<int>(_playerStatResource.MaximumHP),
currentVT: new AutoProp<int>(_playerStatResource.CurrentVT), currentVT: new AutoProp<int>(_playerStatResource.CurrentVT),
maximumVT: new AutoProp<int>(_playerStatResource.MaximumVT), maximumVT: new AutoProp<int>(_playerStatResource.MaximumVT),
currentAttack: new AutoProp<int>(_playerStatResource.CurrentAttack), currentAttack: new AutoProp<int>(_playerStatResource.CurrentAttack),
currentDefense: new AutoProp<int>(_playerStatResource.CurrentDefense), currentDefense: new AutoProp<int>(_playerStatResource.CurrentDefense),
maxAttack: new AutoProp<int>(_playerStatResource.MaxAttack), maxAttack: new AutoProp<int>(_playerStatResource.MaxAttack),
maxDefense: new AutoProp<int>(_playerStatResource.MaxDefense), maxDefense: new AutoProp<int>(_playerStatResource.MaxDefense),
bonusAttack: new AutoProp<int>(_playerStatResource.BonusAttack), bonusAttack: new AutoProp<int>(_playerStatResource.BonusAttack),
bonusDefense: new AutoProp<int>(_playerStatResource.BonusDefense), bonusDefense: new AutoProp<int>(_playerStatResource.BonusDefense),
currentExp: new AutoProp<double>(_playerStatResource.CurrentExp), currentExp: new AutoProp<double>(_playerStatResource.CurrentExp),
expToNextLevel: new AutoProp<int>(_playerStatResource.ExpToNextLevel), expToNextLevel: new AutoProp<int>(_playerStatResource.ExpToNextLevel),
currentLevel: new AutoProp<int>(_playerStatResource.CurrentLevel), currentLevel: new AutoProp<int>(_playerStatResource.CurrentLevel),
luck: new AutoProp<double>(_playerStatResource.Luck)); luck: new AutoProp<double>(_playerStatResource.Luck));
return playerStats; return playerStats;
} }
private bool PlayerIsHittingGeometry() private bool PlayerIsHittingGeometry()
{ {
var collisions = WallCheck.GetCollidingBodies(); var collisions = WallCheck.GetCollidingBodies();
return collisions.Count > 0; return collisions.Count > 0;
} }
private void WallCheck_BodyEntered(Node body) private void WallCheck_BodyEntered(Node body)
{ {
PlayerLogic.Input(new PlayerLogic.Input.AttackAnimationFinished()); PlayerLogic.Input(new PlayerLogic.Input.AttackAnimationFinished());
GD.Print("Hit wall"); GD.Print("Hit wall");
AnimationPlayer.Stop(); AnimationPlayer.Stop();
} }
} }