Aetheria/Assets/Scripts/Gameplay/ItemPickup.cs
Meta Vens 48b42992ee Loot 3 group A: the grab is presented, not simulated
IGrabPresenter.Grab(GrabEvent) only; the binder's Transform and velocity resolvers go. TractorBeam
presents a running grab (aims at Started's TargetPosition, emits at Power, dark otherwise). FieldDriver
draws its tendril from GrabEvents alone with a real-time phase clock over each event's PhaseDuration and
writes no transform and destroys nothing; its grab times move to FieldTester, now the scene's stand-in sim
that owns its pickup's timeline and pose. ItemPickup keeps its label lit while held. ImpactPresenterSmoke
becomes CapabilityPresentationSmoke with a grab section; its two untracked-ship expectations follow
DrawsAt since contact-hud. Builds (Unity 6000.3.24f1 batchmode: smoke ok, scene opens, no missing script).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 18:43:15 +02:00

68 lines
2.4 KiB
C#

using System;
using TMPro;
using CultMath;
using CultMath.UnityBridge;
using UnityEngine;
using static CultMath.math;
using Random = UnityEngine.Random;
// How one floating item looks. Zone owns the item's every fact (existence, position, expiry, pickup); once bound,
// this reads the latest sim body and draws, spun for show, and writes nothing back (rulings unity-presents-only-r3
// and sim-speed-presentation). An unbound pickup (FieldTester's test scene) leaves its transform alone.
public class ItemPickup : MonoBehaviour
{
public float RotationSpeed = 1f;
public float GridOffset;
public float LabelFadeDuration = .5f;
public float LabelPersistDuration = 3;
public float LabelDisplayAngle = 15;
public float LabelDisplayMaxDistance = 500;
public Transform ScanLabelContainer;
public TextMeshPro ScanLabel;
public ItemInstance Item { get; set; }
public float3 ViewOrigin { get; set; }
public float3 ViewDirection { get; set; }
private float _displayTime;
private float _timeOffset;
private Zone _zone;
private FloatingItem _floating;
public void Bind(Zone zone, FloatingItem floating)
{
_zone = zone;
_floating = floating;
Item = floating.Item;
_timeOffset = Random.value * 100;
Place();
}
private void Place()
{
var p = _floating.Body.Position;
transform.position = new Vector3(p.x, _zone.GetHeight(p) + GridOffset, p.y);
}
private void Update()
{
if (_floating != null)
{
Place();
transform.localRotation = Quaternion.Euler(
sin(Time.time - _timeOffset * RotationSpeed) * 90, 0, cos(Time.time - _timeOffset * RotationSpeed) * 90);
}
var diff = transform.position.ToCultMath() - ViewOrigin;
var toThis = normalize(diff);
var viewAngle = acos(dot(toThis, ViewDirection)) * Mathf.Rad2Deg;
if (length(diff) < LabelDisplayMaxDistance && viewAngle < LabelDisplayAngle)
_displayTime = Time.time;
if (_floating?.Holder != null) _displayTime = Time.time;
var targetAlpha = Time.time - _displayTime < LabelPersistDuration ? 1 : 0;
var c = ScanLabel.color;
c.a = c.a + sign(targetAlpha - c.a) * (Time.deltaTime / LabelFadeDuration);
ScanLabel.color = c;
ScanLabelContainer.rotation = Quaternion.LookRotation((-toThis).ToUnity());
}
}