Compare commits

...

169 Commits

Author SHA1 Message Date
34c125e6bb no message 2026-02-15 01:14:09 -08:00
66905c9b53 Overhaul 2026-02-15 01:06:46 -08:00
Pal
a1f4a29eb3 Mask of Zeal, Mask Placeholder, Health Item Placeholder, VT pickup animation added. Temp Environment added to Final Floor. 2026-02-15 00:57:19 -08:00
a6ea1b1873 laptop 2026-02-14 19:26:33 -08:00
Pal
47ceb2f613 Remove E, fix UI, Altar scale better 2026-02-14 16:49:16 -08:00
bf6b0d50c3 Additional in progress changes 2026-02-13 23:39:49 -08:00
c7603a163f Revert "update"
This reverts commit fe0241ac88.
2026-02-13 16:33:44 -08:00
a20c80d922 Remaining changes 2026-02-13 16:33:30 -08:00
e14007b7f4 Merge branch 'main' into item_changes 2026-02-13 16:24:40 -08:00
b17c134c9a Add debug info folder that got filtered by git 2026-02-13 16:12:34 -08:00
fe0241ac88 update 2026-02-13 16:11:38 -08:00
0ab6ef1343 In progress item changes 2026-02-13 15:59:10 -08:00
Pal
638946d23a UI Mockup Ver.2 With Correct Transparencies 2026-02-13 15:48:04 -08:00
Pal
b56668dcbe 3D Render Icons Wave 1 Added 2026-02-13 14:44:03 -08:00
d6faf8642a Fix up debug info overlay 2026-02-13 10:14:42 -08:00
68b1455c53 Fix restorative 2026-02-12 23:28:51 -08:00
9615e1e251 Audio fade 2026-02-12 22:42:18 -08:00
c755485855 Add sample Stele to overworld 2026-02-12 21:39:10 -08:00
d503413140 Fix up effect items
Fix up minimap
Add some debug info
2026-02-12 20:49:09 -08:00
Pal
ac31c3ae65 Deleted a bunch of unused files.
Added environment to A2 exit, deleted environment from other A2 rooms.
2026-02-12 20:43:52 -08:00
Pal
549040c339 Gaps Fixed, Overworld Visual Fixes, Some lighting and additions to block rooms, may have accidentally moved a folder but cant find which. 2026-02-12 20:17:13 -08:00
c246d8d654 Fix crit calculation and bonus attack/def/luck 2026-02-12 02:58:30 -08:00
b475df6f68 Implement most jewels 2026-02-12 02:36:25 -08:00
230b47061d Add item spawn menu
Fix game over bug
Start adding more implementation for jewels
2026-02-11 15:25:20 -08:00
8ce38c3c13 Redesign and reimplement inventory menu
Add jewels but no implementation yet (needed redesign of inventory menu to function correctly)
2026-02-11 04:08:42 -08:00
5451f0b31f Persuader 2026-02-10 18:42:46 -08:00
92b4e8662f Add projectiles 2026-02-10 18:03:53 -08:00
2f377d2d7a Add curse element, increase throw speed/damage of Ciello 2026-02-10 15:37:12 -08:00
363ee1cd33 Turn more HQs on 2026-02-10 15:02:55 -08:00
97198afe18 Fix dropped/thrown item behavior 2026-02-10 14:57:41 -08:00
Pal
fdc4a6f2c1 Small Map Fixes 2026-02-10 01:55:56 -08:00
Pal
843a100218 Small Changes 2026-02-10 01:21:47 -08:00
Pal
8001556f37 Item Icons Wave 2 2026-02-10 00:49:15 -08:00
90d054a3c6 Clean up events, add weapon tag for weaker on swing 2026-02-09 23:50:15 -08:00
aba325ff2b Implement plasma sword instakill 2026-02-09 23:01:52 -08:00
bfaa324e6a Fix references and start more weapon tag implementation 2026-02-09 22:16:49 -08:00
f08c69fa10 Add weapons 2026-02-09 21:37:40 -08:00
9d6aa6d88d Update item icons 2026-02-09 21:05:45 -08:00
654e368a65 Fix exported project not loading issue 2026-02-09 20:24:48 -08:00
ce727b523a Change import settings for textures 2026-02-09 18:52:31 -08:00
Pal
6a474576f0 wave 1 of item icons
(cherry picked from commit 6355ffb26d)
2026-02-09 17:20:39 -08:00
8dd194a202 Fix level up, fix gallery room height, fix enemy heights, fix gospel of dimension spawn height, put minimap on right visual layer for some floors, add script to floor scene file 2026-02-06 03:24:34 -08:00
70a33d68cf Add navigation regions to all floors
Actually fix the annoying LookAt bug for real this time I hope
2026-02-06 00:00:22 -08:00
da8c4209d7 Fix annoying issue with LookAt
Add jump scare
2026-02-05 20:10:41 -08:00
c6fbc9f553 Rework dimmable audio stream 2026-02-05 14:45:28 -08:00
36b851254e Add collisions to A2 corridors
Start implementation of fading ambient audio in/out on level load
2026-02-05 14:17:45 -08:00
147f04d2ff Modify palm of heaven, remove random box item in altar 2026-02-05 10:52:13 -08:00
8ea881edb3 Item holy and all element implementation, still need to work out some of the math for it
Fix bonus defense for weapons
2026-02-05 10:48:24 -08:00
8a99771491 Add more boxes, implement weapon that gets stronger on lower HP 2026-02-04 23:51:07 -08:00
d45bc67722 Partially implementation of box items 2026-02-04 22:30:10 -08:00
13ebe54474 Fix some control issues 2026-02-04 02:34:23 -08:00
b9a1888bfc Add implementation for box item 2026-02-04 02:11:00 -08:00
5ae556cb4b Fix some broken stuff (player spawn, disabled input after loading next floor) 2026-02-04 02:04:04 -08:00
52dc8fb9e4 Box item implementation 2026-02-04 01:49:17 -08:00
affa5e1f79 Player step SFX pitch change 2026-02-03 22:15:02 -08:00
Pal
35a625f636 merge 2026-02-03 21:06:28 -08:00
Pal
d5de5f7379 player anim changes 2026-02-03 21:05:20 -08:00
3e6e21977e Fix Altar scaling 2026-02-03 20:52:14 -08:00
Pal
4a2d131276 Boss floor changes minor 2026-02-03 19:54:05 -08:00
Pal
d9c2ba7ed1 Added Memorial Steles, boss A floor visual polish, re-enabled overworld distance scenery 2026-02-03 04:18:25 -08:00
051ffbbcb1 Fix bug with Boss A (hit box didn't get scaled correctly)
Fix reset of death animation
2026-02-03 02:24:54 -08:00
9747d7d2c5 Revert boss floor environment lighting change 2026-02-03 02:04:21 -08:00
34dce8c5a2 Loading screen fixes, transition screen UI fixes 2026-02-03 01:47:42 -08:00
51010c4f7d Fix overworld collisions 2026-02-03 00:34:19 -08:00
fd96eb2dc9 Fix minimap height
Rework boss code to separate boss A concerns from demon wall
2026-02-02 23:24:48 -08:00
51c8f26e50 Minor fixes for NPC dialogue stuff
Add collisions for overworld
2026-02-02 22:39:33 -08:00
4c90eb6f07 Fix additional collisions 2026-02-02 21:59:48 -08:00
30f0a078a9 Boss A fixes 2026-02-02 20:01:54 -08:00
6e4a4d605c Partially fix boss room A, only one guy moves though 2026-02-02 00:27:11 -08:00
Pal
836b9eb26d idk merghhe 2026-02-01 16:48:25 -08:00
Pal
cb2df83079 idk 2026-02-01 16:48:02 -08:00
0282ef68f3 Collisions again 2026-02-01 16:00:44 -08:00
Pal
1678d79bbd Added E symbol to Exit floors 2026-02-01 14:02:52 -08:00
Pal
20d2890b37 Pit A, Dismantled, Puer texture fixes 2026-02-01 13:46:14 -08:00
Pal
e85c8d51f1 1A fix 2026-02-01 05:07:13 -08:00
Pal
3e178257aa merger 2026-02-01 05:03:10 -08:00
Pal
25b6d53ec4 Minimap added to all floors, all minimaps added and refined.
Less overlap neccessary for revealer cubes
2026-02-01 05:02:15 -08:00
a9ed8fda10 Add minimap shadows 2026-01-31 18:31:50 -08:00
Pal
4801d7d9b3 Special Collision Maps Added 2026-01-31 14:15:43 -08:00
Pal
f08817a586 Merging 2026-01-30 20:35:10 -08:00
Pal
4b23c2ca6f Floor Maps and room updates 2026-01-30 20:34:07 -08:00
39b2bc631d Collisions for floor set B 2026-01-29 01:12:44 -08:00
f346f0f529 Fix up Set A room collisions more and organize node structure 2026-01-28 00:53:35 -08:00
4ffe04fcff Exploding wall implementation 2026-01-26 02:15:03 -08:00
2ef838f270 Forgot a bit 2026-01-26 01:43:40 -08:00
e63a94210c Looking into room collisions we need; set up plastique behavior 2026-01-25 19:40:59 -08:00
865934399d Boss fixes, various changes 2026-01-25 19:01:34 -08:00
2622ed4423 Fix maze floor 2026-01-25 16:56:05 -08:00
79dd6eb33a Fix player height 2026-01-25 16:06:20 -08:00
Pal
bba0bb5ecd Minimap Reveal system tscns added, floor adjustments, collisions added for mazefloor 2026-01-25 02:19:11 -08:00
Pal
d6b20ce4c2 Merge branch 'main' of https://git.zenny.quest/zenayi/GameJamDungeon 2026-01-24 18:55:38 -08:00
Pal
60d8c55c7d Finished All Basic Floor layouts 2026-01-24 18:55:35 -08:00
d3a3c18d13 Fix doors 2026-01-24 15:13:27 -08:00
Pal
12993bced0 Floors 1ABC and 2ABC complete 2026-01-23 18:02:20 -08:00
Pal
8fd7a5133d A1 Floor Progress 2026-01-22 17:54:12 -08:00
Pal
97472f2a61 Deleted old unused Corridor tscns, B set (Floors 9-15) complete 2026-01-22 16:14:58 -08:00
5284a7c00d Some geometry fixes, fix spawn height for overworld 2026-01-22 01:08:25 -08:00
Pal
92b39c1ee9 Lighting for new rooms etc 2026-01-21 14:46:48 -08:00
faf3288061 Script types for block rooms 2026-01-21 01:59:12 -08:00
b715d6b459 Add collisions to block rooms 2026-01-21 01:25:28 -08:00
9897acffac Unlockable door implementation (currently opens with regular attack) 2026-01-21 00:50:21 -08:00
aa9e14c498 Make overworld doors be of unlockable type, add behavior later 2026-01-20 23:33:28 -08:00
945c5e14bb Licking all the room files 2026-01-20 22:01:22 -08:00
a1f67c3d71 Fix dying loop, add death counter, add die button to debug 2026-01-20 21:18:10 -08:00
Pal
670f8baabf Added/Fixed Rooms 2026-01-20 18:29:21 -08:00
Pal
6a62d3d943 Room fixes, started mapping floors 2026-01-17 00:00:37 -08:00
3fe45cb3e7 First floor set collisions 2026-01-15 01:37:57 -08:00
Pal
db218f26e7 Merge branch 'main' of https://git.zenny.quest/zenayi/GameJamDungeon 2026-01-14 00:46:57 -08:00
Pal
eb4e901c8a Boss 1 death animation animations added, wall explosion animation added. 2026-01-14 00:46:15 -08:00
48a00933d9 Adjust sprite sizes to accomodate pixel melt 2026-01-13 23:12:49 -08:00
Pal
be8bbcac28 Corridor Assetts Implemented 2026-01-13 23:06:06 -08:00
c5cb586e4b Change item effect service to only affect regular enemy types for strong item effects 2026-01-13 21:38:12 -08:00
1e97eb9ede Elemental resistance calculation changes 2026-01-13 21:25:09 -08:00
Pal
0591dccc31 Merge branch 'main' of https://git.zenny.quest/zenayi/GameJamDungeon 2026-01-12 22:06:57 -08:00
Pal
70f7642d0f Special room with bombable wall added + start on plastique explosoin 2026-01-12 22:06:43 -08:00
7e8826f143 Restore health fountain 2026-01-12 20:26:05 -08:00
Pal
f13afc9fdc Re-scaled environment models imported and implemented. 2026-01-12 19:39:34 -08:00
04543fcfac Add plastique item
Add Door object
Initial work for look up animation
2026-01-02 17:06:01 -08:00
5b9de11e5a Add Resume/Exit buttons to pause menu, handle logic for returning to main menu and starting a new game 2025-12-05 20:05:28 -08:00
678916be89 Enemy viewer revamp, loading screen improvement 2025-12-05 16:30:13 -08:00
Pal
f39bd8ecdb Added many player.tscn animated sprite2Ds 2025-12-04 20:21:24 -08:00
Pal
76f4adc5be gallery and options rendered images set to linear filterling 2025-12-04 01:18:03 -08:00
Pal
95227946d1 Merge branch 'main' of https://git.zenny.quest/zenayi/GameJamDungeon 2025-12-03 23:54:32 -08:00
Pal
1ee3e97f85 Overworld Gate object added, visual assetts added, test animation added 2025-12-03 23:54:20 -08:00
6f90a0985a General UI Work 2025-12-03 23:21:29 -08:00
34742d568e Play test animation 2025-12-03 22:29:24 -08:00
Pal
3c369f79f7 Added shadows to enemies 2025-12-01 21:05:33 -08:00
Pal
fe45f0bcf2 Screen renders added, fonts added, altar model updated 2025-12-01 20:13:15 -08:00
edff41af22 Edit dialogue balloons 2025-12-01 01:01:33 -08:00
4ee4e02a51 Take damage/Screen shake 2025-11-30 22:35:26 -08:00
8f8cc217dc Death animation 2025-11-30 21:37:31 -08:00
c491ea5050 Gallery Menu initial implementation 2025-11-30 20:17:08 -08:00
Pal
725547d388 Late November Merge 2025-11-26 02:38:43 -08:00
Pal
539430d112 VFX Assetts Added 2025-11-26 02:37:45 -08:00
46402401b4 Add basic implementation for footsteps
Add disengage to eden pillar
Fix level up sound effect trigger
2025-11-26 02:33:03 -08:00
ed9e611fd9 Work SFX work
Fix up Eden Pillar behavior
2025-11-26 02:12:24 -08:00
db7a1df1f7 Started implementing SFX
Fixed Shield animation jumps and secondary attack
Fixed demon wall stone behavior
Made overworld ambient sounds unpausable
2025-11-25 03:04:07 -08:00
Pal
3e8c11d55d VFX assetts imported 2025-11-22 15:55:13 -08:00
613fc3bf60 Setup player screen FX 2025-11-19 00:05:20 -08:00
f69e219643 Add game tab to options menu, fix focus of GUI elements, add Delete option for save file 2025-11-18 20:28:43 -08:00
Pal
0afbf38bf9 Merge branch 'main' of https://git.zenny.quest/zenayi/GameJamDungeon 2025-11-17 19:18:01 -08:00
Pal
23fdf7309d VFX Assetts Added 2025-11-17 19:17:52 -08:00
fcffdb3b35 Fix shadows 2025-11-16 20:07:44 -08:00
7210133330 Add projectiles to various enemies 2025-11-16 16:35:48 -08:00
Pal
e7bae342c9 SFX additions 2025-11-16 16:12:21 -08:00
Pal
c22fde3bb5 November Merge 2025-11-12 16:14:43 -08:00
Pal
7cb5b20293 weird things I cant undo 2025-11-12 16:14:14 -08:00
a5846e08dc Rewrite and simplify Inventory Menu, various fixes for item effects 2025-11-04 01:12:16 -08:00
7b7fc910bd Button remapping work 2025-11-03 02:48:05 -08:00
9fc875eda5 Add options menu persistence 2025-10-30 23:59:05 -07:00
Pal
dc3c458414 merge 2025-10-30 15:59:01 -07:00
Pal
ba38c6443c merging stuff 2025-10-30 15:58:19 -07:00
575a565a2c Fix spawn point for player, add flee behavior for gold sproingy 2025-10-29 11:03:07 -07:00
21d8c4770d Big fix patch for enemy behaviors 2025-10-29 00:35:15 -07:00
Pal
1a77695155 Merger 2025-10-27 20:10:58 -07:00
Pal
11cbb8c4e5 SFX batch 1 import and some implementation, minimap icons added 2025-10-27 20:08:17 -07:00
eed50bc04e Fix standard enemy logic 2025-10-27 19:41:03 -07:00
2d55ae9cc4 Add secondary attack options 2025-10-27 18:03:14 -07:00
0b909e4e7e Teleport skill added for Chinthe 2025-10-27 17:48:27 -07:00
7e6dca1c29 Rework game over logic and game initialization 2025-10-27 15:04:01 -07:00
720696aed0 Try to get Chinthe to stop jumping upon reaching player 2025-10-26 15:05:18 -07:00
286c221530 Improvements to save and loading
Improvements to Chinthe animation logic
Fix broken Godot Tool system and just use a more manual approach to setting map nodes
Remove ItemDatabase from individual room scenes
2025-10-24 01:33:18 -07:00
f5360adbf1 Basic projectile implementation (put sample version in FilthEaterModelView) 2025-10-23 18:50:05 -07:00
bc161a58b3 Additional refactoring and fixing of equipment data
Add attack data to enemy attacks (might need to rework a little bit for primary/secondary attacks)
2025-10-23 00:05:44 -07:00
f0c4e65783 Refactor stats 2025-10-22 16:24:07 -07:00
6ec45c4805 Refactor Player class to use components, also use components in Enemy class types and fiddle with boss structure 2025-10-22 02:41:08 -07:00
44fd8c82b0 Rework enemy behavior (still in progress but shouldn't crash) 2025-10-20 19:24:50 -07:00
Pal
20b659681a More enemy VFX added and attacks setup, old Shield of Heaven frames removed, player camera rotation fixed 2025-10-15 20:17:04 -07:00
Pal
2786c95c22 re-rendered all frames for chinthe, added teleport in animation as well as VFX for attack and teleport. rough draft of a setup for map glow. 2025-10-09 03:23:27 -07:00
14011 changed files with 193409 additions and 87539 deletions

5
.gitignore vendored
View File

@@ -715,3 +715,8 @@ healthchecksdb
# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/
/Zennysoft.Game.Ma/src/map/dungeon/models/Area 2/Puer/A2-Puer.glb.import
/Zennysoft.Game.Ma/src/audio/AMB/amb_beach.wav.import
/Zennysoft.Game.Ma/src/audio/AMB/amb_perlin.wav.import
/Zennysoft.Game.Ma/src/audio/AMB/amb_white_noise.wav.import
/Zennysoft.Game.Ma/src/audio/AMB/amb_wind_loop_altar.wav.import

View File

@@ -10,6 +10,8 @@ public interface IAppRepo : IDisposable
event Action? MainMenuEntered;
event Action? DataViewerExited;
void SkipSplashScreen();
void OnMainMenuEntered();
@@ -19,4 +21,6 @@ public interface IAppRepo : IDisposable
void OnExitGame();
void OnGameOver();
void OnDataViewerExited();
}

View File

@@ -0,0 +1,5 @@
namespace Zennysoft.Game.Abstractions.Entity;
public interface IBehavior
{
}

View File

@@ -0,0 +1,11 @@
namespace Zennysoft.Game.Abstractions.Entity
{
public interface IAction
{
public Task PerformAction();
}
public interface IAction<T>
{
public Task PerformAction(T arg);
}
}

View File

@@ -1,8 +0,0 @@
namespace Zennysoft.Game.Abstractions;
public interface IStackable
{
int Count { get; }
void SetCount(int count);
}

View File

@@ -1,6 +0,0 @@
namespace Zennysoft.Game.Abstractions;
public interface ICanPatrol
{
public void Patrol();
}

View File

@@ -2,5 +2,5 @@
public interface IHealthPack
{
public double RestoreAmount { get; }
public int RestoreAmount { get; }
}

View File

@@ -1,15 +1,18 @@
using System.Text.Json;
using System.Text.Json.Serialization.Metadata;
using System.Text.Json.Serialization.Metadata;
namespace Zennysoft.Game.Abstractions;
public interface ISaveFileManager<T>
public interface ISaveFileManager
{
public Task WriteToFile(T gameData, params IJsonTypeInfoResolver?[] resolvers);
public Task WriteToFile<T>(T gameData, params IJsonTypeInfoResolver?[] resolvers);
public Task WriteToFile(T gameData, string filePath, params IJsonTypeInfoResolver?[] resolvers);
public Task WriteToFile<T>(T gameData, string filePath, params IJsonTypeInfoResolver?[] resolvers);
public Task<T?> ReadFromFile(params IJsonTypeInfoResolver?[] resolvers);
public Task<T?> ReadFromFile<T>(params IJsonTypeInfoResolver?[] resolvers);
public Task<T?> ReadFromFile(string filePath, params IJsonTypeInfoResolver?[] resolvers);
public Task<T?> ReadFromFile<T>(string filePath, params IJsonTypeInfoResolver?[] resolvers);
public void DeleteSaveData(string filePath);
public void DeleteSaveData();
}

View File

@@ -0,0 +1,54 @@
namespace Zennysoft.Game.Implementation;
using Chickensoft.GodotNodeInterfaces;
using Godot;
using Zennysoft.Game.Abstractions;
public partial class DimmableAudioStreamPlayer3D : AudioStreamPlayer3D, IDimmableAudioStreamPlayer
{
#region Constants
// -60 to -80 is considered inaudible for decibels.
public const float VOLUME_DB_INAUDIBLE = -80f;
public const double FADE_DURATION = 3d; // seconds
#endregion Constants
public ITween? FadeTween { get; set; }
public float InitialVolumeDb;
public override void _Ready()
{
InitialVolumeDb = VolumeDb;
VolumeDb = VOLUME_DB_INAUDIBLE;
}
public void FadeIn()
{
SetupFade(InitialVolumeDb, Tween.EaseType.Out);
Play();
}
public void FadeOut()
{
SetupFade(VOLUME_DB_INAUDIBLE, Tween.EaseType.In);
FadeTween!.TweenCallback(Callable.From(Stop));
}
public void SetupFade(float volumeDb, Tween.EaseType ease)
{
FadeTween?.Kill();
FadeTween = GodotInterfaces.Adapt<ITween>(CreateTween());
FadeTween.TweenProperty(
this,
"volume_db",
volumeDb,
FADE_DURATION
).SetTrans(Tween.TransitionType.Circ).SetEase(ease);
}
public override void _EnterTree() => FadeIn();
public override void _ExitTree() => FadeOut();
}

View File

@@ -0,0 +1,10 @@
using Chickensoft.Collections;
namespace Zennysoft.Game.Implementation;
public interface IStackable
{
AutoProp<int> Count { get; }
void SetCount(int count);
}

View File

@@ -9,10 +9,9 @@ using Zennysoft.Game.Abstractions;
namespace Zennysoft.Game.Implementation;
public class SaveFileManager<T> : ISaveFileManager<T>
public class SaveFileManager : ISaveFileManager
{
private readonly IFileSystem _fileSystem;
private readonly JsonSerializerOptions _jsonOptions;
private string _defaultSaveLocation;
public const string DEFAULT_SAVE_FILE_NAME = "game.json";
@@ -23,27 +22,17 @@ public class SaveFileManager<T> : ISaveFileManager<T>
GodotSerialization.Setup();
Serializer.AddConverter(new Texture2DConverter());
var upgradeDependencies = new Blackboard();
_jsonOptions = new JsonSerializerOptions
{
Converters = {
new SerializableTypeConverter(upgradeDependencies)
},
WriteIndented = true
};
}
public Task<T?> ReadFromFile(params IJsonTypeInfoResolver?[] resolvers)
public Task<T?> ReadFromFile<T>(params IJsonTypeInfoResolver?[] resolvers)
{
if (!_fileSystem.File.Exists(_defaultSaveLocation))
throw new FileNotFoundException();
return ReadFromFile(_defaultSaveLocation, resolvers);
return ReadFromFile<T>(_defaultSaveLocation, resolvers);
}
public async Task<T?> ReadFromFile(string filePath, params IJsonTypeInfoResolver?[] resolvers)
public async Task<T?> ReadFromFile<T>(string filePath, params IJsonTypeInfoResolver?[] resolvers)
{
if (!_fileSystem.File.Exists(filePath))
throw new FileNotFoundException();
@@ -51,20 +40,42 @@ public class SaveFileManager<T> : ISaveFileManager<T>
var json = await _fileSystem.File.ReadAllTextAsync(filePath);
var resolver = new SerializableTypeResolver();
_jsonOptions.TypeInfoResolver = JsonTypeInfoResolver.Combine([resolver, .. resolvers]);
return JsonSerializer.Deserialize<T?>(json, _jsonOptions);
var upgradeDependencies = new Blackboard();
var jsonOptions = new JsonSerializerOptions
{
Converters = {
new SerializableTypeConverter(upgradeDependencies)
},
WriteIndented = true
};
jsonOptions.TypeInfoResolver = JsonTypeInfoResolver.Combine([resolver, .. resolvers]);
return JsonSerializer.Deserialize<T?>(json, jsonOptions);
}
public Task WriteToFile(T gameData, params IJsonTypeInfoResolver?[] resolvers)
public Task WriteToFile<T>(T gameData, params IJsonTypeInfoResolver?[] resolvers)
{
return WriteToFile(gameData, _defaultSaveLocation, resolvers);
}
public async Task WriteToFile(T gameData, string filePath, params IJsonTypeInfoResolver?[] resolvers)
public async Task WriteToFile<T>(T gameData, string filePath, params IJsonTypeInfoResolver?[] resolvers)
{
var resolver = new SerializableTypeResolver();
_jsonOptions.TypeInfoResolver = JsonTypeInfoResolver.Combine([resolver, .. resolvers]);
var json = JsonSerializer.Serialize(gameData, _jsonOptions);
var upgradeDependencies = new Blackboard();
var jsonOptions = new JsonSerializerOptions
{
Converters = {
new SerializableTypeConverter(upgradeDependencies)
},
WriteIndented = true
};
jsonOptions.TypeInfoResolver = JsonTypeInfoResolver.Combine([resolver, .. resolvers]);
var json = JsonSerializer.Serialize(gameData, jsonOptions);
await _fileSystem.File.WriteAllTextAsync(filePath, json);
}
public void DeleteSaveData() => DeleteSaveData(_defaultSaveLocation);
public void DeleteSaveData(string filePath) => _fileSystem.File.Delete(filePath);
}

View File

@@ -13,7 +13,7 @@
<PackageReference Include="Chickensoft.Serialization.Godot" Version="0.7.6" />
<PackageReference Include="System.IO.Abstractions" Version="22.0.11" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Zennysoft.Game.Abstractions\Zennysoft.Game.Abstractions.csproj" />

View File

@@ -8,6 +8,7 @@ public class AppRepo : IAppRepo
public event Action? MainMenuEntered;
public event Action? GameEntered;
public event Action? GameExited;
public event Action? DataViewerExited;
private bool _disposedValue;
@@ -21,6 +22,8 @@ public class AppRepo : IAppRepo
public void OnGameOver() => GameExited?.Invoke();
public void OnDataViewerExited() => DataViewerExited?.Invoke();
protected void Dispose(bool disposing)
{
if (!_disposedValue)

View File

@@ -24,6 +24,8 @@ public partial class AppLogic
public readonly record struct EnemyViewerOpened;
public readonly record struct EnemyViewerExited;
public readonly record struct GalleryOpened;
}
}

View File

@@ -4,6 +4,8 @@ public partial class AppLogic
{
public static class Output
{
public readonly record struct Initialize;
public readonly record struct FadeToBlack;
public readonly record struct ShowSplashScreen;
@@ -24,6 +26,8 @@ public partial class AppLogic
public readonly record struct ShowMainMenu;
public readonly record struct CloseGame;
public readonly record struct ExitGame;
public readonly record struct GameOver;
@@ -32,6 +36,8 @@ public partial class AppLogic
public readonly record struct EnemyViewerOpened;
public readonly record struct EnemyViewerExited;
public readonly record struct GalleryOpened;
}
}

View File

@@ -9,5 +9,5 @@ public interface IAppLogic : ILogicBlock<AppLogic.State>;
[LogicBlock(typeof(State), Diagram = true)]
public partial class AppLogic : LogicBlock<AppLogic.State>, IAppLogic
{
public override Transition GetInitialState() => To<State.MainMenu>();
public override Transition GetInitialState() => To<State.Initialize>();
}

View File

@@ -9,7 +9,7 @@ public partial class AppLogic
public partial record State
{
[Meta]
public partial record EnemyViewer : State
public partial record EnemyViewer : State, IGet<Input.EnemyViewerExited>
{
public EnemyViewer()
{
@@ -18,7 +18,13 @@ public partial class AppLogic
{
Output(new Output.EnemyViewerOpened());
});
this.OnExit(() =>
{
Output(new Output.EnemyViewerExited());
});
}
public Transition On(in Input.EnemyViewerExited input) => To<MainMenu>();
}
}
}

View File

@@ -9,7 +9,7 @@ public partial class AppLogic
public partial record State
{
[Meta]
public partial record GameStarted : State
public partial record GameStarted : State, IGet<Input.QuitGame>
{
public GameStarted()
{
@@ -26,6 +26,11 @@ public partial class AppLogic
OnDetach(() => Get<IAppRepo>().GameExited -= OnGameExited);
}
public Transition On(in Input.QuitGame input)
{
Output(new Output.CloseGame());
return To<MainMenu>();
}
public void OnGameExited() => Input(new Input.QuitGame());
}
}

View File

@@ -0,0 +1,21 @@
namespace Zennysoft.Ma.Adapter;
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
public partial class AppLogic
{
public partial record State
{
[Meta]
public partial record Initialize : State, IGet<Input.SaveFileLoaded>
{
public Initialize()
{
this.OnEnter(() => Output(new Output.Initialize()));
}
public Transition On(in Input.SaveFileLoaded input) => To<SplashScreen>();
}
}
}

View File

@@ -15,7 +15,7 @@ public partial class AppLogic
this.OnEnter(() => Output(new Output.StartLoadingSaveFile()));
}
public Transition On(in Input.SaveFileLoaded input) => To<GameStarted>();
public Transition On(in Input.SaveFileLoaded input) => To<MainMenu>();
}
}
}

View File

@@ -7,15 +7,21 @@ public partial class AppLogic
public partial record State
{
[Meta]
public partial record MainMenu : State, IGet<Input.NewGame>, IGet<Input.EnemyViewerOpened>
public partial record MainMenu : State, IGet<Input.NewGame>, IGet<Input.EnemyViewerOpened>, IGet<Input.QuitGame>
{
public MainMenu()
{
OnAttach(() => Output(new Output.ShowMainMenu()));
}
public Transition On(in Input.NewGame input) => To<GameStarted>();
public Transition On(in Input.EnemyViewerOpened input) => To<EnemyViewer>();
public Transition On(in Input.QuitGame input)
{
Output(new Output.ExitGame());
return ToSelf();
}
}
}
}

View File

@@ -16,15 +16,17 @@ public partial class AppLogic
this.OnEnter(() => Output(new Output.ShowSplashScreen()));
OnAttach(
() => Get<IAppRepo>().SplashScreenSkipped += OnSplashScreenSkipped
);
() =>
{
Get<IAppRepo>().SplashScreenSkipped += OnSplashScreenSkipped;
});
OnDetach(
() => Get<IAppRepo>().SplashScreenSkipped -= OnSplashScreenSkipped
);
}
public Transition On(in Input.FadeOutFinished input) => To<GameStarted>();
public Transition On(in Input.FadeOutFinished input) => To<MainMenu>();
public void OnSplashScreenSkipped() =>
Output(new Output.HideSplashScreen());

View File

@@ -1,30 +0,0 @@
using Godot;
namespace Zennysoft.Ma.Adapter;
public partial class AudioManager : Node
{
#pragma warning disable IDE0044 // Add readonly modifier
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
private static string _sfxPath = $"res://src/audio/sfx";
private AudioStreamPlayer _audioPlayer;
private IDictionary<SoundEffects, AudioStream> _sfxDictionary;
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
#pragma warning restore IDE0044 // Add readonly modifier
public override void _Ready()
{
_audioPlayer = new AudioStreamPlayer();
_sfxDictionary = new Dictionary<SoundEffects, AudioStream>();
var soundEffects = Enum.GetValues(typeof(SoundEffects));
foreach (var effect in soundEffects)
_sfxDictionary.Add((SoundEffects)effect, GD.Load<AudioStream>(_sfxPath + effect + ".ogg"));
}
public void Play(SoundEffects soundEffect)
{
_sfxDictionary.TryGetValue(soundEffect, out var stream);
_audioPlayer.Stream = stream;
_audioPlayer.Play();
}
}

View File

@@ -1,14 +0,0 @@
namespace Zennysoft.Ma.Adapter;
public enum SoundEffects
{
Cancel,
Equip,
Heal,
MenuBack,
MoveThroughOptions,
PlayerAttack,
PlayerHitWall,
Sort,
Unequip
}

View File

@@ -3,50 +3,30 @@ using Zennysoft.Ma.Adapter.Entity;
namespace Zennysoft.Ma.Adapter
{
public class DamageCalculator : IDamageCalculator
public static class DamageCalculator
{
public double CalculateDamage(double damage,
ElementType elementType,
double defense,
ElementalResistanceSet elementalResistanceSet,
bool isCriticalHit = false,
bool ignoreDefense = false,
bool ignoreElementalResistance = false)
public static int CalculateDamage(AttackData damage, double defense, ElementalResistanceSet elementalResistanceSet)
{
var calculatedDamage = damage;
if (!ignoreElementalResistance)
calculatedDamage = CalculateElementalResistance(calculatedDamage, elementType, elementalResistanceSet);
if (!ignoreDefense)
var calculatedDamage = damage.BaseDamage;
if (!damage.IgnoreDefense)
calculatedDamage = CalculateDefenseResistance(calculatedDamage, defense);
if (isCriticalHit)
calculatedDamage *= 2;
return calculatedDamage;
if (!damage.IgnoreElementalResistance)
calculatedDamage = CalculateElementalResistance(calculatedDamage, elementalResistanceSet.ElementalResistance[damage.ElementType]);
return Mathf.Max(1, calculatedDamage);
}
private static double CalculateDefenseResistance(double incomingDamage, double defense)
private static int CalculateDefenseResistance(int incomingDamage, double defense)
{
return Mathf.Max(incomingDamage - defense, 0.0);
var result = incomingDamage - (int)(incomingDamage * (defense / 100));
return result;
}
private static double CalculateElementalResistance(
double incomingDamage,
ElementType incomingElementType,
ElementalResistanceSet elementalResistanceSet)
private static int CalculateElementalResistance(
int incomingDamage,
double elementalResistance)
{
var resistance = elementalResistanceSet.ElementalResistance[incomingElementType];
return Mathf.Max(incomingDamage - (incomingDamage * resistance), 0.0);
var result = incomingDamage - (int)(incomingDamage * (elementalResistance / 100));
return result;
}
}
public interface IDamageCalculator
{
public double CalculateDamage(double damage,
ElementType elementType,
double defense,
ElementalResistanceSet elementalResistanceSet,
bool isCriticalHit = false,
bool ignoreDefense = false,
bool ignoreElementalResistance = false);
}
}

View File

@@ -7,5 +7,8 @@ public enum ElementType
Telluric,
Hydric,
Igneous,
Ferrum
Ferrum,
Holy,
Curse,
All
}

View File

@@ -0,0 +1,18 @@
using Chickensoft.Collections;
namespace Zennysoft.Ma.Adapter;
public interface IAttackComponent : IEntityComponent
{
public IAutoProp<int> CurrentAttack { get; }
public IAutoProp<int> MaximumAttack { get; }
public void Restore(int restoreAmount);
public void Reduce(int reduceAmount);
public void SetAttack(int attack);
public void RaiseMaximumAttack(int raiseAmount);
}

View File

@@ -0,0 +1,18 @@
using Chickensoft.Collections;
namespace Zennysoft.Ma.Adapter;
public interface IDefenseComponent : IEntityComponent
{
public IAutoProp<int> CurrentDefense { get; }
public IAutoProp<int> MaximumDefense { get; }
public void Restore(int restoreAmount);
public void Reduce(int reduceAmount);
public void SetDefense(int attack);
public void RaiseMaximumDefense(int raiseAmount);
}

View File

@@ -0,0 +1,6 @@
namespace Zennysoft.Ma.Adapter;
public interface IEntityComponent
{
public void Reset();
}

View File

@@ -0,0 +1,39 @@
using Chickensoft.Collections;
using Godot;
using Zennysoft.Ma.Adapter.Entity;
namespace Zennysoft.Ma.Adapter;
public interface IEquipmentComponent : IEntityComponent
{
public IAutoProp<IWeapon> EquippedWeapon { get; }
public IAutoProp<IArmor> EquippedArmor { get; }
public IAutoProp<IAccessory> EquippedAccessory { get; }
public IAutoProp<IEquipableItem> EquippedAmmo { get; }
public void Equip(IEquipableItem equipable);
public void Unequip(IEquipableItem equipable);
public bool IsItemEquipped(IEquipableItem item);
public void UpdateEquipment(IEquipableItem equipable);
public bool AugmentableEquipmentExists();
public int BonusAttack { get; }
public int BonusDefense { get; }
public int BonusHP { get; }
public int BonusVT { get; }
public int BonusLuck { get; }
public ElementalResistanceSet ElementalResistance { get; }
public event Action<IEquipableItem> EquipmentChanged;
}

View File

@@ -0,0 +1,24 @@
using Chickensoft.Collections;
namespace Zennysoft.Ma.Adapter;
public interface IExperiencePointsComponent : IEntityComponent
{
public IAutoProp<int> CurrentExp { get; }
public IAutoProp<int> ExpToNextLevel { get; }
public IAutoProp<double> ExpGainRate { get; }
public IAutoProp<int> Level { get; }
public void ModifyExpGainRate(double newRate);
public void Gain(int baseExpGain);
public void GainUnmodified(int flateRateExpGain);
public void LevelUp();
public event Action PlayerLevelUp;
}

View File

@@ -0,0 +1,25 @@
using Chickensoft.Collections;
namespace Zennysoft.Ma.Adapter;
public interface IHealthComponent : IEntityComponent
{
public IAutoProp<int> CurrentHP { get; }
public IAutoProp<int> MaximumHP { get; }
public event Action? HealthReachedZero;
public event Action? DamageTaken;
public bool AtFullHealth { get; }
public void Heal(int healAmount);
public void Damage(int damageAmount);
public void SetCurrentHealth(int health);
public void SetMaximumHealth(int health);
public void RaiseMaximumHP(int raiseAmount, bool restoreHP = false);
}

View File

@@ -0,0 +1,10 @@
using Chickensoft.Collections;
namespace Zennysoft.Ma.Adapter;
public interface ILuckComponent : IEntityComponent
{
public IAutoProp<int> Luck { get; }
public void IncreaseLuck(int value);
}

View File

@@ -0,0 +1,22 @@
using Chickensoft.Collections;
namespace Zennysoft.Ma.Adapter;
public interface IVTComponent : IEntityComponent
{
public IAutoProp<int> CurrentVT { get; }
public IAutoProp<int> MaximumVT { get; }
public bool AtFullVT { get; }
public void Restore(int restoreAmount);
public void Reduce(int reduceAmount);
public void SetVT(int vt);
public void RaiseMaximumVT(int raiseAmount, bool restoreVT = true);
public void SetMaximumVT(int vt);
}

View File

@@ -0,0 +1,3 @@
namespace Zennysoft.Ma.Adapter;
public record AttackData(int BaseDamage, ElementType ElementType, bool IgnoreDefense = false, bool IgnoreElementalResistance = false);

View File

@@ -0,0 +1 @@
uid://ci7va4hsq6hyt

View File

@@ -0,0 +1 @@
uid://87d8kluait8y

View File

@@ -0,0 +1,9 @@
[gd_scene load_steps=2 format=3 uid="uid://c7e5g8l6wuph"]
[ext_resource type="Script" uid="uid://87d8kluait8y" path="res://src/enemy/behaviors/PatrolBehavior.cs" id="1_lobva"]
[node name="NavigationAgent" type="NavigationAgent3D"]
avoidance_enabled = true
debug_enabled = true
script = ExtResource("1_lobva")
_patrolSpeed = 100.0

View File

@@ -1,10 +1,15 @@
namespace Zennysoft.Ma.Adapter.Entity
using Chickensoft.Serialization;
namespace Zennysoft.Ma.Adapter.Entity
{
public record ElementalResistanceSet
{
[Save("elemental_resist_set")]
public Dictionary<ElementType, double> ElementalResistance { get; }
public ElementalResistanceSet(double aeolicResistance, double hydricResistance, double igneousResistance, double ferrumResistance, double telluricResistance)
public static ElementalResistanceSet None => new ElementalResistanceSet(0, 0, 0, 0, 0, 0, 0);
public ElementalResistanceSet(double aeolicResistance, double hydricResistance, double igneousResistance, double ferrumResistance, double telluricResistance, double holyResistance, double curseResistance)
{
ElementalResistance = new Dictionary<ElementType, double>
{
@@ -14,7 +19,22 @@
{ ElementType.Igneous, igneousResistance },
{ ElementType.Ferrum, ferrumResistance },
{ ElementType.Telluric, telluricResistance },
{ ElementType.Holy, holyResistance },
{ ElementType.Curse, curseResistance },
{ ElementType.All, aeolicResistance + hydricResistance + igneousResistance + ferrumResistance + telluricResistance + holyResistance + curseResistance },
};
}
public static ElementalResistanceSet operator +(ElementalResistanceSet left, ElementalResistanceSet right)
{
return new ElementalResistanceSet(
left.ElementalResistance[ElementType.Aeolic] + right.ElementalResistance[ElementType.Aeolic],
left.ElementalResistance[ElementType.Hydric] + right.ElementalResistance[ElementType.Hydric],
left.ElementalResistance[ElementType.Igneous] + right.ElementalResistance[ElementType.Igneous],
left.ElementalResistance[ElementType.Ferrum] + right.ElementalResistance[ElementType.Ferrum],
left.ElementalResistance[ElementType.Telluric] + right.ElementalResistance[ElementType.Telluric],
left.ElementalResistance[ElementType.Holy] + right.ElementalResistance[ElementType.Holy],
left.ElementalResistance[ElementType.Curse] + right.ElementalResistance[ElementType.Curse]);
}
}
}

View File

@@ -0,0 +1,42 @@
using Chickensoft.GodotNodeInterfaces;
using Godot;
using System.Collections.Immutable;
using Zennysoft.Game.Ma;
namespace Zennysoft.Ma.Adapter.Entity
{
public interface IEnemy : ICharacterBody3D
{
public void Activate();
public void Idle();
public void Die();
public void PerformAction();
public void ReturnToDefaultState();
public void OnAbsorb();
public void OnMorph();
public IDungeonRoom GetCurrentRoom(ImmutableList<IDungeonRoom> dungeonRooms);
public void MoveEnemyToNewRoom(IDungeonRoom newRoom);
public IHealthComponent HealthComponent { get; }
public IAttackComponent AttackComponent { get; }
public IDefenseComponent DefenseComponent { get; }
public ElementalResistanceSet ElementalResistanceSet { get; }
public int InitialHP { get; }
public int InitialAttack { get; }
public int InitialDefense { get; }
}
}

View File

@@ -0,0 +1,9 @@
using Godot;
namespace Zennysoft.Ma.Adapter.Entity
{
public interface IKnockbackable
{
void Knockback(float impulse, Vector3 direction);
}
}

View File

@@ -0,0 +1,167 @@
using Zennysoft.Ma.Adapter;
public class Augment
{
public JewelTags AugmentTag;
public Augment(JewelTags tag, IAugmentType augment)
{
AugmentTag = tag;
AugmentType = augment;
}
public IAugmentType AugmentType { get; set; }
}
public class HPRecoverySpeedAugment : IAugmentType
{
private readonly IPlayer _player;
public HPRecoverySpeedAugment(IPlayer player)
{
_player = player;
}
public void Apply() => _player.HealthTimerHPRate += 2;
public void Remove() => _player.HealthTimerHPRate -= 2;
}
public class BasicAugment : IAugmentType
{
public void Apply()
{
// do nothing
}
public void Remove()
{
// do nothing
}
}
public class HastenVTAugment : IAugmentType
{
private readonly IPlayer _player;
public HastenVTAugment(IPlayer player)
{
_player = player;
}
public void Apply() => _player.ModifyHealthTimerSpeed(_player.HealthTimerSpeedModifier + 0.25f);
public void Remove() => _player.ModifyHealthTimerSpeed(_player.HealthTimerSpeedModifier - 0.25f);
}
public class SlowVTReductionAugment : IAugmentType
{
private readonly IPlayer _player;
public SlowVTReductionAugment(IPlayer player)
{
_player = player;
}
public void Apply() => _player.ModifyHealthTimerSpeed(_player.HealthTimerSpeedModifier - 0.25f);
public void Remove() => _player.ModifyHealthTimerSpeed(_player.HealthTimerSpeedModifier + 0.25f);
}
public class IncreaseEXPRateAugment : IAugmentType
{
private readonly IPlayer _player;
public IncreaseEXPRateAugment(IPlayer player)
{
_player = player;
}
public void Apply() => _player.ExperiencePointsComponent.ModifyExpGainRate(_player.ExperiencePointsComponent.ExpGainRate.Value + 0.25f);
public void Remove() => _player.ExperiencePointsComponent.ModifyExpGainRate(_player.ExperiencePointsComponent.ExpGainRate.Value - 0.25f);
}
public class LowerEXPRateAugment : IAugmentType
{
private readonly IPlayer _player;
public LowerEXPRateAugment(IPlayer player)
{
_player = player;
}
public void Apply() => _player.ExperiencePointsComponent.ModifyExpGainRate(_player.ExperiencePointsComponent.ExpGainRate.Value - 0.25f);
public void Remove() => _player.ExperiencePointsComponent.ModifyExpGainRate(_player.ExperiencePointsComponent.ExpGainRate.Value + 0.25f);
}
public class LowerHPRecoveryAugment : IAugmentType
{
private readonly IPlayer _player;
public LowerHPRecoveryAugment(IPlayer player)
{
_player = player;
}
public void Apply() => _player.HealthTimerHPRate -= 1;
public void Remove() => _player.HealthTimerHPRate += 1;
}
public class IdentifyAllItemsAugment : IAugmentType
{
private readonly IPlayer _player;
public IdentifyAllItemsAugment(IPlayer player)
{
_player = player;
}
public void Apply()
{
_player.AutoIdentifyItems = true;
foreach (var item in _player.Inventory.Items.ToList())
{
if (item.ItemTag == ItemTag.MysteryItem)
_player.IdentifyItem(item);
}
}
public void Remove()
{
var weaponAugment = _player.EquipmentComponent.EquippedWeapon.Value.Augment;
var armorAugment = _player.EquipmentComponent.EquippedArmor.Value.Augment;
var accessoryAugment = _player.EquipmentComponent.EquippedAccessory.Value.Augment;
var augments = new List<Augment?>() { weaponAugment, armorAugment, accessoryAugment };
if (augments.Count(x => x != null && x.AugmentTag == JewelTags.AutoIdentifyAllItems) > 1)
return;
else
_player.AutoIdentifyItems = false;
}
}
public class RevivePlayerAugment : IAugmentType
{
private readonly IPlayer _player;
public RevivePlayerAugment(IPlayer player)
{
_player = player;
}
public void Apply()
{
_player.AutoRevive = true;
}
public void Remove()
{
var weaponAugment = _player.EquipmentComponent.EquippedWeapon.Value.Augment;
var armorAugment = _player.EquipmentComponent.EquippedArmor.Value.Augment;
var accessoryAugment = _player.EquipmentComponent.EquippedAccessory.Value.Augment;
var augments = new List<Augment?>() { weaponAugment, armorAugment, accessoryAugment };
if (augments.Count(x => x != null && x.AugmentTag == JewelTags.ReviveUserOnce) > 1)
return;
else
_player.AutoRevive = false;
}
}

View File

@@ -0,0 +1,6 @@
public interface IAugmentType
{
void Apply();
void Remove();
}

View File

@@ -0,0 +1,24 @@
namespace Zennysoft.Ma.Adapter;
public enum ItemTag
{
None,
BreaksOnChange,
MysteryItem,
DamagesPlayer,
ContainsRestorative,
ContainsWeapon,
ContainsArmor,
ContainsBox,
RandomSpell,
ContainsAccessory,
DropTo1HPAndGainRareItem,
TradeOneRandomItem,
TradeAllRandomItems,
ContainsUnobtainedItem,
ContainsBasicItem,
RestrictUnequip,
UnequipAllItems,
EjectAllItems,
UseAllItems
}

View File

@@ -0,0 +1,21 @@
public enum JewelTags
{
None,
AeolicElement,
IncreaseHPRecovery,
HastenVT,
LowerEXPGain,
Glue,
ItemRescue,
HydricElement,
IgneousElement,
IncreaseEXPGain,
LowerHPRecovery,
SlowVTReduction,
AutoIdentifyAllItems,
ReviveUserOnce,
TelluricElement,
IncreaseAtkDefLuck,
IncreaseLuck
}

View File

@@ -16,4 +16,9 @@ public enum UsableItemTag
RaiseCurrentDefenseArmor,
RaiseLevel,
RandomEffect,
DoubleExp,
LowerTargetTo1HP,
CanChangeAffinity,
TeleportToRandomLocation,
WarpToExitIfFound
}

View File

@@ -0,0 +1,18 @@
namespace Zennysoft.Ma.Adapter;
public enum WeaponTag
{
None,
SelfDamage,
IgnoreAffinity,
IgnoreDefense,
Knockback,
InverseHPAttackPower,
RustChanceSelfAndEnemy,
Instakill,
DegradeOnSwing,
DoubleAttack,
TripleAttack,
ElementalProjectile,
KineticProjectile
}

View File

@@ -1,18 +0,0 @@
using Chickensoft.Introspection;
using Chickensoft.Serialization;
using Zennysoft.Game.Ma;
namespace Zennysoft.Ma.Adapter;
[Meta, Id("game_data")]
public partial record GameData
{
[Save("player_data")]
public required PlayerData PlayerData { get; init; }
[Save("map_data")]
public required MapData MapData { get; init; }
[Save("rescued_items")]
public required RescuedItemDatabase RescuedItems { get; init; }
}

View File

@@ -1,6 +1,8 @@
using Chickensoft.Collections;
using Godot;
using Zennysoft.Game.Abstractions;
using Zennysoft.Game.Implementation;
using Zennysoft.Ma.Adapter.Entity;
namespace Zennysoft.Ma.Adapter;
@@ -18,7 +20,7 @@ public interface IGameRepo : IDisposable
event Action? DoubleExpTimeEnd;
event Action<InventoryItem>? RemoveItemFromInventoryEvent;
event Action<IBaseInventoryItem>? RemoveItemFromInventoryEvent;
event Action? PlayerAttack;
@@ -26,11 +28,11 @@ public interface IGameRepo : IDisposable
event Action? PlayerAttackedEnemy;
event Action<EquipableItem>? EquippedItem;
event Action<IEquipableItem>? EquippedItem;
event Action<EquipableItem>? UnequippedItem;
event Action<IEquipableItem>? UnequippedItem;
event Action<IHealthPack>? RestorativePickedUp;
event Action<IEnemy>? EnemyDied;
void Pause();
@@ -46,23 +48,21 @@ public interface IGameRepo : IDisposable
public void AnnounceMessageInInventory(string message);
public void RemoveItemFromInventory(InventoryItem item);
public void RemoveItemFromInventory(IBaseInventoryItem item);
public void OnPlayerAttack();
public void OnPlayerAttackedWall();
public void OnPlayerAttackedEnemy();
public void OnRestorativePickedUp(IHealthPack restorative);
public void CloseInventory();
public void GameEnded();
public void OnEquippedItem(EquipableItem item);
public void OnEquippedItem(IEquipableItem item);
public void OnUnequippedItem(EquipableItem item);
public void OnUnequippedItem(IEquipableItem item);
public void OnEnemyDied(IEnemy enemy);
public double ExpRate { get; }
}
@@ -75,13 +75,13 @@ public class GameRepo : IGameRepo
public event Action<string>? AnnounceMessageInInventoryEvent;
public event Action<int>? DoubleExpTimeStart;
public event Action? DoubleExpTimeEnd;
public event Action<InventoryItem>? RemoveItemFromInventoryEvent;
public event Action<IBaseInventoryItem>? RemoveItemFromInventoryEvent;
public event Action? PlayerAttack;
public event Action? PlayerAttackedWall;
public event Action? PlayerAttackedEnemy;
public event Action<EquipableItem>? EquippedItem;
public event Action<EquipableItem>? UnequippedItem;
public event Action<IHealthPack>? RestorativePickedUp;
public event Action<IEquipableItem>? EquippedItem;
public event Action<IEquipableItem>? UnequippedItem;
public event Action<IEnemy>? EnemyDied;
public IAutoProp<bool> IsPaused => _isPaused;
private readonly AutoProp<bool> _isPaused;
@@ -111,14 +111,14 @@ public class GameRepo : IGameRepo
{
AnnounceMessageInInventory("Experience points temporarily doubled.");
DoubleExpTimeStart?.Invoke(lengthOfEffect.Seconds);
ExpRate = 2;
ExpRate *= 2;
}
public void EndDoubleExp()
{
AnnounceMessageOnMainScreen("Experience points effect wore off.");
DoubleExpTimeEnd?.Invoke();
ExpRate = 1;
ExpRate /= 2;
}
public void AnnounceMessageOnMainScreen(string message)
@@ -131,7 +131,7 @@ public class GameRepo : IGameRepo
AnnounceMessageInInventoryEvent?.Invoke(message);
}
public void RemoveItemFromInventory(InventoryItem item)
public void RemoveItemFromInventory(IBaseInventoryItem item)
{
RemoveItemFromInventoryEvent?.Invoke(item);
}
@@ -146,24 +146,16 @@ public class GameRepo : IGameRepo
PlayerAttackedWall?.Invoke();
}
public void OnPlayerAttackedEnemy()
{
PlayerAttackedEnemy?.Invoke();
}
public void OnRestorativePickedUp(IHealthPack restorative)
{
RestorativePickedUp?.Invoke(restorative);
}
public void CloseInventory()
{
CloseInventoryEvent?.Invoke();
}
public void OnEquippedItem(EquipableItem item) => EquippedItem?.Invoke(item);
public void OnEquippedItem(IEquipableItem item) => EquippedItem?.Invoke(item);
public void OnUnequippedItem(EquipableItem item) => UnequippedItem?.Invoke(item);
public void OnUnequippedItem(IEquipableItem item) => UnequippedItem?.Invoke(item);
public void OnEnemyDied(IEnemy enemy) => EnemyDied?.Invoke(enemy);
public void GameEnded()
{

View File

@@ -8,14 +8,14 @@ public partial class GameState
public readonly record struct LoadGame;
public readonly record struct ContinueGame;
public readonly record struct ReturnToMainMenu;
public readonly record struct ExitGame;
public readonly record struct LoadNextFloor;
public readonly record struct InventoryButtonPressed;
public readonly record struct InteractButtonPressed;
public readonly record struct PauseButtonPressed;
public readonly record struct DebugButtonPressed;
@@ -28,6 +28,8 @@ public partial class GameState
public readonly record struct CloseTeleport;
public readonly record struct CloseInventory;
public readonly record struct GameOver;
}
}

View File

@@ -6,6 +6,8 @@ public partial class GameState
{
public readonly record struct InitializeGame;
public readonly record struct ExitGame;
public readonly record struct LoadGameFromFile;
public readonly record struct OpenInventoryMenu;

View File

@@ -16,11 +16,6 @@ public partial class GameState
OnAttach(() => Get<IGameRepo>().Pause());
}
public Transition On(in Input.ReturnToMainMenu input)
{
return To<MainMenu>();
}
public Transition On(in Input.UseTeleport input)
{
Output(new Output.OpenFloorExitScreen());

View File

@@ -8,7 +8,7 @@ public partial class GameState
public partial record State
{
[Meta, LogicBlock(typeof(State), Diagram = true)]
public partial record DebugMenu : State, IGet<Input.DebugButtonPressed>
public partial record DebugMenu : InGame, IGet<Input.DebugButtonPressed>
{
public Transition On(in Input.DebugButtonPressed input)
{

View File

@@ -8,7 +8,7 @@ public partial class GameState
public partial record State
{
[Meta, LogicBlock(typeof(State), Diagram = true)]
public partial record FloorExitScreen : State, IGet<Input.LoadNextFloor>, IGet<Input.ReturnToMainMenu>
public partial record FloorExitScreen : State, IGet<Input.LoadNextFloor>
{
public FloorExitScreen()
{
@@ -20,11 +20,6 @@ public partial class GameState
Output(new Output.LoadNextFloor());
return To<InGame>();
}
public Transition On(in Input.ReturnToMainMenu input)
{
return To<MainMenu>();
}
}
}
}

View File

@@ -8,17 +8,19 @@ public partial class GameState
public partial record State
{
[Meta, LogicBlock(typeof(State), Diagram = true)]
public partial record GameOver : State, IGet<Input.ContinueGame>, IGet<Input.ReturnToMainMenu>
public partial record GameOver : InGame, IGet<Input.NewGame>, IGet<Input.ExitGame>
{
public Transition On(in Input.ContinueGame input)
public Transition On(in Input.NewGame input)
{
Output(new Output.InitializeGame());
return To<InGame>();
}
public Transition On(in Input.ReturnToMainMenu input)
public Transition On(in Input.ExitGame input)
{
return To<MainMenu>();
Output(new Output.ClosePauseScreen());
Output(new Output.ExitGame());
return To<State>();
}
}
}

View File

@@ -8,9 +8,15 @@ public partial class GameState
public partial record State
{
[Meta, LogicBlock(typeof(State), Diagram = true)]
public partial record InventoryScreen : State, IGet<Input.InventoryButtonPressed>
public partial record InventoryScreen : State, IGet<Input.InteractButtonPressed>, IGet<Input.CloseInventory>
{
public Transition On(in Input.InventoryButtonPressed input)
public Transition On(in Input.InteractButtonPressed input)
{
Output(new Output.CloseInventoryMenu());
return To<InGame>();
}
public Transition On(in Input.CloseInventory input)
{
Output(new Output.CloseInventoryMenu());
return To<InGame>();

View File

@@ -1,32 +0,0 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
namespace Zennysoft.Ma.Adapter;
public partial class GameState
{
public partial record State
{
[Meta, LogicBlock(typeof(State), Diagram = true)]
public partial record MainMenu : State, IGet<Input.NewGame>, IGet<Input.ContinueGame>, IGet<Input.LoadGame>
{
public Transition On(in Input.NewGame input)
{
Output(new Output.InitializeGame());
return To<InGame>();
}
public Transition On(in Input.ContinueGame input)
{
Output(new Output.InitializeGame());
return To<InGame>();
}
public Transition On(in Input.LoadGame input)
{
Output(new Output.LoadGameFromFile());
return To<InGame>();
}
}
}
}

View File

@@ -1,5 +1,6 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
using static Zennysoft.Ma.Adapter.GameState.Output;
namespace Zennysoft.Ma.Adapter;
@@ -8,13 +9,20 @@ public partial class GameState
public partial record State
{
[Meta, LogicBlock(typeof(State), Diagram = true)]
public partial record PauseScreen : State, IGet<Input.PauseButtonPressed>
public partial record PauseScreen : State, IGet<Input.PauseButtonPressed>, IGet<Input.ExitGame>
{
public Transition On(in Input.PauseButtonPressed input)
{
Output(new Output.ClosePauseScreen());
return To<InGame>();
}
public Transition On(in Input.ExitGame input)
{
Output(new Output.ClosePauseScreen());
Output(new Output.ExitGame());
return To<State>();
}
}
}
}

View File

@@ -1,11 +0,0 @@
using Chickensoft.Introspection;
using Chickensoft.Serialization;
namespace Zennysoft.Ma.Adapter;
[Meta, Id("equipable_item")]
public abstract partial class EquipableItem : InventoryItem
{
[Save("equipable_item_is_equipped")]
public bool IsEquipped { get; set; }
}

View File

@@ -0,0 +1,5 @@
using Zennysoft.Ma.Adapter;
public interface IAccessory : IEquipableItem, IAugmentableItem
{
}

View File

@@ -0,0 +1,5 @@
using Zennysoft.Ma.Adapter;
public interface IArmor : IEquipableItem, IAugmentableItem
{
}

View File

@@ -0,0 +1,5 @@
public interface IAugmentItem : IBaseInventoryItem
{
public IAugmentType Augment { get; }
}

View File

@@ -0,0 +1,7 @@
namespace Zennysoft.Ma.Adapter
{
public interface IAugmentableItem
{
public Augment? Augment { get; }
}
}

View File

@@ -0,0 +1,15 @@
using Godot;
using Zennysoft.Ma.Adapter;
public interface IBaseInventoryItem
{
public string ItemName { get; }
public string Description { get; }
public float SpawnRate { get; }
public int ThrowDamage { get; }
public float ThrowSpeed { get; }
public ItemTag ItemTag { get; }
public abstract Texture2D GetTexture();
}

View File

@@ -0,0 +1,9 @@
namespace Zennysoft.Ma.Adapter
{
public interface IDroppedItem
{
void RescueItem();
public IBaseInventoryItem Item { get; }
}
}

View File

@@ -0,0 +1,14 @@
using Zennysoft.Ma.Adapter.Entity;
public interface IEquipableItem : IBaseInventoryItem
{
public int BonusAttack { get; }
public int BonusDefense { get; }
public int BonusHP { get; }
public int BonusVT { get; }
public int BonusLuck { get; }
public bool Glued { get; set; }
public ElementalResistanceSet ElementalResistance { get; }
}

View File

@@ -2,13 +2,20 @@
public interface IInventory
{
public List<InventoryItem> Items { get; }
public bool PickUpItem(IBaseInventoryItem item);
public bool TryAdd(InventoryItem inventoryItem);
public List<IBaseInventoryItem> Items { get; }
public bool TryInsert(InventoryItem inventoryItem, int index);
public bool TryAdd(IBaseInventoryItem inventoryItem);
public void Remove(InventoryItem inventoryItem);
public bool TryInsert(IBaseInventoryItem inventoryItem, int index);
public void Sort();
public void Remove(IBaseInventoryItem inventoryItem);
public bool Sort(IWeapon currentWeapon, IArmor currentArmor, IAccessory currentAccessory, IEquipableItem ammo);
public bool AtCapacity();
public event Action<string> BroadcastMessage;
public event Action InventoryChanged;
}

View File

@@ -0,0 +1,6 @@
namespace Zennysoft.Ma.Adapter;
public interface IThrownItem
{
public IBaseInventoryItem ItemThatIsThrown { get; set; }
}

View File

@@ -0,0 +1,5 @@
using Zennysoft.Ma.Adapter;
public interface IWeapon : IEquipableItem, IAugmentableItem
{
}

View File

@@ -1,26 +0,0 @@
using Chickensoft.Introspection;
using Chickensoft.Serialization;
using Godot;
namespace Zennysoft.Ma.Adapter;
[Meta, Id("inventory_item")]
public abstract partial class InventoryItem : Node3D
{
[Save("inventory_item_id")]
public Guid ID => Guid.NewGuid();
[Save("inventory_item_name")]
public abstract string ItemName { get; }
[Save("inventory_item_description")]
public abstract string Description { get; }
[Save("inventory_item_spawn_rate")]
public abstract float SpawnRate { get; }
[Save("inventory_item_throw_damage")]
public abstract double ThrowDamage { get; }
[Save("inventory_item_throw_speed")]
public abstract float ThrowSpeed { get; }
[Save("inventory_item_tag")]
public abstract ItemTag ItemTag { get; }
public abstract Texture2D GetTexture();
}

View File

@@ -1,6 +1,5 @@
using Chickensoft.Introspection;
using Chickensoft.Serialization;
using Zennysoft.Game.Abstractions;
namespace Zennysoft.Ma.Adapter;
@@ -8,10 +7,10 @@ namespace Zennysoft.Ma.Adapter;
public partial class RescuedItemDatabase
{
[Save("rescued_item_list")]
public List<InventoryItem> Items { get; init; }
public List<IBaseInventoryItem> Items { get; init; }
public RescuedItemDatabase()
{
Items = new List<InventoryItem>();
Items = new List<IBaseInventoryItem>();
}
}

View File

@@ -1,8 +0,0 @@
namespace Zennysoft.Ma.Adapter;
public enum ItemTag
{
None,
BreaksOnChange,
MysteryItem
}

View File

@@ -1,11 +0,0 @@
namespace Zennysoft.Ma.Adapter;
public enum ThrowableItemTag
{
None,
DoubleExp,
LowerTargetTo1HP,
CanChangeAffinity,
TeleportToRandomLocation,
WarpToExitIfFound
}

View File

@@ -1,9 +0,0 @@
namespace Zennysoft.Ma.Adapter;
public enum WeaponTag
{
None,
SelfDamage,
IgnoreAffinity,
Knockback,
}

View File

@@ -11,9 +11,6 @@ public partial class ItemTagEnumContext : JsonSerializerContext;
[JsonSerializable(typeof(AccessoryTag))]
public partial class AccessoryTagEnumContext : JsonSerializerContext;
[JsonSerializable(typeof(ThrowableItemTag))]
public partial class ThrowableItemTagEnumContext : JsonSerializerContext;
[JsonSerializable(typeof(UsableItemTag))]
public partial class UsableItemTagEnumContext : JsonSerializerContext;
@@ -21,4 +18,4 @@ public partial class UsableItemTagEnumContext : JsonSerializerContext;
public partial class BoxItemTagEnumContext : JsonSerializerContext;
[JsonSerializable(typeof(ElementType))]
public partial class ElementTypeEnumContext : JsonSerializerContext;
public partial class ElementTypeEnumContext : JsonSerializerContext;

View File

@@ -0,0 +1,17 @@
using Chickensoft.GodotNodeInterfaces;
using Godot;
using System.Collections.Immutable;
namespace Zennysoft.Game.Ma;
public interface IDungeonFloor : INode3D
{
void InitializeDungeon();
public abstract (Vector3 Rotation, Vector3 Position) GetPlayerSpawnPoint();
public ImmutableList<IDungeonRoom> Rooms { get; }
public void FadeOutAudio();
public bool FloorIsLoaded { get; set; }
}

View File

@@ -1,6 +1,6 @@
using Chickensoft.GodotNodeInterfaces;
using Godot;
using System.Collections.Immutable;
using Zennysoft.Ma.Adapter.Entity;
namespace Zennysoft.Game.Ma;
public interface IDungeonRoom : INode3D

View File

@@ -10,8 +10,8 @@ public class Module
public static void Bootstrap(Container container)
{
container.RegisterSingleton<IFileSystem, FileSystem>();
container.RegisterSingleton<ISaveFileManager<GameData>, SaveFileManager<GameData>>();
container.RegisterSingleton<IMaSaveFileManager<GameData>, MaSaveFileManager<GameData>>();
container.RegisterSingleton<ISaveFileManager, SaveFileManager>();
container.RegisterSingleton<IMaSaveFileManager, MaSaveFileManager>();
container.RegisterSingleton<IGameRepo, GameRepo>();
container.RegisterSingleton<IGameState, GameState>();
container.RegisterSingleton<IDimmableAudioStreamPlayer, DimmableAudioStreamPlayer>();

View File

@@ -1,66 +1,67 @@
using Chickensoft.Collections;
using Chickensoft.GodotNodeInterfaces;
using Godot;
using Zennysoft.Game.Abstractions;
namespace Zennysoft.Ma.Adapter;
public interface IPlayer : IKillable
public interface IPlayer : IKillable, ICharacterBody3D
{
public void InitializePlayerState();
public void ResetPlayerData();
public void Activate();
public void Deactivate();
public void Attack();
public void TakeDamage(double damage, ElementType elementType = ElementType.None, bool isCriticalHit = false);
public void TakeDamage(AttackData damage);
public void Knockback(float impulse);
public void GainExp(double expGained);
public void LevelUp();
public void Move(float delta);
public void TeleportPlayer((Vector3 Rotation, Vector3 Position) newTransform);
public void TeleportPlayer(Transform3D newTransform);
public void Equip(IEquipableItem equipable);
public void HealHP(int amount);
public void Unequip(IEquipableItem equipable);
public void RaiseHP(int amount);
public void PlayJumpScareAnimation();
public void HealVT(int amount);
public void ApplyNewAugment(IAugmentItem jewel, IAugmentableItem equipableItem);
public void RaiseVT(int amount);
public void ModifyBonusAttack(int amount);
public void ModifyBonusDefense(int amount);
public void ModifyMaximumHP(int amount);
public void ModifyMaximumVT(int amount);
public void ModifyBonusLuck(double amount);
public void SetHealthTimerStatus(bool isActive);
public void IdentifyItem(IBaseInventoryItem unidentifiedItem);
public IInventory Inventory { get; }
public PlayerStats Stats { get; }
public IHealthComponent HealthComponent { get; }
public Vector3 CurrentPosition { get; }
public IVTComponent VTComponent { get; }
public Basis CurrentBasis { get; }
public IAttackComponent AttackComponent { get; }
public AutoProp<EquipableItem> EquippedWeapon { get; }
public IDefenseComponent DefenseComponent { get; }
public AutoProp<EquipableItem> EquippedArmor { get; }
public IExperiencePointsComponent ExperiencePointsComponent { get; }
public AutoProp<EquipableItem> EquippedAccessory { get; }
public ILuckComponent LuckComponent { get; }
public void Equip(EquipableItem equipable);
public IEquipmentComponent EquipmentComponent { get; }
public void Unequip(EquipableItem equipable);
public void SetHealthTimerStatus(bool isActive);
public void ModifyHealthTimerSpeed(float newModifier);
public bool AutoRevive { get; set; }
public int TotalAttack { get; }
public int TotalDefense { get; }
public int TotalLuck { get; }
public int HealthTimerHPRate { get; set; }
public float HealthTimerSpeedModifier { get; }
public bool AutoIdentifyItems { get; set; }
public event Action PlayerDied;
public delegate IBaseInventoryItem RerollItem(IBaseInventoryItem item);
}

View File

@@ -1,20 +0,0 @@
using Chickensoft.Introspection;
using Chickensoft.Serialization;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma;
[Meta, Id("player_data")]
public partial record PlayerData
{
[Save("player_stats")]
public required PlayerStats PlayerStats { get; init; }
[Save("player_inventory")]
public required IInventory Inventory { get; init; }
}
[Meta, Id("map_data")]
public partial record MapData
{
}

View File

@@ -1,115 +0,0 @@
namespace Zennysoft.Ma.Adapter;
using Chickensoft.Collections;
using Godot;
public class PlayerStats
{
public PlayerStats(AutoProp<int> currentHP,
AutoProp<int> maximumHP,
AutoProp<int> currentVT,
AutoProp<int> maximumVT,
AutoProp<int> currentAttack,
AutoProp<int> maxAttack,
AutoProp<int> bonusAttack,
AutoProp<int> currentDefense,
AutoProp<int> maxDefense,
AutoProp<int> bonusDefense,
AutoProp<double> currentExp,
AutoProp<int> expToNextLevel,
AutoProp<int> currentLevel,
AutoProp<double> luck)
{
CurrentHP = currentHP;
MaximumHP = maximumHP;
CurrentVT = currentVT;
MaximumVT = maximumVT;
CurrentAttack = currentAttack;
MaxAttack = maxAttack;
BonusAttack = bonusAttack;
CurrentDefense = currentDefense;
MaxDefense = maxDefense;
BonusDefense = bonusDefense;
CurrentExp = currentExp;
ExpToNextLevel = expToNextLevel;
CurrentLevel = currentLevel;
Luck = luck;
}
public AutoProp<int> CurrentHP { get; init; }
public AutoProp<int> MaximumHP { get; init; }
public AutoProp<int> CurrentVT { get; init; }
public AutoProp<int> MaximumVT { get; init; }
public AutoProp<int> CurrentAttack { get; init; }
public AutoProp<int> MaxAttack { get; init; }
public AutoProp<int> BonusAttack { get; init; }
public AutoProp<int> CurrentDefense { get; init; }
public AutoProp<int> MaxDefense { get; init; }
public AutoProp<int> BonusDefense { get; init; }
public AutoProp<double> CurrentExp { get; init; }
public AutoProp<int> ExpToNextLevel { get; init; }
public AutoProp<int> CurrentLevel { get; init; }
public AutoProp<double> Luck { get; init; }
public void SetCurrentHP(int newValue)
{
var clampedValue = Mathf.Clamp(newValue, 0, MaximumHP.Value);
CurrentHP.OnNext(clampedValue);
}
public void SetMaximumHP(int newValue)
{
MaximumHP.OnNext(newValue);
}
public void SetCurrentVT(int newValue)
{
var clampedValue = Mathf.Clamp(newValue, 0, MaximumVT.Value);
CurrentVT.OnNext(clampedValue);
}
public void SetMaximumVT(int newValue)
{
MaximumVT.OnNext(newValue);
}
public void SetCurrentExp(double newValue)
{
CurrentExp.OnNext(newValue);
}
public void SetCurrentLevel(int newValue)
{
CurrentLevel.OnNext(newValue);
}
public void SetCurrentAttack(int newValue)
{
var clampedValue = Mathf.Clamp(newValue, 0, MaxAttack.Value);
CurrentAttack.OnNext(clampedValue);
}
public void SetBonusAttack(int newValue)
{
BonusAttack.OnNext(newValue);
}
public void SetMaxAttack(int newValue)
{
MaxAttack.OnNext(newValue);
}
public void SetCurrentDefense(int newValue)
{
var clampedValue = Mathf.Clamp(newValue, 0, MaxDefense.Value);
CurrentDefense.OnNext(clampedValue);
}
public void SetBonusDefense(int newValue)
{
BonusDefense.OnNext(newValue);
}
public void SetMaxDefense(int newValue)
{
MaxDefense.OnNext(newValue);
}
public void SetExpToNextLevel(int newValue)
{
ExpToNextLevel.OnNext(newValue);
}
public void SetLuck(double newValue)
{
var clampedValue = Mathf.Clamp(newValue, 0, 1.0);
Luck.OnNext(clampedValue);
}
}

View File

@@ -8,14 +8,10 @@ public partial class PlayerLogic
{
public readonly record struct PhysicsTick(double Delta);
public readonly record struct Moved(Vector3 GlobalPosition, Transform3D GlobalTransform);
public readonly record struct Enable;
public readonly record struct Attack;
public readonly record struct AttackAnimationFinished;
public readonly record struct Die;
}
}

View File

@@ -9,5 +9,5 @@ public interface IPlayerLogic : ILogicBlock<PlayerLogic.State>;
[LogicBlock(typeof(State), Diagram = true)]
public partial class PlayerLogic : LogicBlock<PlayerLogic.State>, IPlayerLogic
{
public override Transition GetInitialState() => To<State.Idle>();
public override Transition GetInitialState() => To<State.Alive>();
}

View File

@@ -1,18 +0,0 @@
using Chickensoft.Introspection;
namespace Zennysoft.Ma.Adapter;
public partial class PlayerLogic
{
public partial record State
{
[Meta]
public partial record Attacking : Alive, IGet<Input.AttackAnimationFinished>
{
public Transition On(in Input.AttackAnimationFinished input)
{
return To<Idle>();
}
}
}
}

View File

@@ -1,22 +0,0 @@
using Chickensoft.Introspection;
using Godot;
namespace Zennysoft.Ma.Adapter;
public partial class PlayerLogic
{
public abstract partial record State
{
[Meta, Id("player_logic_state_alive_idle")]
public partial record Idle : Alive, IGet<Input.Attack>
{
public virtual Transition On(in Input.Attack input)
{
GD.Print("Attacking...");
Output(new Output.Animations.Attack());
return To<Attacking>();
}
}
}
}

View File

@@ -8,7 +8,7 @@ public partial class PlayerLogic
public partial record State
{
[Meta, Id("player_logic_alive")]
public abstract partial record Alive : State, IGet<Input.PhysicsTick>, IGet<Input.Die>
public partial record Alive : State, IGet<Input.PhysicsTick>, IGet<Input.Die>
{
public virtual Transition On(in Input.PhysicsTick input)
{

View File

@@ -16,7 +16,7 @@ public partial class PlayerLogic
OnDetach(() => Get<IAppRepo>().GameEntered -= OnGameEntered);
}
public Transition On(in Input.Enable input) => To<Idle>();
public Transition On(in Input.Enable input) => To<Alive>();
public void OnGameEntered() => Input(new Input.Enable());
}

View File

@@ -0,0 +1,14 @@
using Chickensoft.Introspection;
using Chickensoft.Serialization;
namespace Zennysoft.Ma.Adapter;
[Meta, Id("quest_data")]
public partial record QuestData
{
[Save("death_count")]
public int DeathCount { get; set; } = 0;
[Save("quest_data_1")]
public bool QuestMarker1 { get; set; } = false;
}

View File

@@ -4,28 +4,28 @@ using Zennysoft.Game.Abstractions;
namespace Zennysoft.Ma.Adapter;
public interface IMaSaveFileManager<T>
public interface IMaSaveFileManager
{
Task Save(T gameData);
Task Save<T>(T gameData);
Task<T?> Load();
Task<T?> Load<T>();
}
public sealed class MaSaveFileManager<T> : IMaSaveFileManager<T>
public sealed class MaSaveFileManager : IMaSaveFileManager
{
private readonly ISaveFileManager<T> _saveFileManager;
private readonly ISaveFileManager _saveFileManager;
private ImmutableList<IJsonTypeInfoResolver> _converters;
public MaSaveFileManager(ISaveFileManager<T> saveFileManager)
public MaSaveFileManager(ISaveFileManager saveFileManager)
{
_saveFileManager = saveFileManager;
_converters = [WeaponTagEnumContext.Default, ItemTagEnumContext.Default, ElementTypeEnumContext.Default, AccessoryTagEnumContext.Default, ThrowableItemTagEnumContext.Default, UsableItemTagEnumContext.Default, BoxItemTagEnumContext.Default];
_converters = [WeaponTagEnumContext.Default, ItemTagEnumContext.Default, ElementTypeEnumContext.Default, AccessoryTagEnumContext.Default, UsableItemTagEnumContext.Default, BoxItemTagEnumContext.Default];
}
public async Task Save(T gameData)
public async Task Save<T>(T gameData)
{
await _saveFileManager.WriteToFile(gameData, [.. _converters]);
}
public async Task<T?> Load() => await _saveFileManager.ReadFromFile([.. _converters]);
public async Task<T?> Load<T>() => await _saveFileManager.ReadFromFile<T>([.. _converters]);
}

View File

@@ -1,5 +1,6 @@
using Chickensoft.Introspection;
using Chickensoft.LogicBlocks;
using Zennysoft.Game.Implementation;
namespace Zennysoft.Ma.Adapter;
@@ -37,7 +38,7 @@ public partial class InGameUILogic
Output(new Output.AnnounceMessageInInventory(message));
}
private void OnRemoveItemFromInventory(InventoryItem item) => Output(new Output.RemoveItemFromInventory(item));
private void OnRemoveItemFromInventory(IBaseInventoryItem item) => Output(new Output.RemoveItemFromInventory(item));
}
}

View File

@@ -1,4 +1,5 @@
using Zennysoft.Game.Ma;
using Zennysoft.Game.Implementation;
using Zennysoft.Game.Ma;
namespace Zennysoft.Ma.Adapter;
public partial class InGameUILogic
@@ -7,7 +8,7 @@ public partial class InGameUILogic
{
public readonly record struct AnnounceMessageOnMainScreen(string Message);
public readonly record struct AnnounceMessageInInventory(string Message);
public readonly record struct RemoveItemFromInventory(InventoryItem Item);
public readonly record struct RemoveItemFromInventory(IBaseInventoryItem Item);
public readonly record struct ShowInventory;
public readonly record struct HideInventory;
}

View File

@@ -7,12 +7,12 @@
</PropertyGroup>
<ItemGroup>
<Compile Remove="Game\state\states\**" />
<Compile Remove="Map\**" />
<EmbeddedResource Remove="Game\state\states\**" />
<EmbeddedResource Remove="Map\**" />
<None Remove="Game\state\states\**" />
<None Remove="Map\**" />
<Compile Remove="Actions\**" />
<Compile Remove="Game\state\states\**" />
<EmbeddedResource Remove="Actions\**" />
<EmbeddedResource Remove="Game\state\states\**" />
<None Remove="Actions\**" />
<None Remove="Game\state\states\**" />
</ItemGroup>
<ItemGroup>

View File

@@ -1,51 +0,0 @@
<Project Sdk="Godot.NET.Sdk/4.4.0">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<EnableDynamicLoading>true</EnableDynamicLoading>
<!-- Use NativeAOT. -->
<PublishAot>true</PublishAot>
</PropertyGroup>
<ItemGroup>
<Compile Remove="src\items\weapons\models\**" />
<EmbeddedResource Remove="src\items\weapons\models\**" />
</ItemGroup>
<ItemGroup>
<!-- Root the assemblies to avoid trimming. -->
<TrimmerRootAssembly Include="GodotSharp" />
<TrimmerRootAssembly Include="$(TargetName)" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Chickensoft.AutoInject" Version="2.5.0" />
<PackageReference Include="Chickensoft.GodotNodeInterfaces" Version="2.4.0" />
<PackageReference Include="Chickensoft.Introspection" Version="2.2.0" />
<PackageReference Include="Chickensoft.Introspection.Generator" Version="2.2.0" />
<PackageReference Include="Chickensoft.LogicBlocks" Version="5.16.0" />
<PackageReference Include="Chickensoft.LogicBlocks.DiagramGenerator" Version="5.16.0" />
<PackageReference Include="Chickensoft.SaveFileBuilder" Version="1.1.0" />
<PackageReference Include="Chickensoft.Serialization.Godot" Version="0.7.6" />
<PackageReference Include="GodotSharp.SourceGenerators" Version="2.6.0-250131-2115.Release" />
<PackageReference Include="SimpleInjector" Version="5.5.0" />
<PackageReference Include="SSH.NET" Version="2024.2.0" />
<PackageReference Include="System.IO.Abstractions" Version="22.0.11" />
<PackageReference Include="Zeroconf" Version="3.7.16" />
</ItemGroup>
<ItemGroup>
<Folder Include="src\ui\dialogue\" />
</ItemGroup>
<ItemGroup>
<None Include=".editorconfig" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Zennysoft.Game.Godot.Implementation\Zennysoft.Game.Implementation.csproj" />
<ProjectReference Include="..\Zennysoft.Game.Ma.Implementation\Zennysoft.Ma.Adapter.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Update="Godot.SourceGenerators" Version="4.4.0-dev.2" />
</ItemGroup>
<ItemGroup>
<PackageReference Update="GodotSharp" Version="4.4.0-dev.2" />
</ItemGroup>
<ItemGroup>
<PackageReference Update="GodotSharpEditor" Version="4.4.0-dev.2" />
</ItemGroup>
</Project>

View File

@@ -1,68 +0,0 @@
using Godot;
using Godot.Collections;
using System.Linq;
using Zennysoft.Game.Ma;
[Tool]
public partial class DungeonFloorLayout : LayoutType
{
[Export]
public DungeonFloorSetType SetType
{
get => _setType;
set
{
_setType = value;
LayoutWithSpawnRate = [];
NotifyPropertyListChanged();
}
}
[ExportToolButton("Populate Map Data")]
public Callable PopulateMapList => Callable.From(() => PopulateDictionary(SetType));
[Export]
public Dictionary<string, float> LayoutWithSpawnRate { get; private set; }
[Export]
public Dictionary<EnemyType, float> EnemySpawnRates { get; set; } = default!;
private string _floorPath = "res://src/map/dungeon/floors/";
private DungeonFloorSetType _setType;
private void PopulateDictionary(DungeonFloorSetType setType)
{
var floorPath = _floorPath;
var floorType = string.Empty;
if (setType == DungeonFloorSetType.SetA)
floorType = "SetAFloors";
else if (setType == DungeonFloorSetType.SetB)
floorType = "SetBFloors";
var pathToScenes = $"{floorPath}/{floorType}";
var files = DirAccess.GetFilesAt(pathToScenes).Where(x => x.EndsWith(".tscn"));
var newMaps = new Dictionary<string, float>();
foreach (var file in files)
{
if (LayoutWithSpawnRate.ContainsKey($"{floorType}/{file}"))
{
var spawnRate = LayoutWithSpawnRate.TryGetValue($"{floorType}/{file}", out var currentSpawnRate);
newMaps.Add($"{floorType}/{file}", currentSpawnRate);
}
else
newMaps.Add($"{floorType}/{file}", 1.0f);
}
LayoutWithSpawnRate = newMaps;
NotifyPropertyListChanged();
}
public enum DungeonFloorSetType
{
SetA,
SetB
}
}

View File

@@ -1 +0,0 @@
uid://ci7o3nn4mdo8o

View File

@@ -1,22 +0,0 @@
#if TOOLS
using Godot;
using Zennysoft.Game.Ma;
[Tool]
public partial class DungeonFloorLayoutNode : EditorPlugin
{
public override void _EnterTree()
{
// Initialization of the plugin goes here.
var script = GD.Load<Script>("res://addons/dungeon_floor_layout/DungeonFloorLayout.cs");
var texture = GD.Load<Texture2D>("res://addons/dungeon_floor_layout/icon.png");
AddCustomType(nameof(DungeonFloorLayout), nameof(LayoutType), script, texture);
}
public override void _ExitTree()
{
// Clean-up of the plugin goes here.
RemoveCustomType(nameof(DungeonFloorLayout));
}
}
#endif

Binary file not shown.

Before

Width:  |  Height:  |  Size: 161 B

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