diff --git a/src/enemy/Enemy.cs b/src/enemy/Enemy.cs index 985b0d30..2363da74 100644 --- a/src/enemy/Enemy.cs +++ b/src/enemy/Enemy.cs @@ -177,7 +177,13 @@ public partial class Enemy : CharacterBody3D, IEnemy, IProvide { if (CurrentHP.Value > 0) { - var damage = DamageCalculator.CalculatePlayerDamage(hitBox.Damage, hitBox.GetParent().PlayerStatInfo, EnemyStatInfo, GameRepo.EquippedWeapon); + var isCriticalHit = false; + var rng = new RandomNumberGenerator(); + rng.Randomize(); + var roll = rng.Randf(); + if (roll <= GameRepo.EquippedWeapon.WeaponInfo.Luck) + isCriticalHit = true; + var damage = DamageCalculator.CalculatePlayerDamage(hitBox.Damage, hitBox.GetParent().PlayerStatInfo, EnemyStatInfo, GameRepo.EquippedWeapon, isCriticalHit); GD.Print($"Enemy Hit for {damage} damage."); EnemyLogic.Input(new EnemyLogic.Input.HitByPlayer(damage)); } @@ -204,3 +210,11 @@ public partial class Enemy : CharacterBody3D, IEnemy, IProvide EnemyBinding.Dispose(); } } + +public enum WeaponTag +{ + SelfDamage, + IgnoreAffinity, + Knockback, + BreaksOnChange +} diff --git a/src/enemy/EnemyStatInfo.cs b/src/enemy/EnemyStatInfo.cs index da0c95dc..78f9609d 100644 --- a/src/enemy/EnemyStatInfo.cs +++ b/src/enemy/EnemyStatInfo.cs @@ -14,6 +14,9 @@ namespace GameJamDungeon [Export] public int BaseDefense { get; set; } + [Export] + public float Luck { get; set; } = 0.05f; + [Export] public double TelluricResistance { get; set; } diff --git a/src/game/Game.cs b/src/game/Game.cs index 79b708be..2f54a7e6 100644 --- a/src/game/Game.cs +++ b/src/game/Game.cs @@ -75,6 +75,12 @@ public partial class Game : Node3D, IGame var currentFloor = Floors.ElementAt(_currentFloor); currentFloor.CallDeferred(MethodName.QueueFree, []); + if (GameRepo.EquippedWeapon.WeaponInfo.Name == "Palm of Heaven") + { + GameRepo.InventoryItems.Value.Remove(GameRepo.EquippedWeapon); + GameRepo.OnWeaponEquipped(new Weapon()); + } + }) .Handle((in GameLogic.Output.SetPauseMode output) => { diff --git a/src/items/weapons/WeaponInfo.cs b/src/items/weapons/WeaponInfo.cs index 598b1a71..4ef3b3d2 100644 --- a/src/items/weapons/WeaponInfo.cs +++ b/src/items/weapons/WeaponInfo.cs @@ -1,3 +1,4 @@ +using GameJamDungeon; using Godot; [GlobalClass] @@ -6,6 +7,12 @@ public partial class WeaponInfo : InventoryItemInfo [Export] public required int Damage { get; set; } + [Export] + public double Luck { get; set; } = 0.05; + + [Export] + public double AttackSpeed { get; set; } = 1; + [Export] public double TelluricDamageBonus { get; set; } @@ -20,4 +27,7 @@ public partial class WeaponInfo : InventoryItemInfo [Export] public double FerrumDamageBonus { get; set; } + + [Export] + public Godot.Collections.Array WeaponTags { get; set; } } diff --git a/src/items/weapons/resources/Kubel.tres b/src/items/weapons/resources/Kubel.tres index 1f5f0b73..8791b93d 100644 --- a/src/items/weapons/resources/Kubel.tres +++ b/src/items/weapons/resources/Kubel.tres @@ -5,10 +5,13 @@ [resource] script = ExtResource("1_kbje7") Damage = 11 +Luck = 0.05 +AttackSpeed = 1.0 TelluricDamageBonus = 0.0 AeolicDamageBonus = 0.0 BaseHydricDamageBonus = 0.0 IgneousDamageBonus = 0.0 FerrumDamageBonus = 0.0 +WeaponTags = Array[int]([1]) Name = "Kubel" Description = "A very powerful spear. For every hit, you lose 5 HP." diff --git a/src/items/weapons/resources/Palm of Heaven.tres b/src/items/weapons/resources/Palm of Heaven.tres index e9f95240..b2699698 100644 --- a/src/items/weapons/resources/Palm of Heaven.tres +++ b/src/items/weapons/resources/Palm of Heaven.tres @@ -5,10 +5,13 @@ [resource] script = ExtResource("1_pwwg7") Damage = 10 +Luck = 0.05 +AttackSpeed = 1.0 TelluricDamageBonus = 0.0 AeolicDamageBonus = 0.0 BaseHydricDamageBonus = 0.0 IgneousDamageBonus = 0.0 FerrumDamageBonus = 0.0 +WeaponTags = Array[int]([3]) Name = "Palm of Heaven" Description = "Very Powerful. Breaks upon leaving the floor." diff --git a/src/items/weapons/resources/RareSword.tres b/src/items/weapons/resources/RareSword.tres index 021aced7..924f97c4 100644 --- a/src/items/weapons/resources/RareSword.tres +++ b/src/items/weapons/resources/RareSword.tres @@ -5,9 +5,12 @@ [resource] script = ExtResource("1_oqgv2") Damage = 7 +Luck = 0.85 +AttackSpeed = 1.5 TelluricDamageBonus = 0.0 AeolicDamageBonus = 0.0 BaseHydricDamageBonus = 0.0 IgneousDamageBonus = 0.0 +FerrumDamageBonus = 0.0 Name = "Rare sword" Description = "Rare" diff --git a/src/items/weapons/resources/Rondo.tres b/src/items/weapons/resources/Rondo.tres index df5e8ce3..3e1d0301 100644 --- a/src/items/weapons/resources/Rondo.tres +++ b/src/items/weapons/resources/Rondo.tres @@ -5,10 +5,13 @@ [resource] script = ExtResource("1_xfb0x") Damage = 7 +Luck = 0.05 +AttackSpeed = 1.333 TelluricDamageBonus = 0.0 AeolicDamageBonus = 0.0 BaseHydricDamageBonus = 0.0 IgneousDamageBonus = 0.0 FerrumDamageBonus = 0.0 +WeaponTags = Array[int]([1]) Name = "Rondo" Description = "An eastern blade outside of time and reproach." diff --git a/src/items/weapons/resources/Sword Sword Odette.tres b/src/items/weapons/resources/Sword Sword Odette.tres index 4df29960..5b4d235d 100644 --- a/src/items/weapons/resources/Sword Sword Odette.tres +++ b/src/items/weapons/resources/Sword Sword Odette.tres @@ -5,11 +5,14 @@ [resource] script = ExtResource("1_cik6n") Damage = 12 +Luck = 0.05 +AttackSpeed = 1.25 TelluricDamageBonus = 0.0 AeolicDamageBonus = 0.0 BaseHydricDamageBonus = 0.0 IgneousDamageBonus = 0.0 FerrumDamageBonus = 0.0 +WeaponTags = Array[int]([1]) Name = "Swan Sword Odette" Description = "Ignores Affinity. diff --git a/src/player/Player.cs b/src/player/Player.cs index fc834317..c4823150 100644 --- a/src/player/Player.cs +++ b/src/player/Player.cs @@ -2,6 +2,7 @@ using Chickensoft.Collections; using Chickensoft.GodotNodeInterfaces; using Chickensoft.Introspection; +using Chickensoft.LogicBlocks; using Chickensoft.SaveFileBuilder; using Godot; @@ -75,8 +76,6 @@ namespace GameJamDungeon [Node] public IArea3D CollisionDetector { get; set; } = default!; - private IAutoProp EquippedWeapon { get; set; } = default!; - private AutoProp _currentHP { get; set; } = default!; private AutoProp _currentVT { get; set; } = default!; @@ -100,9 +99,6 @@ namespace GameJamDungeon GameRepo.SetPlayerGlobalPosition(GlobalPosition); GameRepo.PlayerGlobalPosition.Sync += OnPlayerPositionUpdated; - EquippedWeapon = new AutoProp(new Weapon()); - EquippedWeapon.Sync += OnEquippedWeaponChanged; - _currentHP = new AutoProp(PlayerStatInfo.MaximumHP); _currentVT = new AutoProp(PlayerStatInfo.MaximumVT); _currentHP.Sync += OnHPChanged; @@ -129,7 +125,11 @@ namespace GameJamDungeon }) .Handle((in PlayerLogic.Output.Animations.Attack output) => { + var attackSpeed = (float)GameRepo.EquippedWeapon.WeaponInfo.AttackSpeed; + AnimationPlayer.SetSpeedScale(attackSpeed); AnimationPlayer.Play("attack"); + if (GameRepo.EquippedWeapon.WeaponInfo.WeaponTags.Contains(WeaponTag.SelfDamage)) + _currentHP.OnNext(_currentHP.Value - 5); }) .Handle((in PlayerLogic.Output.ThrowItem output) => { @@ -154,7 +154,13 @@ namespace GameJamDungeon if (_currentHP.Value > 0) { var enemy = hitBox.GetParent(); - var damage = DamageCalculator.CalculateEnemyDamage(hitBox.Damage, PlayerStatInfo, enemy.EnemyStatInfo, GameRepo.EquippedArmor); + var isCriticalHit = false; + var rng = new RandomNumberGenerator(); + rng.Randomize(); + var roll = rng.Randf(); + if (roll <= enemy.EnemyStatInfo.Luck) + isCriticalHit = true; + var damage = DamageCalculator.CalculateEnemyDamage(hitBox.Damage, PlayerStatInfo, enemy.EnemyStatInfo, GameRepo.EquippedArmor, isCriticalHit); _currentHP.OnNext(_currentHP.Value - damage); GD.Print($"Player hit for {damage} damage."); } diff --git a/src/system/stats/DamageCalculator.cs b/src/system/stats/DamageCalculator.cs index 339595d5..cd15e6cc 100644 --- a/src/system/stats/DamageCalculator.cs +++ b/src/system/stats/DamageCalculator.cs @@ -4,27 +4,52 @@ namespace GameJamDungeon { public static class DamageCalculator { - public static double CalculatePlayerDamage(int attackDamage, PlayerStatInfo playerStatInfo, EnemyStatInfo enemyStatInfo, Weapon weapon) + public static double CalculatePlayerDamage(int attackDamage, PlayerStatInfo playerStatInfo, EnemyStatInfo enemyStatInfo, Weapon weapon, bool isCriticalHit) { var baseDamage = attackDamage + playerStatInfo.BaseAttack; - var elementADamage = (weapon.WeaponInfo.BaseHydricDamageBonus > 0 ? weapon.WeaponInfo.BaseHydricDamageBonus - enemyStatInfo.HydricResistance : 0) / 100; - var elementBDamage = (weapon.WeaponInfo.IgneousDamageBonus > 0 ? weapon.WeaponInfo.IgneousDamageBonus - enemyStatInfo.IgneousResistance : 0) / 100; - var elementCDamage = (weapon.WeaponInfo.TelluricDamageBonus > 0 ? weapon.WeaponInfo.TelluricDamageBonus - enemyStatInfo.TelluricResistance : 0) / 100; - var elementDDamage = (weapon.WeaponInfo.AeolicDamageBonus > 0 ? weapon.WeaponInfo.AeolicDamageBonus - enemyStatInfo.AeolicResistance : 0) / 100; - var elementalBonusDamage = baseDamage + (baseDamage * elementADamage) + (baseDamage * elementBDamage) + (baseDamage * elementCDamage) + (baseDamage * elementDDamage); + var hydricResistance = enemyStatInfo.HydricResistance; + var igneousResistance = enemyStatInfo.IgneousResistance; + var telluricResistance = enemyStatInfo.TelluricResistance; + var aeolicResistance = enemyStatInfo.AeolicResistance; + var ferrumResistance = enemyStatInfo.FerrumResistance; + + if (weapon.WeaponInfo.WeaponTags.Contains(WeaponTag.IgnoreAffinity)) + { + hydricResistance = 0; + igneousResistance = 0; + telluricResistance = 0; + aeolicResistance = 0; + ferrumResistance = 0; + } + + var elementADamage = (weapon.WeaponInfo.BaseHydricDamageBonus > 0 ? weapon.WeaponInfo.BaseHydricDamageBonus - hydricResistance : 0) / 100; + var elementBDamage = (weapon.WeaponInfo.IgneousDamageBonus > 0 ? weapon.WeaponInfo.IgneousDamageBonus - igneousResistance : 0) / 100; + var elementCDamage = (weapon.WeaponInfo.TelluricDamageBonus > 0 ? weapon.WeaponInfo.TelluricDamageBonus - telluricResistance : 0) / 100; + var elementDDamage = (weapon.WeaponInfo.AeolicDamageBonus > 0 ? weapon.WeaponInfo.AeolicDamageBonus - aeolicResistance : 0) / 100; + var elementEDamage = (weapon.WeaponInfo.FerrumDamageBonus > 0 ? weapon.WeaponInfo.FerrumDamageBonus - ferrumResistance : 0) / 100; + var elementalBonusDamage = baseDamage + (baseDamage * elementADamage) + (baseDamage * elementBDamage) + (baseDamage * elementCDamage) + (baseDamage * elementDDamage) + (baseDamage * elementEDamage); var calculatedDamage = elementalBonusDamage - enemyStatInfo.BaseDefense; + + if (isCriticalHit) + calculatedDamage *= 2; + return calculatedDamage; } - public static double CalculateEnemyDamage(int attackDamage, PlayerStatInfo playerStatInfo, EnemyStatInfo enemyStatInfo, Armor armor) + public static double CalculateEnemyDamage(int attackDamage, PlayerStatInfo playerStatInfo, EnemyStatInfo enemyStatInfo, Armor armor, bool isCriticalHit) { var baseDamage = attackDamage + enemyStatInfo.BaseAttack; var elementADamage = (enemyStatInfo.BaseHydricDamageBonus > 0 ? enemyStatInfo.BaseHydricDamageBonus - armor.ArmorInfo.HydricResistance : 0) / 100; var elementBDamage = (enemyStatInfo.IgneousDamageBonus > 0 ? enemyStatInfo.IgneousDamageBonus - armor.ArmorInfo.IgneousResistance : 0) / 100; var elementCDamage = (enemyStatInfo.TelluricDamageBonus > 0 ? enemyStatInfo.TelluricDamageBonus - armor.ArmorInfo.TelluricResistance : 0) / 100; var elementDDamage = (enemyStatInfo.AeolicDamageBonus > 0 ? enemyStatInfo.AeolicDamageBonus - armor.ArmorInfo.AeolicResistance : 0) / 100; - var elementalBonusDamage = baseDamage + (baseDamage * elementADamage) + (baseDamage * elementBDamage) + (baseDamage * elementCDamage) + (baseDamage * elementDDamage); + var elementEDamage = (enemyStatInfo.FerrumDamageBonus > 0 ? enemyStatInfo.FerrumDamageBonus - armor.ArmorInfo.FerrumResistance : 0) / 100; + var elementalBonusDamage = baseDamage + (baseDamage * elementADamage) + (baseDamage * elementBDamage) + (baseDamage * elementCDamage) + (baseDamage * elementDDamage) + (baseDamage * elementDDamage); var calculatedDamage = elementalBonusDamage - playerStatInfo.BaseDefense - (armor != null ? armor.ArmorInfo.Defense : 0); + + if (isCriticalHit) + calculatedDamage *= 2; + return calculatedDamage; } }