Implement plasma sword instakill

This commit is contained in:
2026-02-09 23:01:52 -08:00
parent bfaa324e6a
commit aba325ff2b
12 changed files with 273 additions and 219 deletions

View File

@@ -46,6 +46,10 @@ public interface IPlayer : IKillable, ICharacterBody3D
public bool CanEquipState { get; set; } public bool CanEquipState { get; set; }
public int TotalAttack { get; }
public int TotalDefense { get; }
public int TotalLuck { get; }
public event Action PlayerDied; public event Action PlayerDied;
public delegate InventoryItem RerollItem(InventoryItem item); public delegate InventoryItem RerollItem(InventoryItem item);
} }

View File

@@ -15,301 +15,301 @@ public class EffectService
public EffectService(IGame game, IPlayer player, IMap map) public EffectService(IGame game, IPlayer player, IMap map)
{ {
_game = game; _game = game;
_player = player; _player = player;
_map = map; _map = map;
} }
public void TeleportEnemiesToCurrentRoom() public void TeleportEnemiesToCurrentRoom()
{ {
var currentFloor = _game.CurrentFloor; var currentFloor = _game.CurrentFloor;
var rooms = currentFloor.Rooms; var rooms = currentFloor.Rooms;
var currentRoom = _map.GetPlayersCurrentRoom(); var currentRoom = _map.GetPlayersCurrentRoom();
if (currentRoom is not MonsterRoom) if (currentRoom is not MonsterRoom)
return; return;
var validRooms = rooms.OfType<MonsterRoom>().ToList(); var validRooms = rooms.OfType<MonsterRoom>().ToList();
if (currentRoom is MonsterRoom monsterRoom) if (currentRoom is MonsterRoom monsterRoom)
validRooms.Remove(monsterRoom); validRooms.Remove(monsterRoom);
var currentMonsterRoom = (MonsterRoom)currentRoom; var currentMonsterRoom = (MonsterRoom)currentRoom;
var enemyList = validRooms.SelectMany(x => x.GetEnemiesInCurrentRoom()).OfType<Enemy>(); var enemyList = validRooms.SelectMany(x => x.GetEnemiesInCurrentRoom()).OfType<Enemy>();
foreach (var enemy in enemyList) foreach (var enemy in enemyList)
enemy.MoveEnemyToNewRoom(currentMonsterRoom); enemy.MoveEnemyToNewRoom(currentMonsterRoom);
} }
public void KillHalfEnemiesInRoom() public void KillHalfEnemiesInRoom()
{ {
var currentRoom = _map.GetPlayersCurrentRoom(); var currentRoom = _map.GetPlayersCurrentRoom();
if (currentRoom is not MonsterRoom) if (currentRoom is not MonsterRoom)
return; return;
var currentMonsterRoom = (MonsterRoom)currentRoom; var currentMonsterRoom = (MonsterRoom)currentRoom;
var enemyList = currentMonsterRoom.GetEnemiesInCurrentRoom().OfType<Enemy>().ToList(); var enemyList = currentMonsterRoom.GetEnemiesInCurrentRoom().OfType<Enemy>().ToList();
var enemiesToKill = enemyList.Count / 2; var enemiesToKill = enemyList.Count / 2;
for (var i = 0; i < enemiesToKill; i++) for (var i = 0; i < enemiesToKill; i++)
enemyList[i].Die(); enemyList[i].Die();
} }
public void TurnAllEnemiesInRoomIntoHealingItem() public void TurnAllEnemiesInRoomIntoHealingItem()
{ {
var currentRoom = _map.GetPlayersCurrentRoom(); var currentRoom = _map.GetPlayersCurrentRoom();
if (currentRoom is not MonsterRoom) if (currentRoom is not MonsterRoom)
return; return;
var currentEnemies = currentRoom.EnemiesInRoom.OfType<Enemy>().ToList(); var currentEnemies = currentRoom.EnemiesInRoom.OfType<Enemy>().ToList();
foreach (var enemy in currentEnemies) foreach (var enemy in currentEnemies)
{ {
enemy.OnMorph(); enemy.OnMorph();
DropHealingItem(enemy.GlobalPosition); DropHealingItem(enemy.GlobalPosition);
} }
if (currentEnemies.Any()) if (currentEnemies.Any())
SfxDatabase.Instance.Play(SoundEffect.TurnAllEnemiesIntoHealingItems); SfxDatabase.Instance.Play(SoundEffect.TurnAllEnemiesIntoHealingItems);
} }
public void DropHealingItem(Vector3 vector) public void DropHealingItem(Vector3 vector)
{ {
var consumableFolder = "res://src/items/consumable"; var consumableFolder = "res://src/items/consumable";
var restorativeScene = GD.Load<PackedScene>($"{consumableFolder}/ConsumableItem.tscn"); var restorativeScene = GD.Load<PackedScene>($"{consumableFolder}/ConsumableItem.tscn");
var consumable = restorativeScene.Instantiate<ConsumableItem>(); var consumable = restorativeScene.Instantiate<ConsumableItem>();
var resourceFiles = DirAccess.GetFilesAt($"{consumableFolder}/resources"); var resourceFiles = DirAccess.GetFilesAt($"{consumableFolder}/resources");
var rng = new RandomNumberGenerator(); var rng = new RandomNumberGenerator();
rng.Randomize(); rng.Randomize();
var randomResource = resourceFiles[rng.RandiRange(0, resourceFiles.Length - 1)]; var randomResource = resourceFiles[rng.RandiRange(0, resourceFiles.Length - 1)];
var randomFile = ResourceLoader.Load<ConsumableItemStats>($"{consumableFolder}/resources/{randomResource}"); var randomFile = ResourceLoader.Load<ConsumableItemStats>($"{consumableFolder}/resources/{randomResource}".TrimSuffix(".remap"));
consumable.Stats = randomFile; consumable.Stats = randomFile;
_game.AddChild(consumable); _game.AddChild(consumable);
consumable.GlobalPosition = vector; consumable.GlobalPosition = vector;
} }
public void HealAllEnemiesAndPlayerInRoomToFull() public void HealAllEnemiesAndPlayerInRoomToFull()
{ {
var currentRoom = _map.GetPlayersCurrentRoom(); var currentRoom = _map.GetPlayersCurrentRoom();
if (currentRoom is not MonsterRoom) if (currentRoom is not MonsterRoom)
return; return;
currentRoom.EnemiesInRoom.ForEach(e => e.HealthComponent.SetCurrentHealth(e.HealthComponent.MaximumHP.Value)); currentRoom.EnemiesInRoom.ForEach(e => e.HealthComponent.SetCurrentHealth(e.HealthComponent.MaximumHP.Value));
_player.HealthComponent.SetCurrentHealth(_player.HealthComponent.MaximumHP.Value); _player.HealthComponent.SetCurrentHealth(_player.HealthComponent.MaximumHP.Value);
} }
public void AbsorbHPFromAllEnemiesInRoom() public void AbsorbHPFromAllEnemiesInRoom()
{ {
var currentRoom = _map.GetPlayersCurrentRoom(); var currentRoom = _map.GetPlayersCurrentRoom();
if (currentRoom is not MonsterRoom) if (currentRoom is not MonsterRoom)
return; return;
var currentEnemies = currentRoom.EnemiesInRoom; var currentEnemies = currentRoom.EnemiesInRoom;
var hpToAbsorb = 0.0; var hpToAbsorb = 0.0;
foreach (var enemy in currentEnemies) foreach (var enemy in currentEnemies)
{ {
var absorbAmount = enemy.HealthComponent.MaximumHP.Value * 0.05; var absorbAmount = enemy.HealthComponent.MaximumHP.Value * 0.05;
enemy.HealthComponent.Damage((int)absorbAmount); enemy.HealthComponent.Damage((int)absorbAmount);
hpToAbsorb += absorbAmount; hpToAbsorb += absorbAmount;
enemy.OnAbsorb(); enemy.OnAbsorb();
} }
_player.HealthComponent.Heal((int)hpToAbsorb); _player.HealthComponent.Heal((int)hpToAbsorb);
GD.Print("HP to absorb: " + hpToAbsorb); GD.Print("HP to absorb: " + hpToAbsorb);
} }
public void DealElementalDamageToAllEnemiesInRoom(ElementType elementType) public void DealElementalDamageToAllEnemiesInRoom(ElementType elementType)
{ {
var currentRoom = _map.GetPlayersCurrentRoom(); var currentRoom = _map.GetPlayersCurrentRoom();
if (currentRoom is not MonsterRoom) if (currentRoom is not MonsterRoom)
return; return;
var currentEnemies = currentRoom.EnemiesInRoom; var currentEnemies = currentRoom.EnemiesInRoom;
foreach (var enemy in currentEnemies) foreach (var enemy in currentEnemies)
{ {
var damageDealt = DamageCalculator.CalculateDamage(new AttackData(20, elementType), 10, enemy.ElementalResistanceSet); var damageDealt = DamageCalculator.CalculateDamage(new AttackData(20, elementType), 10, enemy.ElementalResistanceSet);
enemy.HealthComponent.Damage(damageDealt); enemy.HealthComponent.Damage(damageDealt);
} }
} }
public void SwapHPandVT() public void SwapHPandVT()
{ {
var oldHp = _player.HealthComponent.CurrentHP.Value; var oldHp = _player.HealthComponent.CurrentHP.Value;
var oldVt = _player.VTComponent.CurrentVT.Value; var oldVt = _player.VTComponent.CurrentVT.Value;
_player.HealthComponent.SetCurrentHealth(oldVt); _player.HealthComponent.SetCurrentHealth(oldVt);
_player.VTComponent.SetVT(oldHp); _player.VTComponent.SetVT(oldHp);
SfxDatabase.Instance.Play(SoundEffect.SwapHPAndVT); SfxDatabase.Instance.Play(SoundEffect.SwapHPAndVT);
} }
public void RandomEffect(EffectItem item) public void RandomEffect(EffectItem item)
{ {
var itemEffects = Enum.GetValues<UsableItemTag>().ToList(); var itemEffects = Enum.GetValues<UsableItemTag>().ToList();
itemEffects.Remove(UsableItemTag.RandomEffect); itemEffects.Remove(UsableItemTag.RandomEffect);
itemEffects.Remove(UsableItemTag.None); itemEffects.Remove(UsableItemTag.None);
var randomEffect = new Godot.Collections.Array<UsableItemTag>(itemEffects).PickRandom(); var randomEffect = new Godot.Collections.Array<UsableItemTag>(itemEffects).PickRandom();
item.SetEffectTag(randomEffect); item.SetEffectTag(randomEffect);
_game.UseItem(item); _game.UseItem(item);
} }
public void RaiseCurrentWeaponAttack() public void RaiseCurrentWeaponAttack()
{ {
if (string.IsNullOrEmpty(_player.EquipmentComponent.EquippedWeapon.Value.ItemName)) if (string.IsNullOrEmpty(_player.EquipmentComponent.EquippedWeapon.Value.ItemName))
return; return;
var currentWeapon = (Weapon)_player.EquipmentComponent.EquippedWeapon.Value; var currentWeapon = (Weapon)_player.EquipmentComponent.EquippedWeapon.Value;
currentWeapon.IncreaseWeaponAttack(1); currentWeapon.IncreaseWeaponAttack(1);
SfxDatabase.Instance.Play(SoundEffect.IncreaseStat); SfxDatabase.Instance.Play(SoundEffect.IncreaseStat);
} }
public void RaiseCurrentArmorDefense() public void RaiseCurrentArmorDefense()
{ {
if (string.IsNullOrEmpty(_player.EquipmentComponent.EquippedArmor.Value.ItemName)) if (string.IsNullOrEmpty(_player.EquipmentComponent.EquippedArmor.Value.ItemName))
return; return;
var currentArmor = (Armor)_player.EquipmentComponent.EquippedArmor.Value; var currentArmor = (Armor)_player.EquipmentComponent.EquippedArmor.Value;
currentArmor.IncreaseArmorDefense(1); currentArmor.IncreaseArmorDefense(1);
SfxDatabase.Instance.Play(SoundEffect.IncreaseStat); SfxDatabase.Instance.Play(SoundEffect.IncreaseStat);
} }
public void RaiseLevel() => _player.LevelUp(); public void RaiseLevel() => _player.LevelUp();
public void TeleportToRandomRoom(IEnemy enemy) public void TeleportToRandomRoom(IEnemy enemy)
{ {
var currentFloor = _game.CurrentFloor; var currentFloor = _game.CurrentFloor;
var rooms = currentFloor.Rooms; var rooms = currentFloor.Rooms;
var currentRoom = enemy.GetCurrentRoom(rooms); var currentRoom = enemy.GetCurrentRoom(rooms);
var validRooms = rooms.OfType<MonsterRoom>().ToList(); var validRooms = rooms.OfType<MonsterRoom>().ToList();
if (currentRoom is MonsterRoom currentMonsterRoom) if (currentRoom is MonsterRoom currentMonsterRoom)
validRooms.Remove(currentMonsterRoom); validRooms.Remove(currentMonsterRoom);
if (validRooms.Count == 0) if (validRooms.Count == 0)
return; return;
var roomsGodotCollection = new Godot.Collections.Array<MonsterRoom>(validRooms); var roomsGodotCollection = new Godot.Collections.Array<MonsterRoom>(validRooms);
var randomRoom = roomsGodotCollection.PickRandom(); var randomRoom = roomsGodotCollection.PickRandom();
enemy.MoveEnemyToNewRoom(randomRoom); enemy.MoveEnemyToNewRoom(randomRoom);
} }
public void TeleportToRandomRoom(IPlayer player) public void TeleportToRandomRoom(IPlayer player)
{ {
var currentFloor = _game.CurrentFloor; var currentFloor = _game.CurrentFloor;
var rooms = currentFloor.Rooms; var rooms = currentFloor.Rooms;
var currentRoom = rooms.SingleOrDefault(x => x.IsPlayerInRoom); var currentRoom = rooms.SingleOrDefault(x => x.IsPlayerInRoom);
var validRooms = rooms.OfType<MonsterRoom>().ToList(); var validRooms = rooms.OfType<MonsterRoom>().ToList();
if (currentRoom is MonsterRoom currentMonsterRoom) if (currentRoom is MonsterRoom currentMonsterRoom)
validRooms.Remove(currentMonsterRoom); validRooms.Remove(currentMonsterRoom);
if (validRooms.Count == 0) if (validRooms.Count == 0)
return; return;
var roomsGodotCollection = new Godot.Collections.Array<MonsterRoom>(validRooms); var roomsGodotCollection = new Godot.Collections.Array<MonsterRoom>(validRooms);
var randomRoom = roomsGodotCollection.PickRandom(); var randomRoom = roomsGodotCollection.PickRandom();
var spawnPoint = randomRoom.PlayerSpawn; var spawnPoint = randomRoom.PlayerSpawn;
player.TeleportPlayer((spawnPoint.Rotation, new Vector3(spawnPoint.Position.X, 0, spawnPoint.Position.Z))); player.TeleportPlayer((spawnPoint.Rotation, new Vector3(spawnPoint.Position.X, 0, spawnPoint.Position.Z)));
SfxDatabase.Instance.Play(SoundEffect.TeleportToRandomRoom); SfxDatabase.Instance.Play(SoundEffect.TeleportToRandomRoom);
} }
public void ChangeAffinity(ThrowableItem throwableItem) public void ChangeAffinity(ThrowableItem throwableItem)
{ {
var maximumElements = Enum.GetNames(typeof(ElementType)).Length; var maximumElements = Enum.GetNames(typeof(ElementType)).Length;
var newElement = ((int)throwableItem.ElementType + 1) % maximumElements; var newElement = ((int)throwableItem.ElementType + 1) % maximumElements;
throwableItem.SetElementType((ElementType)newElement); throwableItem.SetElementType((ElementType)newElement);
// TODO: Make this an inventory animation to cycle through elements. // TODO: Make this an inventory animation to cycle through elements.
throwableItem.SetDescription( throwableItem.SetDescription(
$"Inflicts {throwableItem.ElementType} damage when thrown." + $"Inflicts {throwableItem.ElementType} damage when thrown." +
$"{System.Environment.NewLine}Use item to change Affinity."); $"{System.Environment.NewLine}Use item to change Affinity.");
throwableItem.SetCount(throwableItem.Count + 1); throwableItem.SetCount(throwableItem.Count + 1);
} }
public void WarpToExit() public void WarpToExit()
{ {
var exitRoom = _game.CurrentFloor.Rooms.OfType<ExitRoom>().Single(); var exitRoom = _game.CurrentFloor.Rooms.OfType<ExitRoom>().Single();
if (exitRoom.PlayerDiscoveredRoom) if (exitRoom.PlayerDiscoveredRoom)
{ {
SfxDatabase.Instance.Play(SoundEffect.TeleportToExit); SfxDatabase.Instance.Play(SoundEffect.TeleportToExit);
_player.TeleportPlayer((exitRoom.PlayerSpawn.Rotation, exitRoom.PlayerSpawn.Position)); _player.TeleportPlayer((exitRoom.PlayerSpawn.Rotation, exitRoom.PlayerSpawn.Position));
} }
} }
public void DamagesPlayer(int damage) 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) public void RerollItem(InventoryItem itemToReroll)
{ {
var itemReroller = new ItemReroller(ItemDatabase.Instance); var itemReroller = new ItemReroller(ItemDatabase.Instance);
itemReroller.RerollItem(itemToReroll, _player.Inventory); itemReroller.RerollItem(itemToReroll, _player.Inventory);
} }
public T GetRandomItemOfType<T>(T itemToExclude = null) public T GetRandomItemOfType<T>(T itemToExclude = null)
where T : InventoryItem => ItemDatabase.Instance.PickItem(itemToExclude); where T : InventoryItem => ItemDatabase.Instance.PickItem(itemToExclude);
public void RandomSpell() public void RandomSpell()
{ {
throw new NotImplementedException("Spells not implemented yet."); throw new NotImplementedException("Spells not implemented yet.");
} }
public void DropTo1HPAndGainRareItem<T>() public void DropTo1HPAndGainRareItem<T>()
where T : InventoryItem where T : InventoryItem
{ {
_player.HealthComponent.SetCurrentHealth(1); _player.HealthComponent.SetCurrentHealth(1);
_player.Inventory.TryAdd(ItemDatabase.Instance.PickRareItem<T>()); _player.Inventory.TryAdd(ItemDatabase.Instance.PickRareItem<T>());
} }
public void TradeRandomItem<T>(BoxItem box) public void TradeRandomItem<T>(BoxItem box)
where T : InventoryItem where T : InventoryItem
{ {
var tradableItems = _player.Inventory.Items.OfType<T>().Where(x => x != box).ToList(); var tradableItems = _player.Inventory.Items.OfType<T>().Where(x => x != box).ToList();
var rng = new RandomNumberGenerator(); var rng = new RandomNumberGenerator();
rng.Randomize(); rng.Randomize();
var randomIndex = rng.RandiRange(0, tradableItems.Count - 1); var randomIndex = rng.RandiRange(0, tradableItems.Count - 1);
var randomItem = tradableItems[randomIndex]; var randomItem = tradableItems[randomIndex];
if (randomItem is EquipableItem equipableItem) if (randomItem is EquipableItem equipableItem)
{ {
if (_player.EquipmentComponent.IsItemEquipped(equipableItem)) if (_player.EquipmentComponent.IsItemEquipped(equipableItem))
_player.Unequip(equipableItem); _player.Unequip(equipableItem);
} }
_player.Inventory.Remove(randomItem); _player.Inventory.Remove(randomItem);
GetRandomItemOfType<T>(); GetRandomItemOfType<T>();
} }
public IEnumerable<InventoryItem> TradeAllRandomItems<T>(BoxItem box) public IEnumerable<InventoryItem> TradeAllRandomItems<T>(BoxItem box)
where T : InventoryItem where T : InventoryItem
{ {
var newInventory = new List<InventoryItem>(); var newInventory = new List<InventoryItem>();
var items = _player.Inventory.Items.OfType<T>().Where(x => x != box).ToList(); var items = _player.Inventory.Items.OfType<T>().Where(x => x != box).ToList();
foreach (var item in items) foreach (var item in items)
newInventory.Add(GetRandomItemOfType<T>()); newInventory.Add(GetRandomItemOfType<T>());
return newInventory; return newInventory;
} }
public void GetUnobtainedItem() public void GetUnobtainedItem()
{ {
var pickableItems = ItemDatabase.Instance.Items.Except(_player.Inventory.Items).ToList(); var pickableItems = ItemDatabase.Instance.Items.Except(_player.Inventory.Items).ToList();
var rng = new RandomNumberGenerator(); var rng = new RandomNumberGenerator();
rng.Randomize(); rng.Randomize();
var randomIndex = rng.RandiRange(0, pickableItems.Count - 1); var randomIndex = rng.RandiRange(0, pickableItems.Count - 1);
var selectedItem = pickableItems[randomIndex]; var selectedItem = pickableItems[randomIndex];
if (selectedItem is ThrowableItem throwableItem) if (selectedItem is ThrowableItem throwableItem)
throwableItem.SetCount(rng.RandiRange(throwableItem.Stats.MinimumCount, throwableItem.Stats.MaximumCount)); throwableItem.SetCount(rng.RandiRange(throwableItem.Stats.MinimumCount, throwableItem.Stats.MaximumCount));
_player.Inventory.TryAdd(selectedItem); _player.Inventory.TryAdd(selectedItem);
} }
public void GetBasicItem<T>() public void GetBasicItem<T>()
where T : InventoryItem where T : InventoryItem
{ {
_player.Inventory.TryAdd(ItemDatabase.Instance.PickBasicItem<T>()); _player.Inventory.TryAdd(ItemDatabase.Instance.PickBasicItem<T>());
} }
} }

View File

@@ -29,7 +29,7 @@ public abstract partial class InventoryItemStats : Resource
[Export] [Export]
[Save("weapon_luck")] [Save("weapon_luck")]
public double BonusLuck { get; set; } = 0.05; public int BonusLuck { get; set; } = 5;
[Export] [Export]
[Save("equipment_bonus_hp")] [Save("equipment_bonus_hp")]

View File

@@ -36,6 +36,8 @@ public partial class Accessory : EquipableItem
public override int BonusVT => Stats.BonusVT; 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); public override ElementalResistanceSet ElementalResistance => new ElementalResistanceSet(Stats.AeolicResistance, Stats.HydricResistance, Stats.IgneousResistance, Stats.FerrumResistance, Stats.TelluricResistance, Stats.HolyResistance);
[Save("accessory_tag")] [Save("accessory_tag")]
@@ -43,6 +45,9 @@ public partial class Accessory : EquipableItem
public override ItemTag ItemTag => Stats.ItemTag; public override ItemTag ItemTag => Stats.ItemTag;
[Save("accessory_bonus_luck")]
private int _bonusLuck { get; set; } = 0;
[Export] [Export]
[Save("accessory_stats")] [Save("accessory_stats")]
public AccessoryStats Stats { get; set; } = new AccessoryStats(); public AccessoryStats Stats { get; set; } = new AccessoryStats();

View File

@@ -31,9 +31,14 @@ public partial class Armor : EquipableItem
public override int BonusDefense => Stats.BonusDefense + _bonusDefense; public override int BonusDefense => Stats.BonusDefense + _bonusDefense;
public override int BonusLuck { get => Stats.BonusLuck + _bonusLuck; }
[Save("bonus_defense")] [Save("bonus_defense")]
private int _bonusDefense { get; set; } = 0; 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 override ElementalResistanceSet ElementalResistance => new ElementalResistanceSet(Stats.AeolicResistance, Stats.HydricResistance, Stats.IgneousResistance, Stats.FerrumResistance, Stats.TelluricResistance, Stats.HolyResistance);
public void IncreaseArmorDefense(int bonus) => _bonusDefense += bonus; public void IncreaseArmorDefense(int bonus) => _bonusDefense += bonus;

View File

@@ -55,12 +55,17 @@ public partial class Weapon : EquipableItem
public override int BonusDefense { get => Stats.BonusDefense + _bonusDefense; } public override int BonusDefense { get => Stats.BonusDefense + _bonusDefense; }
public override int BonusLuck { get => Stats.BonusLuck + _bonusLuck; }
[Save("weapon_bonus_damage")] [Save("weapon_bonus_damage")]
private int _bonusDamage { get; set; } = 0; private int _bonusDamage { get; set; } = 0;
[Save("weapon_bonus_damage")] [Save("weapon_bonus_damage")]
private int _bonusDefense { get; set; } = 0; private int _bonusDefense { get; set; } = 0;
[Save("weapon_bonus_luck")]
private int _bonusLuck { get; set; } = 0;
[Export] [Export]
[Save("weapon_stats")] [Save("weapon_stats")]
public WeaponStats Stats { get; set; } = new WeaponStats(); public WeaponStats Stats { get; set; } = new WeaponStats();

View File

@@ -21,6 +21,10 @@ public partial class WeaponStats : InventoryItemStats
[Save("weapon_tag")] [Save("weapon_tag")]
public WeaponTag WeaponTag { get; set; } = WeaponTag.None; public WeaponTag WeaponTag { get; set; } = WeaponTag.None;
[Export]
[Save("weapon_self_damage")]
public int SelfDamage { get; set; } = 0;
[Export] [Export]
public SoundEffect SoundEffect { get; set; } = SoundEffect.WeaponQuickSlash; public SoundEffect SoundEffect { get; set; } = SoundEffect.WeaponQuickSlash;
} }

View File

@@ -7,14 +7,15 @@
script = ExtResource("2_rgna4") script = ExtResource("2_rgna4")
AttackSpeed = 1.0 AttackSpeed = 1.0
WeaponElement = 0 WeaponElement = 0
WeaponTag = 0 WeaponTag = 7
SelfDamage = 0
SoundEffect = 22 SoundEffect = 22
Name = "Plasma Sword" Name = "Plasma Sword"
Description = "Has the power to occasionally instantly disintegrate an enemy" Description = "Has the power to occasionally instantly disintegrate an enemy"
SpawnRate = 0.05 SpawnRate = 0.5
BonusAttack = 14 BonusAttack = 0
BonusDefense = 0 BonusDefense = 0
BonusLuck = 0.05 BonusLuck = 10
BonusHP = 0 BonusHP = 0
BonusVT = 0 BonusVT = 0
AeolicResistance = 0 AeolicResistance = 0

View File

@@ -108,6 +108,7 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<IPlayer>
private Vector3 _knockbackDirection = Vector3.Zero; private Vector3 _knockbackDirection = Vector3.Zero;
private ItemReroller _itemReroller; private ItemReroller _itemReroller;
private PlayerEffectService _playerEffectService;
public void Initialize() public void Initialize()
{ {
@@ -128,6 +129,7 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<IPlayer>
EquipmentComponent = new EquipmentComponent(); EquipmentComponent = new EquipmentComponent();
_itemReroller = new ItemReroller(ItemDatabase.Instance); _itemReroller = new ItemReroller(ItemDatabase.Instance);
_playerEffectService = new PlayerEffectService(this);
Settings = new PlayerLogic.Settings() { RotationSpeed = RotationSpeed, MoveSpeed = MoveSpeed, Acceleration = Acceleration }; Settings = new PlayerLogic.Settings() { RotationSpeed = RotationSpeed, MoveSpeed = MoveSpeed, Acceleration = Acceleration };
@@ -212,7 +214,7 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<IPlayer>
{ {
_camera3D.AddShake(1.0f); _camera3D.AddShake(1.0f);
TakeDamageAnimationPlayer.Play("take_damage"); 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); HealthComponent.Damage(damageReceived);
SfxDatabase.Instance.Play(SoundEffect.TakeDamage); SfxDatabase.Instance.Play(SoundEffect.TakeDamage);
} }
@@ -225,6 +227,12 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<IPlayer>
public void PlayJumpScareAnimation() => PlayerFXAnimations.Play("jump_scare"); 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() public void LevelUp()
{ {
ExperiencePointsComponent.LevelUp(); ExperiencePointsComponent.LevelUp();
@@ -414,11 +422,9 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<IPlayer>
private void HitEnemy(IEnemy enemy) private void HitEnemy(IEnemy enemy)
{ {
var ignoreElementalResistance = (EquipmentComponent.EquippedWeapon.Value as Weapon).WeaponTag == WeaponTag.IgnoreAffinity; var weapon = EquipmentComponent.EquippedWeapon.Value as Weapon;
var ignoreDefense = (EquipmentComponent.EquippedWeapon.Value as Weapon).WeaponTag == WeaponTag.IgnoreDefense; var isCriticalHit = BattleExtensions.IsCriticalHit(TotalLuck);
var isCriticalHit = BattleExtensions.IsCriticalHit(LuckComponent.Luck.Value + EquipmentComponent.BonusLuck); var totalDamage = TotalAttack;
var totalDamage = AttackComponent.CurrentAttack.Value + EquipmentComponent.BonusAttack;
var element = (EquipmentComponent.EquippedWeapon.Value as Weapon).WeaponElement;
if (isCriticalHit) if (isCriticalHit)
{ {
@@ -426,21 +432,16 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<IPlayer>
SfxDatabase.Instance.Play(SoundEffect.Crit); 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); var damageDealt = DamageCalculator.CalculateDamage(baseAttack, enemy.DefenseComponent.CurrentDefense.Value, enemy.ElementalResistanceSet);
enemy.HealthComponent.Damage(damageDealt); 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()); knockbackable.Knockback(0.3f, -CurrentBasis.Z.Normalized());
if (((Weapon)EquipmentComponent.EquippedWeapon.Value).WeaponTag == WeaponTag.SelfDamage) if (weapon.WeaponTag == WeaponTag.SelfDamage)
HealthComponent.Damage(5); _playerEffectService.TakeSelfDamage(weapon.Stats.SelfDamage);
if (((Weapon)EquipmentComponent.EquippedWeapon.Value).WeaponTag == WeaponTag.Instakill) if (weapon.WeaponTag == WeaponTag.Instakill)
{ _playerEffectService.Instakill(enemy);
var rng = new RandomNumberGenerator();
rng.Randomize();
if (rng.Randf() <= LuckComponent.Luck.Value)
enemy.Die();
}
} }
private void CollisionDetector_AreaEntered(Area3D area) private void CollisionDetector_AreaEntered(Area3D area)

View File

@@ -427,6 +427,34 @@ tracks/1/keys = {
"values": [0, 29] "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"] [sub_resource type="Animation" id="Animation_v5qoq"]
resource_name = "normal_attack" resource_name = "normal_attack"
length = 0.666669 length = 0.666669
@@ -456,34 +484,6 @@ tracks/1/keys = {
"values": [0, 30] "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"] [sub_resource type="AnimationLibrary" id="AnimationLibrary_ebyyx"]
_data = { _data = {
&"Divinity_Recall": SubResource("Animation_j5wmh"), &"Divinity_Recall": SubResource("Animation_j5wmh"),
@@ -12094,6 +12094,7 @@ animations = [{
collision_layer = 802 collision_layer = 802
collision_mask = 775 collision_mask = 775
script = ExtResource("1_xcol5") script = ExtResource("1_xcol5")
HealthTimerIsActive = true
[node name="MainCollision" type="CollisionShape3D" parent="."] [node name="MainCollision" type="CollisionShape3D" parent="."]
unique_name_in_owner = true 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) transform = Transform3D(1, 0, 0, 0, 0.999848, -0.0174524, 0, 0.0174524, 0.999848, 0.003, 2.1, -0.01)
current = true current = true
fov = 52.9 fov = 52.9
_maxZ = null
[node name="MeshInstance3D" type="MeshInstance3D" parent="Camera/Camera3D"] [node name="MeshInstance3D" type="MeshInstance3D" parent="Camera/Camera3D"]
extra_cull_margin = 16384.0 extra_cull_margin = 16384.0

View File

@@ -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();
}
}

View File

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