Move files and folders to new repo format to enable multi-project format
This commit is contained in:
493
Zennysoft.Game.Ma/src/game/Game.cs
Normal file
493
Zennysoft.Game.Ma/src/game/Game.cs
Normal file
@@ -0,0 +1,493 @@
|
||||
|
||||
namespace Zennysoft.Game.Ma;
|
||||
|
||||
using Chickensoft.AutoInject;
|
||||
using Chickensoft.Collections;
|
||||
using Chickensoft.Introspection;
|
||||
using Chickensoft.SaveFileBuilder;
|
||||
using Chickensoft.Serialization;
|
||||
using Chickensoft.Serialization.Godot;
|
||||
using Zennysoft.Game.Ma.src.items;
|
||||
using Godot;
|
||||
using System;
|
||||
using System.IO.Abstractions;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization.Metadata;
|
||||
using static Zennysoft.Game.Ma.GameLogic.State;
|
||||
|
||||
[Meta(typeof(IAutoNode))]
|
||||
public partial class Game : Node3D, IGame
|
||||
{
|
||||
public override void _Notification(int what) => this.Notify(what);
|
||||
|
||||
IGameRepo IProvide<IGameRepo>.Value() => GameRepo;
|
||||
|
||||
IGame IProvide<IGame>.Value() => this;
|
||||
|
||||
IPlayer IProvide<IPlayer>.Value() => Player;
|
||||
|
||||
IGameEventDepot IProvide<IGameEventDepot>.Value() => GameEventDepot;
|
||||
|
||||
public IInstantiator Instantiator { get; set; } = default!;
|
||||
|
||||
public IGameLogic GameLogic { get; set; } = default!;
|
||||
|
||||
public IGameEventDepot GameEventDepot { get; set; } = default!;
|
||||
|
||||
public IGameRepo GameRepo { get; set; } = default!;
|
||||
|
||||
public GameLogic.IBinding GameBinding { get; set; } = default!;
|
||||
|
||||
[Signal]
|
||||
public delegate void StatRaisedAlertEventHandler(string statRaisedAlert);
|
||||
|
||||
[Dependency] public IAppRepo AppRepo => this.DependOn<IAppRepo>();
|
||||
|
||||
#region Nodes
|
||||
[Node] private IMap Map { get; set; } = default!;
|
||||
|
||||
[Node] private InGameUI InGameUI { get; set; } = default!;
|
||||
|
||||
[Node] private IFloorClearMenu FloorClearMenu { get; set; } = default!;
|
||||
|
||||
[Node] private DeathMenu DeathMenu { get; set; } = default!;
|
||||
|
||||
[Node] private IPauseMenu PauseMenu { get; set; } = default!;
|
||||
|
||||
[Node] private InGameAudio InGameAudio { get; set; } = default!;
|
||||
|
||||
[Node] private Timer DoubleEXPTimer { get; set; } = default!;
|
||||
|
||||
[Node] private IPlayer Player { get; set; } = default!;
|
||||
#endregion
|
||||
|
||||
#region Save
|
||||
public JsonSerializerOptions JsonOptions { get; set; } = default!;
|
||||
|
||||
public const string SAVE_FILE_NAME = "game.json";
|
||||
|
||||
public IFileSystem FileSystem { get; set; } = default!;
|
||||
|
||||
public string SaveFilePath { 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();
|
||||
#endregion
|
||||
|
||||
public RescuedItemDatabase RescuedItems { get; set; } = default!;
|
||||
|
||||
private EffectService _effectService;
|
||||
|
||||
public void Setup()
|
||||
{
|
||||
FileSystem = new FileSystem();
|
||||
|
||||
SaveFilePath = FileSystem.Path.Join(OS.GetUserDataDir(), SAVE_FILE_NAME);
|
||||
|
||||
GameRepo = new GameRepo();
|
||||
GameLogic = new GameLogic();
|
||||
GameEventDepot = new GameEventDepot();
|
||||
GameLogic.Set(GameRepo);
|
||||
GameLogic.Set(AppRepo);
|
||||
GameLogic.Set(GameEventDepot);
|
||||
GameLogic.Set(Player);
|
||||
GameLogic.Set(Map);
|
||||
Instantiator = new Instantiator(GetTree());
|
||||
RescuedItems = new RescuedItemDatabase();
|
||||
|
||||
var resolver = new SerializableTypeResolver();
|
||||
GodotSerialization.Setup();
|
||||
Serializer.AddConverter(new Texture2DConverter());
|
||||
|
||||
var upgradeDependencies = new Blackboard();
|
||||
|
||||
JsonOptions = new JsonSerializerOptions
|
||||
{
|
||||
Converters = {
|
||||
new SerializableTypeConverter(upgradeDependencies)
|
||||
},
|
||||
TypeInfoResolver = JsonTypeInfoResolver.Combine(resolver, WeaponTagEnumContext.Default, ItemTagEnumContext.Default, ElementTypeEnumContext.Default, AccessoryTagEnumContext.Default, ThrowableItemTagEnumContext.Default, UsableItemTagEnumContext.Default, BoxItemTagEnumContext.Default),
|
||||
WriteIndented = true
|
||||
};
|
||||
|
||||
GameChunk = new SaveChunk<GameData>(
|
||||
(chunk) =>
|
||||
{
|
||||
var gameData = new GameData()
|
||||
{
|
||||
PlayerData = new PlayerData()
|
||||
{
|
||||
PlayerStats = new PlayerStats()
|
||||
{
|
||||
CurrentHP = Player.Stats.CurrentHP.Value,
|
||||
MaximumHP = Player.Stats.MaximumHP.Value,
|
||||
CurrentVT = Player.Stats.CurrentVT.Value,
|
||||
MaximumVT = Player.Stats.MaximumVT.Value,
|
||||
CurrentAttack = Player.Stats.CurrentAttack.Value,
|
||||
BonusAttack = Player.Stats.BonusAttack.Value,
|
||||
MaxAttack = Player.Stats.MaxAttack.Value,
|
||||
CurrentDefense = Player.Stats.CurrentDefense.Value,
|
||||
BonusDefense = Player.Stats.BonusDefense.Value,
|
||||
MaxDefense = Player.Stats.MaxDefense.Value,
|
||||
CurrentExp = Player.Stats.CurrentExp.Value,
|
||||
CurrentLevel = Player.Stats.CurrentLevel.Value,
|
||||
ExpToNextLevel = Player.Stats.ExpToNextLevel.Value,
|
||||
Luck = Player.Stats.Luck.Value
|
||||
},
|
||||
Inventory = Player.Inventory
|
||||
},
|
||||
MapData = new MapData()
|
||||
{
|
||||
FloorScenes = Map.FloorScenes
|
||||
},
|
||||
RescuedItems = new RescuedItemDatabase() { Items = RescuedItems.Items }
|
||||
};
|
||||
|
||||
return gameData;
|
||||
},
|
||||
onLoad: (chunk, data) =>
|
||||
{
|
||||
RescuedItems = data.RescuedItems;
|
||||
chunk.LoadChunkSaveData(data.PlayerData);
|
||||
chunk.LoadChunkSaveData(data.MapData);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public void OnResolved()
|
||||
{
|
||||
SaveFile = new SaveFile<GameData>(
|
||||
root: GameChunk,
|
||||
onSave: async (GameData data) =>
|
||||
{
|
||||
// Save the game data to disk.
|
||||
var json = JsonSerializer.Serialize(data, JsonOptions);
|
||||
await FileSystem.File.WriteAllTextAsync(SaveFilePath, json);
|
||||
},
|
||||
onLoad: async () =>
|
||||
{
|
||||
// Load the game data from disk.
|
||||
if (!FileSystem.File.Exists(SaveFilePath))
|
||||
{
|
||||
GD.Print("No save file to load");
|
||||
return null;
|
||||
}
|
||||
|
||||
var json = await FileSystem.File.ReadAllTextAsync(SaveFilePath);
|
||||
return JsonSerializer.Deserialize<GameData>(json, JsonOptions);
|
||||
}
|
||||
);
|
||||
|
||||
GameBinding = GameLogic.Bind();
|
||||
GameBinding
|
||||
.Handle((in GameLogic.Output.StartGame _) =>
|
||||
{
|
||||
GameRepo.Resume();
|
||||
InGameUI.Show();
|
||||
})
|
||||
.Handle((in GameLogic.Output.GoToOverworld _) =>
|
||||
{
|
||||
GameEventDepot.OnOverworldEntered();
|
||||
})
|
||||
.Handle((in GameLogic.Output.SetPauseMode output) => CallDeferred(nameof(SetPauseMode), output.IsPaused))
|
||||
.Handle((in GameLogic.Output.ShowPauseMenu _) =>
|
||||
{
|
||||
PauseMenu.Show();
|
||||
PauseMenu.FadeIn();
|
||||
Input.MouseMode = Input.MouseModeEnum.Visible;
|
||||
PauseMenu.SetProcessUnhandledInput(true);
|
||||
})
|
||||
.Handle((in GameLogic.Output.LoadNextFloor _) => { Map.SpawnNextFloor(); })
|
||||
.Handle((in GameLogic.Output.HidePauseMenu _) => { PauseMenu.Hide(); })
|
||||
.Handle((in GameLogic.Output.ExitPauseMenu _) => { PauseMenu.FadeOut(); Input.MouseMode = Input.MouseModeEnum.Visible; PauseMenu.SetProcessUnhandledInput(false); })
|
||||
.Handle((in GameLogic.Output.ShowFloorClearMenu _) => { FloorClearMenu.Show(); FloorClearMenu.FadeIn(); })
|
||||
.Handle((in GameLogic.Output.ExitFloorClearMenu _) => { FloorClearMenu.FadeOut(); })
|
||||
.Handle((in GameLogic.Output.OpenInventory _) => { InGameUI.ShowInventoryScreen(); InGameUI.InventoryMenu.SetProcessInput(true); })
|
||||
.Handle((in GameLogic.Output.HideInventory _) => { InGameUI.HideInventoryScreen(); InGameUI.InventoryMenu.SetProcessInput(false); })
|
||||
.Handle((in GameLogic.Output.ShowMiniMap _) => { InGameUI.ShowMiniMap(); })
|
||||
.Handle((in GameLogic.Output.HideMiniMap _) => { InGameUI.HideMiniMap(); })
|
||||
.Handle((in GameLogic.Output.ShowAskForTeleport _) => { GameRepo.Pause(); InGameUI.UseTeleportPrompt.FadeIn(); InGameUI.SetProcessInput(true); })
|
||||
.Handle((in GameLogic.Output.HideAskForTeleport _) => { GameRepo.Resume(); InGameUI.UseTeleportPrompt.FadeOut(); InGameUI.SetProcessInput(false); })
|
||||
.Handle((in GameLogic.Output.ShowLostScreen _) => { DeathMenu.Show(); DeathMenu.FadeIn(); })
|
||||
.Handle((in GameLogic.Output.ExitLostScreen _) => { DeathMenu.FadeOut(); })
|
||||
.Handle((in GameLogic.Output.SaveGame _) =>
|
||||
{
|
||||
SaveFile.Save();
|
||||
AppRepo.OnExitGame();
|
||||
GetTree().Quit();
|
||||
// Back to title screen
|
||||
});
|
||||
GameLogic.Start();
|
||||
|
||||
GameLogic.Input(new GameLogic.Input.Initialize());
|
||||
|
||||
this.Provide();
|
||||
|
||||
PauseMenu.TransitionCompleted += OnPauseMenuTransitioned;
|
||||
PauseMenu.UnpauseButtonPressed += PauseMenu_UnpauseButtonPressed;
|
||||
InGameUI.InventoryMenu.ClosedMenu += InventoryMenu_CloseInventory;
|
||||
InGameUI.MinimapButtonReleased += Player_MinimapButtonReleased;
|
||||
|
||||
InGameUI.UseTeleportPrompt.TeleportToNextFloor += UseTeleportPrompt_TeleportToNextFloor;
|
||||
InGameUI.UseTeleportPrompt.CloseTeleportPrompt += UseTeleportPrompt_CloseTeleportPrompt;
|
||||
|
||||
FloorClearMenu.GoToNextFloor += FloorClearMenu_GoToNextFloor;
|
||||
FloorClearMenu.SaveAndExit += FloorClearMenu_SaveAndExit;
|
||||
FloorClearMenu.TransitionCompleted += FloorClearMenu_TransitionCompleted;
|
||||
|
||||
GameEventDepot.RestorativePickedUp += GameEventDepot_RestorativePickedUp;
|
||||
|
||||
DoubleEXPTimer.Timeout += DoubleEXPTimer_Timeout;
|
||||
|
||||
_effectService = new EffectService(this, Player);
|
||||
}
|
||||
|
||||
public void LoadExistingGame()
|
||||
{
|
||||
SaveFile.Load()
|
||||
.ContinueWith((_) => CallDeferred(nameof(FinishedLoadingSaveFile)));
|
||||
}
|
||||
|
||||
public void TogglePause()
|
||||
{
|
||||
if (GameLogic.Value is Paused)
|
||||
GameLogic.Input(new GameLogic.Input.UnpauseGame());
|
||||
else
|
||||
GameLogic.Input(new GameLogic.Input.PauseGame());
|
||||
}
|
||||
|
||||
public void ToggleInventory()
|
||||
{
|
||||
if (GameLogic.Value is InventoryOpened)
|
||||
GameLogic.Input(new GameLogic.Input.CloseInventory());
|
||||
else
|
||||
GameLogic.Input(new GameLogic.Input.OpenInventory());
|
||||
}
|
||||
|
||||
public void ToggleMinimap()
|
||||
{
|
||||
GameLogic.Input(new GameLogic.Input.MiniMapButtonPressed());
|
||||
}
|
||||
|
||||
public void FloorExitReached()
|
||||
{
|
||||
GameLogic.Input(new GameLogic.Input.AskForTeleport());
|
||||
GameEventDepot.OnTeleportEntered();
|
||||
}
|
||||
|
||||
public void UseItem(InventoryItem item)
|
||||
{
|
||||
if (item is ConsumableItem consumableItem)
|
||||
{
|
||||
if (Player.Stats.CurrentHP == Player.Stats.MaximumHP && consumableItem.RaiseHPAmount > 0)
|
||||
Player.RaiseHP(consumableItem.RaiseHPAmount);
|
||||
if (Player.Stats.CurrentVT == Player.Stats.MaximumVT && consumableItem.RaiseVTAmount > 0)
|
||||
Player.RaiseVT(consumableItem.RaiseVTAmount);
|
||||
|
||||
if (consumableItem.HealHPAmount > 0 && Player.Stats.CurrentHP != Player.Stats.MaximumHP)
|
||||
Player.HealHP(consumableItem.HealHPAmount);
|
||||
if (consumableItem.HealVTAmount > 0 && Player.Stats.CurrentVT != Player.Stats.MaximumVT)
|
||||
Player.HealVT(consumableItem.HealVTAmount);
|
||||
}
|
||||
if (item is EffectItem effectItem)
|
||||
{
|
||||
switch (effectItem.UsableItemTag)
|
||||
{
|
||||
case UsableItemTag.DoubleEXP:
|
||||
DoubleEXP(TimeSpan.FromSeconds(30));
|
||||
break;
|
||||
case UsableItemTag.TeleportAllEnemiesToRoom:
|
||||
_effectService.TeleportEnemiesToCurrentRoom();
|
||||
break;
|
||||
case UsableItemTag.KillHalfEnemiesInRoom:
|
||||
_effectService.KillHalfEnemiesInRoom();
|
||||
break;
|
||||
case UsableItemTag.TurnAllEnemiesIntoHealingItem:
|
||||
_effectService.TurnAllEnemiesInRoomIntoHealingItem();
|
||||
break;
|
||||
case UsableItemTag.HealsAllInRoomToMaxHP:
|
||||
_effectService.HealAllEnemiesAndPlayerInRoomToFull();
|
||||
break;
|
||||
case UsableItemTag.AbsorbHPFromAllEnemiesInRoom:
|
||||
_effectService.AbsorbHPFromAllEnemiesInRoom();
|
||||
break;
|
||||
case UsableItemTag.DealElementalDamageToAllEnemiesInRoom:
|
||||
_effectService.DealElementalDamageToAllEnemiesInRoom(ElementType.Hydric);
|
||||
break;
|
||||
case UsableItemTag.SwapHPAndVT:
|
||||
_effectService.SwapHPandVT();
|
||||
break;
|
||||
case UsableItemTag.RaiseCurrentWeaponAttack:
|
||||
_effectService.RaiseCurrentWeaponAttack();
|
||||
break;
|
||||
case UsableItemTag.RaiseCurrentDefenseArmor:
|
||||
_effectService.RaiseCurrentArmorDefense();
|
||||
break;
|
||||
case UsableItemTag.RaiseLevel:
|
||||
_effectService.RaiseLevel();
|
||||
break;
|
||||
case UsableItemTag.RandomEffect:
|
||||
_effectService.RandomEffect(effectItem);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (item is ThrowableItem throwableItem)
|
||||
{
|
||||
if (throwableItem.HealHPAmount > 0)
|
||||
Player.HealHP(throwableItem.HealHPAmount);
|
||||
if (throwableItem.HealVTAmount > 0)
|
||||
Player.HealVT(throwableItem.HealVTAmount);
|
||||
|
||||
if (throwableItem.ThrowableItemTag == ThrowableItemTag.TeleportToRandomLocation)
|
||||
_effectService.TeleportToRandomRoom(Player);
|
||||
|
||||
if (throwableItem.ThrowableItemTag == ThrowableItemTag.CanChangeAffinity)
|
||||
_effectService.ChangeAffinity(throwableItem);
|
||||
|
||||
if (throwableItem.ThrowableItemTag == ThrowableItemTag.WarpToExitIfFound)
|
||||
_effectService.WarpToExit(Player);
|
||||
}
|
||||
}
|
||||
|
||||
public void DropItem(InventoryItem item)
|
||||
{
|
||||
var droppedScene = GD.Load<PackedScene>("res://src/items/dropped/DroppedItem.tscn");
|
||||
var dropped = droppedScene.Instantiate<DroppedItem>();
|
||||
dropped.Item = item;
|
||||
AddChild(dropped);
|
||||
dropped.Drop();
|
||||
}
|
||||
|
||||
public void ThrowItem(InventoryItem item)
|
||||
{
|
||||
var thrownScene = GD.Load<PackedScene>("res://src/items/thrown/ThrownItem.tscn");
|
||||
var thrown = thrownScene.Instantiate<ThrownItem>();
|
||||
thrown.ItemThatIsThrown = item;
|
||||
AddChild(thrown);
|
||||
thrown.Position += new Vector3(0, 1.5f, 0);
|
||||
thrown.Throw(_effectService);
|
||||
}
|
||||
|
||||
public void AnnounceMessageOnInventoryScreen(string message)
|
||||
{
|
||||
InGameUI.InventoryMenu.ShowMessage(message);
|
||||
}
|
||||
|
||||
public void AnnounceMessageOnMainScreen(string message)
|
||||
{
|
||||
InGameUI.PlayerInfoUI.DisplayMessage(message);
|
||||
}
|
||||
|
||||
public IDungeonFloor CurrentFloor => Map.CurrentFloor;
|
||||
|
||||
public void EnemyDefeated(Vector3 defeatedLocation, EnemyStatResource resource)
|
||||
{
|
||||
Player.GainExp(resource.ExpFromDefeat * GameRepo.EXPRate);
|
||||
}
|
||||
|
||||
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 = vector;
|
||||
}
|
||||
|
||||
private void UseTeleportPrompt_CloseTeleportPrompt()
|
||||
{
|
||||
GameLogic.Input(new GameLogic.Input.HideAskForTeleport());
|
||||
}
|
||||
|
||||
private void UseTeleportPrompt_TeleportToNextFloor()
|
||||
{
|
||||
GameLogic.Input(new GameLogic.Input.FloorExitReached());
|
||||
GameEventDepot.OnDungeonAThemeAreaEntered();
|
||||
}
|
||||
|
||||
private void PauseMenu_UnpauseButtonPressed()
|
||||
{
|
||||
GameLogic.Input(new GameLogic.Input.UnpauseGame());
|
||||
}
|
||||
|
||||
private void Player_PauseButtonPressed()
|
||||
{
|
||||
GameLogic.Input(new GameLogic.Input.PauseGame());
|
||||
}
|
||||
|
||||
private void Player_MinimapButtonReleased()
|
||||
{
|
||||
GameLogic.Input(new GameLogic.Input.MiniMapButtonReleased());
|
||||
}
|
||||
|
||||
private void FloorClearMenu_TransitionCompleted()
|
||||
{
|
||||
GameRepo.Resume();
|
||||
if (Player.EquippedWeapon.Value.ItemTag == ItemTag.BreaksOnChange)
|
||||
Player.Unequip(Player.EquippedWeapon.Value);
|
||||
if (Player.EquippedArmor.Value.ItemTag == ItemTag.BreaksOnChange)
|
||||
Player.Unequip(Player.EquippedArmor.Value);
|
||||
if (Player.EquippedAccessory.Value.ItemTag == ItemTag.BreaksOnChange)
|
||||
Player.Unequip(Player.EquippedAccessory.Value);
|
||||
}
|
||||
|
||||
private void FloorClearMenu_GoToNextFloor()
|
||||
{
|
||||
GameLogic.Input(new GameLogic.Input.GoToNextFloor());
|
||||
}
|
||||
|
||||
private void FloorClearMenu_SaveAndExit()
|
||||
{
|
||||
// Save
|
||||
GameLogic.Input(new GameLogic.Input.SaveGame());
|
||||
}
|
||||
|
||||
private void GameEventDepot_RestorativePickedUp(Restorative obj)
|
||||
=> Player.Stats.SetCurrentVT(Player.Stats.CurrentVT.Value + obj.VTRestoreAmount);
|
||||
|
||||
private void SetPauseMode(bool isPaused)
|
||||
{
|
||||
if (GetTree() != null)
|
||||
GetTree().Paused = isPaused;
|
||||
}
|
||||
|
||||
public async void DoubleEXP(TimeSpan lengthOfEffect)
|
||||
{
|
||||
ToggleInventory();
|
||||
AnnounceMessageOnMainScreen("Experience points temporarily doubled.");
|
||||
DoubleEXPTimer.Start(lengthOfEffect.Seconds);
|
||||
GameRepo.EXPRate = 2;
|
||||
}
|
||||
|
||||
private void DoubleEXPTimer_Timeout()
|
||||
{
|
||||
DoubleEXPTimer.Stop();
|
||||
GameRepo.EXPRate = 1;
|
||||
AnnounceMessageOnMainScreen("Experience points effect wore off.");
|
||||
}
|
||||
|
||||
private void InventoryMenu_CloseInventory() => GameLogic.Input(new GameLogic.Input.CloseInventory());
|
||||
|
||||
public void NextFloorLoaded()
|
||||
{
|
||||
GameLogic.Input(new GameLogic.Input.HideFloorClearMenu());
|
||||
}
|
||||
|
||||
private void OnPauseMenuTransitioned()
|
||||
{
|
||||
GameLogic.Input(new GameLogic.Input.PauseMenuTransitioned());
|
||||
}
|
||||
|
||||
public void OnStart() =>
|
||||
GameLogic.Input(new GameLogic.Input.StartGame());
|
||||
|
||||
private void FinishedLoadingSaveFile()
|
||||
{
|
||||
EmitSignal(SignalName.SaveFileLoaded);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user