Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
SCR_NearbyContextDisplay.c
Go to the documentation of this file.
1//#define DEBUG_NEARBY_CONTEXT_DISPLAY
2
3class SCR_NearbyContextDisplay : SCR_InfoDisplayExtended
4{
5 [Attribute("5", UIWidgets.Slider, "Maximum amount of individual cached Widgets per kind.")]
6 protected int m_iMaxPrecachedWidgets;
7
8 [Attribute("{BF5FA7B21D658280}UI/layouts/HUD/InteractionSystem/ContextBasicInteractionBlip.layout", UIWidgets.ResourceNamePicker, "Layout", "layout", category: "NearbyContextDisplay")]
10
11 [Attribute("", UIWidgets.ResourceNamePicker, "edds", "edds")]
13
14 [Attribute("", desc: "SCR_NearbyContextCachingConfig Config that hold all the Widgets that will be cached.", params:"conf class=SCR_NearbyContextCachingConfig")]
16
17 [Attribute("0.2", desc: "Minimum Opacity of the Blip Widget when calculating Opacity based on distance to Camera.")]
18 protected float m_fMinWidgetAlpha;
19
20 [Attribute("", desc: "each ID must be unique")]
21 protected ref array<ref SCR_NearbyContextColorsComponentInteract> m_aNearbyWidgetColors;
22
24
26 protected SCR_InteractionHandlerComponent m_InteractionHandlerComponent;
27
29
31 private ref array<Widget> m_aWidgets = {};
33 private int m_iVisibleWidgets;
35 private float m_fOriginalSizeX;
37 private float m_fOriginalSizeY;
39 protected float m_fInteractionDistance;
42
43 protected bool m_bIsInitialized;
44
45 protected string m_sDefaultMapKey;
46 protected ref array<Widget> m_aCurrentlyUsedWidgets = {};
47
48 protected ref array<SCR_NearbyContextCachingData> m_aConfigLayouts = {};
49
52
55
56 protected ref TraceParam m_RaycastParam = new TraceParam();
57
58 //------------------------------------------------------------------------------------------------
61 {
62 int widgetsCount = m_aWidgets.Count();
63 for (int i = widgetsCount-1; i >= 0; i--)
64 {
65 if (m_aWidgets[i])
66 m_aWidgets[i].RemoveFromHierarchy();
67 }
68
69 m_aWidgets.Clear();
70 }
71
72 //------------------------------------------------------------------------------------------------
73 override void DisplayStartDraw(IEntity owner)
74 {
75 if (!m_wRoot)
76 {
77 Print("The SCR_NearbyContextDisplay root is NULL, the pre-cached SCR_NearbyContextDisplay widgets cannot be re-rooted!", LogLevel.ERROR);
78 return;
79 }
80
81 m_bIsInitialized = true;
82
83 // Re-root the blips to the info display root
84 foreach (Widget w : m_aWidgets)
85 {
86 m_wRoot.AddChild(w);
87 }
88
90
91 m_RaycastParam.Flags = TraceFlags.WORLD | TraceFlags.ENTS;
92
93 // For better performance the Cached Widgets are only checked ervery 10 seconds instead of on each update.
94 GetGame().GetCallqueue().CallLater(ClearCachedWidgets, 10000, true);
95 }
96
97 //------------------------------------------------------------------------------------------------
98 override void DisplayInit(IEntity owner)
99 {
100 m_InteractionHandlerComponent = SCR_InteractionHandlerComponent.Cast(owner.FindComponent(SCR_InteractionHandlerComponent));
102 Print("Interaction handler component could not be found! SCR_NearbyContextDisplay will not work.", LogLevel.WARNING);
103
105 m_fContextCollectDistance = m_InteractionHandlerComponent.GetNearbyCollectionRadius();
106
108 {
110 if (rsc && rsc.IsValid())
111 {
112 BaseContainer container = rsc.GetResource().ToBaseContainer();
113 if (container)
114 m_CachingConfig = SCR_NearbyContextCachingConfig.Cast(BaseContainerTools.CreateInstanceFromContainer(container));
115 }
116 }
117
118 if (!m_CachingConfig)
119 return;
120
121 m_CachingConfig.GetCachedLayouts(m_aConfigLayouts);
122
123 // Precache all wanted widgets
124 foreach (SCR_NearbyContextCachingData data : m_aConfigLayouts)
125 {
127 int precacheAmount;
128
129 data.GetLayout(layout);
130 data.GetPrecacheAmount(precacheAmount);
131
132 if (!layout)
133 continue;
134
135 CacheWidget(layout, precacheAmount);
136 }
137
139 {
141 foreach (SCR_NearbyContextColorsComponentInteract colorset : m_aNearbyWidgetColors)
142 {
143 if (!m_mCachedActionColors.Contains(colorset.m_eId))
144 m_mCachedActionColors.Insert(colorset.m_eId, colorset);
145 }
146 }
147 }
148
149 //------------------------------------------------------------------------------------------------
150 override void DisplayStopDraw(IEntity owner)
151 {
152 if (!m_aCurrentlyUsedWidgets.IsEmpty())
153 {
154 foreach (Widget w : m_aCurrentlyUsedWidgets)
155 {
156 if (w)
157 w.SetVisible(false);
158 }
159
161 }
162 }
163
164 //------------------------------------------------------------------------------------------------
165 override void DisplayUpdate(IEntity owner, float timeSlice)
166 {
167 bool bCameraActive = m_CameraHandler && m_CameraHandler.IsCameraActive();
168 // m_InteractionHandlerComponent.SetNearbyCollectionEnabled(true);
169 if (!bCameraActive || !m_InteractionHandlerComponent.GetNearbyCollectionEnabled())
170 {
171 // if we are currently displaying something, clear it.
172 if (!m_aCurrentlyUsedWidgets.IsEmpty())
173 {
174 foreach (Widget w : m_aCurrentlyUsedWidgets)
175 {
176 if (w)
177 w.SetVisible(false);
178 }
179
181 }
182
183 return;
184 }
185
186 // Fetch nearby contexts
187 array<UserActionContext> contexts = {};
188 m_InteractionHandlerComponent.GetNearbyAvailableContextList(contexts);
189
190 array<UserActionContext> additionalContexts = {};
191 m_InteractionHandlerComponent.GetNearbyShowableContextList(additionalContexts);
192 foreach(UserActionContext ctx: additionalContexts)
193 {
195 contexts.Insert(ctx);
196 }
197
198 #ifdef DEBUG_ACTIONICONS
199 contexts.InsertAll(additionalContexts);
200 #endif
201
202 // Get required data
203 BaseWorld world = owner.GetWorld();
204 int cameraIndex = world.GetCurrentCameraId();
205 vector mat[4];
206 world.GetCamera(cameraIndex, mat);
207 vector referencePos = mat[3];
208
209 // Iterate through individual contexts,
210 // validate that they are visible,
211 // and update their widget representation
212 const float threshold = 0.25;
213 const float fovBase = 100; // whatever fits
214
215 // Field of view and screen resolution is necessary to compute proper position and size of widgets
216 float zoom = 1; // world.GetCameraVerticalFOV(cameraIndex) - missing crucial getter
217 CameraManager cameraManager = GetGame().GetCameraManager();
218 if (cameraManager)
219 {
220 CameraBase camera = cameraManager.CurrentCamera();
221
222 #ifdef DEBUG_NEARBY_CONTEXT_DISPLAY
223 if (camera.GetProjType() == CameraType.NONE)
224 PrintFormat("%1 [DisplayUpdate] None Projection", this);
225 #endif
226
227 if (camera && camera.GetProjType() != CameraType.NONE)
228 zoom = fovBase / Math.Max(camera.GetVerticalFOV(), 1);
229 }
230
231 // Screen resolution is necessary to know how far away the widget is from screen center
232 // or from cursor, if we ever allow player to use mouse cursor or eye tracking software to select the actions.
233 float resX; float resY;
234 GetGame().GetWorkspace().GetScreenSize(resX, resY);
235
236 UserActionContext currentContext = m_InteractionHandlerComponent.GetCurrentContext();
237
238 // If we draw something, hide it first
239 if (!m_aCurrentlyUsedWidgets.IsEmpty())
240 {
241 foreach (Widget w : m_aCurrentlyUsedWidgets)
242 {
243 if (w)
244 w.SetVisible(false);
245 }
246
248 }
249
250 bool isOverrideEnabled = m_InteractionHandlerComponent.GetManualCollectionOverride();
251 SCR_NearbyContextColorsComponentInteract nearbyColors;
253 IEntity entAction;
254
255 #ifdef DEBUG_ACTIONICONS
256 array<BaseUserAction> outActions = {};
257 #else
258 BaseUserAction currAction;
259 #endif
260
261 // Iterate through every available context and assign a Widget to it
262 foreach (UserActionContext ctx : contexts)
263 {
264 // Do not draw currently select one
265 if (currentContext == ctx)
266 continue;
267
268 vector position = ctx.GetOrigin();
269 float posX, posY;
270 GetWorldToScreenPosition(world, cameraIndex, position, posX, posY);
271
272 #ifdef DEBUG_ACTIONICONS
273 if (ctx.GetActionsList(outActions) < 1)
274 continue;
275
276 entAction = outActions[0].GetOwner();
277 if (!entAction)
278 continue;
279
280 foreach(BaseUserAction baseAction: outActions)
281 {
282 SCR_ActionUIInfo actionUIInfo = SCR_ActionUIInfo.Cast(baseAction.GetUIInfo());
283
284 if (!actionUIInfo)
285 Print("DEBUG_ACTIONICONS:: No icons found for "+ baseAction);
286 }
287 #else
288 currAction = ctx.GetAction(0);
289 if (!currAction)
290 continue;
291
292 entAction = currAction.GetOwner();
293 if (!entAction)
294 continue;
295 #endif
296
297 // Just ignore actions out of reach or out of screen, we will fade them out anyway
298 if (IsOnScreen(resX, resY, posX, posY) && IsInLineOfSight(position, mat, threshold))
299 {
300 // Only do the raycast if there is no collection override and the context has UseRaycast enabled
301 if (!isOverrideEnabled && ctx.ShouldCheckLineOfSight() && IsObstructed(position, referencePos, world, entAction))
302 continue;
303
304 //Get the widget array from map using the layout as key
305 SCR_ActionContextUIInfo info = SCR_ActionContextUIInfo.Cast(ctx.GetUIInfo());
306 Widget widget;
307 array<Widget> mapWidgets;
308
309 if (info)
310 {
311 string layout = info.GetLayout();
312 string mapKey;
313
314 // Get the GUID of the Layout as it's used as key in the map holding the array of different widgets
315 if (layout)
316 mapKey = layout;
317 else
318 mapKey = m_sDefaultMapKey;
319
320 mapWidgets = m_mCachedWidgets.Get(mapKey);
321
322 if (!mapWidgets || mapWidgets.IsEmpty())
323 CacheWidget(mapKey, 1);
324
325 mapWidgets = m_mCachedWidgets.Get(mapKey);
326
327 foreach (Widget w : mapWidgets)
328 {
329 if (m_aCurrentlyUsedWidgets.Contains(w))
330 continue;
331
332 widget = w;
333 m_aCurrentlyUsedWidgets.Insert(w);
334 break;
335 }
336
338 if (!widget)
339 {
340 CacheWidget(mapKey, 1, true, widget);
341 m_aCurrentlyUsedWidgets.Insert(widget);
342 }
343
344 }
345 else
346 {
347 mapWidgets = m_mCachedWidgets.Get(m_sDefaultMapKey);
348
349 if (!mapWidgets || mapWidgets.IsEmpty())
351
352 mapWidgets = m_mCachedWidgets.Get(m_sDefaultMapKey);
353
354 foreach (Widget w : mapWidgets)
355 {
356 if (m_aCurrentlyUsedWidgets.Contains(w))
357 continue;
358
359 widget = w;
360 m_aCurrentlyUsedWidgets.Insert(w);
361 break;
362 }
363
365 if (!widget)
366 {
367 CacheWidget(m_sDefaultMapKey, 1, true, widget);
368 m_aCurrentlyUsedWidgets.Insert(widget);
369 }
370 }
371
372 if (widget)
373 {
374 FrameSlot.SetPos(widget, posX, posY);
375
376 bool visible = ctx.IsInVisibilityAngle(referencePos);
377 float distanceLimit = m_fContextCollectDistance * zoom;
378 float distanceSq = vector.DistanceSq(position, referencePos);
379 float distance = Math.Sqrt(distanceSq);
380
381 SetWidgetAlpha(widget, distance, distanceLimit);
382 widget.SetZOrder(-(int)distance);
383
385 if (!widgetComp)
386 continue;
387
388 if (widgetComp.GetAssignedContext() != ctx)
389 {
390 widgetComp.OnAssigned(info, ctx);
391
392 if (!widgetComp.HasColorData())
394
395 widgetComp.UpdateColors(ctx);
396 }
397
398 widgetComp.ChangeVisibility(visible);
399 }
400 }
401 }
402 // Enable required amount of widgets
404 }
405
406 //------------------------------------------------------------------------------------------------
407 // Add layouts to cache
413 void CacheWidget(ResourceName layout, int amount, bool returnWidget = false, Widget widgetToReturn = null)
414 {
415 if (!layout)
416 return;
417
418 string key = layout;
419 array<Widget> cachedWidgets = {};
420 WorkspaceWidget workSpace = GetGame().GetWorkspace();
421 Widget widgetToCache;
422
423 if (m_mCachedWidgets.Contains(key))
424 {
425 cachedWidgets = m_mCachedWidgets.Get(key);
426 }
427
428 // Create all the Widgets and insert them into the map
429 for (int i = 0; i < amount; i++)
430 {
431 widgetToCache = workSpace.CreateWidgets(layout, m_wRoot);
432
433 if (widgetToCache)
434 {
435 cachedWidgets.Insert(widgetToCache);
436
437 FrameSlot.SetAnchor(widgetToCache, 0, 0);
438 FrameSlot.SetAlignment(widgetToCache, 0.5, 0.5);
439 widgetToCache.SetVisible(false);
440
441 // If the system is not fully initialized and the RootWidget does not exist yet we add them to the Widget array, so they can be reparented
442 if (!m_bIsInitialized && !m_wRoot)
443 m_aWidgets.Insert(widgetToCache);
444 }
445
446 }
447
448 m_mCachedWidgets.Set(key, cachedWidgets);
449
450 if (returnWidget)
451 widgetToReturn = cachedWidgets[0];
452 }
453
454 //------------------------------------------------------------------------------------------------
457 protected void ClearCachedWidgets()
458 {
459 int count;
460 int maxCachedAmount = m_iMaxPrecachedWidgets;
461 Widget cachedWidget;
462
463 SCR_NearbyContextCachingData data;
464 foreach (string mapKey, array<Widget> cachedWidgets : m_mCachedWidgets)
465 {
466 data = m_CachingConfig.GetDataFromLayout(mapKey);
467
468 if (data)
469 data.GetMaxCacheAmount(maxCachedAmount);
470
471 count = cachedWidgets.Count();
472
473 if (count <= maxCachedAmount)
474 continue;
475
476 for (int index = count - 1; index >= maxCachedAmount; index--)
477 {
478 cachedWidget = cachedWidgets[index];
479
480 if (cachedWidget && !cachedWidget.IsVisible())
481 {
482 cachedWidget.RemoveFromHierarchy();
483 cachedWidgets.Remove(index);
484 }
485 }
486
487 m_mCachedWidgets.Set(mapKey, cachedWidgets);
488 }
489 }
490
491 //------------------------------------------------------------------------------------------------
498 protected bool IsObstructed(vector contextPos, vector cameraPos, BaseWorld world, notnull IEntity entAction)
499 {
500 m_RaycastParam.Start = cameraPos;
501 m_RaycastParam.End = contextPos;
502
503 if (world.TraceMove(m_RaycastParam, IsCharacter) < m_InteractionHandlerComponent.GetRaycastThreshold())
504 {
505 if (!m_RaycastParam.TraceEnt)
506 return true;
507
508 IEntity parentEntityRay = m_RaycastParam.TraceEnt.GetRootParent();
509 IEntity parentEntityAct = entAction.GetRootParent();
510
511 if (parentEntityRay != parentEntityAct)
512 return true;
513 }
514
515 return false;
516 }
517
518 //------------------------------------------------------------------------------------------------
522 protected static bool IsCharacter(notnull IEntity entity)
523 {
524 return ChimeraCharacter.Cast(entity) == null;
525 }
526
527 //------------------------------------------------------------------------------------------------
529 protected void SetVisibleWidgets()
530 {
531 if (!m_wRoot || m_aCurrentlyUsedWidgets.IsEmpty())
532 return;
533
534 foreach (Widget w : m_aCurrentlyUsedWidgets)
535 {
536 if (w)
537 w.SetVisible(true);
538 }
539 }
540
541 //------------------------------------------------------------------------------------------------
546 protected void SetWidgetAlpha(Widget widget, float distance, float limit)
547 {
548 float alpha = 1;
549 if (limit != 0 && distance >= m_fInteractionDistance)
550 alpha = Math.Clamp(limit - distance, m_fMinWidgetAlpha, limit) / limit;
551
552 // Apply opacity
553 widget.SetOpacity(alpha);
554 }
555
556 //------------------------------------------------------------------------------------------------
563 protected bool GetWorldToScreenPosition(BaseWorld world, int cameraIndex, vector worldPosition, out float posX, out float posY)
564 {
565 vector screenPosition = GetGame().GetWorkspace().ProjWorldToScreen(worldPosition, world, cameraIndex);
566 posX = screenPosition[0];
567 posY = screenPosition[1];
568
569 return false;
570 }
571
572 //------------------------------------------------------------------------------------------------
578 protected bool IsInLineOfSight(vector point, vector transform[4], float val)
579 {
580 vector direction = point - transform[3];
581 direction.Normalize();
582
583 const float threshold = 0.25;
584 if (vector.Dot(direction, transform[2]) > val)
585 return true;
586
587 return false;
588 }
589
590 //------------------------------------------------------------------------------------------------
597 protected bool IsOnScreen(float resX, float resY, float posX, float posY)
598 {
599 if (posX > resX || posY > resY || posX < 0 || posY < 0)
600 return false;
601
602 return true;
603 }
604
605 //------------------------------------------------------------------------------------------------
612 protected float GetDistanceFromScreenCenter(float resX, float resY, float posX, float posY)
613 {
614 float aspectRatio = resX / resY;
615
616 // Get distance from center as 0,1
617 // where 0 = center, 1 = edge (only for 1920 x 1080)
618 // when screen is larger than default we increase the distance to larger than 1
619 float xDistance = Math.AbsFloat(posX - resX * 0.5) / resX * aspectRatio;
620 float yDistance = Math.AbsFloat(posY - resY * 0.5) / resY;
621
622 // calculate last unknown length
623 return Math.Sqrt(Math.Pow(xDistance, 2) + Math.Pow(yDistance, 2));
624 }
625}
ArmaReforgerScripted GetGame()
Definition game.c:1398
float distance
vector direction
vector position
SCR_DestructionSynchronizationComponentClass ScriptComponentClass int index
Get all prefabs that have the spawner data
UI layouts Menus CleanSweep CleanSweepAreaSelection layout
Widget m_wRoot
SCR_CharacterCameraHandlerComponent m_CameraHandler
UI Textures DeployMenu Briefing conflict_HintBanner_1_UI desc
proto external IEntity GetOwner()
Returns the parent entity of this action.
proto external Managed FindComponent(typename typeName)
proto external BaseWorld GetWorld()
proto external IEntity GetRootParent()
Definition Math.c:13
Object holding reference to resource. In destructor release the resource.
Definition Resource.c:25
bool IsObstructed(vector contextPos, vector cameraPos, BaseWorld world, notnull IEntity entAction)
float m_fInteractionDistance
Distance in meters in which a context can be interacted with. Defined in 'InteractionHandlerComponent...
void CacheWidget(ResourceName layout, int amount, bool returnWidget=false, Widget widgetToReturn=null)
float m_fContextCollectDistance
Distnace in meters in which contexts will be collected and visible to indicate a possible interaction...
override void DisplayUpdate(IEntity owner, float timeSlice)
void ~SCR_NearbyContextDisplay()
Performs cleanup.
override void DisplayStartDraw(IEntity owner)
bool IsInLineOfSight(vector point, vector transform[4], float val)
ref array< ref SCR_NearbyContextColorsComponentInteract > m_aNearbyWidgetColors
void SetVisibleWidgets()
Iterates through available precached widgets and leaves only provided count enabled.
static bool IsCharacter(notnull IEntity entity)
SCR_InteractionHandlerComponent m_InteractionHandlerComponent
Interaction handler attached to parent entity.
ref map< SCR_ENearbyInteractionContextColors, ref SCR_NearbyContextColorsComponentInteract > m_mCachedActionColors
Holds all the different cached id with the enum as key.
override void DisplayStopDraw(IEntity owner)
bool GetWorldToScreenPosition(BaseWorld world, int cameraIndex, vector worldPosition, out float posX, out float posY)
ref array< Widget > m_aCurrentlyUsedWidgets
bool IsOnScreen(float resX, float resY, float posX, float posY)
void SetWidgetAlpha(Widget widget, float distance, float limit)
ref SCR_NearbyContextCachingConfig m_CachingConfig
float GetDistanceFromScreenCenter(float resX, float resY, float posX, float posY)
override void DisplayInit(IEntity owner)
ref map< string, ref array< Widget > > m_mCachedWidgets
Holds all the different cached Widgets with the Layout GUID as key.
ref array< SCR_NearbyContextCachingData > m_aConfigLayouts
void SetColorsData(map< SCR_ENearbyInteractionContextColors, ref SCR_NearbyContextColorsComponentInteract > cachedActionColors)
void UpdateColors(UserActionContext ctx, SCR_ENearbyInteractionContextColors nearbyColorState=UIConstants.NEARBY_INTERACTION_DEFAULT_STATE)
UserActionContext GetAssignedContext()
Get the context that currently uses this Widget.
void OnAssigned(SCR_ActionContextUIInfo info, UserActionContext context)
Definition Types.c:486
proto void Print(void var, LogLevel level=LogLevel.NORMAL)
Prints content of variable to console/log.
LogLevel
Enum with severity of the logging message.
Definition LogLevel.c:14
proto void PrintFormat(string fmt, void param1=NULL, void param2=NULL, void param3=NULL, void param4=NULL, void param5=NULL, void param6=NULL, void param7=NULL, void param8=NULL, void param9=NULL, LogLevel level=LogLevel.NORMAL)
SCR_FieldOfViewSettings Attribute
TraceFlags
Definition TraceFlags.c:13
CameraType
Definition CameraType.c:14