transport.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713
  1. /*!
  2. Base native class of all vehicles in game.
  3. */
  4. #ifdef FEATURE_NETWORK_RECONCILIATION
  5. class TransportOwnerState : PawnOwnerState
  6. {
  7. proto native void SetWorldTransform(vector transform[4]);
  8. proto native void GetWorldTransform(out vector transform[4]);
  9. proto native void SetLinearVelocity(vector value);
  10. proto native void GetLinearVelocity(out vector value);
  11. proto native void SetAngularVelocity(vector value);
  12. proto native void GetAngularVelocity(out vector value);
  13. proto native void SetBuoyancySubmerged(float value);
  14. proto native float GetBuoyancySubmerged();
  15. #ifdef DIAG_DEVELOPER
  16. override event void GetTransform(inout vector transform[4])
  17. {
  18. GetWorldTransform(transform);
  19. }
  20. #endif
  21. };
  22. class TransportMove : PawnMove
  23. {
  24. proto native void SetWorldTransform(vector transform[4]);
  25. proto native void GetWorldTransform(out vector transform[4]);
  26. proto native void SetLinearVelocity(vector value);
  27. proto native void GetLinearVelocity(out vector value);
  28. proto native void SetAngularVelocity(vector value);
  29. proto native void GetAngularVelocity(out vector value);
  30. #ifdef DIAG_DEVELOPER
  31. override event void GetTransform(inout vector transform[4])
  32. {
  33. GetWorldTransform(transform);
  34. }
  35. #endif
  36. };
  37. //! Uses NetworkMoveStrategy.NONE
  38. class Transport extends Pawn
  39. #else
  40. class Transport extends EntityAI
  41. #endif
  42. {
  43. //! Shared context across all vehicles for flipping
  44. private static ref VehicleFlippedContext m_FlippedContext;
  45. ref TIntArray m_SingleUseActions;
  46. ref TIntArray m_ContinuousActions;
  47. ref TIntArray m_InteractActions;
  48. protected bool m_EngineZoneReceivedHit;
  49. protected vector m_fuelPos;
  50. protected ref set<int> m_UnconsciousCrewMemberIndices;
  51. protected ref set<int> m_DeadCrewMemberIndices;
  52. void Transport()
  53. {
  54. RegisterNetSyncVariableBool("m_EngineZoneReceivedHit");
  55. if ( MemoryPointExists("refill") )
  56. m_fuelPos = GetMemoryPointPos("refill");
  57. else
  58. m_fuelPos = "0 0 0";
  59. m_UnconsciousCrewMemberIndices = new set<int>();
  60. m_DeadCrewMemberIndices = new set<int>();
  61. }
  62. override void EEHitBy(TotalDamageResult damageResult, int damageType, EntityAI source, int component, string dmgZone, string ammo, vector modelPos, float speedCoef)
  63. {
  64. super.EEHitBy(damageResult, damageType, source, component, dmgZone, ammo, modelPos, speedCoef);
  65. SetEngineZoneReceivedHit(dmgZone == "Engine");
  66. }
  67. override int GetMeleeTargetType()
  68. {
  69. return EMeleeTargetType.NONALIGNABLE;
  70. }
  71. //!
  72. protected override event typename GetOwnerStateType()
  73. {
  74. return TransportOwnerState;
  75. }
  76. //!
  77. protected override event typename GetMoveType()
  78. {
  79. return TransportMove;
  80. }
  81. //! Synchronizes car's state in case the simulation is not running.
  82. proto native void Synchronize();
  83. //! Returns crew capacity of this vehicle.
  84. proto native int CrewSize();
  85. //! Returns crew member index based on action component index.
  86. //! -1 is returned when no crew position corresponds to given component index.
  87. proto native int CrewPositionIndex( int componentIdx );
  88. //! Returns crew member index based on player's instance.
  89. //! -1 is returned when the player is not isnide.
  90. proto native int CrewMemberIndex( Human player );
  91. //! Returns crew member based on position index.
  92. //! Null can be returned if no Human is present on the given position.
  93. proto native Human CrewMember( int posIdx );
  94. //! Returns the driver
  95. //! Null can be returned if no Human is present.
  96. proto native Human CrewDriver();
  97. //! Reads entry point and direction into vehicle on given position in model space.
  98. proto void CrewEntry( int posIdx, out vector pos, out vector dir );
  99. //! Reads entry point and direction into vehicle on given position in world space.
  100. proto void CrewEntryWS( int posIdx, out vector pos, out vector dir );
  101. //! Returns crew transformation indside vehicle in model space
  102. proto void CrewTransform( int posIdx, out vector mat[4] );
  103. //! Returns crew transformation indside vehicle in world space
  104. proto void CrewTransformWS( int posIdx, out vector mat[4] );
  105. //! Performs transfer of player from world into vehicle on given position.
  106. proto native void CrewGetIn( Human player, int posIdx );
  107. //! Performs transfer of player from vehicle into world from given position.
  108. proto native Human CrewGetOut( int posIdx );
  109. //! Handles death of player in vehicle and awakes its physics if needed
  110. proto native void CrewDeath( int posIdx );
  111. /*!
  112. Is called when the crew member enters the driver seat
  113. \param[in] player The player that entered the driver seat
  114. */
  115. void OnDriverEnter(Human player) {}
  116. /*!
  117. Is called when the crew member leaves the driver seat
  118. \param[in] player The player that left the driver seat
  119. */
  120. void OnDriverExit(Human player) {}
  121. //! ---------------- deterministic random numbers ------------------------
  122. /*!
  123. \brief Random number in range of <0,0xffffffff> - !!! use this only during deterministic simulation (EOnSimulate/EOnPostSimulate)
  124. \return int value in range of <0,0xffffffff>
  125. */
  126. proto native int Random();
  127. /*!
  128. \brief Random number in range of <0,pRange-1> - !!! use this only during deterministic simulation (EOnSimulate/EOnPostSimulate)
  129. @param pRange upper bounds of random number
  130. \return int value in range of <0,pRange-1>
  131. */
  132. proto native float RandomRange(int pRange);
  133. /*!
  134. \brief Random number in range of <0,1> - !!! use this only during deterministic simulation (EOnSimulate/EOnPostSimulate)
  135. \return float value in range of <0,1>
  136. */
  137. proto native float Random01();
  138. /*!
  139. \brief Random number in range of <min,max> - !!! use this only during deterministic simulation (EOnSimulate/EOnPostSimulate)
  140. \return float value in range of <min,max>
  141. */
  142. float RandomFloat(float min, float max)
  143. {
  144. return Random01() * (max - min) + min;
  145. }
  146. /*!
  147. Is called every time when vehicle collides with other object.
  148. \param[in] zoneName configured vehicle's zone that was hit
  149. \param[in] localPos position where the vehicle was hit in vehicle's space
  150. \param[in] other object with which the vehicle is colliding
  151. \param[in] data contact properties
  152. */
  153. void OnContact(string zoneName, vector localPos, IEntity other, Contact data) {}
  154. /*!
  155. Called after every input simulation step.
  156. Note that the player character and other systems can always change the internal state.
  157. It is highly recommended to store state of custom inputs elsewhere and call Setters here.
  158. \param[in] dt delta time since last called in seconds
  159. */
  160. void OnInput(float dt) {}
  161. /*!
  162. Is called every game frame on client, fixed rate on server
  163. \param[in] dt delta time since last called in seconds
  164. */
  165. void OnUpdate(float dt) {}
  166. override bool IsTransport()
  167. {
  168. return true;
  169. }
  170. override bool IsIgnoredByConstruction()
  171. {
  172. return false;
  173. }
  174. override bool IsHealthVisible()
  175. {
  176. return true;
  177. }
  178. override bool ShowZonesHealth()
  179. {
  180. return true;
  181. }
  182. override int GetHideIconMask()
  183. {
  184. return EInventoryIconVisibility.HIDE_VICINITY;
  185. }
  186. float GetMomentum()
  187. {
  188. return GetVelocity(this).Length() * dBodyGetMass(this);
  189. }
  190. bool IsVitalSparkPlug()
  191. {
  192. return true;
  193. }
  194. string GetVehicleType()
  195. {
  196. return "VehicleTypeUndefined";
  197. }
  198. string GetActionCompNameFuel()
  199. {
  200. return "refill";
  201. }
  202. float GetActionDistanceFuel()
  203. {
  204. return 1.5;
  205. }
  206. vector GetRefillPointPosWS()
  207. {
  208. return ModelToWorld( m_fuelPos );
  209. }
  210. protected /*sealed*/ VehicleFlippedContext GetFlipContext()
  211. {
  212. if (!m_FlippedContext)
  213. {
  214. m_FlippedContext = new VehicleFlippedContext();
  215. }
  216. #ifdef DIAG_DEVELOPER
  217. m_FlippedContext.Reset(DiagMenu.GetBool(DiagMenuIDs.VEHICLE_DRAW_FLIP_CONTEXT));
  218. #endif
  219. return m_FlippedContext;
  220. }
  221. protected bool DetectFlippedUsingSurface(VehicleFlippedContext ctx, float angleTolerance)
  222. {
  223. vector corners[4];
  224. GetTightlyPackedCorners(ETransformationAxis.BOTTOM, corners);
  225. // compute the average position to find the lowest "center-most" position
  226. vector avgPosition = vector.Zero;
  227. for (int i = 0; i < 4; i++)
  228. {
  229. avgPosition = avgPosition + corners[i];
  230. }
  231. avgPosition = avgPosition * 0.25;
  232. // get depth of the water to determine if we should use the roadway surface normal or just up vector
  233. float depth = GetGame().GetWaterDepth(avgPosition);
  234. vector normal = vector.Up;
  235. vector dirUp = GetDirectionUp();
  236. bool testLand = depth < -1.0;
  237. // iterate over the corners to find the average normal
  238. if (testLand)
  239. {
  240. // trace roadway, incase the vehicle is on a rock, or bridge
  241. ctx.m_SurfaceParams.type = SurfaceDetectionType.Roadway;
  242. // ignore expensive water computation, we already know we are above land
  243. ctx.m_SurfaceParams.includeWater = false;
  244. // ignore this vehicle, it may have a roadway LOD
  245. ctx.m_SurfaceParams.ignore = this;
  246. // detect closest to the given point
  247. ctx.m_SurfaceParams.rsd = RoadSurfaceDetection.CLOSEST;
  248. for (i = 0; i < 4; i++)
  249. {
  250. ctx.m_SurfaceParams.position = corners[i];
  251. GetGame().GetSurface(ctx.m_SurfaceParams, ctx.m_SurfaceResult);
  252. corners[i][1] = ctx.m_SurfaceResult.height;
  253. }
  254. vector d0 = vector.Direction(corners[0], corners[1]);
  255. vector d1 = vector.Direction(corners[0], corners[2]);
  256. d0.Normalize();
  257. d1.Normalize();
  258. // cross product the two directions to get the normal vector of the land
  259. normal = d0 * d1;
  260. }
  261. bool isFlipped = vector.Dot(normal, dirUp) < Math.Cos(angleTolerance * Math.DEG2RAD);
  262. #ifdef DIAG_DEVELOPER
  263. if (ctx.IsDebug())
  264. {
  265. int color = 0xFF00FF00;
  266. if (isFlipped)
  267. color = 0xFFFF0000;
  268. ctx.AddShape(Shape.Create(ShapeType.LINE, color, ShapeFlags.NOZBUFFER, corners[0], corners[0] + normal));
  269. ctx.AddShape(Shape.Create(ShapeType.LINE, color, ShapeFlags.NOZBUFFER, corners[1], corners[1] + normal));
  270. ctx.AddShape(Shape.Create(ShapeType.LINE, color, ShapeFlags.NOZBUFFER, corners[2], corners[2] + normal));
  271. ctx.AddShape(Shape.Create(ShapeType.LINE, color, ShapeFlags.NOZBUFFER, corners[3], corners[3] + normal));
  272. }
  273. #endif
  274. return isFlipped;
  275. }
  276. //! Override based on vehicle implementation (Car, Boat, modded, etc.)
  277. protected bool DetectFlipped(VehicleFlippedContext ctx)
  278. {
  279. return false;
  280. }
  281. //! Don't override, may change to native for caching 'DetectFlipped' in the future based on active-ness (i.e. only updated when vehicle changes active state)
  282. /*sealed*/ bool IsFlipped()
  283. {
  284. VehicleFlippedContext ctx = GetFlipContext();
  285. ctx.m_bIsAction = false;
  286. ctx.m_ActionPlayer = null;
  287. return DetectFlipped(ctx);
  288. }
  289. //! Don't override, may change to native for caching 'DetectFlipped' in the future based on active-ness (i.e. only updated when vehicle changes active state)
  290. /*sealed*/ bool IsActionFlipped(Man player)
  291. {
  292. VehicleFlippedContext ctx = GetFlipContext();
  293. ctx.m_bIsAction = true;
  294. ctx.m_ActionPlayer = player;
  295. return DetectFlipped(ctx);
  296. }
  297. bool IsAnyCrewPresent()
  298. {
  299. for (int index = 0; index < CrewSize(); ++index)
  300. {
  301. if (CrewMember(index) != null)
  302. return true;
  303. }
  304. return false;
  305. }
  306. float GetTransportCameraDistance()
  307. {
  308. return 4.0;
  309. }
  310. void MarkCrewMemberUnconscious(int crewMemberIndex);
  311. void MarkCrewMemberDead(int crewMemberIndex);
  312. vector GetTransportCameraOffset()
  313. {
  314. return "0 1.3 0";
  315. }
  316. int GetAnimInstance()
  317. {
  318. #ifndef CFGMODS_DEFINE_TEST
  319. Error("GetAnimInstance() not implemented");
  320. return 0;
  321. #else
  322. return 2;
  323. #endif
  324. }
  325. int GetSeatAnimationType( int posIdx )
  326. {
  327. #ifndef CFGMODS_DEFINE_TEST
  328. Error("GetSeatAnimationType() not implemented");
  329. #endif
  330. return 0;
  331. }
  332. int Get3rdPersonCameraType()
  333. {
  334. #ifndef CFGMODS_DEFINE_TEST
  335. Error("Get3rdPersonCameraType() not implemented");
  336. return 0;
  337. #else
  338. return 31;
  339. #endif
  340. }
  341. bool CrewCanGetThrough( int posIdx )
  342. {
  343. #ifndef CFGMODS_DEFINE_TEST
  344. return false;
  345. #else
  346. return true;
  347. #endif
  348. }
  349. bool CanReachSeatFromSeat( int currentSeat, int nextSeat )
  350. {
  351. #ifndef CFGMODS_DEFINE_TEST
  352. return false;
  353. #else
  354. return true;
  355. #endif
  356. }
  357. bool CanReachSeatFromDoors( string pSeatSelection, vector pFromPos, float pDistance = 1.0 )
  358. {
  359. #ifndef CFGMODS_DEFINE_TEST
  360. return false;
  361. #else
  362. return true;
  363. #endif
  364. }
  365. bool CanReachDoorsFromSeat( string pDoorsSelection, int pCurrentSeat )
  366. {
  367. #ifndef CFGMODS_DEFINE_TEST
  368. return false;
  369. #else
  370. return true;
  371. #endif
  372. }
  373. int GetSeatIndexFromDoor( string pDoorSelection )
  374. {
  375. //Potientially could be fixed some other way, currently follows the unfortunate pattern that CanReachDoorsFromSeat has created
  376. switch (pDoorSelection)
  377. {
  378. case "DoorsDriver":
  379. return 0;
  380. break;
  381. case "DoorsCoDriver":
  382. return 1;
  383. break;
  384. case "DoorsCargo1":
  385. return 2;
  386. break;
  387. case "DoorsCargo2":
  388. return 3;
  389. break;
  390. }
  391. return -1;
  392. }
  393. bool IsIgnoredObject( Object o )
  394. {
  395. if (!o)
  396. return false;
  397. //! If the player can't interact (hint: collide) with the object, then it is ignored
  398. int layer = dBodyGetInteractionLayer(o);
  399. bool interacts = dGetInteractionLayer(this, PhxInteractionLayers.CHARACTER, layer);
  400. if (!interacts)
  401. {
  402. return true;
  403. }
  404. DayZPlayer player;
  405. if (Class.CastTo(player, o))
  406. {
  407. //! Ignore any player that is currently in this vehicle
  408. HumanCommandVehicle hcv = player.GetCommand_Vehicle();
  409. if (hcv && hcv.GetTransport() == this)
  410. {
  411. return true;
  412. }
  413. }
  414. EntityAI e = EntityAI.Cast(o);
  415. // CanBeSkinned means it is a dead entity which should not block the door
  416. return ( ( e && (e.IsZombie() || e.IsHologram()) ) || o.CanBeSkinned() || o.IsBush() || o.IsTree() );
  417. }
  418. void SetEngineZoneReceivedHit(bool pState)
  419. {
  420. m_EngineZoneReceivedHit = pState;
  421. SetSynchDirty();
  422. }
  423. bool HasEngineZoneReceivedHit()
  424. {
  425. return m_EngineZoneReceivedHit;
  426. }
  427. override void GetDebugActions(out TSelectableActionInfoArrayEx outputList)
  428. {
  429. super.GetDebugActions(outputList);
  430. outputList.Insert(new TSelectableActionInfoWithColor(SAT_DEBUG_ACTION, EActions.DELETE, "Delete", FadeColors.RED));
  431. if (Gizmo_IsSupported())
  432. outputList.Insert(new TSelectableActionInfoWithColor(SAT_DEBUG_ACTION, EActions.GIZMO_OBJECT, "Gizmo Object", FadeColors.LIGHT_GREY));
  433. outputList.Insert(new TSelectableActionInfoWithColor(SAT_DEBUG_ACTION, EActions.GIZMO_PHYSICS, "Gizmo Physics (SP Only)", FadeColors.LIGHT_GREY)); // intentionally allowed for testing physics desync
  434. outputList.Insert(new TSelectableActionInfoWithColor(SAT_DEBUG_ACTION, EActions.SEPARATOR, "___________________________", FadeColors.RED));
  435. }
  436. override bool OnAction(int action_id, Man player, ParamsReadContext ctx)
  437. {
  438. if (super.OnAction(action_id, player, ctx))
  439. return true;
  440. if (GetGame().IsClient() || !GetGame().IsMultiplayer())
  441. {
  442. switch (action_id)
  443. {
  444. case EActions.GIZMO_OBJECT:
  445. GetGame().GizmoSelectObject(this);
  446. return true;
  447. case EActions.GIZMO_PHYSICS:
  448. GetGame().GizmoSelectPhysics(GetPhysics());
  449. return true;
  450. }
  451. }
  452. if (GetGame().IsServer())
  453. {
  454. switch (action_id)
  455. {
  456. case EActions.DELETE:
  457. Delete();
  458. return true;
  459. }
  460. }
  461. return false;
  462. }
  463. bool IsAreaAtDoorFree( int currentSeat, float maxAllowedObjHeight, inout vector extents, out vector transform[4] )
  464. {
  465. GetTransform(transform);
  466. vector crewPos;
  467. vector crewDir;
  468. CrewEntry( currentSeat, crewPos, crewDir );
  469. vector entry[4];
  470. entry[2] = crewDir;
  471. entry[0] = vector.Up * crewDir;
  472. entry[1] = entry[2] * entry[0];
  473. entry[3] = crewPos;
  474. Math3D.MatrixMultiply4( transform, entry, transform );
  475. vector position = transform[3];
  476. vector orientation = Math3D.MatrixToAngles(transform);
  477. position[1] = position[1] + maxAllowedObjHeight + (extents[1] * 0.5);
  478. array<Object> excluded = new array<Object>;
  479. array<Object> collided = new array<Object>;
  480. excluded.Insert(this);
  481. GetGame().IsBoxColliding(position, orientation, extents, excluded, collided);
  482. orientation.RotationMatrixFromAngles(transform);
  483. transform[3] = position;
  484. foreach (Object o : collided)
  485. {
  486. EntityAI e = EntityAI.Cast(o);
  487. if (IsIgnoredObject(o))
  488. continue;
  489. vector minmax[2];
  490. if (o.GetCollisionBox(minmax))
  491. return false;
  492. }
  493. return true;
  494. }
  495. bool IsAreaAtDoorFree( int currentSeat, float maxAllowedObjHeight = 0.5, float horizontalExtents = 0.5, float playerHeight = 1.7 )
  496. {
  497. vector transform[4];
  498. vector extents;
  499. extents[0] = horizontalExtents;
  500. extents[1] = playerHeight;
  501. extents[2] = horizontalExtents;
  502. return IsAreaAtDoorFree( currentSeat, maxAllowedObjHeight, extents, transform );
  503. }
  504. Shape DebugFreeAreaAtDoor( int currentSeat, float maxAllowedObjHeight = 0.5, float horizontalExtents = 0.5, float playerHeight = 1.7 )
  505. {
  506. int color = ARGB(20, 0, 255, 0);
  507. vector transform[4];
  508. vector extents;
  509. extents[0] = horizontalExtents;
  510. extents[1] = playerHeight;
  511. extents[2] = horizontalExtents;
  512. if (!IsAreaAtDoorFree( currentSeat, maxAllowedObjHeight, extents, transform ))
  513. {
  514. color = ARGB(20, 255, 0, 0);
  515. }
  516. Shape shape = Debug.DrawBox(-extents * 0.5, extents * 0.5, color);
  517. shape.SetMatrix(transform);
  518. return shape;
  519. }
  520. };
  521. class VehicleContactData
  522. {
  523. vector m_LocalPos;
  524. IEntity m_Other;
  525. float m_Impulse;
  526. void SetData(vector localPos, IEntity other, float impulse)
  527. {
  528. m_LocalPos = localPos;
  529. m_Other = other;
  530. m_Impulse = impulse;
  531. }
  532. };
  533. class VehicleFlippedContext
  534. {
  535. bool m_bIsAction = false;
  536. Man m_ActionPlayer;
  537. ref SurfaceDetectionParameters m_SurfaceParams = new SurfaceDetectionParameters();
  538. ref SurfaceDetectionResult m_SurfaceResult = new SurfaceDetectionResult();
  539. #ifdef DIAG_DEVELOPER
  540. private ref array<Shape> m_DebugShapes;
  541. private bool m_bIsDebug;
  542. void VehicleFlippedContext()
  543. {
  544. m_DebugShapes = new array<Shape>();
  545. }
  546. void ~VehicleFlippedContext()
  547. {
  548. Reset();
  549. }
  550. void Reset(bool isDebug = false)
  551. {
  552. foreach (Shape shape : m_DebugShapes)
  553. {
  554. shape.Destroy();
  555. }
  556. m_DebugShapes.Clear();
  557. m_bIsDebug = isDebug;
  558. }
  559. void AddShape(Shape shape)
  560. {
  561. m_DebugShapes.Insert(shape);
  562. }
  563. bool IsDebug()
  564. {
  565. return m_bIsDebug;
  566. }
  567. #endif
  568. [Obsolete("no replacement")]
  569. protected void HandleByCrewMemberState(ECrewMemberState state);
  570. };