DayZ 1.30
DayZ Explorer by KGB
 
Загрузка...
Поиск...
Не найдено
CCTLiquid.c
См. документацию.
2{
3 protected static const float HIT_POS_TOLERANCE = 1.0;
5 protected static const float SEA_FALLBACK_DISTANCE_BUFFER = 1.5;
6 protected static const float SYNTHETIC_POND_SAMPLE_STEP = 1.0;
7 protected static const float SYNTHETIC_POND_SURFACE_TOLERANCE = 0.35;
8 protected static const float SYNTHETIC_POND_FALLBACK_SURFACE_TOLERANCE = 1.75;
9 protected static const float SYNTHETIC_CURSOR_DISTANCE_BUFFER = 1.5;
10 protected static const float SEA_CURSOR_STICK_DISTANCE = 0.75;
11 protected static const int SEA_CURSOR_STICK_TIME_MS = 350;
12 protected static const float SYNTHETIC_CURSOR_CACHE_AIM_TOLERANCE = 1.75;
13 protected static const int SYNTHETIC_CURSOR_CACHE_MAX_AGE_MS = 350;
14 protected static const float SEA_HIT_RAW_Y_TOLERANCE = 1.25;
15 protected static const float SEA_CURSOR_MIN_DOWN_DOT = 0.035;
16 protected static const float LIQUID_SURFACE_RAYCAST_OFFSET = 0.2;
17
21
22 protected float m_MaxVerticalReach;
23 protected float m_MaxActionDistance;
24 protected int m_AllowedLiquidSource;
27 protected int m_LiquidType = LIQUID_NONE;
31
32 #ifdef DIAG_DEVELOPER
33 protected bool m_DebugSyntheticCursor;
34 protected ref array<Shape> m_DebugSyntheticCursorShapes;
35 #endif
36
37 void CCTLiquid(int allowedLiquidSource, float maximalTargetDistance = UAMaxDistances.DEFAULT, float maxVerticalReach = UAMaxDistances.DEFAULT, SurfaceDetectionType surfaceDetectionType = SurfaceDetectionType.Roadway, bool allowSeaLevelCursorFallback = false)
38 {
42
43 m_MaxActionDistance = maximalTargetDistance;
44 m_MaxVerticalReach = maxVerticalReach;
45 m_AllowedLiquidSource = allowedLiquidSource;
46 m_AllowSeaLevelCursorFallback = allowSeaLevelCursorFallback;
47
49 m_SurfaceDetectionParams.type = surfaceDetectionType;
50 m_SurfaceDetectionParams.includeWater = true;
52 m_SurfaceDetectionParams.rsd = RoadSurfaceDetection.CLOSEST;
53
55 m_SurfaceDetectionParamsNoWater.type = surfaceDetectionType;
56 m_SurfaceDetectionParamsNoWater.includeWater = false;
58 m_SurfaceDetectionParamsNoWater.rsd = RoadSurfaceDetection.CLOSEST;
59
60 #ifdef DIAG_DEVELOPER
61 m_DebugSyntheticCursor = DiagMenu.GetBool(DiagMenuIDs.SYNTHETIC_FISHING_CURSOR);
62 m_DebugSyntheticCursorShapes = new array<Shape>();
63 #endif
64 }
65
66 override bool Can(PlayerBase player, ActionTarget target)
67 {
68 if (!target)
69 return false;
70
74
75 vector playerPos = player.GetPosition();
76 Object targetObj = target.GetObject();
77 vector objectPosition = vector.Zero;
78 bool isValidInteract = false;
79
81 if (targetObj)
82 {
83 m_WaterSourceType = targetObj.GetWaterSourceObjectType();
84 m_LiquidSourceType = targetObj.GetLiquidSourceObjectType();
85
86 if (targetObj.IsWell())
87 {
88 TIntArray componentIndices = new TIntArray();
89 targetObj.GetActionComponentsForSelectionName(targetObj.GetViewGeometryLevel(), UAMisc.LIQUID_SOURCE_SELECTION, componentIndices);
90
91 if (componentIndices.Count() > 0)
92 isValidInteract = componentIndices.Find(target.GetComponentIndex()) != -1;
93 else
94 isValidInteract = true;
95 }
96 else
97 {
98 objectPosition = targetObj.GetPosition();
99 isValidInteract = IsValidTargetObject(playerPos, objectPosition, m_WaterSourceType, m_LiquidSourceType, target.IsProxy());
100 }
101 }
102
103 vector hitPosition = target.GetCursorHitPos();
104
105 #ifdef DIAG_DEVELOPER
106 m_DebugSyntheticCursor = DiagMenu.GetBool(DiagMenuIDs.SYNTHETIC_FISHING_CURSOR);
107 #endif
108
111 if (m_AllowSeaLevelCursorFallback && !targetObj)
112 {
113 vector resolvedHitPosition;
114 int resolvedLiquidType;
115
116 if (TryResolveSyntheticCursor(player, playerPos, hitPosition, resolvedHitPosition, resolvedLiquidType))
117 {
118 hitPosition = resolvedHitPosition;
119 target.SetCursorHitPos(hitPosition);
120
121 m_LiquidType = resolvedLiquidType;
123 }
124 }
125
126 if (isValidInteract && targetObj)
127 {
128 m_LiquidType = targetObj.GetLiquidSourceType();
130 }
131
132 string surfaceType;
133 if (CheckSurfaceLiquid(player, targetObj, hitPosition, playerPos, isValidInteract, surfaceType))
134 {
136 }
137
138 if (!targetObj && surfaceType == string.Empty && m_LiquidType == LIQUID_NONE)
139 {
140 if (!CheckSeaLevelFallback(hitPosition, playerPos))
141 {
142 #ifdef DIAG_DEVELOPER
143 DbgUI.Begin("", 10, 200);
144 if (m_DebugSyntheticCursor)
145 {
146 DbgUI.Text(string.Format("CCTLiquid CheckSeaLevelFallback rejected: reason=could not create sea hit point | hitPosition=%1 | playerPos=%2", hitPosition, playerPos));
147 }
148 DbgUI.End();
149 #endif
150 }
151 }
152
154 }
155
156 protected bool IsValidLiquid(int liquid)
157 {
158 return (liquid & m_AllowedLiquidSource) != 0;
159 }
160
161 protected bool IsValidTargetObject(vector playerPos, vector objectPosition, EWaterSourceObjectType waterSourceType, ELiquidSourceObjectType liquidSourceType, bool isProxy)
162 {
164 if ((waterSourceType == EWaterSourceObjectType.NONE && liquidSourceType == ELiquidSourceObjectType.NONE) || isProxy)
165 return false;
166
168 if (IsNearlyZero(objectPosition))
169 return false;
170
172 if (vector.Distance(playerPos, objectPosition) > m_MaxActionDistance)
173 return false;
174
176 float heightDiff = Math.AbsFloat(objectPosition[1] - playerPos[1]);
177 return heightDiff <= m_MaxVerticalReach;
178 }
179
180 protected bool CheckSurfaceLiquid(PlayerBase player, Object targetObj, vector hitPosition, vector playerPos, bool isValidInteract, out string surfaceType)
181 {
182 surfaceType = string.Empty;
183
185 if (isValidInteract)
186 return false;
187
189 if (IsNearlyZero(hitPosition))
190 return false;
191
192 m_SurfaceDetectionParams.position = hitPosition;
194 if (!g_Game.GetSurface(m_SurfaceDetectionParams, surfaceResult))
195 return false;
196
197 if (!surfaceResult)
198 return false;
199
200 SurfaceInfo surfaceInfo = surfaceResult.surface;
201 if (!surfaceInfo)
202 return false;
203
204 surfaceType = surfaceInfo.GetSurfaceType();
205 if (surfaceType == string.Empty)
206 return false;
207
208 float surfaceHeight = surfaceResult.height;
209 float heightDiffSurface = Math.AbsFloat(hitPosition[1] - surfaceHeight);
210 float playerToSurfaceHeight = Math.AbsFloat(playerPos[1] - surfaceHeight);
211
212 int liquidType = g_Game.CorrectLiquidType(surfaceInfo.GetLiquidType());
213 if (liquidType == LIQUID_NONE)
214 return false;
215
216 vector liquidSurfacePos = hitPosition;
217 liquidSurfacePos[1] = surfaceHeight;
218
220 if (!IsCursorActuallyOnLiquidSurface(hitPosition, liquidSurfacePos))
221 return false;
222
223 vector surfacePos = liquidSurfacePos;
224 surfacePos[1] = surfacePos[1] + LIQUID_SURFACE_RAYCAST_OFFSET;
225
226 if (vector.Distance(playerPos, surfacePos) > m_MaxActionDistance)
227 return false;
228
229 if (targetObj)
230 {
231 if (heightDiffSurface > 0.25)
232 return false;
233
234 if (!HasClearPathToLiquidSurface(player, surfacePos))
235 return false;
236 }
237 else
238 {
239 if (!HasClearPathToLiquidSurface(player, surfacePos))
240 return false;
241 }
242
243 if (playerToSurfaceHeight > m_MaxVerticalReach)
244 return false;
245
246 m_LiquidType = liquidType;
247 return true;
248 }
249
250 protected bool HasClearPathToLiquidSurface(PlayerBase player, vector surfacePos)
251 {
252 vector rayStart;
253 MiscGameplayFunctions.GetHeadBonePos(player, rayStart);
254 RaycastRVParams rayInput = new RaycastRVParams(rayStart, surfacePos, player);
255 rayInput.flags = CollisionFlags.FIRSTCONTACT;
256 rayInput.type = ObjIntersectFire;
257 rayInput.sorted = true;
258 rayInput.ignore = player;
259
261 if (!DayZPhysics.RaycastRVProxy(rayInput, results))
262 return true;
263
264 float surfaceDist = vector.Distance(rayStart, surfacePos);
265 foreach (RaycastRVResult result : results)
266 {
267 if (!result)
268 continue;
269
270 Object hitObj = result.obj;
271 if (!hitObj)
272 continue;
273
274 float hitDist = vector.Distance(rayStart, result.pos);
276 if (hitDist < surfaceDist)
277 return false;
278 }
279
280 return true;
281 }
282
283 protected bool CheckSeaLevelFallback(vector hitPosition, vector playerPos)
284 {
285 vector normalizedSeaHit;
286 if (!TryGetNormalizedSeaHitPoint(playerPos, hitPosition, normalizedSeaHit))
287 return false;
288
290 return true;
291 }
292
293 protected bool TryGetLiquidSurfaceAt(vector probePosition, out vector surfacePosition, out int liquidType, out string surfaceType)
294 {
295 surfacePosition = vector.Zero;
296 liquidType = LIQUID_NONE;
297 surfaceType = string.Empty;
298
299 m_SurfaceDetectionParams.position = probePosition;
301 if (!g_Game.GetSurface(m_SurfaceDetectionParams, surfaceResult))
302 return false;
303
304 if (!surfaceResult)
305 return false;
306
307 SurfaceInfo surfaceInfo = surfaceResult.surface;
308 if (!surfaceInfo)
309 return false;
310
311 surfaceType = surfaceInfo.GetSurfaceType();
312 if (surfaceType == string.Empty)
313 return false;
314
315 liquidType = g_Game.CorrectLiquidType(surfaceInfo.GetLiquidType());
316 if (liquidType == LIQUID_NONE)
317 return false;
318
319 surfacePosition = probePosition;
320 surfacePosition[1] = surfaceResult.height;
321
322 return true;
323 }
324
325 protected bool TryBuildSeaLevelHitPosition(PlayerBase player, vector playerPos, out vector seaHitPosition)
326 {
327 seaHitPosition = vector.Zero;
328
330 if (g_Game.IsHeadlessOrDedicatedServer())
331 return false;
332
333 float seaLevel = g_Game.SurfaceGetSeaLevel();
334 float seaLevelMax = g_Game.SurfaceGetSeaLevelMax();
335
336 if (playerPos[1] > seaLevelMax + m_MaxVerticalReach)
337 return false;
338
339 vector cameraPos = g_Game.GetCurrentCameraPosition();
340 vector cameraDir = g_Game.GetCurrentCameraDirection();
341
342 if (cameraPos == vector.Zero || cameraDir == vector.Zero)
343 return false;
344
345 cameraDir.Normalize();
346
348 if (safeDistance <= 1.0)
349 safeDistance = m_MaxActionDistance * 0.8;
350
352 float cameraToSeaHeight = cameraPos[1] - seaLevel;
353 if (cameraToSeaHeight <= 0.0)
354 return false;
355
357 float minStableDownDot = SEA_CURSOR_MIN_DOWN_DOT;
358 float maxAllowedCameraDirY = -minStableDownDot;
359
360 if (cameraDir[1] > maxAllowedCameraDirY)
361 {
362 #ifdef DIAG_DEVELOPER
363 DbgUI.Begin("", 10, 200);
364 if (m_DebugSyntheticCursor)
365 {
366 DbgUI.Text(string.Format("CCTLiquid TryBuildSeaLevelHitPosition rejected: reason=camera too shallow | cameraDirY=%1 | minStableDownDot=%2 | height=%3 | safeDist=%4", cameraDir[1], minStableDownDot, cameraToSeaHeight, safeDistance));
367 }
368 DbgUI.End();
369 #endif
370
371 return false;
372 }
373
375 float t = (seaLevel - cameraPos[1]) / cameraDir[1];
376 if (t <= 0)
377 return false;
378
379 vector candidate = cameraPos + cameraDir * t;
380 candidate[1] = seaLevel;
381
382 float safeDistanceSq = safeDistance * safeDistance;
383
385 if (vector.DistanceSq(playerPos, candidate) > safeDistanceSq)
386 {
387 #ifdef DIAG_DEVELOPER
388 DbgUI.Begin("", 20, 200);
389 if (m_DebugSyntheticCursor)
390 {
391 DbgUI.Text(string.Format("CCTLiquid TryBuildSeaLevelHitPosition rejected: reason=Real sea intersection too far | player=%1 | candidate=%2 | dist=%3 | safeDist=%4", playerPos.ToString(), candidate.ToString(), vector.Distance(playerPos, candidate), safeDistance));
392 }
393 DbgUI.End();
394 #endif
395 return false;
396 }
397
398 if (!IsSeaHitPositionValid(playerPos, candidate))
399 return false;
400
401 vector seaSurfacePos = candidate;
402 seaSurfacePos[1] = seaSurfacePos[1] + LIQUID_SURFACE_RAYCAST_OFFSET;
403
404 if (!HasClearPathToLiquidSurface(player, seaSurfacePos))
405 return false;
406
408 candidate = ApplySyntheticHitCursorStability(playerPos, candidate, LIQUID_SALTWATER);
409 if (!IsSeaHitPositionValid(playerPos, candidate))
410 return false;
411
412 vector cachedOrStableSeaSurfacePos = candidate;
413 cachedOrStableSeaSurfacePos[1] = cachedOrStableSeaSurfacePos[1] + LIQUID_SURFACE_RAYCAST_OFFSET;
414
415 if (!HasClearPathToLiquidSurface(player, cachedOrStableSeaSurfacePos))
416 return false;
417
418 seaHitPosition = candidate;
419 return true;
420 }
421
422 protected bool IsSeaHitPositionValid(vector playerPos, vector hitPosition)
423 {
424 vector normalizedHit;
425 if (!TryGetNormalizedSeaHitPoint(playerPos, hitPosition, normalizedHit))
426 return false;
427
428 return true;
429 }
430
431 protected bool TryBuildPondHitPosition(PlayerBase player, vector playerPos, out vector pondHitPosition)
432 {
433 #ifdef DIAG_DEVELOPER
434 float debugBestDelta = 999999.0;
435 int debugSampleCount = 0;
436 int debugValidKnownSurfaceCount = 0;
437 string debugPondFailReason = "no pond samples";
438 #endif
439
440 pondHitPosition = vector.Zero;
441
443 if (g_Game.IsHeadlessOrDedicatedServer())
444 return false;
445
446 vector cameraPos = g_Game.GetCurrentCameraPosition();
447 vector cameraDir = g_Game.GetCurrentCameraDirection();
448
449 if (cameraPos == vector.Zero || cameraDir == vector.Zero)
450 return false;
451
452 cameraDir.Normalize();
453
455 if (cameraDir[1] > 0.15)
456 return false;
457
459 if (safeDistance <= 1.0)
460 safeDistance = m_MaxActionDistance * 0.8;
461
462 vector bestSurfacePos = vector.Zero;
463 int bestLiquidType = LIQUID_NONE;
464 float bestDelta = 999999.0;
465 vector pondSurfacePos = vector.Zero;
466
467 for (float dist = 1.0; dist <= safeDistance; dist += SYNTHETIC_POND_SAMPLE_STEP)
468 {
469 #ifdef DIAG_DEVELOPER
470 debugSampleCount++;
471 #endif
472
473 vector probePos = cameraPos + cameraDir * dist;
474
475 vector surfacePos;
476 int liquidType;
477 string surfaceType;
478
479 if (!TryGetLiquidSurfaceAt(probePos, surfacePos, liquidType, surfaceType))
480 {
481 #ifdef DIAG_DEVELOPER
482 debugPondFailReason = "TryGetLiquidSurfaceAt failed";
483 #endif
484 continue;
485 }
486
487 if (!IsAllowedPondLiquidSurface(playerPos, surfacePos, liquidType))
488 {
489 #ifdef DIAG_DEVELOPER
490 debugPondFailReason = "known pond surface invalid";
491 #endif
492 continue;
493 }
494
495 #ifdef DIAG_DEVELOPER
496 debugValidKnownSurfaceCount++;
497 #endif
498
499 float rayToSurfaceDelta = Math.AbsFloat(probePos[1] - surfacePos[1]);
500 if (rayToSurfaceDelta < bestDelta)
501 {
502 bestDelta = rayToSurfaceDelta;
503 bestSurfacePos = surfacePos;
504 bestLiquidType = liquidType;
505
506 #ifdef DIAG_DEVELOPER
507 debugBestDelta = bestDelta;
508 debugPondFailReason = "best pond surface too far from camera ray";
509 #endif
510 }
511
513 if (rayToSurfaceDelta <= SYNTHETIC_POND_SURFACE_TOLERANCE)
514 {
515 surfacePos = ApplySyntheticHitCursorStability(playerPos, surfacePos, liquidType);
516
517 if (!IsValidPondHitPosition(playerPos, surfacePos, liquidType))
518 continue;
519
520 pondSurfacePos = surfacePos;
521 pondSurfacePos[1] = pondSurfacePos[1] + LIQUID_SURFACE_RAYCAST_OFFSET;
522
523 if (!HasClearPathToLiquidSurface(player, pondSurfacePos))
524 continue;
525
526 pondHitPosition = surfacePos;
527 return true;
528 }
529 }
530
532 if (!IsNearlyZero(bestSurfacePos) && bestDelta <= SYNTHETIC_POND_FALLBACK_SURFACE_TOLERANCE)
533 {
534 bestSurfacePos = ApplySyntheticHitCursorStability(playerPos, bestSurfacePos, bestLiquidType);
535 if (IsValidPondHitPosition(playerPos, bestSurfacePos, bestLiquidType))
536 {
537 pondSurfacePos = bestSurfacePos;
538 pondSurfacePos[1] = pondSurfacePos[1] + LIQUID_SURFACE_RAYCAST_OFFSET;
539
540 if (!HasClearPathToLiquidSurface(player, pondSurfacePos))
541 return false;
542
543 pondHitPosition = bestSurfacePos;
544 return true;
545 }
546 }
547
548 #ifdef DIAG_DEVELOPER
549 DbgUI.Begin("", 50, 240);
550 if (m_DebugSyntheticCursor)
551 {
552 DbgUI.Text(string.Format("CCTLiquid TryBuildPondHitPosition failed: reason=%1 | samples=%2 | validSurfaces=%3 | bestDelta=%4 | tolerance=%5 | relaxedTolerance=%6", debugPondFailReason, debugSampleCount, debugValidKnownSurfaceCount, debugBestDelta, SYNTHETIC_POND_SURFACE_TOLERANCE, SYNTHETIC_POND_FALLBACK_SURFACE_TOLERANCE));
553 }
554 DbgUI.End();
555 #endif
556
557 return false;
558 }
559
560 protected bool IsValidPondHitPosition(vector playerPos, vector hitPosition, int liquidType = LIQUID_NONE)
561 {
562 vector surfacePos;
563 string surfaceType;
564 int validatedLiquidType;
565
566 return TryValidatePondHitPosition(playerPos, hitPosition, surfacePos, validatedLiquidType, surfaceType, liquidType);
567 }
568
569 protected bool TryGetNormalizedSeaHitPoint(vector playerPos, vector hitPosition, out vector normalizedHit)
570 {
571 normalizedHit = vector.Zero;
572
573 if (IsNearlyZero(hitPosition))
574 return false;
575
576 if (!g_Game.SurfaceIsSea(hitPosition[0], hitPosition[2]))
577 return false;
578
579 float seaLevel = g_Game.SurfaceGetSeaLevel();
580 float seaLevelMax = g_Game.SurfaceGetSeaLevelMax();
581
583 float hitToSeaHeight = Math.AbsFloat(hitPosition[1] - seaLevel);
584 if (hitToSeaHeight > SEA_HIT_RAW_Y_TOLERANCE)
585 return false;
586
587 if (hitPosition[1] > seaLevelMax + m_MaxVerticalReach)
588 return false;
589
590 normalizedHit = hitPosition;
591 normalizedHit[1] = seaLevel;
592
594 if (vector.Distance(playerPos, normalizedHit) > m_MaxActionDistance)
595 return false;
596
598 float playerToSeaHeight = Math.AbsFloat(playerPos[1] - seaLevel);
599 if (playerToSeaHeight > m_MaxVerticalReach)
600 return false;
601
602 return true;
603 }
604
605 protected bool TryBuildSyntheticCursor(PlayerBase player, vector playerPos, out vector syntheticHitPosition, out int syntheticLiquidType)
606 {
607 syntheticHitPosition = vector.Zero;
608 syntheticLiquidType = LIQUID_NONE;
609
611 if (g_Game.IsHeadlessOrDedicatedServer())
612 return false;
613
615 {
616 if (TryBuildSeaLevelHitPosition(player, playerPos, syntheticHitPosition))
617 {
618 syntheticLiquidType = LIQUID_SALTWATER;
619 return true;
620 }
621 }
622
624 {
625 if (TryBuildPondHitPosition(player, playerPos, syntheticHitPosition))
626 {
628 syntheticLiquidType = LIQUID_FRESHWATER;
629 return true;
630 }
631 }
632
633 return false;
634 }
635
636 protected bool TryResolveSyntheticCursor(PlayerBase player, vector playerPos, vector currentHitPosition, out vector resolvedHitPosition, out int resolvedLiquidType)
637 {
638 resolvedHitPosition = currentHitPosition;
639 resolvedLiquidType = LIQUID_NONE;
640
642 if (TryValidateHitCursor(player, playerPos, currentHitPosition, resolvedHitPosition, resolvedLiquidType))
643 return true;
644
646 if (currentHitPosition != vector.Zero)
647 {
649
650 #ifdef DIAG_DEVELOPER
651 if (m_DebugSyntheticCursor)
652 DebugDrawSyntheticCursor(player, currentHitPosition, vector.Zero, false, "current non-liquid hit prevents synthetic cursor", LIQUID_NONE);
653 else
654 ClearDebugCursorShapes();
655 #endif
656
657 return false;
658 }
659
661 if (g_Game.IsHeadlessOrDedicatedServer())
662 return false;
663
665 vector syntheticHitPosition;
666 int syntheticLiquidType;
667
668 if (!TryBuildSyntheticCursor(player, playerPos, syntheticHitPosition, syntheticLiquidType))
669 {
671 if (TryUseCachedSyntheticCursor(player, playerPos, currentHitPosition, resolvedHitPosition, resolvedLiquidType))
672 {
673 #ifdef DIAG_DEVELOPER
674 if (m_DebugSyntheticCursor)
675 DebugDrawSyntheticCursor(player, currentHitPosition, resolvedHitPosition, true, "accepted cached synthetic cursor", resolvedLiquidType);
676 else
677 ClearDebugCursorShapes();
678 #endif
679
680 return true;
681 }
682
684
685 #ifdef DIAG_DEVELOPER
686 if (m_DebugSyntheticCursor)
687 DebugDrawSyntheticCursor(player, currentHitPosition, syntheticHitPosition, false, "synthetic cursor failed", LIQUID_NONE);
688 else
689 ClearDebugCursorShapes();
690 #endif
691
692 return false;
693 }
694
696 if (!TryValidateHitCursor(player, playerPos, syntheticHitPosition, resolvedHitPosition, resolvedLiquidType))
697 {
699
700 #ifdef DIAG_DEVELOPER
701 if (m_DebugSyntheticCursor)
702 DebugDrawSyntheticCursor(player, currentHitPosition, syntheticHitPosition, false, "synthetic cursor invalid after build", syntheticLiquidType);
703 else
704 ClearDebugCursorShapes();
705 #endif
706
707 return false;
708 }
709
710 #ifdef DIAG_DEVELOPER
711 if (m_DebugSyntheticCursor)
712 DebugDrawSyntheticCursor(player, currentHitPosition, resolvedHitPosition, true, "accepted synthetic cursor", resolvedLiquidType);
713 else
714 ClearDebugCursorShapes();
715 #endif
716
717 return true;
718 }
719
720 protected bool TryValidatePondHitPosition(vector playerPos, vector hitPosition, out vector surfacePos, out int liquidType, out string surfaceType, int expectedLiquidType = LIQUID_NONE)
721 {
722 surfacePos = vector.Zero;
723 liquidType = LIQUID_NONE;
724 surfaceType = string.Empty;
725
726 if (IsNearlyZero(hitPosition))
727 return false;
728
729 if (vector.Distance(playerPos, hitPosition) > m_MaxActionDistance)
730 return false;
731
732 if (!g_Game.SurfaceIsPond(hitPosition[0], hitPosition[2]))
733 return false;
734
735 int detectedLiquidType;
736 if (!TryGetLiquidSurfaceAt(hitPosition, surfacePos, detectedLiquidType, surfaceType))
737 return false;
738
740 liquidType = detectedLiquidType;
741
743 if (expectedLiquidType != LIQUID_NONE && (liquidType & expectedLiquidType) == 0)
744 return false;
745
746 if (!IsAllowedPondLiquidSurface(playerPos, surfacePos, liquidType))
747 return false;
748
749 float hitToSurfaceHeight = Math.AbsFloat(hitPosition[1] - surfacePos[1]);
750 if (hitToSurfaceHeight > SYNTHETIC_POND_SURFACE_TOLERANCE)
751 return false;
752
753 return true;
754 }
755
762
763 protected vector ApplySyntheticHitCursorStability(vector playerPos, vector candidate, int liquidType)
764 {
765 int now = g_Game.GetTime();
767 {
768 int age = now - m_LastSyntheticHitCursorTime;
769
770 if (age <= SEA_CURSOR_STICK_TIME_MS)
771 {
773 float deltaSq = vector.DistanceSq(candidate, m_LastSyntheticHitCursor);
774
775 if (deltaSq <= stickDistSq)
776 {
777 if (liquidType == LIQUID_SALTWATER && IsSeaHitPositionValid(playerPos, m_LastSyntheticHitCursor))
778 {
781 }
782
783 if (liquidType != LIQUID_SALTWATER && IsValidPondHitPosition(playerPos, m_LastSyntheticHitCursor, liquidType))
784 {
787 }
788 }
789 }
790 }
791
792 m_LastSyntheticHitCursor = candidate;
795
796 return candidate;
797 }
798
799 protected bool TryValidateHitCursor(PlayerBase player, vector playerPos, vector hitPosition, out vector resolvedHitPosition, out int resolvedLiquidType)
800 {
801 resolvedHitPosition = hitPosition;
802 resolvedLiquidType = LIQUID_NONE;
803
804 if (IsNearlyZero(hitPosition))
805 return false;
806
809 {
810 vector normalizedSeaHit;
811 if (TryGetNormalizedSeaHitPoint(playerPos, hitPosition, normalizedSeaHit))
812 {
813 if (!HasClearPathToLiquidSurface(player, normalizedSeaHit))
814 return false;
815
816 if (!IsCursorActuallyOnLiquidSurface(hitPosition, normalizedSeaHit))
817 return false;
818
819 resolvedHitPosition = normalizedSeaHit;
820 resolvedLiquidType = LIQUID_SALTWATER;
821 return true;
822 }
823 }
824
827 {
828 vector surfacePos;
829 string surfaceType;
830 int detectedLiquidType;
831
832 if (TryValidatePondHitPosition(playerPos, hitPosition, surfacePos, detectedLiquidType, surfaceType))
833 {
834 if (!HasClearPathToLiquidSurface(player, surfacePos))
835 return false;
836
837 if (!IsCursorActuallyOnLiquidSurface(hitPosition, surfacePos))
838 return false;
839
840 resolvedHitPosition = surfacePos;
841 resolvedLiquidType = detectedLiquidType;
842 return true;
843 }
844 }
845
846 return false;
847 }
848
849 protected bool TryUseCachedSyntheticCursor(PlayerBase player, vector playerPos, vector currentHitPosition, out vector resolvedHitPosition, out int resolvedLiquidType)
850 {
851 resolvedHitPosition = vector.Zero;
852 resolvedLiquidType = LIQUID_NONE;
853
855 return false;
856
857 int now = g_Game.GetTime();
858 int age = now - m_LastSyntheticHitCursorTime;
859
862 return false;
863
866 return false;
867
868 if (TryValidateHitCursor(player, playerPos, m_LastSyntheticHitCursor, resolvedHitPosition, resolvedLiquidType))
869 {
871 return true;
872 }
873
874 return false;
875 }
876
877 protected bool IsPositionNearCurrentCameraRay(vector position, float maxPerpendicularDistance)
878 {
879 if (position == vector.Zero)
880 return false;
881
882 vector cameraPos = g_Game.GetCurrentCameraPosition();
883 vector cameraDir = g_Game.GetCurrentCameraDirection();
884
885 if (cameraPos == vector.Zero || cameraDir == vector.Zero)
886 return false;
887
888 cameraDir.Normalize();
889
890 vector cameraToPosition = position - cameraPos;
891 float projectedDistance = vector.Dot(cameraToPosition, cameraDir);
892
894 if (projectedDistance <= 0.0)
895 return false;
896
898
899 if (safeDistance <= 1.0)
900 safeDistance = m_MaxActionDistance * 0.8;
901
903 if (projectedDistance > safeDistance + maxPerpendicularDistance)
904 return false;
905
906 vector closestPointOnRay = cameraPos + cameraDir * projectedDistance;
907 float maxPerpendicularDistanceSq = maxPerpendicularDistance * maxPerpendicularDistance;
908
909 return vector.DistanceSq(position, closestPointOnRay) <= maxPerpendicularDistanceSq;
910 }
911
912 protected bool IsCachedSyntheticCursorRelevant(vector currentHitPosition, vector cachedHitPosition)
913 {
914 if (cachedHitPosition == vector.Zero)
915 return false;
916
918
920 if (currentHitPosition != vector.Zero)
921 {
922 return vector.DistanceSq(currentHitPosition, cachedHitPosition) <= aimToleranceSq;
923 }
924
927 }
928
929 protected bool IsAllowedPondLiquidSurface(vector playerPos, vector surfacePos, int liquidType)
930 {
931 if (liquidType == LIQUID_NONE || liquidType == LIQUID_SALTWATER)
932 return false;
933
934 if ((liquidType & m_AllowedLiquidSource) == 0)
935 return false;
936
937 if (!g_Game.SurfaceIsPond(surfacePos[0], surfacePos[2]))
938 return false;
939
940 if (vector.Distance(playerPos, surfacePos) > m_MaxActionDistance)
941 return false;
942
943 float playerToSurfaceHeight = Math.AbsFloat(playerPos[1] - surfacePos[1]);
944 if (playerToSurfaceHeight > m_MaxVerticalReach)
945 return false;
946
947 return true;
948 }
949
950 protected bool TryGetNonWaterSurfaceAt(vector probePosition, out vector surfacePosition)
951 {
952 surfacePosition = vector.Zero;
953
954 m_SurfaceDetectionParamsNoWater.position = probePosition;
955 m_SurfaceDetectionParamsNoWater.rsd = RoadSurfaceDetection.CLOSEST;
957 if (!g_Game.GetSurface(m_SurfaceDetectionParamsNoWater, surfaceResult))
958 return false;
959
960 if (!surfaceResult)
961 return false;
962
963 SurfaceInfo surfaceInfo = surfaceResult.surface;
964 if (!surfaceInfo)
965 return false;
966
967 string surfaceType = surfaceInfo.GetSurfaceType();
968 if (surfaceType == string.Empty)
969 return false;
970
971 int surfaceLiquid = surfaceInfo.GetLiquidType();
973 if (surfaceLiquid > LIQUID_NONE)
974 return false;
975
976 surfacePosition = probePosition;
977 surfacePosition[1] = surfaceResult.height;
978
979 return true;
980 }
981
982 protected bool IsCursorActuallyOnLiquidSurface(vector hitPosition, vector liquidSurfacePos)
983 {
984 if (IsNearlyZero(hitPosition))
985 return false;
986
987 if (IsNearlyZero(liquidSurfacePos))
988 return false;
989
990 if (hitPosition[1] >= liquidSurfacePos[1])
991 return false;
992
993 vector nonWaterSurfacePos;
994 if (TryGetNonWaterSurfaceAt(hitPosition, nonWaterSurfacePos))
995 {
997 if (nonWaterSurfacePos[1] > liquidSurfacePos[1])
998 return false;
999 }
1000
1001 return true;
1002 }
1003
1004 override bool CanContinue(PlayerBase player, ActionTarget target)
1005 {
1006 return Can(player, target);
1007 }
1008
1010 {
1011 return m_LiquidType;
1012 }
1013
1015 {
1016 return m_WaterSourceType;
1017 }
1018
1020 {
1021 return m_LiquidSourceType;
1022 }
1023
1025 {
1026 return vector.DistanceSq(value, vector.Zero) < 0.0001;
1027 }
1028
1029 #ifdef DIAG_DEVELOPER
1030 protected void ClearDebugCursorShapes()
1031 {
1032 if (!m_DebugSyntheticCursorShapes)
1033 return;
1034
1035 for (int i = 0; i < m_DebugSyntheticCursorShapes.Count(); i++)
1036 {
1037 Shape shape = m_DebugSyntheticCursorShapes[i];
1038 if (shape)
1039 Debug.RemoveShape(shape);
1040 }
1041
1042 m_DebugSyntheticCursorShapes.Clear();
1043 }
1044
1045 protected void DebugDrawSyntheticCursor(PlayerBase player, vector originalHitPosition, vector cursorHitPosition, bool accepted, string reason, int liquidType = LIQUID_NONE)
1046 {
1047 ClearDebugCursorShapes();
1048
1049 if (!player)
1050 return;
1051
1052 vector playerPos = player.GetPosition();
1053
1054 vector headPos;
1055 MiscGameplayFunctions.GetHeadBonePos(player, headPos);
1056
1057 int colorAccepted = ARGB(255, 0, 255, 64);
1058 int colorRejected = ARGB(255, 255, 32, 32);
1059 int colorLine = ARGB(255, 0, 180, 255);
1060 int colorOriginal = ARGB(255, 255, 255, 0);
1061
1062 int finalColor;
1063 if (accepted)
1064 finalColor = colorAccepted;
1065 else
1066 finalColor = colorRejected;
1067
1069 if (!IsNearlyZero(cursorHitPosition))
1070 {
1071 m_DebugSyntheticCursorShapes.Insert(Debug.DrawSphere(cursorHitPosition, 0.35, finalColor, ShapeFlags.NOZBUFFER));
1072 m_DebugSyntheticCursorShapes.Insert(Debug.DrawLine(headPos, cursorHitPosition, colorLine));
1073
1075 vector x1 = cursorHitPosition;
1076 vector x2 = cursorHitPosition;
1077 vector z1 = cursorHitPosition;
1078 vector z2 = cursorHitPosition;
1079
1080 x1[0] = x1[0] - 0.5;
1081 x2[0] = x2[0] + 0.5;
1082 z1[2] = z1[2] - 0.5;
1083 z2[2] = z2[2] + 0.5;
1084
1085 m_DebugSyntheticCursorShapes.Insert(Debug.DrawLine(x1, x2, finalColor));
1086 m_DebugSyntheticCursorShapes.Insert(Debug.DrawLine(z1, z2, finalColor));
1087 }
1088
1090 if (!IsNearlyZero(originalHitPosition))
1091 {
1092 m_DebugSyntheticCursorShapes.Insert(Debug.DrawSphere(originalHitPosition, 0.2, colorOriginal, ShapeFlags.NOZBUFFER));
1093 m_DebugSyntheticCursorShapes.Insert(Debug.DrawLine(playerPos, originalHitPosition, colorOriginal));
1094 }
1095
1096 float dist = 0.0;
1097 bool isSea = false;
1098 bool isPond = false;
1099
1100 if (!IsNearlyZero(cursorHitPosition))
1101 {
1102 dist = vector.Distance(playerPos, cursorHitPosition);
1103 isSea = g_Game.SurfaceIsSea(cursorHitPosition[0], cursorHitPosition[2]);
1104 isPond = g_Game.SurfaceIsPond(cursorHitPosition[0], cursorHitPosition[2]);
1105 }
1106
1107 DbgUI.Begin("", 30, 200);
1108 {
1109 DbgUI.Text(string.Format("CCTLiquid TryResolveSyntheticCursor: accepted=%1 | reason=%2 | liquid=%3 | player=%4 | originalHit=%5 | resolvedHit=%6 | dist=%7 | isSea=%8 | isPond=%9", accepted, reason, liquidType, playerPos.ToString(), originalHitPosition.ToString(), cursorHitPosition.ToString(), dist, isSea, isPond));
1110 }
1111 DbgUI.End();
1112 }
1113 #endif
1114}
void ActionTarget(Object object, Object parent, int componentIndex, vector cursorHitPos, float utility, string surfaceName="")
Определения ActionTargets.c:125
string Debug()
Определения CachedEquipmentStorageBase.c:26
DayZGame g_Game
Определения DayZGame.c:4074
vector position
Определения DustKickupEffects.c:20
DiagMenuIDs
Определения EDiagMenuIDs.c:2
ELiquidSourceObjectType
Определения ELiquidSourceObjectType.c:2
EWaterSourceObjectType
Определения EWaterSourceObjectType.c:2
Empty
Определения Hand_States.c:14
SurfaceDetectionType
Определения SurfaceInfo.c:86
UseObjectsMode
Определения SurfaceInfo.c:74
Определения CCTBase.c:2
int m_WaterSourceType
Определения CCTLiquid.c:28
int GetLiquidType()
Определения CCTLiquid.c:1009
bool HasClearPathToLiquidSurface(PlayerBase player, vector surfacePos)
Определения CCTLiquid.c:250
static const float HIT_POS_TOLERANCE_SQ
Определения CCTLiquid.c:4
bool TryBuildPondHitPosition(PlayerBase player, vector playerPos, out vector pondHitPosition)
Определения CCTLiquid.c:431
static const float SEA_CURSOR_STICK_DISTANCE
Определения CCTLiquid.c:10
bool IsCursorActuallyOnLiquidSurface(vector hitPosition, vector liquidSurfacePos)
Определения CCTLiquid.c:982
bool TryValidatePondHitPosition(vector playerPos, vector hitPosition, out vector surfacePos, out int liquidType, out string surfaceType, int expectedLiquidType=LIQUID_NONE)
Определения CCTLiquid.c:720
bool TryResolveSyntheticCursor(PlayerBase player, vector playerPos, vector currentHitPosition, out vector resolvedHitPosition, out int resolvedLiquidType)
Определения CCTLiquid.c:636
int GetWaterSourceType()
Определения CCTLiquid.c:1014
bool IsNearlyZero(vector value)
Определения CCTLiquid.c:1024
override bool CanContinue(PlayerBase player, ActionTarget target)
Определения CCTLiquid.c:1004
bool TryGetNonWaterSurfaceAt(vector probePosition, out vector surfacePosition)
Определения CCTLiquid.c:950
bool IsValidPondHitPosition(vector playerPos, vector hitPosition, int liquidType=LIQUID_NONE)
Определения CCTLiquid.c:560
int m_AllowedLiquidSource
Определения CCTLiquid.c:24
bool TryGetNormalizedSeaHitPoint(vector playerPos, vector hitPosition, out vector normalizedHit)
Определения CCTLiquid.c:569
int GetLiquidSourceType()
Определения CCTLiquid.c:1019
static const float HIT_POS_TOLERANCE
Определения CCTLiquid.c:3
bool TryBuildSeaLevelHitPosition(PlayerBase player, vector playerPos, out vector seaHitPosition)
Определения CCTLiquid.c:325
void CCTLiquid(int allowedLiquidSource, float maximalTargetDistance=UAMaxDistances.DEFAULT, float maxVerticalReach=UAMaxDistances.DEFAULT, SurfaceDetectionType surfaceDetectionType=SurfaceDetectionType.Roadway, bool allowSeaLevelCursorFallback=false)
Определения CCTLiquid.c:37
bool IsValidTargetObject(vector playerPos, vector objectPosition, EWaterSourceObjectType waterSourceType, ELiquidSourceObjectType liquidSourceType, bool isProxy)
Определения CCTLiquid.c:161
int m_LiquidType
Определения CCTLiquid.c:27
bool m_HasLastSyntheticHitCursor
Определения CCTLiquid.c:19
static const float SEA_CURSOR_MIN_DOWN_DOT
Определения CCTLiquid.c:15
bool CheckSeaLevelFallback(vector hitPosition, vector playerPos)
Определения CCTLiquid.c:283
float m_MaxVerticalReach
Определения CCTLiquid.c:22
vector ApplySyntheticHitCursorStability(vector playerPos, vector candidate, int liquidType)
Определения CCTLiquid.c:763
int m_LiquidSourceType
Определения CCTLiquid.c:29
static const float SYNTHETIC_CURSOR_CACHE_AIM_TOLERANCE
Определения CCTLiquid.c:12
void ClearSyntheticCursorCache()
Определения CCTLiquid.c:756
bool TryGetLiquidSurfaceAt(vector probePosition, out vector surfacePosition, out int liquidType, out string surfaceType)
Определения CCTLiquid.c:293
bool TryBuildSyntheticCursor(PlayerBase player, vector playerPos, out vector syntheticHitPosition, out int syntheticLiquidType)
Определения CCTLiquid.c:605
bool TryUseCachedSyntheticCursor(PlayerBase player, vector playerPos, vector currentHitPosition, out vector resolvedHitPosition, out int resolvedLiquidType)
Определения CCTLiquid.c:849
ref SurfaceDetectionParameters m_SurfaceDetectionParams
Определения CCTLiquid.c:25
bool IsCachedSyntheticCursorRelevant(vector currentHitPosition, vector cachedHitPosition)
Определения CCTLiquid.c:912
static const float SYNTHETIC_POND_FALLBACK_SURFACE_TOLERANCE
Определения CCTLiquid.c:8
static const float SYNTHETIC_CURSOR_DISTANCE_BUFFER
Определения CCTLiquid.c:9
vector m_LastSyntheticHitCursor
Определения CCTLiquid.c:18
bool IsAllowedPondLiquidSurface(vector playerPos, vector surfacePos, int liquidType)
Определения CCTLiquid.c:929
float m_MaxActionDistance
Определения CCTLiquid.c:23
static const int SEA_CURSOR_STICK_TIME_MS
Определения CCTLiquid.c:11
bool IsValidLiquid(int liquid)
Определения CCTLiquid.c:156
static const float SYNTHETIC_POND_SAMPLE_STEP
Определения CCTLiquid.c:6
int m_LastSyntheticHitCursorTime
Определения CCTLiquid.c:20
bool TryValidateHitCursor(PlayerBase player, vector playerPos, vector hitPosition, out vector resolvedHitPosition, out int resolvedLiquidType)
Определения CCTLiquid.c:799
bool IsSeaHitPositionValid(vector playerPos, vector hitPosition)
Определения CCTLiquid.c:422
bool IsPositionNearCurrentCameraRay(vector position, float maxPerpendicularDistance)
Определения CCTLiquid.c:877
static const float SEA_HIT_RAW_Y_TOLERANCE
Определения CCTLiquid.c:14
static const float LIQUID_SURFACE_RAYCAST_OFFSET
Определения CCTLiquid.c:16
override bool Can(PlayerBase player, ActionTarget target)
Определения CCTLiquid.c:66
bool m_AllowSeaLevelCursorFallback
Определения CCTLiquid.c:30
ref SurfaceDetectionParameters m_SurfaceDetectionParamsNoWater
Определения CCTLiquid.c:26
bool CheckSurfaceLiquid(PlayerBase player, Object targetObj, vector hitPosition, vector playerPos, bool isValidInteract, out string surfaceType)
Определения CCTLiquid.c:180
static const float SEA_FALLBACK_DISTANCE_BUFFER
Определения CCTLiquid.c:5
static const float SYNTHETIC_POND_SURFACE_TOLERANCE
Определения CCTLiquid.c:7
static const int SYNTHETIC_CURSOR_CACHE_MAX_AGE_MS
Определения CCTLiquid.c:13
static proto bool RaycastRVProxy(notnull RaycastRVParams in, out notnull array< ref RaycastRVResult > results, array< Object > excluded=null)
Определения DayZPhysics.c:124
Определения DbgUI.c:60
static void RemoveShape(out Shape shape)
Определения 3_Game/DayZ/tools/Debug.c:113
Определения 3_Game/DayZ/tools/Debug.c:2
Определения EnDebug.c:241
Определения EnMath.c:7
Определения ObjectTyped.c:2
Определения PlayerBaseClient.c:2
bool sorted
used only if groundOnly = false
Определения DayZPhysics.c:75
int type
Определения DayZPhysics.c:73
Object ignore
ignore this object in collision, used only if groundOnly is false
Определения DayZPhysics.c:54
CollisionFlags flags
Определения DayZPhysics.c:63
Определения DayZPhysics.c:50
Определения DayZPhysics.c:99
SurfaceInfo surface
Returned SurfaceInfo, plain pointer.
Определения SurfaceInfo.c:134
float height
Height position.
Определения SurfaceInfo.c:122
proto int GetLiquidType()
See 'LiquidTypes' in 'constants.c'.
proto string GetSurfaceType()
Определения SurfaceInfo.c:9
const float DEFAULT
Определения ActionConstants.c:114
const string LIQUID_SOURCE_SELECTION
Определения ActionConstants.c:180
Определения ActionConstants.c:176
Result for an object found in CGame.IsBoxCollidingGeometryProxy.
static proto native float DistanceSq(vector v1, vector v2)
Returns the square distance between tips of two 3D vectors.
static float Dot(vector v1, vector v2)
Returns Dot product of vector v1 and vector v2.
Определения EnConvert.c:305
static const vector Zero
Определения EnConvert.c:123
proto float Normalize()
Normalizes vector. Returns length.
static proto native float Distance(vector v1, vector v2)
Returns the distance between tips of two 3D vectors.
proto string ToString(bool beautify=true)
Vector to string.
Определения EnConvert.c:119
CollisionFlags
Определения EnDebug.c:141
ShapeFlags
Определения EnDebug.c:126
static proto native void End()
static proto native void Begin(string windowTitle, float x=0, float y=0)
static proto native void Text(string label)
static proto bool GetBool(int id, bool reverse=false)
Get value as bool from the given script id.
class DiagMenu Shape
don't call destructor directly. Use Destroy() instead
array< int > TIntArray
Определения EnScript.c:733
const int LIQUID_NONE
Определения 3_Game/DayZ/constants.c:566
const int LIQUID_FRESHWATER
Определения 3_Game/DayZ/constants.c:588
const int LIQUID_SALTWATER
Определения 3_Game/DayZ/constants.c:587
static proto float AbsFloat(float f)
Returns absolute value.
int ARGB(int a, int r, int g, int b)
Определения proto.c:322