hologram.c 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609
  1. class Hologram
  2. {
  3. #ifdef SERVER
  4. protected const int SPAWN_FLAGS = ECE_CREATEPHYSICS;
  5. #else
  6. protected const int SPAWN_FLAGS = ECE_LOCAL;
  7. #endif
  8. #ifdef DIAG_DEVELOPER
  9. string m_CollisionDetails;
  10. #endif
  11. protected const string SUFFIX_MATERIAL_DEPLOYABLE = "_deployable.rvmat";
  12. protected const string SUFFIX_MATERIAL_UNDEPLOYABLE = "_undeployable.rvmat";
  13. protected const string SUFFIX_MATERIAL_POWERED = "_powered.rvmat";
  14. protected const vector PLACEMENT_RC_START_OFFSET = "0 1 0";
  15. protected const vector PLACEMENT_RC_END_OFFSET = "0 -2 0";
  16. protected ItemBase m_Parent;
  17. protected EntityAI m_Projection;
  18. protected PlayerBase m_Player;
  19. protected ProjectionTrigger m_ProjectionTrigger;
  20. protected string m_ProjectionTypename;
  21. protected bool m_IsColliding;
  22. protected bool m_IsCollidingGPlot;
  23. protected bool m_IsSlope;
  24. protected bool m_IsCollidingPlayer;
  25. protected bool m_IsFloating;
  26. protected bool m_UpdatePosition;
  27. protected bool m_IsHidden;
  28. protected vector m_DefaultOrientation;
  29. protected vector m_Rotation;
  30. protected vector m_y_p_r_previous;
  31. protected vector m_ContactDir;
  32. protected vector m_FromAdjusted;
  33. protected const string ANIMATION_PLACING = "Placing";
  34. protected const string ANIMATION_INVENTORY = "Inventory";
  35. protected const string SELECTION_PLACING = "placing";
  36. protected const string SELECTION_INVENTORY = "inventory";
  37. protected const float SMALL_PROJECTION_RADIUS = 1;
  38. protected const float SMALL_PROJECTION_GROUND = 2;
  39. protected const float DISTANCE_SMALL_PROJECTION = 1; //! Deprecated
  40. protected const float LARGE_PROJECTION_DISTANCE_LIMIT = 6;
  41. protected const float PROJECTION_TRANSITION_MIN = 1;
  42. protected const float PROJECTION_TRANSITION_MAX = 0.25;
  43. protected const float LOOKING_TO_SKY = 0.75;
  44. static const float DEFAULT_MAX_PLACEMENT_HEIGHT_DIFF = 1.5;
  45. protected float m_SlopeTolerance;
  46. protected bool m_AlignToTerrain;
  47. protected vector m_YawPitchRollLimit;
  48. protected int m_ContactComponent;
  49. protected ref set<string> m_SelectionsToRefresh = new set<string>;
  50. // Watchtower correction variables
  51. // These watchtower component names should be corrected when colliding with them as they are supposed to be "trigger boxes", not colliders
  52. static const protected ref array<string> m_WatchtowerIgnoreComponentNames = new array<string>;
  53. // These watchtower components are supposed to be trigger boxes, but should block placement on them (currently only the boxes above the stairs)
  54. static const protected ref array<string> m_WatchtowerBlockedComponentNames = new array<string>;
  55. void Hologram(PlayerBase player, vector pos, ItemBase item)
  56. {
  57. m_Player = player;
  58. m_Parent = item;
  59. m_Projection = null;
  60. m_ProjectionTrigger = null;
  61. m_UpdatePosition = true;
  62. m_ContactComponent = -1;
  63. m_Rotation = "0 0 0";
  64. m_FromAdjusted = "0 0 0";
  65. // If the static names are empty, generate the needed names
  66. // Refer to their definitions to see why these are required
  67. if (m_WatchtowerIgnoreComponentNames.Count() == 0)
  68. {
  69. string baseStringBegin = Watchtower.BASE_VIEW_NAME;
  70. string baseIgnoreStringEnd = Watchtower.BASE_WALL_NAME;
  71. int floors = Watchtower.MAX_WATCHTOWER_FLOORS;
  72. int walls = Watchtower.MAX_WATCHTOWER_WALLS;
  73. string compName;
  74. for (int i = 1; i < floors + 1; ++i)
  75. {
  76. compName = baseStringBegin + i.ToString();
  77. for (int j = 1; j < walls + 1; ++j)
  78. m_WatchtowerIgnoreComponentNames.Insert(compName + baseIgnoreStringEnd + j.ToString());
  79. if (i != 1)
  80. m_WatchtowerBlockedComponentNames.Insert(compName);
  81. else
  82. m_WatchtowerIgnoreComponentNames.Insert(compName);
  83. }
  84. }
  85. string configPathProjectionTypename = string.Format("CfgVehicles %1 projectionTypename", m_Parent.GetType());
  86. if (GetGame().ConfigIsExisting(configPathProjectionTypename))
  87. {
  88. m_ProjectionTypename = GetGame().ConfigGetTextOut(configPathProjectionTypename);
  89. }
  90. EntityAI projectionEntity;
  91. if (GetGame().IsMultiplayer() && GetGame().IsServer())
  92. {
  93. projectionEntity = EntityAI.Cast(GetGame().CreateObjectEx(ProjectionBasedOnParent(), pos, ECE_PLACE_ON_SURFACE));
  94. projectionEntity.SetAllowDamage(false);
  95. SetProjectionEntity(projectionEntity);
  96. SetAnimations();
  97. }
  98. else
  99. {
  100. projectionEntity = EntityAI.Cast(GetGame().CreateObjectEx(ProjectionBasedOnParent(), pos, ECE_TRACE|ECE_LOCAL));
  101. if (projectionEntity == null)
  102. {
  103. ErrorEx(string.Format("Cannot create hologram entity from config class %1", ProjectionBasedOnParent()), ErrorExSeverity.WARNING);
  104. return;
  105. }
  106. SetProjectionEntity(projectionEntity);
  107. SetAnimations();
  108. CreateTrigger();
  109. RefreshTrigger();
  110. }
  111. if (ItemBase.Cast(projectionEntity))
  112. {
  113. ItemBase.Cast(GetProjectionEntity()).SetIsHologram(true);
  114. }
  115. string configPathSlope = string.Format("CfgVehicles %1 slopeTolerance", GetProjectionEntity().GetType());
  116. if (GetGame().ConfigIsExisting(configPathSlope))
  117. {
  118. m_SlopeTolerance = GetGame().ConfigGetFloat(configPathSlope);
  119. }
  120. string configPathAlign = string.Format("CfgVehicles %1 alignHologramToTerain", GetProjectionEntity().GetType());
  121. if (GetGame().ConfigIsExisting(configPathAlign))
  122. {
  123. m_AlignToTerrain = GetGame().ConfigGetInt(configPathAlign);
  124. }
  125. string configPathOrientationLimit = string.Format("CfgVehicles %1 yawPitchRollLimit", GetProjectionEntity().GetType());
  126. if (GetGame().ConfigIsExisting(configPathOrientationLimit))
  127. {
  128. m_YawPitchRollLimit = GetGame().ConfigGetVector(configPathOrientationLimit);
  129. }
  130. }
  131. void ~Hologram()
  132. {
  133. if (GetGame())
  134. {
  135. if (m_Projection)
  136. {
  137. GetGame().ObjectDelete(m_Projection);
  138. }
  139. if (m_ProjectionTrigger)
  140. {
  141. GetGame().ObjectDelete(m_ProjectionTrigger);
  142. }
  143. }
  144. #ifdef DIAG_DEVELOPER
  145. DestroyDebugCollisionBox();
  146. #endif
  147. }
  148. void SetAnimations()
  149. {
  150. if ( m_Projection.HasAnimation( ANIMATION_PLACING ) )
  151. {
  152. m_Projection.SetAnimationPhase( ANIMATION_PLACING, 0 );
  153. SetSelectionToRefresh( SELECTION_PLACING );
  154. if ( m_Projection.HasAnimation( ANIMATION_INVENTORY ) )
  155. {
  156. m_Projection.SetAnimationPhase( ANIMATION_INVENTORY, 1 );
  157. }
  158. }
  159. else
  160. {
  161. UpdateSelections();
  162. SetSelectionToRefresh( SELECTION_INVENTORY );
  163. }
  164. }
  165. // Updates selections on hologram object so they reflect the state of the parent object's selections.
  166. void UpdateSelections()
  167. {
  168. string cfg_access = "CfgVehicles " + m_Projection.GetType() + " AnimationSources ";
  169. if ( GetGame().ConfigIsExisting(cfg_access) )
  170. {
  171. int cfg_access_count = g_Game.ConfigGetChildrenCount(cfg_access);
  172. for ( int i = 0; i < cfg_access_count; ++i )
  173. {
  174. string found_anim;
  175. GetGame().ConfigGetChildName(cfg_access, i, found_anim);
  176. float anim_phase = m_Parent.GetAnimationPhase(found_anim);
  177. m_Projection.SetAnimationPhase(found_anim, anim_phase);
  178. }
  179. }
  180. }
  181. string ProjectionBasedOnParent()
  182. {
  183. return GetProjectionName(m_Parent);
  184. }
  185. string GetProjectionName(ItemBase item)
  186. {
  187. //! configuration override
  188. if (m_ProjectionTypename != "")
  189. {
  190. return m_ProjectionTypename;
  191. }
  192. if (!item)
  193. {
  194. return "";
  195. }
  196. if (item.CanMakeGardenplot())
  197. {
  198. return "GardenPlotPlacing";
  199. }
  200. //Camping & Base building
  201. if (item.IsInherited( TentBase ) || item.IsBasebuildingKit())
  202. {
  203. return item.GetType() + "Placing";
  204. }
  205. return item.GetType();
  206. }
  207. //! DEPRECATED
  208. static bool DoesHaveProjection(ItemBase item)
  209. {
  210. return item && (item.IsDeployable() || item.CanMakeGardenplot() || item.IsInherited(DeployableContainer_Base));
  211. }
  212. // update loop for visuals and collisions of the hologram
  213. void UpdateHologram(float timeslice)
  214. {
  215. if (!m_Parent)
  216. {
  217. m_Player.TogglePlacingLocal();
  218. return;
  219. }
  220. if (IsRestrictedFromAdvancedPlacing())
  221. {
  222. m_Player.TogglePlacingLocal();
  223. return;
  224. }
  225. if (!GetUpdatePosition())
  226. return;
  227. #ifdef DIAG_DEVELOPER
  228. DebugConfigValues();
  229. DestroyDebugCollisionBox();
  230. #endif
  231. // update hologram position
  232. SetProjectionPosition(GetProjectionEntityPosition(m_Player));
  233. SetProjectionOrientation(AlignProjectionOnTerrain(timeslice));
  234. EvaluateCollision();
  235. RefreshTrigger();
  236. CheckPowerSource();
  237. RefreshVisual();
  238. m_Projection.OnHologramBeingPlaced(m_Player);
  239. }
  240. vector AlignProjectionOnTerrain( float timeslice )
  241. {
  242. vector y_p_r;
  243. if ( m_AlignToTerrain )
  244. {
  245. vector projection_orientation_angles = GetDefaultOrientation() + GetProjectionRotation();
  246. vector mat0[3];
  247. vector mat1[3];
  248. vector mat2[3];
  249. vector projection_position = m_Projection.GetPosition();
  250. vector normal;
  251. if ( m_ContactDir.Length() > 0 )
  252. {
  253. normal = m_ContactDir;
  254. }
  255. else
  256. {
  257. normal = GetGame().SurfaceGetNormal( projection_position[0], projection_position[2] );
  258. }
  259. vector angles = normal.VectorToAngles();
  260. angles[1] = angles[1] + 270;
  261. angles[0] = Math.Clamp( angles[0], 0, 360 );
  262. angles[1] = Math.Clamp( angles[1], 0, 360 );
  263. angles[2] = Math.Clamp( angles[2], 0, 360 );
  264. projection_orientation_angles[0] = projection_orientation_angles[0] + ( 360 - angles[0] );
  265. Math3D.YawPitchRollMatrix( projection_orientation_angles, mat0 );
  266. Math3D.YawPitchRollMatrix( angles, mat1 );
  267. Math3D.MatrixMultiply3( mat1, mat0, mat2 );
  268. y_p_r = Math3D.MatrixToAngles( mat2 );
  269. }
  270. else
  271. {
  272. y_p_r = GetDefaultOrientation() + GetProjectionRotation();
  273. if ( y_p_r[0] > 180 )
  274. {
  275. y_p_r[0] = y_p_r[0] - 360;
  276. }
  277. if ( y_p_r[0] < -180 )
  278. {
  279. y_p_r[0] = y_p_r[0] + 360;
  280. }
  281. }
  282. return SmoothProjectionMovement( y_p_r, timeslice );
  283. }
  284. vector SmoothProjectionMovement( vector y_p_r, float timeslice )
  285. {
  286. if ( m_y_p_r_previous )
  287. {
  288. if ( Math.AbsFloat( y_p_r[0] - m_y_p_r_previous[0] ) > 100 )
  289. {
  290. if ( y_p_r[0] > 0 )
  291. {
  292. m_y_p_r_previous[0] = m_y_p_r_previous[0] + 360;
  293. }
  294. if ( y_p_r[0] < 0 )
  295. {
  296. m_y_p_r_previous[0] = m_y_p_r_previous[0] - 360;
  297. }
  298. }
  299. y_p_r[0] = Math.Lerp( m_y_p_r_previous[0], y_p_r[0], 15 * timeslice );
  300. y_p_r[1] = Math.Lerp( m_y_p_r_previous[1], y_p_r[1], 15 * timeslice );
  301. y_p_r[2] = Math.Lerp( m_y_p_r_previous[2], y_p_r[2], 15 * timeslice );
  302. }
  303. m_y_p_r_previous = y_p_r;
  304. return y_p_r;
  305. }
  306. void CreateTrigger()
  307. {
  308. Class.CastTo(m_ProjectionTrigger, g_Game.CreateObjectEx("ProjectionTrigger", GetProjectionPosition(), SPAWN_FLAGS));
  309. m_ProjectionTrigger.SetOrientation(GetProjectionOrientation());
  310. m_ProjectionTrigger.SetParentObject(this);
  311. m_ProjectionTrigger.SetParentOwner(m_Player);
  312. RefreshVisual();
  313. }
  314. void RefreshTrigger()
  315. {
  316. vector min_max[2];
  317. GetProjectionCollisionBox( min_max );
  318. m_ProjectionTrigger.SetPosition(GetProjectionPosition());
  319. m_ProjectionTrigger.SetOrientation(GetProjectionOrientation());
  320. m_ProjectionTrigger.SetExtents(min_max[0], min_max[1]);
  321. }
  322. #ifdef DIAG_DEVELOPER
  323. void DebugText(string header, bool mustBeTrue = false, bool condition = true, string info = "")
  324. {
  325. if ( DiagMenu.GetBool(DiagMenuIDs.MISC_HOLOGRAM) )
  326. {
  327. int color = 0xFFFFFFFF;
  328. if (mustBeTrue && !condition || !mustBeTrue && condition)
  329. color = COLOR_RED;
  330. string text = header + condition + info;
  331. DbgUI.ColoredText(color, text);
  332. }
  333. }
  334. protected float m_PitchOverride;
  335. protected float m_RollOverride;
  336. void DebugConfigValues()
  337. {
  338. if ( DiagMenu.GetBool(DiagMenuIDs.MISC_HOLOGRAM) )
  339. {
  340. m_PitchOverride = m_YawPitchRollLimit[1];
  341. m_RollOverride = m_YawPitchRollLimit[2];
  342. DbgUI.InputFloat("slopeTolerance override", m_SlopeTolerance);
  343. DbgUI.SameLine();
  344. DbgUI.InputFloat("pitch limit override", m_PitchOverride);
  345. DbgUI.SameLine();
  346. DbgUI.InputFloat("roll limit override", m_RollOverride);
  347. m_YawPitchRollLimit[1] = m_PitchOverride;
  348. m_YawPitchRollLimit[2] = m_RollOverride;
  349. }
  350. }
  351. #endif
  352. void EvaluateCollision(ItemBase action_item = null)
  353. {
  354. #ifdef DIAG_DEVELOPER
  355. m_CollisionDetails = "";
  356. #endif
  357. if (!m_Player.CanPlaceItem(m_Projection))
  358. {
  359. #ifdef DIAG_DEVELOPER
  360. m_CollisionDetails += "[Player]";
  361. #endif
  362. SetIsColliding(true);
  363. }
  364. else if (IsFloating() || IsHidden() || IsCollidingBBox(action_item) || IsCollidingPlayer() || IsClippingRoof() || !IsBaseViable() || IsCollidingGPlot() || IsCollidingZeroPos() || IsCollidingAngle() || !IsPlacementPermitted() || !HeightPlacementCheck() || IsUnderwater() || IsInTerrain())
  365. {
  366. SetIsColliding(true);
  367. }
  368. else if ( m_Projection.IsInherited( TrapSpawnBase ))
  369. {
  370. #ifdef DIAG_DEVELOPER
  371. DebugText("Inherits from TrapSpawnBase, checking IsPlaceableAtposition", true);
  372. #endif
  373. TrapSpawnBase trapSpawnBase;
  374. Class.CastTo(trapSpawnBase, m_Projection);
  375. SetIsColliding(!trapSpawnBase.IsPlaceableAtPosition(m_Projection.GetPosition()));
  376. }
  377. else if (m_Projection.IsInherited(TrapBase))
  378. {
  379. #ifdef DIAG_DEVELOPER
  380. DebugText("Inherits from TrapBase, checking IsPlaceableAtposition", true);
  381. #endif
  382. TrapBase trapBase;
  383. Class.CastTo(trapBase, m_Projection);
  384. SetIsColliding(!trapBase.IsPlaceableAtPosition(m_Projection.GetPosition()));
  385. }
  386. else
  387. {
  388. SetIsColliding(false);
  389. }
  390. }
  391. bool IsClippingRoof()
  392. {
  393. if (CfgGameplayHandler.GetDisableIsClippingRoofCheck())
  394. return false;
  395. if (GetGame().IsServer() && GetGame().IsMultiplayer())
  396. return false;
  397. //Some locations allow you to look up and attempt placing the hologram on the bottom side of a floor - most notably the floors of a watchtower
  398. //This check also prevents some very unnatural placements
  399. bool b1 = m_Projection.GetPosition()[1] > GetGame().GetCurrentCameraPosition()[1];
  400. bool b2 = false;
  401. #ifdef DIAG_DEVELOPER
  402. vector from, to;
  403. #endif
  404. if (m_Projection.DoPlacingHeightCheck())
  405. {
  406. b2 = MiscGameplayFunctions.IsUnderRoofEx(m_Projection, GameConstants.ROOF_CHECK_RAYCAST_DIST, ObjIntersectFire);
  407. #ifdef DIAG_DEVELOPER
  408. MiscGameplayFunctions.IsUnderRoofFromToCalculation(m_Projection, from, to);
  409. DrawArrow(from, to, !b2);
  410. #endif
  411. }
  412. #ifdef DIAG_DEVELOPER
  413. DebugText("IsClippingRoof: ", false, b1, " | (projection height) " + m_Projection.GetPosition()[1] + " > (camera height) " + GetGame().GetCurrentCameraPosition()[1]);
  414. DebugText("IsClippingRoof: ", false, b2, " | (DoPlacingHeightCheck) " + m_Projection.DoPlacingHeightCheck() + " && (IsUnderRoof) " + MiscGameplayFunctions.IsUnderRoof(m_Projection) + " | from: " + from[1] + " | to: " + to[1]);
  415. #endif
  416. return b1 || b2;
  417. }
  418. bool IsCollidingAngle()
  419. {
  420. if (CfgGameplayHandler.GetDisableIsCollidingAngleCheck())
  421. return false;
  422. vector projection_orientation = m_Projection.GetOrientation();
  423. bool isTrue = Math.AbsFloat( projection_orientation[1] ) > m_YawPitchRollLimit[1] || Math.AbsFloat( projection_orientation[2] ) > m_YawPitchRollLimit[2];
  424. #ifdef DIAG_DEVELOPER
  425. DebugText("IsCollidingAngle: ", false, isTrue, " | (proj pitch) " + Math.AbsFloat( projection_orientation[1] ) + " > (pitch limit) " + m_YawPitchRollLimit[1] + " | (proj roll) " + Math.AbsFloat( projection_orientation[2] ) + " > (roll limit) " + m_YawPitchRollLimit[2]);
  426. #endif
  427. return isTrue;
  428. }
  429. #ifdef DIAG_DEVELOPER
  430. protected Shape m_CollisionBox;
  431. protected void DrawDebugCollisionBox( vector min_max[2], int color )
  432. {
  433. vector mat[4];
  434. m_Projection.GetTransform( mat );
  435. m_CollisionBox = Debug.DrawBox( min_max[0], min_max[1], color );
  436. m_CollisionBox.SetMatrix( mat );
  437. }
  438. protected void DestroyDebugCollisionBox()
  439. {
  440. if ( m_CollisionBox )
  441. {
  442. m_CollisionBox.Destroy();
  443. m_CollisionBox = NULL;
  444. }
  445. }
  446. #endif
  447. bool IsCollidingBBox(ItemBase action_item = null)
  448. {
  449. if (CfgGameplayHandler.GetDisableIsCollidingBBoxCheck())
  450. return false;
  451. vector center;
  452. vector relativeOffset; //we need to lift BBox, because it is calculated from the bottom of projection, and not from the middle
  453. vector absoluteOffset = "0 0.05 0"; //we need to lift BBox even more, because it colliddes with house floors due to various reasons (probably geometry or float imperfections)
  454. vector orientation = GetProjectionOrientation();
  455. vector edgeLength;
  456. vector minMax[2];
  457. array<Object> excludedObjects = new array<Object>();
  458. array<Object> collidedObjects = new array<Object>();
  459. GetProjectionCollisionBox(minMax);
  460. relativeOffset[1] = (minMax[1][1] - minMax[0][1]) * 0.5;
  461. center = m_Projection.GetPosition() + relativeOffset + absoluteOffset;
  462. edgeLength = GetCollisionBoxSize(minMax);
  463. excludedObjects.Insert(m_Projection);
  464. excludedObjects.Insert(m_Player);
  465. if (action_item)
  466. excludedObjects.Insert(action_item);
  467. //add is construction check
  468. // Base building objects behave in a way that causes this test to generate false positives
  469. bool isTrue = GetGame().IsBoxCollidingGeometry(center, orientation, edgeLength, ObjIntersectFire, ObjIntersectGeom, excludedObjects, collidedObjects);
  470. #ifdef DIAG_DEVELOPER
  471. if ( DiagMenu.GetBool(DiagMenuIDs.MISC_HOLOGRAM) )
  472. {
  473. string text = "";
  474. foreach (Object object: collidedObjects)
  475. text += " | " + Object.GetDebugName(object);
  476. DebugText("IsCollidingBBox: ", false, isTrue, text);
  477. int color = 0x01FFFFFF;
  478. if (isTrue)
  479. color = 0x33F22613;
  480. DrawDebugCollisionBox(minMax, color);
  481. }
  482. #endif
  483. return isTrue;
  484. }
  485. bool IsBaseViable()
  486. {
  487. //This function is not required to solve server-side fixes for clipping, saves calculations and potential false negatives
  488. if (GetGame().IsServer() && GetGame().IsMultiplayer())
  489. return true;
  490. if (CfgGameplayHandler.GetDisableIsBaseViableCheck())
  491. return true;
  492. vector from_left_close = m_Projection.CoordToParent(GetLeftCloseProjectionVector()) + PLACEMENT_RC_START_OFFSET;
  493. vector to_left_close_down = from_left_close + PLACEMENT_RC_END_OFFSET;
  494. vector from_right_close = m_Projection.CoordToParent(GetRightCloseProjectionVector()) + PLACEMENT_RC_START_OFFSET;
  495. vector to_right_close_down = from_right_close + PLACEMENT_RC_END_OFFSET;
  496. vector from_left_far = m_Projection.CoordToParent(GetLeftFarProjectionVector() + PLACEMENT_RC_START_OFFSET);
  497. vector to_left_far_down = from_left_far + PLACEMENT_RC_END_OFFSET;
  498. vector from_right_far = m_Projection.CoordToParent(GetRightFarProjectionVector()) + PLACEMENT_RC_START_OFFSET;
  499. vector to_right_far_down = from_right_far + PLACEMENT_RC_END_OFFSET;
  500. vector contact_pos_left_close;
  501. vector contact_pos_right_close;
  502. vector contact_pos_left_far;
  503. vector contact_pos_right_far;
  504. vector contact_dir_left_close;
  505. vector contact_dir_right_close;
  506. vector contact_dir_left_far;
  507. vector contact_dir_right_far;
  508. int contact_component_left_close;
  509. int contact_component_right_close;
  510. int contact_component_left_far;
  511. int contact_component_right_far;
  512. set<Object> results_left_close = new set<Object>;
  513. set<Object> results_right_close = new set<Object>;
  514. set<Object> results_left_far = new set<Object>;
  515. set<Object> results_right_far = new set<Object>;
  516. Object obj_left_close;
  517. Object obj_right_close;
  518. Object obj_left_far;
  519. Object obj_right_far;
  520. //Not sure what the intention here was before, but it boiled down to a very bloated version of what you see here right now
  521. DayZPhysics.RaycastRV(from_left_close, to_left_close_down, contact_pos_left_close, contact_dir_left_close, contact_component_left_close, results_left_close, null, m_Projection, false, false, ObjIntersectFire);
  522. if (results_left_close.Count() > 0)
  523. obj_left_close = results_left_close[results_left_close.Count() - 1];
  524. DayZPhysics.RaycastRV(from_right_close, to_right_close_down, contact_pos_right_close, contact_dir_right_close, contact_component_right_close, results_right_close, null, m_Projection, false, false, ObjIntersectFire);
  525. if (results_right_close.Count() > 0)
  526. obj_right_close = results_right_close[results_right_close.Count() - 1];
  527. DayZPhysics.RaycastRV(from_left_far, to_left_far_down, contact_pos_left_far, contact_dir_left_far, contact_component_left_far, results_left_far, null, m_Projection, false, false, ObjIntersectFire);
  528. if (results_left_far.Count() > 0)
  529. obj_left_far = results_left_far[results_left_far.Count() - 1];
  530. DayZPhysics.RaycastRV(from_right_far, to_right_far_down, contact_pos_right_far, contact_dir_right_far, contact_component_right_far, results_right_far, null, m_Projection, false, false, ObjIntersectFire);
  531. if (results_right_far.Count() > 0)
  532. obj_right_far = results_right_far[results_right_far.Count() - 1];
  533. return IsBaseIntact(obj_left_close, obj_right_close, obj_left_far, obj_right_far ) && IsBaseStatic( obj_left_close ) && IsBaseFlat( contact_pos_left_close, contact_pos_right_close, contact_pos_left_far, contact_pos_right_far);
  534. }
  535. bool IsCollidingGPlot()
  536. {
  537. if (CfgGameplayHandler.GetDisableIsCollidingGPlotCheck())
  538. return false;
  539. #ifdef DIAG_DEVELOPER
  540. DebugText("IsCollidingGPlot: ", false, m_IsCollidingGPlot);
  541. #endif
  542. return m_IsCollidingGPlot;
  543. }
  544. bool IsCollidingZeroPos()
  545. {
  546. vector origin = Vector(0, 0, 0);
  547. bool isTrue = GetProjectionPosition() == origin;
  548. #ifdef DIAG_DEVELOPER
  549. DebugText("IsCollidingZeroPos: ", false, isTrue);
  550. #endif
  551. return isTrue;
  552. }
  553. //! DEPRECATED
  554. bool IsBehindObstacle()
  555. {
  556. ErrorEx("Deprecated check - do not use", ErrorExSeverity.WARNING);
  557. return false;
  558. }
  559. //This function only takes one of the found objects since IsBaseIntact already verifies that all of them are either null or the same object
  560. bool IsBaseStatic( Object objectToCheck )
  561. {
  562. //check if the object below hologram is dynamic object. Dynamic objects like barrels can be taken to hands
  563. //and item which had been placed on top of them, would stay floating in the air
  564. #ifdef DIAG_DEVELOPER
  565. if (objectToCheck == null)
  566. DebugText("IsBaseStatic(must be true): ", true, true, " | objectToCheck is null (this is good)");
  567. else
  568. DebugText("IsBaseStatic(must be true): ", true, IsObjectStatic(objectToCheck));
  569. #endif
  570. return objectToCheck == null || IsObjectStatic(objectToCheck);
  571. }
  572. bool IsObjectStatic( Object obj )
  573. {
  574. return obj.IsBuilding() || obj.IsPlainObject() || (!m_Parent.IsInherited(KitBase) && obj.IsInherited(BaseBuildingBase) && (m_WatchtowerBlockedComponentNames.Find(obj.GetActionComponentName(m_ContactComponent, LOD.NAME_VIEW)) == -1));
  575. }
  576. bool IsBaseIntact( Object under_left_close, Object under_right_close, Object under_left_far, Object under_right_far )
  577. {
  578. bool isTrue = (under_left_close == under_right_close && under_right_close == under_left_far && under_left_far == under_right_far);
  579. #ifdef DIAG_DEVELOPER
  580. DebugText("IsBaseIntact(must be true and all equal): ", true, isTrue, " | ulc: " + Object.GetDebugName(under_left_close) + " | urc: " + Object.GetDebugName(under_right_close) + " | ulf: " + Object.GetDebugName(under_left_far) + " | urf: " + Object.GetDebugName(under_right_far));
  581. if (!isTrue)
  582. {
  583. array<bool> conditions = new array<bool>();
  584. conditions.Insert(under_left_close == null);
  585. conditions.Insert(under_right_close == null);
  586. conditions.Insert(under_left_far == null);
  587. conditions.Insert(under_right_far == null);
  588. int amountOfNull = 0;
  589. if (!under_left_close)
  590. ++amountOfNull;
  591. if (!under_right_close)
  592. ++amountOfNull;
  593. if (!under_left_far)
  594. ++amountOfNull;
  595. if (!under_right_far)
  596. ++amountOfNull;
  597. if ( amountOfNull < 3 )
  598. for ( int i = 0; i < conditions.Count(); ++i)
  599. conditions[i] = !conditions[i];
  600. DrawBaseSpheres(conditions);
  601. }
  602. #endif
  603. return isTrue;
  604. }
  605. #ifdef DIAG_DEVELOPER
  606. void DrawArrow(vector from, vector to, bool condition)
  607. {
  608. if ( DiagMenu.GetBool(DiagMenuIDs.MISC_HOLOGRAM) )
  609. {
  610. int color = 0xFFFFFFFF;
  611. if (!condition)
  612. color = COLOR_RED;
  613. Debug.DrawArrow(from, to, 1, color, ShapeFlags.ONCE);
  614. }
  615. }
  616. void DrawSphere(vector pos, bool condition)
  617. {
  618. if ( DiagMenu.GetBool(DiagMenuIDs.MISC_HOLOGRAM) )
  619. {
  620. int color = 0x01FFFFFF;
  621. if (!condition)
  622. color = 0x99F22613;
  623. Debug.DrawSphere(pos, 1, color, ShapeFlags.ONCE|ShapeFlags.TRANSP|ShapeFlags.NOOUTLINE);
  624. }
  625. }
  626. void DrawBaseSpheres(array<bool> conditions)
  627. {
  628. if ( DiagMenu.GetBool(DiagMenuIDs.MISC_HOLOGRAM) )
  629. {
  630. array<vector> positions = new array<vector>();
  631. positions.Insert(m_Projection.CoordToParent( GetLeftCloseProjectionVector() ));
  632. positions.Insert(m_Projection.CoordToParent( GetRightCloseProjectionVector() ));
  633. positions.Insert(m_Projection.CoordToParent( GetLeftFarProjectionVector() ));
  634. positions.Insert(m_Projection.CoordToParent( GetRightFarProjectionVector() ));
  635. for (int i = 0; i < positions.Count(); ++i)
  636. DrawSphere(positions[i], conditions[i]);
  637. }
  638. }
  639. void DrawDebugArrow(float start, float dist, int color = 0xFF1FFFFF)
  640. {
  641. if ( DiagMenu.GetBool(DiagMenuIDs.MISC_HOLOGRAM) )
  642. {
  643. vector from = m_Player.GetPosition() + start * MiscGameplayFunctions.GetHeadingVector(m_Player);
  644. vector to = m_Player.GetPosition() + dist * MiscGameplayFunctions.GetHeadingVector(m_Player);
  645. Debug.DrawArrow(from, to, 0.5, 0xFF1FFFFF, ShapeFlags.ONCE|ShapeFlags.TRANSP);
  646. }
  647. }
  648. #endif
  649. bool IsBaseFlat( vector contact_pos_left_close, vector contact_pos_right_close, vector contact_pos_left_far, vector contact_pos_right_far )
  650. {
  651. vector projection_pos = GetProjectionPosition();
  652. float slope_pos_left_close = Math.AbsFloat(projection_pos[1] - contact_pos_left_close[1]);
  653. float slope_pos_right_close = Math.AbsFloat(projection_pos[1] - contact_pos_right_close[1]);
  654. float slope_pos_left_far = Math.AbsFloat(projection_pos[1] - contact_pos_left_far[1]);
  655. float slope_pos_right_far = Math.AbsFloat(projection_pos[1] - contact_pos_right_far[1]);
  656. bool isTrue = slope_pos_left_close < m_SlopeTolerance && slope_pos_right_close < m_SlopeTolerance && slope_pos_left_far < m_SlopeTolerance && slope_pos_right_far < m_SlopeTolerance;
  657. #ifdef DIAG_DEVELOPER
  658. DebugText("IsBaseFlat(must be true): ", true, isTrue, " (slope < slopeTolerance) | slopeTolerance: " + m_SlopeTolerance + " | lc: " + slope_pos_left_close + " | rc: " + slope_pos_right_close + " | lf: " + slope_pos_left_far + " | rf: " + slope_pos_right_far);
  659. DrawArrow(projection_pos, contact_pos_left_close, slope_pos_left_close < m_SlopeTolerance);
  660. DrawArrow(projection_pos, contact_pos_right_close, slope_pos_right_close < m_SlopeTolerance);
  661. DrawArrow(projection_pos, contact_pos_left_far, slope_pos_left_far < m_SlopeTolerance);
  662. DrawArrow(projection_pos, contact_pos_right_far, slope_pos_right_far < m_SlopeTolerance);
  663. #endif
  664. return isTrue;
  665. }
  666. //! Checks if the item can be legally placed (usually checked by action as well)
  667. bool IsPlacementPermitted()
  668. {
  669. if (CfgGameplayHandler.GetDisableIsPlacementPermittedCheck())
  670. return true;
  671. bool isTrue = m_Parent && m_Parent.CanBePlaced(m_Player, GetProjectionPosition());
  672. #ifdef DIAG_DEVELOPER
  673. DebugText("IsPlacementPermitted(must be true): ", true, isTrue, " (Note: ItemBase::CanBePlaced() return value)");
  674. #endif
  675. return isTrue;
  676. }
  677. //! Checks height relative to player's position
  678. bool HeightPlacementCheck()
  679. {
  680. if (CfgGameplayHandler.GetDisableHeightPlacementCheck())
  681. return true;
  682. if ( GetProjectionEntity() ) //simple height check
  683. {
  684. vector playerpos = m_Player.GetPosition();
  685. vector projectionpos = GetProjectionPosition();
  686. float delta1 = playerpos[1] - projectionpos[1];
  687. if ( delta1 > DEFAULT_MAX_PLACEMENT_HEIGHT_DIFF || delta1 < -DEFAULT_MAX_PLACEMENT_HEIGHT_DIFF )
  688. {
  689. #ifdef DIAG_DEVELOPER
  690. DebugText("HeightPlacementCheck(must be true): ", true, false, " | Height difference between item and player is larger than " + DEFAULT_MAX_PLACEMENT_HEIGHT_DIFF);
  691. #endif
  692. return false;
  693. }
  694. }
  695. #ifdef DIAG_DEVELOPER
  696. DebugText("HeightPlacementCheck(must be true): ", true, true);
  697. #endif
  698. return true;
  699. }
  700. bool IsUnderwater()
  701. {
  702. if (CfgGameplayHandler.GetDisableIsUnderwaterCheck())
  703. return false;
  704. // Fast check middle of object
  705. string type;
  706. int liquid;
  707. g_Game.SurfaceUnderObjectCorrectedLiquid(m_Projection, type, liquid);
  708. if (liquid & (LIQUID_GROUP_WATER - LIQUID_SNOW))
  709. {
  710. #ifdef DIAG_DEVELOPER
  711. DebugText("IsUnderwater: ", false, true, " | Surface under object is water");
  712. #endif
  713. return true;
  714. }
  715. // Check every corner of the object
  716. vector left_close = m_Projection.CoordToParent( GetLeftCloseProjectionVector() );
  717. vector right_close = m_Projection.CoordToParent( GetRightCloseProjectionVector() );
  718. vector left_far = m_Projection.CoordToParent( GetLeftFarProjectionVector() );
  719. vector right_far = m_Projection.CoordToParent( GetRightFarProjectionVector() );
  720. float maxSea = g_Game.SurfaceGetSeaLevelMax() + 0.03;
  721. bool belowMaxSea = left_close[1] < maxSea || right_close[1] < maxSea || left_far[1] < maxSea || right_far[1] < maxSea;
  722. #ifdef DIAG_DEVELOPER
  723. // I'd rather duplicate this on internal than introduce (even) more raycasts than needed on retail..
  724. float lc = g_Game.GetWaterDepth(left_close);
  725. float rc = g_Game.GetWaterDepth(right_close);
  726. float lf = g_Game.GetWaterDepth(left_far);
  727. float rf = g_Game.GetWaterDepth(right_far);
  728. bool isTrue = (lc > 0 || rc > 0 || lf > 0 || rf > 0 || belowMaxSea);
  729. DebugText("IsUnderwater: ", false, isTrue, " belowMaxSea: " + belowMaxSea + " | (all must be less than zero) | lc: " + lc + " | rc: " + rc + " | lf: " + lf + " | rf: " + rf);
  730. //DebugText("Corner Height: ", false, true, " lc: " + left_close[1] + " | rc: " + right_close[1] + " | lf: " + left_far[1] + " | rf: " + right_far[1]);
  731. if (isTrue)
  732. {
  733. array<bool> conditions = {lc <= 0, rc <= 0, lf <= 0, rf <= 0};
  734. DrawBaseSpheres(conditions);
  735. }
  736. #endif
  737. return (belowMaxSea || g_Game.GetWaterDepth(left_close) > 0 || g_Game.GetWaterDepth(right_close) > 0 || g_Game.GetWaterDepth(left_far) > 0 || g_Game.GetWaterDepth(right_far) > 0);
  738. }
  739. bool IsInTerrain()
  740. {
  741. if (CfgGameplayHandler.GetDisableIsInTerrainCheck())
  742. return false;
  743. vector fromHeightOffset = "0 0.3 0";
  744. vector toHeightOffset = "0 1 0";
  745. vector from_left_close = m_Projection.CoordToParent( GetLeftCloseProjectionVector() ) + fromHeightOffset;
  746. vector to_left_close_down = from_left_close + toHeightOffset;
  747. vector from_right_close = m_Projection.CoordToParent( GetRightCloseProjectionVector() ) + fromHeightOffset;
  748. vector to_right_close_down = from_right_close + toHeightOffset;
  749. vector from_left_far = m_Projection.CoordToParent( GetLeftFarProjectionVector() ) + fromHeightOffset;
  750. vector to_left_far_down = from_left_far + toHeightOffset;
  751. vector from_right_far = m_Projection.CoordToParent( GetRightFarProjectionVector() ) + fromHeightOffset;
  752. vector to_right_far_down = from_right_far + toHeightOffset;
  753. vector contact_pos_left_close;
  754. vector contact_pos_right_close;
  755. vector contact_pos_left_far;
  756. vector contact_pos_right_far;
  757. vector contact_dir_left_close;
  758. vector contact_dir_right_close;
  759. vector contact_dir_left_far;
  760. vector contact_dir_right_far;
  761. int contact_component_left_close;
  762. int contact_component_right_close;
  763. int contact_component_left_far;
  764. int contact_component_right_far;
  765. #ifdef DIAG_DEVELOPER
  766. // I'd rather duplicate this on internal than introduce (even) more raycasts than needed on retail..
  767. set<Object> lcO = new set<Object>();
  768. set<Object> rcO = new set<Object>();
  769. set<Object> lfO = new set<Object>();
  770. set<Object> rfO = new set<Object>();
  771. bool lc = DayZPhysics.RaycastRV( from_left_close, to_left_close_down, contact_pos_left_close, contact_dir_left_close, contact_component_left_close, lcO, m_Projection, m_Projection, false, true, ObjIntersectFire );
  772. bool rc = DayZPhysics.RaycastRV( from_right_close, to_right_close_down, contact_pos_right_close, contact_dir_right_close, contact_component_right_close, rcO, m_Projection, m_Projection, false, true, ObjIntersectFire );
  773. bool lf = DayZPhysics.RaycastRV( from_left_far, to_left_far_down, contact_pos_left_far, contact_dir_left_far, contact_component_left_far, lfO, m_Projection, m_Projection, false, true, ObjIntersectFire );
  774. bool rf = DayZPhysics.RaycastRV( from_right_far, to_right_far_down, contact_pos_right_far, contact_dir_right_far, contact_component_right_far, rfO, m_Projection, m_Projection, false, true, ObjIntersectFire );
  775. bool isTrue = ( lc || rc || lf || rf );
  776. string text = "";
  777. if (isTrue)
  778. {
  779. if (lc)
  780. text += " | lc";
  781. if (rc)
  782. text += " | rc";
  783. if (lf)
  784. text += " | lf";
  785. if (rf)
  786. text += " | rf";
  787. if (lcO.Count() > 0)
  788. text += " | lcO: " + lcO[0];
  789. if (rcO.Count() > 0)
  790. text += " | rcO: " + rcO[0];
  791. if (lfO.Count() > 0)
  792. text += " | lfO: " + lfO[0];
  793. if (rfO.Count() > 0)
  794. text += " | rfO: " + rfO[0];
  795. array<bool> conditions = {!lc, !rc, !lf, !rf};
  796. DrawBaseSpheres(conditions);
  797. }
  798. DebugText("IsInTerrain: ", false, isTrue, text);
  799. #endif
  800. if (DayZPhysics.RaycastRV( from_left_close, to_left_close_down, contact_pos_left_close, contact_dir_left_close, contact_component_left_close, NULL, m_Projection, m_Projection, false, true, ObjIntersectFire ))
  801. return true;
  802. if (DayZPhysics.RaycastRV( from_right_close, to_right_close_down, contact_pos_right_close, contact_dir_right_close, contact_component_right_close, NULL,m_Projection, m_Projection, false, true, ObjIntersectFire ))
  803. return true;
  804. if (DayZPhysics.RaycastRV( from_left_far, to_left_far_down, contact_pos_left_far, contact_dir_left_far, contact_component_left_far, NULL, m_Projection, m_Projection, false, true, ObjIntersectFire ))
  805. return true;
  806. if (DayZPhysics.RaycastRV( from_right_far, to_right_far_down, contact_pos_right_far, contact_dir_right_far, contact_component_right_far, NULL, m_Projection, m_Projection, false, true, ObjIntersectFire ))
  807. return true;
  808. return false;
  809. }
  810. void CheckPowerSource()
  811. {
  812. //in range of its power source.
  813. if (m_Player && m_Parent && m_Parent.HasEnergyManager() && m_Parent.GetCompEM().IsPlugged())
  814. {
  815. // Unplug the device when the player is too far from the power source.
  816. m_Parent.GetCompEM().UpdatePlugState();
  817. // Delete local hologram when plug is rippled out and advanced placement is active
  818. if (GetGame().IsMultiplayer() && GetGame().IsClient())
  819. {
  820. if (!m_Parent.GetCompEM().IsPlugged())
  821. m_Player.TogglePlacingLocal();
  822. }
  823. }
  824. }
  825. EntityAI PlaceEntity( EntityAI entity_for_placing )
  826. {
  827. //string-based comparison
  828. if (entity_for_placing.IsInherited(TentBase) || entity_for_placing.IsBasebuildingKit() )
  829. {
  830. return entity_for_placing;
  831. }
  832. if (m_Projection.IsInherited(GardenPlotPlacing))
  833. {
  834. Class.CastTo(entity_for_placing, GetGame().CreateObjectEx( "GardenPlot", m_Projection.GetPosition(), ECE_OBJECT_SWAP ));
  835. return entity_for_placing;
  836. }
  837. //inheritance comparison
  838. if( !GetProjectionEntity().IsKindOf( m_Parent.GetType() ))
  839. {
  840. Class.CastTo(entity_for_placing, GetGame().CreateObjectEx( m_Projection.GetType(), m_Projection.GetPosition(), ECE_OBJECT_SWAP ));
  841. }
  842. return entity_for_placing;
  843. }
  844. protected void GetProjectionCollisionBox( out vector min_max[2] )
  845. {
  846. if (!m_Projection.GetCollisionBox( min_max ) && m_Projection.MemoryPointExists("box_placing_min"))
  847. {
  848. min_max[0] = m_Projection.GetMemoryPointPos( "box_placing_min" );
  849. min_max[1] = m_Projection.GetMemoryPointPos( "box_placing_max" );
  850. //Debug.DrawSphere(m_Projection.ModelToWorld(min_max[0]) , 0.8,Colors.RED, ShapeFlags.ONCE);
  851. //Debug.DrawSphere(m_Projection.ModelToWorld(min_max[1]), 0.8,Colors.RED, ShapeFlags.ONCE);
  852. }
  853. }
  854. protected vector GetCollisionBoxSize( vector min_max[2] )
  855. {
  856. vector box_size = Vector(1,1,1);
  857. box_size[0] = min_max[1][0] - min_max[0][0];
  858. box_size[2] = min_max[1][2] - min_max[0][2];
  859. box_size[1] = min_max[1][1] - min_max[0][1];
  860. return box_size;
  861. }
  862. vector GetLeftCloseProjectionVector()
  863. {
  864. vector min_max[2];
  865. GetProjectionCollisionBox( min_max );
  866. return min_max[0];
  867. }
  868. vector GetRightCloseProjectionVector()
  869. {
  870. vector min_max[2];
  871. GetProjectionCollisionBox( min_max );
  872. min_max[1][1] = min_max[0][1];
  873. min_max[1][2] = min_max[0][2];
  874. return min_max[1];
  875. }
  876. vector GetLeftFarProjectionVector()
  877. {
  878. vector min_max[2];
  879. GetProjectionCollisionBox( min_max );
  880. min_max[0][2] = min_max[1][2];
  881. return min_max[0];
  882. }
  883. vector GetRightFarProjectionVector()
  884. {
  885. vector min_max[2];
  886. GetProjectionCollisionBox( min_max );
  887. min_max[1][1] = min_max[0][1];
  888. return min_max[1];
  889. }
  890. // Replaced by IsUnderwater, currently unused
  891. bool IsSurfaceWater( vector position )
  892. {
  893. CGame game = GetGame();
  894. return game.SurfaceIsSea( position[0], position[2] ) || game.SurfaceIsPond( position[0], position[2] );
  895. }
  896. bool IsSurfaceSea( vector position )
  897. {
  898. CGame game = GetGame();
  899. return game.SurfaceIsSea( position[0], position[2] );
  900. }
  901. protected vector GetProjectionEntityPosition(PlayerBase player)
  902. {
  903. float minProjectionDistance;
  904. float maxProjectionDistance;
  905. m_ContactDir = vector.Zero;
  906. vector minMax[2];
  907. float projectionRadius = GetProjectionRadius();
  908. float cameraToPlayerDistance = vector.Distance(GetGame().GetCurrentCameraPosition(), player.GetPosition());
  909. if (projectionRadius < SMALL_PROJECTION_RADIUS) // objects with radius smaller than 1m
  910. {
  911. minProjectionDistance = SMALL_PROJECTION_RADIUS;
  912. maxProjectionDistance = SMALL_PROJECTION_RADIUS * 2;
  913. }
  914. else
  915. {
  916. minProjectionDistance = projectionRadius;
  917. maxProjectionDistance = projectionRadius * 2;
  918. maxProjectionDistance = Math.Clamp(maxProjectionDistance, SMALL_PROJECTION_RADIUS, LARGE_PROJECTION_DISTANCE_LIMIT);
  919. }
  920. vector from = GetGame().GetCurrentCameraPosition();
  921. vector to = from + (GetGame().GetCurrentCameraDirection() * (maxProjectionDistance + cameraToPlayerDistance));
  922. vector contactPosition;
  923. set<Object> hitObjects = new set<Object>();
  924. DayZPhysics.RaycastRV(from, to, contactPosition, m_ContactDir, m_ContactComponent, hitObjects, player, m_Projection, false, false, ObjIntersectFire);
  925. bool contactHitProcessed = false;
  926. //! will not push hologram up when there is direct hit of an item
  927. if (!CfgGameplayHandler.GetDisableIsCollidingBBoxCheck())
  928. {
  929. if (hitObjects.Count() > 0)
  930. {
  931. if (hitObjects[0].IsInherited(Watchtower))
  932. {
  933. contactHitProcessed = true;
  934. contactPosition = CorrectForWatchtower(m_ContactComponent, contactPosition, player, hitObjects[0]);
  935. }
  936. if (!contactHitProcessed && hitObjects[0].IsInherited(InventoryItem))
  937. contactPosition = hitObjects[0].GetPosition();
  938. }
  939. }
  940. static const float raycastOriginOffsetOnFail = 0.25;
  941. static const float minDistFromStart = 0.01;
  942. // Camera isn't correctly positioned in some cases, leading to raycasts hitting the object directly behind the camera
  943. if ((hitObjects.Count() > 0) && (vector.DistanceSq(from, contactPosition) < minDistFromStart))
  944. {
  945. from = contactPosition + GetGame().GetCurrentCameraDirection() * raycastOriginOffsetOnFail;
  946. DayZPhysics.RaycastRV(from, to, contactPosition, m_ContactDir, m_ContactComponent, hitObjects, player, m_Projection, false, false, ObjIntersectFire);
  947. }
  948. bool isFloating = SetHologramPosition(player.GetPosition(), minProjectionDistance, maxProjectionDistance, contactPosition);
  949. SetIsFloating(isFloating);
  950. #ifdef DIAG_DEVELOPER
  951. DrawDebugArrow(minProjectionDistance, maxProjectionDistance);
  952. if (DiagMenu.GetBool(DiagMenuIDs.MISC_HOLOGRAM))
  953. {
  954. Debug.DrawSphere(GetProjectionPosition(), 0.1, 0x99FF0000, ShapeFlags.ONCE|ShapeFlags.TRANSP|ShapeFlags.NOOUTLINE);
  955. }
  956. #endif
  957. m_FromAdjusted = from;
  958. return contactPosition;
  959. }
  960. /**
  961. \brief Sets hologram position based on player and projection distance
  962. @param startPosition start position
  963. @param minProjectionDistance lower distance limit
  964. @param maxProjectionDistance higher distance limit
  965. @param inout contactPosition is position of the hologram contact with ground/object
  966. @return true if hologram is floating (is on the near or far edge)
  967. */
  968. protected bool SetHologramPosition(vector startPosition, float minProjectionDistance, float maxProjectionDistance, inout vector contactPosition)
  969. {
  970. float playerToProjectionDistance = vector.Distance(startPosition, contactPosition);
  971. vector playerToProjection;
  972. #ifdef DIAG_DEVELOPER
  973. DebugText("SetHologramPosition::startPosition: ", false, m_IsHidden, string.Format(" | %1", startPosition));
  974. DebugText("SetHologramPosition::contactPosition [in]: ", false, m_IsHidden, string.Format(" | %1", contactPosition));
  975. DebugText("SetHologramPosition::minProjectionDistance: ", false, m_IsHidden, string.Format(" | %1", minProjectionDistance));
  976. DebugText("SetHologramPosition::maxProjectionDistance: ", false, m_IsHidden, string.Format(" | %1", maxProjectionDistance));
  977. DebugText("SetHologramPosition::playerToProjectionDistance: ", false, m_IsHidden, string.Format(" | %1", playerToProjectionDistance));
  978. #endif
  979. //hologram is at min distance from player
  980. if (playerToProjectionDistance <= minProjectionDistance)
  981. {
  982. playerToProjection = contactPosition - startPosition;
  983. playerToProjection.Normalize();
  984. //prevents the hologram to go underground/floor while hologram exceeds minProjectionDistance
  985. playerToProjection[1] = playerToProjection[1] + PROJECTION_TRANSITION_MIN;
  986. contactPosition = startPosition + (playerToProjection * minProjectionDistance);
  987. #ifdef DIAG_DEVELOPER
  988. DebugText("SetHologramPosition::contactPosition[out] (< minProjectDistance): ", false, m_IsHidden, string.Format(" | %1", contactPosition));
  989. #endif
  990. return true;
  991. }
  992. //hologram is at max distance from player
  993. else if (playerToProjectionDistance >= maxProjectionDistance)
  994. {
  995. playerToProjection = contactPosition - startPosition;
  996. playerToProjection.Normalize();
  997. //prevents the hologram to go underground/floor while hologram exceeds maxProjectionDistance
  998. playerToProjection[1] = playerToProjection[1] + PROJECTION_TRANSITION_MAX;
  999. contactPosition = startPosition + (playerToProjection * maxProjectionDistance);
  1000. #ifdef DIAG_DEVELOPER
  1001. DebugText("SetHologramPosition::contactPosition[out] (< maxProjectionDistance): ", false, m_IsHidden, string.Format(" | %1", contactPosition));
  1002. #endif
  1003. return true;
  1004. }
  1005. return false;
  1006. }
  1007. bool IsFenceOrWatchtowerKit()
  1008. {
  1009. return m_Parent.IsBasebuildingKit() || m_Parent.IsInherited(TentBase);
  1010. }
  1011. vector CorrectForWatchtower(int contactComponent, vector contactPos, PlayerBase player, Object hitObject)
  1012. {
  1013. // Raycast has hit one of the trigger boxes that show construction prompts, so projection would be floating in the air without this correction
  1014. if (m_WatchtowerIgnoreComponentNames.Find(hitObject.GetActionComponentName(contactComponent, LOD.NAME_VIEW)) != -1 )
  1015. contactPos[1] = hitObject.GetActionComponentPosition(contactComponent, LOD.NAME_VIEW)[1];
  1016. return contactPos;
  1017. }
  1018. //This function is currently unused
  1019. bool IsProjectionTrap()
  1020. {
  1021. return m_Projection.IsInherited( TrapBase ) || m_Projection.IsInherited( TrapSpawnBase );
  1022. }
  1023. float GetProjectionDiameter()
  1024. {
  1025. float diameter;
  1026. float radius;
  1027. vector diagonal;
  1028. vector min_max[2];
  1029. GetProjectionCollisionBox( min_max );
  1030. diagonal = GetCollisionBoxSize( min_max );
  1031. diameter = diagonal.Length();
  1032. return diameter;
  1033. }
  1034. float GetProjectionRadius()
  1035. {
  1036. float diameter;
  1037. float radius;
  1038. vector diagonal;
  1039. vector min_max[2];
  1040. GetProjectionCollisionBox( min_max );
  1041. diagonal = GetCollisionBoxSize( min_max );
  1042. diameter = diagonal.Length();
  1043. radius = diameter / 2;
  1044. return radius;
  1045. }
  1046. void SetUpdatePosition( bool state )
  1047. {
  1048. m_UpdatePosition = state;
  1049. }
  1050. bool GetUpdatePosition()
  1051. {
  1052. return m_UpdatePosition;
  1053. }
  1054. EntityAI GetParentEntity()
  1055. {
  1056. return m_Parent;
  1057. }
  1058. void SetProjectionEntity( EntityAI projection )
  1059. {
  1060. m_Projection = projection;
  1061. }
  1062. EntityAI GetProjectionEntity()
  1063. {
  1064. return m_Projection;
  1065. }
  1066. void SetIsFloating( bool is_floating )
  1067. {
  1068. m_IsFloating = is_floating;
  1069. }
  1070. void SetIsColliding( bool is_colliding )
  1071. {
  1072. #ifdef DIAG_DEVELOPER
  1073. DebugText("Is colliding: ", false, is_colliding, m_CollisionDetails);
  1074. #endif
  1075. m_IsColliding = is_colliding;
  1076. }
  1077. void SetIsHidden( bool is_hidden )
  1078. {
  1079. m_IsHidden = is_hidden;
  1080. }
  1081. void SetIsCollidingPlayer( bool is_colliding )
  1082. {
  1083. m_IsCollidingPlayer = is_colliding;
  1084. }
  1085. void SetIsCollidingGPlot( bool is_colliding_gplot )
  1086. {
  1087. m_IsCollidingGPlot = is_colliding_gplot;
  1088. }
  1089. bool IsFloating()
  1090. {
  1091. #ifdef DIAG_DEVELOPER
  1092. DebugText("IsFloating: ", false, m_IsFloating);
  1093. #endif
  1094. return m_IsFloating;
  1095. }
  1096. bool IsColliding()
  1097. {
  1098. return m_IsColliding;
  1099. }
  1100. bool IsHidden()
  1101. {
  1102. #ifdef DIAG_DEVELOPER
  1103. DebugText("IsHidden: ", false, m_IsHidden);
  1104. #endif
  1105. return m_IsHidden;
  1106. }
  1107. bool IsCollidingPlayer()
  1108. {
  1109. if (CfgGameplayHandler.GetDisableIsCollidingPlayerCheck())
  1110. return false;
  1111. #ifdef DIAG_DEVELOPER
  1112. DebugText("IsCollidingPlayer: ", false, m_IsCollidingPlayer);
  1113. #endif
  1114. return m_IsCollidingPlayer;
  1115. }
  1116. void SetProjectionPosition(vector position)
  1117. {
  1118. m_Projection.SetPosition( position );
  1119. if (IsFloating())
  1120. {
  1121. m_Projection.SetPosition(SetOnGround(position));
  1122. }
  1123. }
  1124. void SetProjectionOrientation(vector orientation)
  1125. {
  1126. m_Projection.SetOrientation(orientation);
  1127. }
  1128. vector GetProjectionRotation()
  1129. {
  1130. return m_Rotation;
  1131. }
  1132. void AddProjectionRotation( float addition )
  1133. {
  1134. m_Rotation[0] = m_Rotation[0] + addition;
  1135. }
  1136. void SubtractProjectionRotation( float subtraction )
  1137. {
  1138. m_Rotation[0] = m_Rotation[0] - subtraction;
  1139. }
  1140. vector SetOnGround( vector position )
  1141. {
  1142. vector from = position;
  1143. vector ground;
  1144. vector player_to_projection_vector;
  1145. float projection_diameter = GetProjectionDiameter();
  1146. ground = Vector(0, -Math.Max(projection_diameter, SMALL_PROJECTION_GROUND), 0);
  1147. vector to = from + ground;
  1148. vector contact_pos = to;
  1149. RaycastRVParams rayInput = new RaycastRVParams(from, to, m_Projection);
  1150. rayInput.flags = CollisionFlags.ALLOBJECTS;
  1151. array<ref RaycastRVResult> results = new array<ref RaycastRVResult>;
  1152. if (DayZPhysics.RaycastRVProxy(rayInput, results))
  1153. {
  1154. RaycastRVResult res;
  1155. for (int i = 0; i < results.Count(); i++)
  1156. {
  1157. res = results.Get(i);
  1158. if (res.entry || (!res.obj && !res.parent))
  1159. {
  1160. contact_pos = res.pos;
  1161. break;
  1162. }
  1163. }
  1164. }
  1165. //LOS check
  1166. if (contact_pos != "0 0 0")
  1167. {
  1168. vector check_pos;
  1169. vector check_dir;
  1170. int check_component = -1;
  1171. set<Object> hit_object = new set<Object>;
  1172. to = contact_pos;
  1173. to[1] = to[1] + 0.1;
  1174. from = m_FromAdjusted;
  1175. if (DayZPhysics.RaycastRV(from, to, check_pos, check_dir, check_component, hit_object, null, m_Player, false, false, ObjIntersectFire))
  1176. {
  1177. if ((hit_object.Count() > 0)&& (!hit_object[0].IsInherited(Watchtower) || (hit_object[0].IsInherited(Watchtower) && (m_WatchtowerIgnoreComponentNames.Find(hit_object[0].GetActionComponentName(check_component, LOD.NAME_VIEW)) == -1))))
  1178. {
  1179. contact_pos = "0 0 0";
  1180. }
  1181. }
  1182. }
  1183. HideWhenClose(contact_pos);
  1184. return contact_pos;
  1185. }
  1186. vector HideWhenClose( vector pos )
  1187. {
  1188. //if the hologram is too close to player when he looks to the sky, send the projection to away
  1189. vector cam_dir = GetGame().GetCurrentCameraDirection();
  1190. if( cam_dir[1] > LOOKING_TO_SKY )
  1191. {
  1192. pos = "0 0 0";
  1193. }
  1194. return pos;
  1195. }
  1196. vector GetProjectionPosition()
  1197. {
  1198. if (m_Projection)
  1199. return m_Projection.GetPosition();
  1200. return vector.Zero;
  1201. }
  1202. vector GetProjectionOrientation()
  1203. {
  1204. if (m_Projection)
  1205. return m_Projection.GetOrientation();
  1206. return vector.Zero;
  1207. }
  1208. vector GetDefaultOrientation()
  1209. {
  1210. m_DefaultOrientation = GetGame().GetCurrentCameraDirection().VectorToAngles();
  1211. m_DefaultOrientation[1] = 0;
  1212. if (!GetParentEntity().PlacementCanBeRotated())
  1213. {
  1214. m_DefaultOrientation = vector.Zero;
  1215. }
  1216. return m_DefaultOrientation;
  1217. }
  1218. int GetHiddenSelection( string selection )
  1219. {
  1220. int idx = m_Projection.GetHiddenSelectionIndex(selection);
  1221. if ( idx != -1 )
  1222. return idx;
  1223. else
  1224. return 0;
  1225. }
  1226. // the function accepts string
  1227. void SetSelectionToRefresh( string selection )
  1228. {
  1229. m_SelectionsToRefresh.Insert( selection );
  1230. }
  1231. //overloaded function to accept array of strings
  1232. void SetSelectionToRefresh(array<string> selection)
  1233. {
  1234. foreach (string s : selection)
  1235. m_SelectionsToRefresh.Insert(s);
  1236. }
  1237. void RefreshVisual()
  1238. {
  1239. if (m_Projection)
  1240. {
  1241. static const string textureName = "#(argb,8,8,3)color(0.5,0.5,0.5,0.75,ca)";
  1242. int hidden_selection = 0;
  1243. string selection_to_refresh;
  1244. string config_material = string.Format("CfgVehicles %1 hologramMaterial", m_Projection.GetType());
  1245. string hologram_material = GetGame().ConfigGetTextOut(config_material);
  1246. string config_model = string.Format("CfgVehicles %1 hologramMaterialPath", m_Projection.GetType());
  1247. string hologram_material_path = string.Format("%1\\%2%3", GetGame().ConfigGetTextOut(config_model), hologram_material, CorrectMaterialPathName());
  1248. for (int i = 0; i < m_SelectionsToRefresh.Count(); ++i)
  1249. {
  1250. selection_to_refresh = m_SelectionsToRefresh.Get(i);
  1251. hidden_selection = GetHiddenSelection(selection_to_refresh);
  1252. m_Projection.SetObjectTexture(hidden_selection, textureName);
  1253. m_Projection.SetObjectMaterial(hidden_selection, hologram_material_path);
  1254. }
  1255. }
  1256. }
  1257. // Returns correct string to append to material path name
  1258. string CorrectMaterialPathName()
  1259. {
  1260. if (IsColliding() || IsFloating())
  1261. {
  1262. return SUFFIX_MATERIAL_UNDEPLOYABLE;
  1263. }
  1264. else if (m_Parent.HasEnergyManager())
  1265. {
  1266. ComponentEnergyManager comp_em = m_Parent.GetCompEM();
  1267. string SEL_CORD_PLUGGED = m_Parent.GetCompEM().SEL_CORD_PLUGGED;
  1268. string SEL_CORD_FOLDED = m_Parent.GetCompEM().SEL_CORD_FOLDED;
  1269. if (comp_em.IsPlugged() && comp_em.IsEnergySourceAtReach(GetProjectionPosition()))
  1270. {
  1271. m_Projection.SetAnimationPhase(SEL_CORD_PLUGGED, 0);
  1272. m_Projection.SetAnimationPhase(SEL_CORD_FOLDED, 1);
  1273. return SUFFIX_MATERIAL_POWERED;
  1274. }
  1275. else
  1276. {
  1277. m_Projection.SetAnimationPhase(SEL_CORD_PLUGGED, 1);
  1278. m_Projection.SetAnimationPhase(SEL_CORD_FOLDED, 0);
  1279. }
  1280. }
  1281. return SUFFIX_MATERIAL_DEPLOYABLE;
  1282. }
  1283. private bool IsRestrictedFromAdvancedPlacing()
  1284. {
  1285. if (m_Player.IsJumpInProgress())
  1286. return true;
  1287. if (m_Player.IsSwimming())
  1288. return true;
  1289. if (m_Player.IsClimbingLadder())
  1290. return true;
  1291. if (m_Player.IsRaised())
  1292. return true;
  1293. if (m_Player.IsClimbing())
  1294. return true;
  1295. if (m_Player.IsRestrained())
  1296. return true;
  1297. if (m_Player.IsUnconscious())
  1298. return true;
  1299. return false;
  1300. }
  1301. };
  1302. class ProjectionTrigger extends Trigger
  1303. {
  1304. protected int m_TriggerUpdateMs;
  1305. protected Hologram m_ParentObj;
  1306. protected PlayerBase m_Player;
  1307. override void OnEnter( Object obj )
  1308. {
  1309. //Print("OnEnter");
  1310. if ( m_ParentObj )
  1311. {
  1312. m_ParentObj.SetIsCollidingPlayer( true );
  1313. m_TriggerUpdateMs = 50;
  1314. }
  1315. }
  1316. override void OnLeave( Object obj )
  1317. {
  1318. //Print("OnLeave");
  1319. if ( m_ParentObj )
  1320. {
  1321. m_ParentObj.SetIsCollidingPlayer( false );
  1322. }
  1323. }
  1324. override protected void UpdateInsiders(int timeout)
  1325. {
  1326. super.UpdateInsiders(m_TriggerUpdateMs);
  1327. }
  1328. void SetParentObject( Hologram projection )
  1329. {
  1330. m_ParentObj = projection;
  1331. }
  1332. void SetParentOwner( PlayerBase player )
  1333. {
  1334. m_Player = player;
  1335. }
  1336. }