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