Files
night-shift/src/Features/Items/Godot/WorldItemController.cs
T

138 lines
3.8 KiB
C#

using System;
using Game.Features.Items.Contracts;
using Game.Features.Items.Domain;
using Game.Godot.Extensions;
using Godot;
namespace Game.Features.Items.Godot;
public partial class WorldItemController : RigidBody3D, IPickupTarget
{
public const string ItemGroup = "world_items";
[Export] public string Key;
[Export] public string DefinitionId = "item";
[Export] public string DisplayName = "Item";
[Export] public HandRequirement HandRequirement = HandRequirement.One;
[Export] public CollisionShape3D Collision;
[Export] public Node3D WorldVisual;
[Export] public PackedScene HeldViewScene;
protected Node3D HeldView { get; private set; }
protected bool IsHeld => HeldView is not null;
public ItemInfo ItemInfo { get; private set; }
public ItemDefinition Definition { get; private set; }
public IItemInstance Item { get; private set; }
private ItemWorldController _registry;
public override void _Ready()
{
this.AssertRequiredExports();
if (string.IsNullOrWhiteSpace(DefinitionId))
{
throw new InvalidOperationException($"{Name}: item definition id cannot be empty.");
}
if (HeldViewScene is null)
{
throw new InvalidOperationException($"{Name}: held view scene is not assigned.");
}
Definition = new ItemDefinition(new ItemDefinitionId(DefinitionId), DisplayName, HandRequirement);
ItemInfo = new ItemInfo(
ItemInstanceId.From(this.GetStableInstanceIdFrom(Key)),
Definition.Id,
Definition.DisplayName,
Definition.HandRequirement
);
Item = CreateItemInstance(ItemInfo);
AddToGroup(ItemGroup);
RegisterWithAvailableWorld();
ApplyState(Item);
}
public override void _ExitTree()
{
_registry?.Unregister(ItemInfo.Id);
}
public void RegisterWith(ItemWorldController registry)
{
_registry = registry;
}
public void SetHeld(Node3D anchor, IItemInstance item)
{
if (IsHeld)
{
return;
}
Freeze = true;
Sleeping = true;
LinearVelocity = Vector3.Zero;
AngularVelocity = Vector3.Zero;
Collision.SetDeferred(CollisionShape3D.PropertyName.Disabled, true);
WorldVisual.Visible = false;
HeldView = HeldViewScene.Instantiate<Node3D>();
anchor.AddChild(HeldView);
HeldView.Transform = Transform3D.Identity;
OnHeldViewCreated(HeldView);
ApplyState(item);
}
public void SetDropped(Vector3 worldPosition, IItemInstance item)
{
if (HeldView is not null)
{
OnHeldViewRemoving(HeldView);
HeldView.QueueFree();
HeldView = null;
}
GlobalPosition = worldPosition;
LinearVelocity = Vector3.Zero;
AngularVelocity = Vector3.Zero;
WorldVisual.Visible = true;
Collision.SetDeferred(CollisionShape3D.PropertyName.Disabled, false);
Freeze = false;
Sleeping = false;
ApplyState(item);
}
public void ApplyState(IItemInstance item)
{
OnPresentationChanged(item);
}
protected virtual IItemInstance CreateItemInstance(ItemInfo itemInfo) => new PassiveItemInstance(itemInfo);
protected virtual void OnHeldViewCreated(Node3D heldView)
{
}
protected virtual void OnHeldViewRemoving(Node3D heldView)
{
}
protected virtual void OnPresentationChanged(IItemInstance item)
{
}
private void RegisterWithAvailableWorld()
{
foreach (Node node in GetTree().GetNodesInGroup(ItemWorldController.RegistryGroup))
{
if (node is ItemWorldController registry)
{
registry.Register(this);
return;
}
}
}
}