Re-introduce prototype code

This commit is contained in:
2024-08-23 00:39:15 -07:00
parent 8b3d1bed8a
commit 2fb0c364ca
62 changed files with 1685 additions and 187 deletions

130
src/player/Player.cs Normal file
View 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
View 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
View 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; }
}
}

View 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;
}
}
}

View 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);
}
}
}

View File

@@ -0,0 +1,9 @@
namespace GameJamDungeon
{
public partial class PlayerLogic
{
public record Settings(
float RotationSpeed,
float MoveSpeed);
}
}

View 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>;
}
}

View 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>();
}
}

View 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

View 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>();
}
}
}
}
}

View 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>();
}
}
}
}
}

View 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();
}
}
}
}
}

View 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());
}
}
}
}