|
|
|
@ -2,7 +2,7 @@ public partial class PickupController : Node3D |
|
|
|
|
{ |
|
|
|
|
public Item CurrentItem { get; private set; } |
|
|
|
|
public bool HasItemsHeld => GetChildCount() > 0; |
|
|
|
|
MeshInstance3D _placementPreview; |
|
|
|
|
Node3D _placementPreview; |
|
|
|
|
|
|
|
|
|
[Export] public Camera3D Camera { get; set; } |
|
|
|
|
[Export] public float PickupDistance { get; set; } = 2.0f; |
|
|
|
@ -24,11 +24,19 @@ public partial class PickupController : Node3D |
|
|
|
|
|
|
|
|
|
if (@event.IsActionPressed("interact_pickup")) { |
|
|
|
|
if (!HasItemsHeld) { |
|
|
|
|
// Create clone of the item's model, use it as placement preview. |
|
|
|
|
_placementPreview = (Node3D)CurrentItem.Model.Duplicate(0); |
|
|
|
|
_placementPreview.Name = "PlacementPreview"; |
|
|
|
|
_placementPreview.TopLevel = true; |
|
|
|
|
_placementPreview.Visible = false; |
|
|
|
|
SetMeshLayerOutline(_placementPreview, OutlineMode.Exclusive); |
|
|
|
|
AddChild(_placementPreview); |
|
|
|
|
|
|
|
|
|
// Parent item to the `PickupController`. |
|
|
|
|
var prevRot = CurrentItem.GlobalRotation; |
|
|
|
|
CurrentItem.GetParent().RemoveChild(CurrentItem); |
|
|
|
|
AddChild(CurrentItem); |
|
|
|
|
CurrentItem.Mesh.Layers &= (uint)~RenderLayer.Outline; |
|
|
|
|
SetMeshLayerOutline(CurrentItem.Model, OutlineMode.Disable); |
|
|
|
|
CurrentItem.Position = Vector3.Zero; |
|
|
|
|
CurrentItem.GlobalRotation = prevRot; |
|
|
|
|
CurrentItem.CollisionLayer &= (uint)~PhysicsLayer.Static; |
|
|
|
@ -44,7 +52,14 @@ public partial class PickupController : Node3D |
|
|
|
|
// CurrentItem.Freeze = false; |
|
|
|
|
RemoveChild(CurrentItem); |
|
|
|
|
_world.AddChild(CurrentItem); |
|
|
|
|
CurrentItem.GlobalTransform = prevTransform; |
|
|
|
|
|
|
|
|
|
CurrentItem.GlobalTransform = _placementPreview.Visible |
|
|
|
|
? _placementPreview.GlobalTransform |
|
|
|
|
: prevTransform; |
|
|
|
|
|
|
|
|
|
RemoveChild(_placementPreview); |
|
|
|
|
_placementPreview.QueueFree(); |
|
|
|
|
_placementPreview = null; |
|
|
|
|
|
|
|
|
|
GetViewport().SetInputAsHandled(); |
|
|
|
|
} |
|
|
|
@ -59,28 +74,31 @@ public partial class PickupController : Node3D |
|
|
|
|
if (HasItemsHeld) { |
|
|
|
|
if ((RayToMouseCursor() is RayResult ray) && (ray.Collider is Grid)) { |
|
|
|
|
// Snao rotation to nearest axis. |
|
|
|
|
// FIXME: This needs to snap to the |
|
|
|
|
// var globalRot = CurrentItem.GlobalRotation; |
|
|
|
|
// globalRot.X = Snapped(globalRot.X, Tau / 4); |
|
|
|
|
// globalRot.Y = Snapped(globalRot.Y, Tau / 4); |
|
|
|
|
// globalRot.Z = Snapped(globalRot.Z, Tau / 4); |
|
|
|
|
// CurrentItem.GlobalRotation = globalRot; |
|
|
|
|
// FIXME: This needs to snap relative to the grid rotation. |
|
|
|
|
var globalRot = CurrentItem.GlobalRotation; |
|
|
|
|
globalRot.X = Snapped(globalRot.X, Tau / 4); |
|
|
|
|
globalRot.Y = Snapped(globalRot.Y, Tau / 4); |
|
|
|
|
globalRot.Z = Snapped(globalRot.Z, Tau / 4); |
|
|
|
|
_placementPreview.GlobalRotation = globalRot; |
|
|
|
|
|
|
|
|
|
// Snap the position to the grid. |
|
|
|
|
var halfSize = (Vector3)CurrentItem.Size * Grid.StepSize / 2; |
|
|
|
|
var pos = ray.Position + halfSize * (ray.Normal * CurrentItem.GlobalTransform.Basis); |
|
|
|
|
var pos = ray.Position + halfSize * (ray.Normal * _placementPreview.GlobalTransform.Basis); |
|
|
|
|
pos = pos.Snapped(Grid.StepSize * Vector3.One); // FIXME: This does global snapping only |
|
|
|
|
CurrentItem.GlobalPosition = pos; |
|
|
|
|
_placementPreview.GlobalPosition = pos; |
|
|
|
|
_placementPreview.Visible = true; |
|
|
|
|
} else { |
|
|
|
|
_placementPreview.Visible = false; |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
var interactable = RayToMouseCursor()?.Collider; |
|
|
|
|
|
|
|
|
|
// Remove the outline from the previously looked-at item. |
|
|
|
|
if (CurrentItem != null) CurrentItem.Mesh.Layers &= (uint)~RenderLayer.Outline; |
|
|
|
|
if (CurrentItem != null) SetMeshLayerOutline(CurrentItem.Model, OutlineMode.Disable); |
|
|
|
|
// If the ray hits anything and the object hit is an item, set it as current. |
|
|
|
|
CurrentItem = interactable as Item; |
|
|
|
|
// Add the outline to the currently looked-at item. |
|
|
|
|
if (CurrentItem != null) CurrentItem.Mesh.Layers |= (uint)RenderLayer.Outline; |
|
|
|
|
if (CurrentItem != null) SetMeshLayerOutline(CurrentItem.Model, OutlineMode.Enable); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -89,7 +107,11 @@ public partial class PickupController : Node3D |
|
|
|
|
if (CurrentItem == null) return; |
|
|
|
|
if (!IsInstanceValid(CurrentItem)) { |
|
|
|
|
CurrentItem = null; |
|
|
|
|
_placementPreview?.QueueFree(); |
|
|
|
|
if (_placementPreview != null) { |
|
|
|
|
RemoveChild(_placementPreview); |
|
|
|
|
_placementPreview.QueueFree(); |
|
|
|
|
_placementPreview = null; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -115,4 +137,16 @@ public partial class PickupController : Node3D |
|
|
|
|
public Vector3 Position { get; } = (Vector3)dict["position"]; |
|
|
|
|
public Vector3 Normal { get; } = (Vector3)dict["normal"]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
enum OutlineMode { Disable, Enable, Exclusive } |
|
|
|
|
static void SetMeshLayerOutline(Node3D parent, OutlineMode mode) |
|
|
|
|
{ |
|
|
|
|
var children = parent.FindChildren("*", "MeshInstance3D", owned: false); |
|
|
|
|
foreach (var mesh in children.Cast<MeshInstance3D>()) |
|
|
|
|
switch (mode) { |
|
|
|
|
case OutlineMode.Disable: mesh.Layers &= ~(uint)RenderLayer.Outline; break; |
|
|
|
|
case OutlineMode.Enable: mesh.Layers |= (uint)RenderLayer.Outline; break; |
|
|
|
|
case OutlineMode.Exclusive: mesh.Layers = (uint)RenderLayer.Outline; break; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|