using ProjectM.Simulation;
using Unity.Entities;
using Unity.Mathematics;
using Unity.NetCode;
namespace ProjectM.Client
{
///
/// Client-only twin-stick input gather. Samples the new Input System action map (the generated
/// ProjectMInput wrapper over Assets/Settings/Project M Input.inputactions) once per
/// frame and writes on the locally-owned player ghost (filtered to
/// ). Runs in — NOT the
/// prediction loop — so devices are read once per frame, never re-read during rollback.
///
/// Implemented as a managed (not a Burst ISystem) because it holds
/// and reads the managed Input System wrapper. Fire is an : the event field
/// is reset each frame and raised via Set() on the press edge, so a single click fires
/// exactly once; netcode accumulates the absolute Count into the command buffer across the
/// frame→tick boundary (read back in AbilityFireSystem as the predicted-spawn key).
///
///
/// NOTE: Input System types are fully qualified (e.g. UnityEngine.Vector2) and
/// using UnityEngine.InputSystem; is intentionally omitted — that namespace defines a
/// PlayerInput type that collides with and
/// makes the Entities generator bind RefRW<PlayerInput> to the managed class (a
/// spurious CS8377). The generated ProjectMInput wrapper lives in this assembly.
///
///
[UpdateInGroup(typeof(GhostInputSystemGroup))]
[WorldSystemFilter(WorldSystemFilterFlags.ClientSimulation)]
public partial class PlayerInputGatherSystem : SystemBase
{
private ProjectMInput _controls;
protected override void OnCreate()
{
RequireForUpdate();
_controls = new ProjectMInput();
_controls.Gameplay.Enable();
}
protected override void OnDestroy()
{
if (_controls != null)
{
_controls.Gameplay.Disable();
_controls.Dispose();
_controls = null;
}
}
protected override void OnUpdate()
{
var gameplay = _controls.Gameplay;
float2 move = (float2)gameplay.Move.ReadValue();
float2 aim = (float2)gameplay.Aim.ReadValue();
// Right-stick deadzone: a resting stick yields zero Aim so PlayerAimSystem falls back to the
// movement heading (controller-first directional aim).
if (math.lengthsq(aim) < 0.04f)
aim = float2.zero;
bool firePressed = gameplay.Fire.WasPressedThisFrame();
foreach (var input in SystemAPI.Query>().WithAll())
{
input.ValueRW.Move = move;
input.ValueRW.Aim = aim;
// Reset the per-frame event, then raise it on the press edge. Netcode latches the
// absolute Count into the command buffer; AbilityFireSystem reads it as the SpawnId key.
input.ValueRW.Fire = default;
if (firePressed)
input.ValueRW.Fire.Set();
}
}
}
}