#pragma once #include #include #include #include #include extern "C" { #include #include } namespace wherigo { // ── Unit conversion ─────────────────────────────────────────────────────────── inline double distanceToMeters(double value, const char *units) { if (!units || !strcmp(units,"meters") || !strcmp(units,"metres") || !strcmp(units,"m")) return value; if (!strcmp(units,"kilometers") || !strcmp(units,"kilometres") || !strcmp(units,"km")) return value * 1000.0; if (!strcmp(units,"feet") || !strcmp(units,"ft")) return value * 0.3048; if (!strcmp(units,"miles") || !strcmp(units,"mi")) return value * 1609.344; if (!strcmp(units,"nauticalmiles")) return value * 1852.0; return value; } inline double metersToUnit(double meters, const char *units) { if (!units || !strcmp(units,"meters") || !strcmp(units,"metres") || !strcmp(units,"m")) return meters; if (!strcmp(units,"kilometers") || !strcmp(units,"kilometres") || !strcmp(units,"km")) return meters / 1000.0; if (!strcmp(units,"feet") || !strcmp(units,"ft")) return meters / 0.3048; if (!strcmp(units,"miles") || !strcmp(units,"mi")) return meters / 1609.344; if (!strcmp(units,"nauticalmiles")) return meters / 1852.0; return meters; } // ── Geodesic calculations ───────────────────────────────────────────────────── // Haversine formula → {distance_meters, bearing_degrees [0,360)} inline std::pair haversine(double lat1, double lon1, double lat2, double lon2) { constexpr double R = 6371000.0; constexpr double D2R = M_PI / 180.0; const double rl1 = lat1*D2R, rl2 = lat2*D2R; const double dl = (lat2-lat1)*D2R, dl2 = (lon2-lon1)*D2R; const double a = std::sin(dl/2)*std::sin(dl/2) + std::cos(rl1)*std::cos(rl2)*std::sin(dl2/2)*std::sin(dl2/2); const double dist = R * 2.0 * std::asin(std::min(1.0, std::sqrt(a))); const double brg = std::atan2(std::sin(dl2)*std::cos(rl2), std::cos(rl1)*std::sin(rl2) - std::sin(rl1)*std::cos(rl2)*std::cos(dl2)); return {dist, std::fmod(brg*(180.0/M_PI)+360.0, 360.0)}; } // Destination point given start, distance (m), bearing (deg) inline std::pair translatePoint(double lat, double lon, double distMeters, double bearingDeg) { constexpr double R = 6371000.0; constexpr double D2R = M_PI / 180.0; constexpr double R2D = 180.0 / M_PI; const double d = distMeters / R; const double b = bearingDeg * D2R; const double r1 = lat * D2R; const double r2 = std::asin(std::sin(r1)*std::cos(d) + std::cos(r1)*std::sin(d)*std::cos(b)); const double dl = std::atan2(std::sin(b)*std::sin(d)*std::cos(r1), std::cos(d) - std::sin(r1)*std::sin(r2)); return {r2*R2D, lon + dl*R2D}; } // Ray-casting point-in-polygon. pts = {lat, lon} pairs. inline bool pointInPolygon(double playerLat, double playerLon, const std::vector>& pts) { const int n = static_cast(pts.size()); if (n < 3) return false; bool inside = false; for (int i = 0, j = n-1; i < n; j = i++) { const double lati = pts[i].first, loni = pts[i].second; const double latj = pts[j].first, lonj = pts[j].second; if (((lati > playerLat) != (latj > playerLat)) && (playerLon < (lonj-loni)*(playerLat-lati)/(latj-lati) + loni)) inside = !inside; } return inside; } // Distance (m) from point to nearest point on segment; optionally returns that point inline double distToSegment(double plat, double plon, double lat1, double lon1, double lat2, double lon2, double *nearLatOut = nullptr, double *nearLonOut = nullptr) { const double dlat = lat2-lat1, dlon = lon2-lon1; const double len2 = dlat*dlat + dlon*dlon; const double t = (len2 > 1e-12) ? std::max(0.0, std::min(1.0, ((plat-lat1)*dlat + (plon-lon1)*dlon) / len2)) : 0.0; const double nLat = lat1 + t*dlat, nLon = lon1 + t*dlon; if (nearLatOut) *nearLatOut = nLat; if (nearLonOut) *nearLonOut = nLon; return haversine(plat, plon, nLat, nLon).first; } // Nearest edge of zone polygon → {distance_m, bearing_deg} inline std::pair nearestZoneEdge( double plat, double plon, const std::vector>& pts) { double minDist = std::numeric_limits::max(), minBrg = 0.0; const int n = static_cast(pts.size()); for (int i = 0, j = n-1; i < n; j = i++) { double nLat, nLon; const double d = distToSegment(plat, plon, pts[j].first, pts[j].second, pts[i].first, pts[i].second, &nLat, &nLon); if (d < minDist) { minDist = d; minBrg = haversine(plat, plon, nLat, nLon).second; } } return {minDist, minBrg}; } // ── Lua table helpers ───────────────────────────────────────────────────────── // Push a Distance object (value in meters) with __call metatable inline void pushDistance(lua_State *L, double meters) { lua_newtable(L); lua_pushnumber(L, meters); lua_setfield(L, -2, "value"); lua_pushstring(L, "Distance"); lua_setfield(L, -2, "_classname"); luaL_getmetatable(L, "Wherigo.Distance"); if (lua_istable(L, -1)) lua_setmetatable(L, -2); else lua_pop(L, 1); } // Push a Bearing object (value in degrees [0,360)) with __call metatable inline void pushBearing(lua_State *L, double degrees) { lua_newtable(L); lua_pushnumber(L, std::fmod(degrees+360.0, 360.0)); lua_setfield(L, -2, "value"); lua_pushstring(L, "Bearing"); lua_setfield(L, -2, "_classname"); luaL_getmetatable(L, "Wherigo.Bearing"); if (lua_istable(L, -1)) lua_setmetatable(L, -2); else lua_pop(L, 1); } // Read zone Points array from Lua table at absolute stack index inline std::vector> readZonePoints(lua_State *L, int zoneIdx) { std::vector> pts; lua_getfield(L, zoneIdx, "Points"); if (!lua_istable(L, -1)) { lua_pop(L, 1); return pts; } const int n = static_cast(lua_objlen(L, -1)); pts.reserve(n); for (int i = 1; i <= n; ++i) { lua_rawgeti(L, -1, i); if (lua_istable(L, -1)) { lua_getfield(L, -1, "latitude"); double lat = lua_tonumber(L,-1); lua_pop(L,1); lua_getfield(L, -1, "longitude"); double lon = lua_tonumber(L,-1); lua_pop(L,1); pts.push_back({lat, lon}); } lua_pop(L, 1); } lua_pop(L, 1); return pts; } } // namespace wherigo