Server validates the synced cursor position. It should not invent one from camera data.
Dynamic minimum downward angle. If the ray is too close to horizontal, tiny camera sway causes huge sea cursor jumps.
Only reject near-horizontal aim. Do not scale this directly with height, because the real intersection check below decides whether the sea-plane hit is within range.
Real intersection of camera ray with sea plane.
If the real cursor/sea intersection is beyond allowed range, do not clamp a closer fake point.
Apply small deadband/sticky cache.
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
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
346
348 if (safeDistance <= 1.0)
350
352 float cameraToSeaHeight = cameraPos[1] - seaLevel;
353 if (cameraToSeaHeight <= 0.0)
354 return false;
355
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
399 return false;
400
401 vector seaSurfacePos = candidate;
403
405 return false;
406
410 return false;
411
412 vector cachedOrStableSeaSurfacePos = candidate;
414
416 return false;
417
418 seaHitPosition = candidate;
419 return true;
420 }
bool HasClearPathToLiquidSurface(PlayerBase player, vector surfacePos)
static const float SEA_CURSOR_MIN_DOWN_DOT
vector ApplySyntheticHitCursorStability(vector playerPos, vector candidate, int liquidType)
float m_MaxActionDistance
bool IsSeaHitPositionValid(vector playerPos, vector hitPosition)
static const float LIQUID_SURFACE_RAYCAST_OFFSET
static const float SEA_FALLBACK_DISTANCE_BUFFER
proto float Normalize()
Normalizes vector. Returns length.
proto string ToString(bool beautify=true)
Vector to string.
const int LIQUID_SALTWATER