using Godot; using System.Linq; using System; using Zennysoft.Ma.Adapter; using Zennysoft.Ma.Adapter.Entity; using System.Collections.Generic; namespace Zennysoft.Game.Ma; public class EffectService { private readonly IGame _game; private readonly IPlayer _player; private readonly IMap _map; public EffectService(IGame game, IPlayer player, IMap map) { _game = game; _player = player; _map = map; } public void TeleportEnemiesToCurrentRoom(List enemyList) { var currentRoom = _map.CurrentFloor.GetPlayersCurrentRoom(); if (currentRoom is MonsterRoom currentMonsterRoom) { foreach (var enemy in enemyList) enemy.MoveEnemyToNewRoom(currentMonsterRoom); } } public void KillHalfEnemiesInRoom() { var currentRoom = _map.CurrentFloor.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(); } public void TurnAllEnemiesInRoomIntoHealingItem() { var currentRoom = _map.CurrentFloor.GetPlayersCurrentRoom(); 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); } 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}".TrimSuffix(".remap")); consumable.Stats = randomFile; _game.AddChild(consumable); consumable.GlobalPosition = vector; } public void HealAllEnemiesAndPlayerInRoomToFull() { var currentRoom = _map.CurrentFloor.GetPlayersCurrentRoom(); if (currentRoom is not MonsterRoom) return; currentRoom.EnemiesInRoom.ForEach(e => e.HealthComponent.SetCurrentHealth(e.HealthComponent.MaximumHP.Value)); currentRoom.EnemiesInRoom.ForEach(e => e.OnHealed()); _player.HealthComponent.SetCurrentHealth(_player.HealthComponent.MaximumHP.Value); SfxDatabase.Instance.Play(SoundEffect.HealHP); } public int AbsorbHPFromAllEnemiesInRoom() { var currentRoom = _map.CurrentFloor.GetPlayersCurrentRoom(); if (currentRoom is not MonsterRoom) return 0; var currentEnemies = currentRoom.EnemiesInRoom; var hpToAbsorb = 0.0; foreach (var enemy in currentEnemies) { var absorbAmount = enemy.HealthComponent.CurrentHP.Value * 0.25; enemy.HealthComponent.Damage((int)absorbAmount, ElementType.None); hpToAbsorb += absorbAmount; enemy.OnAbsorb(); } return (int)hpToAbsorb; } public void DealElementalDamageToAllEnemiesInRoom(ElementType elementType) { var currentRoom = _map.CurrentFloor.GetPlayersCurrentRoom(); 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, elementType); } } public void SwapHPandVT() { var oldHp = Mathf.Max(1, _player.HealthComponent.CurrentHP.Value); var oldVt = Mathf.Max(1, _player.VTComponent.CurrentVT.Value); _player.HealthComponent.SetCurrentHealth(oldVt); _player.VTComponent.SetVT(oldHp); SfxDatabase.Instance.Play(SoundEffect.SwapHPAndVT); } public void RandomEffect() { var randomEffects = Enum.GetValues(typeof(UsableItemTag)).Cast().Except([UsableItemTag.RandomEffect, UsableItemTag.None]).ToList(); var rng = new RandomNumberGenerator(); rng.Randomize(); var index = rng.RandiRange(0, randomEffects.Count - 1); var randomItem = new EffectItem() { Stats = new EffectItemStats() { UsableItemTag = randomEffects[index], BonusAttack = 1, BonusDefense = 1, BonusLuck = 10 } }; _game.UseItem(randomItem); } public void DoubleExp() { _game.DoubleExp(); } public void RaiseCurrentWeaponAttack() { if (string.IsNullOrEmpty(_player.EquipmentComponent.EquippedWeapon.Value.ItemName)) return; var currentWeapon = (Weapon)_player.EquipmentComponent.EquippedWeapon.Value; currentWeapon.IncreaseAttack(1); SfxDatabase.Instance.Play(SoundEffect.IncreaseStat); } public void RaiseCurrentArmorDefense() { if (string.IsNullOrEmpty(_player.EquipmentComponent.EquippedArmor.Value.ItemName)) return; var currentArmor = (Armor)_player.EquipmentComponent.EquippedArmor.Value; currentArmor.IncreaseArmorDefense(1); SfxDatabase.Instance.Play(SoundEffect.IncreaseStat); } public void LowerCurrentArmorDefense() { if (string.IsNullOrEmpty(_player.EquipmentComponent.EquippedArmor.Value.ItemName)) return; var currentArmor = (Armor)_player.EquipmentComponent.EquippedArmor.Value; currentArmor.DecreaseArmorDefense(1); SfxDatabase.Instance.Play(SoundEffect.DecreaseStat); } public void RestoreParameters(IPlayer player) { var hpToRestore = player.HealthComponent.MaximumHP.Value; player.HealthComponent.SetCurrentHealth(hpToRestore); var vtToRestore = player.VTComponent.MaximumVT.Value; player.VTComponent.SetVT(vtToRestore); var attackToRestore = player.AttackComponent.MaximumAttack.Value - player.AttackComponent.CurrentAttack.Value; player.AttackComponent.Restore(attackToRestore); var defenseToRestore = player.DefenseComponent.MaximumDefense.Value - player.DefenseComponent.CurrentDefense.Value; player.DefenseComponent.Restore(defenseToRestore); if (player.LuckComponent.Luck.Value < player.LuckComponent.InitialLuck) player.LuckComponent.IncreaseLuck(player.LuckComponent.InitialLuck - player.LuckComponent.Luck.Value); } public void RaiseLevel() { var expToNextLevel = _player.ExperiencePointsComponent.ExpToNextLevel.Value - _player.ExperiencePointsComponent.CurrentExp.Value; _player.ExperiencePointsComponent.GainUnmodified(expToNextLevel); } public void LowerLevel() { var expToNextLevel = _player.ExperiencePointsComponent.ExpToNextLevel.Value - _player.ExperiencePointsComponent.CurrentExp.Value; _player.ExperiencePointsComponent.LevelDown(); } 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); if (validRooms.Count == 0) return; var roomsGodotCollection = new Godot.Collections.Array(validRooms); var randomRoom = roomsGodotCollection.PickRandom(); enemy.MoveEnemyToNewRoom(randomRoom); } public void DisableEnemyMovement(IEnemy enemy) { enemy.SetEnemySpeedByMultiplier(0); } public void CloneEnemy(IEnemy enemy) { var enemyPosition = new Vector3(enemy.GlobalPosition.X, 0f, enemy.GlobalPosition.Z); var enemyType = EnemyTypeToEnemyConverter.Convert(enemy); var duplicatedEnemy = EnemyTypeToEnemyConverter.Convert(enemyType); duplicatedEnemy.GlobalPosition = enemy.GlobalPosition + enemy.GlobalBasis.X; _map.AddChild(duplicatedEnemy); } public void MeltAllEquipment(IPlayer player) { var weapon = player.EquipmentComponent.EquippedWeapon.Value; var armor = player.EquipmentComponent.EquippedArmor.Value; var accessory = player.EquipmentComponent.EquippedAccessory.Value; var ammo = player.EquipmentComponent.EquippedAmmo.Value; if (weapon != null) { player.Unequip(weapon); player.Inventory.Remove(weapon); } if (armor != null) { player.Unequip(armor); player.Inventory.Remove(armor); } if (accessory != null) { player.Unequip(accessory); player.Inventory.Remove(accessory); } if (ammo != null) { player.Unequip(ammo); player.Inventory.Remove(ammo); } } public void GlueAllEquipment(IPlayer player) { var weapon = player.EquipmentComponent.EquippedWeapon.Value; var armor = player.EquipmentComponent.EquippedArmor.Value; var accessory = player.EquipmentComponent.EquippedAccessory.Value; if (weapon != null && !string.IsNullOrEmpty(weapon.ItemName)) weapon.Glued = true; if (armor != null && !string.IsNullOrEmpty(armor.ItemName)) armor.Glued = true; if (accessory != null && !string.IsNullOrEmpty(accessory.ItemName)) accessory.Glued = true; player.EquipmentComponent.UpdateEquipment(weapon); player.EquipmentComponent.UpdateEquipment(armor); player.EquipmentComponent.UpdateEquipment(accessory); } public void TeleportToRandomRoom(IPlayer player) { var currentFloor = _game.CurrentFloor; var rooms = currentFloor.Rooms; var currentRoom = rooms.SingleOrDefault(x => x.IsPlayerInRoom); var validRooms = rooms.OfType().ToList(); if (currentRoom is MonsterRoom currentMonsterRoom) validRooms.Remove(currentMonsterRoom); 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.GlobalPosition.X, _player.GlobalPosition.Y, spawnPoint.GlobalPosition.Z))); SfxDatabase.Instance.Play(SoundEffect.TeleportToRandomRoom); } public bool WarpToExit() { var exitRoom = _game.CurrentFloor.Rooms.OfType().Single(); if (exitRoom.PlayerDiscoveredRoom) { SfxDatabase.Instance.Play(SoundEffect.TeleportToExit); var exitPoint = exitRoom.PlayerSpawn.GlobalPosition; _player.TeleportPlayer((exitRoom.PlayerSpawn.Rotation, new Vector3(exitPoint.X, _player.GlobalPosition.Y, exitPoint.Z))); return true; } return false; } public void DamagesPlayer(int damage) { _player.TakeDamage(new AttackData(damage, ElementType.None, true, true)); } public void RerollItem(IBaseInventoryItem itemToReroll) { var itemReroller = new ItemReroller(ItemDatabase.Instance); itemReroller.RerollItem(itemToReroll, _player.Inventory); } public T GetRandomItemOfType(params T[] itemsToExclude) where T : IBaseInventoryItem => ItemDatabase.Instance.PickItem(itemsToExclude); public void RandomSpell(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); } public T DropTo1HPAndGainRareItem() where T : IBaseInventoryItem { _player.HealthComponent.SetCurrentHealth(1); return ItemDatabase.Instance.PickRareItem(); } public void TradeRandomItem(BoxItem box) where T : IBaseInventoryItem { var tradableItems = _player.Inventory.Items.OfType().Where(x => x.ItemTag != ItemTag.KeyItem).ToList(); var rng = new RandomNumberGenerator(); rng.Randomize(); var randomIndex = rng.RandiRange(0, tradableItems.Count - 1); var randomItem = tradableItems[randomIndex]; if (randomItem is IEquipableItem equipableItem && _player.EquipmentComponent.IsItemEquipped(equipableItem)) _player.Unequip(equipableItem); _player.Inventory.Remove(randomItem); GetRandomItemOfType(); } public IEnumerable TradeAllRandomItems(BoxItem box) { var newInventory = new List(); var items = _player.Inventory.Items.Where(x => x.ItemTag != ItemTag.KeyItem).ToList(); foreach (var item in items) newInventory.Add(GetRandomItemOfType()); return newInventory; } public IBaseInventoryItem 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]; if (selectedItem is ThrowableItem throwableItem) throwableItem.SetCount(rng.RandiRange(throwableItem.Stats.MinimumCount, throwableItem.Stats.MaximumCount)); return selectedItem; } public T GetBasicItem() where T : IBaseInventoryItem { return ItemDatabase.Instance.PickBasicItem(); } }