Reorganize SFX

This commit is contained in:
2025-03-10 22:13:09 -07:00
parent ed12c1cf15
commit 192f2eb316
29 changed files with 590 additions and 611 deletions

View File

@@ -15,6 +15,8 @@ public partial class InGameAudioLogic
public readonly record struct PlayPlayerAttackWallSound; public readonly record struct PlayPlayerAttackWallSound;
public readonly record struct PlayPlayerAttackEnemySound;
public readonly record struct PlayMenuScrollSound; public readonly record struct PlayMenuScrollSound;
public readonly record struct PlayEquipSound; public readonly record struct PlayEquipSound;

View File

@@ -23,6 +23,8 @@ public interface IGameRepo : IDisposable
event Action? PlayerAttackedWall; event Action? PlayerAttackedWall;
event Action? PlayerAttackedEnemy;
void Pause(); void Pause();
void Resume(); void Resume();
@@ -43,6 +45,8 @@ public interface IGameRepo : IDisposable
public void OnPlayerAttackedWall(); public void OnPlayerAttackedWall();
public void OnPlayerAttackedEnemy();
public void CloseInventory(); public void CloseInventory();
public void GameEnded(); public void GameEnded();
@@ -61,6 +65,7 @@ public class GameRepo : IGameRepo
public event Action<InventoryItem>? RemoveItemFromInventoryEvent; public event Action<InventoryItem>? RemoveItemFromInventoryEvent;
public event Action? PlayerAttack; public event Action? PlayerAttack;
public event Action? PlayerAttackedWall; public event Action? PlayerAttackedWall;
public event Action? PlayerAttackedEnemy;
public IAutoProp<bool> IsPaused => _isPaused; public IAutoProp<bool> IsPaused => _isPaused;
private readonly AutoProp<bool> _isPaused; private readonly AutoProp<bool> _isPaused;
@@ -97,6 +102,7 @@ public class GameRepo : IGameRepo
public void EndDoubleExp() public void EndDoubleExp()
{ {
AnnounceMessageOnMainScreen("Experience points effect wore off."); AnnounceMessageOnMainScreen("Experience points effect wore off.");
DoubleExpTimeEnd?.Invoke();
ExpRate = 1; ExpRate = 1;
} }
@@ -125,6 +131,11 @@ public class GameRepo : IGameRepo
PlayerAttackedWall?.Invoke(); PlayerAttackedWall?.Invoke();
} }
public void OnPlayerAttackedEnemy()
{
PlayerAttackedEnemy?.Invoke();
}
public void CloseInventory() public void CloseInventory()
{ {
CloseInventoryEvent?.Invoke(); CloseInventoryEvent?.Invoke();

View File

@@ -1,4 +1,4 @@
using Chickensoft.AutoInject; using Chickensoft.AutoInject;
using Chickensoft.GodotNodeInterfaces; using Chickensoft.GodotNodeInterfaces;
using Chickensoft.Introspection; using Chickensoft.Introspection;
using Godot; using Godot;
@@ -38,80 +38,80 @@ public partial class App : CanvasLayer, IApp
public void Initialize() public void Initialize()
{ {
var container = new SimpleInjector.Container(); var container = new SimpleInjector.Container();
container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle(); container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
container.Register<IAppRepo, AppRepo>(Lifestyle.Singleton); container.Register<IAppRepo, AppRepo>(Lifestyle.Singleton);
container.Register<IAppLogic, AppLogic>(Lifestyle.Singleton); container.Register<IAppLogic, AppLogic>(Lifestyle.Singleton);
container.Verify(); container.Verify();
Instantiator = new Instantiator(GetTree()); Instantiator = new Instantiator(GetTree());
AppRepo = container.GetInstance<IAppRepo>(); AppRepo = container.GetInstance<IAppRepo>();
AppLogic = container.GetInstance<IAppLogic>(); AppLogic = container.GetInstance<IAppLogic>();
AppLogic.Set(AppRepo); AppLogic.Set(AppRepo);
AppLogic.Set(new AppLogic.Data()); AppLogic.Set(new AppLogic.Data());
Menu.NewGame += OnNewGame; Menu.NewGame += OnNewGame;
Menu.LoadGame += OnLoadGame; Menu.LoadGame += OnLoadGame;
Menu.Quit += OnQuit; Menu.Quit += OnQuit;
AnimationPlayer.AnimationFinished += OnAnimationFinished; AnimationPlayer.AnimationFinished += OnAnimationFinished;
Input.MouseMode = Input.MouseModeEnum.Visible; Input.MouseMode = Input.MouseModeEnum.Visible;
this.Provide(); this.Provide();
} }
public void OnReady() public void OnReady()
{ {
AppBinding = AppLogic.Bind(); AppBinding = AppLogic.Bind();
AppBinding AppBinding
.Handle((in AppLogic.Output.ShowSplashScreen _) => .Handle((in AppLogic.Output.ShowSplashScreen _) =>
{ {
HideMenus(); HideMenus();
BlankScreen.Hide(); BlankScreen.Hide();
Splash.Show(); Splash.Show();
}) })
.Handle((in AppLogic.Output.HideSplashScreen _) => .Handle((in AppLogic.Output.HideSplashScreen _) =>
{ {
BlankScreen.Show(); BlankScreen.Show();
FadeToBlack(); FadeToBlack();
}) })
.Handle((in AppLogic.Output.SetupGameScene _) => .Handle((in AppLogic.Output.SetupGameScene _) =>
{ {
Game = Instantiator.LoadAndInstantiate<Game>(GAME_SCENE_PATH); Game = Instantiator.LoadAndInstantiate<Game>(GAME_SCENE_PATH);
GameWindow.AddChildEx(Game); GameWindow.AddChildEx(Game);
Instantiator.SceneTree.Paused = false; Instantiator.SceneTree.Paused = false;
}) })
.Handle((in AppLogic.Output.ShowMainMenu _) => .Handle((in AppLogic.Output.ShowMainMenu _) =>
{ {
// Load everything while we're showing a black screen, then fade in. // Load everything while we're showing a black screen, then fade in.
HideMenus(); HideMenus();
Menu.Show(); Menu.Show();
FadeInFromBlack(); FadeInFromBlack();
Menu.NewGameButton.GrabFocus(); Menu.NewGameButton.GrabFocus();
}) })
.Handle((in AppLogic.Output.FadeToBlack _) => FadeToBlack()) .Handle((in AppLogic.Output.FadeToBlack _) => FadeToBlack())
.Handle((in AppLogic.Output.HideGame _) => FadeToBlack()) .Handle((in AppLogic.Output.HideGame _) => FadeToBlack())
.Handle((in AppLogic.Output.ShowGame _) => .Handle((in AppLogic.Output.ShowGame _) =>
{ {
HideMenus(); HideMenus();
FadeInFromBlack(); FadeInFromBlack();
}) })
.Handle((in AppLogic.Output.StartLoadingSaveFile _) => .Handle((in AppLogic.Output.StartLoadingSaveFile _) =>
{ {
Game.SaveFileLoaded += OnSaveFileLoaded; Game.SaveFileLoaded += OnSaveFileLoaded;
Game.LoadExistingGame(); Game.LoadExistingGame();
}) })
.Handle((in AppLogic.Output.ExitGame _) => .Handle((in AppLogic.Output.ExitGame _) =>
{ {
GetTree().Quit(); GetTree().Quit();
}); });
AppLogic.Start(); AppLogic.Start();
} }
public void OnNewGame() => AppLogic.Input(new AppLogic.Input.NewGame()); public void OnNewGame() => AppLogic.Input(new AppLogic.Input.NewGame());
@@ -122,44 +122,44 @@ public partial class App : CanvasLayer, IApp
public void OnSaveFileLoaded() public void OnSaveFileLoaded()
{ {
Game.SaveFileLoaded -= OnSaveFileLoaded; Game.SaveFileLoaded -= OnSaveFileLoaded;
AppLogic.Input(new AppLogic.Input.SaveFileLoaded()); AppLogic.Input(new AppLogic.Input.SaveFileLoaded());
} }
public void FadeInFromBlack() public void FadeInFromBlack()
{ {
BlankScreen.Show(); BlankScreen.Show();
AnimationPlayer.Play("fade_in"); AnimationPlayer.Play("fade_in");
} }
public void FadeToBlack() public void FadeToBlack()
{ {
BlankScreen.Show(); BlankScreen.Show();
AnimationPlayer.Play("fade_out"); AnimationPlayer.Play("fade_out");
} }
public void HideMenus() public void HideMenus()
{ {
Splash.Hide(); Splash.Hide();
Menu.Hide(); Menu.Hide();
} }
public void OnAnimationFinished(StringName animation) public void OnAnimationFinished(StringName animation)
{ {
if (animation == "fade_in") if (animation == "fade_in")
{ {
AppLogic.Input(new AppLogic.Input.FadeInFinished()); AppLogic.Input(new AppLogic.Input.FadeInFinished());
BlankScreen.Hide(); BlankScreen.Hide();
return; return;
} }
AppLogic.Input(new AppLogic.Input.FadeOutFinished()); AppLogic.Input(new AppLogic.Input.FadeOutFinished());
} }
public void OnExitTree() public void OnExitTree()
{ {
AppLogic.Stop(); AppLogic.Stop();
AppBinding.Dispose(); AppBinding.Dispose();
AppRepo.Dispose(); AppRepo.Dispose();
} }
} }

View File

@@ -37,6 +37,8 @@ public partial class InGameAudio : Node
[Node] public IAudioStreamPlayer PlayerAttackWallSFX { get; set; } = default!; [Node] public IAudioStreamPlayer PlayerAttackWallSFX { get; set; } = default!;
[Node] public IAudioStreamPlayer PlayerAttackEnemySFX { get; set; } = default!;
[Node] public IAudioStreamPlayer MenuScrollSFX { get; set; } = default!; [Node] public IAudioStreamPlayer MenuScrollSFX { get; set; } = default!;
[Node] public IAudioStreamPlayer EquipSFX { get; set; } = default!; [Node] public IAudioStreamPlayer EquipSFX { get; set; } = default!;
@@ -77,7 +79,8 @@ public partial class InGameAudio : Node
.Handle((in InGameAudioLogic.Output.PlayHealingItemSound _) => PlayHealingItemSound()) .Handle((in InGameAudioLogic.Output.PlayHealingItemSound _) => PlayHealingItemSound())
.Handle((in InGameAudioLogic.Output.PlayTeleportSound _) => PlayTeleportSound()) .Handle((in InGameAudioLogic.Output.PlayTeleportSound _) => PlayTeleportSound())
.Handle((in InGameAudioLogic.Output.PlayPlayerAttackSound _) => { PlayerAttackSFX.Stop(); PlayerAttackSFX.Play(); }) .Handle((in InGameAudioLogic.Output.PlayPlayerAttackSound _) => { PlayerAttackSFX.Stop(); PlayerAttackSFX.Play(); })
.Handle((in InGameAudioLogic.Output.PlayPlayerAttackWallSound _) => { PlayerAttackWallSFX.Stop(); PlayerAttackWallSFX.Play(); }); .Handle((in InGameAudioLogic.Output.PlayPlayerAttackWallSound _) => { PlayerAttackWallSFX.Stop(); PlayerAttackWallSFX.Play(); })
.Handle((in InGameAudioLogic.Output.PlayPlayerAttackEnemySound _) => { PlayerAttackEnemySFX.Stop(); PlayerAttackEnemySFX.Play(); });
InGameAudioLogic.Start(); InGameAudioLogic.Start();
} }

View File

@@ -3,17 +3,17 @@
[ext_resource type="Script" uid="uid://2mnouyn1jcqs" path="res://src/audio/InGameAudio.cs" id="1_gpmcr"] [ext_resource type="Script" uid="uid://2mnouyn1jcqs" path="res://src/audio/InGameAudio.cs" id="1_gpmcr"]
[ext_resource type="AudioStream" uid="uid://dfu0fksb6slhx" path="res://src/audio/music/droney.mp3" id="2_8hfyr"] [ext_resource type="AudioStream" uid="uid://dfu0fksb6slhx" path="res://src/audio/music/droney.mp3" id="2_8hfyr"]
[ext_resource type="AudioStream" uid="uid://d2jrktp06xsba" path="res://src/audio/music/crossing-the-gate.mp3" id="3_wbmd6"] [ext_resource type="AudioStream" uid="uid://d2jrktp06xsba" path="res://src/audio/music/crossing-the-gate.mp3" id="3_wbmd6"]
[ext_resource type="Script" path="res://src/audio/BGMPlayer.cs" id="3_wtvpb"] [ext_resource type="Script" uid="uid://d2usinntpmcry" path="res://src/audio/BGMPlayer.cs" id="3_wtvpb"]
[ext_resource type="AudioStream" uid="uid://dn2e2hqujlia1" path="res://src/audio/music/tar-winds.mp3" id="4_surnl"] [ext_resource type="AudioStream" uid="uid://dn2e2hqujlia1" path="res://src/audio/music/tar-winds.mp3" id="4_surnl"]
[ext_resource type="AudioStream" uid="uid://t3g04u722f2k" path="res://src/audio/music/useless immune system-1.mp3" id="6_agr3r"] [ext_resource type="AudioStream" uid="uid://t3g04u722f2k" path="res://src/audio/music/useless immune system-1.mp3" id="6_agr3r"]
[ext_resource type="AudioStream" uid="uid://cn8cugshq3o8k" path="res://src/audio/sfx/PlayerHitWallSFX.wav" id="7_8vh2f"] [ext_resource type="AudioStream" uid="uid://cn8cugshq3o8k" path="res://src/audio/sfx/PlayerHitWallSFX.wav" id="7_8vh2f"]
[ext_resource type="AudioStream" uid="uid://dor0in0x2fg48" path="res://src/audio/sfx/TempFFVII/menu-move.ogg" id="7_777nl"] [ext_resource type="AudioStream" uid="uid://dor0in0x2fg48" path="res://src/audio/sfx/MenuScrollSFX.ogg" id="7_777nl"]
[ext_resource type="AudioStream" uid="uid://d1mlduwauechv" path="res://src/audio/sfx/PlayerAttackSFX.wav" id="7_wtvpb"] [ext_resource type="AudioStream" uid="uid://d1mlduwauechv" path="res://src/audio/sfx/PlayerAttackSFX.wav" id="7_wtvpb"]
[ext_resource type="AudioStream" uid="uid://r1tryiit38i8" path="res://src/audio/sfx/TempFFVII/menu-back.ogg" id="8_1xcgo"] [ext_resource type="AudioStream" uid="uid://r1tryiit38i8" path="res://src/audio/sfx/MenuBackSFX.ogg" id="8_1xcgo"]
[ext_resource type="AudioStream" uid="uid://bjj61s8q2gwb8" path="res://src/audio/sfx/TempFFVII/junction.ogg" id="8_kwybb"] [ext_resource type="AudioStream" uid="uid://bjj61s8q2gwb8" path="res://src/audio/sfx/EquipSFX.ogg" id="8_kwybb"]
[ext_resource type="AudioStream" uid="uid://myx4s8lmarc2" path="res://src/audio/sfx/TempFFVII/something-earned.ogg" id="10_3lcw5"] [ext_resource type="AudioStream" uid="uid://myx4s8lmarc2" path="res://src/audio/sfx/HealSFX.ogg" id="10_3lcw5"]
[ext_resource type="AudioStream" uid="uid://dci08kmwsu6k1" path="res://src/audio/sfx/TempFFVII/teleport.ogg" id="11_offhc"] [ext_resource type="AudioStream" uid="uid://dci08kmwsu6k1" path="res://src/audio/sfx/ExitSFX.ogg" id="11_offhc"]
[ext_resource type="AudioStream" uid="uid://d3sn7c614uj2n" path="res://src/audio/sfx/TempFFVII/sort.ogg" id="12_wprjr"] [ext_resource type="AudioStream" uid="uid://d3sn7c614uj2n" path="res://src/audio/sfx/SortSFX.ogg" id="12_wprjr"]
[node name="InGameAudio" type="Node"] [node name="InGameAudio" type="Node"]
process_mode = 3 process_mode = 3

View File

@@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://bjj61s8q2gwb8"
path="res://.godot/imported/EquipSFX.ogg-6a8dd8843f1eb38662d6d370e4b2f930.oggvorbisstr"
[deps]
source_file="res://src/audio/sfx/EquipSFX.ogg"
dest_files=["res://.godot/imported/EquipSFX.ogg-6a8dd8843f1eb38662d6d370e4b2f930.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

View File

@@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://dci08kmwsu6k1"
path="res://.godot/imported/ExitSFX.ogg-c16c2cdec4e3d13c44aa79cc98a70d7b.oggvorbisstr"
[deps]
source_file="res://src/audio/sfx/ExitSFX.ogg"
dest_files=["res://.godot/imported/ExitSFX.ogg-c16c2cdec4e3d13c44aa79cc98a70d7b.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

View File

@@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://myx4s8lmarc2"
path="res://.godot/imported/HealSFX.ogg-8e33fc432d3aa613fb5d921400562a51.oggvorbisstr"
[deps]
source_file="res://src/audio/sfx/HealSFX.ogg"
dest_files=["res://.godot/imported/HealSFX.ogg-8e33fc432d3aa613fb5d921400562a51.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

View File

@@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://r1tryiit38i8"
path="res://.godot/imported/MenuBackSFX.ogg-940782881d58f1c4c5f95977b5f42009.oggvorbisstr"
[deps]
source_file="res://src/audio/sfx/MenuBackSFX.ogg"
dest_files=["res://.godot/imported/MenuBackSFX.ogg-940782881d58f1c4c5f95977b5f42009.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

View File

@@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://dor0in0x2fg48"
path="res://.godot/imported/MenuScrollSFX.ogg-b1800108be1a856f464e62089f5b0279.oggvorbisstr"
[deps]
source_file="res://src/audio/sfx/MenuScrollSFX.ogg"
dest_files=["res://.godot/imported/MenuScrollSFX.ogg-b1800108be1a856f464e62089f5b0279.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

View File

@@ -0,0 +1,19 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://d3sn7c614uj2n"
path="res://.godot/imported/SortSFX.ogg-7f616a0a2af3902cddc2ea22947a5f02.oggvorbisstr"
[deps]
source_file="res://src/audio/sfx/SortSFX.ogg"
dest_files=["res://.godot/imported/SortSFX.ogg-7f616a0a2af3902cddc2ea22947a5f02.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

View File

@@ -1,23 +0,0 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://t28qhjuibv3f"
valid=false
[deps]
source_file="res://src/audio/sfx/TempFFVII/Equip.wav"
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=0

View File

@@ -1,19 +0,0 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://bjj61s8q2gwb8"
path="res://.godot/imported/junction.ogg-f4350086a08e048d3008edcdc25abf96.oggvorbisstr"
[deps]
source_file="res://src/audio/sfx/TempFFVII/junction.ogg"
dest_files=["res://.godot/imported/junction.ogg-f4350086a08e048d3008edcdc25abf96.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

View File

@@ -1,19 +0,0 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://r1tryiit38i8"
path="res://.godot/imported/menu-back.ogg-3ec385c1a9cfaaa1be4ba85197708f0c.oggvorbisstr"
[deps]
source_file="res://src/audio/sfx/TempFFVII/menu-back.ogg"
dest_files=["res://.godot/imported/menu-back.ogg-3ec385c1a9cfaaa1be4ba85197708f0c.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

View File

@@ -1,19 +0,0 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://dor0in0x2fg48"
path="res://.godot/imported/menu-move.ogg-d71b0989e00dd1d4488a72c7dde3d41d.oggvorbisstr"
[deps]
source_file="res://src/audio/sfx/TempFFVII/menu-move.ogg"
dest_files=["res://.godot/imported/menu-move.ogg-d71b0989e00dd1d4488a72c7dde3d41d.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

View File

@@ -1,24 +0,0 @@
[remap]
importer="wav"
type="AudioStreamWAV"
uid="uid://tce7m18vkgao"
path="res://.godot/imported/menu.wav-3931712b8a8483f269018c4a880368b2.sample"
[deps]
source_file="res://src/audio/sfx/TempFFVII/menu.wav"
dest_files=["res://.godot/imported/menu.wav-3931712b8a8483f269018c4a880368b2.sample"]
[params]
force/8_bit=false
force/mono=false
force/max_rate=false
force/max_rate_hz=44100
edit/trim=false
edit/normalize=false
edit/loop_mode=0
edit/loop_begin=0
edit/loop_end=-1
compress/mode=0

View File

@@ -1,19 +0,0 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://myx4s8lmarc2"
path="res://.godot/imported/something-earned.ogg-150627e4deb45db30e8dd2f98ddcc5a8.oggvorbisstr"
[deps]
source_file="res://src/audio/sfx/TempFFVII/something-earned.ogg"
dest_files=["res://.godot/imported/something-earned.ogg-150627e4deb45db30e8dd2f98ddcc5a8.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

View File

@@ -1,19 +0,0 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://d3sn7c614uj2n"
path="res://.godot/imported/sort.ogg-cb2a2c4769c8e6574221a3c313e75bcf.oggvorbisstr"
[deps]
source_file="res://src/audio/sfx/TempFFVII/sort.ogg"
dest_files=["res://.godot/imported/sort.ogg-cb2a2c4769c8e6574221a3c313e75bcf.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

View File

@@ -1,19 +0,0 @@
[remap]
importer="oggvorbisstr"
type="AudioStreamOggVorbis"
uid="uid://dci08kmwsu6k1"
path="res://.godot/imported/teleport.ogg-9024f7b675b201a391dee183da020b1d.oggvorbisstr"
[deps]
source_file="res://src/audio/sfx/TempFFVII/teleport.ogg"
dest_files=["res://.godot/imported/teleport.ogg-9024f7b675b201a391dee183da020b1d.oggvorbisstr"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

View File

@@ -20,57 +20,57 @@ public partial class Sproingy : Enemy, IHasPrimaryAttack, ICanPatrol
public void OnReady() public void OnReady()
{ {
SetPhysicsProcess(true); SetPhysicsProcess(true);
((EnemyModelView2D)_enemyModelView).Hitbox.AreaEntered += Hitbox_AreaEntered; ((EnemyModelView2D)_enemyModelView).Hitbox.AreaEntered += Hitbox_AreaEntered;
} }
public void OnPhysicsProcess(double delta) public void OnPhysicsProcess(double delta)
{ {
_enemyLogic.Input(new EnemyLogic.Input.PhysicsTick(delta)); _enemyLogic.Input(new EnemyLogic.Input.PhysicsTick(delta));
if (_enemyLogic.Value is not EnemyLogic.State.Activated) if (_enemyLogic.Value is not EnemyLogic.State.Activated)
return; return;
if (_enemyLogic.Value is EnemyLogic.State.FollowPlayer && GlobalPosition.DistanceTo(_player.CurrentPosition) < 1.5f) if (_enemyLogic.Value is EnemyLogic.State.FollowPlayer && GlobalPosition.DistanceTo(_player.CurrentPosition) < 1.5f)
_enemyLogic.Input(new EnemyLogic.Input.StartAttacking()); _enemyLogic.Input(new EnemyLogic.Input.StartAttacking());
if (_enemyLogic.Value is EnemyLogic.State.FollowPlayer && GlobalPosition.DistanceTo(_player.CurrentPosition) > 30f) if (_enemyLogic.Value is EnemyLogic.State.FollowPlayer && GlobalPosition.DistanceTo(_player.CurrentPosition) > 30f)
_enemyLogic.Input(new EnemyLogic.Input.LostPlayer()); _enemyLogic.Input(new EnemyLogic.Input.LostPlayer());
if (_enemyLogic.Value is EnemyLogic.State.Attacking && GlobalPosition.DistanceTo(_player.CurrentPosition) > 3f) if (_enemyLogic.Value is EnemyLogic.State.Attacking && GlobalPosition.DistanceTo(_player.CurrentPosition) > 3f)
_enemyLogic.Input(new EnemyLogic.Input.Alerted()); _enemyLogic.Input(new EnemyLogic.Input.Alerted());
_navigationAgentClient.CalculateVelocity(GlobalPosition, true); _navigationAgentClient.CalculateVelocity(GlobalPosition, true);
base._PhysicsProcess(delta); base._PhysicsProcess(delta);
} }
public override void TakeAction() public override void TakeAction()
{ {
PrimaryAttack(); PrimaryAttack();
} }
public void PrimaryAttack() public void PrimaryAttack()
{ {
_enemyModelView.PlayPrimaryAttackAnimation(); _enemyModelView.PlayPrimaryAttackAnimation();
} }
public override void SetTarget(Vector3 target) => _navigationAgentClient.SetTarget(target); public override void SetTarget(Vector3 target) => _navigationAgentClient.SetTarget(target);
public void Patrol() public void Patrol()
{ {
var rng = new RandomNumberGenerator(); var rng = new RandomNumberGenerator();
rng.Randomize(); rng.Randomize();
var randomizedSpot = new Vector3(rng.RandfRange(-5.0f, 5.0f), 0, rng.RandfRange(-5.0f, 5.0f)); var randomizedSpot = new Vector3(rng.RandfRange(-5.0f, 5.0f), 0, rng.RandfRange(-5.0f, 5.0f));
_enemyLogic.Input(new EnemyLogic.Input.PatrolToRandomSpot(GlobalPosition + randomizedSpot)); _enemyLogic.Input(new EnemyLogic.Input.PatrolToRandomSpot(GlobalPosition + randomizedSpot));
_enemyLogic.Input(new EnemyLogic.Input.StartPatrol()); _enemyLogic.Input(new EnemyLogic.Input.StartPatrol());
} }
private void Hitbox_AreaEntered(Area3D area) private void Hitbox_AreaEntered(Area3D area)
{ {
var target = area.GetOwner(); var target = area.GetOwner();
if (target is IPlayer player) if (target is IPlayer player)
{ {
var damage = _enemyStatResource.CurrentAttack * PrimaryAttackElementalDamageBonus; var damage = _enemyStatResource.CurrentAttack * PrimaryAttackElementalDamageBonus;
player.TakeDamage(damage, PrimaryAttackElementalType, BattleExtensions.IsCriticalHit(_enemyStatResource.Luck)); player.TakeDamage(damage, PrimaryAttackElementalType, BattleExtensions.IsCriticalHit(_enemyStatResource.Luck));
} }
} }
} }

View File

@@ -27,13 +27,13 @@ metadata/_custom_type_script = ExtResource("2_oln85")
radius = 0.106078 radius = 0.106078
height = 1.23076 height = 1.23076
[sub_resource type="SphereShape3D" id="SphereShape3D_8vcnq"]
radius = 0.57308
[sub_resource type="CylinderShape3D" id="CylinderShape3D_jbgmx"] [sub_resource type="CylinderShape3D" id="CylinderShape3D_jbgmx"]
height = 5.0 height = 5.0
radius = 1.0 radius = 1.0
[sub_resource type="SphereShape3D" id="SphereShape3D_8vcnq"]
radius = 0.57308
[node name="Sproingy" type="CharacterBody3D"] [node name="Sproingy" type="CharacterBody3D"]
process_mode = 1 process_mode = 1
collision_layer = 10 collision_layer = 10
@@ -44,46 +44,54 @@ axis_lock_angular_z = true
script = ExtResource("1_xsluo") script = ExtResource("1_xsluo")
_enemyStatResource = SubResource("Resource_oln85") _enemyStatResource = SubResource("Resource_oln85")
[node name="NavigationAgentClient" parent="." instance=ExtResource("3_ut5m2")]
unique_name_in_owner = true
[node name="CollisionShape" type="CollisionShape3D" parent="."] [node name="CollisionShape" type="CollisionShape3D" parent="."]
unique_name_in_owner = true unique_name_in_owner = true
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0) transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0)
shape = SubResource("CapsuleShape3D_cwfph") shape = SubResource("CapsuleShape3D_cwfph")
[node name="LineOfSight" type="Area3D" parent="."] [node name="Navigation" type="Node3D" parent="."]
[node name="NavigationAgentClient" parent="Navigation" instance=ExtResource("3_ut5m2")]
unique_name_in_owner = true
[node name="Collision" type="Node3D" parent="."]
[node name="Collision" type="Area3D" parent="Collision"]
collision_layer = 2048
collision_mask = 0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Collision/Collision"]
shape = SubResource("SphereShape3D_8vcnq")
[node name="LineOfSight" type="Area3D" parent="Collision"]
unique_name_in_owner = true unique_name_in_owner = true
transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0) transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0)
collision_layer = 2 collision_layer = 2
collision_mask = 2 collision_mask = 2
[node name="CollisionShape3D" type="CollisionShape3D" parent="LineOfSight"] [node name="CollisionShape3D" type="CollisionShape3D" parent="Collision/LineOfSight"]
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0, -2) transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0, -2)
shape = SubResource("CylinderShape3D_jbgmx") shape = SubResource("CylinderShape3D_jbgmx")
[node name="PatrolTimer" type="Timer" parent="."] [node name="Raycast" type="RayCast3D" parent="Collision"]
unique_name_in_owner = true
wait_time = 10.0
autostart = true
[node name="AttackTimer" type="Timer" parent="."]
unique_name_in_owner = true
wait_time = 0.8
autostart = true
[node name="Raycast" type="RayCast3D" parent="."]
unique_name_in_owner = true unique_name_in_owner = true
target_position = Vector3(0, 0, -5) target_position = Vector3(0, 0, -5)
collision_mask = 3 collision_mask = 3
[node name="EnemyModelView" parent="." instance=ExtResource("4_o3b7p")] [node name="Visual" type="Node3D" parent="."]
[node name="EnemyModelView" parent="Visual" instance=ExtResource("4_o3b7p")]
unique_name_in_owner = true unique_name_in_owner = true
transform = Transform3D(1.5, 0, 0, 0, 1.5, 0, 0, 0, 1.5, 0, 0.0862446, 0) transform = Transform3D(1.5, 0, 0, 0, 1.5, 0, 0, 0, 1.5, 0, 0.0862446, 0)
[node name="Collision" type="Area3D" parent="."] [node name="Timers" type="Node" parent="."]
collision_layer = 2048
collision_mask = 0
[node name="CollisionShape3D" type="CollisionShape3D" parent="Collision"] [node name="PatrolTimer" type="Timer" parent="Timers"]
shape = SubResource("SphereShape3D_8vcnq") unique_name_in_owner = true
wait_time = 10.0
autostart = true
[node name="AttackTimer" type="Timer" parent="Timers"]
unique_name_in_owner = true
wait_time = 0.8
autostart = true

View File

@@ -106,506 +106,508 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<ISaveChunk<Play
#region Initialization #region Initialization
public void Initialize() public void Initialize()
{ {
AnimationPlayer.AnimationFinished += OnAnimationFinished; AnimationPlayer.AnimationFinished += OnAnimationFinished;
_expToNextLevel = new Dictionary<int, int> _expToNextLevel = new Dictionary<int, int>
{ {
{ 2, 12 }, { 2, 12 },
{ 3, 39 }, { 3, 39 },
{ 4, 87 }, { 4, 87 },
{ 5, 162 }, { 5, 162 },
{ 6, 270 }, { 6, 270 },
{ 7, 417 }, { 7, 417 },
{ 8, 609 } { 8, 609 }
}; };
} }
public void Setup() public void Setup()
{ {
var container = new SimpleInjector.Container(); var container = new SimpleInjector.Container();
container.Register<IPlayerLogic, PlayerLogic>(Lifestyle.Singleton); container.Register<IPlayerLogic, PlayerLogic>(Lifestyle.Singleton);
container.Verify(); container.Verify();
Settings = new PlayerLogic.Settings() { RotationSpeed = PlayerStatResource.RotationSpeed, MoveSpeed = PlayerStatResource.MoveSpeed, Acceleration = PlayerStatResource.Acceleration }; Settings = new PlayerLogic.Settings() { RotationSpeed = PlayerStatResource.RotationSpeed, MoveSpeed = PlayerStatResource.MoveSpeed, Acceleration = PlayerStatResource.Acceleration };
Stats = new PlayerStatController(); Stats = new PlayerStatController();
Stats.Init( Stats.Init(
new PlayerStats new PlayerStats
{ {
CurrentHP = PlayerStatResource.CurrentHP, CurrentHP = PlayerStatResource.CurrentHP,
MaximumHP = PlayerStatResource.MaximumHP, MaximumHP = PlayerStatResource.MaximumHP,
CurrentVT = PlayerStatResource.CurrentVT, CurrentVT = PlayerStatResource.CurrentVT,
MaximumVT = PlayerStatResource.MaximumVT, MaximumVT = PlayerStatResource.MaximumVT,
CurrentAttack = PlayerStatResource.CurrentAttack, CurrentAttack = PlayerStatResource.CurrentAttack,
BonusAttack = PlayerStatResource.BonusAttack, BonusAttack = PlayerStatResource.BonusAttack,
MaxAttack = PlayerStatResource.MaxAttack, MaxAttack = PlayerStatResource.MaxAttack,
CurrentDefense = PlayerStatResource.CurrentDefense, CurrentDefense = PlayerStatResource.CurrentDefense,
BonusDefense = PlayerStatResource.BonusDefense, BonusDefense = PlayerStatResource.BonusDefense,
MaxDefense = PlayerStatResource.MaxDefense, MaxDefense = PlayerStatResource.MaxDefense,
CurrentExp = PlayerStatResource.CurrentExp, CurrentExp = PlayerStatResource.CurrentExp,
CurrentLevel = PlayerStatResource.CurrentLevel, CurrentLevel = PlayerStatResource.CurrentLevel,
ExpToNextLevel = PlayerStatResource.ExpToNextLevel, ExpToNextLevel = PlayerStatResource.ExpToNextLevel,
Luck = PlayerStatResource.Luck Luck = PlayerStatResource.Luck
}); });
Inventory = new Inventory(); Inventory = new Inventory();
PlayerLogic = container.GetInstance<IPlayerLogic>(); PlayerLogic = container.GetInstance<IPlayerLogic>();
PlayerLogic.Set(this as IPlayer); PlayerLogic.Set(this as IPlayer);
PlayerLogic.Set(Settings); PlayerLogic.Set(Settings);
PlayerLogic.Set(Stats); PlayerLogic.Set(Stats);
PlayerLogic.Set(_gameRepo); PlayerLogic.Set(_gameRepo);
var defaultWeapon = new Weapon(); var defaultWeapon = new Weapon();
defaultWeapon.Stats = _defaultWeapon; defaultWeapon.Stats = _defaultWeapon;
var defaultArmor = new Armor(); var defaultArmor = new Armor();
defaultArmor.Stats = _defaultArmor; defaultArmor.Stats = _defaultArmor;
Inventory.TryAdd(defaultWeapon); Inventory.TryAdd(defaultWeapon);
Inventory.TryAdd(defaultArmor); Inventory.TryAdd(defaultArmor);
EquippedWeapon.Sync += EquippedWeapon_Sync; EquippedWeapon.Sync += EquippedWeapon_Sync;
EquippedArmor.Sync += EquippedArmor_Sync; EquippedArmor.Sync += EquippedArmor_Sync;
EquippedAccessory.Sync += EquippedAccessory_Sync; EquippedAccessory.Sync += EquippedAccessory_Sync;
Stats.CurrentHP.Sync += CurrentHP_Sync; Stats.CurrentHP.Sync += CurrentHP_Sync;
Stats.CurrentExp.Sync += CurrentEXP_Sync; Stats.CurrentExp.Sync += CurrentEXP_Sync;
Equip(defaultWeapon); Equip(defaultWeapon);
Equip(defaultArmor); Equip(defaultArmor);
HealthTimer.WaitTime = _healthTimerWaitTime; HealthTimer.WaitTime = _healthTimerWaitTime;
HealthTimer.Timeout += OnHealthTimerTimeout; HealthTimer.Timeout += OnHealthTimerTimeout;
Hitbox.AreaEntered += Hitbox_AreaEntered; Hitbox.AreaEntered += Hitbox_AreaEntered;
CollisionDetector.AreaEntered += CollisionDetector_AreaEntered; CollisionDetector.AreaEntered += CollisionDetector_AreaEntered;
} }
public void OnResolved() public void OnResolved()
{ {
PlayerChunk = new SaveChunk<PlayerData>( PlayerChunk = new SaveChunk<PlayerData>(
onSave: (chunk) => new PlayerData() onSave: (chunk) => new PlayerData()
{ {
PlayerStats = new PlayerStats() PlayerStats = new PlayerStats()
{ {
CurrentHP = Stats.CurrentHP.Value, CurrentHP = Stats.CurrentHP.Value,
MaximumHP = Stats.MaximumHP.Value, MaximumHP = Stats.MaximumHP.Value,
CurrentVT = Stats.CurrentVT.Value, CurrentVT = Stats.CurrentVT.Value,
MaximumVT = Stats.MaximumVT.Value, MaximumVT = Stats.MaximumVT.Value,
CurrentAttack = Stats.CurrentAttack.Value, CurrentAttack = Stats.CurrentAttack.Value,
BonusAttack = Stats.BonusAttack.Value, BonusAttack = Stats.BonusAttack.Value,
MaxAttack = Stats.MaxAttack.Value, MaxAttack = Stats.MaxAttack.Value,
CurrentDefense = Stats.CurrentDefense.Value, CurrentDefense = Stats.CurrentDefense.Value,
BonusDefense = Stats.BonusDefense.Value, BonusDefense = Stats.BonusDefense.Value,
MaxDefense = Stats.MaxDefense.Value, MaxDefense = Stats.MaxDefense.Value,
CurrentExp = Stats.CurrentExp.Value, CurrentExp = Stats.CurrentExp.Value,
CurrentLevel = Stats.CurrentLevel.Value, CurrentLevel = Stats.CurrentLevel.Value,
ExpToNextLevel = Stats.ExpToNextLevel.Value, ExpToNextLevel = Stats.ExpToNextLevel.Value,
Luck = Stats.Luck.Value Luck = Stats.Luck.Value
}, },
Inventory = Inventory Inventory = Inventory
}, },
onLoad: (chunk, data) => onLoad: (chunk, data) =>
{ {
Stats.Init(data.PlayerStats); Stats.Init(data.PlayerStats);
Inventory = data.Inventory; Inventory = data.Inventory;
} }
); );
PlayerBinding = PlayerLogic.Bind(); PlayerBinding = PlayerLogic.Bind();
PlayerBinding PlayerBinding
.Handle((in PlayerLogic.Output.Animations.Attack output) => .Handle((in PlayerLogic.Output.Animations.Attack output) =>
{ {
if (PlayerIsHittingGeometry()) if (PlayerIsHittingGeometry())
{ {
AnimationPlayer.Play("hit_wall"); AnimationPlayer.Play("hit_wall");
_gameRepo.OnPlayerAttackedWall(); _gameRepo.OnPlayerAttackedWall();
} }
else else
{ {
var attackSpeed = ((Weapon)EquippedWeapon.Value).AttackSpeed; var attackSpeed = ((Weapon)EquippedWeapon.Value).AttackSpeed;
AnimationPlayer.SetSpeedScale((float)attackSpeed); AnimationPlayer.SetSpeedScale((float)attackSpeed);
AnimationPlayer.Play("attack"); AnimationPlayer.Play("attack");
_gameRepo.OnPlayerAttack(); _gameRepo.OnPlayerAttack();
} }
}) })
.Handle((in PlayerLogic.Output.ThrowItem output) => .Handle((in PlayerLogic.Output.ThrowItem output) =>
{ {
}) })
.Handle((in PlayerLogic.Output.Move output) => .Handle((in PlayerLogic.Output.Move output) =>
{ {
Move(output.delta); Move(output.delta);
}); });
GameChunk.AddChunk(PlayerChunk); GameChunk.AddChunk(PlayerChunk);
PlayerLogic.Start(); PlayerLogic.Start();
this.Provide(); this.Provide();
} }
public void OnReady() public void OnReady()
{ {
SetPhysicsProcess(true); SetPhysicsProcess(true);
SwordSlashAnimation.Position = GetViewport().GetVisibleRect().Size / 2; SwordSlashAnimation.Position = GetViewport().GetVisibleRect().Size / 2;
} }
#endregion #endregion
public void Attack() public void Attack()
{ {
PlayerLogic.Input(new PlayerLogic.Input.Attack()); PlayerLogic.Input(new PlayerLogic.Input.Attack());
} }
public void PlayerPause() public void PlayerPause()
{ {
Game.TogglePause(); Game.TogglePause();
} }
public void RaiseHP(int amountToRaise) public void RaiseHP(int amountToRaise)
{ {
Stats.SetMaximumHP(Stats.MaximumHP.Value + amountToRaise); Stats.SetMaximumHP(Stats.MaximumHP.Value + amountToRaise);
Stats.SetCurrentHP(Stats.MaximumHP.Value); Stats.SetCurrentHP(Stats.MaximumHP.Value);
_gameRepo.AnnounceMessageInInventory($"{amountToRaise}MAXHP Up."); _gameRepo.AnnounceMessageInInventory($"{amountToRaise}MAXHP Up.");
} }
public void HealHP(int amountToRestore) public void HealHP(int amountToRestore)
{ {
Stats.SetCurrentHP(Stats.CurrentHP.Value + amountToRestore); Stats.SetCurrentHP(Stats.CurrentHP.Value + amountToRestore);
var raiseString = amountToRestore == 1000 ? "MAX" : $"{amountToRestore}"; var raiseString = amountToRestore == 1000 ? "MAX" : $"{amountToRestore}";
_gameRepo.AnnounceMessageInInventory($"{raiseString}HP Restored."); _gameRepo.AnnounceMessageInInventory($"{raiseString}HP Restored.");
} }
public void RaiseVT(int amountToRaise) public void RaiseVT(int amountToRaise)
{ {
if (Stats.CurrentVT == Stats.MaximumVT) if (Stats.CurrentVT == Stats.MaximumVT)
{ {
Stats.SetMaximumVT(Stats.MaximumVT.Value + amountToRaise); Stats.SetMaximumVT(Stats.MaximumVT.Value + amountToRaise);
Stats.SetCurrentVT(Stats.MaximumVT.Value); Stats.SetCurrentVT(Stats.MaximumVT.Value);
_gameRepo.AnnounceMessageInInventory($"{amountToRaise}MAXVT Up."); _gameRepo.AnnounceMessageInInventory($"{amountToRaise}MAXVT Up.");
} }
} }
public void HealVT(int amountToRestore) public void HealVT(int amountToRestore)
{ {
Stats.SetCurrentVT(Stats.CurrentVT.Value + amountToRestore); Stats.SetCurrentVT(Stats.CurrentVT.Value + amountToRestore);
var raiseString = amountToRestore == 1000 ? "MAX" : $"{amountToRestore}"; var raiseString = amountToRestore == 1000 ? "MAX" : $"{amountToRestore}";
_gameRepo.AnnounceMessageInInventory($"{raiseString}VT Restored."); _gameRepo.AnnounceMessageInInventory($"{raiseString}VT Restored.");
} }
public void ModifyBonusAttack(int amount) public void ModifyBonusAttack(int amount)
{ {
Stats.SetBonusAttack(Stats.BonusAttack.Value + amount); Stats.SetBonusAttack(Stats.BonusAttack.Value + amount);
} }
public void ModifyBonusDefense(int amount) public void ModifyBonusDefense(int amount)
{ {
Stats.SetBonusDefense(Stats.BonusDefense.Value + amount); Stats.SetBonusDefense(Stats.BonusDefense.Value + amount);
} }
public void ModifyMaximumHP(int amount) public void ModifyMaximumHP(int amount)
{ {
Stats.SetMaximumHP(Stats.MaximumHP.Value + amount); Stats.SetMaximumHP(Stats.MaximumHP.Value + amount);
} }
public void ModifyMaximumVT(int amount) public void ModifyMaximumVT(int amount)
{ {
Stats.SetMaximumVT(Stats.MaximumVT.Value + amount); Stats.SetMaximumVT(Stats.MaximumVT.Value + amount);
} }
public void ModifyBonusLuck(double amount) public void ModifyBonusLuck(double amount)
{ {
Stats.SetLuck(Stats.Luck.Value + amount); Stats.SetLuck(Stats.Luck.Value + amount);
} }
public void Move(float delta) public void Move(float delta)
{ {
var rawInput = GlobalInputVector; var rawInput = GlobalInputVector;
var strafeLeftInput = LeftStrafeInputVector; var strafeLeftInput = LeftStrafeInputVector;
var strafeRightInput = RightStrafeInputVector; var strafeRightInput = RightStrafeInputVector;
var transform = Transform; var transform = Transform;
transform.Basis = new Basis(Vector3.Up, Settings.RotationSpeed * -rawInput.X * delta) * transform.Basis; transform.Basis = new Basis(Vector3.Up, Settings.RotationSpeed * -rawInput.X * delta) * transform.Basis;
var moveDirection = new Vector3(strafeRightInput - strafeLeftInput, 0, rawInput.Z).Normalized(); var moveDirection = new Vector3(strafeRightInput - strafeLeftInput, 0, rawInput.Z).Normalized();
var velocity = Basis * moveDirection * Settings.MoveSpeed * Settings.Acceleration; var velocity = Basis * moveDirection * Settings.MoveSpeed * Settings.Acceleration;
_knockbackStrength = _knockbackStrength * 0.9f; _knockbackStrength = _knockbackStrength * 0.9f;
Transform = Transform with { Basis = transform.Basis }; Transform = Transform with { Basis = transform.Basis };
Velocity = velocity + (_knockbackDirection * _knockbackStrength); Velocity = velocity + (_knockbackDirection * _knockbackStrength);
MoveAndSlide(); MoveAndSlide();
} }
public void TeleportPlayer(Transform3D newTransform) public void TeleportPlayer(Transform3D newTransform)
{ {
Transform = newTransform; Transform = newTransform;
} }
public void TakeDamage(double damage, ElementType elementType, bool isCriticalHit = false) public void TakeDamage(double damage, ElementType elementType, bool isCriticalHit = false)
{ {
if (Stats.CurrentHP.Value > 0) if (Stats.CurrentHP.Value > 0)
{ {
damage = CalculateDefenseResistance(damage); damage = CalculateDefenseResistance(damage);
if (isCriticalHit) if (isCriticalHit)
damage *= 2; damage *= 2;
Stats.SetCurrentHP(Stats.CurrentHP.Value - (int)damage); Stats.SetCurrentHP(Stats.CurrentHP.Value - (int)damage);
} }
} }
public void Knockback(float impulse) public void Knockback(float impulse)
{ {
_knockbackStrength = impulse; _knockbackStrength = impulse;
_knockbackDirection = GlobalBasis.Z.Normalized(); _knockbackDirection = GlobalBasis.Z.Normalized();
} }
public void GainExp(double expGained) public void GainExp(double expGained)
{ {
Stats.SetCurrentExp(Stats.CurrentExp.Value + expGained); Stats.SetCurrentExp(Stats.CurrentExp.Value + expGained);
} }
public void LevelUp() public void LevelUp()
{ {
var nextLevel = Stats.CurrentLevel.Value + 1; var nextLevel = Stats.CurrentLevel.Value + 1;
var expToNextLevel = _expToNextLevel[nextLevel]; var expToNextLevel = _expToNextLevel[nextLevel];
var newCurrentExp = Mathf.Max(Stats.CurrentExp.Value - Stats.ExpToNextLevel.Value, 0); var newCurrentExp = Mathf.Max(Stats.CurrentExp.Value - Stats.ExpToNextLevel.Value, 0);
Stats.SetCurrentLevel(nextLevel); Stats.SetCurrentLevel(nextLevel);
Stats.SetExpToNextLevel(expToNextLevel); Stats.SetExpToNextLevel(expToNextLevel);
Stats.SetCurrentExp(newCurrentExp); Stats.SetCurrentExp(newCurrentExp);
} }
public void Die() => PlayerLogic.Input(new PlayerLogic.Input.Die()); public void Die() => PlayerLogic.Input(new PlayerLogic.Input.Die());
public override void _UnhandledInput(InputEvent @event) public override void _UnhandledInput(InputEvent @event)
{ {
if (@event.IsActionPressed(GameInputs.Pause)) if (@event.IsActionPressed(GameInputs.Pause))
PlayerPause(); PlayerPause();
if (@event.IsActionPressed(GameInputs.Attack)) if (@event.IsActionPressed(GameInputs.Attack))
Attack(); Attack();
} }
public void OnPhysicsProcess(double delta) public void OnPhysicsProcess(double delta)
{ {
PlayerLogic.Input(new PlayerLogic.Input.PhysicsTick(delta)); PlayerLogic.Input(new PlayerLogic.Input.PhysicsTick(delta));
PlayerLogic.Input(new PlayerLogic.Input.Moved(GlobalPosition, GlobalTransform)); PlayerLogic.Input(new PlayerLogic.Input.Moved(GlobalPosition, GlobalTransform));
} }
public void Equip(EquipableItem equipable) public void Equip(EquipableItem equipable)
{ {
if (equipable is Weapon weapon) if (equipable is Weapon weapon)
{ {
Unequip(_equippedWeapon.Value); Unequip(_equippedWeapon.Value);
weapon.IsEquipped = true; weapon.IsEquipped = true;
_equippedWeapon.OnNext(weapon); _equippedWeapon.OnNext(weapon);
} }
else if (equipable is Armor armor) else if (equipable is Armor armor)
{ {
Unequip(_equippedArmor.Value); Unequip(_equippedArmor.Value);
armor.IsEquipped = true; armor.IsEquipped = true;
_equippedArmor.OnNext(armor); _equippedArmor.OnNext(armor);
} }
else if (equipable is Accessory accessory) else if (equipable is Accessory accessory)
{ {
Unequip(_equippedAccessory.Value); Unequip(_equippedAccessory.Value);
accessory.IsEquipped = true; accessory.IsEquipped = true;
_equippedAccessory.OnNext(accessory); _equippedAccessory.OnNext(accessory);
} }
else else
throw new NotImplementedException("Item type is not supported."); throw new NotImplementedException("Item type is not supported.");
} }
public void Unequip(EquipableItem equipable) public void Unequip(EquipableItem equipable)
{ {
if (equipable is Weapon weapon) if (equipable is Weapon weapon)
{ {
weapon.IsEquipped = false; weapon.IsEquipped = false;
ModifyBonusAttack(-weapon.Damage); ModifyBonusAttack(-weapon.Damage);
_equippedWeapon.OnNext(new Weapon()); _equippedWeapon.OnNext(new Weapon());
} }
else if (equipable is Armor armor) else if (equipable is Armor armor)
{ {
armor.IsEquipped = false; armor.IsEquipped = false;
ModifyBonusDefense(-armor.Defense); ModifyBonusDefense(-armor.Defense);
_equippedArmor.OnNext(new Armor()); _equippedArmor.OnNext(new Armor());
} }
else if (equipable is Accessory accessory) else if (equipable is Accessory accessory)
{ {
accessory.IsEquipped = false; accessory.IsEquipped = false;
ModifyMaximumHP(-accessory.MaxHPUp); ModifyMaximumHP(-accessory.MaxHPUp);
ModifyMaximumVT(-accessory.MaxVTUp); ModifyMaximumVT(-accessory.MaxVTUp);
ModifyBonusAttack(-accessory.ATKUp); ModifyBonusAttack(-accessory.ATKUp);
ModifyBonusDefense(-accessory.DEFUp); ModifyBonusDefense(-accessory.DEFUp);
ModifyBonusLuck(-accessory.LuckUp); ModifyBonusLuck(-accessory.LuckUp);
_equippedAccessory.OnNext(new Accessory()); _equippedAccessory.OnNext(new Accessory());
} }
else else
throw new NotImplementedException("Item type is not supported."); throw new NotImplementedException("Item type is not supported.");
if (equipable.ItemTag == ItemTag.BreaksOnChange) if (equipable.ItemTag == ItemTag.BreaksOnChange)
Inventory.Remove(equipable); Inventory.Remove(equipable);
} }
private static Vector3 GlobalInputVector private static Vector3 GlobalInputVector
{ {
get get
{ {
var rawInput = Input.GetVector(GameInputs.MoveLeft, GameInputs.MoveRight, GameInputs.MoveUp, GameInputs.MoveDown); var rawInput = Input.GetVector(GameInputs.MoveLeft, GameInputs.MoveRight, GameInputs.MoveUp, GameInputs.MoveDown);
var input = new Vector3 var input = new Vector3
{ {
X = rawInput.X, X = rawInput.X,
Z = rawInput.Y Z = rawInput.Y
}; };
return input with { Y = 0f }; return input with { Y = 0f };
} }
} }
private static float LeftStrafeInputVector private static float LeftStrafeInputVector
{ {
get get
{ {
return Input.GetActionStrength(GameInputs.StrafeLeft); return Input.GetActionStrength(GameInputs.StrafeLeft);
} }
} }
private static float RightStrafeInputVector private static float RightStrafeInputVector
{ {
get get
{ {
return Input.GetActionStrength(GameInputs.StrafeRight); return Input.GetActionStrength(GameInputs.StrafeRight);
} }
} }
private void ThrowItem() private void ThrowItem()
{ {
var itemScene = GD.Load<PackedScene>("res://src/items/throwable/ThrowableItem.tscn"); var itemScene = GD.Load<PackedScene>("res://src/items/throwable/ThrowableItem.tscn");
var throwItem = itemScene.Instantiate<ThrowableItem>(); var throwItem = itemScene.Instantiate<ThrowableItem>();
GetTree().Root.AddChildEx(throwItem); GetTree().Root.AddChildEx(throwItem);
throwItem.GlobalPosition = CurrentPosition + new Vector3(0, 3.5f, 0); throwItem.GlobalPosition = CurrentPosition + new Vector3(0, 3.5f, 0);
throwItem.GlobalRotation = GlobalRotation; throwItem.GlobalRotation = GlobalRotation;
} }
private void OnAnimationFinished(StringName animation) private void OnAnimationFinished(StringName animation)
{ {
PlayerLogic.Input(new PlayerLogic.Input.AttackAnimationFinished()); PlayerLogic.Input(new PlayerLogic.Input.AttackAnimationFinished());
} }
private void OnExitTree() private void OnExitTree()
{ {
PlayerLogic.Stop(); PlayerLogic.Stop();
PlayerBinding.Dispose(); PlayerBinding.Dispose();
AnimationPlayer.AnimationFinished -= OnAnimationFinished; AnimationPlayer.AnimationFinished -= OnAnimationFinished;
} }
private void OnPlayerPositionUpdated(Vector3 globalPosition) => GlobalPosition = globalPosition; private void OnPlayerPositionUpdated(Vector3 globalPosition) => GlobalPosition = globalPosition;
private void OnHealthTimerTimeout() private void OnHealthTimerTimeout()
{ {
if (Stats.CurrentHP.Value <= 0) if (Stats.CurrentHP.Value <= 0)
return; return;
if (Stats.CurrentVT.Value > 0) if (Stats.CurrentVT.Value > 0)
{ {
if (((Accessory)EquippedAccessory.Value).AccessoryTag == AccessoryTag.HalfVTConsumption) if (((Accessory)EquippedAccessory.Value).AccessoryTag == AccessoryTag.HalfVTConsumption)
{ {
reduceOnTick = !reduceOnTick; reduceOnTick = !reduceOnTick;
} }
Stats.SetCurrentHP(Stats.CurrentHP.Value + 1); Stats.SetCurrentHP(Stats.CurrentHP.Value + 1);
if (reduceOnTick) if (reduceOnTick)
Stats.SetCurrentVT(Stats.CurrentVT.Value - 1); Stats.SetCurrentVT(Stats.CurrentVT.Value - 1);
} }
else else
Stats.SetCurrentHP(Stats.CurrentHP.Value - 1); Stats.SetCurrentHP(Stats.CurrentHP.Value - 1);
} }
private void EquippedWeapon_Sync(EquipableItem obj) private void EquippedWeapon_Sync(EquipableItem obj)
{ {
ModifyBonusAttack(((Weapon)obj).Damage); ModifyBonusAttack(((Weapon)obj).Damage);
} }
private void EquippedArmor_Sync(EquipableItem obj) private void EquippedArmor_Sync(EquipableItem obj)
{ {
ModifyBonusDefense(((Armor)obj).Defense); ModifyBonusDefense(((Armor)obj).Defense);
} }
private void EquippedAccessory_Sync(EquipableItem accessory) private void EquippedAccessory_Sync(EquipableItem accessory)
{ {
ModifyMaximumHP(((Accessory)accessory).MaxHPUp); ModifyMaximumHP(((Accessory)accessory).MaxHPUp);
ModifyMaximumVT(((Accessory)accessory).MaxVTUp); ModifyMaximumVT(((Accessory)accessory).MaxVTUp);
ModifyBonusAttack(((Accessory)accessory).ATKUp); ModifyBonusAttack(((Accessory)accessory).ATKUp);
ModifyBonusDefense(((Accessory)accessory).DEFUp); ModifyBonusDefense(((Accessory)accessory).DEFUp);
ModifyBonusLuck(((Accessory)accessory).LuckUp); ModifyBonusLuck(((Accessory)accessory).LuckUp);
} }
private void CurrentHP_Sync(int newHealth) private void CurrentHP_Sync(int newHealth)
{ {
if (newHealth <= 0) if (newHealth <= 0)
Die(); Die();
} }
private void CurrentEXP_Sync(double newExp) private void CurrentEXP_Sync(double newExp)
{ {
if (Stats.CurrentExp.Value >= Stats.ExpToNextLevel.Value) if (Stats.CurrentExp.Value >= Stats.ExpToNextLevel.Value)
LevelUp(); LevelUp();
} }
private double CalculateDefenseResistance(double incomingDamage) private double CalculateDefenseResistance(double incomingDamage)
{ {
return Mathf.Max(incomingDamage - Stats.CurrentDefense.Value - Stats.BonusDefense.Value, 0.0); return Mathf.Max(incomingDamage - Stats.CurrentDefense.Value - Stats.BonusDefense.Value, 0.0);
} }
private void Hitbox_AreaEntered(Area3D area) private void Hitbox_AreaEntered(Area3D area)
{ {
var target = area.GetOwner(); var target = area.GetOwner();
if (target is IEnemy enemy) if (target is IEnemy enemy)
HitEnemy(enemy); HitEnemy(enemy);
} }
private void HitEnemy(IEnemy enemy) private void HitEnemy(IEnemy enemy)
{ {
var attackValue = Stats.CurrentAttack.Value + Stats.BonusAttack.Value; var attackValue = Stats.CurrentAttack.Value + Stats.BonusAttack.Value;
var ignoreElementalResistance = ((Weapon)EquippedWeapon.Value).WeaponTag == WeaponTag.IgnoreAffinity; var ignoreElementalResistance = ((Weapon)EquippedWeapon.Value).WeaponTag == WeaponTag.IgnoreAffinity;
var isCriticalHit = BattleExtensions.IsCriticalHit(Stats.Luck.Value); var isCriticalHit = BattleExtensions.IsCriticalHit(Stats.Luck.Value);
var element = ((Weapon)EquippedWeapon.Value).WeaponElement; var element = ((Weapon)EquippedWeapon.Value).WeaponElement;
enemy.TakeDamage( enemy.TakeDamage(
attackValue * ((Weapon)EquippedWeapon.Value).ElementalDamageBonus, attackValue * ((Weapon)EquippedWeapon.Value).ElementalDamageBonus,
element, element,
isCriticalHit, isCriticalHit,
false, false,
ignoreElementalResistance); ignoreElementalResistance);
if (((Weapon)EquippedWeapon.Value).WeaponTag == WeaponTag.Knockback) if (((Weapon)EquippedWeapon.Value).WeaponTag == WeaponTag.Knockback)
enemy.Knockback(0.3f, -CurrentBasis.Z.Normalized()); enemy.Knockback(0.3f, -CurrentBasis.Z.Normalized());
_gameRepo.OnPlayerAttackedEnemy();
} }
private void CollisionDetector_AreaEntered(Area3D area) private void CollisionDetector_AreaEntered(Area3D area)
{ {
if (area.GetParent() is InventoryItem inventoryItem) if (area.GetParent() is InventoryItem inventoryItem)
{ {
var isAdded = Inventory.TryAdd(inventoryItem); var isAdded = Inventory.TryAdd(inventoryItem);
if (isAdded) if (isAdded)
{ {
_gameRepo.AnnounceMessageOnMainScreen($"{inventoryItem.ItemName} picked up."); _gameRepo.AnnounceMessageOnMainScreen($"{inventoryItem.ItemName} picked up.");
inventoryItem.QueueFree(); inventoryItem.QueueFree();
} }
else else
_gameRepo.AnnounceMessageOnMainScreen($"Could not pick up {inventoryItem.ItemName}."); _gameRepo.AnnounceMessageOnMainScreen($"Could not pick up {inventoryItem.ItemName}.");
} }
if (area.GetParent() is DroppedItem droppedItem) if (area.GetParent() is DroppedItem droppedItem)
{ {
var isAdded = Inventory.TryAdd(droppedItem.Item); var isAdded = Inventory.TryAdd(droppedItem.Item);
if (isAdded) if (isAdded)
{ {
_gameRepo.AnnounceMessageOnMainScreen($"{droppedItem.Item.ItemName} picked up."); _gameRepo.AnnounceMessageOnMainScreen($"{droppedItem.Item.ItemName} picked up.");
droppedItem.QueueFree(); droppedItem.QueueFree();
} }
else else
_gameRepo.AnnounceMessageOnMainScreen($"Could not pick up {droppedItem.Item.ItemName}."); _gameRepo.AnnounceMessageOnMainScreen($"Could not pick up {droppedItem.Item.ItemName}.");
} }
} }
private bool PlayerIsHittingGeometry() private bool PlayerIsHittingGeometry()
{ {
var collisions = WallCheck.GetCollidingBodies(); var collisions = WallCheck.GetCollidingBodies();
return collisions.Count > 0; return collisions.Count > 0;
} }
private void WallCheck_BodyEntered(Node body) private void WallCheck_BodyEntered(Node body)
{ {
PlayerLogic.Input(new PlayerLogic.Input.AttackAnimationFinished()); PlayerLogic.Input(new PlayerLogic.Input.AttackAnimationFinished());
GD.Print("Hit wall"); GD.Print("Hit wall");
AnimationPlayer.Stop(); AnimationPlayer.Stop();
} }
} }