138 lines
3.8 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|
|
}
|