Major Player refactor

This commit is contained in:
2025-02-07 02:29:50 -08:00
parent 0cdae88952
commit fe1a1e61ef
132 changed files with 2554 additions and 2478 deletions

View File

@@ -1,43 +1,44 @@
using Godot;
using System;
namespace GameJamDungeon;
public partial class CharacterBody3d : CharacterBody3D
{
public const float Speed = 5.0f;
public const float JumpVelocity = 4.5f;
public const float Speed = 5.0f;
public const float JumpVelocity = 4.5f;
public override void _PhysicsProcess(double delta)
{
Vector3 velocity = Velocity;
public override void _PhysicsProcess(double delta)
{
Vector3 velocity = Velocity;
// Add the gravity.
if (!IsOnFloor())
{
velocity += GetGravity() * (float)delta;
}
// Add the gravity.
if (!IsOnFloor())
{
velocity += GetGravity() * (float)delta;
}
// Handle Jump.
if (Input.IsActionJustPressed("ui_accept") && IsOnFloor())
{
velocity.Y = JumpVelocity;
}
// Handle Jump.
if (Input.IsActionJustPressed("ui_accept") && IsOnFloor())
{
velocity.Y = JumpVelocity;
}
// Get the input direction and handle the movement/deceleration.
// As good practice, you should replace UI actions with custom gameplay actions.
Vector2 inputDir = Input.GetVector("ui_left", "ui_right", "ui_up", "ui_down");
Vector3 direction = (Transform.Basis * new Vector3(inputDir.X, 0, inputDir.Y)).Normalized();
if (direction != Vector3.Zero)
{
velocity.X = direction.X * Speed;
velocity.Z = direction.Z * Speed;
}
else
{
velocity.X = Mathf.MoveToward(Velocity.X, 0, Speed);
velocity.Z = Mathf.MoveToward(Velocity.Z, 0, Speed);
}
// Get the input direction and handle the movement/deceleration.
// As good practice, you should replace UI actions with custom gameplay actions.
Vector2 inputDir = Input.GetVector("ui_left", "ui_right", "ui_up", "ui_down");
Vector3 direction = (Transform.Basis * new Vector3(inputDir.X, 0, inputDir.Y)).Normalized();
if (direction != Vector3.Zero)
{
velocity.X = direction.X * Speed;
velocity.Z = direction.Z * Speed;
}
else
{
velocity.X = Mathf.MoveToward(Velocity.X, 0, Speed);
velocity.Z = Mathf.MoveToward(Velocity.Z, 0, Speed);
}
Velocity = velocity;
MoveAndSlide();
}
Velocity = velocity;
MoveAndSlide();
}
}

View File

@@ -3,78 +3,77 @@ using Chickensoft.GodotNodeInterfaces;
using Chickensoft.Introspection;
using Godot;
namespace GameJamDungeon
namespace GameJamDungeon;
public interface IApp : ICanvasLayer, IProvide<IAppRepo>;
[Meta(typeof(IAutoNode))]
public partial class App : CanvasLayer, IApp
{
public interface IApp : ICanvasLayer, IProvide<IAppRepo>;
public override void _Notification(int what) => this.Notify(what);
[Meta(typeof(IAutoNode))]
public partial class App : CanvasLayer, IApp
public const string GAME_SCENE_PATH = "res://src/game/Game.tscn";
public IGame Game { get; set; } = default!;
public IInstantiator Instantiator { get; set; } = default!;
IAppRepo IProvide<IAppRepo>.Value() => AppRepo;
public IAppRepo AppRepo { get; set; } = default!;
public IAppLogic AppLogic { get; set; } = default!;
public AppLogic.IBinding AppBinding { get; set; } = default!;
[Node] public IMenu Menu { get; set; } = default!;
[Node] public ISubViewport GameWindow { get; set; } = default!;
public void Initialize()
{
public override void _Notification(int what) => this.Notify(what);
Instantiator = new Instantiator(GetTree());
AppRepo = new AppRepo();
AppLogic = new AppLogic();
AppLogic.Set(AppRepo);
Menu.NewGame += OnNewGame;
Menu.Quit += OnQuit;
Input.MouseMode = Input.MouseModeEnum.Visible;
this.Provide();
}
public const string GAME_SCENE_PATH = "res://src/game/Game.tscn";
public void OnReady()
{
AppBinding = AppLogic.Bind();
public IGame Game { get; set; } = default!;
public IInstantiator Instantiator { get; set; } = default!;
IAppRepo IProvide<IAppRepo>.Value() => AppRepo;
public IAppRepo AppRepo { get; set; } = default!;
public IAppLogic AppLogic { get; set; } = default!;
public AppLogic.IBinding AppBinding { get; set; } = default!;
[Node] public IMenu Menu { get; set; } = default!;
[Node] public ISubViewport GameWindow { get; set; } = default!;
public void Initialize()
{
Instantiator = new Instantiator(GetTree());
AppRepo = new AppRepo();
AppLogic = new AppLogic();
AppLogic.Set(AppRepo);
Menu.NewGame += OnNewGame;
Menu.Quit += OnQuit;
Input.MouseMode = Input.MouseModeEnum.Visible;
this.Provide();
}
public void OnReady()
{
AppBinding = AppLogic.Bind();
AppBinding
.Handle((in AppLogic.Output.ShowLoadingScreen _) =>
{
Menu.Hide();
})
.Handle((in AppLogic.Output.SetupGameScene _) =>
{
Menu.Hide();
Instantiator.SceneTree.Paused = true;
Game = Instantiator.LoadAndInstantiate<Game>(GAME_SCENE_PATH);
GameWindow.AddChildEx(Game);
})
.Handle((in AppLogic.Output.ShowGame _) =>
{
Instantiator.SceneTree.Paused = false;
Game.Show();
});
AppBinding
.Handle((in AppLogic.Output.ShowLoadingScreen _) =>
{
Menu.Hide();
})
.Handle((in AppLogic.Output.SetupGameScene _) =>
{
Menu.Hide();
Instantiator.SceneTree.Paused = true;
Game = Instantiator.LoadAndInstantiate<Game>(GAME_SCENE_PATH);
GameWindow.AddChildEx(Game);
})
.Handle((in AppLogic.Output.ShowGame _) =>
{
Instantiator.SceneTree.Paused = false;
Game.Show();
});
AppLogic.Start();
}
AppLogic.Start();
}
public void OnNewGame() => AppLogic.Input(new AppLogic.Input.NewGame());
public void OnNewGame() => AppLogic.Input(new AppLogic.Input.NewGame());
public void OnQuit() => AppLogic.Input(new AppLogic.Input.QuitGame());
public void OnQuit() => AppLogic.Input(new AppLogic.Input.QuitGame());
public void OnExitTree()
{
AppLogic.Stop();
AppBinding.Dispose();
AppRepo.Dispose();
}
public void OnExitTree()
{
AppLogic.Stop();
AppBinding.Dispose();
AppRepo.Dispose();
}
}

View File

@@ -1,77 +1,76 @@
using System;
namespace GameJamDungeon
namespace GameJamDungeon;
public interface IAppRepo : IDisposable
{
public interface IAppRepo : IDisposable
event Action? GameEntered;
event Action? GameExited;
event Action? SplashScreenSkipped;
event Action? MainMenuEntered;
event Action? ShowLoadingScreen;
void SkipSplashScreen();
void OnMainMenuEntered();
void OnEnterGame();
void OnExitGame();
void OnGameOver();
void OnShowLoadingScreen();
}
public class AppRepo : IAppRepo
{
public event Action? SplashScreenSkipped;
public event Action? MainMenuEntered;
public event Action? GameEntered;
public event Action? GameExited;
public event Action? ShowLoadingScreen;
private bool _disposedValue;
public void SkipSplashScreen() => SplashScreenSkipped?.Invoke();
public void OnMainMenuEntered() => MainMenuEntered?.Invoke();
public void OnEnterGame() => GameEntered?.Invoke();
public void OnExitGame() => GameExited?.Invoke();
public void OnGameOver() => GameExited?.Invoke();
public void OnShowLoadingScreen() => ShowLoadingScreen?.Invoke();
protected void Dispose(bool disposing)
{
event Action? GameEntered;
event Action? GameExited;
event Action? SplashScreenSkipped;
event Action? MainMenuEntered;
event Action? ShowLoadingScreen;
void SkipSplashScreen();
void OnMainMenuEntered();
void OnEnterGame();
void OnExitGame();
void OnGameOver();
void OnShowLoadingScreen();
if (!_disposedValue)
{
if (disposing)
{
// Dispose managed objects.
SplashScreenSkipped = null;
MainMenuEntered = null;
GameEntered = null;
GameExited = null;
ShowLoadingScreen = null;
}
_disposedValue = true;
}
}
public class AppRepo : IAppRepo
public void Dispose()
{
public event Action? SplashScreenSkipped;
public event Action? MainMenuEntered;
public event Action? GameEntered;
public event Action? GameExited;
public event Action? ShowLoadingScreen;
private bool _disposedValue;
public void SkipSplashScreen() => SplashScreenSkipped?.Invoke();
public void OnMainMenuEntered() => MainMenuEntered?.Invoke();
public void OnEnterGame() => GameEntered?.Invoke();
public void OnExitGame() => GameExited?.Invoke();
public void OnGameOver() => GameExited?.Invoke();
public void OnShowLoadingScreen() => ShowLoadingScreen?.Invoke();
protected void Dispose(bool disposing)
{
if (!_disposedValue)
{
if (disposing)
{
// Dispose managed objects.
SplashScreenSkipped = null;
MainMenuEntered = null;
GameEntered = null;
GameExited = null;
ShowLoadingScreen = null;
}
_disposedValue = true;
}
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}

View File

@@ -1,20 +1,19 @@
namespace GameJamDungeon
namespace GameJamDungeon;
public partial class AppLogic
{
public partial class AppLogic
public static class Input
{
public static class Input
{
public readonly record struct NewGame;
public readonly record struct NewGame;
public readonly record struct LoadGameFinished;
public readonly record struct LoadGameFinished;
public readonly record struct FadeOutFinished;
public readonly record struct FadeOutFinished;
public readonly record struct QuitGame;
public readonly record struct QuitGame;
public readonly record struct GameOver;
public readonly record struct GameOver;
public readonly record struct ShowLoadingScreen;
}
public readonly record struct ShowLoadingScreen;
}
}

View File

@@ -1,32 +1,31 @@
namespace GameJamDungeon
namespace GameJamDungeon;
public partial class AppLogic
{
public partial class AppLogic
public static class Output
{
public static class Output
{
public readonly record struct FadeToBlack;
public readonly record struct FadeToBlack;
public readonly record struct ShowSplashScreen;
public readonly record struct ShowSplashScreen;
public readonly record struct HideSplashScreen;
public readonly record struct HideSplashScreen;
public readonly record struct RemoveExistingGame;
public readonly record struct RemoveExistingGame;
public readonly record struct PlayGame;
public readonly record struct PlayGame;
public readonly record struct ShowGame;
public readonly record struct ShowGame;
public readonly record struct HideGame;
public readonly record struct HideGame;
public readonly record struct SetupGameScene;
public readonly record struct SetupGameScene;
public readonly record struct ShowLoadingScreen;
public readonly record struct ShowLoadingScreen;
public readonly record struct ShowMainMenu;
public readonly record struct ShowMainMenu;
public readonly record struct ExitGame;
public readonly record struct ExitGame;
public readonly record struct GameOver;
}
public readonly record struct GameOver;
}
}

View File

@@ -1,11 +1,10 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
namespace GameJamDungeon
namespace GameJamDungeon;
public partial class AppLogic
{
public partial class AppLogic
{
[Meta]
public abstract partial record State : StateLogic<State>;
}
[Meta]
public abstract partial record State : StateLogic<State>;
}

View File

@@ -1,14 +1,13 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
namespace GameJamDungeon
{
public interface IAppLogic : ILogicBlock<AppLogic.State>;
namespace GameJamDungeon;
[Meta]
[LogicBlock(typeof(State), Diagram = true)]
public partial class AppLogic : LogicBlock<AppLogic.State>, IAppLogic
{
public override Transition GetInitialState() => To<State.SetupGameScene>();
}
public interface IAppLogic : ILogicBlock<AppLogic.State>;
[Meta]
[LogicBlock(typeof(State), Diagram = true)]
public partial class AppLogic : LogicBlock<AppLogic.State>, IAppLogic
{
public override Transition GetInitialState() => To<State.SetupGameScene>();
}

View File

@@ -1,29 +1,28 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
namespace GameJamDungeon
{
public partial class AppLogic
{
public partial record State
{
[Meta]
public partial record SetupGameScene : State, IGet<Input.LoadGameFinished>
{
public SetupGameScene()
{
this.OnEnter(() =>
{
Output(new Output.SetupGameScene());
Output(new Output.ShowGame());
Input(new Input.LoadGameFinished());
});
}
namespace GameJamDungeon;
public Transition On(in Input.LoadGameFinished input)
public partial class AppLogic
{
public partial record State
{
[Meta]
public partial record SetupGameScene : State, IGet<Input.LoadGameFinished>
{
public SetupGameScene()
{
this.OnEnter(() =>
{
return To<InGame>();
}
Output(new Output.SetupGameScene());
Output(new Output.ShowGame());
Input(new Input.LoadGameFinished());
});
}
public Transition On(in Input.LoadGameFinished input)
{
return To<InGame>();
}
}
}

View File

@@ -1,37 +1,36 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
namespace GameJamDungeon
namespace GameJamDungeon;
public partial class AppLogic
{
public partial class AppLogic
public partial record State
{
public partial record State
[Meta]
public partial record InGame : State, IGet<Input.GameOver>
{
[Meta]
public partial record InGame : State, IGet<Input.GameOver>
public InGame()
{
public InGame()
this.OnEnter(() =>
{
Get<IAppRepo>().OnEnterGame();
Output(new Output.ShowGame());
});
this.OnExit(() => Output(new Output.HideGame()));
this.OnEnter(() =>
{
Get<IAppRepo>().OnEnterGame();
Output(new Output.ShowGame());
});
this.OnExit(() => Output(new Output.HideGame()));
OnAttach(() => Get<IAppRepo>().GameExited += OnGameExited);
OnDetach(() => Get<IAppRepo>().GameExited -= OnGameExited);
}
public Transition On(in Input.GameOver input)
{
Output(new Output.RemoveExistingGame());
return To<MainMenu>();
}
public void OnGameExited() => Input(new Input.GameOver());
OnAttach(() => Get<IAppRepo>().GameExited += OnGameExited);
OnDetach(() => Get<IAppRepo>().GameExited -= OnGameExited);
}
public Transition On(in Input.GameOver input)
{
Output(new Output.RemoveExistingGame());
return To<MainMenu>();
}
public void OnGameExited() => Input(new Input.GameOver());
}
}
}

View File

@@ -1,34 +1,33 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
namespace GameJamDungeon
{
public partial class AppLogic
{
public partial record State
{
[Meta]
public partial record MainMenu : State, IGet<Input.NewGame>, IGet<Input.QuitGame>
{
public MainMenu()
{
this.OnEnter(() =>
{
Get<IAppRepo>().OnMainMenuEntered();
Output(new Output.ShowMainMenu());
});
}
public Transition On(in Input.NewGame input)
{
Output(new Output.SetupGameScene());
return To<LoadingScreen>();
}
namespace GameJamDungeon;
public Transition On(in Input.QuitGame input)
public partial class AppLogic
{
public partial record State
{
[Meta]
public partial record MainMenu : State, IGet<Input.NewGame>, IGet<Input.QuitGame>
{
public MainMenu()
{
this.OnEnter(() =>
{
Output(new Output.ExitGame());
return ToSelf();
}
Get<IAppRepo>().OnMainMenuEntered();
Output(new Output.ShowMainMenu());
});
}
public Transition On(in Input.NewGame input)
{
Output(new Output.SetupGameScene());
return To<LoadingScreen>();
}
public Transition On(in Input.QuitGame input)
{
Output(new Output.ExitGame());
return ToSelf();
}
}
}

View File

@@ -1,34 +1,33 @@
namespace GameJamDungeon
namespace GameJamDungeon;
public partial class InGameAudioLogic
{
public partial class InGameAudioLogic
public static class Output
{
public static class Output
{
#region BGM
public readonly record struct PlayOverworldMusic;
#region BGM
public readonly record struct PlayOverworldMusic;
public readonly record struct PlayDungeonThemeAMusic;
#endregion
public readonly record struct PlayDungeonThemeAMusic;
#endregion
#region SFX
public readonly record struct PlayPlayerAttackSound;
#region SFX
public readonly record struct PlayPlayerAttackSound;
public readonly record struct PlayMenuScrollSound;
public readonly record struct PlayMenuScrollSound;
public readonly record struct PlayEquipSound;
public readonly record struct PlayEquipSound;
public readonly record struct PlayInventorySortedSound;
public readonly record struct PlayInventorySortedSound;
public readonly record struct PlayMenuBackSound;
public readonly record struct PlayMenuBackSound;
public readonly record struct PlayHealingItemSound;
public readonly record struct PlayHealingItemSound;
public readonly record struct PlayTeleportSound;
#endregion
public readonly record struct PlayTeleportSound;
#endregion
public readonly record struct PlayGameMusic;
public readonly record struct PlayGameMusic;
public readonly record struct StopGameMusic;
}
public readonly record struct StopGameMusic;
}
}

View File

@@ -1,6 +1,5 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
using System;
namespace GameJamDungeon;

View File

@@ -1,5 +1,4 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
namespace GameJamDungeon;

View File

@@ -1,6 +1,4 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
using System;
namespace GameJamDungeon;

View File

@@ -4,151 +4,149 @@ using Chickensoft.GodotNodeInterfaces;
using Chickensoft.Introspection;
using Godot;
namespace GameJamDungeon
namespace GameJamDungeon;
public interface IBoss : ICharacterBody3D
{
public interface IBoss : ICharacterBody3D
public AnimationTree AnimationTree { get; }
public AnimationPlayer HitAnimation { get; }
public Timer AttackTimer { get; }
public void Activate();
public AutoProp<double> CurrentHP { get; }
}
[Meta(typeof(IAutoNode))]
public partial class Boss : CharacterBody3D, IBoss, IProvide<IBossLogic>
{
public override void _Notification(int what) => this.Notify(what);
public IBossLogic BossLogic { get; set; } = default!;
[Export] public EnemyStatResource BossResource { get; set; } = default!;
IBossLogic IProvide<IBossLogic>.Value() => BossLogic;
public BossLogic.IBinding BossBinding { get; set; } = default!;
[Dependency] public IGameRepo GameRepo => this.DependOn<IGameRepo>();
[Dependency] public IGameEventDepot GameEventDepot => this.DependOn<IGameEventDepot>();
[Dependency] public IGame Game => this.DependOn<IGame>();
[Dependency] public IPlayer Player => this.DependOn<IPlayer>();
[Node] public AnimationTree AnimationTree { get; set; } = default!;
[Node] public Timer AttackTimer { get; set; } = default!;
[Node] public AnimationPlayer HitAnimation { get; set; } = default!;
[Node] public Area3D Hitbox { get; set; } = default!;
[Node] public Area3D AttackBox { get; set; } = default!;
[Node] public Area3D SecondaryAttackBox { get; set; } = default!;
public AutoProp<double> CurrentHP { get; set; }
public void Setup()
{
public AnimationTree AnimationTree { get; }
BossLogic = new BossLogic();
BossLogic.Set(this as IBoss);
BossLogic.Set(GameRepo);
public AnimationPlayer HitAnimation { get; }
public Timer AttackTimer { get; }
public void Activate();
public AutoProp<double> CurrentHP { get; }
SetPhysicsProcess(false);
Hide();
}
[Meta(typeof(IAutoNode))]
public partial class Boss : CharacterBody3D, IBoss, IProvide<IBossLogic>
public void OnResolved()
{
public override void _Notification(int what) => this.Notify(what);
public IBossLogic BossLogic { get; set; } = default!;
[Export] public EnemyStatResource BossResource { get; set; } = default!;
IBossLogic IProvide<IBossLogic>.Value() => BossLogic;
public BossLogic.IBinding BossBinding { get; set; } = default!;
[Dependency] public IGameRepo GameRepo => this.DependOn<IGameRepo>();
[Dependency] public IGameEventDepot GameEventDepot => this.DependOn<IGameEventDepot>();
[Dependency] public IGame Game => this.DependOn<IGame>();
[Node] public AnimationTree AnimationTree { get; set; } = default!;
[Node] public Timer AttackTimer { get; set; } = default!;
[Node] public AnimationPlayer HitAnimation { get; set; } = default!;
[Node] public Area3D Hitbox { get; set; } = default!;
[Node] public Area3D AttackBox { get; set; } = default!;
[Node] public Area3D SecondaryAttackBox { get; set; } = default!;
public AutoProp<double> CurrentHP { get; set; }
public void Setup()
BossBinding = BossLogic.Bind();
BossBinding
.Handle((in BossLogic.Output.Defeated output) =>
{
BossLogic = new BossLogic();
BossLogic.Set(this as IBoss);
BossLogic.Set(GameRepo);
HitAnimation.Play("Defeated");
});
this.Provide();
BossLogic.Start();
CurrentHP = new AutoProp<double>(BossResource.MaximumHP);
CurrentHP.Sync += OnHPChanged;
AttackTimer.Timeout += AttackTimer_Timeout;
Hitbox.AreaEntered += Hitbox_AreaEntered;
HitAnimation.AnimationFinished += HitAnimation_AnimationFinished;
AttackBox.AreaEntered += AttackBox_AreaEntered;
SecondaryAttackBox.AreaEntered += SecondaryAttackBox_AreaEntered;
}
SetPhysicsProcess(false);
Hide();
}
private void AttackBox_AreaEntered(Area3D area)
{
//var bossHitDamage = DamageCalculator.CalculateEnemyAttackDamage(GameRepo.PlayerData.CurrentDefense.Value + GameRepo.PlayerData.BonusDefense,
// BossResource,
// GameRepo.PlayerData.Inventory.EquippedArmor.Value.ArmorStats,
// false);
//GameRepo.PlayerData.SetCurrentHP(GameRepo.PlayerData.CurrentHP.Value - Mathf.RoundToInt(bossHitDamage));
}
public void OnResolved()
private void SecondaryAttackBox_AreaEntered(Area3D area)
{
//var bossHitDamage = DamageCalculator.CalculateEnemyAttackDamage(GameRepo.PlayerData.CurrentDefense.Value + GameRepo.PlayerData.BonusDefense,
// BossResource,
// GameRepo.PlayerData.Inventory.EquippedArmor.Value.ArmorStats,
// false);
//var nerfDamage = bossHitDamage *= 0.25f;
//GameRepo.PlayerData.SetCurrentHP(GameRepo.PlayerData.CurrentHP.Value - Mathf.RoundToInt(nerfDamage));
Player.Knockback(115f);
}
private void HitAnimation_AnimationFinished(StringName animName)
{
if (animName == "Defeated")
QueueFree();
}
private void Hitbox_AreaEntered(Area3D area)
{
if (area is IHitbox)
{
BossBinding = BossLogic.Bind();
BossBinding
.Handle((in BossLogic.Output.Defeated output) =>
{
HitAnimation.Play("Defeated");
});
this.Provide();
BossLogic.Start();
CurrentHP = new AutoProp<double>(BossResource.MaximumHP);
CurrentHP.Sync += OnHPChanged;
AttackTimer.Timeout += AttackTimer_Timeout;
Hitbox.AreaEntered += Hitbox_AreaEntered;
HitAnimation.AnimationFinished += HitAnimation_AnimationFinished;
AttackBox.AreaEntered += AttackBox_AreaEntered;
SecondaryAttackBox.AreaEntered += SecondaryAttackBox_AreaEntered;
var isCriticalHit = false;
var rng = new RandomNumberGenerator();
rng.Randomize();
var roll = rng.Randf();
if (roll <= Player.Inventory.EquippedWeapon.Value.Luck)
isCriticalHit = true;
}
}
private void AttackBox_AreaEntered(Area3D area)
{
//var bossHitDamage = DamageCalculator.CalculateEnemyAttackDamage(GameRepo.PlayerData.CurrentDefense.Value + GameRepo.PlayerData.BonusDefense,
// BossResource,
// GameRepo.PlayerData.Inventory.EquippedArmor.Value.ArmorStats,
// false);
//GameRepo.PlayerData.SetCurrentHP(GameRepo.PlayerData.CurrentHP.Value - Mathf.RoundToInt(bossHitDamage));
}
public void Activate()
{
BossLogic.Input(new BossLogic.Input.Activate());
}
private void SecondaryAttackBox_AreaEntered(Area3D area)
{
//var bossHitDamage = DamageCalculator.CalculateEnemyAttackDamage(GameRepo.PlayerData.CurrentDefense.Value + GameRepo.PlayerData.BonusDefense,
// BossResource,
// GameRepo.PlayerData.Inventory.EquippedArmor.Value.ArmorStats,
// false);
//var nerfDamage = bossHitDamage *= 0.25f;
//GameRepo.PlayerData.SetCurrentHP(GameRepo.PlayerData.CurrentHP.Value - Mathf.RoundToInt(nerfDamage));
Game.Player.ApplyCentralImpulseToPlayer(GlobalBasis.Z.Normalized());
}
private void AttackTimer_Timeout()
{
var random = new RandomNumberGenerator();
random.Randomize();
var selection = random.RandWeighted([0.2f, 0.8f]);
if (selection == 0)
BossLogic.Input(new BossLogic.Input.SecondaryAttack());
else
BossLogic.Input(new BossLogic.Input.PrimaryAttack());
}
private void HitAnimation_AnimationFinished(StringName animName)
{
if (animName == "Defeated")
QueueFree();
}
public void OnPhysicsProcess(double delta)
{
BossLogic.Input(new BossLogic.Input.PhysicsTick(delta));
MoveAndSlide();
}
private void Hitbox_AreaEntered(Area3D area)
{
if (area is IHitbox)
{
var isCriticalHit = false;
var rng = new RandomNumberGenerator();
rng.Randomize();
var roll = rng.Randf();
if (roll <= GameRepo.PlayerData.Inventory.EquippedWeapon.Value.WeaponStats.Luck)
isCriticalHit = true;
//var damage = DamageCalculator.CalculateWeaponAttackDamage(GameRepo.PlayerData.CurrentAttack.Value + GameRepo.PlayerData.BonusAttack, BossResource, GameRepo.PlayerData.Inventory.EquippedWeapon.Value.WeaponStats, isCriticalHit);
//GD.Print($"Enemy Hit for {damage} damage.");
//BossLogic.Input(new BossLogic.Input.HitByPlayer(damage));
}
}
public void Activate()
{
BossLogic.Input(new BossLogic.Input.Activate());
}
private void AttackTimer_Timeout()
{
var random = new RandomNumberGenerator();
random.Randomize();
var selection = random.RandWeighted([0.2f, 0.8f]);
if (selection == 0)
BossLogic.Input(new BossLogic.Input.SecondaryAttack());
else
BossLogic.Input(new BossLogic.Input.PrimaryAttack());
}
public void OnPhysicsProcess(double delta)
{
BossLogic.Input(new BossLogic.Input.PhysicsTick(delta));
MoveAndSlide();
}
private void OnHPChanged(double newHP)
{
if (newHP <= 0)
BossLogic.Input(new BossLogic.Input.BossDefeated());
}
private void OnHPChanged(double newHP)
{
if (newHP <= 0)
BossLogic.Input(new BossLogic.Input.BossDefeated());
}
}

View File

@@ -1,20 +1,19 @@
namespace GameJamDungeon
namespace GameJamDungeon;
public partial class BossLogic
{
public partial class BossLogic
public static class Input
{
public static class Input
{
public readonly record struct Activate();
public readonly record struct Activate();
public readonly record struct PhysicsTick(double Delta);
public readonly record struct PhysicsTick(double Delta);
public readonly record struct PrimaryAttack();
public readonly record struct PrimaryAttack();
public readonly record struct SecondaryAttack();
public readonly record struct SecondaryAttack();
public readonly record struct HitByPlayer(double Damage);
public readonly record struct HitByPlayer(double Damage);
public readonly record struct BossDefeated();
}
public readonly record struct BossDefeated();
}
}

View File

@@ -1,12 +1,11 @@
namespace GameJamDungeon
{
public partial class BossLogic
{
public static class Output
{
public readonly record struct HitByPlayer(double CurrentHP);
namespace GameJamDungeon;
public readonly record struct Defeated();
}
public partial class BossLogic
{
public static class Output
{
public readonly record struct HitByPlayer(double CurrentHP);
public readonly record struct Defeated();
}
}

View File

@@ -1,16 +1,15 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
namespace GameJamDungeon
namespace GameJamDungeon;
public partial class BossLogic
{
public partial class BossLogic
[Meta]
public abstract partial record State : StateLogic<State>
{
[Meta]
public abstract partial record State : StateLogic<State>
protected State()
{
protected State()
{
}
}
}
}

View File

@@ -1,14 +1,13 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
namespace GameJamDungeon
{
public interface IBossLogic : ILogicBlock<BossLogic.State>;
namespace GameJamDungeon;
[Meta, Id("boss_logic")]
[LogicBlock(typeof(State), Diagram = true)]
public partial class BossLogic : LogicBlock<BossLogic.State>, IBossLogic
{
public override Transition GetInitialState() => To<State.Idle>();
}
public interface IBossLogic : ILogicBlock<BossLogic.State>;
[Meta, Id("boss_logic")]
[LogicBlock(typeof(State), Diagram = true)]
public partial class BossLogic : LogicBlock<BossLogic.State>, IBossLogic
{
public override Transition GetInitialState() => To<State.Idle>();
}

View File

@@ -1,32 +1,31 @@
using Chickensoft.Introspection;
using Godot;
namespace GameJamDungeon
namespace GameJamDungeon;
public partial class BossLogic
{
public partial class BossLogic
public partial record State
{
public partial record State
[Meta, Id("boss_logic_state_alive")]
public abstract partial record Alive : State, IGet<Input.HitByPlayer>, IGet<Input.BossDefeated>
{
[Meta, Id("boss_logic_state_alive")]
public abstract partial record Alive : State, IGet<Input.HitByPlayer>, IGet<Input.BossDefeated>
{
}
}
public Transition On(in Input.HitByPlayer input)
{
var enemy = Get<IBoss>();
enemy.HitAnimation.Play("Hit");
enemy.CurrentHP.OnNext(enemy.CurrentHP.Value - input.Damage);
GD.Print("Current HP: " + enemy.CurrentHP.Value);
Output(new Output.HitByPlayer());
return ToSelf();
}
public Transition On(in Input.HitByPlayer input)
{
var enemy = Get<IBoss>();
enemy.HitAnimation.Play("Hit");
enemy.CurrentHP.OnNext(enemy.CurrentHP.Value - input.Damage);
GD.Print("Current HP: " + enemy.CurrentHP.Value);
Output(new Output.HitByPlayer());
return ToSelf();
}
public Transition On(in Input.BossDefeated input)
{
Output(new Output.Defeated());
return To<Defeated>();
}
public Transition On(in Input.BossDefeated input)
{
Output(new Output.Defeated());
return To<Defeated>();
}
}
}

View File

@@ -1,34 +1,33 @@
using Chickensoft.Introspection;
using Godot;
namespace GameJamDungeon
namespace GameJamDungeon;
public partial class BossLogic
{
public partial class BossLogic
public partial record State
{
public partial record State
[Meta, Id("boss_logic_state_approach_player")]
public partial record ApproachPlayer : Alive, IGet<Input.PhysicsTick>
{
[Meta, Id("boss_logic_state_approach_player")]
public partial record ApproachPlayer : Alive, IGet<Input.PhysicsTick>
public Transition On(in Input.PhysicsTick input)
{
public Transition On(in Input.PhysicsTick input)
{
var gameRepo = Get<IGameRepo>();
var boss = Get<IBoss>();
var delta = (float)input.Delta;
var boss = Get<IBoss>();
var player = Get<IPlayer>();
var delta = (float)input.Delta;
var playerPosition = new Vector3(gameRepo.PlayerGlobalPosition.Value.X, boss.GlobalPosition.Y, gameRepo.PlayerGlobalPosition.Value.Z);
var playerPosition = new Vector3(player.CurrentPosition.X, boss.GlobalPosition.Y, player.CurrentPosition.Z);
if (boss.GlobalPosition.DistanceTo(gameRepo.PlayerGlobalPosition.Value) <= 5.0f)
return To<EngagePlayer>();
if (boss.GlobalPosition.DistanceTo(player.CurrentPosition) <= 5.0f)
return To<EngagePlayer>();
var moveToward = boss.GlobalPosition.MoveToward(playerPosition, (float)delta * 3f);
boss.GlobalPosition = moveToward;
var moveToward = boss.GlobalPosition.MoveToward(playerPosition, (float)delta * 3f);
boss.GlobalPosition = moveToward;
var targetDirection = boss.GlobalPosition - gameRepo.PlayerGlobalPosition.Value;
boss.GlobalRotation = new Vector3(boss.GlobalRotation.X, Mathf.LerpAngle(boss.GlobalRotation.Y, Mathf.Atan2(-targetDirection.X, -targetDirection.Z), delta * 3f), boss.GlobalRotation.Z);
boss.AnimationTree.Get("parameters/playback").As<AnimationNodeStateMachinePlayback>().Travel("Walk");
return ToSelf();
}
var targetDirection = boss.GlobalPosition - player.CurrentPosition;
boss.GlobalRotation = new Vector3(boss.GlobalRotation.X, Mathf.LerpAngle(boss.GlobalRotation.Y, Mathf.Atan2(-targetDirection.X, -targetDirection.Z), delta * 3f), boss.GlobalRotation.Z);
boss.AnimationTree.Get("parameters/playback").As<AnimationNodeStateMachinePlayback>().Travel("Walk");
return ToSelf();
}
}
}

View File

@@ -1,15 +1,14 @@
using Chickensoft.Introspection;
namespace GameJamDungeon
namespace GameJamDungeon;
public partial class BossLogic
{
public partial class BossLogic
public partial record State
{
public partial record State
[Meta, Id("boss_logic_state_defeated")]
public partial record Defeated : State
{
[Meta, Id("boss_logic_state_defeated")]
public partial record Defeated : State
{
}
}
}
}

View File

@@ -1,47 +1,46 @@
using Chickensoft.Introspection;
using Godot;
namespace GameJamDungeon
namespace GameJamDungeon;
public partial class BossLogic
{
public partial class BossLogic
public partial record State
{
public partial record State
[Meta, Id("boss_logic_state_engage_player")]
public partial record EngagePlayer : Alive, IGet<Input.PhysicsTick>, IGet<Input.PrimaryAttack>, IGet<Input.SecondaryAttack>
{
[Meta, Id("boss_logic_state_engage_player")]
public partial record EngagePlayer : Alive, IGet<Input.PhysicsTick>, IGet<Input.PrimaryAttack>, IGet<Input.SecondaryAttack>
public EngagePlayer()
{
public EngagePlayer()
{
OnAttach(() => Get<IBoss>().AnimationTree.Get("parameters/playback").As<AnimationNodeStateMachinePlayback>().Travel("Idle"));
}
OnAttach(() => Get<IBoss>().AnimationTree.Get("parameters/playback").As<AnimationNodeStateMachinePlayback>().Travel("Idle"));
}
public Transition On(in Input.PhysicsTick input)
{
var gameRepo = Get<IGameRepo>();
var boss = Get<IBoss>();
var delta = (float)input.Delta;
var playerPosition = new Vector3(gameRepo.PlayerGlobalPosition.Value.X, boss.GlobalPosition.Y, gameRepo.PlayerGlobalPosition.Value.Z);
var targetDirection = boss.GlobalPosition - gameRepo.PlayerGlobalPosition.Value;
boss.GlobalRotation = new Vector3(boss.GlobalRotation.X, Mathf.LerpAngle(boss.GlobalRotation.Y, Mathf.Atan2(-targetDirection.X, -targetDirection.Z), delta * 3f), boss.GlobalRotation.Z);
if (boss.GlobalPosition.DistanceTo(gameRepo.PlayerGlobalPosition.Value) > 5.0f)
return To<ApproachPlayer>();
public Transition On(in Input.PhysicsTick input)
{
var boss = Get<IBoss>();
var player = Get<IPlayer>();
var delta = (float)input.Delta;
var playerPosition = new Vector3(player.CurrentPosition.X, boss.GlobalPosition.Y, player.CurrentPosition.Z);
var targetDirection = boss.GlobalPosition - player.CurrentPosition;
boss.GlobalRotation = new Vector3(boss.GlobalRotation.X, Mathf.LerpAngle(boss.GlobalRotation.Y, Mathf.Atan2(-targetDirection.X, -targetDirection.Z), delta * 3f), boss.GlobalRotation.Z);
if (boss.GlobalPosition.DistanceTo(player.CurrentPosition) > 5.0f)
return To<ApproachPlayer>();
return ToSelf();
}
return ToSelf();
}
public Transition On(in Input.PrimaryAttack input)
{
var boss = Get<IBoss>();
boss.AnimationTree.Get("parameters/playback").As<AnimationNodeStateMachinePlayback>().Travel("PrimaryAttack");
return ToSelf();
}
public Transition On(in Input.PrimaryAttack input)
{
var boss = Get<IBoss>();
boss.AnimationTree.Get("parameters/playback").As<AnimationNodeStateMachinePlayback>().Travel("PrimaryAttack");
return ToSelf();
}
public Transition On(in Input.SecondaryAttack input)
{
var boss = Get<IBoss>();
boss.AnimationTree.Get("parameters/playback").As<AnimationNodeStateMachinePlayback>().Travel("SecondaryAttack");
return ToSelf();
}
public Transition On(in Input.SecondaryAttack input)
{
var boss = Get<IBoss>();
boss.AnimationTree.Get("parameters/playback").As<AnimationNodeStateMachinePlayback>().Travel("SecondaryAttack");
return ToSelf();
}
}
}

View File

@@ -1,29 +1,28 @@
using Chickensoft.Introspection;
namespace GameJamDungeon
namespace GameJamDungeon;
public partial class BossLogic
{
public partial class BossLogic
public partial record State
{
public partial record State
[Meta, Id("boss_logic_state_idle")]
public partial record Idle : Alive, IGet<Input.Activate>
{
[Meta, Id("boss_logic_state_idle")]
public partial record Idle : Alive, IGet<Input.Activate>
public Idle()
{
public Idle()
OnDetach(() =>
{
OnDetach(() =>
{
var boss = Get<IBoss>();
boss.SetPhysicsProcess(true);
boss.Show();
boss.AttackTimer.Start();
}
);
}
public Transition On(in Input.Activate input)
{
return To<ApproachPlayer>();
var boss = Get<IBoss>();
boss.SetPhysicsProcess(true);
boss.Show();
boss.AttackTimer.Start();
}
);
}
public Transition On(in Input.Activate input)
{
return To<ApproachPlayer>();
}
}
}

View File

@@ -1,11 +1,10 @@
using Chickensoft.AutoInject;
using Chickensoft.Introspection;
using GameJamDungeon;
using Godot;
using System;
using System.Diagnostics;
using System.Linq;
namespace GameJamDungeon;
[Meta(typeof(IAutoNode))]
public partial class DataViewer : Control
{

View File

@@ -2,6 +2,8 @@ using Chickensoft.AutoInject;
using Chickensoft.Introspection;
using Godot;
namespace GameJamDungeon;
[Meta(typeof(IAutoNode))]
public partial class DataViewerRepository : Node
{

View File

@@ -23,6 +23,8 @@ public partial class Enemy : RigidBody3D, IEnemy, IProvide<IEnemyLogic>
[Dependency] IGameRepo GameRepo => this.DependOn<IGameRepo>();
[Dependency] IGameEventDepot GameEventDepot => this.DependOn<IGameEventDepot>();
[Dependency] IPlayer Player => this.DependOn<IPlayer>();
#endregion
#region Exports
@@ -56,15 +58,13 @@ public partial class Enemy : RigidBody3D, IEnemy, IProvide<IEnemyLogic>
_enemyLogic = new EnemyLogic();
_enemyLogic.Set(_enemyStatResource);
_enemyLogic.Set(this as IEnemy);
_enemyLogic.Set(GameRepo);
_enemyLogic.Set(Player);
}
public void TakeDamage(double damage, ElementType elementType, bool isCriticalHit = false, bool ignoreDefense = false, bool ignoreElementalResistance = false)
{
if (_currentHP.Value > 0)
{
ApplyKnockback();
if (!ignoreElementalResistance)
damage = CalculateElementalResistance(damage, elementType);
if (!ignoreDefense)
@@ -93,11 +93,17 @@ public partial class Enemy : RigidBody3D, IEnemy, IProvide<IEnemyLogic>
var lookAtPosition = new Vector3(lookAtDir.X, GlobalPosition.Y, lookAtDir.Z);
if (GlobalPosition.DistanceTo(target) > 1.0f && !velocity.IsEqualApprox(Vector3.Zero) && !Position.IsEqualApprox(lookAtPosition))
LookAt(lookAtPosition + new Vector3(0.001f, 0.001f, 0.001f), Vector3.Up);
EnemyModelView.RotateModel(GlobalTransform.Basis, -GameRepo.PlayerGlobalTransform.Value.Basis.Z);
EnemyModelView.RotateModel(GlobalTransform.Basis, -Player.CurrentBasis.Z);
_knockbackStrength = _knockbackStrength * 0.9f;
MoveAndCollide(velocity + (_knockbackDirection * _knockbackStrength));
}
public void Knockback(float impulse, Vector3 direction)
{
_knockbackDirection = direction;
_knockbackStrength = 0.3f;
}
public void Die()
{
_enemyLogic.Input(new EnemyLogic.Input.EnemyDefeated());
@@ -157,7 +163,7 @@ public partial class Enemy : RigidBody3D, IEnemy, IProvide<IEnemyLogic>
private void OnAttackTimeout()
{
if (GlobalPosition.DistanceTo(GameRepo.PlayerGlobalPosition.Value) > 2.5f)
if (GlobalPosition.DistanceTo(Player.CurrentPosition) > 2.5f)
return;
var rng = new RandomNumberGenerator();
@@ -173,13 +179,13 @@ public partial class Enemy : RigidBody3D, IEnemy, IProvide<IEnemyLogic>
var overlappingBodies = LineOfSight.GetOverlappingBodies();
foreach (var overlap in overlappingBodies)
{
if (Raycast.GlobalPosition != GameRepo.PlayerGlobalPosition.Value)
Raycast.LookAt(GameRepo.PlayerGlobalPosition.Value, Vector3.Up);
if (Raycast.GlobalPosition != Player.CurrentPosition)
Raycast.LookAt(Player.CurrentPosition, Vector3.Up);
Raycast.ForceRaycastUpdate();
if (Raycast.IsColliding())
{
var collider = Raycast.GetCollider();
if (collider is IPlayer player)
if (collider is IPlayer)
{
Raycast.DebugShapeCustomColor = Color.FromString("Purple", Colors.Purple);
_enemyLogic.Input(new EnemyLogic.Input.Alerted());
@@ -188,15 +194,6 @@ public partial class Enemy : RigidBody3D, IEnemy, IProvide<IEnemyLogic>
}
}
private void ApplyKnockback()
{
if (GameRepo.PlayerData.Inventory.EquippedWeapon.Value.WeaponStats.WeaponTags.Contains(WeaponTag.Knockback))
{
_knockbackDirection = -GameRepo.PlayerGlobalTransform.Value.Basis.Z.Normalized();
_knockbackStrength = 0.3f;
}
}
private void OnHPChanged(double newHP)
{
if (newHP <= 0)

View File

@@ -1,5 +1,7 @@
using Godot;
namespace GameJamDungeon;
public partial class EnemyDatabase : Node
{
[Export]

View File

@@ -1,14 +1,13 @@
using Godot;
namespace GameJamDungeon
{
[GlobalClass]
public partial class EnemyLoreInfo : Resource
{
[Export]
public string Name { get; set; }
namespace GameJamDungeon;
[Export]
public string Description { get; set; }
}
[GlobalClass]
public partial class EnemyLoreInfo : Resource
{
[Export]
public string Name { get; set; }
[Export]
public string Description { get; set; }
}

View File

@@ -1,9 +1,10 @@
using Chickensoft.AutoInject;
using Chickensoft.GodotNodeInterfaces;
using Chickensoft.Introspection;
using GameJamDungeon;
using Godot;
namespace GameJamDungeon;
public interface IEnemyModelView : INode3D
{
public void PlayPrimaryAttackAnimation();

View File

@@ -1,50 +1,49 @@
using Godot;
namespace GameJamDungeon
namespace GameJamDungeon;
[GlobalClass]
public partial class EnemyStatResource : Resource
{
[GlobalClass]
public partial class EnemyStatResource : Resource
{
[Export]
public double CurrentHP { get; set; }
[Export]
public double CurrentHP { get; set; }
[Export]
public double MaximumHP { get; set; }
[Export]
public double MaximumHP { get; set; }
[Export]
public int CurrentAttack { get; set; }
[Export]
public int CurrentAttack { get; set; }
[Export]
public int CurrentDefense { get; set; }
[Export]
public int CurrentDefense { get; set; }
[Export]
public int MaxAttack { get; set; }
[Export]
public int MaxAttack { get; set; }
[Export]
public int MaxDefense { get; set; }
[Export]
public int MaxDefense { get; set; }
[Export]
public int ExpFromDefeat { get; set; }
[Export]
public int ExpFromDefeat { get; set; }
[Export]
public double Luck { get; set; } = 0.05f;
[Export]
public double Luck { get; set; } = 0.05f;
[Export]
public double TelluricResistance { get; set; }
[Export]
public double TelluricResistance { get; set; }
[Export]
public double AeolicResistance { get; set; }
[Export]
public double AeolicResistance { get; set; }
[Export]
public double HydricResistance { get; set; }
[Export]
public double HydricResistance { get; set; }
[Export]
public double IgneousResistance { get; set; }
[Export]
public double IgneousResistance { get; set; }
[Export]
public double FerrumResistance { get; set; }
[Export]
public double FerrumResistance { get; set; }
[Export]
public float DropsSoulGemChance { get; set; } = 0.75f;
}
[Export]
public float DropsSoulGemChance { get; set; } = 0.75f;
}

View File

@@ -2,15 +2,15 @@
namespace GameJamDungeon;
public interface IEnemy
public interface IEnemy : IKillable
{
public void TakeAction();
public void TakeDamage(double damage, ElementType elementType, bool isCriticalHit = false, bool ignoreDefense = false, bool ignoreElementalResistance = false);
public void TakeDamage(double damage, ElementType elementType = ElementType.None, bool isCriticalHit = false, bool ignoreDefense = false, bool ignoreElementalResistance = false);
public void Knockback(float impulse, Vector3 direction);
public void MoveToLocation(Vector3 target, float delta);
public void Die();
public double CurrentHP { get; }
}

View File

@@ -1,5 +1,7 @@
using Godot;
namespace GameJamDungeon;
public partial class HorseHead : Node3D
{
}

View File

@@ -1,8 +1,9 @@
using Chickensoft.AutoInject;
using Chickensoft.Introspection;
using GameJamDungeon;
using Godot;
namespace GameJamDungeon;
[Meta(typeof(IAutoNode))]
public partial class OxFace : Node3D
{
@@ -12,6 +13,8 @@ public partial class OxFace : Node3D
[Dependency] public IGameRepo GameRepo => this.DependOn<IGameRepo>();
[Dependency] public IPlayer Player => this.DependOn<IPlayer>();
[Node] public Timer AttackTimer { get; set; } = default!;
private bool isInRange = false;
@@ -36,10 +39,10 @@ public partial class OxFace : Node3D
public override void _PhysicsProcess(double delta)
{
var playerPosition = new Vector3(GameRepo.PlayerGlobalPosition.Value.X, GlobalPosition.Y, GameRepo.PlayerGlobalPosition.Value.Z);
var targetDirection = GlobalPosition - GameRepo.PlayerGlobalPosition.Value;
var playerPosition = new Vector3(Player.CurrentPosition.X, GlobalPosition.Y, Player.CurrentPosition.Z);
var targetDirection = GlobalPosition - Player.CurrentPosition;
GlobalRotation = new Vector3(GlobalRotation.X, Mathf.LerpAngle(GlobalRotation.Y, Mathf.Atan2(-targetDirection.X, -targetDirection.Z), (float)delta * 3f), GlobalRotation.Z);
if (GlobalPosition.DistanceTo(GameRepo.PlayerGlobalPosition.Value) > 5.0f)
if (GlobalPosition.DistanceTo(Player.CurrentPosition) > 5.0f)
{
isInRange = false;
var moveToward = GlobalPosition.MoveToward(playerPosition, (float)delta * 3f);

View File

@@ -8,9 +8,9 @@
script = ExtResource("2_8vcnq")
CurrentHP = 50.0
MaximumHP = 50.0
CurrentAttack = 8
CurrentAttack = 20
CurrentDefense = 5
MaxAttack = 8
MaxAttack = 20
MaxDefense = 5
ExpFromDefeat = 15
Luck = 0.05

View File

@@ -1,23 +1,22 @@

using Godot;
namespace GameJamDungeon
namespace GameJamDungeon;
public partial class EnemyLogic
{
public partial class EnemyLogic
public static class Input
{
public static class Input
{
public readonly record struct Alerted();
public readonly record struct Alerted();
public readonly record struct LostPlayer();
public readonly record struct LostPlayer();
public readonly record struct PhysicsTick(double Delta);
public readonly record struct PhysicsTick(double Delta);
public readonly record struct EnemyDefeated();
public readonly record struct EnemyDefeated();
public readonly record struct PatrolToRandomSpot(Vector3 PatrolTarget);
public readonly record struct PatrolToRandomSpot(Vector3 PatrolTarget);
public readonly record struct AttackTimer;
}
public readonly record struct AttackTimer;
}
}

View File

@@ -1,18 +1,17 @@
using Godot;
namespace GameJamDungeon
namespace GameJamDungeon;
public partial class EnemyLogic
{
public partial class EnemyLogic
public static class Output
{
public static class Output
{
public readonly record struct MoveTowardsPlayer(Vector3 TargetPosition);
public readonly record struct MoveTowardsPlayer(Vector3 TargetPosition);
public readonly record struct MovementComputed(Vector3 LinearVelocity);
public readonly record struct MovementComputed(Vector3 LinearVelocity);
public readonly record struct TakeAction();
public readonly record struct TakeAction();
public readonly record struct Defeated();
}
public readonly record struct Defeated();
}
}

View File

@@ -1,7 +1,6 @@
namespace GameJamDungeon
namespace GameJamDungeon;
public partial class EnemyLogic
{
public partial class EnemyLogic
{
public record Settings(float MaximumHP);
}
public record Settings(float MaximumHP);
}

View File

@@ -1,16 +1,15 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
namespace GameJamDungeon
namespace GameJamDungeon;
public partial class EnemyLogic
{
public partial class EnemyLogic
[Meta]
public abstract partial record State : StateLogic<State>
{
[Meta]
public abstract partial record State : StateLogic<State>
protected State()
{
protected State()
{
}
}
}
}

View File

@@ -1,14 +1,13 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
namespace GameJamDungeon
{
public interface IEnemyLogic : ILogicBlock<EnemyLogic.State>;
namespace GameJamDungeon;
[Meta, Id("enemy_logic")]
[LogicBlock(typeof(State), Diagram = true)]
public partial class EnemyLogic : LogicBlock<EnemyLogic.State>, IEnemyLogic
{
public override Transition GetInitialState() => To<State.Idle>();
}
public interface IEnemyLogic : ILogicBlock<EnemyLogic.State>;
[Meta, Id("enemy_logic")]
[LogicBlock(typeof(State), Diagram = true)]
public partial class EnemyLogic : LogicBlock<EnemyLogic.State>, IEnemyLogic
{
public override Transition GetInitialState() => To<State.Idle>();
}

View File

@@ -1,26 +1,24 @@
using Chickensoft.Introspection;
using Godot;
namespace GameJamDungeon
namespace GameJamDungeon;
public partial class EnemyLogic
{
public partial class EnemyLogic
public partial record State
{
public partial record State
[Meta, Id("enemy_logic_state_alive")]
public abstract partial record Alive : State, IGet<Input.AttackTimer>, IGet<Input.EnemyDefeated>
{
[Meta, Id("enemy_logic_state_alive")]
public abstract partial record Alive : State, IGet<Input.AttackTimer>, IGet<Input.EnemyDefeated>
public Transition On(in Input.AttackTimer input)
{
public Transition On(in Input.AttackTimer input)
{
Output(new Output.TakeAction());
return ToSelf();
}
Output(new Output.TakeAction());
return ToSelf();
}
public Transition On(in Input.EnemyDefeated input)
{
Output(new Output.Defeated());
return To<Defeated>();
}
public Transition On(in Input.EnemyDefeated input)
{
Output(new Output.Defeated());
return To<Defeated>();
}
}
}

View File

@@ -1,29 +1,27 @@
using Chickensoft.Introspection;
using Godot;
namespace GameJamDungeon
namespace GameJamDungeon;
public partial class EnemyLogic
{
public partial class EnemyLogic
public partial record State
{
public partial record State
[Meta, Id("enemy_logic_state_followplayer")]
public partial record FollowPlayer : Alive, IGet<Input.PhysicsTick>, IGet<Input.LostPlayer>
{
[Meta, Id("enemy_logic_state_followplayer")]
public partial record FollowPlayer : Alive, IGet<Input.PhysicsTick>, IGet<Input.LostPlayer>
public Transition On(in Input.PhysicsTick input)
{
public Transition On(in Input.PhysicsTick input)
{
var delta = input.Delta;
var enemy = Get<IEnemy>();
var gameRepo = Get<IGameRepo>();
var target = gameRepo.PlayerGlobalPosition.Value;
enemy.MoveToLocation(target, (float)delta);
return ToSelf();
}
var delta = input.Delta;
var enemy = Get<IEnemy>();
var player = Get<IPlayer>();
var target = player.CurrentPosition;
enemy.MoveToLocation(target, (float)delta);
return ToSelf();
}
public Transition On(in Input.LostPlayer input)
{
return To<Idle>();
}
public Transition On(in Input.LostPlayer input)
{
return To<Idle>();
}
}
}

View File

@@ -1,6 +1,8 @@
using DialogueManagerRuntime;
using Godot;
namespace GameJamDungeon;
public partial class DialogueController : Node
{
public static PackedScene DialogueBalloon { get; set; }

View File

@@ -1,12 +1,11 @@
namespace GameJamDungeon
namespace GameJamDungeon;
public enum ElementType
{
public enum ElementType
{
None,
Aeolic,
Telluric,
Hydric,
Igneous,
Ferrum
}
None,
Aeolic,
Telluric,
Hydric,
Igneous,
Ferrum
}

View File

@@ -3,7 +3,6 @@ namespace GameJamDungeon;
using Chickensoft.AutoInject;
using Chickensoft.Introspection;
using GameJamDungeon.src.item_rescue;
using Godot;
using System;
@@ -16,6 +15,8 @@ public partial class Game : Node3D, IGame
IGame IProvide<IGame>.Value() => this;
IPlayer IProvide<IPlayer>.Value() => Player;
IGameEventDepot IProvide<IGameEventDepot>.Value() => GameEventDepot;
public IInstantiator Instantiator { get; set; } = default!;
@@ -61,6 +62,7 @@ public partial class Game : Node3D, IGame
GameLogic.Set(GameRepo);
GameLogic.Set(AppRepo);
GameLogic.Set(GameEventDepot);
GameLogic.Set(Player);
Instantiator = new Instantiator(GetTree());
RescuedItems = new RescuedItemDatabase();
}
@@ -117,29 +119,27 @@ public partial class Game : Node3D, IGame
InGameUI.UseTeleportPrompt.TeleportToNextFloor += UseTeleportPrompt_TeleportToNextFloor;
InGameUI.UseTeleportPrompt.CloseTeleportPrompt += UseTeleportPrompt_CloseTeleportPrompt;
GameRepo.PlayerData.Inventory.InventoryAtCapacity += PlayerInventory_InventoryAtCapacity;
GameRepo.PlayerData.Inventory.PickedUpItem += Inventory_PickedUpItem;
GameRepo.PlayerData.Inventory.RaiseStatRequest += Inventory_RaiseStatRequest;
Player.Inventory.InventoryAtCapacity += PlayerInventory_InventoryAtCapacity;
Player.Inventory.PickedUpItem += Inventory_PickedUpItem;
Player.Inventory.RaiseStatRequest += Inventory_RaiseStatRequest;
FloorClearMenu.GoToNextFloor += FloorClearMenu_GoToNextFloor;
FloorClearMenu.SaveAndExit += FloorClearMenu_SaveAndExit;
FloorClearMenu.TransitionCompleted += FloorClearMenu_TransitionCompleted;
Player.InventoryButtonPressed += Player_InventoryButtonPressed;
Player.MinimapButtonHeld += Player_MinimapButtonHeld;
Player.PauseButtonPressed += Player_PauseButtonPressed;
GameRepo.PlayerData.CurrentHP.Sync += CurrentHP_Sync;
GameEventDepot.EnemyDefeated += OnEnemyDefeated;
GameEventDepot.RestorativePickedUp += GameEventDepot_RestorativePickedUp;
DoubleEXPTimer.Timeout += DoubleEXPTimer_Timeout;
}
private void CurrentHP_Sync(int currentHP)
public void ToggleInventory()
{
if (currentHP <= 0)
GameLogic.Input(new GameLogic.Input.GameOver());
GameLogic.Input(new GameLogic.Input.OpenInventory());
}
public void ToggleMinimap()
{
GameLogic.Input(new GameLogic.Input.MiniMapButtonPressed());
}
private void Inventory_PickedUpItem(string pickedUpItemName)
@@ -160,14 +160,14 @@ public partial class Game : Node3D, IGame
{
var thrownScene = GD.Load<PackedScene>("res://src/items/thrown/ThrownItem.tscn");
var thrown = thrownScene.Instantiate<ThrownItem>();
thrown.ThrownItemStats = item.Info;
thrown.ItemThatIsThrown = item;
AddChild(thrown);
thrown.Throw();
}
private void OnEnemyDefeated(Vector3 vector, EnemyStatResource resource)
{
GameRepo.PlayerData.SetCurrentExp(GameRepo.PlayerData.CurrentExp.Value + (resource.ExpFromDefeat * GameRepo.EXPRate));
Player.GainExp(resource.ExpFromDefeat * GameRepo.EXPRate);
DropRestorative(vector);
}
@@ -205,21 +205,11 @@ public partial class Game : Node3D, IGame
GameLogic.Input(new GameLogic.Input.MiniMapButtonReleased());
}
private void Player_MinimapButtonHeld()
{
GameLogic.Input(new GameLogic.Input.MiniMapButtonPressed());
}
private void Player_InventoryButtonPressed()
{
GameLogic.Input(new GameLogic.Input.OpenInventory());
}
private void FloorClearMenu_TransitionCompleted()
{
GameRepo.Resume();
if (GameRepo.PlayerData.Inventory.EquippedWeapon.Value.WeaponStats.WeaponTags.Contains(WeaponTag.BreaksOnChange))
GameRepo.PlayerData.Inventory.Unequip(GameRepo.PlayerData.Inventory.EquippedWeapon.Value);
if (Player.Inventory.EquippedWeapon.Value.WeaponTags.Contains(WeaponTag.BreaksOnChange))
Player.Inventory.Unequip(Player.Inventory.EquippedWeapon.Value);
}
private void FloorClearMenu_GoToNextFloor()
@@ -235,7 +225,7 @@ public partial class Game : Node3D, IGame
}
private void GameEventDepot_RestorativePickedUp(Restorative obj)
=> GameRepo.PlayerData.SetCurrentVT(GameRepo.PlayerData.CurrentVT.Value + obj.VTRestoreAmount);
=> Player.Stats.SetCurrentVT(Player.Stats.CurrentVT.Value + obj.VTRestoreAmount);
private void Inventory_RaiseStatRequest(InventoryItemStats itemStats)
{
@@ -251,34 +241,34 @@ public partial class Game : Node3D, IGame
public void RaiseHP(int amountToRaise)
{
if (GameRepo.PlayerData.CurrentHP.Value == GameRepo.PlayerData.MaximumHP.Value)
if (Player.Stats.CurrentHP.Value == Player.Stats.MaximumHP.Value)
{
GameRepo.PlayerData.SetMaximumHP(GameRepo.PlayerData.MaximumHP.Value + amountToRaise);
GameRepo.PlayerData.SetCurrentHP(GameRepo.PlayerData.MaximumHP.Value);
Player.Stats.SetMaximumHP(Player.Stats.MaximumHP.Value + amountToRaise);
Player.Stats.SetCurrentHP(Player.Stats.MaximumHP.Value);
EmitSignal(SignalName.StatRaisedAlert, $"{amountToRaise}MAXHP Up.");
}
}
public void HealHP(int amountToRestore)
{
GameRepo.PlayerData.SetCurrentHP(GameRepo.PlayerData.CurrentHP.Value + amountToRestore);
Player.Stats.SetCurrentHP(Player.Stats.CurrentHP.Value + amountToRestore);
var raiseString = amountToRestore == 1000 ? "MAX" : $"{amountToRestore}";
EmitSignal(SignalName.StatRaisedAlert, $"{raiseString}HP Restored.");
}
public void RaiseVT(int amountToRaise)
{
if (GameRepo.PlayerData.CurrentVT.Value == GameRepo.PlayerData.MaximumVT.Value)
if (Player.Stats.CurrentVT.Value == Player.Stats.MaximumVT.Value)
{
GameRepo.PlayerData.SetMaximumVT(GameRepo.PlayerData.MaximumVT.Value + amountToRaise);
GameRepo.PlayerData.SetCurrentVT(GameRepo.PlayerData.MaximumVT.Value);
Player.Stats.SetMaximumVT(Player.Stats.MaximumVT.Value + amountToRaise);
Player.Stats.SetCurrentVT(Player.Stats.MaximumVT.Value);
EmitSignal(SignalName.StatRaisedAlert, $"{amountToRaise}MAXVT Up.");
}
}
public void HealVT(int amountToRestore)
{
GameRepo.PlayerData.SetCurrentVT(GameRepo.PlayerData.CurrentVT.Value + amountToRestore);
Player.Stats.SetCurrentVT(Player.Stats.CurrentVT.Value + amountToRestore);
var raiseString = amountToRestore == 1000 ? "MAX" : $"{amountToRestore}";
EmitSignal(SignalName.StatRaisedAlert, $"{raiseString}VT Restored.");
}
@@ -309,7 +299,7 @@ public partial class Game : Node3D, IGame
{
var transform = Map.GetPlayerSpawnPosition();
GameRepo.SetPlayerGlobalTransform(transform);
GameRepo.SetPlayerGlobalPosition(new Vector3(transform.Origin.X, -2, transform.Origin.Z));
Player.TeleportPlayer(new Vector3(transform.Origin.X, -1.75f, transform.Origin.Z));
GameLogic.Input(new GameLogic.Input.HideFloorClearMenu());
}

View File

@@ -48,7 +48,7 @@ process_mode = 1
[node name="Player" parent="SubViewportContainer/SubViewport/PauseContainer" instance=ExtResource("3_kk6ly")]
unique_name_in_owner = true
process_mode = 1
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -2.74459, 1.22144)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.283374, 1.22144)
[node name="Map" parent="SubViewportContainer/SubViewport/PauseContainer" instance=ExtResource("3_d8awv")]
unique_name_in_owner = true

View File

@@ -1,12 +1,11 @@
using Chickensoft.Introspection;
using Chickensoft.Serialization;
namespace GameJamDungeon
namespace GameJamDungeon;
[Meta, Id("game_data")]
public partial record GameData
{
[Meta, Id("game_data")]
public partial record GameData
{
[Save("player_data")]
public required PlayerData PlayerData { get; init; }
}
[Save("player_data")]
public required PlayerStats PlayerData { get; init; }
}

View File

@@ -1,114 +1,113 @@
using Godot;
using System;
namespace GameJamDungeon
namespace GameJamDungeon;
public interface IGameEventDepot : IDisposable
{
public interface IGameEventDepot : IDisposable
event Action? OverworldEntered;
public void OnOverworldEntered();
event Action? DungeonAThemeAreaEntered;
public void OnDungeonAThemeAreaEntered();
event Action? DungeonBThemeAreaEntered;
public void OnDungeonBThemeAreaEntered();
event Action? DungeonCThemeAreaEntered;
public void OnDungeonCThemeAreaEntered();
event Action? TeleportEntered;
public void OnTeleportEntered();
event Action? MenuScrolled;
public void OnMenuScrolled();
event Action? MenuBackedOut;
public void OnMenuBackedOut();
event Action<Weapon>? EquippedWeapon;
public void OnEquippedWeapon(Weapon equippedWeapon);
event Action? UnequippedWeapon;
public void OnUnequippedWeapon();
event Action<Armor>? EquippedArmor;
public void OnEquippedArmor(Armor equippedArmor);
event Action? UnequippedArmor;
public void OnUnequippedArmor();
event Action<Accessory>? EquippedAccessory;
public void OnEquippedAccessory(Accessory equippedAccessory);
event Action? UnequippedAccessory;
public void OnUnequippedAccessory();
event Action? InventorySorted;
public void OnInventorySorted();
event Action<ConsumableItemStats>? HealingItemConsumed;
public void OnHealingItemConsumed(ConsumableItemStats item);
event Action<Vector3, EnemyStatResource>? EnemyDefeated;
public void OnEnemyDefeated(Vector3 position, EnemyStatResource enemyStatResource);
event Action<Restorative>? RestorativePickedUp;
public void OnRestorativePickedUp(Restorative restorative);
}
public class GameEventDepot : IGameEventDepot
{
public event Action? OverworldEntered;
public event Action? DungeonAThemeAreaEntered;
public event Action? DungeonBThemeAreaEntered;
public event Action? DungeonCThemeAreaEntered;
public event Action? TeleportEntered;
public event Action? MenuScrolled;
public event Action? MenuBackedOut;
public event Action<Weapon>? EquippedWeapon;
public event Action? UnequippedWeapon;
public event Action<Armor>? EquippedArmor;
public event Action? UnequippedArmor;
public event Action<Accessory>? EquippedAccessory;
public event Action? UnequippedAccessory;
public event Action? InventorySorted;
public event Action<ConsumableItemStats>? HealingItemConsumed;
public event Action<Restorative>? RestorativePickedUp;
public event Action<Vector3, EnemyStatResource>? EnemyDefeated;
public void OnOverworldEntered() => OverworldEntered?.Invoke();
public void OnDungeonAThemeAreaEntered() => DungeonAThemeAreaEntered?.Invoke();
public void OnDungeonBThemeAreaEntered() => DungeonBThemeAreaEntered?.Invoke();
public void OnDungeonCThemeAreaEntered() => DungeonCThemeAreaEntered?.Invoke();
public void OnTeleportEntered() => TeleportEntered?.Invoke();
public void OnMenuScrolled() => MenuScrolled?.Invoke();
public void OnMenuBackedOut() => MenuBackedOut?.Invoke();
public void OnEquippedWeapon(Weapon equippedWeapon) => EquippedWeapon?.Invoke(equippedWeapon);
public void OnUnequippedWeapon() => UnequippedWeapon?.Invoke();
public void OnEquippedArmor(Armor equippedArmor) => EquippedArmor?.Invoke(equippedArmor);
public void OnUnequippedArmor() => UnequippedArmor?.Invoke();
public void OnEquippedAccessory(Accessory equippedAccessory) => EquippedAccessory?.Invoke(equippedAccessory);
public void OnUnequippedAccessory() => UnequippedAccessory?.Invoke();
public void OnInventorySorted() => InventorySorted?.Invoke();
public void OnHealingItemConsumed(ConsumableItemStats item) => HealingItemConsumed?.Invoke(item);
public void OnRestorativePickedUp(Restorative restorative) => RestorativePickedUp?.Invoke(restorative);
public void OnEnemyDefeated(Vector3 position, EnemyStatResource enemyStatResource) => EnemyDefeated?.Invoke(position, enemyStatResource);
public void Dispose()
{
event Action? OverworldEntered;
public void OnOverworldEntered();
event Action? DungeonAThemeAreaEntered;
public void OnDungeonAThemeAreaEntered();
event Action? DungeonBThemeAreaEntered;
public void OnDungeonBThemeAreaEntered();
event Action? DungeonCThemeAreaEntered;
public void OnDungeonCThemeAreaEntered();
event Action? TeleportEntered;
public void OnTeleportEntered();
event Action? MenuScrolled;
public void OnMenuScrolled();
event Action? MenuBackedOut;
public void OnMenuBackedOut();
event Action<Weapon>? EquippedWeapon;
public void OnEquippedWeapon(Weapon equippedWeapon);
event Action? UnequippedWeapon;
public void OnUnequippedWeapon();
event Action<Armor>? EquippedArmor;
public void OnEquippedArmor(Armor equippedArmor);
event Action? UnequippedArmor;
public void OnUnequippedArmor();
event Action<Accessory>? EquippedAccessory;
public void OnEquippedAccessory(Accessory equippedAccessory);
event Action? UnequippedAccessory;
public void OnUnequippedAccessory();
event Action? InventorySorted;
public void OnInventorySorted();
event Action<ConsumableItemStats>? HealingItemConsumed;
public void OnHealingItemConsumed(ConsumableItemStats item);
event Action<Vector3, EnemyStatResource>? EnemyDefeated;
public void OnEnemyDefeated(Vector3 position, EnemyStatResource enemyStatResource);
event Action<Restorative>? RestorativePickedUp;
public void OnRestorativePickedUp(Restorative restorative);
}
public class GameEventDepot : IGameEventDepot
{
public event Action? OverworldEntered;
public event Action? DungeonAThemeAreaEntered;
public event Action? DungeonBThemeAreaEntered;
public event Action? DungeonCThemeAreaEntered;
public event Action? TeleportEntered;
public event Action? MenuScrolled;
public event Action? MenuBackedOut;
public event Action<Weapon>? EquippedWeapon;
public event Action? UnequippedWeapon;
public event Action<Armor>? EquippedArmor;
public event Action? UnequippedArmor;
public event Action<Accessory>? EquippedAccessory;
public event Action? UnequippedAccessory;
public event Action? InventorySorted;
public event Action<ConsumableItemStats>? HealingItemConsumed;
public event Action<Restorative>? RestorativePickedUp;
public event Action<Vector3, EnemyStatResource>? EnemyDefeated;
public void OnOverworldEntered() => OverworldEntered?.Invoke();
public void OnDungeonAThemeAreaEntered() => DungeonAThemeAreaEntered?.Invoke();
public void OnDungeonBThemeAreaEntered() => DungeonBThemeAreaEntered?.Invoke();
public void OnDungeonCThemeAreaEntered() => DungeonCThemeAreaEntered?.Invoke();
public void OnTeleportEntered() => TeleportEntered?.Invoke();
public void OnMenuScrolled() => MenuScrolled?.Invoke();
public void OnMenuBackedOut() => MenuBackedOut?.Invoke();
public void OnEquippedWeapon(Weapon equippedWeapon) => EquippedWeapon?.Invoke(equippedWeapon);
public void OnUnequippedWeapon() => UnequippedWeapon?.Invoke();
public void OnEquippedArmor(Armor equippedArmor) => EquippedArmor?.Invoke(equippedArmor);
public void OnUnequippedArmor() => UnequippedArmor?.Invoke();
public void OnEquippedAccessory(Accessory equippedAccessory) => EquippedAccessory?.Invoke(equippedAccessory);
public void OnUnequippedAccessory() => UnequippedAccessory?.Invoke();
public void OnInventorySorted() => InventorySorted?.Invoke();
public void OnHealingItemConsumed(ConsumableItemStats item) => HealingItemConsumed?.Invoke(item);
public void OnRestorativePickedUp(Restorative restorative) => RestorativePickedUp?.Invoke(restorative);
public void OnEnemyDefeated(Vector3 position, EnemyStatResource enemyStatResource) => EnemyDefeated?.Invoke(position, enemyStatResource);
public void Dispose()
{
GC.SuppressFinalize(this);
}
GC.SuppressFinalize(this);
}
}

View File

@@ -1,41 +1,40 @@
namespace GameJamDungeon
namespace GameJamDungeon;
public partial class GameLogic
{
public partial class GameLogic
public static class Input
{
public static class Input
{
public readonly record struct StartGame;
public readonly record struct StartGame;
public readonly record struct Initialize;
public readonly record struct Initialize;
public readonly record struct GoToOverworld;
public readonly record struct GoToOverworld;
public readonly record struct SaveGame;
public readonly record struct SaveGame;
public readonly record struct OpenInventory;
public readonly record struct OpenInventory;
public readonly record struct CloseInventory;
public readonly record struct CloseInventory;
public readonly record struct MiniMapButtonPressed;
public readonly record struct MiniMapButtonPressed;
public readonly record struct MiniMapButtonReleased;
public readonly record struct MiniMapButtonReleased;
public readonly record struct FloorExitReached;
public readonly record struct HideFloorClearMenu;
public readonly record struct FloorExitReached;
public readonly record struct HideFloorClearMenu;
public readonly record struct GameOver;
public readonly record struct GameOver;
public readonly record struct GoToNextFloor;
public readonly record struct GoToNextFloor;
public readonly record struct PauseGame;
public readonly record struct PauseGame;
public readonly record struct UnpauseGame;
public readonly record struct UnpauseGame;
public readonly record struct PauseMenuTransitioned;
public readonly record struct PauseMenuTransitioned;
public readonly record struct AskForTeleport;
public readonly record struct AskForTeleport;
public readonly record struct HideAskForTeleport;
}
public readonly record struct HideAskForTeleport;
}
}

View File

@@ -1,44 +1,41 @@
using System.Collections.Generic;
namespace GameJamDungeon;
namespace GameJamDungeon
public partial class GameLogic
{
public partial class GameLogic
public static class Output
{
public static class Output
{
public readonly record struct StartGame;
public readonly record struct StartGame;
public readonly record struct ShowPauseMenu;
public readonly record struct ShowPauseMenu;
public readonly record struct HidePauseMenu;
public readonly record struct HidePauseMenu;
public readonly record struct ExitPauseMenu;
public readonly record struct ExitPauseMenu;
public readonly record struct OpenInventory();
public readonly record struct OpenInventory();
public readonly record struct HideInventory;
public readonly record struct HideInventory;
public readonly record struct SetPauseMode(bool IsPaused);
public readonly record struct SetPauseMode(bool IsPaused);
public readonly record struct ShowMiniMap;
public readonly record struct ShowMiniMap;
public readonly record struct HideMiniMap;
public readonly record struct HideMiniMap;
public readonly record struct ShowLostScreen;
public readonly record struct ShowLostScreen;
public readonly record struct ExitLostScreen;
public readonly record struct ExitLostScreen;
public readonly record struct LoadNextFloor;
public readonly record struct LoadNextFloor;
public readonly record struct ShowFloorClearMenu;
public readonly record struct ShowFloorClearMenu;
public readonly record struct ExitFloorClearMenu;
public readonly record struct ExitFloorClearMenu;
public readonly record struct ShowAskForTeleport;
public readonly record struct ShowAskForTeleport;
public readonly record struct HideAskForTeleport;
public readonly record struct HideAskForTeleport;
public readonly record struct GoToOverworld;
}
public readonly record struct GoToOverworld;
}
}

View File

@@ -1,28 +1,27 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
namespace GameJamDungeon
{
public partial class GameLogic
{
[Meta]
public abstract partial record State : StateLogic<State>
{
protected State()
{
OnAttach(() =>
{
var gameRepo = Get<IGameRepo>();
gameRepo.IsPaused.Sync += OnIsPaused;
});
OnDetach(() =>
{
var gameRepo = Get<IGameRepo>();
gameRepo.IsPaused.Sync -= OnIsPaused;
});
}
namespace GameJamDungeon;
public void OnIsPaused(bool isPaused) => Output(new Output.SetPauseMode(isPaused));
public partial class GameLogic
{
[Meta]
public abstract partial record State : StateLogic<State>
{
protected State()
{
OnAttach(() =>
{
var gameRepo = Get<IGameRepo>();
gameRepo.IsPaused.Sync += OnIsPaused;
});
OnDetach(() =>
{
var gameRepo = Get<IGameRepo>();
gameRepo.IsPaused.Sync -= OnIsPaused;
});
}
public void OnIsPaused(bool isPaused) => Output(new Output.SetPauseMode(isPaused));
}
}

View File

@@ -1,14 +1,13 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
namespace GameJamDungeon
{
public interface IGameLogic : ILogicBlock<GameLogic.State>;
namespace GameJamDungeon;
[Meta]
[LogicBlock(typeof(State), Diagram = true)]
public partial class GameLogic : LogicBlock<GameLogic.State>, IGameLogic
{
public override Transition GetInitialState() => To<State.GameStarted>();
}
public interface IGameLogic : ILogicBlock<GameLogic.State>;
[Meta]
[LogicBlock(typeof(State), Diagram = true)]
public partial class GameLogic : LogicBlock<GameLogic.State>, IGameLogic
{
public override Transition GetInitialState() => To<State.GameStarted>();
}

View File

@@ -12,16 +12,6 @@ public interface IGameRepo : IDisposable
IAutoProp<bool> IsPaused { get; }
IAutoProp<Vector3> PlayerGlobalPosition { get; }
IAutoProp<Transform3D> PlayerGlobalTransform { get; }
PlayerData PlayerData { get; }
public void SetPlayerData(PlayerData playerData);
void SetPlayerGlobalPosition(Vector3 playerGlobalPosition);
void SetPlayerGlobalTransform(Transform3D playerGlobalTransform);
public int MaxItemSize { get; }
@@ -35,18 +25,12 @@ public class GameRepo : IGameRepo
{
public event Action? Ended;
public IAutoProp<Vector3> PlayerGlobalPosition => _playerGlobalPosition;
private readonly AutoProp<Vector3> _playerGlobalPosition;
public IAutoProp<Transform3D> PlayerGlobalTransform => _playerGlobalTransform;
private readonly AutoProp<Transform3D> _playerGlobalTransform;
public IAutoProp<bool> IsPaused => _isPaused;
private readonly AutoProp<bool> _isPaused;
public PlayerData PlayerData => _playerData;
private PlayerData _playerData;
public int MaxItemSize => 20;
public int EXPRate { get; set; } = 1;
@@ -58,7 +42,6 @@ public class GameRepo : IGameRepo
public GameRepo()
{
_isPaused = new AutoProp<bool>(false);
_playerGlobalPosition = new AutoProp<Vector3>(Vector3.Zero);
_playerGlobalTransform = new AutoProp<Transform3D>(Transform3D.Identity);
}
@@ -74,12 +57,8 @@ public class GameRepo : IGameRepo
GD.Print("Resume");
}
public void SetPlayerGlobalPosition(Vector3 playerGlobalPosition) => _playerGlobalPosition.OnNext(playerGlobalPosition);
public void SetPlayerGlobalTransform(Transform3D playerGlobalTransform) => _playerGlobalTransform.OnNext(playerGlobalTransform);
public void SetPlayerData(PlayerData playerData) => _playerData = playerData;
public void OnGameEnded()
{
Pause();
@@ -92,8 +71,6 @@ public class GameRepo : IGameRepo
{
if (disposing)
{
_playerGlobalPosition.OnCompleted();
_playerGlobalPosition.Dispose();
_isPaused.OnCompleted();
_isPaused.Dispose();
}

View File

@@ -3,10 +3,9 @@ namespace GameJamDungeon;
using Chickensoft.AutoInject;
using Chickensoft.GodotNodeInterfaces;
using GameJamDungeon.src.item_rescue;
using System;
public interface IGame : IProvide<IGameRepo>, IProvide<IGameEventDepot>, IProvide<IGame>, INode3D
public interface IGame : IProvide<IGameRepo>, IProvide<IGameEventDepot>, IProvide<IGame>, IProvide<IPlayer>, INode3D
{
event Game.StatRaisedAlertEventHandler StatRaisedAlert;
@@ -25,4 +24,8 @@ public interface IGame : IProvide<IGameRepo>, IProvide<IGameEventDepot>, IProvid
public void RaiseVT(int amountToRaise);
public void DoubleEXP(TimeSpan lengthOfEffect);
public void ToggleInventory();
public void ToggleMinimap();
}

View File

@@ -1,27 +1,25 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
namespace GameJamDungeon
namespace GameJamDungeon;
public partial class GameLogic
{
public partial class GameLogic
public partial record State
{
public partial record State
[Meta]
public partial record AskForTeleport : Playing, IGet<Input.FloorExitReached>, IGet<Input.HideAskForTeleport>
{
[Meta]
public partial record AskForTeleport : Playing, IGet<Input.FloorExitReached>, IGet<Input.HideAskForTeleport>
public AskForTeleport()
{
public AskForTeleport()
{
this.OnAttach(() => { Get<IGameRepo>().Pause(); Output(new Output.ShowAskForTeleport()); });
this.OnDetach(() => { Output(new Output.HideAskForTeleport()); });
}
this.OnAttach(() => { Get<IGameRepo>().Pause(); Output(new Output.ShowAskForTeleport()); });
this.OnDetach(() => { Output(new Output.HideAskForTeleport()); });
}
public Transition On(in Input.FloorExitReached input) => To<FloorClearedDecisionState>();
public Transition On(in Input.FloorExitReached input) => To<FloorClearedDecisionState>();
public Transition On(in Input.HideAskForTeleport input)
{
return To<Playing>();
}
public Transition On(in Input.HideAskForTeleport input)
{
return To<Playing>();
}
}
}

View File

@@ -1,31 +1,29 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
namespace GameJamDungeon
namespace GameJamDungeon;
public partial class GameLogic
{
public partial class GameLogic
public partial record State
{
public partial record State
[Meta]
public partial record FloorClearedDecisionState : Playing, IGet<Input.GoToNextFloor>, IGet<Input.HideFloorClearMenu>
{
[Meta]
public partial record FloorClearedDecisionState : Playing, IGet<Input.GoToNextFloor>, IGet<Input.HideFloorClearMenu>
public FloorClearedDecisionState()
{
public FloorClearedDecisionState()
{
this.OnAttach(() => { Get<IGameRepo>().Pause(); Output(new Output.ShowFloorClearMenu()); });
this.OnDetach(() => { Output(new Output.ExitFloorClearMenu()); });
}
this.OnAttach(() => { Get<IGameRepo>().Pause(); Output(new Output.ShowFloorClearMenu()); });
this.OnDetach(() => { Output(new Output.ExitFloorClearMenu()); });
}
public Transition On(in Input.GoToNextFloor input)
{
Output(new Output.LoadNextFloor());
return ToSelf();
}
public Transition On(in Input.GoToNextFloor input)
{
Output(new Output.LoadNextFloor());
return ToSelf();
}
public Transition On(in Input.HideFloorClearMenu input)
{
return To<Playing>();
}
public Transition On(in Input.HideFloorClearMenu input)
{
return To<Playing>();
}
}
}

View File

@@ -1,26 +1,25 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
namespace GameJamDungeon
{
public partial class GameLogic
{
public partial record State
{
[Meta]
public partial record GameStarted : State, IGet<Input.Initialize>
{
public GameStarted()
{
this.OnEnter(() => { Get<IGameRepo>().Pause(); });
this.OnExit(() => { Get<IGameRepo>().Resume(); });
}
namespace GameJamDungeon;
public Transition On(in Input.Initialize input)
{
Output(new Output.StartGame());
return To<Playing>();
}
public partial class GameLogic
{
public partial record State
{
[Meta]
public partial record GameStarted : State, IGet<Input.Initialize>
{
public GameStarted()
{
this.OnEnter(() => { Get<IGameRepo>().Pause(); });
this.OnExit(() => { Get<IGameRepo>().Resume(); });
}
public Transition On(in Input.Initialize input)
{
Output(new Output.StartGame());
return To<Playing>();
}
}
}

View File

@@ -1,25 +1,24 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
namespace GameJamDungeon
{
public partial class GameLogic
{
public partial record State
{
[Meta]
public partial record InventoryOpened : Playing, IGet<Input.CloseInventory>
{
public InventoryOpened()
{
this.OnEnter(() => { Get<IGameRepo>().Pause(); Output(new Output.OpenInventory()); });
this.OnExit(() => { Get<IGameRepo>().Resume(); Output(new Output.HideInventory()); });
}
namespace GameJamDungeon;
public Transition On(in Input.CloseInventory input)
{
return To<Playing>();
}
public partial class GameLogic
{
public partial record State
{
[Meta]
public partial record InventoryOpened : Playing, IGet<Input.CloseInventory>
{
public InventoryOpened()
{
this.OnEnter(() => { Get<IGameRepo>().Pause(); Output(new Output.OpenInventory()); });
this.OnExit(() => { Get<IGameRepo>().Resume(); Output(new Output.HideInventory()); });
}
public Transition On(in Input.CloseInventory input)
{
return To<Playing>();
}
}
}

View File

@@ -1,23 +1,22 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
namespace GameJamDungeon
{
public partial class GameLogic
{
public partial record State
{
[Meta]
public partial record MinimapOpen : Playing, IGet<Input.MiniMapButtonReleased>
{
public MinimapOpen()
{
this.OnEnter(() => { Get<IGameRepo>().Pause(); Output(new Output.ShowMiniMap()); });
this.OnExit(() => { Get<IGameRepo>().Resume(); Output(new Output.HideMiniMap()); });
}
namespace GameJamDungeon;
public Transition On(in Input.MiniMapButtonReleased input) => To<Playing>();
public partial class GameLogic
{
public partial record State
{
[Meta]
public partial record MinimapOpen : Playing, IGet<Input.MiniMapButtonReleased>
{
public MinimapOpen()
{
this.OnEnter(() => { Get<IGameRepo>().Pause(); Output(new Output.ShowMiniMap()); });
this.OnExit(() => { Get<IGameRepo>().Resume(); Output(new Output.HideMiniMap()); });
}
public Transition On(in Input.MiniMapButtonReleased input) => To<Playing>();
}
}
}

View File

@@ -1,27 +1,26 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
namespace GameJamDungeon
{
public partial class GameLogic
{
public partial record State
{
[Meta]
public partial record Paused : Playing, IGet<Input.UnpauseGame>
{
public Paused()
{
this.OnEnter(() =>
{
Get<IGameRepo>().Pause();
Output(new Output.ShowPauseMenu());
});
this.OnExit(() => Output(new Output.ExitPauseMenu()));
}
namespace GameJamDungeon;
public virtual Transition On(in Input.UnpauseGame input) => To<Resuming>();
public partial class GameLogic
{
public partial record State
{
[Meta]
public partial record Paused : Playing, IGet<Input.UnpauseGame>
{
public Paused()
{
this.OnEnter(() =>
{
Get<IGameRepo>().Pause();
Output(new Output.ShowPauseMenu());
});
this.OnExit(() => Output(new Output.ExitPauseMenu()));
}
public virtual Transition On(in Input.UnpauseGame input) => To<Resuming>();
}
}
}

View File

@@ -1,42 +1,40 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
namespace GameJamDungeon
namespace GameJamDungeon;
public partial class GameLogic
{
public partial class GameLogic
public partial record State
{
public partial record State
[Meta]
public partial record Playing : State,
IGet<Input.OpenInventory>,
IGet<Input.MiniMapButtonPressed>,
IGet<Input.GameOver>,
IGet<Input.AskForTeleport>,
IGet<Input.PauseGame>,
IGet<Input.GoToOverworld>
{
[Meta]
public partial record Playing : State,
IGet<Input.OpenInventory>,
IGet<Input.MiniMapButtonPressed>,
IGet<Input.GameOver>,
IGet<Input.AskForTeleport>,
IGet<Input.PauseGame>,
IGet<Input.GoToOverworld>
public Playing()
{
public Playing()
{
}
}
public void OnEnded() => Input(new Input.GameOver());
public void OnEnded() => Input(new Input.GameOver());
public Transition On(in Input.OpenInventory input) => To<InventoryOpened>();
public Transition On(in Input.OpenInventory input) => To<InventoryOpened>();
public Transition On(in Input.MiniMapButtonPressed input) => To<MinimapOpen>();
public Transition On(in Input.MiniMapButtonPressed input) => To<MinimapOpen>();
public Transition On(in Input.GameOver input) => To<Quit>();
public Transition On(in Input.GameOver input) => To<Quit>();
public Transition On(in Input.AskForTeleport input) => To<AskForTeleport>();
public Transition On(in Input.AskForTeleport input) => To<AskForTeleport>();
public Transition On(in Input.PauseGame input) => To<Paused>();
public Transition On(in Input.PauseGame input) => To<Paused>();
public Transition On(in Input.GoToOverworld input)
{
Output(new Output.GoToOverworld());
return ToSelf();
}
public Transition On(in Input.GoToOverworld input)
{
Output(new Output.GoToOverworld());
return ToSelf();
}
}
}

View File

@@ -1,19 +1,18 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
namespace GameJamDungeon
namespace GameJamDungeon;
public partial class GameLogic
{
public partial class GameLogic
public partial record State
{
public partial record State
[Meta]
public partial record Quit : State
{
[Meta]
public partial record Quit : State
public Quit()
{
public Quit()
{
this.OnEnter(() => Output(new Output.ShowLostScreen()));
}
this.OnEnter(() => Output(new Output.ShowLostScreen()));
}
}
}

View File

@@ -1,6 +1,7 @@
using Chickensoft.GodotNodeInterfaces;
using Godot;
namespace GameJamDungeon;
public interface IHitbox : IArea3D
{
}

View File

@@ -1,11 +1,12 @@
using Chickensoft.AutoInject;
using Chickensoft.GodotNodeInterfaces;
using Chickensoft.Introspection;
using GameJamDungeon;
using Godot;
using System.Linq;
using System.Threading.Tasks;
namespace GameJamDungeon;
public interface IInventoryMenu : IControl
{
public Task RedrawInventory();
@@ -18,14 +19,13 @@ public partial class InventoryMenu : Control, IInventoryMenu
{
public override void _Notification(int what) => this.Notify(what);
[Dependency]
public IGameRepo GameRepo => this.DependOn<IGameRepo>();
[Dependency] public IGameRepo GameRepo => this.DependOn<IGameRepo>();
[Dependency]
public IGame Game => this.DependOn<IGame>();
[Dependency] public IGame Game => this.DependOn<IGame>();
[Dependency]
public IGameEventDepot GameEventDepot => this.DependOn<IGameEventDepot>();
[Dependency] public IPlayer Player => this.DependOn<IPlayer>();
[Dependency] public IGameEventDepot GameEventDepot => this.DependOn<IGameEventDepot>();
[Signal]
public delegate void ClosedMenuEventHandler();
@@ -78,20 +78,20 @@ public partial class InventoryMenu : Control, IInventoryMenu
public void OnResolved()
{
GameRepo.PlayerData.CurrentHP.Sync += CurrentHP_Sync;
GameRepo.PlayerData.MaximumHP.Sync += MaximumHP_Sync;
GameRepo.PlayerData.CurrentVT.Sync += CurrentVT_Sync;
GameRepo.PlayerData.MaximumVT.Sync += MaximumVT_Sync;
GameRepo.PlayerData.CurrentAttack.Sync += CurrentAttack_Sync;
GameRepo.PlayerData.MaxAttack.Sync += MaxAttack_Sync;
GameRepo.PlayerData.CurrentDefense.Sync += CurrentDefense_Sync;
GameRepo.PlayerData.MaxDefense.Sync += MaxDefense_Sync;
GameRepo.PlayerData.CurrentExp.Sync += CurrentExp_Sync;
GameRepo.PlayerData.ExpToNextLevel.Sync += ExpToNextLevel_Sync;
GameRepo.PlayerData.CurrentLevel.Sync += CurrentLevel_Sync;
GameRepo.PlayerData.Inventory.EquippedWeapon.Sync += EquippedWeapon_Sync;
GameRepo.PlayerData.Inventory.EquippedArmor.Sync += EquippedArmor_Sync;
GameRepo.PlayerData.Inventory.EquippedAccessory.Sync += EquippedAccessory_Sync;
Player.Stats.CurrentHP.Sync += CurrentHP_Sync;
Player.Stats.MaximumHP.Sync += MaximumHP_Sync;
Player.Stats.CurrentVT.Sync += CurrentVT_Sync;
Player.Stats.MaximumVT.Sync += MaximumVT_Sync;
Player.Stats.CurrentAttack.Sync += CurrentAttack_Sync;
Player.Stats.MaxAttack.Sync += MaxAttack_Sync;
Player.Stats.CurrentDefense.Sync += CurrentDefense_Sync;
Player.Stats.MaxDefense.Sync += MaxDefense_Sync;
Player.Stats.CurrentExp.Sync += CurrentExp_Sync;
Player.Stats.ExpToNextLevel.Sync += ExpToNextLevel_Sync;
Player.Stats.CurrentLevel.Sync += CurrentLevel_Sync;
Player.Inventory.EquippedWeapon.Sync += EquippedWeapon_Sync;
Player.Inventory.EquippedArmor.Sync += EquippedArmor_Sync;
Player.Inventory.EquippedAccessory.Sync += EquippedAccessory_Sync;
Game.StatRaisedAlert += Game_StatRaisedAlert;
SetProcessInput(false);
@@ -110,44 +110,44 @@ public partial class InventoryMenu : Control, IInventoryMenu
private void EquippedAccessory_Sync(Accessory obj)
{
ATKBonusLabel.Text = $"{GameRepo.PlayerData.BonusAttack:+0;-#;\\.\\.\\.}";
DEFBonusLabel.Text = $"{GameRepo.PlayerData.BonusDefense:+0;-#;\\.\\.\\.}";
ATKBonusLabel.Text = $"{Player.Stats.BonusAttack.Value:+0;-#;\\.\\.\\.}";
DEFBonusLabel.Text = $"{Player.Stats.BonusDefense.Value:+0;-#;\\.\\.\\.}";
}
private void EquippedArmor_Sync(Armor obj)
{
ATKBonusLabel.Text = $"{GameRepo.PlayerData.BonusAttack:+0;-#;\\.\\.\\.}";
DEFBonusLabel.Text = $"{GameRepo.PlayerData.BonusDefense:+0;-#;\\.\\.\\.}";
ATKBonusLabel.Text = $"{Player.Stats.BonusAttack.Value:+0;-#;\\.\\.\\.}";
DEFBonusLabel.Text = $"{Player.Stats.BonusDefense.Value:+0;-#;\\.\\.\\.}";
}
private void EquippedWeapon_Sync(Weapon obj)
{
ATKBonusLabel.Text = $"{GameRepo.PlayerData.BonusAttack:+0;-#;\\.\\.\\.}";
DEFBonusLabel.Text = $"{GameRepo.PlayerData.BonusDefense:+0;-#;\\.\\.\\.}";
ATKBonusLabel.Text = $"{Player.Stats.BonusAttack.Value:+0;-#;\\.\\.\\.}";
DEFBonusLabel.Text = $"{Player.Stats.BonusDefense.Value:+0;-#;\\.\\.\\.}";
}
private void CurrentLevel_Sync(int obj) => CurrentLevelLabel.Text = $"Level {obj:D2}";
private void ExpToNextLevel_Sync(int obj) => EXPValue.Text = $"{GameRepo.PlayerData.CurrentExp.Value}/{obj}";
private void ExpToNextLevel_Sync(int obj) => EXPValue.Text = $"{Player.Stats.CurrentExp.Value}/{obj}";
// TODO: Change font style when EXP Bonus effect is active
private void CurrentExp_Sync(int obj) => EXPValue.Text = $"{obj}/{GameRepo.PlayerData.ExpToNextLevel.Value}";
private void CurrentExp_Sync(int obj) => EXPValue.Text = $"{obj}/{Player.Stats.ExpToNextLevel.Value}";
private void MaxDefense_Sync(int obj) => DEFValue.Text = $"{GameRepo.PlayerData.CurrentDefense.Value}/{obj}";
private void MaxDefense_Sync(int obj) => DEFValue.Text = $"{Player.Stats.CurrentDefense.Value}/{obj}";
private void CurrentDefense_Sync(int obj) => DEFValue.Text = $"{obj}/{GameRepo.PlayerData.MaxDefense.Value}";
private void CurrentDefense_Sync(int obj) => DEFValue.Text = $"{obj}/{Player.Stats.MaxDefense.Value}";
private void MaxAttack_Sync(int obj) => ATKValue.Text = $"{GameRepo.PlayerData.CurrentAttack.Value}/{obj}";
private void MaxAttack_Sync(int obj) => ATKValue.Text = $"{Player.Stats.CurrentAttack.Value}/{obj}";
private void CurrentAttack_Sync(int obj) => ATKValue.Text = $"{obj}/{GameRepo.PlayerData.MaxAttack.Value}";
private void CurrentAttack_Sync(int obj) => ATKValue.Text = $"{obj}/{Player.Stats.MaxAttack.Value}";
private void MaximumVT_Sync(int obj) => VTValue.Text = $"{GameRepo.PlayerData.CurrentVT.Value}/{obj}";
private void MaximumVT_Sync(int obj) => VTValue.Text = $"{Player.Stats.CurrentVT.Value}/{obj}";
private void CurrentVT_Sync(int obj) => VTValue.Text = $"{obj}/{GameRepo.PlayerData.MaximumVT.Value}";
private void CurrentVT_Sync(int obj) => VTValue.Text = $"{obj}/{Player.Stats.MaximumVT.Value}";
private void MaximumHP_Sync(int obj) => HPValue.Text = $"{GameRepo.PlayerData.CurrentHP.Value}/{obj}";
private void MaximumHP_Sync(int obj) => HPValue.Text = $"{Player.Stats.CurrentHP.Value}/{obj}";
private void CurrentHP_Sync(int obj) => HPValue.Text = $"{obj}/{GameRepo.PlayerData.MaximumHP.Value}";
private void CurrentHP_Sync(int obj) => HPValue.Text = $"{obj}/{Player.Stats.MaximumHP.Value}";
public async Task RedrawInventory()
{
@@ -161,7 +161,7 @@ public partial class InventoryMenu : Control, IInventoryMenu
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
public override void _Input(InputEvent @event)
{
var inventory = GameRepo.PlayerData.Inventory;
var inventory = Player.Inventory;
if (@event.IsActionPressed(GameInputs.UiCancel))
{
@@ -249,8 +249,8 @@ public partial class InventoryMenu : Control, IInventoryMenu
if (ItemSlots.Any())
{
var item = ItemSlots.ElementAt(_currentIndex).Item;
ItemDescriptionTitle.Text = $"{item.Info.Name}";
ItemEffectLabel.Text = $"{item.Info.Description}";
ItemDescriptionTitle.Text = $"{item.ItemName}";
ItemEffectLabel.Text = $"{item.Description}";
}
}
@@ -267,7 +267,7 @@ public partial class InventoryMenu : Control, IInventoryMenu
if (currentItem is IEquipableItem equipable)
{
var isEquipped = GameRepo.PlayerData.Inventory.IsEquipped(equipable);
var isEquipped = Player.Inventory.IsEquipped(equipable);
UseButton.Text = isEquipped ? "Unequip" : "Equip";
ThrowButton.Disabled = isEquipped ? true : false;
ThrowButton.FocusMode = isEquipped ? FocusModeEnum.None : FocusModeEnum.All;
@@ -311,7 +311,7 @@ public partial class InventoryMenu : Control, IInventoryMenu
private async void PopulateInventory()
{
var inventory = GameRepo.PlayerData.Inventory;
var inventory = Player.Inventory;
var numberOfItemsToDisplay = _currentPageNumber == InventoryPageNumber.FirstPage ? Mathf.Min(inventory.Items.Count, _itemsPerPage) : Mathf.Min(inventory.Items.Count - _itemsPerPage, _itemsPerPage);
var indexToStart = _currentPageNumber == InventoryPageNumber.FirstPage ? 0 : _itemsPerPage;
@@ -338,14 +338,14 @@ public partial class InventoryMenu : Control, IInventoryMenu
itemSlot.Item = item;
ItemsPage.AddChildEx(itemSlot);
if (itemSlot.Item is IEquipableItem equipable && GameRepo.PlayerData.Inventory.IsEquipped(equipable))
if (itemSlot.Item is IEquipableItem equipable && Player.Inventory.IsEquipped(equipable))
itemSlot.SetEquippedItemStyle();
}
if (ItemSlots.Any())
{
ItemSlots.ElementAt(_currentIndex).SetSelectedItemStyle();
if (ItemSlots.ElementAt(_currentIndex).Item is IEquipableItem equipable && GameRepo.PlayerData.Inventory.IsEquipped(equipable))
if (ItemSlots.ElementAt(_currentIndex).Item is IEquipableItem equipable && Player.Inventory.IsEquipped(equipable))
ItemSlots.ElementAt(_currentIndex).SetEquippedSelectedItemStyle();
}
}
@@ -354,7 +354,7 @@ public partial class InventoryMenu : Control, IInventoryMenu
{
await ToSignal(GetTree().CreateTimer(0.1f), "timeout");
itemSlot.SetItemStyle();
if (itemSlot.Item is IEquipableItem equipable && GameRepo.PlayerData.Inventory.IsEquipped(equipable))
if (itemSlot.Item is IEquipableItem equipable && Player.Inventory.IsEquipped(equipable))
itemSlot.SetEquippedItemStyle();
}
@@ -362,8 +362,8 @@ public partial class InventoryMenu : Control, IInventoryMenu
{
await ToSignal(GetTree().CreateTimer(0.1f), "timeout");
itemSlot.SetSelectedItemStyle();
ItemDescriptionTitle.Text = $"{itemSlot.Item.Info.Name}";
ItemEffectLabel.Text = $"{itemSlot.Item.Info.Description}";
ItemDescriptionTitle.Text = $"{itemSlot.Item.ItemName}";
ItemEffectLabel.Text = $"{itemSlot.Item.Description}";
}
private async Task EquipOrUnequipItem()
@@ -371,16 +371,16 @@ public partial class InventoryMenu : Control, IInventoryMenu
var itemSlot = ItemSlots[_currentIndex];
if (itemSlot.Item is IEquipableItem equipableItem)
{
if (GameRepo.PlayerData.Inventory.IsEquipped(equipableItem))
if (Player.Inventory.IsEquipped(equipableItem))
{
ItemEffectLabel.Text = $"{itemSlot.Item.GetType()} unequipped.";
GameRepo.PlayerData.Inventory.Unequip(equipableItem);
Player.Inventory.Unequip(equipableItem);
itemSlot.SetSelectedItemStyle();
}
else
{
ItemEffectLabel.Text = $"{itemSlot.Item.GetType()} equipped.";
GameRepo.PlayerData.Inventory.Equip(equipableItem);
Player.Inventory.Equip(equipableItem);
itemSlot.SetEquippedSelectedItemStyle();
}
@@ -404,7 +404,7 @@ public partial class InventoryMenu : Control, IInventoryMenu
private async void DestroyItem(IUsableItem usableItem)
{
GameRepo.PlayerData.Inventory.Remove(usableItem);
Player.Inventory.Remove(usableItem);
if (_currentIndex >= ItemSlots.Length - 1)
_currentIndex--;
if (_currentIndex <= 0)
@@ -416,7 +416,7 @@ public partial class InventoryMenu : Control, IInventoryMenu
var currentItem = ItemSlots[_currentIndex].Item;
Game.ThrowItem(currentItem);
GameRepo.PlayerData.Inventory.Remove(currentItem);
Player.Inventory.Remove(currentItem);
if (_currentIndex >= ItemSlots.Length - 1)
_currentIndex--;
@@ -430,7 +430,7 @@ public partial class InventoryMenu : Control, IInventoryMenu
{
var currentItem = ItemSlots[_currentIndex].Item;
Game.DropItem(currentItem);
GameRepo.PlayerData.Inventory.Remove(currentItem);
Player.Inventory.Remove(currentItem);
if (_currentIndex >= ItemSlots.Length - 1)
_currentIndex--;

View File

@@ -1,6 +1,8 @@
using GameJamDungeon;
using Godot;
namespace GameJamDungeon;
public partial class ItemLabel : Label
{
public ItemLabel()
@@ -8,8 +10,6 @@ public partial class ItemLabel : Label
LabelSettings = UnequippedItemFont;
}
public IInventoryItem InventoryItem { get; set; } = default!;
private static LabelSettings UnequippedItemFont => GD.Load<LabelSettings>("res://src/ui/label_settings/MainTextRegular.tres");
private static LabelSettings EquippedItemFont => GD.Load<LabelSettings>("res://src/ui/label_settings/MainTextFontEquipped.tres");

View File

@@ -1,9 +1,10 @@
using Chickensoft.AutoInject;
using Chickensoft.GodotNodeInterfaces;
using Chickensoft.Introspection;
using GameJamDungeon;
using Godot;
namespace GameJamDungeon;
public interface IItemSlot : IHBoxContainer
{
public IInventoryItem Item { get; set; }
@@ -22,8 +23,9 @@ public partial class ItemSlot : HBoxContainer, IItemSlot
{
public override void _Notification(int what) => this.Notify(what);
[Dependency]
public IGameRepo GameRepo => this.DependOn<IGameRepo>();
[Dependency] public IGameRepo GameRepo => this.DependOn<IGameRepo>();
[Dependency] public IPlayer Player => this.DependOn<IPlayer>();
//[Node] public Label EquipBonus { get; set; } = default!;
@@ -39,12 +41,12 @@ public partial class ItemSlot : HBoxContainer, IItemSlot
public void OnReady()
{
ItemName.Text = Item.Info.Name;
ItemName.Text = Item.ItemName;
//EquipBonus.Text = "...";
ItemTexture.Texture = Item.Info.Texture;
GameRepo.PlayerData.Inventory.EquippedWeapon.Sync += EquippedWeapon_Sync;
GameRepo.PlayerData.Inventory.EquippedArmor.Sync += EquippedArmor_Sync;
GameRepo.PlayerData.Inventory.EquippedAccessory.Sync += EquippedAccessory_Sync;
ItemTexture.Texture = Item.GetTexture();
Player.Inventory.EquippedWeapon.Sync += EquippedWeapon_Sync;
Player.Inventory.EquippedArmor.Sync += EquippedArmor_Sync;
Player.Inventory.EquippedAccessory.Sync += EquippedAccessory_Sync;
}
private void EquippedWeapon_Sync(Weapon obj)
@@ -90,7 +92,7 @@ public partial class ItemSlot : HBoxContainer, IItemSlot
}
public void SetSelectedItemStyle()
{
if (Item is IEquipableItem equipableItem && GameRepo.PlayerData.Inventory.IsEquipped(equipableItem))
if (Item is IEquipableItem equipableItem && Player.Inventory.IsEquipped(equipableItem))
{
ItemName.LabelSettings = SelectedEquippedItemFont;
//EquipBonus.LabelSettings = EquippedItemFont;

View File

@@ -3,6 +3,8 @@ using Chickensoft.Introspection;
using GameJamDungeon;
using Godot;
namespace GameJamDungeon;
[Meta(typeof(IAutoNode))]
public partial class ItemRescue : Area3D
{

View File

@@ -1,14 +1,13 @@
using System.Collections.Generic;
namespace GameJamDungeon.src.item_rescue
{
public class RescuedItemDatabase
{
public List<IInventoryItem> Items;
namespace GameJamDungeon;
public RescuedItemDatabase()
{
Items = new List<IInventoryItem>();
}
public class RescuedItemDatabase
{
public List<IInventoryItem> Items;
public RescuedItemDatabase()
{
Items = new List<IInventoryItem>();
}
}

View File

@@ -5,6 +5,8 @@ using Godot;
using System.Collections.Generic;
using System.Linq;
namespace GameJamDungeon;
[Meta(typeof(IAutoNode))]
public partial class RescuedItems : Node3D
{

View File

@@ -1,9 +1,8 @@
namespace GameJamDungeon
{
public interface IEquipableItem : IInventoryItem
{
public void Equip();
namespace GameJamDungeon;
public void Unequip();
}
public interface IEquipableItem : IInventoryItem
{
public void Equip();
public void Unequip();
}

View File

@@ -2,7 +2,6 @@
using Chickensoft.GodotNodeInterfaces;
using Godot;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
@@ -34,6 +33,8 @@ public interface IInventory : INode
event Inventory.PickedUpItemEventHandler PickedUpItem;
event Inventory.AccessoryUnequippedEventHandler AccessoryUnequipped;
event Inventory.RaiseStatRequestEventHandler RaiseStatRequest;
}
public partial class Inventory : Node, IInventory
@@ -44,7 +45,7 @@ public partial class Inventory : Node, IInventory
[Signal]
public delegate void InventoryAtCapacityEventHandler(string rejectedItemName);
[Signal]
public delegate void AccessoryUnequippedEventHandler(AccessoryStats unequippedAccessory);
public delegate void AccessoryUnequippedEventHandler(Accessory unequippedAccessory);
[Signal]
public delegate void RaiseStatRequestEventHandler(ConsumableItemStats consumableItemStats);
[Signal]
@@ -70,12 +71,12 @@ public partial class Inventory : Node, IInventory
{
if (Items.Count >= _maxInventorySize)
{
EmitSignal(SignalName.InventoryAtCapacity, inventoryItem.Info.Name);
EmitSignal(SignalName.InventoryAtCapacity, inventoryItem.ItemName);
return false;
}
Items.Add(inventoryItem);
EmitSignal(SignalName.PickedUpItem, inventoryItem.Info.Name);
EmitSignal(SignalName.PickedUpItem, inventoryItem.ItemName);
return true;
}
@@ -98,14 +99,14 @@ public partial class Inventory : Node, IInventory
if (equipable is Weapon weapon)
{
_equippedWeapon.OnNext(new Weapon());
if (weapon.WeaponStats.WeaponTags.Contains(WeaponTag.BreaksOnChange))
if (weapon.WeaponTags.Contains(WeaponTag.BreaksOnChange))
Items.Remove(weapon);
}
else if (equipable is Armor armor)
_equippedArmor.OnNext(new Armor());
else if (equipable is Accessory accessory)
{
EmitSignal(SignalName.AccessoryUnequipped, _equippedAccessory.Value.AccessoryStats);
EmitSignal(SignalName.AccessoryUnequipped, _equippedAccessory.Value);
_equippedAccessory.OnNext(new Accessory());
}
else
@@ -146,10 +147,10 @@ public partial class Inventory : Node, IInventory
{
public int Compare(Weapon x, Weapon y)
{
if (x.WeaponStats.Damage == y.WeaponStats.Damage)
return x.WeaponStats.Name.CompareTo(y.WeaponStats.Name);
if (x.Damage == y.Damage)
return x.ItemName.CompareTo(y.ItemName);
return x.WeaponStats.Damage < y.WeaponStats.Damage ? 1 : -1;
return x.Damage < y.Damage ? 1 : -1;
}
}
@@ -157,10 +158,10 @@ public partial class Inventory : Node, IInventory
{
public int Compare(Armor x, Armor y)
{
if (x.ArmorStats.Defense == y.ArmorStats.Defense)
return x.ArmorStats.Name.CompareTo(y.ArmorStats.Name);
if (x.Defense == y.Defense)
return x.ItemName.CompareTo(y.ItemName);
return x.ArmorStats.Defense < y.ArmorStats.Defense ? 1 : -1;
return x.Defense < y.Defense ? 1 : -1;
}
}
@@ -168,7 +169,7 @@ public partial class Inventory : Node, IInventory
{
public int Compare(Accessory x, Accessory y)
{
return x.AccessoryStats.Name.CompareTo(y.AccessoryStats.Name);
return x.ItemName.CompareTo(y.ItemName);
}
}
@@ -176,14 +177,14 @@ public partial class Inventory : Node, IInventory
{
public int Compare(ConsumableItem x, ConsumableItem y)
{
return x.ConsumableItemInfo.Name.CompareTo(y.ConsumableItemInfo.Name);
return x.ItemName.CompareTo(y.ItemName);
}
}
public class ThrowableComparer : IComparer<ThrowableItem>
{
public int Compare(ThrowableItem x, ThrowableItem y)
{
return x.ThrowableItemInfo.Name.CompareTo(y.ThrowableItemInfo.Name);
return x.ItemName.CompareTo(y.ItemName);
}
}
}

View File

@@ -1,17 +1,29 @@
using Chickensoft.GodotNodeInterfaces;
using Godot;
using System;
namespace GameJamDungeon
namespace GameJamDungeon;
public interface IInventoryItem : INode3D
{
public interface IInventoryItem : INode3D
{
public Guid ID { get; }
public Guid ID { get; }
public InventoryItemStats Info { get; }
}
string ItemName { get; }
public interface IUsableItem : IInventoryItem
{
public void Use();
}
string Description { get; }
float SpawnRate { get; }
double ThrowDamage { get; }
float ThrowSpeed { get; }
Texture2D GetTexture();
public void SetItemStats(InventoryItemStats inventoryItemStats);
}
public interface IUsableItem : IInventoryItem
{
public void Use();
}

View File

@@ -1,5 +1,6 @@
using Godot;
namespace GameJamDungeon;
public partial class InventoryItemStats : Resource
{
[Export]

View File

@@ -1,75 +1,74 @@
using Godot;
using System.Collections.Generic;
namespace GameJamDungeon
namespace GameJamDungeon;
public partial class ItemDatabase : Node
{
public partial class ItemDatabase : Node
[Export]
public PackedScene WeaponScene { get; set; }
[Export]
public PackedScene ArmorScene { get; set; }
[Export]
public PackedScene AccessoryScene { get; set; }
[Export]
public PackedScene ThrowableItemScene { get; set; }
[Export]
public PackedScene ConsumableItemScene { get; set; }
public IInventoryItem[] Initialize()
{
[Export]
public PackedScene WeaponScene { get; set; }
var database = new List<IInventoryItem>();
var armorResources = DirAccess.GetFilesAt("res://src/items/armor/resources/");
var weaponResources = DirAccess.GetFilesAt("res://src/items/weapons/resources/");
var accessoryResources = DirAccess.GetFilesAt("res://src/items/accessory/resources/");
var throwableResources = DirAccess.GetFilesAt("res://src/items/throwable/resources/");
var consumableResources = DirAccess.GetFilesAt("res://src/items/consumable/resources/");
[Export]
public PackedScene ArmorScene { get; set; }
[Export]
public PackedScene AccessoryScene { get; set; }
[Export]
public PackedScene ThrowableItemScene { get; set; }
[Export]
public PackedScene ConsumableItemScene { get; set; }
public IInventoryItem[] Initialize()
foreach (var armor in armorResources)
{
var database = new List<IInventoryItem>();
var armorResources = DirAccess.GetFilesAt("res://src/items/armor/resources/");
var weaponResources = DirAccess.GetFilesAt("res://src/items/weapons/resources/");
var accessoryResources = DirAccess.GetFilesAt("res://src/items/accessory/resources/");
var throwableResources = DirAccess.GetFilesAt("res://src/items/throwable/resources/");
var consumableResources = DirAccess.GetFilesAt("res://src/items/consumable/resources/");
foreach (var armor in armorResources)
{
var armorInfo = GD.Load<ArmorStats>($"res://src/items/armor/resources/{armor}");
var armorScene = ArmorScene.Instantiate<Armor>();
armorScene.ArmorStats = armorInfo;
database.Add(armorScene);
}
foreach (var weapon in weaponResources)
{
var weaponInfo = GD.Load<WeaponStats>($"res://src/items/weapons/resources/{weapon}");
var weaponScene = WeaponScene.Instantiate<Weapon>();
weaponScene.WeaponStats = weaponInfo;
database.Add(weaponScene);
}
foreach (var accessory in accessoryResources)
{
var accessoryInfo = GD.Load<AccessoryStats>($"res://src/items/accessory/resources/{accessory}");
var accessoryScene = AccessoryScene.Instantiate<Accessory>();
accessoryScene.AccessoryStats = accessoryInfo;
database.Add(accessoryScene);
}
foreach (var throwable in throwableResources)
{
var throwableItemInfo = GD.Load<ThrowableItemStats>($"res://src/items/throwable/resources/{throwable}");
var throwableItemScene = ThrowableItemScene.Instantiate<ThrowableItem>();
throwableItemScene.ThrowableItemInfo = throwableItemInfo;
database.Add(throwableItemScene);
}
foreach (var consumable in consumableResources)
{
var consumableItemInfo = GD.Load<ConsumableItemStats>($"res://src/items/consumable/resources/{consumable}");
var consumableItemScene = ConsumableItemScene.Instantiate<ConsumableItem>();
consumableItemScene.ConsumableItemInfo = consumableItemInfo;
database.Add(consumableItemScene);
}
return database.ToArray();
var armorInfo = GD.Load<ArmorStats>($"res://src/items/armor/resources/{armor}");
var armorScene = ArmorScene.Instantiate<Armor>();
armorScene.SetItemStats(armorInfo);
database.Add(armorScene);
}
foreach (var weapon in weaponResources)
{
var weaponInfo = GD.Load<WeaponStats>($"res://src/items/weapons/resources/{weapon}");
var weaponScene = WeaponScene.Instantiate<Weapon>();
weaponScene.SetItemStats(weaponInfo);
database.Add(weaponScene);
}
foreach (var accessory in accessoryResources)
{
var accessoryInfo = GD.Load<AccessoryStats>($"res://src/items/accessory/resources/{accessory}");
var accessoryScene = AccessoryScene.Instantiate<Accessory>();
accessoryScene.SetItemStats(accessoryInfo);
database.Add(accessoryScene);
}
foreach (var throwable in throwableResources)
{
var throwableItemInfo = GD.Load<ThrowableItemStats>($"res://src/items/throwable/resources/{throwable}");
var throwableItemScene = ThrowableItemScene.Instantiate<ThrowableItem>();
throwableItemScene.SetItemStats(throwableItemInfo);
database.Add(throwableItemScene);
}
foreach (var consumable in consumableResources)
{
var consumableItemInfo = GD.Load<ConsumableItemStats>($"res://src/items/consumable/resources/{consumable}");
var consumableItemScene = ConsumableItemScene.Instantiate<ConsumableItem>();
consumableItemScene.SetItemStats(consumableItemInfo);
database.Add(consumableItemScene);
}
return database.ToArray();
}
}

View File

@@ -1,8 +1,10 @@
using Chickensoft.AutoInject;
using Chickensoft.Introspection;
using GameJamDungeon;
using Godot;
using System;
using System.Collections.Immutable;
namespace GameJamDungeon;
[Meta(typeof(IAutoNode))]
public partial class Accessory : Node3D, IEquipableItem
@@ -13,21 +15,45 @@ public partial class Accessory : Node3D, IEquipableItem
[Dependency] public IGameEventDepot GameEventDepot => this.DependOn<IGameEventDepot>();
public InventoryItemStats Info => AccessoryStats;
[Dependency] public IPlayer Player => this.DependOn<IPlayer>();
[Export]
public AccessoryStats AccessoryStats { get; set; } = new AccessoryStats();
[Export] private AccessoryStats _accessoryStats { get; set; } = new AccessoryStats();
[Node] public Sprite3D Sprite { get; set; } = default!;
[Node] private Sprite3D Sprite { get; set; } = new Sprite3D();
[Node] public Area3D Pickup { get; set; } = default!;
public Guid ID => Guid.NewGuid();
public string ItemName => _accessoryStats.Name;
public string Description => _accessoryStats.Description;
public float SpawnRate => _accessoryStats.SpawnRate;
public Texture2D GetTexture() => _accessoryStats.Texture;
public double ThrowDamage => _accessoryStats.ThrowDamage;
public float ThrowSpeed => _accessoryStats.ThrowSpeed;
public int MaxHPUp => _accessoryStats.MaxHPUp;
public int MaxVTUp => _accessoryStats.MaxVTUp;
public double LuckUp => _accessoryStats.LuckUp;
public ImmutableList<AccessoryTag> AccessoryTags => [.. _accessoryStats.AccessoryTags];
public void OnReady()
{
Sprite.Texture = AccessoryStats.Texture;
Pickup.BodyEntered += OnEntered;
Sprite.Texture = _accessoryStats.Texture;
}
public void SetItemStats(InventoryItemStats inventoryItemStats)
{
_accessoryStats = (AccessoryStats)inventoryItemStats;
}
public void Equip()
@@ -42,12 +68,12 @@ public partial class Accessory : Node3D, IEquipableItem
public void Throw()
{
GameRepo.PlayerData.Inventory.Remove(this);
Player.Inventory.Remove(this);
}
public void OnEntered(Node3D body)
{
var isAdded = GameRepo.PlayerData.Inventory.TryAdd(this);
var isAdded = Player.Inventory.TryAdd(this);
if (isAdded)
QueueFree();
}

View File

@@ -1,5 +1,4 @@
using Godot;
using System;
namespace GameJamDungeon;
@@ -13,7 +12,7 @@ public partial class AccessoryStats : InventoryItemStats
public int DEFUp { get; set; } = 0;
[Export]
public double LUCKUp { get; set; } = 0;
public double LuckUp { get; set; } = 0;
[Export]
public int MaxHPUp { get; set; } = 0;

View File

@@ -1,33 +1,50 @@
using Chickensoft.AutoInject;
using Chickensoft.Introspection;
using GameJamDungeon;
using Godot;
using System;
namespace GameJamDungeon;
[Meta(typeof(IAutoNode))]
public partial class Armor : Node3D, IEquipableItem
{
public override void _Notification(int what) => this.Notify(what);
[Dependency] public IGameRepo GameRepo => this.DependOn<IGameRepo>();
[Dependency] public IGameEventDepot GameEventDepot => this.DependOn<IGameEventDepot>();
public InventoryItemStats Info => ArmorStats;
[Dependency] public IPlayer Player => this.DependOn<IPlayer>();
[Export]
public ArmorStats ArmorStats { get; set; } = new ArmorStats();
[Export] private ArmorStats _armorStats { get; set; } = new ArmorStats();
[Node] public Sprite3D Sprite { get; set; } = default!;
[Node] private Sprite3D Sprite { get; set; } = new Sprite3D();
[Node] public Area3D Pickup { get; set; } = default!;
[Node] private Area3D Pickup { get; set; } = default!;
public Guid ID => Guid.NewGuid();
public string ItemName => _armorStats.Name;
public string Description => _armorStats.Description;
public float SpawnRate => _armorStats.SpawnRate;
public Texture2D GetTexture() => _armorStats.Texture;
public double ThrowDamage => _armorStats.ThrowDamage;
public float ThrowSpeed => _armorStats.ThrowSpeed;
public int Defense => _armorStats.Defense;
public void OnReady()
{
Sprite.Texture = ArmorStats.Texture;
Pickup.BodyEntered += OnEntered;
Sprite.Texture = _armorStats.Texture;
}
public void SetItemStats(InventoryItemStats inventoryItemStats)
{
_armorStats = (ArmorStats)inventoryItemStats;
}
public void Equip()
@@ -42,17 +59,17 @@ public partial class Armor : Node3D, IEquipableItem
public void Throw()
{
GameRepo.PlayerData.Inventory.Remove(this);
Player.Inventory.Remove(this);
}
public void Drop()
{
GameRepo.PlayerData.Inventory.Remove(this);
Player.Inventory.Remove(this);
}
public void OnEntered(Node3D body)
{
var isAdded = GameRepo.PlayerData.Inventory.TryAdd(this);
var isAdded = Player.Inventory.TryAdd(this);
if (isAdded)
QueueFree();
}

View File

@@ -1,6 +1,6 @@
[gd_scene load_steps=3 format=3 uid="uid://dorr7v1tkeiy0"]
[ext_resource type="Script" path="res://src/items/armor/Armor.cs" id="1_cmjpq"]
[ext_resource type="Script" uid="uid://bxvre2y2caa3h" path="res://src/items/armor/Armor.cs" id="1_cmjpq"]
[sub_resource type="BoxShape3D" id="BoxShape3D_qdeu2"]
size = Vector3(0.778381, 0.929947, 0.731567)

View File

@@ -1,9 +1,10 @@
using Chickensoft.AutoInject;
using Chickensoft.Introspection;
using GameJamDungeon;
using Godot;
using System;
namespace GameJamDungeon;
[Meta(typeof(IAutoNode))]
public partial class ConsumableItem : Node3D, IUsableItem
{
@@ -13,42 +14,57 @@ public partial class ConsumableItem : Node3D, IUsableItem
public delegate void RaiseStatRequestEventHandler(ConsumableItemStats consumableItemStats);
[Dependency] public IGame Game => this.DependOn<IGame>();
[Dependency] public IGameRepo GameRepo => this.DependOn<IGameRepo>();
[Dependency] public IGameEventDepot GameEventDepot => this.DependOn<IGameEventDepot>();
public InventoryItemStats Info => ConsumableItemInfo;
[Dependency] public IPlayer Player => this.DependOn<IPlayer>();
[Export]
public ConsumableItemStats ConsumableItemInfo { get; set; }
private ConsumableItemStats _consumableItemStats { get; set; }
[Node] public Sprite3D Sprite { get; set; } = default!;
[Node] private Sprite3D Sprite { get; set; } = new Sprite3D();
[Node] public Area3D Pickup { get; set; } = default!;
[Node] private Area3D Pickup { get; set; } = default!;
public Guid ID => Guid.NewGuid();
public string ItemName => _consumableItemStats.Name;
public string Description => _consumableItemStats.Description;
public float SpawnRate => _consumableItemStats.SpawnRate;
public Texture2D GetTexture() => _consumableItemStats.Texture;
public double ThrowDamage => _consumableItemStats.ThrowDamage;
public float ThrowSpeed => _consumableItemStats.ThrowSpeed;
public void Use()
{
if (ConsumableItemInfo.RaiseHPAmount > 0)
Game.RaiseHP(ConsumableItemInfo.RaiseHPAmount);
if (ConsumableItemInfo.RaiseVTAmount > 0)
Game.RaiseVT(ConsumableItemInfo.RaiseVTAmount);
if (_consumableItemStats.RaiseHPAmount > 0)
Game.RaiseHP(_consumableItemStats.RaiseHPAmount);
if (_consumableItemStats.RaiseVTAmount > 0)
Game.RaiseVT(_consumableItemStats.RaiseVTAmount);
if (ConsumableItemInfo.HealHPAmount > 0 && GameRepo.PlayerData.CurrentHP.Value != GameRepo.PlayerData.MaximumHP.Value)
Game.HealHP(ConsumableItemInfo.HealHPAmount);
if (ConsumableItemInfo.HealVTAmount > 0 && GameRepo.PlayerData.CurrentVT.Value != GameRepo.PlayerData.MaximumVT.Value)
Game.HealVT(ConsumableItemInfo.HealVTAmount);
if (_consumableItemStats.HealHPAmount > 0 && Player.Stats.CurrentHP.Value != Player.Stats.MaximumHP.Value)
Game.HealHP(_consumableItemStats.HealHPAmount);
if (_consumableItemStats.HealVTAmount > 0 && Player.Stats.CurrentVT.Value != Player.Stats.MaximumVT.Value)
Game.HealVT(_consumableItemStats.HealVTAmount);
}
public void SetItemStats(InventoryItemStats inventoryItemStats)
{
_consumableItemStats = (ConsumableItemStats)inventoryItemStats;
}
public void OnReady()
{
Sprite.Texture = ConsumableItemInfo.Texture;
Pickup.BodyEntered += OnEntered;
Sprite.Texture = _consumableItemStats.Texture;
}
public void OnEntered(Node3D body)
{
var isAdded = GameRepo.PlayerData.Inventory.TryAdd(this);
var isAdded = Player.Inventory.TryAdd(this);
if (isAdded)
QueueFree();
}

View File

@@ -3,86 +3,87 @@ using Chickensoft.GodotNodeInterfaces;
using Chickensoft.Introspection;
using Godot;
namespace GameJamDungeon
namespace GameJamDungeon;
public interface IDroppedItem : IRigidBody3D
{
public interface IDroppedItem : IRigidBody3D
void RescueItem();
}
[Meta(typeof(IAutoNode))]
public partial class DroppedItem : RigidBody3D, IDroppedItem
{
public override void _Notification(int what) => this.Notify(what);
[Dependency] public IGameRepo GameRepo => this.DependOn<IGameRepo>();
[Dependency] public IGame Game => this.DependOn<IGame>();
[Dependency] public IPlayer Player => this.DependOn<IPlayer>();
[Node] private Sprite2D Sprite { get; set; } = default!;
public IInventoryItem Item { get; set; }
public void OnResolved()
{
void RescueItem();
ContactMonitor = true;
BodyEntered += DroppedItem_BodyEntered;
Sprite.Texture = Item.GetTexture();
}
[Meta(typeof(IAutoNode))]
public partial class DroppedItem : RigidBody3D, IDroppedItem
public async void Drop()
{
public override void _Notification(int what) => this.Notify(what);
AddCollisionExceptionWith((Node)Game.Player);
GlobalPosition = Player.CurrentPosition + Vector3.Up;
ApplyCentralImpulse(-Player.CurrentBasis.Z.Normalized() * 5.0f);
await ToSignal(GetTree().CreateTimer(1.5), "timeout");
RemoveCollisionExceptionWith((Node)Game.Player);
}
[Dependency] public IGameRepo GameRepo => this.DependOn<IGameRepo>();
public void RescueItem()
{
ContactMonitor = false;
PlayRescueAnimation();
Game.RescuedItems.Items.Add(Item);
}
[Dependency] public IGame Game => this.DependOn<IGame>();
private void PlayRescueAnimation()
{
LoadShader("res://src/vfx/shaders/PixelMelt.gdshader");
var tweener = GetTree().CreateTween();
SetShaderValue(true);
tweener.TweenMethod(Callable.From((float x) => SetShaderValue(x)), 0.0f, 0.3f, 2f);
tweener.TweenCallback(Callable.From(QueueFree));
}
[Node] public Sprite2D Sprite { get; set; } = default!;
private void LoadShader(string shaderPath)
{
var shader = GD.Load<Shader>(shaderPath);
Sprite.Material = new ShaderMaterial();
var shaderMaterial = (ShaderMaterial)Sprite.Material;
shaderMaterial.Shader = shader;
}
public IInventoryItem Item { get; set; }
private void SetShaderValue(float shaderValue)
{
var shaderMaterial = (ShaderMaterial)Sprite.Material;
shaderMaterial.SetShaderParameter("progress", shaderValue);
}
public void OnResolved()
private void SetShaderValue(bool shaderValue)
{
var shaderMaterial = (ShaderMaterial)Sprite.Material;
shaderMaterial.SetShaderParameter("reverse", shaderValue);
}
private void DroppedItem_BodyEntered(Node body)
{
if (body is IPlayer player)
{
ContactMonitor = true;
BodyEntered += DroppedItem_BodyEntered;
Sprite.Texture = Item.Info.Texture;
}
public async void Drop()
{
AddCollisionExceptionWith((Node)Game.Player);
GlobalPosition = Game.Player.GlobalPosition + Vector3.Up;
ApplyCentralImpulse(-Game.Player.GlobalBasis.Z.Normalized() * 5.0f);
await ToSignal(GetTree().CreateTimer(1.5), "timeout");
RemoveCollisionExceptionWith((Node)Game.Player);
}
public void RescueItem()
{
ContactMonitor = false;
PlayRescueAnimation();
Game.RescuedItems.Items.Add(Item);
}
private void PlayRescueAnimation()
{
LoadShader("res://src/vfx/shaders/PixelMelt.gdshader");
var tweener = GetTree().CreateTween();
SetShaderValue(true);
tweener.TweenMethod(Callable.From((float x) => SetShaderValue(x)), 0.0f, 0.3f, 2f);
tweener.TweenCallback(Callable.From(QueueFree));
}
private void LoadShader(string shaderPath)
{
var shader = GD.Load<Shader>(shaderPath);
Sprite.Material = new ShaderMaterial();
var shaderMaterial = (ShaderMaterial)Sprite.Material;
shaderMaterial.Shader = shader;
}
private void SetShaderValue(float shaderValue)
{
var shaderMaterial = (ShaderMaterial)Sprite.Material;
shaderMaterial.SetShaderParameter("progress", shaderValue);
}
private void SetShaderValue(bool shaderValue)
{
var shaderMaterial = (ShaderMaterial)Sprite.Material;
shaderMaterial.SetShaderParameter("reverse", shaderValue);
}
private void DroppedItem_BodyEntered(Node body)
{
if (body is IPlayer player)
{
var isAdded = GameRepo.PlayerData.Inventory.TryAdd(Item);
if (isAdded)
QueueFree();
}
var isAdded = Player.Inventory.TryAdd(Item);
if (isAdded)
QueueFree();
}
}
}

View File

@@ -1,8 +1,9 @@
using Chickensoft.AutoInject;
using Chickensoft.Introspection;
using GameJamDungeon;
using Godot;
namespace GameJamDungeon;
[Meta(typeof(IAutoNode))]
public partial class Restorative : Node3D
{

View File

@@ -1,8 +1,10 @@
using Chickensoft.AutoInject;
using Chickensoft.Introspection;
using GameJamDungeon;
using Godot;
using System;
using System.Collections.Immutable;
namespace GameJamDungeon;
[Meta(typeof(IAutoNode))]
public partial class ThrowableItem : Node3D, IUsableItem
@@ -11,56 +13,75 @@ public partial class ThrowableItem : Node3D, IUsableItem
[Dependency] public IGame Game => this.DependOn<IGame>();
[Dependency] public IGameRepo GameRepo => this.DependOn<IGameRepo>();
public InventoryItemStats Info => ThrowableItemInfo;
public int Count { get; }
[Dependency] public IPlayer Player => this.DependOn<IPlayer>();
public Guid ID => Guid.NewGuid();
public string ItemName => _throwableItemStats.Name;
public string Description => _throwableItemStats.Description;
public float SpawnRate => _throwableItemStats.SpawnRate;
public Texture2D GetTexture() => _throwableItemStats.Texture;
public double ThrowDamage => _throwableItemStats.ThrowDamage;
public float ThrowSpeed => _throwableItemStats.ThrowSpeed;
public ElementType ElementType => _throwableItemStats.ElementType;
public ImmutableList<ThrowableItemTag> ThrowableItemTags => [.. _throwableItemStats.ThrowableItemTags];
public int Count { get; }
public void SetItemStats(InventoryItemStats inventoryItemStats)
{
_throwableItemStats = (ThrowableItemStats)inventoryItemStats;
}
public void OnEntered(Node3D body)
{
var isAdded = Player.Inventory.TryAdd(this);
if (isAdded)
QueueFree();
}
[Export]
public ThrowableItemStats ThrowableItemInfo { get; set; }
private ThrowableItemStats _throwableItemStats { get; set; }
[Node] public Sprite3D Sprite { get; set; } = default!;
[Node] private Sprite3D Sprite { get; set; } = new Sprite3D();
[Node] public Area3D Pickup { get; set; } = default!;
[Node] private Area3D Pickup { get; set; } = default!;
public void OnResolved()
{
Sprite.Texture = ThrowableItemInfo.Texture;
Pickup.BodyEntered += OnEntered;
Sprite.Texture = _throwableItemStats.Texture;
}
public void Use()
{
if (ThrowableItemInfo.HealHPAmount > 0)
Game.HealHP(ThrowableItemInfo.HealHPAmount);
if (ThrowableItemInfo.HealVTAmount > 0)
Game.HealVT(ThrowableItemInfo.HealVTAmount);
if (_throwableItemStats.HealHPAmount > 0)
Game.HealHP(_throwableItemStats.HealHPAmount);
if (_throwableItemStats.HealVTAmount > 0)
Game.HealVT(_throwableItemStats.HealVTAmount);
if (ThrowableItemInfo.UsableItemTags.Contains(UsableItemTag.DoubleEXP))
if (_throwableItemStats.UsableItemTags.Contains(UsableItemTag.DoubleEXP))
Game.DoubleEXP(TimeSpan.FromSeconds(30));
if (ThrowableItemInfo.ThrowableItemTags.Contains(ThrowableItemTag.CanChangeAffinity))
if (_throwableItemStats.ThrowableItemTags.Contains(ThrowableItemTag.CanChangeAffinity))
ChangeAffinity();
}
private void ChangeAffinity()
{
var maximumElements = Enum.GetNames(typeof(ElementType)).Length;
ThrowableItemInfo.ElementType = ThrowableItemInfo.ElementType + 1 % maximumElements;
_throwableItemStats.ElementType = _throwableItemStats.ElementType + 1 % maximumElements;
// TODO: Make this an inventory animation to cycle through elements.
ThrowableItemInfo.Description =
$"Inflicts {ThrowableItemInfo.ElementType} damage when thrown." +
_throwableItemStats.Description =
$"Inflicts {_throwableItemStats.ElementType} damage when thrown." +
$"{System.Environment.NewLine}Use item to change Affinity.";
}
public void OnEntered(Node3D body)
{
var isAdded = GameRepo.PlayerData.Inventory.TryAdd(this);
if (isAdded)
QueueFree();
}
}

View File

@@ -1,4 +1,4 @@
using System.ComponentModel;
namespace GameJamDungeon;
public enum ThrowableItemTag
{

View File

@@ -1,53 +1,52 @@
using Chickensoft.AutoInject;
using Chickensoft.Introspection;
using GameJamDungeon;
using Godot;
namespace GameJamDungeon;
[Meta(typeof(IAutoNode))]
public partial class ThrownItem : RigidBody3D
{
public override void _Notification(int what) => this.Notify(what);
[Dependency] public IGame Game => this.DependOn<IGame>();
[Dependency] public IPlayer Player => this.DependOn<IPlayer>();
public InventoryItemStats ThrownItemStats;
public IInventoryItem ItemThatIsThrown;
[Node] public Sprite3D Sprite { get; set; } = default!;
public void OnResolved()
{
BodyEntered += ThrownItem_BodyEntered;
GlobalPosition = Game.Player.GlobalPosition + new Vector3(0, 0, 0);
Sprite.Texture = ThrownItemStats.Texture;
AddCollisionExceptionWith((Node)Game.Player);
GlobalPosition = Player.CurrentPosition;
Sprite.Texture = ItemThatIsThrown.GetTexture();
AddCollisionExceptionWith((Node)Player);
}
private void ThrownItem_BodyEntered(Node body)
{
if (body is IEnemy enemy)
{
CalculateEffect(enemy);
}
QueueFree();
}
public void Throw()
{
ApplyCentralImpulse(-Game.Player.GlobalBasis.Z.Normalized() * ThrownItemStats.ThrowSpeed);
ApplyCentralImpulse(-Player.CurrentBasis.Z.Normalized() * ItemThatIsThrown.ThrowSpeed);
}
private void CalculateEffect(IEnemy enemy)
{
if (ThrownItemStats is ThrowableItemStats throwableItemStats)
if (ItemThatIsThrown is ThrowableItem throwableItem)
{
if (throwableItemStats.ThrowableItemTags.Contains(ThrowableItemTag.LowerTargetTo1HP))
enemy.TakeDamage(enemy.CurrentHP - 1, ElementType.None, false, true, true);
if (throwableItem.ThrowableItemTags.Contains(ThrowableItemTag.LowerTargetTo1HP))
enemy.TakeDamage(enemy.CurrentHP - 1, ignoreDefense: true, ignoreElementalResistance: true);
else
enemy.TakeDamage(throwableItemStats.ThrowDamage, throwableItemStats.ElementType);
enemy.TakeDamage(throwableItem.ThrowDamage, throwableItem.ElementType);
}
else
{
enemy.TakeDamage(ThrownItemStats.ThrowDamage, ElementType.None);
enemy.TakeDamage(ItemThatIsThrown.ThrowDamage);
}
}
}
}

View File

@@ -1,34 +1,65 @@
using Chickensoft.AutoInject;
using Chickensoft.Introspection;
using GameJamDungeon;
using Godot;
using System;
using System.Collections.Immutable;
namespace GameJamDungeon;
[Meta(typeof(IAutoNode))]
public partial class Weapon : Node3D, IInventoryItem, IEquipableItem
{
public override void _Notification(int what) => this.Notify(what);
[Dependency] public IGameRepo GameRepo => this.DependOn<IGameRepo>();
public InventoryItemStats Info => WeaponStats;
[Dependency] public IPlayer Player => this.DependOn<IPlayer>();
[Signal]
public delegate void EquippedItemEventHandler(Weapon equippedWeapon);
[Export]
public WeaponStats WeaponStats { get; set; } = new WeaponStats();
private WeaponStats _weaponStats { get; set; } = new WeaponStats();
[Node] public Sprite3D Sprite { get; set; } = default!;
[Node] public Sprite3D Sprite { get; set; } = new Sprite3D();
[Node] public Area3D Pickup { get; set; } = default!;
public Texture2D GetTexture() => _weaponStats.Texture;
public Guid ID => Guid.NewGuid();
public string ItemName => _weaponStats.Name;
public string Description => _weaponStats.Description;
public float SpawnRate => _weaponStats.SpawnRate;
public int Damage => _weaponStats.Damage;
public double ThrowDamage => _weaponStats.ThrowDamage;
public float ThrowSpeed => _weaponStats.ThrowSpeed;
public double Luck => _weaponStats.Luck;
public double AttackSpeed => _weaponStats.AttackSpeed;
public ImmutableList<WeaponTag> WeaponTags => [.. _weaponStats.WeaponTags];
public ElementType WeaponElement => _weaponStats.WeaponElement;
public double ElementalDamageBonus => _weaponStats.ElementalDamageBonus;
public void OnReady()
{
Sprite.Texture = WeaponStats.Texture;
Pickup.BodyEntered += OnEntered;
Sprite.Texture = _weaponStats.Texture;
}
public void SetItemStats(InventoryItemStats inventoryItemStats)
{
_weaponStats = (WeaponStats)inventoryItemStats;
var texture = ResourceLoader.Load(inventoryItemStats.Texture.ResourcePath) as Texture2D;
Sprite.Texture = texture;
}
public void Equip()
@@ -43,17 +74,17 @@ public partial class Weapon : Node3D, IInventoryItem, IEquipableItem
public void Throw()
{
GameRepo.PlayerData.Inventory.Remove(this);
Player.Inventory.Remove(this);
}
public void Drop()
{
GameRepo.PlayerData.Inventory.Remove(this);
Player.Inventory.Remove(this);
}
public void OnEntered(Node3D body)
{
var isAdded = GameRepo.PlayerData.Inventory.TryAdd(this);
var isAdded = Player.Inventory.TryAdd(this);
if (isAdded)
QueueFree();
}

View File

@@ -1,6 +1,7 @@
using GameJamDungeon;
using Godot;
namespace GameJamDungeon;
[GlobalClass]
public partial class WeaponStats : InventoryItemStats
{

View File

@@ -1,7 +0,0 @@
using Godot;
using System;
public partial class Rigid : RigidBody3D
{
}

View File

@@ -1,9 +1,9 @@
[gd_scene load_steps=9 format=3 uid="uid://by67pn7fdsg1m"]
[ext_resource type="Script" path="res://src/map/Map.cs" id="1_bw70o"]
[ext_resource type="Script" uid="uid://14e8mu48ed4" path="res://src/map/Map.cs" id="1_bw70o"]
[ext_resource type="PackedScene" uid="uid://dl6h1djc27ddl" path="res://src/map/overworld/Floor0.tscn" id="2_0m8h8"]
[ext_resource type="PackedScene" uid="uid://bc1sp6xwe0j65" path="res://src/map/dungeon/floors/Floor01.tscn" id="2_merfv"]
[ext_resource type="Script" path="res://src/map/dungeon/code/Floor0.cs" id="3_n0f1p"]
[ext_resource type="Script" uid="uid://c1nhqlem1ew3m" path="res://src/map/dungeon/code/Floor0.cs" id="3_n0f1p"]
[ext_resource type="PackedScene" uid="uid://g28xmp6cn16h" path="res://src/map/dungeon/floors/Floor11.tscn" id="3_niasb"]
[ext_resource type="PackedScene" uid="uid://dmiqwmivkjgmq" path="res://src/map/dungeon/floors/Floor02.tscn" id="4_8y0oy"]
[ext_resource type="PackedScene" uid="uid://bjqgl5u05ia04" path="res://src/map/dungeon/Teleport.tscn" id="5_jiohg"]

View File

@@ -1,8 +1,9 @@
using Chickensoft.AutoInject;
using Chickensoft.Introspection;
using GameJamDungeon;
using Godot;
namespace GameJamDungeon;
[Meta(typeof(IAutoNode))]
public partial class BossFloor : Node3D, IDungeonFloor
{

View File

@@ -3,67 +3,66 @@ using Chickensoft.Introspection;
using Godot;
using System.Linq;
namespace GameJamDungeon
namespace GameJamDungeon;
[Meta(typeof(IAutoNode))]
public partial class BossRoom : Node3D, IDungeonRoom
{
[Meta(typeof(IAutoNode))]
public partial class BossRoom : Node3D, IDungeonRoom
public override void _Notification(int what) => this.Notify(what);
[Dependency] public IGameRepo GameRepo => this.DependOn<IGameRepo>();
[Node] public Marker3D PlayerSpawn { get; set; } = default!;
[Node] public Marker3D TeleportSpawn { get; set; } = default!;
[Node] public Marker3D ItemSpawnPoint { get; set; } = default!;
[Node] public ItemDatabase ItemDatabase { get; set; } = default!;
[Node] public Node3D HorseHeadStatue { get; set; } = default!;
[Node] public Node3D OxFaceStatue { get; set; } = default!;
[Node] public Boss OxFace { get; set; } = default!;
[Node] public Boss HorseFace { get; set; } = default!;
[Node] public Area3D ActivateTrap { get; set; } = default!;
[Node] public StaticBody3D GateCollision { get; set; } = default!;
public void Setup()
{
public override void _Notification(int what) => this.Notify(what);
SpawnItems();
ActivateTrap.BodyEntered += StartBossFight;
OxFace.CurrentHP.Sync += BossHPUpdate;
HorseFace.CurrentHP.Sync += BossHPUpdate;
}
[Dependency] public IGameRepo GameRepo => this.DependOn<IGameRepo>();
private void BossHPUpdate(double obj)
{
if (OxFace.CurrentHP.Value <= 0 && HorseFace.CurrentHP.Value <= 0)
GateCollision.CallDeferred(MethodName.QueueFree);
}
[Node] public Marker3D PlayerSpawn { get; set; } = default!;
private void SpawnItems()
{
var database = ItemDatabase.Initialize().OfType<ConsumableItem>().ToArray();
var rng = new RandomNumberGenerator();
rng.Randomize();
var weights = database.Select(x => x.SpawnRate).ToArray();
var selectedItem = database[rng.RandWeighted(weights)];
var duplicated = selectedItem.Duplicate((int)DuplicateFlags.UseInstantiation) as Node3D;
duplicated.Position = ItemSpawnPoint.Position;
AddChild(duplicated);
}
[Node] public Marker3D TeleportSpawn { get; set; } = default!;
[Node] public Marker3D ItemSpawnPoint { get; set; } = default!;
[Node] public ItemDatabase ItemDatabase { get; set; } = default!;
[Node] public Node3D HorseHeadStatue { get; set; } = default!;
[Node] public Node3D OxFaceStatue { get; set; } = default!;
[Node] public Boss OxFace { get; set; } = default!;
[Node] public Boss HorseFace { get; set; } = default!;
[Node] public Area3D ActivateTrap { get; set; } = default!;
[Node] public StaticBody3D GateCollision { get; set; } = default!;
public void Setup()
{
SpawnItems();
ActivateTrap.BodyEntered += StartBossFight;
OxFace.CurrentHP.Sync += BossHPUpdate;
HorseFace.CurrentHP.Sync += BossHPUpdate;
}
private void BossHPUpdate(double obj)
{
if (OxFace.CurrentHP.Value <= 0 && HorseFace.CurrentHP.Value <= 0)
GateCollision.CallDeferred(MethodName.QueueFree);
}
private void SpawnItems()
{
var database = ItemDatabase.Initialize().OfType<ConsumableItem>().ToArray();
var rng = new RandomNumberGenerator();
rng.Randomize();
var weights = database.Select(x => x.Info.SpawnRate).ToArray();
var selectedItem = database[rng.RandWeighted(weights)];
var duplicated = selectedItem.Duplicate((int)DuplicateFlags.UseInstantiation) as Node3D;
duplicated.Position = ItemSpawnPoint.Position;
AddChild(duplicated);
}
private void StartBossFight(Node3D body)
{
OxFaceStatue.Hide();
HorseHeadStatue.Hide();
OxFace.Activate();
HorseFace.Activate();
}
private void StartBossFight(Node3D body)
{
OxFaceStatue.Hide();
HorseHeadStatue.Hide();
OxFace.Activate();
HorseFace.Activate();
}
}

View File

@@ -1,90 +1,83 @@
using Chickensoft.AutoInject;
using Chickensoft.GodotNodeInterfaces;
using Chickensoft.Introspection;
using Godot;
using System.Collections.Generic;
using System.Linq;
public interface IDungeonFloor : INode3D
namespace GameJamDungeon
{
void InitializeDungeon();
public Transform3D GetPlayerSpawnPoint();
public Vector3 GetTeleportSpawnPoint();
}
[Meta(typeof(IAutoNode))]
public partial class DungeonFloor : Node3D, IDungeonFloor
{
public override void _Notification(int what) => this.Notify(what);
[Node] public GodotObject DungeonGenerator { get; set; } = default!;
[Node] public NavigationRegion3D NavigationRegion3D { get; set; } = default!;
private Transform3D _playerSpawnPoint;
private Vector3 _teleportSpawnPoint;
internal List<IDungeonRoom> Rooms { get; private set; }
public void InitializeDungeon()
[Meta(typeof(IAutoNode))]
public partial class DungeonFloor : Node3D, IDungeonFloor
{
Rooms = new List<IDungeonRoom>();
DungeonGenerator.Call("generate");
NavigationRegion3D.BakeNavigationMesh();
Rooms = FindAllDungeonRooms([.. GetChildren()], Rooms);
_playerSpawnPoint = RandomizePlayerSpawnPoint();
_teleportSpawnPoint = RandomizeTeleportSpawnPointAwayFromPosition(_playerSpawnPoint.Origin);
}
public override void _Notification(int what) => this.Notify(what);
public Transform3D GetPlayerSpawnPoint() => _playerSpawnPoint;
[Node] public GodotObject DungeonGenerator { get; set; } = default!;
public Vector3 GetTeleportSpawnPoint() => _teleportSpawnPoint;
[Node] public NavigationRegion3D NavigationRegion3D { get; set; } = default!;
private Transform3D RandomizePlayerSpawnPoint()
{
var rng = new RandomNumberGenerator();
rng.Randomize();
var rngDistribution = new List<float>();
var randomSpawnLocations = Rooms
.Select(x => x.PlayerSpawn);
var godotCollection = new Godot.Collections.Array<Marker3D>(randomSpawnLocations);
var result = godotCollection.PickRandom();
return result.GlobalTransform;
}
private Transform3D _playerSpawnPoint;
private Vector3 RandomizeTeleportSpawnPointAwayFromPosition(Vector3 target)
{
var rng = new RandomNumberGenerator();
rng.Randomize();
var rngDistribution = new List<float>();
var roomsSortedByDistance = Rooms
.Select(x => x.TeleportSpawn.GlobalPosition)
.OrderByDescending(x => x.DistanceTo(target))
.ToArray();
var rngIndex = 1.0;
var rngSteps = rngIndex / roomsSortedByDistance.Count();
foreach (var room in roomsSortedByDistance)
private Vector3 _teleportSpawnPoint;
internal List<IDungeonRoom> Rooms { get; private set; }
public void InitializeDungeon()
{
rngIndex -= rngSteps;
rngDistribution.Add((float)rngIndex);
Rooms = new List<IDungeonRoom>();
DungeonGenerator.Call("generate");
NavigationRegion3D.BakeNavigationMesh();
Rooms = FindAllDungeonRooms([.. GetChildren()], Rooms);
_playerSpawnPoint = RandomizePlayerSpawnPoint();
_teleportSpawnPoint = RandomizeTeleportSpawnPointAwayFromPosition(_playerSpawnPoint.Origin);
}
var result = roomsSortedByDistance[rng.RandWeighted([.. rngDistribution])];
return result;
}
public Transform3D GetPlayerSpawnPoint() => _playerSpawnPoint;
private List<IDungeonRoom> FindAllDungeonRooms(List<Node> nodesToSearch, List<IDungeonRoom> roomsFound)
{
if (nodesToSearch.Count == 0)
return roomsFound;
public Vector3 GetTeleportSpawnPoint() => _teleportSpawnPoint;
foreach (var node in nodesToSearch)
if (node is IDungeonRoom dungeonRoom)
roomsFound.Add(dungeonRoom);
private Transform3D RandomizePlayerSpawnPoint()
{
var rng = new RandomNumberGenerator();
rng.Randomize();
var rngDistribution = new List<float>();
var randomSpawnLocations = Rooms
.Select(x => x.PlayerSpawn);
var godotCollection = new Godot.Collections.Array<Marker3D>(randomSpawnLocations);
var result = godotCollection.PickRandom();
return result.GlobalTransform;
}
return FindAllDungeonRooms(nodesToSearch.SelectMany(x => x.GetChildren()).ToList(), roomsFound);
private Vector3 RandomizeTeleportSpawnPointAwayFromPosition(Vector3 target)
{
var rng = new RandomNumberGenerator();
rng.Randomize();
var rngDistribution = new List<float>();
var roomsSortedByDistance = Rooms
.Select(x => x.TeleportSpawn.GlobalPosition)
.OrderByDescending(x => x.DistanceTo(target))
.ToArray();
var rngIndex = 1.0;
var rngSteps = rngIndex / roomsSortedByDistance.Length;
foreach (var room in roomsSortedByDistance)
{
rngIndex -= rngSteps;
rngDistribution.Add((float)rngIndex);
}
var result = roomsSortedByDistance[rng.RandWeighted([.. rngDistribution])];
return result;
}
private static List<IDungeonRoom> FindAllDungeonRooms(List<Node> nodesToSearch, List<IDungeonRoom> roomsFound)
{
if (nodesToSearch.Count == 0)
return roomsFound;
foreach (var node in nodesToSearch)
if (node is IDungeonRoom dungeonRoom)
roomsFound.Add(dungeonRoom);
return FindAllDungeonRooms(nodesToSearch.SelectMany(x => x.GetChildren()).ToList(), roomsFound);
}
}
}

View File

@@ -5,6 +5,7 @@ using GameJamDungeon;
using Godot;
using System.Linq;
namespace GameJamDungeon;
public interface IDungeonRoom : INode3D
{
public Marker3D PlayerSpawn { get; set; }
@@ -50,7 +51,7 @@ public partial class DungeonRoom : Node3D, IDungeonRoom
break;
numberOfItemsToSpawn--;
var weights = database.Select(x => x.Info.SpawnRate).ToArray();
var weights = database.Select(x => x.SpawnRate).ToArray();
var selectedItem = database[rng.RandWeighted(weights)];
var duplicated = selectedItem.Duplicate((int)DuplicateFlags.UseInstantiation) as Node3D;
duplicated.Position = new Vector3(spawnPoint.Position.X, -1.75f, spawnPoint.Position.Z);

View File

@@ -2,6 +2,7 @@
using Chickensoft.Introspection;
using Godot;
namespace GameJamDungeon;
[Meta(typeof(IAutoNode))]
public partial class Floor0 : Node3D, IDungeonFloor
{

View File

@@ -0,0 +1,12 @@
using Chickensoft.GodotNodeInterfaces;
using Godot;
namespace GameJamDungeon;
public interface IDungeonFloor : INode3D
{
void InitializeDungeon();
public Transform3D GetPlayerSpawnPoint();
public Vector3 GetTeleportSpawnPoint();
}

View File

@@ -2,6 +2,7 @@ using Chickensoft.AutoInject;
using Chickensoft.Introspection;
using Godot;
namespace GameJamDungeon;
[Meta(typeof(IAutoNode))]
public partial class MinimapManager : Area3D
{

View File

@@ -1,8 +1,8 @@
using Chickensoft.AutoInject;
using Chickensoft.Introspection;
using GameJamDungeon;
using Godot;
namespace GameJamDungeon;
[Meta(typeof(IAutoNode))]
public partial class Overworld : Node3D, IDungeonFloor
{

View File

@@ -705,7 +705,7 @@ skeleton = NodePath("")
[node name="PlayerSpawn" type="Marker3D" parent="Antechamber"]
unique_name_in_owner = true
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -8.61757, -2.04983, 0.580412)
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -8.61757, -1.89174, 0.580412)
[node name="ItemSpawnPoints" type="Node3D" parent="Antechamber"]
unique_name_in_owner = true

View File

@@ -1,9 +1,8 @@
[gd_scene load_steps=8 format=3 uid="uid://dl6h1djc27ddl"]
[gd_scene load_steps=7 format=3 uid="uid://dl6h1djc27ddl"]
[ext_resource type="Script" uid="uid://cuhfkyh3d7noa" path="res://src/map/dungeon/code/Overworld.cs" id="1_2ce63"]
[ext_resource type="PackedScene" uid="uid://duis2vhf5ojy3" path="res://src/item_rescue/ItemRescue.tscn" id="2_4ixnb"]
[ext_resource type="PackedScene" uid="uid://tc5kdfoggrng" path="res://src/item_rescue/RescuedItems.tscn" id="3_tbcl3"]
[ext_resource type="PackedScene" uid="uid://1fl6s352e2ej" path="res://src/items/throwable/ThrowableItem.tscn" id="4_wibf0"]
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_pb22g"]
@@ -57,7 +56,4 @@ collision_mask = 3
[node name="CollisionShape3D" type="CollisionShape3D" parent="Spawn Rescued Items/Area3D"]
shape = SubResource("SphereShape3D_tbcl3")
[node name="ThrowableItem" parent="." instance=ExtResource("4_wibf0")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.5, 0)
[connection signal="body_entered" from="Spawn Rescued Items/Area3D" to="Rescued Items" method="OnSpawnItemsEntered"]

View File

@@ -4,6 +4,8 @@ using Chickensoft.Introspection;
using GameJamDungeon;
using Godot;
namespace GameJamDungeon;
public interface IMenu : IControl
{
event Menu.NewGameEventHandler NewGame;

View File

@@ -3,28 +3,27 @@ using Chickensoft.GodotNodeInterfaces;
using Chickensoft.Introspection;
using Godot;
namespace GameJamDungeon
namespace GameJamDungeon;
public interface ISplash : IControl;
[Meta(typeof(IAutoNode))]
public partial class Splash : Control, ISplash
{
public interface ISplash : IControl;
public override void _Notification(int what) => this.Notify(what);
[Meta(typeof(IAutoNode))]
public partial class Splash : Control, ISplash
{
public override void _Notification(int what) => this.Notify(what);
[Dependency]
public IAppRepo AppRepo => this.DependOn<IAppRepo>();
[Dependency]
public IAppRepo AppRepo => this.DependOn<IAppRepo>();
[Node]
public IAnimationPlayer AnimationPlayer { get; set; } = default!;
[Node]
public IAnimationPlayer AnimationPlayer { get; set; } = default!;
public void OnReady() =>
AnimationPlayer.AnimationFinished += OnAnimationFinished;
public void OnReady() =>
AnimationPlayer.AnimationFinished += OnAnimationFinished;
public void OnExitTree()
=> AnimationPlayer.AnimationFinished -= OnAnimationFinished;
public void OnExitTree()
=> AnimationPlayer.AnimationFinished -= OnAnimationFinished;
public void OnAnimationFinished(StringName name)
=> AppRepo.SkipSplashScreen();
}
public void OnAnimationFinished(StringName name)
=> AppRepo.SkipSplashScreen();
}

Some files were not shown because too many files have changed in this diff Show More