mirror of
https://gitlab.com/MaddoScientisto/cirnogodot.git
synced 2026-06-01 11:15:33 +00:00
128 lines
No EOL
3.7 KiB
C#
128 lines
No EOL
3.7 KiB
C#
using System.Linq;
|
|
using Cirno.Scripts.Resources;
|
|
using Godot;
|
|
using Godot.Collections;
|
|
|
|
namespace Cirno.Scripts.Interactables;
|
|
|
|
public partial class ItemPickup3D : Interactable3D
|
|
{
|
|
private const float GravityAccel = 9.8f;
|
|
// How far below the item to probe for the floor each physics step.
|
|
private const float FloorProbeDistance = 10f;
|
|
|
|
[Export] public Array<LootItem> LootTable = [];
|
|
|
|
/// <summary>
|
|
/// Distance from the item's origin to its bottom edge.
|
|
/// Lifts the item above the floor surface so it doesn't clip into it.
|
|
/// Default matches the GenericItem3D collision shape: offset (0.065) + half cylinder height (0.269).
|
|
/// </summary>
|
|
[Export] public float GroundOffset { get; set; } = 0.204f;
|
|
|
|
private bool _autoPickup = false;
|
|
private bool _simulating = false;
|
|
private Vector3 _velocity = Vector3.Zero;
|
|
|
|
public bool AutoPickup => _autoPickup;
|
|
|
|
public override void _Ready()
|
|
{
|
|
_autoPickup = LootTable.Any(x => x.AutoPickup);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Applies an initial velocity and enables gravity simulation so the pickup
|
|
/// arcs through the air and settles on the floor.
|
|
/// </summary>
|
|
public void Launch(Vector3 velocity)
|
|
{
|
|
_velocity = velocity;
|
|
_simulating = true;
|
|
}
|
|
|
|
public override void _PhysicsProcess(double delta)
|
|
{
|
|
if (!_simulating) return;
|
|
|
|
_velocity.Y -= GravityAccel * (float)delta;
|
|
var nextPosition = GlobalPosition + _velocity * (float)delta;
|
|
|
|
// Cast downward to detect the floor so we don't sink through it.
|
|
var spaceState = GetWorld3D()?.DirectSpaceState;
|
|
var query = PhysicsRayQueryParameters3D.Create(
|
|
GlobalPosition,
|
|
GlobalPosition + Vector3.Down * FloorProbeDistance,
|
|
// Exclude the item's own collision layer (layer 32 per scene) and hit static geometry only.
|
|
0b1
|
|
);
|
|
query.Exclude = [GetRid()];
|
|
var hit = spaceState?.IntersectRay(query) ?? [];
|
|
|
|
if (hit.Count > 0)
|
|
{
|
|
var floorY = hit["position"].AsVector3().Y;
|
|
if (nextPosition.Y - GroundOffset <= floorY)
|
|
{
|
|
nextPosition.Y = floorY + GroundOffset;
|
|
_simulating = false;
|
|
_velocity = Vector3.Zero;
|
|
}
|
|
}
|
|
|
|
GlobalPosition = nextPosition;
|
|
}
|
|
|
|
public override bool Activate(ActivationType activationType = ActivationType.Toggle)
|
|
{
|
|
if (!MeetsRequirements()) return false;
|
|
Collect();
|
|
|
|
return true;
|
|
}
|
|
|
|
public void AddItemsToInventory()
|
|
{
|
|
var failedItems = new Array<LootItem>();
|
|
foreach (var item in LootTable)
|
|
{
|
|
if (!InventoryManager.Instance.AddItem(item))
|
|
{
|
|
failedItems.Add(item);
|
|
}
|
|
}
|
|
|
|
if (failedItems.Count > 0)
|
|
{
|
|
foreach (var failedItem in failedItems)
|
|
{
|
|
var dup = this.Duplicate() as ItemPickup;
|
|
this.AddSibling(dup);
|
|
dup.LootTable = [failedItem];
|
|
}
|
|
}
|
|
|
|
// Delet This
|
|
|
|
QueueFree();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Auto-pickup items are handled exclusively by <c>AutoPickupModule3D</c>.
|
|
/// Returning <c>false</c> here prevents them from entering the normal
|
|
/// interaction selector, so they cannot be manually activated by the player.
|
|
/// </summary>
|
|
public override bool CanActivate() => !_autoPickup;
|
|
|
|
public void Collect()
|
|
{
|
|
AddItemsToInventory();
|
|
}
|
|
|
|
public void SetSprite(Texture2D sprite)
|
|
{
|
|
var spriteNode = GetNodeOrNull<Sprite3D>("Sprite3D");
|
|
if (spriteNode is null) return;
|
|
spriteNode.Texture = sprite;
|
|
}
|
|
} |