Files
GameJamDungeon/Zennysoft.Game.Ma/src/game/Game.cs
T

967 lines
35 KiB
C#

namespace Zennysoft.Game.Ma;
using Chickensoft.AutoInject;
using Chickensoft.Introspection;
using Chickensoft.SaveFileBuilder;
using Godot;
using System;
using System.Text.Json;
using Zennysoft.Ma.Adapter;
using System.IO;
using System.Threading.Tasks;
using Zennysoft.Game.Implementation;
using Zennysoft.Ma.Adapter.Entity;
using System.Linq;
[Meta(typeof(IAutoNode))]
public partial class Game : Node3D, IGame
{
public override void _Notification(int what) => this.Notify(what);
IGame IProvide<IGame>.Value() => this;
IGameRepo IProvide<IGameRepo>.Value() => GameRepo;
IPlayer IProvide<IPlayer>.Value() => _player;
IMap IProvide<IMap>.Value() => _map;
private static SimpleInjector.Container _container;
public IGameState GameState { get; set; } = default!;
public IGameRepo GameRepo { get; set; } = default!;
public GameState.IBinding GameBinding { get; set; } = default!;
#region Nodes
[Node] private Node PauseContainer { get; set; } = default!;
[Node] private InGameUI InGameUI { get; set; } = default!;
[Node] private IFloorClearMenu LoadNextLevel { get; set; } = default!;
[Node] private IGameOverMenu GameOverMenu { get; set; } = default!;
[Node] private IPauseMenu PauseMenu { get; set; } = default!;
#endregion
#region Save
public JsonSerializerOptions JsonOptions { get; set; } = default!;
public ISaveFile<GameData> SaveFile { get; set; } = default!;
public ISaveChunk<GameData> GameChunk { get; set; } = default!;
ISaveChunk<GameData> IProvide<ISaveChunk<GameData>>.Value() => GameChunk;
[Signal]
public delegate void SaveFileLoadedEventHandler();
public event Action GameExitRequested;
public event Action GameLoaded;
#endregion
public RescuedItemDatabase RescuedItems { get; set; } = default!;
public ItemDatabase ItemDatabase { get; private set; }
public QuestData QuestData { get; private set; }
public SarcoData SarcoData { get; private set; }
public NpcData NpcData { get; private set; }
public StatData StatData { get; private set; }
public ItemRescueMenu ItemRescueMenu { get => InGameUI.ItemRescueMenu; }
private EffectService _effectService;
private IInstantiator _instantiator;
private IPlayer _player;
private IMap _map;
private Timer _doubleExpTimer;
private Timer _rustTimer;
private Timer _rustDurationTimer;
[Signal] private delegate void OnLoadLevelRequestEventHandler();
public event Action<string> InventoryEventNotification;
public Game()
{
_container = new SimpleInjector.Container();
Module.Bootstrap(_container);
GameRepo = _container.GetInstance<IGameRepo>();
GameState = _container.GetInstance<IGameState>();
QuestData = new QuestData();
RescuedItems = new RescuedItemDatabase(20);
SarcoData = new SarcoData();
NpcData = new NpcData() { SteleDiscovered = [] };
ItemDatabase = ItemDatabase.Instance;
GameChunk = new SaveChunk<GameData>(
(chunk) =>
{
var gameData = new GameData()
{
RescuedItems = new RescuedItemDatabase(RescuedItems.GetItems(), 20),
QuestData = new QuestData()
{
DeathCount = QuestData.DeathCount,
QuestMarker1 = QuestData.QuestMarker1
},
SarcoData = new SarcoData()
{
AeolicSarcoAcquired = SarcoData.AeolicSarcoAcquired,
IgneousSarcoAcquired = SarcoData.IgneousSarcoAcquired,
TelluricSarcoAcquired = SarcoData.TelluricSarcoAcquired,
HydricSarcoAcquired = SarcoData.HydricSarcoAcquired,
FerrumSarcoAcquired = SarcoData.FerrumSarcoAcquired,
SanktaSarcoAcquired = SarcoData.SanktaSarcoAcquired,
ShuraSarcoAcquired = SarcoData.ShuraSarcoAcquired,
},
NpcData = new NpcData()
{
SteleDiscovered = NpcData.SteleDiscovered
},
StatData = new StatData()
{
SproingyDefeated = StatData.SproingyDefeated
}
};
return gameData;
},
onLoad:
(chunk, data) =>
{
RescuedItems = data.RescuedItems ?? new RescuedItemDatabase();
QuestData = data.QuestData ?? new QuestData();
SarcoData = data.SarcoData ?? new SarcoData();
NpcData = data.NpcData ?? new NpcData() { SteleDiscovered = [] };
StatData = data.StatData ?? new StatData();
}
);
var saveFileManager = new MaSaveFileManager();
SaveFile = new SaveFile<GameData>(
root: GameChunk,
onSave: saveFileManager.Save,
onLoad: async () =>
{
try
{
var gameData = await saveFileManager.Load<GameData>();
return gameData;
}
catch (FileNotFoundException)
{
GD.Print("No save file found.");
}
return null;
}
);
}
public void Setup()
{
_instantiator = new Instantiator(GetTree());
_player = _instantiator.LoadAndInstantiate<Player>("res://src/player/Player.tscn");
_map = _instantiator.LoadAndInstantiate<Map>("res://src/map/Map.tscn");
_map.SpawnPointCreated += MovePlayer;
PauseContainer.AddChild((Player)_player);
PauseContainer.AddChild((Map)_map);
}
public async void OnResolved()
{
LoadExistingGame();
await InitializeGame();
GameState.Set(GameRepo);
GameState.Set(_player);
GameState.Set(_map);
GameState.Set(InGameUI);
HandleGameLogic();
GameState.Start();
this.Provide();
InGameUI.UseTeleportPrompt.TeleportToNextFloor += UseTeleportPrompt_TeleportToNextFloor;
InGameUI.UseTeleportPrompt.CloseTeleportPrompt += UseTeleportPrompt_CloseTeleportPrompt;
LoadNextLevel.GoToNextFloor += FloorClearMenu_GoToNextFloor;
LoadNextLevel.Exit += FloorClearMenu_Exit;
LoadNextLevel.TransitionCompleted += FloorClearMenu_TransitionCompleted;
OnLoadLevelRequest += LoadLevel;
GameRepo.CloseInventoryEvent += ExitInventoryAction;
GameRepo.EnemyDied += GameRepo_EnemyDied;
_player.Inventory.BroadcastMessage += BroadcastMessage;
_map.FloorLoaded += OnFloorLoadFinished;
_player.PlayerDied += GameOver;
GameOverMenu.NewGame += OnNewGame;
GameOverMenu.QuitGame += OnQuit;
PauseMenu.ExitGamePressed += OnQuit;
_doubleExpTimer = new Timer();
_doubleExpTimer.WaitTime = 30;
_doubleExpTimer.Timeout += EndDoubleExpTimer;
AddChild(_doubleExpTimer);
_player.StatusEffectComponent.Rust.Changed += RustStatusChanged;
_rustTimer = new Timer();
_rustTimer.WaitTime = 2f;
_rustTimer.Timeout += RustTimeout;
_rustDurationTimer = new Timer();
_rustDurationTimer.WaitTime = _player.StatusEffectComponent.RustDuration;
_rustDurationTimer.Timeout += RustWoreOff;
AddChild(_rustTimer);
AddChild(_rustDurationTimer);
GameRepo.IsPaused.Sync += IsPaused_Sync;
InGameUI.PlayerInfoUI.Activate();
InGameUI.Show();
GameRepo.Resume();
}
public void LoadExistingGame() => SaveFile.Load().ContinueWith((_) => CallDeferred(nameof(FinishedLoadingSaveFile)));
public async Task InitializeGame()
{
_player.ResetPlayerData();
_map.InitializeMapData();
_effectService = new EffectService(this, _player, _map);
await _map.LoadFloor();
}
public async Task Save() => await SaveFile.Save();
public void FloorExitReached() => GameState.Input(new GameState.Input.FloorExitEntered());
public void ShowMinimap(bool isVisible)
{
if (_map.CurrentFloor is SpecialFloor)
InGameUI.ShowMinimap(false);
else
InGameUI.ShowMinimap(true);
}
public async Task UseItem(IBaseInventoryItem item)
{
if (item.ItemTag == ItemTag.MysteryItem)
_effectService.RerollItem(item);
switch (item)
{
case BoxItem boxItem:
await EnactBoxItemEffects(boxItem);
break;
case ConsumableItem consumableItem:
EnactConsumableItemEffects(consumableItem);
break;
case EffectItem effectItem:
await EnactEffectItemEffects(effectItem);
break;
}
RemoveItemOrSubtractFromItemCount(item);
}
public void DropItem(IBaseInventoryItem item)
{
var droppedScene = GD.Load<PackedScene>("res://src/items/dropped/DroppedItem.tscn");
var dropped = droppedScene.Instantiate<DroppedItem>();
dropped.Item = item;
_map.AddChild(dropped);
dropped.Drop();
_player.Inventory.Remove(item);
}
public void SetItem(IBaseInventoryItem item)
{
var setScene = GD.Load<PackedScene>("res://src/items/misc/SetItem.tscn");
var setItem = setScene.Instantiate<SetItem>();
_map.AddChild(setItem);
setItem.Set();
_player.Inventory.Remove(item);
}
public void ThrowItem(IBaseInventoryItem item)
{
var thrownScene = GD.Load<PackedScene>("res://src/items/thrown/ThrownItem.tscn");
var thrown = thrownScene.Instantiate<ThrownItem>();
thrown.ItemThatIsThrown = item;
_map.AddChild(thrown);
thrown.Throw(_effectService);
RemoveItemOrSubtractFromItemCount(item);
}
public void DoubleExp()
{
GameRepo.AnnounceMessageOnMainScreen("Experience points temporarily doubled.");
if (_doubleExpTimer.TimeLeft == 0)
_player.ExperiencePointsComponent.ModifyExpGainRate(_player.ExperiencePointsComponent.ExpGainRate.Value * 2);
_doubleExpTimer.Start();
}
public IDungeonFloor CurrentFloor => _map.CurrentFloor;
public async void GameOver()
{
QuestData.DeathCount++;
await Save();
_player.Deactivate();
GameRepo.Pause();
GameState.Input(new GameState.Input.GameOver());
}
public override void _Input(InputEvent @event)
{
if (@event.IsActionPressed(GameInputs.Debug))
GameState.Input(new GameState.Input.DebugButtonPressed());
if (@event.IsActionPressed(GameInputs.Pause))
GameState.Input(new GameState.Input.PauseButtonPressed());
if (Input.IsActionJustPressed(GameInputs.Inventory))
GameState.Input(new GameState.Input.InventoryButtonPressed());
if (Input.IsActionJustPressed(GameInputs.Interact))
GameState.Input(new GameState.Input.InteractButtonPressed());
}
private void HandleGameLogic()
{
GameBinding = GameState.Bind();
GameBinding
.Handle((in GameState.Output.InitializeGame _) =>
{
LoadExistingGame();
InitializeGame();
})
.Handle((in GameState.Output.OpenPauseScreen _) =>
{
GameRepo.Pause();
PauseMenu.Show();
PauseMenu.FadeIn();
Input.MouseMode = Input.MouseModeEnum.Visible;
PauseMenu.SetProcessUnhandledInput(true);
})
.Handle((in GameState.Output.ClosePauseScreen _) =>
{
PauseMenu.FadeOut();
Input.MouseMode = Input.MouseModeEnum.Visible;
PauseMenu.SetProcessUnhandledInput(false);
GameRepo.Resume();
})
.Handle((in GameState.Output.OpenInventoryMenu _) =>
{
GameRepo.Pause();
InGameUI.InventoryMenu.Show();
InGameUI.InventoryMenu.SetProcessInput(true);
SfxDatabase.Instance.Play(SoundEffect.OpenInventory);
})
.Handle((in GameState.Output.CloseInventoryMenu _) =>
{
CloseInventory();
})
.Handle((in GameState.Output.OpenDebugMenu _) =>
{
InGameUI.DebugMenu.Show();
InGameUI.PlayerInfoUI.Hide();
_player.Deactivate();
})
.Handle((in GameState.Output.CloseDebugMenu _) =>
{
InGameUI.DebugMenu.Hide();
InGameUI.PlayerInfoUI.Show();
_player.Activate();
})
.Handle((in GameState.Output.OpenTeleportScreen _) =>
{
InGameUI.UseTeleportPrompt.Show();
InGameUI.UseTeleportPrompt.FadeIn();
})
.Handle((in GameState.Output.OpenFloorExitScreen _) =>
{
InGameUI.UseTeleportPrompt.FadeOut();
LoadNextLevel.Show();
LoadNextLevel.FadeIn();
})
.Handle((in GameState.Output.LoadNextFloor _) =>
{
LoadNextLevel.FadeOut();
EmitSignal(SignalName.OnLoadLevelRequest);
Task.Run(() => Save());
if (_player.EquipmentComponent.EquippedWeapon.Value.ItemTag == ItemTag.BreaksOnFloorExit)
{
var itemToDestroy = _player.EquipmentComponent.EquippedWeapon.Value;
_player.Unequip(itemToDestroy);
_player.Inventory.Remove(itemToDestroy);
}
if (_player.EquipmentComponent.EquippedArmor.Value.ItemTag == ItemTag.BreaksOnFloorExit)
{
var itemToDestroy = _player.EquipmentComponent.EquippedArmor.Value;
_player.Unequip(itemToDestroy);
_player.Inventory.Remove(itemToDestroy);
}
if (_player.EquipmentComponent.EquippedAccessory.Value.ItemTag == ItemTag.BreaksOnFloorExit)
{
var itemToDestroy = _player.EquipmentComponent.EquippedAccessory.Value;
_player.Unequip(itemToDestroy);
_player.Inventory.Remove(itemToDestroy);
}
LoadNextLevel.FadeOut();
})
.Handle((in GameState.Output.ExitGame _) =>
{
OnQuit();
})
.Handle((in GameState.Output.GameOver _) =>
{
GameOverMenu.FadeIn();
var enemies = GetTree().GetNodesInGroup("enemy").OfType<IEnemy>();
foreach (var enemy in enemies)
enemy.CallDeferred(MethodName.QueueFree, []);
});
}
public void SetAffinity(ElementType elementType) => InGameUI.SetAffinity(elementType);
private void FloorClearMenu_Exit()
{
_player.Deactivate();
InGameUI.Hide();
}
private void ExitInventoryAction() => GameState.Input(new GameState.Input.CloseInventory());
private void CloseInventory()
{
GameRepo.Resume();
InGameUI.InventoryMenu.Hide();
InGameUI.InventoryMenu.SetProcessInput(false);
}
private async void LoadLevel()
{
await _map.LoadFloor();
}
private void FloorClearMenu_GoToNextFloor() => GameState.Input(new GameState.Input.LoadNextFloor());
private void DropRestorative(Vector3 vector)
{
var restorativeScene = GD.Load<PackedScene>("res://src/items/restorative/Restorative.tscn");
var restorative = restorativeScene.Instantiate<Restorative>();
AddChild(restorative);
restorative.GlobalPosition = new Vector3(vector.X, 2f, vector.Z) + (-_player.GetGlobalBasis().Z);
}
private void DropItem(Vector3 vector)
{
var randomItem = ItemDatabase.Instance.PickItem<IBaseInventoryItem>() as Node3D;
var duplicated = randomItem.Duplicate((int)DuplicateFlags.UseInstantiation) as Node3D;
AddChild(duplicated);
duplicated.GlobalPosition = new Vector3(vector.X, 2f, vector.Z) + (-_player.GetGlobalBasis().Z);
}
private void UseTeleportPrompt_CloseTeleportPrompt()
{
GameState.Input(new GameState.Input.CloseTeleport());
InGameUI.UseTeleportPrompt.FadeOut();
GameRepo.Resume();
}
private void UseTeleportPrompt_TeleportToNextFloor()
{
GameState.Input(new GameState.Input.UseTeleport());
}
private void PointUpFinished() => GameState.Input(new GameState.Input.UseTeleport());
private void FloorClearMenu_TransitionCompleted()
{
GameRepo.Resume();
}
private void IsPaused_Sync(bool isPaused) => GetTree().Paused = isPaused;
private void FinishedLoadingSaveFile() => EmitSignal(SignalName.SaveFileLoaded);
private async Task EnactBoxItemEffects(BoxItem boxItem)
{
switch (boxItem.ItemTag)
{
case ItemTag.DamagesPlayer:
_effectService.DamagesPlayer(boxItem.Stats.DamageToPlayer);
InventoryEventNotification.Invoke($"{boxItem.Stats.DamageToPlayer} damage done to self.");
await ToSignal(GetTree().CreateTimer(0.5f), "timeout");
GameRepo.CloseInventory();
break;
case ItemTag.ContainsAccessory:
var accessory = _effectService.GetRandomItemOfType<Accessory>();
_player.Inventory.TryAdd(accessory);
InventoryEventNotification.Invoke($"{boxItem.ItemName} contained {accessory.ItemName}.");
break;
case ItemTag.ContainsArmor:
var armor = _effectService.GetRandomItemOfType<Armor>();
_player.Inventory.TryAdd(armor);
InventoryEventNotification.Invoke($"{boxItem.ItemName} contained {armor.ItemName}.");
break;
case ItemTag.ContainsWeapon:
var weapon = _effectService.GetRandomItemOfType<Weapon>();
_player.Inventory.TryAdd(weapon);
InventoryEventNotification.Invoke($"{boxItem.ItemName} contained {weapon.ItemName}.");
break;
case ItemTag.ContainsBox:
var box = _effectService.GetRandomItemOfType<BoxItem>();
_player.Inventory.TryAdd(box);
InventoryEventNotification.Invoke($"{boxItem.ItemName} contained {box.ItemName}.");
break;
case ItemTag.RandomSpell:
var effectItem = _effectService.GetRandomItemOfType<EffectItem>();
_player.Inventory.TryAdd(effectItem);
InventoryEventNotification.Invoke($"{boxItem.ItemName} contained {effectItem.ItemName}.");
break;
case ItemTag.ContainsRestorative:
var restorative = _effectService.GetRandomItemOfType<ConsumableItem>();
_player.Inventory.TryAdd(restorative);
InventoryEventNotification.Invoke($"{boxItem.ItemName} contained {restorative.ItemName}.");
break;
case ItemTag.DropTo1HPAndGainRareItem:
var rareItem = _effectService.DropTo1HPAndGainRareItem<IBaseInventoryItem>();
_player.Inventory.TryAdd(rareItem);
InventoryEventNotification.Invoke($"{boxItem.ItemName} contained {rareItem.ItemName} but cost dear life.");
break;
case ItemTag.TradeOneRandomItem:
var itemsWithoutBox = _player.Inventory.Items.Where(x => x != boxItem).ToList();
var rng = new RandomNumberGenerator();
rng.Randomize();
var index = rng.RandiRange(0, itemsWithoutBox.Count - 1);
var randomItem = itemsWithoutBox[index];
_player.Inventory.Remove(randomItem);
var newItem = _effectService.GetRandomItemOfType<IBaseInventoryItem>();
_player.Inventory.TryAdd(newItem);
InventoryEventNotification.Invoke($"{boxItem.ItemName} contained {newItem.ItemName}.");
break;
case ItemTag.TradeAllRandomItems:
var newInventory = _effectService.TradeAllRandomItems(boxItem);
_player.Inventory.Items.Clear();
_player.Inventory.TryAdd(boxItem);
foreach (var item in newInventory)
_player.Inventory.TryAdd(item);
InventoryEventNotification.Invoke($"All items replaced.");
break;
case ItemTag.ContainsUnobtainedItem:
var unobtainedItem = _effectService.GetUnobtainedItem();
_player.Inventory.TryAdd(unobtainedItem);
InventoryEventNotification.Invoke($"{boxItem.ItemName} contained {unobtainedItem.ItemName}.");
break;
case ItemTag.ContainsBasicItem:
var basicItem = _effectService.GetBasicItem<IBaseInventoryItem>();
InventoryEventNotification.Invoke($"{boxItem.ItemName} contained {basicItem.ItemName}.");
break;
case ItemTag.UnequipAllItems:
_player.Unequip(_player.EquipmentComponent.EquippedWeapon.Value);
_player.Unequip(_player.EquipmentComponent.EquippedArmor.Value);
_player.Unequip(_player.EquipmentComponent.EquippedAccessory.Value);
InventoryEventNotification.Invoke($"Equipment status reset.");
break;
case ItemTag.RestrictUnequip:
_effectService.GlueAllEquipment(_player);
InventoryEventNotification.Invoke($"Currently equipped items have become unmovable.");
break;
case ItemTag.EjectAllItems:
_player.EquipmentComponent.EquippedWeapon.Value.Glued = false;
_player.EquipmentComponent.EquippedArmor.Value.Glued = false;
_player.EquipmentComponent.EquippedAccessory.Value.Glued = false;
_player.Unequip(_player.EquipmentComponent.EquippedWeapon.Value);
_player.Unequip(_player.EquipmentComponent.EquippedArmor.Value);
_player.Unequip(_player.EquipmentComponent.EquippedAccessory.Value);
_player.Inventory.Items.Remove(boxItem);
foreach (var item in _player.Inventory.Items.ToList())
ThrowItem(item);
_player.Inventory.Items.Clear();
GameRepo.CloseInventory();
BroadcastMessage($"All items have been ejected from inventory.");
break;
}
}
private void EnactConsumableItemEffects(ConsumableItem consumableItem)
{
if (consumableItem.HealHPAmount > 0 && consumableItem.HealVTAmount > 0)
{
SfxDatabase.Instance.Play(SoundEffect.Eucharistia);
if (_player.HealthComponent.AtFullHealth && _player.VTComponent.AtFullVT)
{
_player.HealthComponent.RaiseMaximumHP(consumableItem.RaiseHPAmount, true);
_player.VTComponent.RaiseMaximumVT(consumableItem.RaiseVTAmount, true);
InventoryEventNotification.Invoke($"Raised maximum HP by {consumableItem.RaiseHPAmount}." + System.Environment.NewLine + $"Raised maximum VT by {consumableItem.RaiseVTAmount}.");
}
else
{
_player.HealthComponent.Heal(consumableItem.HealHPAmount);
_player.VTComponent.Restore(consumableItem.HealVTAmount);
InventoryEventNotification.Invoke($"Restored {consumableItem.RaiseHPAmount}HP." + System.Environment.NewLine + $"Restored {consumableItem.RaiseVTAmount}VT.");
}
if (_player.StatusEffectComponent.Rust.Value)
{
_player.StatusEffectComponent.Reset();
InventoryEventNotification.Invoke($"All status afflictments have faded.");
}
return;
}
if (_player.HealthComponent.AtFullHealth && consumableItem.RaiseHPAmount > 0)
{
_player.HealthComponent.RaiseMaximumHP(consumableItem.RaiseHPAmount, true);
SfxDatabase.Instance.Play(SoundEffect.IncreaseStat);
InventoryEventNotification.Invoke($"Raised maximum HP by {consumableItem.RaiseHPAmount}.");
}
else if (_player.VTComponent.AtFullVT && consumableItem.RaiseVTAmount > 0)
{
_player.VTComponent.RaiseMaximumVT(consumableItem.RaiseVTAmount, true);
SfxDatabase.Instance.Play(SoundEffect.IncreaseStat);
InventoryEventNotification.Invoke($"Raised maximum VT by {consumableItem.RaiseVTAmount}.");
}
else if (consumableItem.HealHPAmount > 0)
{
var currentHP = _player.HealthComponent.CurrentHP;
_player.HealthComponent.Heal(consumableItem.HealHPAmount);
SfxDatabase.Instance.Play(SoundEffect.HealHP);
InventoryEventNotification.Invoke($"Restored {consumableItem.HealHPAmount}HP.");
}
else if (consumableItem.HealVTAmount > 0)
{
_player.VTComponent.Restore(consumableItem.HealVTAmount);
SfxDatabase.Instance.Play(SoundEffect.HealVT);
InventoryEventNotification.Invoke($"Restored {consumableItem.HealVTAmount}VT.");
}
if (consumableItem.Stats.HealsStatusAilments)
{
_player.StatusEffectComponent.Reset();
InventoryEventNotification.Invoke($"All status afflictments have faded.");
}
}
private async Task EnactEffectItemEffects(EffectItem effectItem)
{
switch (effectItem.UsableItemTag)
{
case UsableItemTag.BriefImmunity:
_player.EnactBriefImmunity();
break;
case UsableItemTag.TeleportAllEnemiesToRoom:
_effectService.TeleportEnemiesToCurrentRoom([.. GetTree().GetNodesInGroup("enemy").OfType<IEnemy>()]);
SfxDatabase.Instance.Play(SoundEffect.RecallEnemies);
GameRepo.CloseInventory();
BroadcastMessage($"All entities have been summoned.");
_player.PlaySpellFX(SpellFXEnum.DivinityRecall);
break;
case UsableItemTag.KillHalfEnemiesInRoom:
_effectService.KillHalfEnemiesInRoom();
GameRepo.CloseInventory();
BroadcastMessage($"The balance has been achieved.");
break;
case UsableItemTag.TurnAllEnemiesIntoHealingItem:
_effectService.TurnAllEnemiesInRoomIntoHealingItem();
GameRepo.CloseInventory();
BroadcastMessage($"Entities in current room have been converted.");
break;
case UsableItemTag.HealsAllInRoomToMaxHP:
_effectService.HealAllEnemiesAndPlayerInRoomToFull();
GameRepo.CloseInventory();
BroadcastMessage($"All present have been renewed.");
break;
case UsableItemTag.AbsorbHPFromAllEnemiesInRoom:
var hpAbsorbed = _effectService.AbsorbHPFromAllEnemiesInRoom();
if (hpAbsorbed == 0)
{
GameRepo.CloseInventory();
BroadcastMessage($"No entities present to absorb from or invalid location.");
return;
}
_player.HealthComponent.Heal(hpAbsorbed);
GameRepo.CloseInventory();
BroadcastMessage($"Entities have surrendered {hpAbsorbed}HP to you.");
_player.PlaySpellFX(SpellFXEnum.Kyuuketsuki);
break;
case UsableItemTag.DealElementalDamageToAllEnemiesInRoom:
_effectService.DealElementalDamageToAllEnemiesInRoom(effectItem.Stats.ElementalDamageType);
GameRepo.CloseInventory();
BroadcastMessage($"All entities present have taken {effectItem.Stats.ElementalDamageType} to you.");
break;
case UsableItemTag.SwapHPAndVT:
_effectService.SwapHPandVT();
InventoryEventNotification.Invoke($"HP and VT have been traded.");
await ToSignal(GetTree().CreateTimer(0.5f), "timeout");
break;
case UsableItemTag.RaiseCurrentWeaponAttack:
if (string.IsNullOrEmpty(_player.EquipmentComponent.EquippedWeapon.Value.ItemName))
return;
_effectService.RaiseCurrentWeaponAttack();
InventoryEventNotification.Invoke($"{_player.EquipmentComponent.EquippedWeapon.Value}'s attack has risen by 1.");
await ToSignal(GetTree().CreateTimer(0.5f), "timeout");
break;
case UsableItemTag.RaiseCurrentDefenseArmor:
if (string.IsNullOrEmpty(_player.EquipmentComponent.EquippedArmor.Value.ItemName))
return;
_effectService.RaiseCurrentArmorDefense();
InventoryEventNotification.Invoke($"{_player.EquipmentComponent.EquippedArmor.Value}'s defense has risen by 1.");
await ToSignal(GetTree().CreateTimer(0.5f), "timeout");
break;
case UsableItemTag.RaiseLevel:
_effectService.RaiseLevel();
InventoryEventNotification.Invoke($"Level increased to {_player.ExperiencePointsComponent.Level.Value}");
await ToSignal(GetTree().CreateTimer(0.5f), "timeout");
break;
case UsableItemTag.LowerLevel:
_effectService.LowerLevel();
InventoryEventNotification.Invoke($"Level decreased to {_player.ExperiencePointsComponent.Level.Value}");
await ToSignal(GetTree().CreateTimer(0.5f), "timeout");
break;
case UsableItemTag.LowerCurrentDefenseArmor:
if (string.IsNullOrEmpty(_player.EquipmentComponent.EquippedArmor.Value.ItemName))
return;
_effectService.LowerCurrentArmorDefense();
InventoryEventNotification.Invoke($"{_player.EquipmentComponent.EquippedArmor.Value}'s defense has been lowered by 1.");
break;
case UsableItemTag.RandomEffect:
_effectService.RandomEffect();
break;
case UsableItemTag.DoubleExp:
_effectService.DoubleExp();
InventoryEventNotification.Invoke($"EXP Rate has been doubled.");
GameRepo.CloseInventory();
_player.PlaySpellFX(SpellFXEnum.AnBradan);
break;
case UsableItemTag.TeleportToRandomLocation:
_effectService.TeleportToRandomRoom(_player);
InventoryEventNotification.Invoke($"Moving to a different room.");
GameRepo.CloseInventory();
break;
case UsableItemTag.WarpToExitIfFound:
var warpedToExit = _effectService.WarpToExit();
if (warpedToExit)
InventoryEventNotification.Invoke($"Moved to exit room.");
else
InventoryEventNotification.Invoke($"Unable to locate exit room.");
await ToSignal(GetTree().CreateTimer(0.5f), "timeout");
GameRepo.CloseInventory();
break;
case UsableItemTag.IncreaseAttack:
_player.AttackComponent.RaiseMaximumAttack(effectItem.Stats.BonusAttack);
SfxDatabase.Instance.Play(SoundEffect.IncreaseStat);
InventoryEventNotification.Invoke($"Attack has permanently risen to {_player.AttackComponent.CurrentAttack.Value}.");
break;
case UsableItemTag.IncreaseDefense:
_player.DefenseComponent.RaiseMaximumDefense(effectItem.Stats.BonusDefense);
SfxDatabase.Instance.Play(SoundEffect.IncreaseStat);
InventoryEventNotification.Invoke($"Defense has permanently risen to {_player.DefenseComponent.CurrentDefense.Value}.");
break;
case UsableItemTag.IncreaseLuck:
_player.LuckComponent.IncreaseLuck(effectItem.Stats.BonusLuck);
SfxDatabase.Instance.Play(SoundEffect.IncreaseStat);
InventoryEventNotification.Invoke($"Luck increased.");
break;
case UsableItemTag.DecreaseAttack:
_player.AttackComponent.Reduce(effectItem.Stats.BonusAttack);
SfxDatabase.Instance.Play(SoundEffect.DecreaseStat);
InventoryEventNotification.Invoke($"Attack has been lowered to {_player.AttackComponent.CurrentAttack.Value}.");
break;
case UsableItemTag.DecreaseDefense:
_player.DefenseComponent.Reduce(effectItem.Stats.BonusDefense);
SfxDatabase.Instance.Play(SoundEffect.DecreaseStat);
InventoryEventNotification.Invoke($"Defense has been lowered to {_player.DefenseComponent.CurrentDefense.Value}.");
break;
case UsableItemTag.DecreaseLuck:
_player.LuckComponent.DecreaseLuck(effectItem.Stats.BonusLuck);
SfxDatabase.Instance.Play(SoundEffect.DecreaseStat);
InventoryEventNotification.Invoke($"Luck decreased.");
break;
case UsableItemTag.DecreaseAllStats:
_player.AttackComponent.Reduce(effectItem.Stats.BonusAttack);
_player.DefenseComponent.Reduce(effectItem.Stats.BonusDefense);
_player.LuckComponent.DecreaseLuck(effectItem.Stats.BonusLuck);
SfxDatabase.Instance.Play(SoundEffect.DecreaseStat);
InventoryEventNotification.Invoke($"All stats have been lowered.");
break;
case UsableItemTag.MeltAllEquipment:
_effectService.MeltAllEquipment(_player);
SfxDatabase.Instance.Play(SoundEffect.DecreaseStat);
InventoryEventNotification.Invoke($"All equipped items have dissolved.");
break;
case UsableItemTag.GlueAllEquipment:
_effectService.GlueAllEquipment(_player);
SfxDatabase.Instance.Play(SoundEffect.DecreaseStat);
InventoryEventNotification.Invoke($"All equipped items have become sticky.");
break;
case UsableItemTag.RestoreStats:
_effectService.RestoreParameters(_player);
SfxDatabase.Instance.Play(SoundEffect.IncreaseStat);
InventoryEventNotification.Invoke($"All stats have been restored.");
break;
case UsableItemTag.LowerTargetTo1HP:
_player.HealthComponent.SetCurrentHealth(1);
SfxDatabase.Instance.Play(SoundEffect.DecreaseStat);
InventoryEventNotification.Invoke($"HP has been reduced to 1.");
break;
case UsableItemTag.DoubleStackedItems:
var stackableItems = _player.Inventory.Items.OfType<IStackable>().Except([effectItem]).ToList();
stackableItems.ForEach(x => x.Count.OnNext(x.Count.Value * 2));
InventoryEventNotification.Invoke($"Items with inventory count have been doubled.");
break;
case UsableItemTag.IdentifyRandomItem:
var unidentifiedItems = _player.Inventory.Items.Where(x => x.ItemTag == ItemTag.MysteryItem).ToList();
if (!unidentifiedItems.Any())
return;
var rng = new RandomNumberGenerator();
rng.Randomize();
var index = rng.RandiRange(0, unidentifiedItems.Count - 1);
var itemToIdentify = unidentifiedItems[index];
var identifiedItem = _player.IdentifyItem(itemToIdentify);
InventoryEventNotification.Invoke($"{itemToIdentify} identified to be {identifiedItem.ItemName}.");
break;
case UsableItemTag.IdentifyAllItemsCostHP:
var unidentifiedItemsAll = _player.Inventory.Items.Where(x => x.ItemTag == ItemTag.MysteryItem).ToList();
if (!unidentifiedItemsAll.Any())
return;
foreach (var item in unidentifiedItemsAll)
_player.IdentifyItem(item);
InventoryEventNotification.Invoke($"All items identified.");
break;
}
_player.EquipmentComponent.UpdateEquipment(_player.EquipmentComponent.EquippedWeapon.Value);
_player.EquipmentComponent.UpdateEquipment(_player.EquipmentComponent.EquippedArmor.Value);
_player.EquipmentComponent.UpdateEquipment(_player.EquipmentComponent.EquippedAccessory.Value);
}
private void RemoveItemOrSubtractFromItemCount(IBaseInventoryItem item)
{
if (item is IStackable stackableItem && stackableItem.Count.Value > 1)
stackableItem.SetCount(stackableItem.Count.Value - 1);
else
_player.Inventory.Remove(item);
}
public void ShowDebugInfo(bool show) => InGameUI.DebugInfo.Visible = show;
private void EndDoubleExpTimer()
{
GameRepo.AnnounceMessageOnMainScreen("Experience points effect wore off.");
SfxDatabase.Instance.Play(SoundEffect.MoveUI);
_player.ExperiencePointsComponent.ModifyExpGainRate(_player.ExperiencePointsComponent.ExpGainRate.Value / 2);
_doubleExpTimer.Stop();
}
private void GameRepo_EnemyDied(IEnemy obj)
{
var rng = new RandomNumberGenerator();
rng.Randomize();
if (rng.Randf() < 0.15f)
DropItem(obj.GlobalPosition);
else
DropRestorative(obj.GlobalPosition);
}
private void BroadcastMessage(string obj)
{
InGameUI.InventoryMessageUI.DisplayMessage(obj);
}
private void MovePlayer((Vector3 Rotation, Vector3 Position) spawnPoint) => _player.TeleportPlayer(spawnPoint);
private void OnNewGame()
{
SaveFile.Load();
GameState.Input(new GameState.Input.NewGame());
GameRepo.Resume();
}
private void OnFloorLoadFinished()
{
LoadNextLevel.Hide();
GameLoaded?.Invoke();
_map.FadeIn();
if (GameOverMenu.Visible)
GameOverMenu.FadeOut();
GameRepo.Resume();
_player.Activate();
}
private void RustStatusChanged(bool rustStatus)
{
if (rustStatus)
{
_rustTimer.Start();
_rustDurationTimer.Start();
GameRepo.AnnounceMessageOnMainScreen("Afflicted with Rust.");
}
else
{
_rustTimer.Stop();
GameRepo.AnnounceMessageOnMainScreen("Rust affliction has faded.");
}
}
private void RustTimeout()
{
_player.HealthComponent.Damage(3, ElementType.Ferrum);
}
private void RustWoreOff() => _player.StatusEffectComponent.Rust.OnNext(false);
public void NotifyInventory(string message) => InventoryEventNotification?.Invoke(message);
private void OnQuit() => GameExitRequested?.Invoke();
public void OnExitTree()
{
InGameUI.UseTeleportPrompt.TeleportToNextFloor -= UseTeleportPrompt_TeleportToNextFloor;
InGameUI.UseTeleportPrompt.CloseTeleportPrompt -= UseTeleportPrompt_CloseTeleportPrompt;
LoadNextLevel.GoToNextFloor -= FloorClearMenu_GoToNextFloor;
LoadNextLevel.Exit -= FloorClearMenu_Exit;
LoadNextLevel.TransitionCompleted -= FloorClearMenu_TransitionCompleted;
OnLoadLevelRequest -= LoadLevel;
GameRepo.CloseInventoryEvent -= ExitInventoryAction;
_player.Inventory.BroadcastMessage -= BroadcastMessage;
_map.FloorLoaded -= OnFloorLoadFinished;
_player.PlayerDied -= GameOver;
GameOverMenu.NewGame -= OnNewGame;
GameOverMenu.QuitGame -= OnQuit;
PauseMenu.ExitGamePressed -= OnQuit;
GameRepo.IsPaused.Sync -= IsPaused_Sync;
}
}