Add initial resource and presenter classes for game entities and effects
This commit is contained in:
25
Code/Autoloads/PresenterRegistry.cs
Normal file
25
Code/Autoloads/PresenterRegistry.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System.Collections.Generic;
|
||||
using GameCore.ECS;
|
||||
using Godot;
|
||||
|
||||
namespace CryptonymThunder.Code.Autoloads;
|
||||
|
||||
public partial class PresenterRegistry : Node
|
||||
{
|
||||
private readonly Dictionary<ulong, Entity> _instanceIdToEntity = new();
|
||||
|
||||
public void RegisterPresenter(Node presenterNode, Entity entity)
|
||||
{
|
||||
_instanceIdToEntity[presenterNode.GetInstanceId()] = entity;
|
||||
}
|
||||
|
||||
public void UnregisterPresenter(Node presenterNode)
|
||||
{
|
||||
_instanceIdToEntity.Remove(presenterNode.GetInstanceId());
|
||||
}
|
||||
|
||||
public bool TryGetEntity(ulong instanceId, out Entity entity)
|
||||
{
|
||||
return _instanceIdToEntity.TryGetValue(instanceId, out entity);
|
||||
}
|
||||
}
|
||||
1
Code/Autoloads/PresenterRegistry.cs.uid
Normal file
1
Code/Autoloads/PresenterRegistry.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://5twln2ckxjj8
|
||||
17
Code/Extensions/Vector3Extensions.cs
Normal file
17
Code/Extensions/Vector3Extensions.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using GameCoreMath = GameCore.Math.Vector3;
|
||||
using GodotMath = Godot.Vector3;
|
||||
|
||||
namespace CryptonymThunder.Code.Extensions;
|
||||
|
||||
public static class Vector3Extensions
|
||||
{
|
||||
public static GodotMath ToGodot(this GameCoreMath v)
|
||||
{
|
||||
return new GodotMath(v.X, v.Y, v.Z);
|
||||
}
|
||||
|
||||
public static GameCoreMath ToGameCore(this GodotMath v)
|
||||
{
|
||||
return new GameCoreMath(v.X, v.Y, v.Z);
|
||||
}
|
||||
}
|
||||
1
Code/Extensions/Vector3Extensions.cs.uid
Normal file
1
Code/Extensions/Vector3Extensions.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dqwjwnmn7x6ny
|
||||
80
Code/Factories/ComponentFactory.cs
Normal file
80
Code/Factories/ComponentFactory.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using CryptonymThunder.Code.Resources;
|
||||
using GameCore.Attributes;
|
||||
using GameCore.Combat;
|
||||
using GameCore.ECS.Interfaces;
|
||||
using GameCore.Input;
|
||||
using GameCore.Movement;
|
||||
using GameCore.Physics;
|
||||
using GameCore.Player;
|
||||
using Godot;
|
||||
|
||||
namespace CryptonymThunder.Code.Factories;
|
||||
|
||||
public class ComponentFactory
|
||||
{
|
||||
private readonly Dictionary<Type, Func<Resource, IComponent>> _factories = new();
|
||||
private readonly EffectFactory _effectFactory = new();
|
||||
|
||||
public ComponentFactory()
|
||||
{
|
||||
Register<AttributeComponentResource>(CreateAttributeComponent);
|
||||
Register<WeaponComponentResource>(CreateWeaponComponent);
|
||||
Register<ProjectileComponentResource>(CreateProjectileComponent);
|
||||
Register<PositionComponentResource>(_ => new PositionComponent());
|
||||
Register<VelocityComponentResource>(_ => new VelocityComponent());
|
||||
Register<InputStateComponentResource>(_ => new InputStateComponent());
|
||||
Register<PlayerComponentResource>(_ => new PlayerComponent());
|
||||
Register<RotationComponentResource>(_ => new RotationComponent());
|
||||
Register<CharacterStateComponentResource>(_ => new CharacterStateComponent());
|
||||
}
|
||||
|
||||
public IComponent Create(Resource resource)
|
||||
{
|
||||
return _factories.TryGetValue(resource.GetType(), out var factory)
|
||||
? factory(resource)
|
||||
: null;
|
||||
}
|
||||
|
||||
private void Register<TResource>(Func<TResource, IComponent> factory) where TResource : Resource
|
||||
{
|
||||
_factories[typeof(TResource)] = res => factory((TResource)res);
|
||||
}
|
||||
|
||||
private static AttributeComponent CreateAttributeComponent(AttributeComponentResource resource)
|
||||
{
|
||||
var component = new AttributeComponent();
|
||||
foreach (var (attribute, value) in resource.BaseValues)
|
||||
{
|
||||
component.SetBaseValue(attribute, value);
|
||||
}
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
private WeaponComponent CreateWeaponComponent(WeaponComponentResource resource)
|
||||
{
|
||||
if (resource.WeaponData == null) return null;
|
||||
var weaponData = resource.WeaponData;
|
||||
|
||||
var onFireEffects = weaponData.OnFireEffects.Select(_effectFactory.Create).ToList();
|
||||
var onHitEffects = weaponData.OnHitEffects.Select(_effectFactory.Create).ToList();
|
||||
|
||||
return new WeaponComponent
|
||||
{
|
||||
FireRate = resource.WeaponData.FireRate,
|
||||
OnFireEffects = onFireEffects,
|
||||
OnHitEffects = onHitEffects,
|
||||
};
|
||||
}
|
||||
|
||||
private ProjectileComponent CreateProjectileComponent(ProjectileComponentResource resource)
|
||||
{
|
||||
return new ProjectileComponent
|
||||
{
|
||||
Lifetime = resource.Lifetime,
|
||||
};
|
||||
}
|
||||
}
|
||||
1
Code/Factories/ComponentFactory.cs.uid
Normal file
1
Code/Factories/ComponentFactory.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cwf0khkssee8b
|
||||
24
Code/Factories/EffectFactory.cs
Normal file
24
Code/Factories/EffectFactory.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices.ComTypes;
|
||||
using CryptonymThunder.Code.Resources;
|
||||
using CryptonymThunder.Code.Resources.Effects;
|
||||
using GameCore.Combat.Effects;
|
||||
using GameCore.Combat.Interfaces;
|
||||
|
||||
namespace CryptonymThunder.Code.Factories;
|
||||
|
||||
public class EffectFactory
|
||||
{
|
||||
public IEffect Create(EffectResource resource)
|
||||
{
|
||||
return resource switch
|
||||
{
|
||||
FireProjectileEffectResource fire =>
|
||||
new BulkProjectileEffect(fire.ProjectileArchetypeId, fire.Count, fire.SpreadAngle, fire.ProjectileSpeed),
|
||||
DamageEffectResource damage => new DamageEffect(damage.Amount),
|
||||
HitscanEffectResource hitscan => new HitscanEffect(hitscan.Range),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(resource),
|
||||
$"Effect type {resource.GetType().Name} not recognized")
|
||||
};
|
||||
}
|
||||
}
|
||||
1
Code/Factories/EffectFactory.cs.uid
Normal file
1
Code/Factories/EffectFactory.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://d1n7re7y0g6q4
|
||||
99
Code/Factories/PresenterFactory.cs
Normal file
99
Code/Factories/PresenterFactory.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
using System.Collections.Generic;
|
||||
using CryptonymThunder.Code.Autoloads;
|
||||
using CryptonymThunder.Code.Resources;
|
||||
using GameCore.ECS;
|
||||
using GameCore.ECS.Interfaces;
|
||||
using GameCore.Movement;
|
||||
using GameCore.Physics;
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
|
||||
namespace CryptonymThunder.Code.Factories;
|
||||
|
||||
public class PresenterFactory(
|
||||
World world,
|
||||
ComponentFactory componentFactory,
|
||||
PresenterRegistry presenterRegistry,
|
||||
Node sceneRoot)
|
||||
{
|
||||
private readonly World _world = world;
|
||||
private readonly ComponentFactory _componentFactory = componentFactory;
|
||||
private PresenterRegistry _presenterRegistry = presenterRegistry;
|
||||
private readonly Node _sceneRoot = sceneRoot;
|
||||
|
||||
public PresenterData CreateEntityFromArchetype(EntityArchetype archetype, GameCore.Math.Vector3? position = null,
|
||||
GameCore.Math.Vector3? rotation = null, GameCore.Math.Vector3? initialVelocity = null)
|
||||
{
|
||||
var entity = _world.CreateEntity();
|
||||
|
||||
foreach (var resource in archetype.ComponentResources)
|
||||
{
|
||||
var component = _componentFactory.Create(resource);
|
||||
if (component != null) _world.AddComponent(entity, component);
|
||||
}
|
||||
|
||||
var presenterNode = archetype.Scene.Instantiate<Node>();
|
||||
if (presenterNode is not IEntityPresenter presenter)
|
||||
{
|
||||
GD.PrintErr($"Archetype scene '{archetype.ResourcePath}' root does not implement IEntityPresenter!");
|
||||
presenterNode.QueueFree();
|
||||
return default;
|
||||
}
|
||||
|
||||
presenter.CoreEntity = entity;
|
||||
|
||||
var components = InitializePresenterComponents(presenterNode, entity);
|
||||
|
||||
if (position.HasValue && _world.GetComponent<PositionComponent>(entity) is { } posComp)
|
||||
posComp.Position = position.Value;
|
||||
if (rotation.HasValue && _world.GetComponent<RotationComponent>(entity) is { } rotComp)
|
||||
rotComp.Rotation = rotation.Value;
|
||||
if (initialVelocity.HasValue && _world.GetComponent<VelocityComponent>(entity) is { } velComp)
|
||||
velComp.DesiredVelocity = initialVelocity.Value;
|
||||
|
||||
_sceneRoot.AddChild(presenterNode);
|
||||
return new PresenterData(entity, presenter, components);
|
||||
}
|
||||
|
||||
public PresenterData RegisterSceneEntity(Node presenterNode, Array<Resource> componentResources)
|
||||
{
|
||||
var entity = _world.CreateEntity();
|
||||
|
||||
foreach (var resource in componentResources)
|
||||
{
|
||||
var component = _componentFactory.Create(resource);
|
||||
if (component != null) _world.AddComponent(entity, component);
|
||||
}
|
||||
|
||||
if (presenterNode is not IEntityPresenter presenter)
|
||||
{
|
||||
GD.PrintErr($"Scene node '{presenterNode.Name}' does not implement IEntityPresenter!");
|
||||
return default;
|
||||
}
|
||||
|
||||
presenter.CoreEntity = entity;
|
||||
var components = InitializePresenterComponents(presenterNode, entity);
|
||||
return new PresenterData(entity, presenter, components);
|
||||
}
|
||||
|
||||
private List<IPresenterComponent> InitializePresenterComponents(Node presenterNode, Entity entity)
|
||||
{
|
||||
_presenterRegistry.RegisterPresenter(presenterNode, entity);
|
||||
|
||||
var components = new List<IPresenterComponent>();
|
||||
foreach (var child in presenterNode.GetChildren(true))
|
||||
{
|
||||
if (child is IPresenterComponent pComponent)
|
||||
{
|
||||
pComponent.Initialize(entity, _world);
|
||||
components.Add(pComponent);
|
||||
}
|
||||
}
|
||||
if (presenterNode is IPresenterComponent rootComponent)
|
||||
{
|
||||
rootComponent.Initialize(entity, _world);
|
||||
components.Add(rootComponent);
|
||||
}
|
||||
return components;
|
||||
}
|
||||
}
|
||||
1
Code/Factories/PresenterFactory.cs.uid
Normal file
1
Code/Factories/PresenterFactory.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://balunj6u4ptjq
|
||||
58
Code/Presenters/CameraPresenterComponent.cs
Normal file
58
Code/Presenters/CameraPresenterComponent.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using CryptonymThunder.Code.Extensions;
|
||||
using GameCore.ECS;
|
||||
using GameCore.ECS.Interfaces;
|
||||
using GameCore.Input;
|
||||
using GameCore.Movement;
|
||||
using Godot;
|
||||
|
||||
namespace CryptonymThunder.Code.Presenters;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class CameraPresenterComponent : Node3D, IPresenterComponent
|
||||
{
|
||||
private Entity _coreEntity;
|
||||
private World _world;
|
||||
private Camera3D _camera;
|
||||
private RotationComponent _rotationComponent;
|
||||
|
||||
private bool _cursorLocked = true;
|
||||
|
||||
public void Initialize(Entity coreEntity, World world)
|
||||
{
|
||||
_coreEntity = coreEntity;
|
||||
_world = world;
|
||||
_rotationComponent = _world.GetComponent<RotationComponent>(_coreEntity);
|
||||
_camera = GetNode<Camera3D>("Camera3D");
|
||||
|
||||
if (_cursorLocked) Input.MouseMode = Input.MouseModeEnum.Captured;
|
||||
}
|
||||
|
||||
public void SyncToPresentation(float delta)
|
||||
{
|
||||
if (_rotationComponent == null || _camera == null) return;
|
||||
|
||||
var coreRotation = _rotationComponent.Rotation;
|
||||
_camera.Rotation = new Vector3(coreRotation.X, 0f, 0f);
|
||||
}
|
||||
|
||||
public void SyncToCore(float delta)
|
||||
{
|
||||
}
|
||||
|
||||
public override void _Input(InputEvent @event)
|
||||
{
|
||||
if (Input.IsActionJustPressed("ui_cancel"))
|
||||
{
|
||||
if (_cursorLocked)
|
||||
{
|
||||
Input.MouseMode = Input.MouseModeEnum.Visible;
|
||||
_cursorLocked = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
Input.MouseMode = Input.MouseModeEnum.Captured;
|
||||
_cursorLocked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1
Code/Presenters/CameraPresenterComponent.cs.uid
Normal file
1
Code/Presenters/CameraPresenterComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://crx03e8buoni3
|
||||
66
Code/Presenters/CharacterBody3DPresenter.cs
Normal file
66
Code/Presenters/CharacterBody3DPresenter.cs
Normal file
@@ -0,0 +1,66 @@
|
||||
using CryptonymThunder.Code.Extensions;
|
||||
using GameCore.ECS;
|
||||
using GameCore.ECS.Interfaces;
|
||||
using GameCore.Input;
|
||||
using GameCore.Movement;
|
||||
using GameCore.Physics;
|
||||
using Godot;
|
||||
|
||||
namespace CryptonymThunder.Code.Presenters;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class CharacterBody3DPresenter : CharacterBody3D, IEntityPresenter, IPresenterComponent
|
||||
{
|
||||
private World _world;
|
||||
private VelocityComponent _velocity;
|
||||
private PositionComponent _position;
|
||||
private RotationComponent _rotation;
|
||||
private CharacterStateComponent _characterState;
|
||||
private InputStateComponent _inputState;
|
||||
private Camera3D _camera;
|
||||
|
||||
public Entity CoreEntity { get; set; }
|
||||
|
||||
public void Initialize(Entity coreEntity, World world)
|
||||
{
|
||||
CoreEntity = coreEntity;
|
||||
_world = world;
|
||||
_velocity = _world.GetComponent<VelocityComponent>(CoreEntity);
|
||||
_position = _world.GetComponent<PositionComponent>(CoreEntity);
|
||||
_rotation = _world.GetComponent<RotationComponent>(CoreEntity);
|
||||
_inputState = _world.GetComponent<InputStateComponent>(CoreEntity);
|
||||
_characterState = _world.GetComponent<CharacterStateComponent>(CoreEntity);
|
||||
_camera = GetNode<Camera3D>("CameraPivot/Camera3D");
|
||||
}
|
||||
|
||||
public void SyncToPresentation(float delta)
|
||||
{
|
||||
if (_rotation != null)
|
||||
{
|
||||
var coreRotation = _rotation.Rotation;
|
||||
Rotation = new Vector3(Rotation.X, coreRotation.Y, Rotation.Z);
|
||||
}
|
||||
|
||||
if (_velocity == null) return;
|
||||
|
||||
var godotVelocity = Velocity;
|
||||
godotVelocity.X = _velocity.DesiredVelocity.X;
|
||||
godotVelocity.Y = _velocity.DesiredVelocity.Y;
|
||||
godotVelocity.Z = _velocity.DesiredVelocity.Z;
|
||||
|
||||
Velocity = godotVelocity;
|
||||
MoveAndSlide();
|
||||
}
|
||||
|
||||
public void SyncToCore(float delta)
|
||||
{
|
||||
if (_position != null) _position.Position = GlobalPosition.ToGameCore();
|
||||
if (_velocity != null) _velocity.ActualVelocity = Velocity.ToGameCore();
|
||||
if (_characterState != null) _characterState.IsOnFloor = IsOnFloor();
|
||||
if (_inputState != null && _camera != null)
|
||||
{
|
||||
_inputState.MuzzlePosition = _camera.GlobalPosition.ToGameCore();
|
||||
_inputState.MuzzleDirection = (-_camera.GlobalTransform.Basis.Z).ToGameCore();
|
||||
}
|
||||
}
|
||||
}
|
||||
1
Code/Presenters/CharacterBody3DPresenter.cs.uid
Normal file
1
Code/Presenters/CharacterBody3DPresenter.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://hkiny1ftv4r7
|
||||
11
Code/Presenters/EntityPresenter.cs
Normal file
11
Code/Presenters/EntityPresenter.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using GameCore.ECS;
|
||||
using GameCore.ECS.Interfaces;
|
||||
using Godot;
|
||||
|
||||
namespace CryptonymThunder.Code.Presenters;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class EntityPresenter : Node3D, IEntityPresenter
|
||||
{
|
||||
public Entity CoreEntity { get; set; }
|
||||
}
|
||||
1
Code/Presenters/EntityPresenter.cs.uid
Normal file
1
Code/Presenters/EntityPresenter.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cb7vaw6xqjs1i
|
||||
157
Code/Presenters/GamePresenter.cs
Normal file
157
Code/Presenters/GamePresenter.cs
Normal file
@@ -0,0 +1,157 @@
|
||||
using System.Collections.Generic;
|
||||
using CryptonymThunder.Code.Autoloads;
|
||||
using CryptonymThunder.Code.Factories;
|
||||
using CryptonymThunder.Code.Resources;
|
||||
using CryptonymThunder.Code.Services;
|
||||
using GameCore.Attributes;
|
||||
using GameCore.Combat;
|
||||
using GameCore.Config;
|
||||
using GameCore.ECS;
|
||||
using GameCore.ECS.Interfaces;
|
||||
using GameCore.Events;
|
||||
using GameCore.Input;
|
||||
using GameCore.Movement;
|
||||
using Godot;
|
||||
|
||||
namespace CryptonymThunder.Code.Presenters;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class GamePresenter : Node
|
||||
{
|
||||
[Export] private ArchetypeDatabase ArchetypesDatabase { get; set; }
|
||||
[Export] private EntityArchetype PlayerArchetype { get; set; }
|
||||
[Export] private SimulationConfigResource SimulationConfig { get; set; }
|
||||
|
||||
private World _world;
|
||||
private PresenterRegistry _presenterRegistry;
|
||||
private GodotInputService _inputService;
|
||||
private GodotWorldQuery _worldQuery;
|
||||
private PresenterFactory _presenterFactory;
|
||||
|
||||
private readonly Dictionary<int, List<IPresenterComponent>> _presenterComponents = new();
|
||||
private readonly Dictionary<int, IEntityPresenter> _presenters = new();
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_presenterRegistry = GetNode<PresenterRegistry>("/root/PresenterRegistry");
|
||||
_inputService = new GodotInputService();
|
||||
_worldQuery = new GodotWorldQuery(this);
|
||||
|
||||
var simConfig = new SimulationConfig();
|
||||
if (SimulationConfig != null)
|
||||
{
|
||||
simConfig.GravityStrength = SimulationConfig.GravityStrength;
|
||||
}
|
||||
|
||||
_world = new World(_inputService, _worldQuery, simConfig);
|
||||
|
||||
_presenterFactory = new PresenterFactory(_world, new ComponentFactory(), _presenterRegistry, this);
|
||||
|
||||
_world.RegisterSystem(new PlayerInputSystem());
|
||||
_world.RegisterSystem(new RotationSystem());
|
||||
_world.RegisterSystem(new GroundMovementSystem());
|
||||
_world.RegisterSystem(new GravitySystem());
|
||||
_world.RegisterSystem(new JumpSystem());
|
||||
|
||||
_world.RegisterSystem(new AttributeSystem());
|
||||
|
||||
_world.RegisterSystem(new WeaponSystem());
|
||||
_world.RegisterSystem(new ProjectileSystem());
|
||||
_world.RegisterSystem(new ProjectileInitializationSystem(_world));
|
||||
|
||||
_world.RegisterSystem(new DamageSystem(_world));
|
||||
_world.RegisterSystem(new ProjectileCleanupSystem());
|
||||
_world.RegisterSystem(new DestructionSystem());
|
||||
|
||||
_world.Subscribe<EntityDiedEvent>(OnEntityDied);
|
||||
_world.Subscribe<SpawnEntityEvent>(OnSpawnEntity);
|
||||
|
||||
RegisterAllSceneEntities();
|
||||
|
||||
var playerData = _presenterFactory.CreateEntityFromArchetype(PlayerArchetype);
|
||||
_presenters.Add(playerData.Entity.Id, playerData.Presenter);
|
||||
_presenterComponents.Add(playerData.Entity.Id, playerData.Components);
|
||||
}
|
||||
|
||||
public override void _Input(InputEvent @event)
|
||||
{
|
||||
_inputService?.HandleInputEvent(@event);
|
||||
}
|
||||
|
||||
public override void _PhysicsProcess(double delta)
|
||||
{
|
||||
if (_presenters.Count == 0) return;
|
||||
|
||||
_inputService.Update();
|
||||
_world.Update((float)delta);
|
||||
_world.ProcessEvents();
|
||||
|
||||
foreach (var componentList in _presenterComponents.Values)
|
||||
{
|
||||
foreach (var component in componentList)
|
||||
{
|
||||
component.SyncToPresentation((float)delta);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var componentList in _presenterComponents.Values)
|
||||
{
|
||||
foreach (var component in componentList)
|
||||
{
|
||||
component.SyncToCore((float)delta);
|
||||
}
|
||||
}
|
||||
|
||||
_inputService.LateUpdate();
|
||||
}
|
||||
|
||||
private void OnSpawnEntity(SpawnEntityEvent @event)
|
||||
{
|
||||
if (ArchetypesDatabase.Archetypes.TryGetValue(@event.ArchetypeId, out var archetype))
|
||||
{
|
||||
var presenterData = _presenterFactory.CreateEntityFromArchetype(archetype, @event.Position, @event.Rotation, @event.InitialVelocity);
|
||||
_presenters.Add(presenterData.Entity.Id, presenterData.Presenter);
|
||||
_presenterComponents.Add(presenterData.Entity.Id, presenterData.Components);
|
||||
|
||||
_world.PublishEvent(new EntitySpawnedEvent(presenterData.Entity, @event.Owner, @event.ArchetypeId));
|
||||
}
|
||||
}
|
||||
|
||||
private void OnEntityDied(EntityDiedEvent e)
|
||||
{
|
||||
if (_presenters.Remove(e.Entity.Id, out var presenter))
|
||||
{
|
||||
_presenterRegistry.UnregisterPresenter(presenter as Node);
|
||||
_presenterComponents.Remove(e.Entity.Id);
|
||||
(presenter as Node)?.QueueFree();
|
||||
}
|
||||
}
|
||||
|
||||
private void RegisterAllSceneEntities()
|
||||
{
|
||||
var sceneEntities = GetTree().GetNodesInGroup("SceneEntities");
|
||||
foreach (var node in sceneEntities)
|
||||
{
|
||||
if (node is SceneEntity sceneEntity)
|
||||
{
|
||||
var parentNode = sceneEntity.GetParent();
|
||||
if (parentNode != null)
|
||||
{
|
||||
var presenterData = _presenterFactory.RegisterSceneEntity(parentNode, sceneEntity.ComponentResources);
|
||||
_presenters.Add(presenterData.Entity.Id, presenterData.Presenter);
|
||||
_presenterComponents.Add(presenterData.Entity.Id, presenterData.Components);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Node GetPresenterNode(Entity entity)
|
||||
{
|
||||
if (_presenters.TryGetValue(entity.Id, out var presenter))
|
||||
{
|
||||
return presenter as Node;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
1
Code/Presenters/GamePresenter.cs.uid
Normal file
1
Code/Presenters/GamePresenter.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cfpm5p102f65x
|
||||
10
Code/Presenters/SceneEntity.cs
Normal file
10
Code/Presenters/SceneEntity.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
|
||||
namespace CryptonymThunder.Code.Presenters;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class SceneEntity : Node
|
||||
{
|
||||
[Export] public Array<Resource> ComponentResources { get; set; } = [];
|
||||
}
|
||||
1
Code/Presenters/SceneEntity.cs.uid
Normal file
1
Code/Presenters/SceneEntity.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://b6x8llipvutqs
|
||||
31
Code/Presenters/TransformPresenterComponent.cs
Normal file
31
Code/Presenters/TransformPresenterComponent.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using CryptonymThunder.Code.Extensions;
|
||||
using GameCore.ECS;
|
||||
using GameCore.ECS.Interfaces;
|
||||
using GameCore.Physics;
|
||||
using Godot;
|
||||
|
||||
namespace CryptonymThunder.Code.Presenters;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class TransformPresenterComponent : Node3D, IPresenterComponent, IEntityPresenter
|
||||
{
|
||||
private PositionComponent _position;
|
||||
|
||||
public Entity CoreEntity { get; set; }
|
||||
|
||||
public void Initialize(Entity coreEntity, World world)
|
||||
{
|
||||
CoreEntity = coreEntity;
|
||||
_position = world.GetComponent<PositionComponent>(coreEntity);
|
||||
}
|
||||
|
||||
public void SyncToPresentation(float delta)
|
||||
{
|
||||
if (_position != null) GlobalPosition = _position.Position.ToGodot();
|
||||
}
|
||||
|
||||
public void SyncToCore(float delta)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
1
Code/Presenters/TransformPresenterComponent.cs.uid
Normal file
1
Code/Presenters/TransformPresenterComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dl2awoy0hyruh
|
||||
10
Code/Resources/ArchetypeDatabase.cs
Normal file
10
Code/Resources/ArchetypeDatabase.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
|
||||
namespace CryptonymThunder.Code.Resources;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class ArchetypeDatabase : Resource
|
||||
{
|
||||
[Export] public Dictionary<string, EntityArchetype> Archetypes { get; set; } = new();
|
||||
}
|
||||
1
Code/Resources/ArchetypeDatabase.cs.uid
Normal file
1
Code/Resources/ArchetypeDatabase.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://df10ksb6f0pjt
|
||||
10
Code/Resources/AttributeComponentResource.cs
Normal file
10
Code/Resources/AttributeComponentResource.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using Godot;
|
||||
using Attribute = GameCore.Attributes.Attribute;
|
||||
|
||||
namespace CryptonymThunder.Code.Resources;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class AttributeComponentResource : Resource
|
||||
{
|
||||
[Export] public Godot.Collections.Dictionary<Attribute, float> BaseValues { get; set; } = new();
|
||||
}
|
||||
1
Code/Resources/AttributeComponentResource.cs.uid
Normal file
1
Code/Resources/AttributeComponentResource.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dc7wq2ij5kwj5
|
||||
9
Code/Resources/CharacterStateComponentResource.cs
Normal file
9
Code/Resources/CharacterStateComponentResource.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Godot;
|
||||
|
||||
namespace CryptonymThunder.Code.Resources;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class CharacterStateComponentResource : Resource
|
||||
{
|
||||
|
||||
}
|
||||
1
Code/Resources/CharacterStateComponentResource.cs.uid
Normal file
1
Code/Resources/CharacterStateComponentResource.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://cdpbn8eiypfbd
|
||||
9
Code/Resources/EffectResource.cs
Normal file
9
Code/Resources/EffectResource.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Godot;
|
||||
|
||||
namespace CryptonymThunder.Code.Resources;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class EffectResource : Resource
|
||||
{
|
||||
|
||||
}
|
||||
1
Code/Resources/EffectResource.cs.uid
Normal file
1
Code/Resources/EffectResource.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bdo6vrtrsr0an
|
||||
9
Code/Resources/Effects/DamageEffectResource.cs
Normal file
9
Code/Resources/Effects/DamageEffectResource.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Godot;
|
||||
|
||||
namespace CryptonymThunder.Code.Resources.Effects;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class DamageEffectResource : EffectResource
|
||||
{
|
||||
[Export] public float Amount { get; set; } = 10f;
|
||||
}
|
||||
1
Code/Resources/Effects/DamageEffectResource.cs.uid
Normal file
1
Code/Resources/Effects/DamageEffectResource.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://btv24gsw1p850
|
||||
12
Code/Resources/Effects/FireProjectileEffectResource.cs
Normal file
12
Code/Resources/Effects/FireProjectileEffectResource.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Godot;
|
||||
|
||||
namespace CryptonymThunder.Code.Resources.Effects;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class FireProjectileEffectResource : EffectResource
|
||||
{
|
||||
[Export] public string ProjectileArchetypeId { get; set; }
|
||||
[Export(PropertyHint.Range, "1,50,1")] public int Count { get; set; } = 1;
|
||||
[Export(PropertyHint.Range, "0,90,0.1")] public float SpreadAngle { get; set; } = 0f;
|
||||
[Export] public float ProjectileSpeed { get; set; } = 30f;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://cht6trljihvle
|
||||
9
Code/Resources/Effects/HitscanEffectResource.cs
Normal file
9
Code/Resources/Effects/HitscanEffectResource.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Godot;
|
||||
|
||||
namespace CryptonymThunder.Code.Resources.Effects;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class HitscanEffectResource : EffectResource
|
||||
{
|
||||
[Export] public float Range { get; set; } = 100f;
|
||||
}
|
||||
1
Code/Resources/Effects/HitscanEffectResource.cs.uid
Normal file
1
Code/Resources/Effects/HitscanEffectResource.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://chc0d0rjcbl65
|
||||
11
Code/Resources/EntityArchetype.cs
Normal file
11
Code/Resources/EntityArchetype.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using Godot;
|
||||
using Godot.Collections;
|
||||
|
||||
namespace CryptonymThunder.Code.Resources;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class EntityArchetype : Resource
|
||||
{
|
||||
[Export] public PackedScene Scene { get; set; }
|
||||
[Export] public Array<Resource> ComponentResources { get; set; } = [];
|
||||
}
|
||||
1
Code/Resources/EntityArchetype.cs.uid
Normal file
1
Code/Resources/EntityArchetype.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://coe688e2jkyjq
|
||||
9
Code/Resources/InputStateComponentResource.cs
Normal file
9
Code/Resources/InputStateComponentResource.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Godot;
|
||||
|
||||
namespace CryptonymThunder.Code.Resources;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class InputStateComponentResource : Resource
|
||||
{
|
||||
|
||||
}
|
||||
1
Code/Resources/InputStateComponentResource.cs.uid
Normal file
1
Code/Resources/InputStateComponentResource.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://y4cbuh2wxigy
|
||||
9
Code/Resources/PlayerComponentResource.cs
Normal file
9
Code/Resources/PlayerComponentResource.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Godot;
|
||||
|
||||
namespace CryptonymThunder.Code.Resources;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class PlayerComponentResource : Resource
|
||||
{
|
||||
|
||||
}
|
||||
1
Code/Resources/PlayerComponentResource.cs.uid
Normal file
1
Code/Resources/PlayerComponentResource.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://p4vr80n70vkt
|
||||
9
Code/Resources/PositionComponentResource.cs
Normal file
9
Code/Resources/PositionComponentResource.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Godot;
|
||||
|
||||
namespace CryptonymThunder.Code.Resources;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class PositionComponentResource : Resource
|
||||
{
|
||||
|
||||
}
|
||||
1
Code/Resources/PositionComponentResource.cs.uid
Normal file
1
Code/Resources/PositionComponentResource.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://df5wmj1jp2oy5
|
||||
10
Code/Resources/ProjectileComponentResource.cs
Normal file
10
Code/Resources/ProjectileComponentResource.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using Godot;
|
||||
|
||||
namespace CryptonymThunder.Code.Resources;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class ProjectileComponentResource : Resource
|
||||
{
|
||||
[Export] public float Speed { get; set; } = 50.0f;
|
||||
[Export] public float Lifetime { get; set; } = 5.0f;
|
||||
}
|
||||
1
Code/Resources/ProjectileComponentResource.cs.uid
Normal file
1
Code/Resources/ProjectileComponentResource.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://ddryrwjq33832
|
||||
9
Code/Resources/RotationComponentResource.cs
Normal file
9
Code/Resources/RotationComponentResource.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Godot;
|
||||
|
||||
namespace CryptonymThunder.Code.Resources;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class RotationComponentResource : Resource
|
||||
{
|
||||
|
||||
}
|
||||
1
Code/Resources/RotationComponentResource.cs.uid
Normal file
1
Code/Resources/RotationComponentResource.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://blm62f85g7icn
|
||||
9
Code/Resources/SimulationConfigResource.cs
Normal file
9
Code/Resources/SimulationConfigResource.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Godot;
|
||||
|
||||
namespace CryptonymThunder.Code.Resources;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class SimulationConfigResource : Resource
|
||||
{
|
||||
[Export] public float GravityStrength { get; set; } = 9.81f;
|
||||
}
|
||||
1
Code/Resources/SimulationConfigResource.cs.uid
Normal file
1
Code/Resources/SimulationConfigResource.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://uearpvfk21ym
|
||||
9
Code/Resources/VelocityComponentResource.cs
Normal file
9
Code/Resources/VelocityComponentResource.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Godot;
|
||||
|
||||
namespace CryptonymThunder.Code.Resources;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class VelocityComponentResource : Resource
|
||||
{
|
||||
|
||||
}
|
||||
1
Code/Resources/VelocityComponentResource.cs.uid
Normal file
1
Code/Resources/VelocityComponentResource.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://t4j1urlupxxv
|
||||
9
Code/Resources/WeaponComponentResource.cs
Normal file
9
Code/Resources/WeaponComponentResource.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Godot;
|
||||
|
||||
namespace CryptonymThunder.Code.Resources;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class WeaponComponentResource : Resource
|
||||
{
|
||||
[Export] public WeaponResource WeaponData { get; set; }
|
||||
}
|
||||
1
Code/Resources/WeaponComponentResource.cs.uid
Normal file
1
Code/Resources/WeaponComponentResource.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bp7mufswr41w6
|
||||
14
Code/Resources/WeaponResource.cs
Normal file
14
Code/Resources/WeaponResource.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
using GameCore.Combat;
|
||||
using Godot;
|
||||
|
||||
namespace CryptonymThunder.Code.Resources;
|
||||
|
||||
[GlobalClass]
|
||||
public partial class WeaponResource : Resource
|
||||
{
|
||||
[Export] public float FireRate { get; set; } = 1.0f;
|
||||
|
||||
[ExportGroup("Effects")]
|
||||
[Export] public Godot.Collections.Array<EffectResource> OnFireEffects { get; set; } = [];
|
||||
[Export] public Godot.Collections.Array<EffectResource> OnHitEffects { get; set; } = [];
|
||||
}
|
||||
1
Code/Resources/WeaponResource.cs.uid
Normal file
1
Code/Resources/WeaponResource.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://hf0iut8o8do3
|
||||
47
Code/Services/GodotInputService.cs
Normal file
47
Code/Services/GodotInputService.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using GameCore.Input.Interfaces;
|
||||
using Godot;
|
||||
using GameCoreMath = GameCore.Math.Vector3;
|
||||
|
||||
namespace CryptonymThunder.Code.Services;
|
||||
|
||||
public class GodotInputService : IInputService
|
||||
{
|
||||
private Vector2 _mouseDeltaAccumulator = Vector2.Zero;
|
||||
private const float MouseSensitivity = 0.002f;
|
||||
|
||||
public bool IsFiring { get; private set; }
|
||||
public bool IsInteracting { get; private set; }
|
||||
public bool IsJumping { get; private set; }
|
||||
public GameCoreMath MoveDirection { get; private set; }
|
||||
public GameCoreMath LookDirection { get; private set; }
|
||||
|
||||
public void HandleInputEvent(InputEvent e)
|
||||
{
|
||||
if (e is InputEventMouseMotion mouseMotion)
|
||||
{
|
||||
_mouseDeltaAccumulator += mouseMotion.Relative;
|
||||
}
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
var godotInputVector = Input.GetVector("left", "right", "forward", "backward");
|
||||
MoveDirection = new GameCoreMath(godotInputVector.X, 0f, godotInputVector.Y);
|
||||
|
||||
LookDirection = new GameCoreMath(
|
||||
-_mouseDeltaAccumulator.Y * MouseSensitivity,
|
||||
-_mouseDeltaAccumulator.X * MouseSensitivity,
|
||||
0f);
|
||||
|
||||
_mouseDeltaAccumulator = Vector2.Zero;
|
||||
|
||||
IsJumping = Input.IsActionJustPressed("jump");
|
||||
IsFiring = Input.IsActionPressed("fire");
|
||||
IsInteracting = Input.IsActionPressed("interact");
|
||||
}
|
||||
|
||||
public void LateUpdate()
|
||||
{
|
||||
LookDirection = GameCoreMath.Zero;
|
||||
}
|
||||
}
|
||||
1
Code/Services/GodotInputService.cs.uid
Normal file
1
Code/Services/GodotInputService.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dp63r0vs7vydp
|
||||
59
Code/Services/GodotWorldQuery.cs
Normal file
59
Code/Services/GodotWorldQuery.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using CryptonymThunder.Code.Autoloads;
|
||||
using CryptonymThunder.Code.Extensions;
|
||||
using CryptonymThunder.Code.Presenters;
|
||||
using GameCore.ECS;
|
||||
using GameCore.ECS.Interfaces;
|
||||
using Godot;
|
||||
using GodotWorld = Godot.World3D;
|
||||
using Vector3 = GameCore.Math.Vector3;
|
||||
|
||||
namespace CryptonymThunder.Code.Services;
|
||||
|
||||
public class GodotWorldQuery(GamePresenter ownerNode) : IWorldQuery
|
||||
{
|
||||
private readonly PresenterRegistry _presenterRegistry = ownerNode.GetNode<PresenterRegistry>("/root/PresenterRegistry");
|
||||
private readonly GodotWorld _godotWorld = ((SceneTree)Engine.GetMainLoop()).Root.World3D;
|
||||
|
||||
public HitResult Raycast(Vector3 from, Vector3 to, Entity? ownerToExclude = null)
|
||||
{
|
||||
var spaceState = _godotWorld.DirectSpaceState;
|
||||
|
||||
var query = PhysicsRayQueryParameters3D.Create(from.ToGodot(), to.ToGodot());
|
||||
query.CollisionMask = 1;
|
||||
query.CollideWithBodies = true;
|
||||
query.CollideWithAreas = false;
|
||||
|
||||
if (ownerToExclude.HasValue)
|
||||
{
|
||||
var ownerPresenter = ownerNode.GetPresenterNode(ownerToExclude.Value);
|
||||
if (ownerPresenter != null)
|
||||
{
|
||||
query.Exclude = [((PhysicsBody3D)ownerPresenter).GetRid()];
|
||||
}
|
||||
}
|
||||
|
||||
var result = spaceState.IntersectRay(query);
|
||||
|
||||
if (result.Count > 0 && result["collider"].Obj is Node3D collider)
|
||||
{
|
||||
if (_presenterRegistry.TryGetEntity(collider.GetInstanceId(), out var hitEntity))
|
||||
{
|
||||
return new HitResult
|
||||
{
|
||||
DidHit = true,
|
||||
HitEntity = hitEntity
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return new HitResult { DidHit = false };
|
||||
}
|
||||
|
||||
public Vector3 RotateVectorByYaw(Vector3 vector, float yawRadians)
|
||||
{
|
||||
var rotatedX = vector.X * (float)Math.Cos(yawRadians) + vector.Z * (float)Math.Sin(yawRadians);
|
||||
var rotatedZ = -vector.X * (float)Math.Sin(yawRadians) + vector.Z * (float)Math.Cos(yawRadians);
|
||||
return new Vector3(rotatedX, vector.Y, rotatedZ);
|
||||
}
|
||||
}
|
||||
1
Code/Services/GodotWorldQuery.cs.uid
Normal file
1
Code/Services/GodotWorldQuery.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://l1tfuwnumj0m
|
||||
Reference in New Issue
Block a user