Add TriggerActionFactory and related resources for button and logic sequence components

This commit is contained in:
2025-10-30 02:21:27 +01:00
parent f2ff758dcb
commit 2c126cd7ea
22 changed files with 250 additions and 13 deletions

View File

@@ -9,6 +9,7 @@ using GameCore.ECS.Interfaces;
using GameCore.Input;
using GameCore.Interaction;
using GameCore.Inventory;
using GameCore.Logic;
using GameCore.Movement;
using GameCore.Physics;
using GameCore.Player;
@@ -21,11 +22,13 @@ public class ComponentFactory
private readonly Dictionary<Type, Func<Resource, IComponent>> _factories = new();
private readonly EffectFactory _effectFactory;
private readonly InteractionRequirementFactory _requirementFactory;
private readonly TriggerActionFactory _triggerActionFactory;
public ComponentFactory(EffectFactory effectFactory, InteractionRequirementFactory requirementFactory)
public ComponentFactory(EffectFactory effectFactory, InteractionRequirementFactory requirementFactory, TriggerActionFactory triggerActionFactory)
{
_effectFactory = effectFactory;
_requirementFactory = requirementFactory;
_triggerActionFactory = triggerActionFactory;
Register<AttributeComponentResource>(CreateAttributeComponent);
Register<WeaponComponentResource>(CreateWeaponComponent);
@@ -40,6 +43,9 @@ public class ComponentFactory
Register<PickupComponentResource>(CreatePickupComponent);
Register<EquipmentComponentResource>(_ => new EquipmentComponent());
Register<DoorComponentResource>(CreateDoorComponent);
Register<WorldIdComponentResource>(res => new WorldIdComponent(res.WorldId));
Register<ButtonComponentResource>(CreateButtonComponent);
Register<LogicSequenceComponentResource>(CreateLogicSequenceComponent);
}
public IComponent Create(Resource resource)
@@ -129,4 +135,47 @@ public class ComponentFactory
return component;
}
private ButtonComponent CreateButtonComponent(ButtonComponentResource resource)
{
var component = new ButtonComponent
{
ChannelId = resource.ChannelId,
IsToggle = resource.IsToggle,
IsOneTimeUse = resource.IsOneTimeUse,
IsPressed = false,
HasBeenUsed = false,
};
foreach (var reqResource in resource.Requirements)
{
var requirement = _requirementFactory.Create(reqResource);
if (requirement != null)
{
component.Requirements.Add(requirement);
}
}
return component;
}
private LogicSequenceComponent CreateLogicSequenceComponent(LogicSequenceComponentResource resource)
{
var component = new LogicSequenceComponent
{
RequiredChannels = resource.RequiredChannels.ToList(),
IsOneTimeTrigger = resource.IsOneTimeTrigger,
};
foreach (var actionResource in resource.OnCompleteActions)
{
var action = _triggerActionFactory.Create(actionResource);
if (action != null)
{
component.OnCompleteActions.Add(action);
}
}
return component;
}
}

View File

@@ -0,0 +1,21 @@
using System;
using CryptonymThunder.Code.Resources;
using GameCore.Logic;
using GameCore.Logic.Interfaces;
namespace CryptonymThunder.Code.Factories;
public class TriggerActionFactory
{
public ITriggerAction Create(TriggerActionResource resource)
{
return resource switch
{
UnlockDoorActionResource unlock => new UnlockDoorAction(unlock.TargetWorldId),
SpawnEntityActionResource spawn => new SpawnEntityAction(spawn.ArchetypeId, spawn.SpawnerWorldId),
DebugMessageActionResource debug => new DebugMessageAction(debug.Message),
_ => throw new ArgumentOutOfRangeException(nameof(resource),
$"TriggerAction type {resource.GetType().Name} not recognized")
};
}
}

View File

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

View File

@@ -15,6 +15,7 @@ using GameCore.Interaction;
using GameCore.Inventory;
using GameCore.Logging;
using GameCore.Logging.Interfaces;
using GameCore.Logic;
using GameCore.Movement;
using GameCore.Player;
using Godot;
@@ -64,7 +65,8 @@ public partial class GamePresenter : Node
var effectFactory = new EffectFactory();
var requirementFactory = new InteractionRequirementFactory();
var componentFactory = new ComponentFactory(effectFactory, requirementFactory);
var triggerActionFactory = new TriggerActionFactory();
var componentFactory = new ComponentFactory(effectFactory, requirementFactory, triggerActionFactory);
var weaponDataService = new GodotWeaponDataService(WeaponDatabase, effectFactory);
_presenterFactory = new PresenterFactory(_world, componentFactory, _presenterRegistry, this);
@@ -85,6 +87,7 @@ public partial class GamePresenter : Node
_world.RegisterSystem(new InteractionSystem(InteractionRange));
_world.RegisterSystem(new DoorSystem());
_world.RegisterSystem(new LogicSequenceSystem(_world));
_world.RegisterSystem(new WeaponSystem());
_world.RegisterSystem(new ProjectileSystem());

View File

@@ -125,10 +125,12 @@ public partial class HudPresenterComponent : Control, IPresenterComponent
var lookingAt = _world.GetComponent<IsLookingAtInteractableComponent>(_playerEntity);
if (lookingAt != null)
{
var door = _world.GetComponent<DoorComponent>(lookingAt.Target);
var targetEntity = lookingAt.Target;
var interactKey = "F";
var door = _world.GetComponent<DoorComponent>(targetEntity);
if (door != null)
{
var interactKey = "F";
_interactLabel.Text = door.CurrentState switch
{
DoorComponent.DoorState.Locked => $"[{interactKey}] Interact (Locked)",
@@ -140,7 +142,26 @@ public partial class HudPresenterComponent : Control, IPresenterComponent
}
else
{
_interactLabel.Visible = false;
var button = _world.GetComponent<ButtonComponent>(targetEntity);
if (button != null)
{
if (button.IsOneTimeUse && button.HasBeenUsed)
{
_interactLabel.Text = "(Used)";
}
else if (button.IsToggle)
{
_interactLabel.Text = button.IsPressed ? $"[{interactKey}] Deactivate" : $"[{interactKey}] Activate";
}
else
{
_interactLabel.Text = $"[{interactKey}] Press Button";
}
_interactLabel.Visible = true;
} else
{
_interactLabel.Visible = false;
}
}
}
else

View File

@@ -0,0 +1,15 @@
using Godot;
using Godot.Collections;
namespace CryptonymThunder.Code.Resources;
[GlobalClass]
public partial class ButtonComponentResource : Resource
{
[Export] public string ChannelId { get; set; } = "default_channel";
[Export] public bool IsToggle { get; set; } = false;
[Export] public bool IsOneTimeUse { get; set; } = false;
[ExportGroup("Requirements")]
[Export] public Array<InteractionRequirementResource> Requirements { get; set; } = [];
}

View File

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

View File

@@ -0,0 +1,9 @@
using Godot;
namespace CryptonymThunder.Code.Resources;
[GlobalClass]
public partial class DebugMessageActionResource : TriggerActionResource
{
[Export(PropertyHint.MultilineText)] public string Message { get; set; } = "Trigger Fired!";
}

View File

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

View File

@@ -0,0 +1,12 @@
using Godot;
using Godot.Collections;
namespace CryptonymThunder.Code.Resources;
[GlobalClass]
public partial class LogicSequenceComponentResource : Resource
{
[Export] public Array<string> RequiredChannels { get; set; } = [];
[Export] public Array<TriggerActionResource> OnCompleteActions { get; set; } = [];
[Export] public bool IsOneTimeTrigger { get; set; } = true;
}

View File

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

View File

@@ -0,0 +1,10 @@
using Godot;
namespace CryptonymThunder.Code.Resources;
[GlobalClass]
public partial class SpawnEntityActionResource : TriggerActionResource
{
[Export] public string ArchetypeId { get; set; } = "enemy_grunt";
[Export] public string SpawnerWorldId { get; set; } = "spawner_location_1";
}

View File

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

View File

@@ -0,0 +1,8 @@
using Godot;
namespace CryptonymThunder.Code.Resources;
public partial class TriggerActionResource : Resource
{
}

View File

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

View File

@@ -0,0 +1,9 @@
using Godot;
namespace CryptonymThunder.Code.Resources;
[GlobalClass]
public partial class UnlockDoorActionResource : TriggerActionResource
{
[Export] public string TargetWorldId { get; set; } = "door_to_unlock";
}

View File

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

View File

@@ -0,0 +1,9 @@
using Godot;
namespace CryptonymThunder.Code.Resources;
[GlobalClass]
public partial class WorldIdComponentResource : Resource
{
[Export] public string WorldId { get; set; } = "unique_id_01";
}

View File

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