SharedMoverController.Input.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  1. using System.Numerics;
  2. using Content.Shared.Alert;
  3. using Content.Shared.CCVar;
  4. using Content.Shared.Follower.Components;
  5. using Content.Shared.Input;
  6. using Content.Shared.Movement.Components;
  7. using Content.Shared.Movement.Events;
  8. using Robust.Shared.GameStates;
  9. using Robust.Shared.Input;
  10. using Robust.Shared.Input.Binding;
  11. using Robust.Shared.Map.Components;
  12. using Robust.Shared.Player;
  13. using Robust.Shared.Prototypes;
  14. using Robust.Shared.Serialization;
  15. using Robust.Shared.Timing;
  16. using Robust.Shared.Utility;
  17. using Robust.Shared.Maths; // Shitmed Change
  18. namespace Content.Shared.Movement.Systems
  19. {
  20. /// <summary>
  21. /// Handles converting inputs into movement.
  22. /// </summary>
  23. public abstract partial class SharedMoverController
  24. {
  25. public bool CameraRotationLocked { get; set; }
  26. public static ProtoId<AlertPrototype> WalkingAlert = "Walking";
  27. private void InitializeInput()
  28. {
  29. var moveUpCmdHandler = new MoverDirInputCmdHandler(this, Direction.North);
  30. var moveLeftCmdHandler = new MoverDirInputCmdHandler(this, Direction.West);
  31. var moveRightCmdHandler = new MoverDirInputCmdHandler(this, Direction.East);
  32. var moveDownCmdHandler = new MoverDirInputCmdHandler(this, Direction.South);
  33. CommandBinds.Builder
  34. .Bind(EngineKeyFunctions.MoveUp, moveUpCmdHandler)
  35. .Bind(EngineKeyFunctions.MoveLeft, moveLeftCmdHandler)
  36. .Bind(EngineKeyFunctions.MoveRight, moveRightCmdHandler)
  37. .Bind(EngineKeyFunctions.MoveDown, moveDownCmdHandler)
  38. .Bind(EngineKeyFunctions.Walk, new WalkInputCmdHandler(this))
  39. .Bind(EngineKeyFunctions.CameraRotateLeft, new CameraRotateInputCmdHandler(this, Direction.East))
  40. .Bind(EngineKeyFunctions.CameraRotateRight, new CameraRotateInputCmdHandler(this, Direction.West))
  41. .Bind(EngineKeyFunctions.CameraReset, new CameraResetInputCmdHandler(this))
  42. // TODO: Relay
  43. // Shuttle
  44. .Bind(ContentKeyFunctions.ShuttleStrafeUp, new ShuttleInputCmdHandler(this, ShuttleButtons.StrafeUp))
  45. .Bind(ContentKeyFunctions.ShuttleStrafeLeft, new ShuttleInputCmdHandler(this, ShuttleButtons.StrafeLeft))
  46. .Bind(ContentKeyFunctions.ShuttleStrafeRight, new ShuttleInputCmdHandler(this, ShuttleButtons.StrafeRight))
  47. .Bind(ContentKeyFunctions.ShuttleStrafeDown, new ShuttleInputCmdHandler(this, ShuttleButtons.StrafeDown))
  48. .Bind(ContentKeyFunctions.ShuttleRotateLeft, new ShuttleInputCmdHandler(this, ShuttleButtons.RotateLeft))
  49. .Bind(ContentKeyFunctions.ShuttleRotateRight, new ShuttleInputCmdHandler(this, ShuttleButtons.RotateRight))
  50. .Bind(ContentKeyFunctions.ShuttleBrake, new ShuttleInputCmdHandler(this, ShuttleButtons.Brake))
  51. .Register<SharedMoverController>();
  52. SubscribeLocalEvent<InputMoverComponent, ComponentInit>(OnInputInit);
  53. SubscribeLocalEvent<InputMoverComponent, ComponentGetState>(OnMoverGetState);
  54. SubscribeLocalEvent<InputMoverComponent, ComponentHandleState>(OnMoverHandleState);
  55. SubscribeLocalEvent<InputMoverComponent, EntParentChangedMessage>(OnInputParentChange);
  56. SubscribeLocalEvent<FollowedComponent, EntParentChangedMessage>(OnFollowedParentChange);
  57. Subs.CVar(_configManager, CCVars.CameraRotationLocked, obj => CameraRotationLocked = obj, true);
  58. Subs.CVar(_configManager, CCVars.GameDiagonalMovement, value => DiagonalMovementEnabled = value, true);
  59. }
  60. /// <summary>
  61. /// Gets the buttons held with opposites cancelled out.
  62. /// </summary>
  63. public static MoveButtons GetNormalizedMovement(MoveButtons buttons)
  64. {
  65. var oldMovement = buttons;
  66. if ((oldMovement & (MoveButtons.Left | MoveButtons.Right)) == (MoveButtons.Left | MoveButtons.Right))
  67. {
  68. oldMovement &= ~MoveButtons.Left;
  69. oldMovement &= ~MoveButtons.Right;
  70. }
  71. if ((oldMovement & (MoveButtons.Up | MoveButtons.Down)) == (MoveButtons.Up | MoveButtons.Down))
  72. {
  73. oldMovement &= ~MoveButtons.Up;
  74. oldMovement &= ~MoveButtons.Down;
  75. }
  76. return oldMovement;
  77. }
  78. protected void SetMoveInput(Entity<InputMoverComponent> entity, MoveButtons buttons)
  79. {
  80. if (entity.Comp.HeldMoveButtons == buttons)
  81. return;
  82. // Relay the fact we had any movement event.
  83. // TODO: Ideally we'd do these in a tick instead of out of sim.
  84. // Shitmed Change Start
  85. Vector2 vector2 = DirVecForButtons(buttons);
  86. Vector2i vector2i = new Vector2i((int)vector2.X, (int)vector2.Y);
  87. Direction dir = (vector2i == Vector2i.Zero) ? Direction.Invalid : vector2i.AsDirection();
  88. var moveEvent = new MoveInputEvent(entity, buttons, dir, buttons != 0);
  89. // Shitmed Change End
  90. entity.Comp.HeldMoveButtons = buttons;
  91. RaiseLocalEvent(entity, ref moveEvent);
  92. Dirty(entity, entity.Comp);
  93. var ev = new SpriteMoveEvent(entity.Comp.HeldMoveButtons != MoveButtons.None);
  94. RaiseLocalEvent(entity, ref ev);
  95. }
  96. private void OnMoverHandleState(Entity<InputMoverComponent> entity, ref ComponentHandleState args)
  97. {
  98. if (args.Current is not InputMoverComponentState state)
  99. return;
  100. // Handle state
  101. entity.Comp.LerpTarget = state.LerpTarget;
  102. entity.Comp.RelativeRotation = state.RelativeRotation;
  103. entity.Comp.TargetRelativeRotation = state.TargetRelativeRotation;
  104. entity.Comp.CanMove = state.CanMove;
  105. entity.Comp.RelativeEntity = EnsureEntity<InputMoverComponent>(state.RelativeEntity, entity.Owner);
  106. // Reset
  107. entity.Comp.LastInputTick = GameTick.Zero;
  108. entity.Comp.LastInputSubTick = 0;
  109. // Shitmed Change Start
  110. Vector2 vector2 = DirVecForButtons(entity.Comp.HeldMoveButtons);
  111. Vector2i vector2i = new Vector2i((int)vector2.X, (int)vector2.Y);
  112. Direction dir = (vector2i == Vector2i.Zero) ? Direction.Invalid : vector2i.AsDirection();
  113. // Shitmed Change End
  114. if (entity.Comp.HeldMoveButtons != state.HeldMoveButtons)
  115. {
  116. var moveEvent = new MoveInputEvent(entity, entity.Comp.HeldMoveButtons, dir, state.HeldMoveButtons != 0); // Shitmed Change
  117. entity.Comp.HeldMoveButtons = state.HeldMoveButtons;
  118. RaiseLocalEvent(entity.Owner, ref moveEvent);
  119. var ev = new SpriteMoveEvent(entity.Comp.HeldMoveButtons != MoveButtons.None);
  120. RaiseLocalEvent(entity, ref ev);
  121. }
  122. }
  123. private void OnMoverGetState(Entity<InputMoverComponent> entity, ref ComponentGetState args)
  124. {
  125. args.State = new InputMoverComponentState()
  126. {
  127. CanMove = entity.Comp.CanMove,
  128. RelativeEntity = GetNetEntity(entity.Comp.RelativeEntity),
  129. LerpTarget = entity.Comp.LerpTarget,
  130. HeldMoveButtons = entity.Comp.HeldMoveButtons,
  131. RelativeRotation = entity.Comp.RelativeRotation,
  132. TargetRelativeRotation = entity.Comp.TargetRelativeRotation,
  133. };
  134. }
  135. private void ShutdownInput()
  136. {
  137. CommandBinds.Unregister<SharedMoverController>();
  138. }
  139. public bool DiagonalMovementEnabled { get; private set; }
  140. protected virtual void HandleShuttleInput(EntityUid uid, ShuttleButtons button, ushort subTick, bool state) { }
  141. public void RotateCamera(EntityUid uid, Angle angle)
  142. {
  143. if (CameraRotationLocked || !MoverQuery.TryGetComponent(uid, out var mover))
  144. return;
  145. mover.TargetRelativeRotation += angle;
  146. Dirty(uid, mover);
  147. }
  148. public void ResetCamera(EntityUid uid)
  149. {
  150. if (CameraRotationLocked ||
  151. !MoverQuery.TryGetComponent(uid, out var mover))
  152. {
  153. return;
  154. }
  155. // Shitmed Change Start
  156. var xform = XformQuery.GetComponent(uid);
  157. if (TryComp(uid, out RelayInputMoverComponent? relay)
  158. && TryComp(relay.RelayEntity, out TransformComponent? relayXform)
  159. && MoverQuery.TryGetComponent(relay.RelayEntity, out var relayMover))
  160. xform = relayXform;
  161. // If we updated parent then cancel the accumulator and force it now.
  162. if (!TryUpdateRelative(mover, xform) && mover.TargetRelativeRotation.Equals(Angle.Zero))
  163. return;
  164. // Shitmed Change End
  165. mover.LerpTarget = TimeSpan.Zero;
  166. mover.TargetRelativeRotation = Angle.Zero;
  167. Dirty(uid, mover);
  168. }
  169. private bool TryUpdateRelative(InputMoverComponent mover, TransformComponent xform)
  170. {
  171. var relative = xform.GridUid;
  172. relative ??= xform.MapUid;
  173. // So essentially what we want:
  174. // 1. If we go from grid to map then preserve our rotation and continue as usual
  175. // 2. If we go from grid -> grid then (after lerp time) snap to nearest cardinal (probably imperceptible)
  176. // 3. If we go from map -> grid then (after lerp time) snap to nearest cardinal
  177. if (mover.RelativeEntity.Equals(relative))
  178. return false;
  179. // Okay need to get our old relative rotation with respect to our new relative rotation
  180. // e.g. if we were right side up on our current grid need to get what that is on our new grid.
  181. var currentRotation = Angle.Zero;
  182. var targetRotation = Angle.Zero;
  183. // Get our current relative rotation
  184. if (XformQuery.TryGetComponent(mover.RelativeEntity, out var oldRelativeXform))
  185. {
  186. currentRotation = _transform.GetWorldRotation(oldRelativeXform, XformQuery) + mover.RelativeRotation;
  187. }
  188. if (XformQuery.TryGetComponent(relative, out var relativeXform))
  189. {
  190. // This is our current rotation relative to our new parent.
  191. mover.RelativeRotation = (currentRotation - _transform.GetWorldRotation(relativeXform)).FlipPositive();
  192. }
  193. // If we went from grid -> map we'll preserve our worldrotation
  194. if (relative != null && HasComp<MapComponent>(relative.Value))
  195. {
  196. targetRotation = currentRotation.FlipPositive().Reduced();
  197. }
  198. // If we went from grid -> grid OR grid -> map then snap the target to cardinal and lerp there.
  199. // OR just rotate to zero (depending on cvar)
  200. else if (relative != null && _mapManager.IsGrid(relative.Value))
  201. {
  202. if (CameraRotationLocked)
  203. targetRotation = Angle.Zero;
  204. else
  205. targetRotation = mover.RelativeRotation.GetCardinalDir().ToAngle().Reduced();
  206. }
  207. mover.RelativeEntity = relative;
  208. mover.TargetRelativeRotation = targetRotation;
  209. return true;
  210. }
  211. public Angle GetParentGridAngle(InputMoverComponent mover)
  212. {
  213. var rotation = mover.RelativeRotation;
  214. if (XformQuery.TryGetComponent(mover.RelativeEntity, out var relativeXform))
  215. return _transform.GetWorldRotation(relativeXform) + rotation;
  216. return rotation;
  217. }
  218. private void OnFollowedParentChange(Entity<FollowedComponent> entity, ref EntParentChangedMessage args)
  219. {
  220. foreach (var foll in entity.Comp.Following)
  221. {
  222. if (!MoverQuery.TryGetComponent(foll, out var mover))
  223. continue;
  224. var ev = new EntParentChangedMessage(foll, null, args.OldMapId, XformQuery.GetComponent(foll));
  225. OnInputParentChange((foll, mover), ref ev);
  226. }
  227. }
  228. private void OnInputParentChange(Entity<InputMoverComponent> entity, ref EntParentChangedMessage args)
  229. {
  230. // If we change our grid / map then delay updating our LastGridAngle.
  231. var relative = args.Transform.GridUid;
  232. relative ??= args.Transform.MapUid;
  233. if (entity.Comp.LifeStage < ComponentLifeStage.Running)
  234. {
  235. entity.Comp.RelativeEntity = relative;
  236. Dirty(entity.Owner, entity.Comp);
  237. return;
  238. }
  239. var oldMapId = args.OldMapId;
  240. var mapId = args.Transform.MapUid;
  241. // If we change maps then reset eye rotation entirely.
  242. if (oldMapId != mapId)
  243. {
  244. entity.Comp.RelativeEntity = relative;
  245. entity.Comp.TargetRelativeRotation = Angle.Zero;
  246. entity.Comp.RelativeRotation = Angle.Zero;
  247. entity.Comp.LerpTarget = TimeSpan.Zero;
  248. Dirty(entity.Owner, entity.Comp);
  249. return;
  250. }
  251. // If we go on a grid and back off then just reset the accumulator.
  252. if (relative == entity.Comp.RelativeEntity)
  253. {
  254. if (entity.Comp.LerpTarget >= Timing.CurTime)
  255. {
  256. entity.Comp.LerpTarget = TimeSpan.Zero;
  257. Dirty(entity.Owner, entity.Comp);
  258. }
  259. return;
  260. }
  261. entity.Comp.LerpTarget = TimeSpan.FromSeconds(InputMoverComponent.LerpTime) + Timing.CurTime;
  262. Dirty(entity.Owner, entity.Comp);
  263. }
  264. private void HandleDirChange(EntityUid entity, Direction dir, ushort subTick, bool state)
  265. {
  266. // Relayed movement just uses the same keybinds given we're moving the relayed entity
  267. // the same as us.
  268. if (TryComp<RelayInputMoverComponent>(entity, out var relayMover))
  269. {
  270. DebugTools.Assert(relayMover.RelayEntity != entity);
  271. DebugTools.AssertNotNull(relayMover.RelayEntity);
  272. if (MoverQuery.TryGetComponent(entity, out var mover))
  273. SetMoveInput((entity, mover), MoveButtons.None);
  274. if (!_mobState.IsIncapacitated(entity))
  275. HandleDirChange(relayMover.RelayEntity, dir, subTick, state);
  276. return;
  277. }
  278. if (!MoverQuery.TryGetComponent(entity, out var moverComp))
  279. return;
  280. // Shitmed Change Start
  281. var moverEntity = new Entity<InputMoverComponent>(entity, moverComp);
  282. var moveEvent = new MoveInputEvent(moverEntity, moverComp.HeldMoveButtons, dir, state);
  283. RaiseLocalEvent(entity, ref moveEvent);
  284. // Shitmed Change End
  285. // For stuff like "Moving out of locker" or the likes
  286. // We'll relay a movement input to the parent.
  287. if (_container.IsEntityInContainer(entity) &&
  288. TryComp(entity, out TransformComponent? xform) &&
  289. xform.ParentUid.IsValid() &&
  290. _mobState.IsAlive(entity))
  291. {
  292. var relayMoveEvent = new ContainerRelayMovementEntityEvent(entity);
  293. RaiseLocalEvent(xform.ParentUid, ref relayMoveEvent);
  294. }
  295. SetVelocityDirection((entity, moverComp), dir, subTick, state);
  296. }
  297. private void OnInputInit(Entity<InputMoverComponent> entity, ref ComponentInit args)
  298. {
  299. var xform = Transform(entity.Owner);
  300. if (!xform.ParentUid.IsValid())
  301. return;
  302. entity.Comp.RelativeEntity = xform.GridUid ?? xform.MapUid;
  303. entity.Comp.TargetRelativeRotation = Angle.Zero;
  304. }
  305. private void HandleRunChange(EntityUid uid, ushort subTick, bool walking)
  306. {
  307. MoverQuery.TryGetComponent(uid, out var moverComp);
  308. if (TryComp<RelayInputMoverComponent>(uid, out var relayMover))
  309. {
  310. // if we swap to relay then stop our existing input if we ever change back.
  311. if (moverComp != null)
  312. {
  313. SetMoveInput((uid, moverComp), MoveButtons.None);
  314. }
  315. HandleRunChange(relayMover.RelayEntity, subTick, walking);
  316. return;
  317. }
  318. if (moverComp == null) return;
  319. SetSprinting((uid, moverComp), subTick, walking);
  320. }
  321. public (Vector2 Walking, Vector2 Sprinting) GetVelocityInput(InputMoverComponent mover)
  322. {
  323. if (!Timing.InSimulation)
  324. {
  325. // Outside of simulation we'll be running client predicted movement per-frame.
  326. // So return a full-length vector as if it's a full tick.
  327. // Physics system will have the correct time step anyways.
  328. var immediateDir = DirVecForButtons(mover.HeldMoveButtons);
  329. return mover.Sprinting ? (Vector2.Zero, immediateDir) : (immediateDir, Vector2.Zero);
  330. }
  331. Vector2 walk;
  332. Vector2 sprint;
  333. float remainingFraction;
  334. if (Timing.CurTick > mover.LastInputTick)
  335. {
  336. walk = Vector2.Zero;
  337. sprint = Vector2.Zero;
  338. remainingFraction = 1;
  339. }
  340. else
  341. {
  342. walk = mover.CurTickWalkMovement;
  343. sprint = mover.CurTickSprintMovement;
  344. remainingFraction = (ushort.MaxValue - mover.LastInputSubTick) / (float)ushort.MaxValue;
  345. }
  346. var curDir = DirVecForButtons(mover.HeldMoveButtons) * remainingFraction;
  347. if (mover.Sprinting)
  348. {
  349. sprint += curDir;
  350. }
  351. else
  352. {
  353. walk += curDir;
  354. }
  355. // Logger.Info($"{curDir}{walk}{sprint}");
  356. return (walk, sprint);
  357. }
  358. /// <summary>
  359. /// Toggles one of the four cardinal directions. Each of the four directions are
  360. /// composed into a single direction vector, <see cref="VelocityDir"/>. Enabling
  361. /// opposite directions will cancel each other out, resulting in no direction.
  362. /// </summary>
  363. public void SetVelocityDirection(Entity<InputMoverComponent> entity, Direction direction, ushort subTick, bool enabled)
  364. {
  365. // Logger.Info($"[{_gameTiming.CurTick}/{subTick}] {direction}: {enabled}");
  366. var bit = direction switch
  367. {
  368. Direction.East => MoveButtons.Right,
  369. Direction.North => MoveButtons.Up,
  370. Direction.West => MoveButtons.Left,
  371. Direction.South => MoveButtons.Down,
  372. _ => throw new ArgumentException(nameof(direction))
  373. };
  374. SetMoveInput(entity, subTick, enabled, bit);
  375. }
  376. private void SetMoveInput(Entity<InputMoverComponent> entity, ushort subTick, bool enabled, MoveButtons bit)
  377. {
  378. // Modifies held state of a movement button at a certain sub tick and updates current tick movement vectors.
  379. ResetSubtick(entity.Comp);
  380. if (subTick >= entity.Comp.LastInputSubTick)
  381. {
  382. var fraction = (subTick - entity.Comp.LastInputSubTick) / (float)ushort.MaxValue;
  383. ref var lastMoveAmount = ref entity.Comp.Sprinting ? ref entity.Comp.CurTickSprintMovement : ref entity.Comp.CurTickWalkMovement;
  384. lastMoveAmount += DirVecForButtons(entity.Comp.HeldMoveButtons) * fraction;
  385. entity.Comp.LastInputSubTick = subTick;
  386. }
  387. var buttons = entity.Comp.HeldMoveButtons;
  388. if (enabled)
  389. {
  390. buttons |= bit;
  391. }
  392. else
  393. {
  394. buttons &= ~bit;
  395. }
  396. SetMoveInput(entity, buttons);
  397. }
  398. private void ResetSubtick(InputMoverComponent component)
  399. {
  400. if (Timing.CurTick <= component.LastInputTick) return;
  401. component.CurTickWalkMovement = Vector2.Zero;
  402. component.CurTickSprintMovement = Vector2.Zero;
  403. component.LastInputTick = Timing.CurTick;
  404. component.LastInputSubTick = 0;
  405. }
  406. public virtual void SetSprinting(Entity<InputMoverComponent> entity, ushort subTick, bool walking)
  407. {
  408. // Logger.Info($"[{_gameTiming.CurTick}/{subTick}] Sprint: {enabled}");
  409. SetMoveInput(entity, subTick, walking, MoveButtons.Walk);
  410. }
  411. /// <summary>
  412. /// Retrieves the normalized direction vector for a specified combination of movement keys.
  413. /// </summary>
  414. private Vector2 DirVecForButtons(MoveButtons buttons)
  415. {
  416. // key directions are in screen coordinates
  417. // _moveDir is in world coordinates
  418. // if the camera is moved, this needs to be changed
  419. var x = 0;
  420. x -= HasFlag(buttons, MoveButtons.Left) ? 1 : 0;
  421. x += HasFlag(buttons, MoveButtons.Right) ? 1 : 0;
  422. var y = 0;
  423. if (DiagonalMovementEnabled || x == 0)
  424. {
  425. y -= HasFlag(buttons, MoveButtons.Down) ? 1 : 0;
  426. y += HasFlag(buttons, MoveButtons.Up) ? 1 : 0;
  427. }
  428. var vec = new Vector2(x, y);
  429. // can't normalize zero length vector
  430. if (vec.LengthSquared() > 1.0e-6)
  431. {
  432. // Normalize so that diagonals aren't faster or something.
  433. vec = vec.Normalized();
  434. }
  435. return vec;
  436. }
  437. private static bool HasFlag(MoveButtons buttons, MoveButtons flag)
  438. {
  439. return (buttons & flag) == flag;
  440. }
  441. private sealed class CameraRotateInputCmdHandler : InputCmdHandler
  442. {
  443. private readonly SharedMoverController _controller;
  444. private readonly Angle _angle;
  445. public CameraRotateInputCmdHandler(SharedMoverController controller, Direction direction)
  446. {
  447. _controller = controller;
  448. _angle = direction.ToAngle();
  449. }
  450. public override bool HandleCmdMessage(IEntityManager entManager, ICommonSession? session, IFullInputCmdMessage message)
  451. {
  452. if (session?.AttachedEntity == null) return false;
  453. if (message.State != BoundKeyState.Up)
  454. return false;
  455. _controller.RotateCamera(session.AttachedEntity.Value, _angle);
  456. return false;
  457. }
  458. }
  459. private sealed class CameraResetInputCmdHandler : InputCmdHandler
  460. {
  461. private readonly SharedMoverController _controller;
  462. public CameraResetInputCmdHandler(SharedMoverController controller)
  463. {
  464. _controller = controller;
  465. }
  466. public override bool HandleCmdMessage(IEntityManager entManager, ICommonSession? session, IFullInputCmdMessage message)
  467. {
  468. if (session?.AttachedEntity == null) return false;
  469. if (message.State != BoundKeyState.Up)
  470. return false;
  471. _controller.ResetCamera(session.AttachedEntity.Value);
  472. return false;
  473. }
  474. }
  475. private sealed class MoverDirInputCmdHandler : InputCmdHandler
  476. {
  477. private readonly SharedMoverController _controller;
  478. private readonly Direction _dir;
  479. public MoverDirInputCmdHandler(SharedMoverController controller, Direction dir)
  480. {
  481. _controller = controller;
  482. _dir = dir;
  483. }
  484. public override bool HandleCmdMessage(IEntityManager entManager, ICommonSession? session, IFullInputCmdMessage message)
  485. {
  486. if (session?.AttachedEntity == null) return false;
  487. _controller.HandleDirChange(session.AttachedEntity.Value, _dir, message.SubTick, message.State == BoundKeyState.Down);
  488. return false;
  489. }
  490. }
  491. private sealed class WalkInputCmdHandler : InputCmdHandler
  492. {
  493. private SharedMoverController _controller;
  494. public WalkInputCmdHandler(SharedMoverController controller)
  495. {
  496. _controller = controller;
  497. }
  498. public override bool HandleCmdMessage(IEntityManager entManager, ICommonSession? session, IFullInputCmdMessage message)
  499. {
  500. if (session?.AttachedEntity == null) return false;
  501. _controller.HandleRunChange(session.AttachedEntity.Value, message.SubTick, message.State == BoundKeyState.Down);
  502. return false;
  503. }
  504. }
  505. private sealed class ShuttleInputCmdHandler : InputCmdHandler
  506. {
  507. private readonly SharedMoverController _controller;
  508. private readonly ShuttleButtons _button;
  509. public ShuttleInputCmdHandler(SharedMoverController controller, ShuttleButtons button)
  510. {
  511. _controller = controller;
  512. _button = button;
  513. }
  514. public override bool HandleCmdMessage(IEntityManager entManager, ICommonSession? session, IFullInputCmdMessage message)
  515. {
  516. if (session?.AttachedEntity == null) return false;
  517. _controller.HandleShuttleInput(session.AttachedEntity.Value, _button, message.SubTick, message.State == BoundKeyState.Down);
  518. return false;
  519. }
  520. }
  521. }
  522. [Flags]
  523. [Serializable, NetSerializable]
  524. public enum MoveButtons : byte
  525. {
  526. None = 0,
  527. Up = 1,
  528. Down = 2,
  529. Left = 4,
  530. Right = 8,
  531. Walk = 16,
  532. AnyDirection = Up | Down | Left | Right,
  533. }
  534. [Flags]
  535. public enum ShuttleButtons : byte
  536. {
  537. None = 0,
  538. StrafeUp = 1 << 0,
  539. StrafeDown = 1 << 1,
  540. StrafeLeft = 1 << 2,
  541. StrafeRight = 1 << 3,
  542. RotateLeft = 1 << 4,
  543. RotateRight = 1 << 5,
  544. Brake = 1 << 6,
  545. }
  546. }