using Unity.Cinemachine;
using UnityEngine;
using UnityEngine.InputSystem;
namespace Jankenbots.Prototype
{
///
/// JANKENBOTS — client-local camera director. Owns four Cinemachine cameras and
/// lets the local player cycle between them; the CinemachineBrain on this Camera
/// blends smoothly on every switch.
///
/// • AvatarFollow — 3rd-person chase on YOUR character (on-foot).
/// • RobotFollow — chase cam on the bot (driving; the robot-follow cam).
/// • StandView — fixed view from your player stand, looking at the bot.
/// • CinematicOrbit— slow auto-orbit of the bot (spectate / flavor).
///
/// The default mode auto-follows the control state: on foot → AvatarFollow, seated
/// (driving) → RobotFollow — reading the SAME seat flag as the control handoff
/// (). Press V to cycle manually; claiming
/// or leaving a seat re-asserts the sensible default.
///
/// The vcams are intentionally "bare" (no procedural body/aim) — the director poses
/// their transforms each LateUpdate from the live targets, and the Brain just blends
/// between whichever is prioritised. Cameras are purely client-local, never networked.
///
[RequireComponent(typeof(Camera))]
public class CameraDirector : MonoBehaviour
{
public enum Mode { AvatarFollow, RobotFollow, StandView, CinematicOrbit }
[Header("Framing")]
public Vector3 avatarOffset = new Vector3(0f, 4f, -6f);
public Vector3 robotOffset = new Vector3(0f, 8f, -12f);
public float orbitRadius = 20f;
public float orbitHeight = 9f;
public float orbitSpeed = 18f; // deg/sec
public float lookHeight = 1.2f; // aim a bit above the target pivot
Transform _bot, _standOverlook;
CinemachineCamera _avatar, _robot, _stand, _orbit;
CinemachineCamera[] _all;
Mode _mode = Mode.AvatarFollow;
bool _userOverride;
bool _lastSeated;
float _orbitAngle;
PlayerAvatar _local;
void Start()
{
if (GetComponent() == null) gameObject.AddComponent();
// Retire the old bespoke bot-only follow cam if it's still on this camera.
var legacy = GetComponent();
if (legacy != null) legacy.enabled = false;
var b = GameObject.Find("Bot"); if (b != null) _bot = b.transform;
var s = GameObject.Find("StandPoint_0"); if (s != null) _standOverlook = s.transform;
var holder = new GameObject("CameraRigs").transform;
_avatar = Make(holder, "CM_AvatarFollow");
_robot = Make(holder, "CM_RobotFollow");
_stand = Make(holder, "CM_StandView");
_orbit = Make(holder, "CM_CinematicOrbit");
_all = new[] { _avatar, _robot, _stand, _orbit };
Apply(_mode);
}
CinemachineCamera Make(Transform parent, string name)
{
var go = new GameObject(name);
go.transform.SetParent(parent, false);
var vc = go.AddComponent();
vc.Priority = 0;
return vc;
}
void Update()
{
if (_local == null) _local = FindLocal();
var kb = Keyboard.current;
if (kb != null && kb.vKey.wasPressedThisFrame)
{
_mode = (Mode)(((int)_mode + 1) % _all.Length);
_userOverride = true;
Apply(_mode);
}
// A seat claim/leave re-asserts the sensible default (clears a manual override).
if (_local != null && _local.IsSeatedLocal != _lastSeated)
{
_lastSeated = _local.IsSeatedLocal;
_userOverride = false;
}
if (!_userOverride && _local != null)
{
Mode want = _local.IsSeatedLocal ? Mode.RobotFollow : Mode.AvatarFollow;
if (want != _mode) { _mode = want; Apply(_mode); }
}
}
void LateUpdate()
{
if (_local != null) PoseFollow(_avatar, _local.transform, avatarOffset);
if (_bot != null)
{
PoseFollow(_robot, _bot, robotOffset);
_orbitAngle += orbitSpeed * Time.deltaTime;
Vector3 off = Quaternion.Euler(0f, _orbitAngle, 0f) * new Vector3(0f, orbitHeight, -orbitRadius);
PoseFollow(_orbit, _bot, off);
if (_standOverlook != null)
{
_stand.transform.position = _standOverlook.position + Vector3.up * 1.5f;
_stand.transform.rotation = Quaternion.LookRotation(
(_bot.position + Vector3.up * lookHeight) - _stand.transform.position);
}
}
}
void PoseFollow(CinemachineCamera vc, Transform target, Vector3 worldOffset)
{
Vector3 pos = target.position + worldOffset;
vc.transform.position = pos;
vc.transform.rotation = Quaternion.LookRotation((target.position + Vector3.up * lookHeight) - pos);
}
PlayerAvatar FindLocal()
{
foreach (var a in FindObjectsByType(FindObjectsSortMode.None))
if (a.IsOwner) return a;
return null;
}
void Apply(Mode m)
{
for (int i = 0; i < _all.Length; i++)
_all[i].Priority = ((int)m == i) ? 20 : 0;
}
}
}