400 lines
13 KiB
C#
400 lines
13 KiB
C#
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<IEnemy> 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<IEnemy>().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<IEnemy>().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<PackedScene>($"{consumableFolder}/ConsumableItem.tscn");
|
|
var consumable = restorativeScene.Instantiate<ConsumableItem>();
|
|
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<ConsumableItemStats>($"{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<UsableItemTag>().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<MonsterRoom>().ToList();
|
|
if (currentRoom is MonsterRoom currentMonsterRoom)
|
|
validRooms.Remove(currentMonsterRoom);
|
|
|
|
if (validRooms.Count == 0)
|
|
return;
|
|
|
|
var roomsGodotCollection = new Godot.Collections.Array<MonsterRoom>(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<MonsterRoom>().ToList();
|
|
if (currentRoom is MonsterRoom currentMonsterRoom)
|
|
validRooms.Remove(currentMonsterRoom);
|
|
|
|
if (validRooms.Count == 0)
|
|
return;
|
|
|
|
var roomsGodotCollection = new Godot.Collections.Array<MonsterRoom>(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<ExitRoom>().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<T>(params T[] itemsToExclude)
|
|
where T : IBaseInventoryItem => ItemDatabase.Instance.PickItem(itemsToExclude);
|
|
|
|
public void RandomSpell(EffectItem item)
|
|
{
|
|
var itemEffects = Enum.GetValues<UsableItemTag>().ToList();
|
|
itemEffects.Remove(UsableItemTag.RandomEffect);
|
|
itemEffects.Remove(UsableItemTag.None);
|
|
var randomEffect = new Godot.Collections.Array<UsableItemTag>(itemEffects).PickRandom();
|
|
item.SetEffectTag(randomEffect);
|
|
_game.UseItem(item);
|
|
}
|
|
|
|
public T DropTo1HPAndGainRareItem<T>()
|
|
where T : IBaseInventoryItem
|
|
{
|
|
_player.HealthComponent.SetCurrentHealth(1);
|
|
return ItemDatabase.Instance.PickRareItem<T>();
|
|
}
|
|
|
|
public void TradeRandomItem<T>(BoxItem box)
|
|
where T : IBaseInventoryItem
|
|
{
|
|
var tradableItems = _player.Inventory.Items.OfType<T>().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<T>();
|
|
}
|
|
|
|
public IEnumerable<IBaseInventoryItem> TradeAllRandomItems(BoxItem box)
|
|
{
|
|
var newInventory = new List<IBaseInventoryItem>();
|
|
var items = _player.Inventory.Items.Where(x => x.ItemTag != ItemTag.KeyItem).ToList();
|
|
foreach (var item in items)
|
|
newInventory.Add(GetRandomItemOfType<IBaseInventoryItem>());
|
|
|
|
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<T>()
|
|
where T : IBaseInventoryItem
|
|
{
|
|
return ItemDatabase.Instance.PickBasicItem<T>();
|
|
}
|
|
}
|