Re-introduce prototype code
This commit is contained in:
4
.editorconfig
Normal file
4
.editorconfig
Normal file
@@ -0,0 +1,4 @@
|
||||
[*.cs]
|
||||
|
||||
# CS8632: The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
|
||||
dotnet_diagnostic.CS8632.severity = silent
|
||||
@@ -20,4 +20,7 @@
|
||||
<ItemGroup>
|
||||
<Folder Include="src\map\dungeon\corridor\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include=".editorconfig" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
11
Properties/launchSettings.json
Normal file
11
Properties/launchSettings.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"profiles": {
|
||||
"Godot": {
|
||||
"commandName": "Executable",
|
||||
"executablePath": "%GODOT%",
|
||||
"commandLineArgs": "--path . --verbose",
|
||||
"workingDirectory": ".",
|
||||
"nativeDebugging": true
|
||||
}
|
||||
}
|
||||
}
|
||||
8
global.json
Normal file
8
global.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"sdk": {
|
||||
"version": "8.0.400"
|
||||
},
|
||||
"msbuild-sdks": {
|
||||
"Godot.NET.Sdk": "4.3.0"
|
||||
}
|
||||
}
|
||||
@@ -69,43 +69,7 @@ Inventory={
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":69,"key_label":0,"unicode":101,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
move_forward={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
move_backward={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
move_right={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":68,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
move_left={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":65,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
move_sprint={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194325,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
move_jump={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":32,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
move_crouch={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194326,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
move_fly_mode={
|
||||
"deadzone": 0.5,
|
||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":70,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||
]
|
||||
}
|
||||
|
||||
[physics]
|
||||
|
||||
common/physics_ticks_per_second=144
|
||||
|
||||
61
src/app/App.cs
Normal file
61
src/app/App.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using Chickensoft.AutoInject;
|
||||
using Chickensoft.GodotNodeInterfaces;
|
||||
using Chickensoft.Introspection;
|
||||
using Godot;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public interface IApp : ICanvasLayer, IProvide<IAppRepo>;
|
||||
|
||||
[Meta(typeof(IAutoNode))]
|
||||
public partial class App : CanvasLayer, IApp
|
||||
{
|
||||
public override void _Notification(int what) => this.Notify(what);
|
||||
|
||||
public const string GAME_SCENE_PATH = "res://src/game/Game.tscn";
|
||||
|
||||
public IGame Game { get; set; } = default!;
|
||||
|
||||
public IInstantiator Instantiator { get; set; } = default!;
|
||||
|
||||
IAppRepo IProvide<IAppRepo>.Value() => AppRepo;
|
||||
|
||||
public IAppRepo AppRepo { get; set; } = default!;
|
||||
public IAppLogic AppLogic { get; set; } = default!;
|
||||
public AppLogic.IBinding AppBinding { get; set; } = default!;
|
||||
|
||||
[Node] public ISubViewport GameWindow { get; set; } = default!;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
Instantiator = new Instantiator(GetTree());
|
||||
AppRepo = new AppRepo();
|
||||
AppLogic = new AppLogic();
|
||||
AppLogic.Set(AppRepo);
|
||||
|
||||
this.Provide();
|
||||
}
|
||||
|
||||
public void OnReady()
|
||||
{
|
||||
Game = Instantiator.LoadAndInstantiate<Game>(GAME_SCENE_PATH);
|
||||
GameWindow.AddChildEx(Game);
|
||||
AppBinding = AppLogic.Bind();
|
||||
AppLogic.Start();
|
||||
|
||||
Instantiator.SceneTree.Paused = false;
|
||||
}
|
||||
|
||||
|
||||
public void OnNewGame() => AppLogic.Input(new AppLogic.Input.NewGame());
|
||||
|
||||
public void OnQuit() => AppLogic.Input(new AppLogic.Input.QuitGame());
|
||||
|
||||
public void OnExitTree()
|
||||
{
|
||||
AppLogic.Stop();
|
||||
AppBinding.Dispose();
|
||||
AppRepo.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
103
src/app/App.tscn
103
src/app/App.tscn
@@ -1,89 +1,22 @@
|
||||
[gd_scene load_steps=8 format=3 uid="uid://cagfc5ridmteu"]
|
||||
[gd_scene load_steps=2 format=3 uid="uid://cagfc5ridmteu"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://wg25dg65ksgg" path="res://src/map/dungeon/DungeonGenerator.tscn" id="1_eapuk"]
|
||||
[ext_resource type="PackedScene" uid="uid://dhpwwqow1ahrc" path="res://src/map/dungeon/rooms/Room1.tscn" id="2_x112h"]
|
||||
[ext_resource type="Script" path="res://addons/SimpleDungeons/DungeonRoom3D.gd" id="3_614ds"]
|
||||
[ext_resource type="PackedScene" uid="uid://bn4gslp2gk8ds" path="res://src/map/dungeon/corridor/Corridor.tscn" id="4_tex41"]
|
||||
[ext_resource type="Script" path="res://src/character_controller_3d.gd" id="4_vtqs3"]
|
||||
[ext_resource type="Script" path="res://src/app/App.cs" id="1_rt73h"]
|
||||
|
||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_ult2r"]
|
||||
radius = 0.25
|
||||
height = 0.5
|
||||
[node name="App" type="CanvasLayer"]
|
||||
script = ExtResource("1_rt73h")
|
||||
|
||||
[sub_resource type="Environment" id="Environment_3njg4"]
|
||||
[node name="SubViewportContainer" type="SubViewportContainer" parent="."]
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
stretch = true
|
||||
|
||||
[node name="App" type="Node"]
|
||||
|
||||
[node name="DungeonGenerator3D" parent="." instance=ExtResource("1_eapuk")]
|
||||
|
||||
[node name="RoomsContainer" type="Node3D" parent="DungeonGenerator3D"]
|
||||
|
||||
[node name="DungeonRoom3D_0" type="Node3D" parent="DungeonGenerator3D/RoomsContainer" instance=ExtResource("2_x112h")]
|
||||
transform = Transform3D(1.19249e-08, 0, -1, 0, 1, 0, 1, 0, 1.19249e-08, 35, 0, 45)
|
||||
script = ExtResource("3_614ds")
|
||||
|
||||
[node name="DungeonRoom3D_1" type="Node3D" parent="DungeonGenerator3D/RoomsContainer" instance=ExtResource("2_x112h")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -25, 0, 35)
|
||||
script = ExtResource("3_614ds")
|
||||
|
||||
[node name="Corridor_2" type="Node3D" parent="DungeonGenerator3D/RoomsContainer" instance=ExtResource("4_tex41")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 25, 0, 45)
|
||||
script = ExtResource("3_614ds")
|
||||
|
||||
[node name="Corridor_3" type="Node3D" parent="DungeonGenerator3D/RoomsContainer" instance=ExtResource("4_tex41")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 15, 0, 45)
|
||||
script = ExtResource("3_614ds")
|
||||
|
||||
[node name="Corridor_4" type="Node3D" parent="DungeonGenerator3D/RoomsContainer" instance=ExtResource("4_tex41")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 5, 0, 45)
|
||||
script = ExtResource("3_614ds")
|
||||
|
||||
[node name="Corridor_5" type="Node3D" parent="DungeonGenerator3D/RoomsContainer" instance=ExtResource("4_tex41")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5, 0, 45)
|
||||
script = ExtResource("3_614ds")
|
||||
|
||||
[node name="Corridor_6" type="Node3D" parent="DungeonGenerator3D/RoomsContainer" instance=ExtResource("4_tex41")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -15, 0, 45)
|
||||
script = ExtResource("3_614ds")
|
||||
|
||||
[node name="Corridor_7" type="Node3D" parent="DungeonGenerator3D/RoomsContainer" instance=ExtResource("4_tex41")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -25, 0, 45)
|
||||
script = ExtResource("3_614ds")
|
||||
|
||||
[node name="Corridor_8" type="Node3D" parent="DungeonGenerator3D/RoomsContainer" instance=ExtResource("4_tex41")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 45, 0, 45)
|
||||
script = ExtResource("3_614ds")
|
||||
|
||||
[node name="Corridor_9" type="Node3D" parent="DungeonGenerator3D/RoomsContainer" instance=ExtResource("4_tex41")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -25, 0, 25)
|
||||
script = ExtResource("3_614ds")
|
||||
|
||||
[node name="Corridor_10" type="Node3D" parent="DungeonGenerator3D/RoomsContainer" instance=ExtResource("4_tex41")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -15, 0, 25)
|
||||
script = ExtResource("3_614ds")
|
||||
|
||||
[node name="Corridor_11" type="Node3D" parent="DungeonGenerator3D/RoomsContainer" instance=ExtResource("4_tex41")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5, 0, 25)
|
||||
script = ExtResource("3_614ds")
|
||||
|
||||
[node name="Corridor_12" type="Node3D" parent="DungeonGenerator3D/RoomsContainer" instance=ExtResource("4_tex41")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 5, 0, 25)
|
||||
script = ExtResource("3_614ds")
|
||||
|
||||
[node name="Corridor_13" type="Node3D" parent="DungeonGenerator3D/RoomsContainer" instance=ExtResource("4_tex41")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 5, 0, 35)
|
||||
script = ExtResource("3_614ds")
|
||||
|
||||
[node name="CharacterBody3D" type="CharacterBody3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 14.0223, -3.8587, -16.1332)
|
||||
script = ExtResource("4_vtqs3")
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="CharacterBody3D"]
|
||||
shape = SubResource("CapsuleShape3D_ult2r")
|
||||
|
||||
[node name="OmniLight3D" type="OmniLight3D" parent="CharacterBody3D"]
|
||||
|
||||
[node name="Camera3D" type="Camera3D" parent="CharacterBody3D"]
|
||||
|
||||
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
|
||||
environment = SubResource("Environment_3njg4")
|
||||
[node name="GameWindow" type="SubViewport" parent="SubViewportContainer"]
|
||||
unique_name_in_owner = true
|
||||
transparent_bg = true
|
||||
handle_input_locally = false
|
||||
audio_listener_enable_3d = true
|
||||
size = Vector2i(1920, 1080)
|
||||
render_target_update_mode = 4
|
||||
|
||||
68
src/app/domain/AppRepo.cs
Normal file
68
src/app/domain/AppRepo.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using System;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public interface IAppRepo : IDisposable
|
||||
{
|
||||
event Action? GameEntered;
|
||||
|
||||
event Action? GameExited;
|
||||
|
||||
event Action? SplashScreenSkipped;
|
||||
|
||||
event Action? MainMenuEntered;
|
||||
|
||||
void SkipSplashScreen();
|
||||
|
||||
void OnMainMenuEntered();
|
||||
|
||||
void OnEnterGame();
|
||||
|
||||
void OnExitGame();
|
||||
|
||||
void OnGameOver();
|
||||
}
|
||||
|
||||
public class AppRepo : IAppRepo
|
||||
{
|
||||
public event Action? SplashScreenSkipped;
|
||||
public event Action? MainMenuEntered;
|
||||
public event Action? GameEntered;
|
||||
public event Action? GameExited;
|
||||
|
||||
private bool _disposedValue;
|
||||
|
||||
public void SkipSplashScreen() => SplashScreenSkipped?.Invoke();
|
||||
|
||||
public void OnMainMenuEntered() => MainMenuEntered?.Invoke();
|
||||
|
||||
public void OnEnterGame() => GameEntered?.Invoke();
|
||||
|
||||
public void OnExitGame() => GameExited?.Invoke();
|
||||
|
||||
public void OnGameOver() => GameExited?.Invoke();
|
||||
|
||||
protected void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
// Dispose managed objects.
|
||||
SplashScreenSkipped = null;
|
||||
MainMenuEntered = null;
|
||||
GameEntered = null;
|
||||
GameExited = null;
|
||||
}
|
||||
|
||||
_disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
18
src/app/state/AppLogic.Input.cs
Normal file
18
src/app/state/AppLogic.Input.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public partial class AppLogic
|
||||
{
|
||||
public static class Input
|
||||
{
|
||||
public readonly record struct NewGame;
|
||||
|
||||
public readonly record struct FadeInFinished;
|
||||
|
||||
public readonly record struct FadeOutFinished;
|
||||
|
||||
public readonly record struct QuitGame;
|
||||
|
||||
public readonly record struct GameOver;
|
||||
}
|
||||
}
|
||||
}
|
||||
30
src/app/state/AppLogic.Output.cs
Normal file
30
src/app/state/AppLogic.Output.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public partial class AppLogic
|
||||
{
|
||||
public static class Output
|
||||
{
|
||||
public readonly record struct FadeToBlack;
|
||||
|
||||
public readonly record struct ShowSplashScreen;
|
||||
|
||||
public readonly record struct HideSplashScreen;
|
||||
|
||||
public readonly record struct RemoveExistingGame;
|
||||
|
||||
public readonly record struct PlayGame;
|
||||
|
||||
public readonly record struct ShowGame;
|
||||
|
||||
public readonly record struct HideGame;
|
||||
|
||||
public readonly record struct SetupGameScene();
|
||||
|
||||
public readonly record struct ShowMainMenu;
|
||||
|
||||
public readonly record struct ExitGame;
|
||||
|
||||
public readonly record struct GameOver;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
src/app/state/AppLogic.State.cs
Normal file
11
src/app/state/AppLogic.State.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Chickensoft.Introspection;
|
||||
using Chickensoft.LogicBlocks;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public partial class AppLogic
|
||||
{
|
||||
[Meta]
|
||||
public abstract partial record State : StateLogic<State>;
|
||||
}
|
||||
}
|
||||
14
src/app/state/AppLogic.cs
Normal file
14
src/app/state/AppLogic.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using Chickensoft.Introspection;
|
||||
using Chickensoft.LogicBlocks;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public interface IAppLogic : ILogicBlock<AppLogic.State>;
|
||||
|
||||
[Meta]
|
||||
[LogicBlock(typeof(State), Diagram = true)]
|
||||
public partial class AppLogic : LogicBlock<AppLogic.State>, IAppLogic
|
||||
{
|
||||
public override Transition GetInitialState() => To<State.SplashScreen>();
|
||||
}
|
||||
}
|
||||
25
src/app/state/AppLogic.g.puml
Normal file
25
src/app/state/AppLogic.g.puml
Normal file
@@ -0,0 +1,25 @@
|
||||
@startuml AppLogic
|
||||
state "AppLogic State" as GameJam2024Practice_AppLogic_State {
|
||||
state "InGame" as GameJam2024Practice_AppLogic_State_InGame
|
||||
state "MainMenu" as GameJam2024Practice_AppLogic_State_MainMenu
|
||||
state "SplashScreen" as GameJam2024Practice_AppLogic_State_SplashScreen
|
||||
state "LeavingMenu" as GameJam2024Practice_AppLogic_State_LeavingMenu
|
||||
}
|
||||
|
||||
GameJam2024Practice_AppLogic_State_InGame --> GameJam2024Practice_AppLogic_State_MainMenu : GameOver
|
||||
GameJam2024Practice_AppLogic_State_LeavingMenu --> GameJam2024Practice_AppLogic_State_InGame : FadeOutFinished
|
||||
GameJam2024Practice_AppLogic_State_MainMenu --> GameJam2024Practice_AppLogic_State_LeavingMenu : NewGame
|
||||
GameJam2024Practice_AppLogic_State_MainMenu --> GameJam2024Practice_AppLogic_State_MainMenu : QuitGame
|
||||
GameJam2024Practice_AppLogic_State_SplashScreen --> GameJam2024Practice_AppLogic_State_MainMenu : FadeOutFinished
|
||||
|
||||
GameJam2024Practice_AppLogic_State_InGame : OnEnter → ShowGame
|
||||
GameJam2024Practice_AppLogic_State_InGame : OnExit → HideGame
|
||||
GameJam2024Practice_AppLogic_State_InGame : OnGameOver → RemoveExistingGame
|
||||
GameJam2024Practice_AppLogic_State_LeavingMenu : OnEnter → FadeToBlack
|
||||
GameJam2024Practice_AppLogic_State_MainMenu : OnEnter → SetupGameScene, ShowMainMenu
|
||||
GameJam2024Practice_AppLogic_State_MainMenu : OnQuitGame → ExitGame
|
||||
GameJam2024Practice_AppLogic_State_SplashScreen : OnEnter → ShowSplashScreen
|
||||
GameJam2024Practice_AppLogic_State_SplashScreen : OnSplashScreenSkipped() → HideSplashScreen
|
||||
|
||||
[*] --> GameJam2024Practice_AppLogic_State_SplashScreen
|
||||
@enduml
|
||||
37
src/app/state/states/InGame.cs
Normal file
37
src/app/state/states/InGame.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using Chickensoft.Introspection;
|
||||
using Chickensoft.LogicBlocks;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public partial class AppLogic
|
||||
{
|
||||
public partial record State
|
||||
{
|
||||
[Meta]
|
||||
public partial record InGame : State, IGet<Input.GameOver>
|
||||
{
|
||||
public InGame()
|
||||
{
|
||||
|
||||
this.OnEnter(() =>
|
||||
{
|
||||
Get<IAppRepo>().OnEnterGame();
|
||||
Output(new Output.ShowGame());
|
||||
});
|
||||
this.OnExit(() => Output(new Output.HideGame()));
|
||||
|
||||
OnAttach(() => Get<IAppRepo>().GameExited += OnGameExited);
|
||||
OnDetach(() => Get<IAppRepo>().GameExited -= OnGameExited);
|
||||
}
|
||||
|
||||
public Transition On(in Input.GameOver input)
|
||||
{
|
||||
Output(new Output.RemoveExistingGame());
|
||||
return To<MainMenu>();
|
||||
}
|
||||
|
||||
public void OnGameExited() => Input(new Input.GameOver());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
22
src/app/state/states/LeavingMenu.cs
Normal file
22
src/app/state/states/LeavingMenu.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using Chickensoft.Introspection;
|
||||
using Chickensoft.LogicBlocks;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public partial class AppLogic
|
||||
{
|
||||
public partial record State
|
||||
{
|
||||
[Meta]
|
||||
public partial record LeavingMenu : State, IGet<Input.FadeOutFinished>
|
||||
{
|
||||
public LeavingMenu()
|
||||
{
|
||||
this.OnEnter(() => Output(new Output.FadeToBlack()));
|
||||
}
|
||||
|
||||
public Transition On(in Input.FadeOutFinished input) => To<InGame>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
35
src/app/state/states/MainMenu.cs
Normal file
35
src/app/state/states/MainMenu.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using Chickensoft.Introspection;
|
||||
using Chickensoft.LogicBlocks;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public partial class AppLogic
|
||||
{
|
||||
public partial record State
|
||||
{
|
||||
[Meta]
|
||||
public partial record MainMenu : State, IGet<Input.NewGame>, IGet<Input.QuitGame>
|
||||
{
|
||||
public MainMenu()
|
||||
{
|
||||
this.OnEnter(() =>
|
||||
{
|
||||
Output(new Output.SetupGameScene());
|
||||
|
||||
Get<IAppRepo>().OnMainMenuEntered();
|
||||
|
||||
Output(new Output.ShowMainMenu());
|
||||
});
|
||||
}
|
||||
public Transition On(in Input.NewGame input) => To<LeavingMenu>();
|
||||
|
||||
public Transition On(in Input.QuitGame input)
|
||||
{
|
||||
Output(new Output.ExitGame());
|
||||
|
||||
return ToSelf();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
33
src/app/state/states/SplashScreen.cs
Normal file
33
src/app/state/states/SplashScreen.cs
Normal file
@@ -0,0 +1,33 @@
|
||||
using Chickensoft.Introspection;
|
||||
using Chickensoft.LogicBlocks;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public partial class AppLogic
|
||||
{
|
||||
public partial record State
|
||||
{
|
||||
[Meta]
|
||||
public partial record SplashScreen : State, IGet<Input.FadeOutFinished>
|
||||
{
|
||||
public SplashScreen()
|
||||
{
|
||||
this.OnEnter(() => Output(new Output.ShowSplashScreen()));
|
||||
|
||||
OnAttach(
|
||||
() => Get<IAppRepo>().SplashScreenSkipped += OnSplashScreenSkipped
|
||||
);
|
||||
|
||||
OnDetach(
|
||||
() => Get<IAppRepo>().SplashScreenSkipped -= OnSplashScreenSkipped
|
||||
);
|
||||
}
|
||||
|
||||
public Transition On(in Input.FadeOutFinished input) => To<MainMenu>();
|
||||
|
||||
public void OnSplashScreenSkipped() =>
|
||||
Output(new Output.HideSplashScreen());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
extends CharacterBody3D
|
||||
|
||||
@export var FORWARD_SPEED = 2.0
|
||||
@export var BACK_SPEED = 1.0
|
||||
@export var TURN_SPEED = 0.025
|
||||
|
||||
var Vec3Z = Vector3.ZERO
|
||||
|
||||
#OPTIONAL: These could be used to change sensitivity of either rotating z or y
|
||||
#var M_LOOK_SENS = 1
|
||||
#var V_LOOK_SENS = 1
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if Input.is_action_pressed("move_forward") and Input.is_action_pressed("move_backward"):
|
||||
velocity.x = 0
|
||||
velocity.z = 0
|
||||
|
||||
elif Input.is_action_pressed("move_forward"):
|
||||
var forwardVector = -Vector3.FORWARD.rotated(Vector3.UP, rotation.y)
|
||||
velocity = -forwardVector * FORWARD_SPEED
|
||||
|
||||
elif Input.is_action_pressed("move_backward"):
|
||||
var backwardVector = Vector3.FORWARD.rotated(Vector3.UP, rotation.y)
|
||||
velocity = -backwardVector * BACK_SPEED
|
||||
|
||||
#If pressing nothing stop velocity
|
||||
else:
|
||||
velocity.x = 0
|
||||
velocity.z = 0
|
||||
|
||||
# IF turn left WHILE moving back, turn right
|
||||
if Input.is_action_pressed("move_left") and Input.is_action_pressed("move_backward"):
|
||||
rotation.z -= Vec3Z.y + TURN_SPEED #* V_LOOK_SENS
|
||||
rotation.z = clamp(rotation.x, -50, 90)
|
||||
rotation.y -= Vec3Z.y + TURN_SPEED #* M_LOOK_SENS
|
||||
|
||||
elif Input.is_action_pressed("move_left"):
|
||||
rotation.z += Vec3Z.y - TURN_SPEED #* V_LOOK_SENS
|
||||
rotation.z = clamp(rotation.x, -50, 90)
|
||||
rotation.y += Vec3Z.y + TURN_SPEED #* M_LOOK_SENS
|
||||
|
||||
# IF turn right WHILE moving back, turn left
|
||||
if Input.is_action_pressed("move_right") and Input.is_action_pressed("move_backward"):
|
||||
rotation.z += Vec3Z.y - TURN_SPEED #* V_LOOK_SENS
|
||||
rotation.z = clamp(rotation.x, -50, 90)
|
||||
rotation.y += Vec3Z.y + TURN_SPEED #* M_LOOK_SENS
|
||||
|
||||
elif Input.is_action_pressed("move_right"):
|
||||
rotation.z -= Vec3Z.y + TURN_SPEED #* V_LOOK_SENS
|
||||
rotation.z = clamp(rotation.x, -50, 90)
|
||||
rotation.y -= Vec3Z.y + TURN_SPEED #* M_LOOK_SENS
|
||||
|
||||
move_and_slide()
|
||||
77
src/game/Game.cs
Normal file
77
src/game/Game.cs
Normal file
@@ -0,0 +1,77 @@
|
||||
namespace GameJamDungeon;
|
||||
|
||||
using Chickensoft.AutoInject;
|
||||
using Chickensoft.GodotNodeInterfaces;
|
||||
using Chickensoft.Introspection;
|
||||
using Godot;
|
||||
|
||||
public interface IGame : IProvide<IGameRepo>, INode3D
|
||||
{
|
||||
}
|
||||
|
||||
[Meta(typeof(IAutoNode))]
|
||||
public partial class Game : Node3D, IGame
|
||||
{
|
||||
public override void _Notification(int what) => this.Notify(what);
|
||||
|
||||
IGameRepo IProvide<IGameRepo>.Value() => GameRepo;
|
||||
|
||||
public IGameLogic GameLogic { get; set; } = default!;
|
||||
|
||||
public IGameRepo GameRepo { get; set; } = default!;
|
||||
|
||||
public GameLogic.IBinding GameBinding { get; set; } = default!;
|
||||
|
||||
[Dependency] public IAppRepo AppRepo => this.DependOn<IAppRepo>();
|
||||
|
||||
public void Setup()
|
||||
{
|
||||
GameRepo = new GameRepo();
|
||||
GameLogic = new GameLogic();
|
||||
GameLogic.Set(GameRepo);
|
||||
GameLogic.Set(AppRepo);
|
||||
}
|
||||
|
||||
public void OnResolved()
|
||||
{
|
||||
GameBinding = GameLogic.Bind();
|
||||
GameBinding
|
||||
.Handle((in GameLogic.Output.StartGame _) => { GameRepo.Resume(); })
|
||||
.Handle((in GameLogic.Output.SetPauseMode output) => { CallDeferred(nameof(SetPauseMode), output.IsPaused); })
|
||||
.Handle((in GameLogic.Output.GameOver _) => { AppRepo.OnGameOver(); });
|
||||
|
||||
GameLogic.Input(new GameLogic.Input.Initialize());
|
||||
|
||||
this.Provide();
|
||||
GameLogic.Start();
|
||||
}
|
||||
|
||||
public override void _Input(InputEvent @event)
|
||||
{
|
||||
if (Input.IsActionJustPressed(GameInputs.Inventory))
|
||||
{
|
||||
GD.Print("Inventory button pressed");
|
||||
GameLogic.Input(new GameLogic.Input.InventoryMenuButtonPressed());
|
||||
}
|
||||
|
||||
if (Input.IsActionJustPressed(GameInputs.MiniMap))
|
||||
{
|
||||
GD.Print("MiniMap button pressed");
|
||||
GameLogic.Input(new GameLogic.Input.MiniMapButtonPressed());
|
||||
}
|
||||
|
||||
if (Input.IsActionJustReleased(GameInputs.MiniMap))
|
||||
{
|
||||
GD.Print("MiniMap button released");
|
||||
GameLogic.Input(new GameLogic.Input.MiniMapButtonReleased());
|
||||
}
|
||||
}
|
||||
|
||||
private void SetPauseMode(bool isPaused)
|
||||
{
|
||||
if (GetTree() != null)
|
||||
GetTree().Paused = isPaused;
|
||||
}
|
||||
|
||||
public void OnStart() => GameLogic.Input(new GameLogic.Input.Start());
|
||||
}
|
||||
83
src/game/Game.tscn
Normal file
83
src/game/Game.tscn
Normal file
@@ -0,0 +1,83 @@
|
||||
[gd_scene load_steps=8 format=3 uid="uid://33ek675mfb5n"]
|
||||
|
||||
[ext_resource type="Script" path="res://src/game/Game.cs" id="1_ytcii"]
|
||||
[ext_resource type="PackedScene" uid="uid://wg25dg65ksgg" path="res://src/map/dungeon/DungeonGenerator.tscn" id="2_cgboj"]
|
||||
[ext_resource type="PackedScene" uid="uid://cfecvvav8kkp6" path="res://src/player/Player.tscn" id="3_kk6ly"]
|
||||
[ext_resource type="PackedScene" uid="uid://dhpwwqow1ahrc" path="res://src/map/dungeon/rooms/Room1.tscn" id="4_56rmd"]
|
||||
[ext_resource type="Script" path="res://addons/SimpleDungeons/DungeonRoom3D.gd" id="5_5i8m5"]
|
||||
[ext_resource type="PackedScene" uid="uid://bn4gslp2gk8ds" path="res://src/map/dungeon/corridor/Corridor.tscn" id="6_5fcqc"]
|
||||
|
||||
[sub_resource type="Environment" id="Environment_fke5g"]
|
||||
|
||||
[node name="Game" type="Node3D"]
|
||||
script = ExtResource("1_ytcii")
|
||||
|
||||
[node name="DungeonGenerator3D" parent="." instance=ExtResource("2_cgboj")]
|
||||
|
||||
[node name="WorldEnvironment" type="WorldEnvironment" parent="DungeonGenerator3D"]
|
||||
environment = SubResource("Environment_fke5g")
|
||||
|
||||
[node name="Player" parent="DungeonGenerator3D" instance=ExtResource("3_kk6ly")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5.55927, -4.70883, 25.1515)
|
||||
|
||||
[node name="RoomsContainer" type="Node3D" parent="DungeonGenerator3D"]
|
||||
|
||||
[node name="DungeonRoom3D_0" type="Node3D" parent="DungeonGenerator3D/RoomsContainer" instance=ExtResource("4_56rmd")]
|
||||
transform = Transform3D(1.19249e-08, 0, -1, 0, 1, 0, 1, 0, 1.19249e-08, 5, 0, 15)
|
||||
script = ExtResource("5_5i8m5")
|
||||
|
||||
[node name="DungeonRoom3D_1" type="Node3D" parent="DungeonGenerator3D/RoomsContainer" instance=ExtResource("4_56rmd")]
|
||||
transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, -25, 0, -25)
|
||||
script = ExtResource("5_5i8m5")
|
||||
|
||||
[node name="Corridor_2" type="Node3D" parent="DungeonGenerator3D/RoomsContainer" instance=ExtResource("6_5fcqc")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5, 0, 15)
|
||||
script = ExtResource("5_5i8m5")
|
||||
|
||||
[node name="Corridor_3" type="Node3D" parent="DungeonGenerator3D/RoomsContainer" instance=ExtResource("6_5fcqc")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5, 0, 5)
|
||||
script = ExtResource("5_5i8m5")
|
||||
|
||||
[node name="Corridor_4" type="Node3D" parent="DungeonGenerator3D/RoomsContainer" instance=ExtResource("6_5fcqc")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5, 0, -5)
|
||||
script = ExtResource("5_5i8m5")
|
||||
|
||||
[node name="Corridor_5" type="Node3D" parent="DungeonGenerator3D/RoomsContainer" instance=ExtResource("6_5fcqc")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -5, 0, -15)
|
||||
script = ExtResource("5_5i8m5")
|
||||
|
||||
[node name="Corridor_6" type="Node3D" parent="DungeonGenerator3D/RoomsContainer" instance=ExtResource("6_5fcqc")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -15, 0, -15)
|
||||
script = ExtResource("5_5i8m5")
|
||||
|
||||
[node name="Corridor_7" type="Node3D" parent="DungeonGenerator3D/RoomsContainer" instance=ExtResource("6_5fcqc")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -15, 0, -25)
|
||||
script = ExtResource("5_5i8m5")
|
||||
|
||||
[node name="Corridor_8" type="Node3D" parent="DungeonGenerator3D/RoomsContainer" instance=ExtResource("6_5fcqc")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 15, 0, 15)
|
||||
script = ExtResource("5_5i8m5")
|
||||
|
||||
[node name="Corridor_9" type="Node3D" parent="DungeonGenerator3D/RoomsContainer" instance=ExtResource("6_5fcqc")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 15, 0, 5)
|
||||
script = ExtResource("5_5i8m5")
|
||||
|
||||
[node name="Corridor_10" type="Node3D" parent="DungeonGenerator3D/RoomsContainer" instance=ExtResource("6_5fcqc")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 5, 0, 5)
|
||||
script = ExtResource("5_5i8m5")
|
||||
|
||||
[node name="Corridor_11" type="Node3D" parent="DungeonGenerator3D/RoomsContainer" instance=ExtResource("6_5fcqc")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -35, 0, -25)
|
||||
script = ExtResource("5_5i8m5")
|
||||
|
||||
[node name="Corridor_12" type="Node3D" parent="DungeonGenerator3D/RoomsContainer" instance=ExtResource("6_5fcqc")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -35, 0, -15)
|
||||
script = ExtResource("5_5i8m5")
|
||||
|
||||
[node name="Corridor_13" type="Node3D" parent="DungeonGenerator3D/RoomsContainer" instance=ExtResource("6_5fcqc")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -25, 0, -15)
|
||||
script = ExtResource("5_5i8m5")
|
||||
|
||||
[node name="Corridor_14" type="Node3D" parent="DungeonGenerator3D/RoomsContainer" instance=ExtResource("6_5fcqc")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -15, 0, -5)
|
||||
script = ExtResource("5_5i8m5")
|
||||
10
src/game/GameData.cs
Normal file
10
src/game/GameData.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using Chickensoft.Introspection;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
[Meta, Id("game_data")]
|
||||
public partial record GameData
|
||||
{
|
||||
public required PlayerData PlayerData { get; init; }
|
||||
}
|
||||
}
|
||||
20
src/game/GameLogic.Input.cs
Normal file
20
src/game/GameLogic.Input.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public partial class GameLogic
|
||||
{
|
||||
public static class Input
|
||||
{
|
||||
public readonly record struct Start;
|
||||
|
||||
public readonly record struct Initialize;
|
||||
|
||||
public readonly record struct InventoryMenuButtonPressed;
|
||||
|
||||
public readonly record struct MiniMapButtonPressed;
|
||||
|
||||
public readonly record struct MiniMapButtonReleased;
|
||||
|
||||
public readonly record struct GameOver;
|
||||
}
|
||||
}
|
||||
}
|
||||
24
src/game/GameLogic.Output.cs
Normal file
24
src/game/GameLogic.Output.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public partial class GameLogic
|
||||
{
|
||||
public static class Output
|
||||
{
|
||||
public readonly record struct StartGame();
|
||||
|
||||
public readonly record struct SetInventoryMode(List<InventoryItem> Inventory);
|
||||
|
||||
public readonly record struct HideInventory();
|
||||
|
||||
public readonly record struct SetPauseMode(bool IsPaused);
|
||||
|
||||
public readonly record struct ShowMiniMap();
|
||||
|
||||
public readonly record struct HideMiniMap();
|
||||
|
||||
public readonly record struct GameOver();
|
||||
}
|
||||
}
|
||||
}
|
||||
28
src/game/GameLogic.State.cs
Normal file
28
src/game/GameLogic.State.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using Chickensoft.Introspection;
|
||||
using Chickensoft.LogicBlocks;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public partial class GameLogic
|
||||
{
|
||||
[Meta]
|
||||
public abstract partial record State : StateLogic<State>
|
||||
{
|
||||
protected State()
|
||||
{
|
||||
OnAttach(() =>
|
||||
{
|
||||
var gameRepo = Get<IGameRepo>();
|
||||
gameRepo.IsPaused.Sync += OnIsPaused;
|
||||
});
|
||||
OnDetach(() =>
|
||||
{
|
||||
var gameRepo = Get<IGameRepo>();
|
||||
gameRepo.IsPaused.Sync -= OnIsPaused;
|
||||
});
|
||||
}
|
||||
|
||||
public void OnIsPaused(bool isPaused) => Output(new Output.SetPauseMode(isPaused));
|
||||
}
|
||||
}
|
||||
}
|
||||
14
src/game/GameLogic.cs
Normal file
14
src/game/GameLogic.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using Chickensoft.Introspection;
|
||||
using Chickensoft.LogicBlocks;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public interface IGameLogic : ILogicBlock<GameLogic.State>;
|
||||
|
||||
[Meta]
|
||||
[LogicBlock(typeof(State), Diagram = true)]
|
||||
public partial class GameLogic : LogicBlock<GameLogic.State>, IGameLogic
|
||||
{
|
||||
public override Transition GetInitialState() => To<State.MenuBackdrop>();
|
||||
}
|
||||
}
|
||||
98
src/game/IGameRepo.cs
Normal file
98
src/game/IGameRepo.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
using Chickensoft.Collections;
|
||||
using Godot;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public interface IGameRepo : IDisposable
|
||||
{
|
||||
event Action? Ended;
|
||||
|
||||
IAutoProp<List<InventoryItem>> InventoryItems { get; }
|
||||
|
||||
IAutoProp<bool> IsInventoryScreenOpened { get; }
|
||||
|
||||
IAutoProp<bool> IsPaused { get; }
|
||||
|
||||
void Pause();
|
||||
|
||||
void Resume();
|
||||
|
||||
IAutoProp<Vector3> PlayerGlobalPosition { get; }
|
||||
|
||||
void SetPlayerGlobalPosition(Vector3 playerGlobalPosition);
|
||||
}
|
||||
|
||||
public class GameRepo : IGameRepo
|
||||
{
|
||||
public event Action? Ended;
|
||||
|
||||
private readonly AutoProp<List<InventoryItem>> _inventoryItems;
|
||||
private readonly AutoProp<bool> _isInventoryScreenOpened;
|
||||
|
||||
public IAutoProp<List<InventoryItem>> InventoryItems => _inventoryItems;
|
||||
|
||||
public IAutoProp<bool> IsInventoryScreenOpened => _isInventoryScreenOpened;
|
||||
|
||||
public IAutoProp<Vector3> PlayerGlobalPosition => _playerGlobalPosition;
|
||||
private readonly AutoProp<Vector3> _playerGlobalPosition;
|
||||
|
||||
public IAutoProp<bool> IsPaused => _isPaused;
|
||||
private readonly AutoProp<bool> _isPaused;
|
||||
|
||||
private bool _disposedValue;
|
||||
|
||||
public GameRepo()
|
||||
{
|
||||
_inventoryItems = new AutoProp<List<InventoryItem>>([]);
|
||||
_isInventoryScreenOpened = new AutoProp<bool>(false);
|
||||
_isPaused = new AutoProp<bool>(false);
|
||||
_playerGlobalPosition = new AutoProp<Vector3>(Vector3.Zero);
|
||||
}
|
||||
|
||||
public void Pause()
|
||||
{
|
||||
_isPaused.OnNext(true);
|
||||
GD.Print("Paused");
|
||||
}
|
||||
|
||||
public void Resume()
|
||||
{
|
||||
_isPaused.OnNext(false);
|
||||
GD.Print("Resume");
|
||||
}
|
||||
|
||||
public void SetPlayerGlobalPosition(Vector3 playerGlobalPosition) => _playerGlobalPosition.OnNext(playerGlobalPosition);
|
||||
|
||||
public void OnGameEnded()
|
||||
{
|
||||
Pause();
|
||||
Ended?.Invoke();
|
||||
}
|
||||
|
||||
protected void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposedValue)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_playerGlobalPosition.OnCompleted();
|
||||
_playerGlobalPosition.Dispose();
|
||||
_inventoryItems.OnCompleted();
|
||||
_inventoryItems.Dispose();
|
||||
_isInventoryScreenOpened.OnCompleted();
|
||||
_isInventoryScreenOpened.Dispose();
|
||||
}
|
||||
|
||||
_disposedValue = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(disposing: true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
20
src/game/state/states/GameLogic.State.Quit.cs
Normal file
20
src/game/state/states/GameLogic.State.Quit.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using Chickensoft.Introspection;
|
||||
using Chickensoft.LogicBlocks;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public partial class GameLogic
|
||||
{
|
||||
public partial record State
|
||||
{
|
||||
[Meta]
|
||||
public partial record Quit : State
|
||||
{
|
||||
public Quit()
|
||||
{
|
||||
this.OnEnter(() => Output(new Output.GameOver()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
22
src/game/state/states/InventoryOpened.cs
Normal file
22
src/game/state/states/InventoryOpened.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using Chickensoft.Introspection;
|
||||
using Chickensoft.LogicBlocks;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public partial class GameLogic
|
||||
{
|
||||
public partial record State
|
||||
{
|
||||
[Meta]
|
||||
public partial record InventoryOpened : State, IGet<Input.InventoryMenuButtonPressed>
|
||||
{
|
||||
public InventoryOpened()
|
||||
{
|
||||
this.OnEnter(() => { Get<IGameRepo>().Pause(); Output(new Output.SetInventoryMode(Get<IGameRepo>().InventoryItems.Value)); });
|
||||
this.OnExit(() => { Output(new Output.HideInventory()); });
|
||||
}
|
||||
public Transition On(in Input.InventoryMenuButtonPressed input) => To<Playing>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
29
src/game/state/states/MenuBackdrop.cs
Normal file
29
src/game/state/states/MenuBackdrop.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
using Chickensoft.Introspection;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public partial class GameLogic
|
||||
{
|
||||
public partial record State
|
||||
{
|
||||
[Meta]
|
||||
public partial record MenuBackdrop : State, IGet<Input.Start>, IGet<Input.Initialize>
|
||||
{
|
||||
public MenuBackdrop()
|
||||
{
|
||||
OnAttach(() => Get<IAppRepo>().GameEntered += OnGameEntered);
|
||||
OnDetach(() => Get<IAppRepo>().GameEntered -= OnGameEntered);
|
||||
}
|
||||
|
||||
public void OnGameEntered() => Input(new Input.Start());
|
||||
|
||||
public Transition On(in Input.Start input) => To<Playing>();
|
||||
|
||||
public Transition On(in Input.Initialize input)
|
||||
{
|
||||
return ToSelf();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
23
src/game/state/states/MiniMapOpen.cs
Normal file
23
src/game/state/states/MiniMapOpen.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Chickensoft.Introspection;
|
||||
using Chickensoft.LogicBlocks;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public partial class GameLogic
|
||||
{
|
||||
public partial record State
|
||||
{
|
||||
[Meta]
|
||||
public partial record MinimapOpen : State, IGet<Input.MiniMapButtonReleased>
|
||||
{
|
||||
public MinimapOpen()
|
||||
{
|
||||
this.OnEnter(() => { Get<IGameRepo>().Pause(); Output(new Output.ShowMiniMap()); });
|
||||
this.OnExit(() => { Output(new Output.HideMiniMap()); });
|
||||
}
|
||||
|
||||
public Transition On(in Input.MiniMapButtonReleased input) => To<Playing>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
26
src/game/state/states/Paused.cs
Normal file
26
src/game/state/states/Paused.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
using Chickensoft.Introspection;
|
||||
using Chickensoft.LogicBlocks;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public partial class GameLogic
|
||||
{
|
||||
public partial record State
|
||||
{
|
||||
[Meta]
|
||||
public partial record Paused : State, IGet<Input.InventoryMenuButtonPressed>, IGet<Input.MiniMapButtonReleased>
|
||||
{
|
||||
public Paused()
|
||||
{
|
||||
this.OnEnter(() => Get<IGameRepo>().Pause());
|
||||
this.OnExit(() => Output(new Output.SetPauseMode(false)));
|
||||
}
|
||||
|
||||
|
||||
public virtual Transition On(in Input.InventoryMenuButtonPressed input) => To<Playing>();
|
||||
|
||||
public virtual Transition On(in Input.MiniMapButtonReleased input) => To<Playing>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
34
src/game/state/states/Playing.cs
Normal file
34
src/game/state/states/Playing.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using Chickensoft.Introspection;
|
||||
using Chickensoft.LogicBlocks;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public partial class GameLogic
|
||||
{
|
||||
public partial record State
|
||||
{
|
||||
[Meta]
|
||||
public partial record Playing : State, IGet<Input.InventoryMenuButtonPressed>, IGet<Input.MiniMapButtonPressed>, IGet<Input.GameOver>
|
||||
{
|
||||
public Playing()
|
||||
{
|
||||
this.OnEnter(() => { Output(new Output.StartGame()); Get<IGameRepo>().Resume(); });
|
||||
|
||||
OnAttach(() => Get<IGameRepo>().Ended += OnEnded);
|
||||
OnDetach(() => Get<IGameRepo>().Ended -= OnEnded);
|
||||
}
|
||||
|
||||
public void OnEnded() => Input(new Input.GameOver());
|
||||
|
||||
public Transition On(in Input.InventoryMenuButtonPressed input) => To<InventoryOpened>();
|
||||
|
||||
public Transition On(in Input.MiniMapButtonPressed input) => To<MinimapOpen>();
|
||||
|
||||
public Transition On(in Input.GameOver input)
|
||||
{
|
||||
return To<Quit>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
126
src/inventory_menu/InventoryMenu.cs
Normal file
126
src/inventory_menu/InventoryMenu.cs
Normal file
@@ -0,0 +1,126 @@
|
||||
using Chickensoft.AutoInject;
|
||||
using Chickensoft.GodotNodeInterfaces;
|
||||
using Chickensoft.Introspection;
|
||||
using GameJamDungeon;
|
||||
using Godot;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public interface IInventoryMenu : IControl
|
||||
{
|
||||
public void PopulateItems(List<InventoryItem> items);
|
||||
|
||||
public void ClearItems();
|
||||
}
|
||||
|
||||
[Meta(typeof(IAutoNode))]
|
||||
public partial class InventoryMenu : Control, IInventoryMenu
|
||||
{
|
||||
public override void _Notification(int what) => this.Notify(what);
|
||||
|
||||
[Node] public IVBoxContainer ItemList { get; set; } = default!;
|
||||
|
||||
[Node] public TextureRect Cursor { get; set; } = default!;
|
||||
|
||||
[Dependency] public IGameRepo GameRepo => this.DependOn<IGameRepo>();
|
||||
|
||||
private int _currentSelection = 0;
|
||||
|
||||
public void PopulateItems(List<InventoryItem> items)
|
||||
{
|
||||
foreach (var item in items)
|
||||
{
|
||||
var label = new WeaponLabel(item) { Text = item.Name };
|
||||
ItemList.AddChild(label);
|
||||
}
|
||||
|
||||
if (ItemList.GetChildCount() > 0)
|
||||
CallDeferred(nameof(InitializeInventoryMenu));
|
||||
}
|
||||
|
||||
public void InitializeInventoryMenu()
|
||||
{
|
||||
if (ItemList.GetChildCount() > 0)
|
||||
{
|
||||
var currentItem = ItemList.GetChild<Control>(_currentSelection);
|
||||
SetCursorLocation(currentItem);
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearItems()
|
||||
{
|
||||
foreach (var item in ItemList.GetChildren())
|
||||
ItemList.RemoveChild(item);
|
||||
}
|
||||
|
||||
public void SetCursorLocation(Control menuItem)
|
||||
{
|
||||
var position = menuItem.GlobalPosition;
|
||||
var size = menuItem.Size;
|
||||
|
||||
Cursor.GlobalPosition = new Vector2(position.X, position.Y + size.Y / 2.0f) - Cursor.Size / 2.0f - new Vector2(15, -5);
|
||||
}
|
||||
|
||||
public void SetCursorToPrevious()
|
||||
{
|
||||
if (ItemList.GetChildCount() == 0)
|
||||
return;
|
||||
|
||||
if (_currentSelection > 0)
|
||||
{
|
||||
_currentSelection -= 1;
|
||||
var selectedMenuItem = ItemList.GetChild<Control>(_currentSelection);
|
||||
SetCursorLocation(selectedMenuItem);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetCursorToNext()
|
||||
{
|
||||
if (ItemList.GetChildCount() == 0)
|
||||
return;
|
||||
|
||||
if (_currentSelection < ItemList.GetChildCount() - 1)
|
||||
{
|
||||
_currentSelection += 1;
|
||||
var selectedMenuItem = ItemList.GetChild<Control>(_currentSelection);
|
||||
SetCursorLocation(selectedMenuItem);
|
||||
}
|
||||
}
|
||||
|
||||
private void UnequipItem(WeaponLabel item)
|
||||
{
|
||||
item.UnequipItem();
|
||||
}
|
||||
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
var input = Vector2.Zero;
|
||||
if (Input.IsActionJustPressed(GameInputs.MoveUp))
|
||||
SetCursorToPrevious();
|
||||
if (Input.IsActionJustPressed(GameInputs.MoveDown))
|
||||
SetCursorToNext();
|
||||
}
|
||||
}
|
||||
|
||||
public partial class WeaponLabel : Label
|
||||
{
|
||||
public WeaponLabel(InventoryItem inventoryItem)
|
||||
{
|
||||
InventoryItem = inventoryItem;
|
||||
LabelSettings = UnequippedItemFont;
|
||||
}
|
||||
|
||||
public InventoryItem InventoryItem { get; set; } = default!;
|
||||
|
||||
private static LabelSettings UnequippedItemFont => GD.Load<LabelSettings>("res://src/vfx/Fonts/InventoryLabelSettings.tres");
|
||||
private static LabelSettings EquippedItemFont => GD.Load<LabelSettings>("res://src/vfx/Fonts/EquippedInventoryLabelSettings.tres");
|
||||
|
||||
public void EquipItem()
|
||||
{
|
||||
LabelSettings = EquippedItemFont;
|
||||
}
|
||||
|
||||
public void UnequipItem()
|
||||
{
|
||||
LabelSettings = UnequippedItemFont;
|
||||
}
|
||||
}
|
||||
8
src/items/ArmorItem.cs
Normal file
8
src/items/ArmorItem.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using Godot;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class ArmorItem : InventoryItem
|
||||
{
|
||||
[Export]
|
||||
public required int Defense { get; set; }
|
||||
}
|
||||
11
src/items/InventoryItem.cs
Normal file
11
src/items/InventoryItem.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Godot;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class InventoryItem : Resource
|
||||
{
|
||||
[Export]
|
||||
public string Name = string.Empty;
|
||||
|
||||
[Export]
|
||||
public string Description = string.Empty;
|
||||
}
|
||||
10
src/items/WeaponItem.cs
Normal file
10
src/items/WeaponItem.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using Godot;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class WeaponItem : InventoryItem
|
||||
{
|
||||
[Export]
|
||||
public required int Damage { get; set; }
|
||||
|
||||
public static WeaponItem Default => new WeaponItem() { Damage = 1 };
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_nsah4"]
|
||||
transparency = 1
|
||||
albedo_texture = ExtResource("2_6scux")
|
||||
|
||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_18cgv"]
|
||||
albedo_texture = ExtResource("2_6scux")
|
||||
@@ -40,19 +41,19 @@ size = Vector3(9, 9, 9)
|
||||
material = SubResource("StandardMaterial3D_18cgv")
|
||||
|
||||
[node name="DOOR?" parent="CSGBox3D" instance=ExtResource("2_vpnlr")]
|
||||
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, -5, -3.5, 0)
|
||||
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, -5, -2.58627, 0)
|
||||
material = SubResource("StandardMaterial3D_cquyy")
|
||||
|
||||
[node name="DOOR?3" parent="CSGBox3D" instance=ExtResource("2_vpnlr")]
|
||||
transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 5, -3.5, 0)
|
||||
transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 5, -2.58627, 0)
|
||||
material = SubResource("StandardMaterial3D_67t3u")
|
||||
|
||||
[node name="DOOR?4" parent="CSGBox3D" instance=ExtResource("2_vpnlr")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -3.5, 5)
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -2.58627, 5)
|
||||
material = SubResource("StandardMaterial3D_ej8w2")
|
||||
|
||||
[node name="DOOR?2" parent="CSGBox3D" instance=ExtResource("2_vpnlr")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -3.5, -5)
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -2.58627, -5)
|
||||
material = SubResource("StandardMaterial3D_oy7nu")
|
||||
|
||||
[node name="RemoveUnusedDoors" type="Node" parent="."]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[gd_scene format=3 uid="uid://ckaw6wjmi0fom"]
|
||||
|
||||
[node name="DOOR" type="CSGBox3D"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.01493, 0)
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.375916, 1.9271, 0)
|
||||
operation = 2
|
||||
size = Vector3(2, 2, 1)
|
||||
size = Vector3(2.75183, 3.82434, 1)
|
||||
|
||||
44
src/map/dungeon/rooms/DungeonRoom.cs
Normal file
44
src/map/dungeon/rooms/DungeonRoom.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using Chickensoft.AutoInject;
|
||||
using Chickensoft.GodotNodeInterfaces;
|
||||
using Chickensoft.Introspection;
|
||||
using GameJamDungeon;
|
||||
using Godot;
|
||||
|
||||
public interface IDungeonRoom : INode3D
|
||||
{
|
||||
DungeonRoomLogic DungeonRoomLogic { get; }
|
||||
public Marker3D PlayerSpawn { get; set; }
|
||||
}
|
||||
|
||||
[Meta(typeof(IAutoNode))]
|
||||
public partial class DungeonRoom : Node3D, IDungeonRoom, IProvide<DungeonRoomLogic>
|
||||
{
|
||||
public override void _Notification(int what) => this.Notify(what);
|
||||
|
||||
DungeonRoomLogic IProvide<DungeonRoomLogic>.Value() => DungeonRoomLogic;
|
||||
|
||||
[Dependency] public IGameRepo GameRepo => this.DependOn<IGameRepo>();
|
||||
|
||||
public DungeonRoomLogic DungeonRoomLogic { get; set; } = default!;
|
||||
|
||||
[Node] public Marker3D PlayerSpawn { get; set; } = default!;
|
||||
|
||||
public DungeonRoomLogic.IBinding DungeonRoomBinding { get; set; } = default!;
|
||||
|
||||
public void Setup()
|
||||
{
|
||||
DungeonRoomLogic = new DungeonRoomLogic();
|
||||
DungeonRoomLogic.Set(this as IDungeonRoom);
|
||||
DungeonRoomLogic.Set(GameRepo);
|
||||
}
|
||||
|
||||
public void OnResolved()
|
||||
{
|
||||
DungeonRoomBinding = DungeonRoomLogic.Bind();
|
||||
|
||||
GameRepo.SetPlayerGlobalPosition(PlayerSpawn.GlobalPosition);
|
||||
|
||||
DungeonRoomLogic.Start();
|
||||
this.Provide();
|
||||
}
|
||||
}
|
||||
14
src/map/dungeon/rooms/DungeonRoomLogic.cs
Normal file
14
src/map/dungeon/rooms/DungeonRoomLogic.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using Chickensoft.Introspection;
|
||||
using Chickensoft.LogicBlocks;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public interface IDungeonRoomLogic : ILogicBlock<DungeonRoomLogic.State>;
|
||||
|
||||
[Meta, Id("dungeon_room_logic")]
|
||||
[LogicBlock(typeof(State), Diagram = true)]
|
||||
public partial class DungeonRoomLogic : LogicBlock<DungeonRoomLogic.State>, IDungeonRoomLogic
|
||||
{
|
||||
public override Transition GetInitialState() => To<State.Idle>();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
[gd_scene load_steps=5 format=3 uid="uid://dhpwwqow1ahrc"]
|
||||
[gd_scene load_steps=6 format=3 uid="uid://dhpwwqow1ahrc"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/SimpleDungeons/DungeonRoom3D.gd" id="1_0tfda"]
|
||||
[ext_resource type="Script" path="res://src/map/dungeon/rooms/DungeonRoom.cs" id="1_ti7ur"]
|
||||
[ext_resource type="PackedScene" uid="uid://ckaw6wjmi0fom" path="res://src/map/dungeon/door/Door.tscn" id="2_mdawx"]
|
||||
[ext_resource type="Texture2D" uid="uid://bidlc5a6lft6" path="res://src/map/dungeon/textures/map_brickwall.jpg" id="2_rw3uc"]
|
||||
|
||||
@@ -12,6 +13,9 @@ uv1_triplanar = true
|
||||
[node name="DungeonRoom3D" type="Node3D"]
|
||||
script = ExtResource("1_0tfda")
|
||||
|
||||
[node name="DungeonRoom" type="Node3D" parent="."]
|
||||
script = ExtResource("1_ti7ur")
|
||||
|
||||
[node name="CSGBox3D" type="CSGBox3D" parent="."]
|
||||
material_override = SubResource("StandardMaterial3D_gt3ar")
|
||||
use_collision = true
|
||||
@@ -24,7 +28,11 @@ use_collision = true
|
||||
size = Vector3(9, 9, 9)
|
||||
|
||||
[node name="DOOR" parent="CSGBox3D" instance=ExtResource("2_mdawx")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -3.47376, 4.74571)
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -2.54039, 4.74571)
|
||||
|
||||
[node name="DOOR2" parent="CSGBox3D" instance=ExtResource("2_mdawx")]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -3.51619, -4.73548)
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -2.58282, -4.73548)
|
||||
|
||||
[node name="PlayerSpawn" type="Marker3D" parent="."]
|
||||
unique_name_in_owner = true
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -4.23461, 0)
|
||||
|
||||
11
src/map/dungeon/rooms/state/DungeonRoomLogic.State.cs
Normal file
11
src/map/dungeon/rooms/state/DungeonRoomLogic.State.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Chickensoft.Introspection;
|
||||
using Chickensoft.LogicBlocks;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public partial class DungeonRoomLogic
|
||||
{
|
||||
[Meta]
|
||||
public abstract partial record State : StateLogic<State>;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using Chickensoft.Introspection;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public partial class DungeonRoomLogic
|
||||
{
|
||||
public partial record State
|
||||
{
|
||||
[Meta, Id("dungeon_room_logic_state_idle")]
|
||||
public partial record Idle : State
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
52
src/menu/Menu.cs
Normal file
52
src/menu/Menu.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using Chickensoft.AutoInject;
|
||||
using Chickensoft.GodotNodeInterfaces;
|
||||
using Chickensoft.Introspection;
|
||||
using GameJamDungeon;
|
||||
using Godot;
|
||||
using System;
|
||||
|
||||
public interface IMenu : IControl
|
||||
{
|
||||
event Menu.NewGameEventHandler NewGame;
|
||||
event Menu.QuitEventHandler Quit;
|
||||
}
|
||||
|
||||
[Meta(typeof(IAutoNode))]
|
||||
public partial class Menu : Control, IMenu
|
||||
{
|
||||
public override void _Notification(int what) => this.Notify(what);
|
||||
|
||||
[Node]
|
||||
public IButton NewGameButton { get; set; } = default!;
|
||||
|
||||
[Node]
|
||||
public IButton QuitButton { get; set; } = default!;
|
||||
|
||||
[Signal]
|
||||
public delegate void NewGameEventHandler();
|
||||
[Signal]
|
||||
public delegate void QuitEventHandler();
|
||||
|
||||
public void OnReady()
|
||||
{
|
||||
NewGameButton.Pressed += OnNewGamePressed;
|
||||
QuitButton.Pressed += OnQuitPressed;
|
||||
NewGameButton.GrabFocus();
|
||||
}
|
||||
|
||||
public void OnExitTree()
|
||||
{
|
||||
NewGameButton.Pressed -= OnNewGamePressed;
|
||||
QuitButton.Pressed -= OnQuitPressed;
|
||||
}
|
||||
|
||||
public void OnNewGamePressed() => EmitSignal(SignalName.NewGame);
|
||||
|
||||
public void OnQuitPressed() => EmitSignal(SignalName.Quit);
|
||||
|
||||
public override void _UnhandledInput(InputEvent @event)
|
||||
{
|
||||
if (Input.IsActionJustPressed(GameInputs.Attack))
|
||||
OnNewGamePressed();
|
||||
}
|
||||
}
|
||||
30
src/menu/splash/Splash.cs
Normal file
30
src/menu/splash/Splash.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using Chickensoft.AutoInject;
|
||||
using Chickensoft.GodotNodeInterfaces;
|
||||
using Chickensoft.Introspection;
|
||||
using Godot;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public interface ISplash : IControl;
|
||||
|
||||
[Meta(typeof(IAutoNode))]
|
||||
public partial class Splash : Control, ISplash
|
||||
{
|
||||
public override void _Notification(int what) => this.Notify(what);
|
||||
|
||||
[Dependency]
|
||||
public IAppRepo AppRepo => this.DependOn<IAppRepo>();
|
||||
|
||||
[Node]
|
||||
public IAnimationPlayer AnimationPlayer { get; set; } = default!;
|
||||
|
||||
public void OnReady() =>
|
||||
AnimationPlayer.AnimationFinished += OnAnimationFinished;
|
||||
|
||||
public void OnExitTree()
|
||||
=> AnimationPlayer.AnimationFinished -= OnAnimationFinished;
|
||||
|
||||
public void OnAnimationFinished(StringName name)
|
||||
=> AppRepo.SkipSplashScreen();
|
||||
}
|
||||
}
|
||||
6
src/minimap/MiniMap.cs
Normal file
6
src/minimap/MiniMap.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
using Godot;
|
||||
using System;
|
||||
|
||||
public partial class MiniMap : Control
|
||||
{
|
||||
}
|
||||
130
src/player/Player.cs
Normal file
130
src/player/Player.cs
Normal file
@@ -0,0 +1,130 @@
|
||||
using Chickensoft.AutoInject;
|
||||
using Chickensoft.GodotNodeInterfaces;
|
||||
using Chickensoft.Introspection;
|
||||
using Godot;
|
||||
using System;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public interface IPlayer : ICharacterBody3D
|
||||
{
|
||||
PlayerLogic PlayerLogic { get; }
|
||||
|
||||
PlayerData PlayerData { get; }
|
||||
|
||||
public Vector3 GetGlobalInputVector();
|
||||
}
|
||||
|
||||
[Meta(typeof(IAutoNode))]
|
||||
public partial class Player : CharacterBody3D, IPlayer, IProvide<PlayerLogic>
|
||||
{
|
||||
public override void _Notification(int what) => this.Notify(what);
|
||||
|
||||
PlayerLogic IProvide<PlayerLogic>.Value() => PlayerLogic;
|
||||
|
||||
[Dependency] public IAppRepo AppRepo => this.DependOn<IAppRepo>();
|
||||
[Dependency] public IGameRepo GameRepo => this.DependOn<IGameRepo>();
|
||||
|
||||
/// <summary>Rotation speed (quaternions?/sec).</summary>
|
||||
[Export(PropertyHint.Range, "0, 100, 0.1")]
|
||||
public float RotationSpeed { get; set; } = 0.5f;
|
||||
|
||||
/// <summary>Player speed (meters/sec).</summary>
|
||||
[Export(PropertyHint.Range, "0, 100, 0.1")]
|
||||
public float MoveSpeed { get; set; } = 2f;
|
||||
|
||||
/// <summary>Player speed (meters^2/sec).</summary>
|
||||
[Export(PropertyHint.Range, "0, 100, 0.1")]
|
||||
public float Acceleration { get; set; } = 1f;
|
||||
|
||||
public PlayerLogic.Settings Settings { get; set; } = default!;
|
||||
|
||||
public PlayerLogic PlayerLogic { get; set; } = default!;
|
||||
|
||||
public PlayerData PlayerData { get; set; } = default!;
|
||||
|
||||
public PlayerLogic.IBinding PlayerBinding { get; set; } = default!;
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public void Setup()
|
||||
{
|
||||
Settings = new PlayerLogic.Settings(
|
||||
RotationSpeed,
|
||||
MoveSpeed);
|
||||
|
||||
PlayerLogic = new PlayerLogic();
|
||||
PlayerLogic.Set(this as IPlayer);
|
||||
PlayerLogic.Set(Settings);
|
||||
PlayerLogic.Set(AppRepo);
|
||||
PlayerLogic.Set(GameRepo);
|
||||
PlayerLogic.Set(PlayerData);
|
||||
|
||||
GameRepo.PlayerGlobalPosition.Sync += OnPlayerPositionUpdated;
|
||||
}
|
||||
|
||||
private void OnPlayerPositionUpdated(Vector3 globalPosition)
|
||||
{
|
||||
GlobalPosition = globalPosition;
|
||||
}
|
||||
|
||||
public void OnResolved()
|
||||
{
|
||||
PlayerBinding = PlayerLogic.Bind();
|
||||
|
||||
PlayerBinding
|
||||
.Handle((in PlayerLogic.Output.MovementComputed output) =>
|
||||
{
|
||||
Transform = Transform with { Basis = output.Rotation };
|
||||
Velocity = output.Velocity;
|
||||
})
|
||||
.Handle((in PlayerLogic.Output.Animations.Attack output) =>
|
||||
{
|
||||
});
|
||||
|
||||
this.Provide();
|
||||
PlayerLogic.Start();
|
||||
}
|
||||
|
||||
public void OnReady()
|
||||
{
|
||||
SetPhysicsProcess(true);
|
||||
}
|
||||
|
||||
public void OnPhysicsProcess(double delta)
|
||||
{
|
||||
PlayerLogic.Input(new PlayerLogic.Input.PhysicsTick(delta));
|
||||
|
||||
var attackIsPressed = Input.IsActionJustPressed(GameInputs.Attack);
|
||||
if (attackIsPressed)
|
||||
PlayerLogic.Input(new PlayerLogic.Input.Attack());
|
||||
|
||||
MoveAndSlide();
|
||||
|
||||
PlayerLogic.Input(new PlayerLogic.Input.Moved(GlobalPosition));
|
||||
}
|
||||
|
||||
public Vector3 GetGlobalInputVector()
|
||||
{
|
||||
var rawInput = Input.GetVector(GameInputs.MoveLeft, GameInputs.MoveRight, GameInputs.MoveUp, GameInputs.MoveDown);
|
||||
|
||||
var input = new Vector3
|
||||
{
|
||||
X = rawInput.X,
|
||||
Z = rawInput.Y
|
||||
};
|
||||
return input with { Y = 0f };
|
||||
}
|
||||
|
||||
public void OnExitTree()
|
||||
{
|
||||
PlayerLogic.Stop();
|
||||
AppRepo.Dispose();
|
||||
GameRepo.Dispose();
|
||||
PlayerBinding.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
24
src/player/Player.tscn
Normal file
24
src/player/Player.tscn
Normal file
@@ -0,0 +1,24 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://cfecvvav8kkp6"]
|
||||
|
||||
[ext_resource type="Script" path="res://src/player/Player.cs" id="1_xcol5"]
|
||||
|
||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_dw45s"]
|
||||
height = 1.2
|
||||
|
||||
[node name="Player" type="CharacterBody3D"]
|
||||
motion_mode = 1
|
||||
script = ExtResource("1_xcol5")
|
||||
RotationSpeed = 0.025
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.937567, 0)
|
||||
shape = SubResource("CapsuleShape3D_dw45s")
|
||||
|
||||
[node name="Camera3D" type="Camera3D" parent="."]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.19694, 0)
|
||||
|
||||
[node name="OmniLight3D" type="OmniLight3D" parent="."]
|
||||
omni_range = 73.156
|
||||
|
||||
[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
|
||||
unique_name_in_owner = true
|
||||
22
src/player/PlayerData.cs
Normal file
22
src/player/PlayerData.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using Chickensoft.Serialization;
|
||||
using Godot;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public partial record PlayerData
|
||||
{
|
||||
[Save("global_transform")]
|
||||
public required Transform3D GlobalTransform { get; init; }
|
||||
|
||||
[Save("state_machine")]
|
||||
public required PlayerLogic StateMachine { get; init; }
|
||||
|
||||
[Save("PlayerEquippedSword")]
|
||||
public required InventoryItem EquippedWeapon { get; set; }
|
||||
|
||||
[Save("PlayerInventory")]
|
||||
public required IEnumerable<InventoryItem> Inventory { get; set; }
|
||||
}
|
||||
}
|
||||
20
src/player/state/PlayerLogic.Input.cs
Normal file
20
src/player/state/PlayerLogic.Input.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using Godot;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public partial class PlayerLogic
|
||||
{
|
||||
public static class Input
|
||||
{
|
||||
public readonly record struct PhysicsTick(double Delta);
|
||||
|
||||
public readonly record struct Moved(Vector3 GlobalPosition);
|
||||
|
||||
public readonly record struct Enable;
|
||||
|
||||
public readonly record struct Attack;
|
||||
|
||||
public readonly record struct AttackAnimationFinished;
|
||||
}
|
||||
}
|
||||
}
|
||||
17
src/player/state/PlayerLogic.Output.cs
Normal file
17
src/player/state/PlayerLogic.Output.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using Godot;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public partial class PlayerLogic
|
||||
{
|
||||
public static class Output
|
||||
{
|
||||
public static class Animations
|
||||
{
|
||||
public readonly record struct Attack;
|
||||
}
|
||||
|
||||
public readonly record struct MovementComputed(Basis Rotation, Vector3 Velocity);
|
||||
}
|
||||
}
|
||||
}
|
||||
9
src/player/state/PlayerLogic.Settings.cs
Normal file
9
src/player/state/PlayerLogic.Settings.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public partial class PlayerLogic
|
||||
{
|
||||
public record Settings(
|
||||
float RotationSpeed,
|
||||
float MoveSpeed);
|
||||
}
|
||||
}
|
||||
11
src/player/state/PlayerLogic.State.cs
Normal file
11
src/player/state/PlayerLogic.State.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Chickensoft.Introspection;
|
||||
using Chickensoft.LogicBlocks;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public partial class PlayerLogic
|
||||
{
|
||||
[Meta]
|
||||
public abstract partial record State : StateLogic<State>;
|
||||
}
|
||||
}
|
||||
14
src/player/state/PlayerLogic.cs
Normal file
14
src/player/state/PlayerLogic.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using Chickensoft.Introspection;
|
||||
using Chickensoft.LogicBlocks;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public interface IPlayerLogic : ILogicBlock<PlayerLogic.State>;
|
||||
|
||||
[Meta, Id("player_logic")]
|
||||
[LogicBlock(typeof(State), Diagram = true)]
|
||||
public partial class PlayerLogic : LogicBlock<PlayerLogic.State>, IPlayerLogic
|
||||
{
|
||||
public override Transition GetInitialState() => To<State.Idle>();
|
||||
}
|
||||
}
|
||||
20
src/player/state/PlayerLogic.g.puml
Normal file
20
src/player/state/PlayerLogic.g.puml
Normal file
@@ -0,0 +1,20 @@
|
||||
@startuml PlayerLogic
|
||||
state "PlayerLogic State" as GameJam2024Practice_PlayerLogic_State {
|
||||
state "Alive" as GameJam2024Practice_PlayerLogic_State_Alive {
|
||||
state "Idle" as GameJam2024Practice_PlayerLogic_State_Idle
|
||||
state "Attacking" as GameJam2024Practice_PlayerLogic_State_Attacking
|
||||
}
|
||||
state "Disabled" as GameJam2024Practice_PlayerLogic_State_Disabled
|
||||
}
|
||||
|
||||
GameJam2024Practice_PlayerLogic_State_Alive --> GameJam2024Practice_PlayerLogic_State_Alive : Moved
|
||||
GameJam2024Practice_PlayerLogic_State_Alive --> GameJam2024Practice_PlayerLogic_State_Alive : PhysicsTick
|
||||
GameJam2024Practice_PlayerLogic_State_Attacking --> GameJam2024Practice_PlayerLogic_State_Idle : AttackAnimationFinished
|
||||
GameJam2024Practice_PlayerLogic_State_Disabled --> GameJam2024Practice_PlayerLogic_State_Idle : Enable
|
||||
GameJam2024Practice_PlayerLogic_State_Idle --> GameJam2024Practice_PlayerLogic_State_Attacking : Attack
|
||||
|
||||
GameJam2024Practice_PlayerLogic_State_Alive : OnPhysicsTick → MovementComputed
|
||||
GameJam2024Practice_PlayerLogic_State_Idle : OnAttack → Attack
|
||||
|
||||
[*] --> GameJam2024Practice_PlayerLogic_State_Disabled
|
||||
@enduml
|
||||
19
src/player/state/states/PlayerLogic.State.Alive.Attacking.cs
Normal file
19
src/player/state/states/PlayerLogic.State.Alive.Attacking.cs
Normal file
@@ -0,0 +1,19 @@
|
||||
using Chickensoft.Introspection;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
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>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
23
src/player/state/states/PlayerLogic.State.Alive.Idle.cs
Normal file
23
src/player/state/states/PlayerLogic.State.Alive.Idle.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Chickensoft.Introspection;
|
||||
using Godot;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
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>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
41
src/player/state/states/PlayerLogic.State.Alive.cs
Normal file
41
src/player/state/states/PlayerLogic.State.Alive.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using Chickensoft.Introspection;
|
||||
using Godot;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public partial class PlayerLogic
|
||||
{
|
||||
public partial record State
|
||||
{
|
||||
[Meta, Id("player_logic_alive")]
|
||||
public abstract partial record Alive : State, IGet<Input.PhysicsTick>, IGet<Input.Moved>
|
||||
{
|
||||
public virtual Transition On(in Input.PhysicsTick input)
|
||||
{
|
||||
var player = Get<IPlayer>();
|
||||
var settings = Get<Settings>();
|
||||
|
||||
var rawInput = player.GetGlobalInputVector();
|
||||
|
||||
var transform = player.Transform;
|
||||
transform.Basis = new Basis(Vector3.Up, settings.RotationSpeed * -rawInput.X) * transform.Basis;
|
||||
var velocity = player.Basis * new Vector3(0, 0, rawInput.Z) * settings.MoveSpeed;
|
||||
|
||||
if (Godot.Input.IsActionPressed(GameInputs.Sprint))
|
||||
velocity *= 3;
|
||||
|
||||
Output(new Output.MovementComputed(transform.Basis, velocity));
|
||||
|
||||
return ToSelf();
|
||||
}
|
||||
|
||||
public virtual Transition On(in Input.Moved input)
|
||||
{
|
||||
var gameRepo = Get<IGameRepo>();
|
||||
gameRepo.SetPlayerGlobalPosition(input.GlobalPosition);
|
||||
return ToSelf();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
24
src/player/state/states/PlayerLogic.State.Disabled.cs
Normal file
24
src/player/state/states/PlayerLogic.State.Disabled.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using Chickensoft.Introspection;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
public partial class PlayerLogic
|
||||
{
|
||||
public abstract partial record State
|
||||
{
|
||||
[Meta, Id("player_logic_state_disabled")]
|
||||
public partial record Disabled : State, IGet<Input.Enable>
|
||||
{
|
||||
public Disabled()
|
||||
{
|
||||
OnAttach(() => Get<IAppRepo>().GameEntered += OnGameEntered);
|
||||
OnDetach(() => Get<IAppRepo>().GameEntered -= OnGameEntered);
|
||||
}
|
||||
|
||||
public Transition On(in Input.Enable input) => To<Idle>();
|
||||
|
||||
public void OnGameEntered() => Input(new Input.Enable());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
9
src/utils/FpsCounter.cs
Normal file
9
src/utils/FpsCounter.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Godot;
|
||||
|
||||
public partial class FpsCounter : Label
|
||||
{
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
this.Text = Engine.GetFramesPerSecond().ToString();
|
||||
}
|
||||
}
|
||||
7
src/utils/GameInputs.cs
Normal file
7
src/utils/GameInputs.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using Godot;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
[InputMap]
|
||||
public partial class GameInputs;
|
||||
}
|
||||
40
src/utils/Instantiator.cs
Normal file
40
src/utils/Instantiator.cs
Normal file
@@ -0,0 +1,40 @@
|
||||
using Godot;
|
||||
|
||||
namespace GameJamDungeon
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility class that loads and instantiates scenes.
|
||||
/// </summary>
|
||||
public interface IInstantiator
|
||||
{
|
||||
/// <summary>Scene tree.</summary>
|
||||
public SceneTree SceneTree { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Loads and instantiates the given scene.
|
||||
/// </summary>
|
||||
/// <param name="path">Path to the scene.</param>
|
||||
/// <typeparam name="T">Type of the scene's root.</typeparam>
|
||||
/// <returns>Instance of the scene.</returns>
|
||||
T LoadAndInstantiate<T>(string path) where T : Node;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Utility class that loads and instantiates scenes.
|
||||
/// </summary>
|
||||
public class Instantiator : IInstantiator
|
||||
{
|
||||
public SceneTree SceneTree { get; }
|
||||
|
||||
public Instantiator(SceneTree sceneTree)
|
||||
{
|
||||
SceneTree = sceneTree;
|
||||
}
|
||||
|
||||
public T LoadAndInstantiate<T>(string path) where T : Node
|
||||
{
|
||||
var scene = GD.Load<PackedScene>(path);
|
||||
return scene.Instantiate<T>();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user