diff --git a/vendor/clouds3d.js b/vendor/clouds3d.js index a351a7f..1f88c75 100644 --- a/vendor/clouds3d.js +++ b/vendor/clouds3d.js @@ -3533,19 +3533,9 @@ function nearestEmergencyCity() { return best; } -function selectEmergencyRoutes() { - emergencyRoutes = []; - emergencyVehicles = []; - emergencyCount = 0; - emergencyLastT = null; - if (!EMERGENCY_ON || !carGeo || !carGeo.secondary || !carGeo.secondary.cnts || !carGeo.secondary.cnts.length) { - window.__emergencyDbg = { enabled: EMERGENCY_ON, startCity: null, routes: 0 }; - return; - } - emergencyStartCity = nearestEmergencyCity(); - var sec = carGeo.secondary; - var targetKm = (EMERGENCY_MIN_KM + EMERGENCY_MAX_KM) / 2; - var candidates = []; +function emergencyCollectCandidates(sec, startCity, minKm, maxKm, nearLimit) { + var targetKm = (minKm + maxKm) / 2; + var out = []; var k = 0; for (var ci = 0; ci < sec.cnts.length; ci++) { var n = sec.cnts[ci]; @@ -3555,11 +3545,11 @@ function selectEmergencyRoutes() { var lastX = sec.pts[last], lastZ = sec.pts[last + 1], lastD = sec.pts[last + 2]; var len = Math.max(0, lastD - firstD); var lenKm = len * KM_PER_TILE; - var dStart = Math.hypot(firstX - emergencyStartCity.x, firstZ - emergencyStartCity.z); - var dEnd = Math.hypot(lastX - emergencyStartCity.x, lastZ - emergencyStartCity.z); + var dStart = Math.hypot(firstX - startCity.x, firstZ - startCity.z); + var dEnd = Math.hypot(lastX - startCity.x, lastZ - startCity.z); var near = Math.min(dStart, dEnd); - if (near < 1.2 && lenKm >= EMERGENCY_MIN_KM * 0.55 && lenKm <= EMERGENCY_MAX_KM * 1.45) { - candidates.push({ + if (near <= nearLimit && lenKm >= minKm && lenKm <= maxKm) { + out.push({ ci: ci, len: len, lenKm: lenKm, near: near, score: Math.abs(lenKm - targetKm) + near * 18, reverse: dEnd < dStart @@ -3567,35 +3557,58 @@ function selectEmergencyRoutes() { } k += n * 3; } - candidates.sort(function (a, b) { return a.score - b.score; }); - if (!candidates.length) { - var best = null, k2 = 0; - for (var ci2 = 0; ci2 < sec.cnts.length; ci2++) { - var n2 = sec.cnts[ci2]; - if (n2 < 2) { k2 += n2 * 3; continue; } - var f2 = k2, l2 = k2 + (n2 - 1) * 3; - var len2 = Math.max(0, sec.pts[l2 + 2] - sec.pts[f2 + 2]); - var near2 = Math.min( - Math.hypot(sec.pts[f2] - emergencyStartCity.x, sec.pts[f2 + 1] - emergencyStartCity.z), - Math.hypot(sec.pts[l2] - emergencyStartCity.x, sec.pts[l2 + 1] - emergencyStartCity.z)); - if (near2 < 2.5 && (!best || len2 > best.len)) best = { ci: ci2, len: len2, lenKm: len2 * KM_PER_TILE, near: near2, reverse: false }; - k2 += n2 * 3; - } - if (best) candidates.push(best); + return out; +} + +function emergencyShuffle(arr) { + for (var i = arr.length - 1; i > 0; i--) { + var j = Math.floor(Math.random() * (i + 1)); + var t = arr[i]; arr[i] = arr[j]; arr[j] = t; } - for (var r = 0; r < Math.min(EMERGENCY_COUNT, candidates.length); r++) { - var route = emergencyChainAt(sec, candidates[r].ci); - if (candidates[r].reverse) route.reverse(); + return arr; +} + +function selectEmergencyRoutes() { + emergencyRoutes = []; + emergencyVehicles = []; + emergencyCount = 0; + emergencyLastT = null; + if (!EMERGENCY_ON || !carGeo || !carGeo.secondary || !carGeo.secondary.cnts || !carGeo.secondary.cnts.length) { + window.__emergencyDbg = { enabled: EMERGENCY_ON, startCity: null, candidates: 0, routes: 0 }; + return; + } + emergencyStartCity = nearestEmergencyCity(); + var sec = carGeo.secondary; + var pool = emergencyCollectCandidates(sec, emergencyStartCity, EMERGENCY_MIN_KM, EMERGENCY_MAX_KM, 1.2); + var poolMode = 'strict'; + if (!pool.length) { + pool = emergencyCollectCandidates(sec, emergencyStartCity, EMERGENCY_MIN_KM * 0.55, EMERGENCY_MAX_KM * 1.45, 1.8); + poolMode = 'relaxed'; + } + if (!pool.length) { + pool = emergencyCollectCandidates(sec, emergencyStartCity, 10, 160, 3.0); + poolMode = 'fallback'; + } + pool.sort(function (a, b) { return a.score - b.score; }); + var top = pool.slice(0, Math.max(EMERGENCY_COUNT * 8, 24)); + emergencyShuffle(top); + var used = {}; + for (var r = 0; r < top.length && emergencyRoutes.length < EMERGENCY_COUNT; r++) { + var cand = top[r]; + if (used[cand.ci]) continue; + used[cand.ci] = true; + var route = emergencyChainAt(sec, cand.ci); + if (cand.reverse) route.reverse(); var len = route.length ? route[route.length - 1].d - route[0].d : 0; if (len <= 0) continue; - emergencyRoutes.push({ pts: route, len: len, lenKm: len * KM_PER_TILE, nearKm: candidates[r].near * KM_PER_TILE }); + emergencyRoutes.push({ pts: route, len: len, lenKm: len * KM_PER_TILE, nearKm: cand.near * KM_PER_TILE }); } for (var v = 0; v < emergencyRoutes.length; v++) { emergencyVehicles.push({ route: v, - d: emergencyRoutes[v].len * (0.08 + 0.72 * ((v * 0.371) % 1)), - dir: ((v % 2) === 0) ? 1 : -1, - seed: (v * 0.6180339887) % 1 + d: emergencyRoutes[v].len * (0.05 + 0.85 * Math.random()), + dir: Math.random() < 0.5 ? 1 : -1, + seed: Math.random() }); } emergencyCount = emergencyVehicles.length; @@ -3608,6 +3621,8 @@ function selectEmergencyRoutes() { pop: emergencyStartCity.pop, km: +(emergencyStartCity.dist * KM_PER_TILE).toFixed(1) } : null, + poolMode: poolMode, + candidates: pool.length, routes: emergencyRoutes.map(function (r) { return { km: +r.lenKm.toFixed(1), nearKm: +r.nearKm.toFixed(1) }; }) }; }