33 protected bool m_DebugSyntheticCursor;
75 vector playerPos = player.GetPosition();
76 Object targetObj = target.GetObject();
78 bool isValidInteract =
false;
86 if (targetObj.IsWell())
91 if (componentIndices.Count() > 0)
92 isValidInteract = componentIndices.Find(target.GetComponentIndex()) != -1;
94 isValidInteract =
true;
98 objectPosition = targetObj.GetPosition();
103 vector hitPosition = target.GetCursorHitPos();
105 #ifdef DIAG_DEVELOPER
113 vector resolvedHitPosition;
114 int resolvedLiquidType;
118 hitPosition = resolvedHitPosition;
119 target.SetCursorHitPos(hitPosition);
126 if (isValidInteract && targetObj)
133 if (
CheckSurfaceLiquid(player, targetObj, hitPosition, playerPos, isValidInteract, surfaceType))
142 #ifdef DIAG_DEVELOPER
144 if (m_DebugSyntheticCursor)
146 DbgUI.
Text(
string.Format(
"CCTLiquid CheckSeaLevelFallback rejected: reason=could not create sea hit point | hitPosition=%1 | playerPos=%2", hitPosition, playerPos));
176 float heightDiff =
Math.
AbsFloat(objectPosition[1] - playerPos[1]);
182 surfaceType =
string.Empty;
205 if (surfaceType ==
string.
Empty)
208 float surfaceHeight = surfaceResult.
height;
209 float heightDiffSurface =
Math.
AbsFloat(hitPosition[1] - surfaceHeight);
210 float playerToSurfaceHeight =
Math.
AbsFloat(playerPos[1] - surfaceHeight);
216 vector liquidSurfacePos = hitPosition;
217 liquidSurfacePos[1] = surfaceHeight;
223 vector surfacePos = liquidSurfacePos;
231 if (heightDiffSurface > 0.25)
253 MiscGameplayFunctions.GetHeadBonePos(player, rayStart);
256 rayInput.
type = ObjIntersectFire;
270 Object hitObj = result.obj;
276 if (hitDist < surfaceDist)
297 surfaceType =
string.Empty;
312 if (surfaceType ==
string.
Empty)
319 surfacePosition = probePosition;
320 surfacePosition[1] = surfaceResult.
height;
330 if (
g_Game.IsHeadlessOrDedicatedServer())
333 float seaLevel =
g_Game.SurfaceGetSeaLevel();
334 float seaLevelMax =
g_Game.SurfaceGetSeaLevelMax();
348 if (safeDistance <= 1.0)
352 float cameraToSeaHeight = cameraPos[1] - seaLevel;
353 if (cameraToSeaHeight <= 0.0)
358 float maxAllowedCameraDirY = -minStableDownDot;
360 if (cameraDir[1] > maxAllowedCameraDirY)
362 #ifdef DIAG_DEVELOPER
364 if (m_DebugSyntheticCursor)
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));
375 float t = (seaLevel - cameraPos[1]) / cameraDir[1];
379 vector candidate = cameraPos + cameraDir * t;
380 candidate[1] = seaLevel;
382 float safeDistanceSq = safeDistance * safeDistance;
387 #ifdef DIAG_DEVELOPER
389 if (m_DebugSyntheticCursor)
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));
401 vector seaSurfacePos = candidate;
412 vector cachedOrStableSeaSurfacePos = candidate;
418 seaHitPosition = candidate;
433 #ifdef DIAG_DEVELOPER
434 float debugBestDelta = 999999.0;
435 int debugSampleCount = 0;
436 int debugValidKnownSurfaceCount = 0;
437 string debugPondFailReason =
"no pond samples";
443 if (
g_Game.IsHeadlessOrDedicatedServer())
455 if (cameraDir[1] > 0.15)
459 if (safeDistance <= 1.0)
464 float bestDelta = 999999.0;
469 #ifdef DIAG_DEVELOPER
473 vector probePos = cameraPos + cameraDir * dist;
481 #ifdef DIAG_DEVELOPER
482 debugPondFailReason =
"TryGetLiquidSurfaceAt failed";
489 #ifdef DIAG_DEVELOPER
490 debugPondFailReason =
"known pond surface invalid";
495 #ifdef DIAG_DEVELOPER
496 debugValidKnownSurfaceCount++;
499 float rayToSurfaceDelta =
Math.
AbsFloat(probePos[1] - surfacePos[1]);
500 if (rayToSurfaceDelta < bestDelta)
502 bestDelta = rayToSurfaceDelta;
503 bestSurfacePos = surfacePos;
504 bestLiquidType = liquidType;
506 #ifdef DIAG_DEVELOPER
507 debugBestDelta = bestDelta;
508 debugPondFailReason =
"best pond surface too far from camera ray";
520 pondSurfacePos = surfacePos;
526 pondHitPosition = surfacePos;
537 pondSurfacePos = bestSurfacePos;
543 pondHitPosition = bestSurfacePos;
548 #ifdef DIAG_DEVELOPER
550 if (m_DebugSyntheticCursor)
564 int validatedLiquidType;
576 if (!
g_Game.SurfaceIsSea(hitPosition[0], hitPosition[2]))
579 float seaLevel =
g_Game.SurfaceGetSeaLevel();
580 float seaLevelMax =
g_Game.SurfaceGetSeaLevelMax();
583 float hitToSeaHeight =
Math.
AbsFloat(hitPosition[1] - seaLevel);
590 normalizedHit = hitPosition;
591 normalizedHit[1] = seaLevel;
598 float playerToSeaHeight =
Math.
AbsFloat(playerPos[1] - seaLevel);
611 if (
g_Game.IsHeadlessOrDedicatedServer())
638 resolvedHitPosition = currentHitPosition;
642 if (
TryValidateHitCursor(player, playerPos, currentHitPosition, resolvedHitPosition, resolvedLiquidType))
650 #ifdef DIAG_DEVELOPER
651 if (m_DebugSyntheticCursor)
652 DebugDrawSyntheticCursor(player, currentHitPosition,
vector.
Zero,
false,
"current non-liquid hit prevents synthetic cursor",
LIQUID_NONE);
654 ClearDebugCursorShapes();
661 if (
g_Game.IsHeadlessOrDedicatedServer())
665 vector syntheticHitPosition;
666 int syntheticLiquidType;
673 #ifdef DIAG_DEVELOPER
674 if (m_DebugSyntheticCursor)
675 DebugDrawSyntheticCursor(player, currentHitPosition, resolvedHitPosition,
true,
"accepted cached synthetic cursor", resolvedLiquidType);
677 ClearDebugCursorShapes();
685 #ifdef DIAG_DEVELOPER
686 if (m_DebugSyntheticCursor)
687 DebugDrawSyntheticCursor(player, currentHitPosition, syntheticHitPosition,
false,
"synthetic cursor failed",
LIQUID_NONE);
689 ClearDebugCursorShapes();
696 if (!
TryValidateHitCursor(player, playerPos, syntheticHitPosition, resolvedHitPosition, resolvedLiquidType))
700 #ifdef DIAG_DEVELOPER
701 if (m_DebugSyntheticCursor)
702 DebugDrawSyntheticCursor(player, currentHitPosition, syntheticHitPosition,
false,
"synthetic cursor invalid after build", syntheticLiquidType);
704 ClearDebugCursorShapes();
710 #ifdef DIAG_DEVELOPER
711 if (m_DebugSyntheticCursor)
712 DebugDrawSyntheticCursor(player, currentHitPosition, resolvedHitPosition,
true,
"accepted synthetic cursor", resolvedLiquidType);
714 ClearDebugCursorShapes();
724 surfaceType =
string.Empty;
732 if (!
g_Game.SurfaceIsPond(hitPosition[0], hitPosition[2]))
735 int detectedLiquidType;
740 liquidType = detectedLiquidType;
743 if (expectedLiquidType !=
LIQUID_NONE && (liquidType & expectedLiquidType) == 0)
749 float hitToSurfaceHeight =
Math.
AbsFloat(hitPosition[1] - surfacePos[1]);
765 int now =
g_Game.GetTime();
775 if (deltaSq <= stickDistSq)
801 resolvedHitPosition = hitPosition;
819 resolvedHitPosition = normalizedSeaHit;
830 int detectedLiquidType;
840 resolvedHitPosition = surfacePos;
841 resolvedLiquidType = detectedLiquidType;
857 int now =
g_Game.GetTime();
891 float projectedDistance =
vector.
Dot(cameraToPosition, cameraDir);
894 if (projectedDistance <= 0.0)
899 if (safeDistance <= 1.0)
903 if (projectedDistance > safeDistance + maxPerpendicularDistance)
906 vector closestPointOnRay = cameraPos + cameraDir * projectedDistance;
907 float maxPerpendicularDistanceSq = maxPerpendicularDistance * maxPerpendicularDistance;
922 return vector.
DistanceSq(currentHitPosition, cachedHitPosition) <= aimToleranceSq;
937 if (!
g_Game.SurfaceIsPond(surfacePos[0], surfacePos[2]))
943 float playerToSurfaceHeight =
Math.
AbsFloat(playerPos[1] - surfacePos[1]);
968 if (surfaceType ==
string.
Empty)
976 surfacePosition = probePosition;
977 surfacePosition[1] = surfaceResult.
height;
990 if (hitPosition[1] >= liquidSurfacePos[1])
993 vector nonWaterSurfacePos;
997 if (nonWaterSurfacePos[1] > liquidSurfacePos[1])
1006 return Can(player, target);
1029 #ifdef DIAG_DEVELOPER
1030 protected void ClearDebugCursorShapes()
1032 if (!m_DebugSyntheticCursorShapes)
1035 for (
int i = 0; i < m_DebugSyntheticCursorShapes.Count(); i++)
1037 Shape shape = m_DebugSyntheticCursorShapes[i];
1042 m_DebugSyntheticCursorShapes.Clear();
1045 protected void DebugDrawSyntheticCursor(PlayerBase player, vector originalHitPosition, vector cursorHitPosition,
bool accepted,
string reason,
int liquidType =
LIQUID_NONE)
1047 ClearDebugCursorShapes();
1052 vector playerPos = player.GetPosition();
1055 MiscGameplayFunctions.GetHeadBonePos(player, headPos);
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);
1064 finalColor = colorAccepted;
1066 finalColor = colorRejected;
1071 m_DebugSyntheticCursorShapes.Insert(
Debug.DrawSphere(cursorHitPosition, 0.35, finalColor,
ShapeFlags.NOZBUFFER));
1072 m_DebugSyntheticCursorShapes.Insert(
Debug.DrawLine(headPos, cursorHitPosition, colorLine));
1075 vector x1 = cursorHitPosition;
1076 vector x2 = cursorHitPosition;
1077 vector z1 = cursorHitPosition;
1078 vector z2 = cursorHitPosition;
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;
1085 m_DebugSyntheticCursorShapes.Insert(
Debug.DrawLine(x1, x2, finalColor));
1086 m_DebugSyntheticCursorShapes.Insert(
Debug.DrawLine(z1, z2, finalColor));
1092 m_DebugSyntheticCursorShapes.Insert(
Debug.DrawSphere(originalHitPosition, 0.2, colorOriginal,
ShapeFlags.NOZBUFFER));
1093 m_DebugSyntheticCursorShapes.Insert(
Debug.DrawLine(playerPos, originalHitPosition, colorOriginal));
1098 bool isPond =
false;
1102 dist = vector.Distance(playerPos, cursorHitPosition);
1103 isSea =
g_Game.SurfaceIsSea(cursorHitPosition[0], cursorHitPosition[2]);
1104 isPond =
g_Game.SurfaceIsPond(cursorHitPosition[0], cursorHitPosition[2]);
1107 DbgUI.Begin(
"", 30, 200);
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));
void ActionTarget(Object object, Object parent, int componentIndex, vector cursorHitPos, float utility, string surfaceName="")
bool HasClearPathToLiquidSurface(PlayerBase player, vector surfacePos)
static const float HIT_POS_TOLERANCE_SQ
bool TryBuildPondHitPosition(PlayerBase player, vector playerPos, out vector pondHitPosition)
static const float SEA_CURSOR_STICK_DISTANCE
bool IsCursorActuallyOnLiquidSurface(vector hitPosition, vector liquidSurfacePos)
bool TryValidatePondHitPosition(vector playerPos, vector hitPosition, out vector surfacePos, out int liquidType, out string surfaceType, int expectedLiquidType=LIQUID_NONE)
bool TryResolveSyntheticCursor(PlayerBase player, vector playerPos, vector currentHitPosition, out vector resolvedHitPosition, out int resolvedLiquidType)
bool IsNearlyZero(vector value)
override bool CanContinue(PlayerBase player, ActionTarget target)
bool TryGetNonWaterSurfaceAt(vector probePosition, out vector surfacePosition)
bool IsValidPondHitPosition(vector playerPos, vector hitPosition, int liquidType=LIQUID_NONE)
int m_AllowedLiquidSource
bool TryGetNormalizedSeaHitPoint(vector playerPos, vector hitPosition, out vector normalizedHit)
int GetLiquidSourceType()
static const float HIT_POS_TOLERANCE
bool TryBuildSeaLevelHitPosition(PlayerBase player, vector playerPos, out vector seaHitPosition)
void CCTLiquid(int allowedLiquidSource, float maximalTargetDistance=UAMaxDistances.DEFAULT, float maxVerticalReach=UAMaxDistances.DEFAULT, SurfaceDetectionType surfaceDetectionType=SurfaceDetectionType.Roadway, bool allowSeaLevelCursorFallback=false)
bool IsValidTargetObject(vector playerPos, vector objectPosition, EWaterSourceObjectType waterSourceType, ELiquidSourceObjectType liquidSourceType, bool isProxy)
bool m_HasLastSyntheticHitCursor
static const float SEA_CURSOR_MIN_DOWN_DOT
bool CheckSeaLevelFallback(vector hitPosition, vector playerPos)
vector ApplySyntheticHitCursorStability(vector playerPos, vector candidate, int liquidType)
static const float SYNTHETIC_CURSOR_CACHE_AIM_TOLERANCE
void ClearSyntheticCursorCache()
bool TryGetLiquidSurfaceAt(vector probePosition, out vector surfacePosition, out int liquidType, out string surfaceType)
bool TryBuildSyntheticCursor(PlayerBase player, vector playerPos, out vector syntheticHitPosition, out int syntheticLiquidType)
bool TryUseCachedSyntheticCursor(PlayerBase player, vector playerPos, vector currentHitPosition, out vector resolvedHitPosition, out int resolvedLiquidType)
ref SurfaceDetectionParameters m_SurfaceDetectionParams
bool IsCachedSyntheticCursorRelevant(vector currentHitPosition, vector cachedHitPosition)
static const float SYNTHETIC_POND_FALLBACK_SURFACE_TOLERANCE
static const float SYNTHETIC_CURSOR_DISTANCE_BUFFER
vector m_LastSyntheticHitCursor
bool IsAllowedPondLiquidSurface(vector playerPos, vector surfacePos, int liquidType)
float m_MaxActionDistance
static const int SEA_CURSOR_STICK_TIME_MS
bool IsValidLiquid(int liquid)
static const float SYNTHETIC_POND_SAMPLE_STEP
int m_LastSyntheticHitCursorTime
bool TryValidateHitCursor(PlayerBase player, vector playerPos, vector hitPosition, out vector resolvedHitPosition, out int resolvedLiquidType)
bool IsSeaHitPositionValid(vector playerPos, vector hitPosition)
bool IsPositionNearCurrentCameraRay(vector position, float maxPerpendicularDistance)
static const float SEA_HIT_RAW_Y_TOLERANCE
static const float LIQUID_SURFACE_RAYCAST_OFFSET
override bool Can(PlayerBase player, ActionTarget target)
bool m_AllowSeaLevelCursorFallback
ref SurfaceDetectionParameters m_SurfaceDetectionParamsNoWater
bool CheckSurfaceLiquid(PlayerBase player, Object targetObj, vector hitPosition, vector playerPos, bool isValidInteract, out string surfaceType)
static const float SEA_FALLBACK_DISTANCE_BUFFER
static const float SYNTHETIC_POND_SURFACE_TOLERANCE
static const int SYNTHETIC_CURSOR_CACHE_MAX_AGE_MS
static proto bool RaycastRVProxy(notnull RaycastRVParams in, out notnull array< ref RaycastRVResult > results, array< Object > excluded=null)
static void RemoveShape(out Shape shape)
bool sorted
used only if groundOnly = false
Object ignore
ignore this object in collision, used only if groundOnly is false
SurfaceInfo surface
Returned SurfaceInfo, plain pointer.
float height
Height position.
proto int GetLiquidType()
See 'LiquidTypes' in 'constants.c'.
proto string GetSurfaceType()
const string LIQUID_SOURCE_SELECTION
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.
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.
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)
const int LIQUID_FRESHWATER
const int LIQUID_SALTWATER
static proto float AbsFloat(float f)
Returns absolute value.
int ARGB(int a, int r, int g, int b)