From aba325ff2b8cac42d8d2f653c7c6409c9d39e165 Mon Sep 17 00:00:00 2001 From: Zenny Date: Mon, 9 Feb 2026 23:01:52 -0800 Subject: [PATCH] Implement plasma sword instakill --- .../Player/IPlayer.cs | 4 + Zennysoft.Game.Ma/src/items/EffectService.cs | 338 +++++++++--------- .../src/items/InventoryItemStats.cs | 2 +- .../src/items/accessory/Accessory.cs | 5 + Zennysoft.Game.Ma/src/items/armor/Armor.cs | 5 + Zennysoft.Game.Ma/src/items/weapons/Weapon.cs | 5 + .../src/items/weapons/WeaponStats.cs | 4 + .../items/weapons/resources/PlasmaSword.tres | 9 +- Zennysoft.Game.Ma/src/player/Player.cs | 35 +- Zennysoft.Game.Ma/src/player/Player.tscn | 58 +-- .../src/player/PlayerEffectService.cs | 26 ++ .../src/player/PlayerEffectService.cs.uid | 1 + 12 files changed, 273 insertions(+), 219 deletions(-) create mode 100644 Zennysoft.Game.Ma/src/player/PlayerEffectService.cs create mode 100644 Zennysoft.Game.Ma/src/player/PlayerEffectService.cs.uid diff --git a/Zennysoft.Game.Ma.Implementation/Player/IPlayer.cs b/Zennysoft.Game.Ma.Implementation/Player/IPlayer.cs index 7456dd566..c1c69a243 100644 --- a/Zennysoft.Game.Ma.Implementation/Player/IPlayer.cs +++ b/Zennysoft.Game.Ma.Implementation/Player/IPlayer.cs @@ -46,6 +46,10 @@ public interface IPlayer : IKillable, ICharacterBody3D public bool CanEquipState { get; set; } + public int TotalAttack { get; } + public int TotalDefense { get; } + public int TotalLuck { get; } + public event Action PlayerDied; public delegate InventoryItem RerollItem(InventoryItem item); } diff --git a/Zennysoft.Game.Ma/src/items/EffectService.cs b/Zennysoft.Game.Ma/src/items/EffectService.cs index 98971d954..63b080182 100644 --- a/Zennysoft.Game.Ma/src/items/EffectService.cs +++ b/Zennysoft.Game.Ma/src/items/EffectService.cs @@ -15,301 +15,301 @@ public class EffectService public EffectService(IGame game, IPlayer player, IMap map) { - _game = game; - _player = player; - _map = map; + _game = game; + _player = player; + _map = map; } public void TeleportEnemiesToCurrentRoom() { - var currentFloor = _game.CurrentFloor; - var rooms = currentFloor.Rooms; - var currentRoom = _map.GetPlayersCurrentRoom(); + var currentFloor = _game.CurrentFloor; + var rooms = currentFloor.Rooms; + var currentRoom = _map.GetPlayersCurrentRoom(); - if (currentRoom is not MonsterRoom) - return; + if (currentRoom is not MonsterRoom) + return; - var validRooms = rooms.OfType().ToList(); - if (currentRoom is MonsterRoom monsterRoom) - validRooms.Remove(monsterRoom); + var validRooms = rooms.OfType().ToList(); + if (currentRoom is MonsterRoom monsterRoom) + validRooms.Remove(monsterRoom); - var currentMonsterRoom = (MonsterRoom)currentRoom; + var currentMonsterRoom = (MonsterRoom)currentRoom; - var enemyList = validRooms.SelectMany(x => x.GetEnemiesInCurrentRoom()).OfType(); + var enemyList = validRooms.SelectMany(x => x.GetEnemiesInCurrentRoom()).OfType(); - foreach (var enemy in enemyList) - enemy.MoveEnemyToNewRoom(currentMonsterRoom); + foreach (var enemy in enemyList) + enemy.MoveEnemyToNewRoom(currentMonsterRoom); } public void KillHalfEnemiesInRoom() { - var currentRoom = _map.GetPlayersCurrentRoom(); - if (currentRoom is not MonsterRoom) - return; + var currentRoom = _map.GetPlayersCurrentRoom(); + if (currentRoom is not MonsterRoom) + return; - var currentMonsterRoom = (MonsterRoom)currentRoom; - var enemyList = currentMonsterRoom.GetEnemiesInCurrentRoom().OfType().ToList(); - var enemiesToKill = enemyList.Count / 2; - for (var i = 0; i < enemiesToKill; i++) - enemyList[i].Die(); + var currentMonsterRoom = (MonsterRoom)currentRoom; + var enemyList = currentMonsterRoom.GetEnemiesInCurrentRoom().OfType().ToList(); + var enemiesToKill = enemyList.Count / 2; + for (var i = 0; i < enemiesToKill; i++) + enemyList[i].Die(); } public void TurnAllEnemiesInRoomIntoHealingItem() { - var currentRoom = _map.GetPlayersCurrentRoom(); + var currentRoom = _map.GetPlayersCurrentRoom(); - if (currentRoom is not MonsterRoom) - return; + if (currentRoom is not MonsterRoom) + return; - var currentEnemies = currentRoom.EnemiesInRoom.OfType().ToList(); - foreach (var enemy in currentEnemies) - { - enemy.OnMorph(); - DropHealingItem(enemy.GlobalPosition); - } - if (currentEnemies.Any()) - SfxDatabase.Instance.Play(SoundEffect.TurnAllEnemiesIntoHealingItems); + var currentEnemies = currentRoom.EnemiesInRoom.OfType().ToList(); + foreach (var enemy in currentEnemies) + { + enemy.OnMorph(); + DropHealingItem(enemy.GlobalPosition); + } + if (currentEnemies.Any()) + SfxDatabase.Instance.Play(SoundEffect.TurnAllEnemiesIntoHealingItems); } public void DropHealingItem(Vector3 vector) { - var consumableFolder = "res://src/items/consumable"; - var restorativeScene = GD.Load($"{consumableFolder}/ConsumableItem.tscn"); - var consumable = restorativeScene.Instantiate(); - var resourceFiles = DirAccess.GetFilesAt($"{consumableFolder}/resources"); - var rng = new RandomNumberGenerator(); - rng.Randomize(); - var randomResource = resourceFiles[rng.RandiRange(0, resourceFiles.Length - 1)]; - var randomFile = ResourceLoader.Load($"{consumableFolder}/resources/{randomResource}"); - consumable.Stats = randomFile; - _game.AddChild(consumable); - consumable.GlobalPosition = vector; + var consumableFolder = "res://src/items/consumable"; + var restorativeScene = GD.Load($"{consumableFolder}/ConsumableItem.tscn"); + var consumable = restorativeScene.Instantiate(); + var resourceFiles = DirAccess.GetFilesAt($"{consumableFolder}/resources"); + var rng = new RandomNumberGenerator(); + rng.Randomize(); + var randomResource = resourceFiles[rng.RandiRange(0, resourceFiles.Length - 1)]; + var randomFile = ResourceLoader.Load($"{consumableFolder}/resources/{randomResource}".TrimSuffix(".remap")); + consumable.Stats = randomFile; + _game.AddChild(consumable); + consumable.GlobalPosition = vector; } public void HealAllEnemiesAndPlayerInRoomToFull() { - var currentRoom = _map.GetPlayersCurrentRoom(); + var currentRoom = _map.GetPlayersCurrentRoom(); - if (currentRoom is not MonsterRoom) - return; + if (currentRoom is not MonsterRoom) + return; - currentRoom.EnemiesInRoom.ForEach(e => e.HealthComponent.SetCurrentHealth(e.HealthComponent.MaximumHP.Value)); - _player.HealthComponent.SetCurrentHealth(_player.HealthComponent.MaximumHP.Value); + currentRoom.EnemiesInRoom.ForEach(e => e.HealthComponent.SetCurrentHealth(e.HealthComponent.MaximumHP.Value)); + _player.HealthComponent.SetCurrentHealth(_player.HealthComponent.MaximumHP.Value); } public void AbsorbHPFromAllEnemiesInRoom() { - var currentRoom = _map.GetPlayersCurrentRoom(); + var currentRoom = _map.GetPlayersCurrentRoom(); - if (currentRoom is not MonsterRoom) - return; + if (currentRoom is not MonsterRoom) + return; - var currentEnemies = currentRoom.EnemiesInRoom; - var hpToAbsorb = 0.0; - foreach (var enemy in currentEnemies) - { - var absorbAmount = enemy.HealthComponent.MaximumHP.Value * 0.05; - enemy.HealthComponent.Damage((int)absorbAmount); - hpToAbsorb += absorbAmount; - enemy.OnAbsorb(); - } - _player.HealthComponent.Heal((int)hpToAbsorb); - GD.Print("HP to absorb: " + hpToAbsorb); + var currentEnemies = currentRoom.EnemiesInRoom; + var hpToAbsorb = 0.0; + foreach (var enemy in currentEnemies) + { + var absorbAmount = enemy.HealthComponent.MaximumHP.Value * 0.05; + enemy.HealthComponent.Damage((int)absorbAmount); + hpToAbsorb += absorbAmount; + enemy.OnAbsorb(); + } + _player.HealthComponent.Heal((int)hpToAbsorb); + GD.Print("HP to absorb: " + hpToAbsorb); } public void DealElementalDamageToAllEnemiesInRoom(ElementType elementType) { - var currentRoom = _map.GetPlayersCurrentRoom(); + var currentRoom = _map.GetPlayersCurrentRoom(); - if (currentRoom is not MonsterRoom) - return; + if (currentRoom is not MonsterRoom) + return; - var currentEnemies = currentRoom.EnemiesInRoom; - foreach (var enemy in currentEnemies) - { - var damageDealt = DamageCalculator.CalculateDamage(new AttackData(20, elementType), 10, enemy.ElementalResistanceSet); - enemy.HealthComponent.Damage(damageDealt); - } + var currentEnemies = currentRoom.EnemiesInRoom; + foreach (var enemy in currentEnemies) + { + var damageDealt = DamageCalculator.CalculateDamage(new AttackData(20, elementType), 10, enemy.ElementalResistanceSet); + enemy.HealthComponent.Damage(damageDealt); + } } public void SwapHPandVT() { - var oldHp = _player.HealthComponent.CurrentHP.Value; - var oldVt = _player.VTComponent.CurrentVT.Value; + var oldHp = _player.HealthComponent.CurrentHP.Value; + var oldVt = _player.VTComponent.CurrentVT.Value; - _player.HealthComponent.SetCurrentHealth(oldVt); - _player.VTComponent.SetVT(oldHp); - SfxDatabase.Instance.Play(SoundEffect.SwapHPAndVT); + _player.HealthComponent.SetCurrentHealth(oldVt); + _player.VTComponent.SetVT(oldHp); + SfxDatabase.Instance.Play(SoundEffect.SwapHPAndVT); } public void RandomEffect(EffectItem item) { - var itemEffects = Enum.GetValues().ToList(); - itemEffects.Remove(UsableItemTag.RandomEffect); - itemEffects.Remove(UsableItemTag.None); - var randomEffect = new Godot.Collections.Array(itemEffects).PickRandom(); - item.SetEffectTag(randomEffect); - _game.UseItem(item); + var itemEffects = Enum.GetValues().ToList(); + itemEffects.Remove(UsableItemTag.RandomEffect); + itemEffects.Remove(UsableItemTag.None); + var randomEffect = new Godot.Collections.Array(itemEffects).PickRandom(); + item.SetEffectTag(randomEffect); + _game.UseItem(item); } public void RaiseCurrentWeaponAttack() { - if (string.IsNullOrEmpty(_player.EquipmentComponent.EquippedWeapon.Value.ItemName)) - return; + if (string.IsNullOrEmpty(_player.EquipmentComponent.EquippedWeapon.Value.ItemName)) + return; - var currentWeapon = (Weapon)_player.EquipmentComponent.EquippedWeapon.Value; - currentWeapon.IncreaseWeaponAttack(1); - SfxDatabase.Instance.Play(SoundEffect.IncreaseStat); + var currentWeapon = (Weapon)_player.EquipmentComponent.EquippedWeapon.Value; + currentWeapon.IncreaseWeaponAttack(1); + SfxDatabase.Instance.Play(SoundEffect.IncreaseStat); } public void RaiseCurrentArmorDefense() { - if (string.IsNullOrEmpty(_player.EquipmentComponent.EquippedArmor.Value.ItemName)) - return; + if (string.IsNullOrEmpty(_player.EquipmentComponent.EquippedArmor.Value.ItemName)) + return; - var currentArmor = (Armor)_player.EquipmentComponent.EquippedArmor.Value; - currentArmor.IncreaseArmorDefense(1); - SfxDatabase.Instance.Play(SoundEffect.IncreaseStat); + var currentArmor = (Armor)_player.EquipmentComponent.EquippedArmor.Value; + currentArmor.IncreaseArmorDefense(1); + SfxDatabase.Instance.Play(SoundEffect.IncreaseStat); } public void RaiseLevel() => _player.LevelUp(); public void TeleportToRandomRoom(IEnemy enemy) { - var currentFloor = _game.CurrentFloor; - var rooms = currentFloor.Rooms; - var currentRoom = enemy.GetCurrentRoom(rooms); - var validRooms = rooms.OfType().ToList(); - if (currentRoom is MonsterRoom currentMonsterRoom) - validRooms.Remove(currentMonsterRoom); + var currentFloor = _game.CurrentFloor; + var rooms = currentFloor.Rooms; + var currentRoom = enemy.GetCurrentRoom(rooms); + var validRooms = rooms.OfType().ToList(); + if (currentRoom is MonsterRoom currentMonsterRoom) + validRooms.Remove(currentMonsterRoom); - if (validRooms.Count == 0) - return; + if (validRooms.Count == 0) + return; - var roomsGodotCollection = new Godot.Collections.Array(validRooms); - var randomRoom = roomsGodotCollection.PickRandom(); + var roomsGodotCollection = new Godot.Collections.Array(validRooms); + var randomRoom = roomsGodotCollection.PickRandom(); - enemy.MoveEnemyToNewRoom(randomRoom); + enemy.MoveEnemyToNewRoom(randomRoom); } public void TeleportToRandomRoom(IPlayer player) { - var currentFloor = _game.CurrentFloor; - var rooms = currentFloor.Rooms; + var currentFloor = _game.CurrentFloor; + var rooms = currentFloor.Rooms; - var currentRoom = rooms.SingleOrDefault(x => x.IsPlayerInRoom); + var currentRoom = rooms.SingleOrDefault(x => x.IsPlayerInRoom); - var validRooms = rooms.OfType().ToList(); - if (currentRoom is MonsterRoom currentMonsterRoom) - validRooms.Remove(currentMonsterRoom); + var validRooms = rooms.OfType().ToList(); + if (currentRoom is MonsterRoom currentMonsterRoom) + validRooms.Remove(currentMonsterRoom); - if (validRooms.Count == 0) - return; + if (validRooms.Count == 0) + return; - var roomsGodotCollection = new Godot.Collections.Array(validRooms); - var randomRoom = roomsGodotCollection.PickRandom(); - var spawnPoint = randomRoom.PlayerSpawn; - player.TeleportPlayer((spawnPoint.Rotation, new Vector3(spawnPoint.Position.X, 0, spawnPoint.Position.Z))); - SfxDatabase.Instance.Play(SoundEffect.TeleportToRandomRoom); + var roomsGodotCollection = new Godot.Collections.Array(validRooms); + var randomRoom = roomsGodotCollection.PickRandom(); + var spawnPoint = randomRoom.PlayerSpawn; + player.TeleportPlayer((spawnPoint.Rotation, new Vector3(spawnPoint.Position.X, 0, spawnPoint.Position.Z))); + SfxDatabase.Instance.Play(SoundEffect.TeleportToRandomRoom); } public void ChangeAffinity(ThrowableItem throwableItem) { - var maximumElements = Enum.GetNames(typeof(ElementType)).Length; - var newElement = ((int)throwableItem.ElementType + 1) % maximumElements; - throwableItem.SetElementType((ElementType)newElement); + var maximumElements = Enum.GetNames(typeof(ElementType)).Length; + var newElement = ((int)throwableItem.ElementType + 1) % maximumElements; + throwableItem.SetElementType((ElementType)newElement); - // TODO: Make this an inventory animation to cycle through elements. - throwableItem.SetDescription( - $"Inflicts {throwableItem.ElementType} damage when thrown." + - $"{System.Environment.NewLine}Use item to change Affinity."); + // TODO: Make this an inventory animation to cycle through elements. + throwableItem.SetDescription( + $"Inflicts {throwableItem.ElementType} damage when thrown." + + $"{System.Environment.NewLine}Use item to change Affinity."); - throwableItem.SetCount(throwableItem.Count + 1); + throwableItem.SetCount(throwableItem.Count + 1); } public void WarpToExit() { - var exitRoom = _game.CurrentFloor.Rooms.OfType().Single(); - if (exitRoom.PlayerDiscoveredRoom) - { - SfxDatabase.Instance.Play(SoundEffect.TeleportToExit); - _player.TeleportPlayer((exitRoom.PlayerSpawn.Rotation, exitRoom.PlayerSpawn.Position)); - } + var exitRoom = _game.CurrentFloor.Rooms.OfType().Single(); + if (exitRoom.PlayerDiscoveredRoom) + { + SfxDatabase.Instance.Play(SoundEffect.TeleportToExit); + _player.TeleportPlayer((exitRoom.PlayerSpawn.Rotation, exitRoom.PlayerSpawn.Position)); + } } public void DamagesPlayer(int damage) { - _player.TakeDamage(new AttackData(damage, ElementType.None, true, true)); + _player.TakeDamage(new AttackData(damage, ElementType.None, true, true)); } public void RerollItem(InventoryItem itemToReroll) { - var itemReroller = new ItemReroller(ItemDatabase.Instance); - itemReroller.RerollItem(itemToReroll, _player.Inventory); + var itemReroller = new ItemReroller(ItemDatabase.Instance); + itemReroller.RerollItem(itemToReroll, _player.Inventory); } public T GetRandomItemOfType(T itemToExclude = null) - where T : InventoryItem => ItemDatabase.Instance.PickItem(itemToExclude); + where T : InventoryItem => ItemDatabase.Instance.PickItem(itemToExclude); public void RandomSpell() { - throw new NotImplementedException("Spells not implemented yet."); + throw new NotImplementedException("Spells not implemented yet."); } public void DropTo1HPAndGainRareItem() - where T : InventoryItem + where T : InventoryItem { - _player.HealthComponent.SetCurrentHealth(1); - _player.Inventory.TryAdd(ItemDatabase.Instance.PickRareItem()); + _player.HealthComponent.SetCurrentHealth(1); + _player.Inventory.TryAdd(ItemDatabase.Instance.PickRareItem()); } public void TradeRandomItem(BoxItem box) - where T : InventoryItem + where T : InventoryItem { - var tradableItems = _player.Inventory.Items.OfType().Where(x => x != box).ToList(); + var tradableItems = _player.Inventory.Items.OfType().Where(x => x != box).ToList(); - var rng = new RandomNumberGenerator(); - rng.Randomize(); - var randomIndex = rng.RandiRange(0, tradableItems.Count - 1); - var randomItem = tradableItems[randomIndex]; - if (randomItem is EquipableItem equipableItem) - { - if (_player.EquipmentComponent.IsItemEquipped(equipableItem)) - _player.Unequip(equipableItem); - } - _player.Inventory.Remove(randomItem); + var rng = new RandomNumberGenerator(); + rng.Randomize(); + var randomIndex = rng.RandiRange(0, tradableItems.Count - 1); + var randomItem = tradableItems[randomIndex]; + if (randomItem is EquipableItem equipableItem) + { + if (_player.EquipmentComponent.IsItemEquipped(equipableItem)) + _player.Unequip(equipableItem); + } + _player.Inventory.Remove(randomItem); - GetRandomItemOfType(); + GetRandomItemOfType(); } public IEnumerable TradeAllRandomItems(BoxItem box) - where T : InventoryItem + where T : InventoryItem { - var newInventory = new List(); - var items = _player.Inventory.Items.OfType().Where(x => x != box).ToList(); - foreach (var item in items) - newInventory.Add(GetRandomItemOfType()); + var newInventory = new List(); + var items = _player.Inventory.Items.OfType().Where(x => x != box).ToList(); + foreach (var item in items) + newInventory.Add(GetRandomItemOfType()); - return newInventory; + return newInventory; } public void GetUnobtainedItem() { - var pickableItems = ItemDatabase.Instance.Items.Except(_player.Inventory.Items).ToList(); - var rng = new RandomNumberGenerator(); - rng.Randomize(); - var randomIndex = rng.RandiRange(0, pickableItems.Count - 1); - var selectedItem = pickableItems[randomIndex]; + var pickableItems = ItemDatabase.Instance.Items.Except(_player.Inventory.Items).ToList(); + var rng = new RandomNumberGenerator(); + rng.Randomize(); + var randomIndex = rng.RandiRange(0, pickableItems.Count - 1); + var selectedItem = pickableItems[randomIndex]; - if (selectedItem is ThrowableItem throwableItem) - throwableItem.SetCount(rng.RandiRange(throwableItem.Stats.MinimumCount, throwableItem.Stats.MaximumCount)); + if (selectedItem is ThrowableItem throwableItem) + throwableItem.SetCount(rng.RandiRange(throwableItem.Stats.MinimumCount, throwableItem.Stats.MaximumCount)); - _player.Inventory.TryAdd(selectedItem); + _player.Inventory.TryAdd(selectedItem); } public void GetBasicItem() - where T : InventoryItem + where T : InventoryItem { - _player.Inventory.TryAdd(ItemDatabase.Instance.PickBasicItem()); + _player.Inventory.TryAdd(ItemDatabase.Instance.PickBasicItem()); } } diff --git a/Zennysoft.Game.Ma/src/items/InventoryItemStats.cs b/Zennysoft.Game.Ma/src/items/InventoryItemStats.cs index cabd8b4e1..0b82d9891 100644 --- a/Zennysoft.Game.Ma/src/items/InventoryItemStats.cs +++ b/Zennysoft.Game.Ma/src/items/InventoryItemStats.cs @@ -29,7 +29,7 @@ public abstract partial class InventoryItemStats : Resource [Export] [Save("weapon_luck")] - public double BonusLuck { get; set; } = 0.05; + public int BonusLuck { get; set; } = 5; [Export] [Save("equipment_bonus_hp")] diff --git a/Zennysoft.Game.Ma/src/items/accessory/Accessory.cs b/Zennysoft.Game.Ma/src/items/accessory/Accessory.cs index 605dc9cfe..0d9a45946 100644 --- a/Zennysoft.Game.Ma/src/items/accessory/Accessory.cs +++ b/Zennysoft.Game.Ma/src/items/accessory/Accessory.cs @@ -36,6 +36,8 @@ public partial class Accessory : EquipableItem public override int BonusVT => Stats.BonusVT; + public override int BonusLuck { get => Stats.BonusLuck + _bonusLuck; } + public override ElementalResistanceSet ElementalResistance => new ElementalResistanceSet(Stats.AeolicResistance, Stats.HydricResistance, Stats.IgneousResistance, Stats.FerrumResistance, Stats.TelluricResistance, Stats.HolyResistance); [Save("accessory_tag")] @@ -43,6 +45,9 @@ public partial class Accessory : EquipableItem public override ItemTag ItemTag => Stats.ItemTag; + [Save("accessory_bonus_luck")] + private int _bonusLuck { get; set; } = 0; + [Export] [Save("accessory_stats")] public AccessoryStats Stats { get; set; } = new AccessoryStats(); diff --git a/Zennysoft.Game.Ma/src/items/armor/Armor.cs b/Zennysoft.Game.Ma/src/items/armor/Armor.cs index cfe229bd3..b83e80ff3 100644 --- a/Zennysoft.Game.Ma/src/items/armor/Armor.cs +++ b/Zennysoft.Game.Ma/src/items/armor/Armor.cs @@ -31,9 +31,14 @@ public partial class Armor : EquipableItem public override int BonusDefense => Stats.BonusDefense + _bonusDefense; + public override int BonusLuck { get => Stats.BonusLuck + _bonusLuck; } + [Save("bonus_defense")] private int _bonusDefense { get; set; } = 0; + [Save("armor_bonus_luck")] + private int _bonusLuck { get; set; } = 0; + public override ElementalResistanceSet ElementalResistance => new ElementalResistanceSet(Stats.AeolicResistance, Stats.HydricResistance, Stats.IgneousResistance, Stats.FerrumResistance, Stats.TelluricResistance, Stats.HolyResistance); public void IncreaseArmorDefense(int bonus) => _bonusDefense += bonus; diff --git a/Zennysoft.Game.Ma/src/items/weapons/Weapon.cs b/Zennysoft.Game.Ma/src/items/weapons/Weapon.cs index 392a1918a..47df0bda2 100644 --- a/Zennysoft.Game.Ma/src/items/weapons/Weapon.cs +++ b/Zennysoft.Game.Ma/src/items/weapons/Weapon.cs @@ -55,12 +55,17 @@ public partial class Weapon : EquipableItem public override int BonusDefense { get => Stats.BonusDefense + _bonusDefense; } + public override int BonusLuck { get => Stats.BonusLuck + _bonusLuck; } + [Save("weapon_bonus_damage")] private int _bonusDamage { get; set; } = 0; [Save("weapon_bonus_damage")] private int _bonusDefense { get; set; } = 0; + [Save("weapon_bonus_luck")] + private int _bonusLuck { get; set; } = 0; + [Export] [Save("weapon_stats")] public WeaponStats Stats { get; set; } = new WeaponStats(); diff --git a/Zennysoft.Game.Ma/src/items/weapons/WeaponStats.cs b/Zennysoft.Game.Ma/src/items/weapons/WeaponStats.cs index 196048871..a5f36ec8e 100644 --- a/Zennysoft.Game.Ma/src/items/weapons/WeaponStats.cs +++ b/Zennysoft.Game.Ma/src/items/weapons/WeaponStats.cs @@ -21,6 +21,10 @@ public partial class WeaponStats : InventoryItemStats [Save("weapon_tag")] public WeaponTag WeaponTag { get; set; } = WeaponTag.None; + [Export] + [Save("weapon_self_damage")] + public int SelfDamage { get; set; } = 0; + [Export] public SoundEffect SoundEffect { get; set; } = SoundEffect.WeaponQuickSlash; } diff --git a/Zennysoft.Game.Ma/src/items/weapons/resources/PlasmaSword.tres b/Zennysoft.Game.Ma/src/items/weapons/resources/PlasmaSword.tres index 531936cee..3d22b81cd 100644 --- a/Zennysoft.Game.Ma/src/items/weapons/resources/PlasmaSword.tres +++ b/Zennysoft.Game.Ma/src/items/weapons/resources/PlasmaSword.tres @@ -7,14 +7,15 @@ script = ExtResource("2_rgna4") AttackSpeed = 1.0 WeaponElement = 0 -WeaponTag = 0 +WeaponTag = 7 +SelfDamage = 0 SoundEffect = 22 Name = "Plasma Sword" Description = "Has the power to occasionally instantly disintegrate an enemy" -SpawnRate = 0.05 -BonusAttack = 14 +SpawnRate = 0.5 +BonusAttack = 0 BonusDefense = 0 -BonusLuck = 0.05 +BonusLuck = 10 BonusHP = 0 BonusVT = 0 AeolicResistance = 0 diff --git a/Zennysoft.Game.Ma/src/player/Player.cs b/Zennysoft.Game.Ma/src/player/Player.cs index cee00b184..2e568a41e 100644 --- a/Zennysoft.Game.Ma/src/player/Player.cs +++ b/Zennysoft.Game.Ma/src/player/Player.cs @@ -108,6 +108,7 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide private Vector3 _knockbackDirection = Vector3.Zero; private ItemReroller _itemReroller; + private PlayerEffectService _playerEffectService; public void Initialize() { @@ -128,6 +129,7 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide EquipmentComponent = new EquipmentComponent(); _itemReroller = new ItemReroller(ItemDatabase.Instance); + _playerEffectService = new PlayerEffectService(this); Settings = new PlayerLogic.Settings() { RotationSpeed = RotationSpeed, MoveSpeed = MoveSpeed, Acceleration = Acceleration }; @@ -212,7 +214,7 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide { _camera3D.AddShake(1.0f); TakeDamageAnimationPlayer.Play("take_damage"); - var damageReceived = DamageCalculator.CalculateDamage(damage, DefenseComponent.CurrentDefense.Value + EquipmentComponent.BonusDefense, EquipmentComponent.ElementalResistance); + var damageReceived = DamageCalculator.CalculateDamage(damage, TotalDefense, EquipmentComponent.ElementalResistance); HealthComponent.Damage(damageReceived); SfxDatabase.Instance.Play(SoundEffect.TakeDamage); } @@ -225,6 +227,12 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide public void PlayJumpScareAnimation() => PlayerFXAnimations.Play("jump_scare"); + public int TotalAttack => AttackComponent.CurrentAttack.Value + EquipmentComponent.BonusAttack; + + public int TotalDefense => DefenseComponent.CurrentDefense.Value + EquipmentComponent.BonusDefense; + + public int TotalLuck => LuckComponent.Luck.Value + EquipmentComponent.BonusLuck; + public void LevelUp() { ExperiencePointsComponent.LevelUp(); @@ -414,11 +422,9 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide private void HitEnemy(IEnemy enemy) { - var ignoreElementalResistance = (EquipmentComponent.EquippedWeapon.Value as Weapon).WeaponTag == WeaponTag.IgnoreAffinity; - var ignoreDefense = (EquipmentComponent.EquippedWeapon.Value as Weapon).WeaponTag == WeaponTag.IgnoreDefense; - var isCriticalHit = BattleExtensions.IsCriticalHit(LuckComponent.Luck.Value + EquipmentComponent.BonusLuck); - var totalDamage = AttackComponent.CurrentAttack.Value + EquipmentComponent.BonusAttack; - var element = (EquipmentComponent.EquippedWeapon.Value as Weapon).WeaponElement; + var weapon = EquipmentComponent.EquippedWeapon.Value as Weapon; + var isCriticalHit = BattleExtensions.IsCriticalHit(TotalLuck); + var totalDamage = TotalAttack; if (isCriticalHit) { @@ -426,21 +432,16 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide SfxDatabase.Instance.Play(SoundEffect.Crit); } - var baseAttack = new AttackData(totalDamage, element, ignoreDefense, ignoreElementalResistance); + var baseAttack = new AttackData(totalDamage, weapon.WeaponElement, weapon.WeaponTag == WeaponTag.IgnoreDefense, weapon.WeaponTag == WeaponTag.IgnoreAffinity); var damageDealt = DamageCalculator.CalculateDamage(baseAttack, enemy.DefenseComponent.CurrentDefense.Value, enemy.ElementalResistanceSet); enemy.HealthComponent.Damage(damageDealt); - if (((Weapon)EquipmentComponent.EquippedWeapon.Value).WeaponTag == WeaponTag.Knockback && enemy is IKnockbackable knockbackable) + if (weapon.WeaponTag == WeaponTag.Knockback && enemy is IKnockbackable knockbackable) knockbackable.Knockback(0.3f, -CurrentBasis.Z.Normalized()); - if (((Weapon)EquipmentComponent.EquippedWeapon.Value).WeaponTag == WeaponTag.SelfDamage) - HealthComponent.Damage(5); - if (((Weapon)EquipmentComponent.EquippedWeapon.Value).WeaponTag == WeaponTag.Instakill) - { - var rng = new RandomNumberGenerator(); - rng.Randomize(); - if (rng.Randf() <= LuckComponent.Luck.Value) - enemy.Die(); - } + if (weapon.WeaponTag == WeaponTag.SelfDamage) + _playerEffectService.TakeSelfDamage(weapon.Stats.SelfDamage); + if (weapon.WeaponTag == WeaponTag.Instakill) + _playerEffectService.Instakill(enemy); } private void CollisionDetector_AreaEntered(Area3D area) diff --git a/Zennysoft.Game.Ma/src/player/Player.tscn b/Zennysoft.Game.Ma/src/player/Player.tscn index 7e74e51ef..4f8bd8350 100644 --- a/Zennysoft.Game.Ma/src/player/Player.tscn +++ b/Zennysoft.Game.Ma/src/player/Player.tscn @@ -427,6 +427,34 @@ tracks/1/keys = { "values": [0, 29] } +[sub_resource type="Animation" id="Animation_wvcio"] +resource_name = "jump_scare" +length = 5.0005 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("SubViewportContainer/SubViewport/Prosc Message:texture") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 4.2), +"transitions": PackedFloat32Array(1, 1), +"update": 1, +"values": [ExtResource("6_bj1ma"), ExtResource("6_bj1ma")] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("SubViewportContainer/SubViewport/Prosc Message:modulate") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0, 0.6, 1.6, 2.6, 3.6, 4.16667), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Color(1, 1, 1, 0), Color(1, 1, 1, 1), Color(1, 1, 1, 0.48), Color(1, 1, 1, 1), Color(1, 1, 1, 0.48), Color(1, 1, 1, 0)] +} + [sub_resource type="Animation" id="Animation_v5qoq"] resource_name = "normal_attack" length = 0.666669 @@ -456,34 +484,6 @@ tracks/1/keys = { "values": [0, 30] } -[sub_resource type="Animation" id="Animation_wvcio"] -resource_name = "jump_scare" -length = 5.0005 -tracks/0/type = "value" -tracks/0/imported = false -tracks/0/enabled = true -tracks/0/path = NodePath("SubViewportContainer/SubViewport/Prosc Message:texture") -tracks/0/interp = 1 -tracks/0/loop_wrap = true -tracks/0/keys = { -"times": PackedFloat32Array(0, 4.2), -"transitions": PackedFloat32Array(1, 1), -"update": 1, -"values": [ExtResource("6_bj1ma"), ExtResource("6_bj1ma")] -} -tracks/1/type = "value" -tracks/1/imported = false -tracks/1/enabled = true -tracks/1/path = NodePath("SubViewportContainer/SubViewport/Prosc Message:modulate") -tracks/1/interp = 1 -tracks/1/loop_wrap = true -tracks/1/keys = { -"times": PackedFloat32Array(0, 0.6, 1.6, 2.6, 3.6, 4.16667), -"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1), -"update": 0, -"values": [Color(1, 1, 1, 0), Color(1, 1, 1, 1), Color(1, 1, 1, 0.48), Color(1, 1, 1, 1), Color(1, 1, 1, 0.48), Color(1, 1, 1, 0)] -} - [sub_resource type="AnimationLibrary" id="AnimationLibrary_ebyyx"] _data = { &"Divinity_Recall": SubResource("Animation_j5wmh"), @@ -12094,6 +12094,7 @@ animations = [{ collision_layer = 802 collision_mask = 775 script = ExtResource("1_xcol5") +HealthTimerIsActive = true [node name="MainCollision" type="CollisionShape3D" parent="."] unique_name_in_owner = true @@ -12112,6 +12113,7 @@ unique_name_in_owner = true transform = Transform3D(1, 0, 0, 0, 0.999848, -0.0174524, 0, 0.0174524, 0.999848, 0.003, 2.1, -0.01) current = true fov = 52.9 +_maxZ = null [node name="MeshInstance3D" type="MeshInstance3D" parent="Camera/Camera3D"] extra_cull_margin = 16384.0 diff --git a/Zennysoft.Game.Ma/src/player/PlayerEffectService.cs b/Zennysoft.Game.Ma/src/player/PlayerEffectService.cs new file mode 100644 index 000000000..8e17a933b --- /dev/null +++ b/Zennysoft.Game.Ma/src/player/PlayerEffectService.cs @@ -0,0 +1,26 @@ +using Godot; +using Zennysoft.Ma.Adapter; +using Zennysoft.Ma.Adapter.Entity; + +internal class PlayerEffectService +{ + private IPlayer _player; + public PlayerEffectService(IPlayer player) + { + _player = player; + } + + public void TakeSelfDamage(int damage) + { + _player.TakeDamage(new AttackData(5, ElementType.None, true, true)); + } + + public void Instakill(IEnemy enemy) + { + var rng = new RandomNumberGenerator(); + rng.Randomize(); + var rand = rng.RandiRange(1, 100); + if (rand <= _player.TotalLuck) + enemy.Die(); + } +} \ No newline at end of file diff --git a/Zennysoft.Game.Ma/src/player/PlayerEffectService.cs.uid b/Zennysoft.Game.Ma/src/player/PlayerEffectService.cs.uid new file mode 100644 index 000000000..db9133b3d --- /dev/null +++ b/Zennysoft.Game.Ma/src/player/PlayerEffectService.cs.uid @@ -0,0 +1 @@ +uid://dgbhf31crsdmi