Skip to content

Instantly share code, notes, and snippets.

@Zorono
Created October 14, 2022 17:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Zorono/abd58a74831317f33601ae0d2b837a57 to your computer and use it in GitHub Desktop.
Save Zorono/abd58a74831317f33601ae0d2b837a57 to your computer and use it in GitHub Desktop.
Simple GPS Route using player gang zones
#if defined __INC_GPS_ROUTE_
#endinput
#else
#define __INC_GPS_ROUTE_
#endif
/*
GPS Route Include by NaS
Shows a route on the radar from the player's location to the specified destination using per-player gang zones.
Multiple routes are also supported, but this limits the lengths of each route.
It is compatible with the RouteConnector Plugin and Modern GPS Plugin.
Include whichever you want to use first.
Credits:
iAmir/AmyrAhmady - samp-player-gangzones Include
https://github.com/AmyrAhmady/samp-player-gangzones
urShadow - Pawn.RakNet Plugin
https://github.com/urShadow/Pawn.RakNet
Gamer_Z - GPS Plugin
kristoisberg - Modern GPS Plugin
Example: https://gist.github.com/Naseband/d32eb5bb66280dfb8464589fd6841121
*/
#if 0
#pragma option -r
#pragma option -d3
#define GPSR_DEBUG
#endif
#if defined _gps_included
//#warning gpsroute.inc: is using Modern GPS Plugin
#else
#if defined ROUTE_CONNECTOR_PLUGIN_INCLUDED
//#warning gpsroute.inc: is using RouteConnector Plugin
#else
#warning gpsroute.inc: You must include the GPS or RouteConnector include
#endif
#endif
#define MAX_PLAYER_GZ 1024
#include <player_gzs>
#define ROUTE_ZONE_SIZE 25.0
#define MAX_GPS_ROUTE_LEN 1000
#define MAX_PLAYER_GPS_ROUTES 2
#define MAX_PLAYER_GPS_ZONES MAX_PLAYER_GZ
#define GPS_ROUTEID_OFFSET 100000 // This is used to avoid conflicts with the routeid in GPS_WhenRouteIsCalculated
#define GPSR_UPDATE_INTERVAL 1250
#define MAX_GPS_ROUTE_ZONES (MAX_PLAYER_GPS_ZONES / MAX_PLAYER_GPS_ROUTES)
enum (+=1)
{
ROUTE_CALC_NONE,
ROUTE_CALC_INIT,
ROUTE_CALC_UPDATE
};
enum E_PLAYER_ROUTE
{
bool:routeActivated,
routeCalcState,
routeExtra,
bool:routeUpdate,
bool:routeInitial,
bool:routeLandVehicles,
routeTimerID,
routePos,
routeLen,
routeNumZones,
routeNode1,
routeNode2,
Float:routeTargetX,
Float:routeTargetY,
Float:routeTargetZ,
Float:routeTargetDistance,
routeColor
};
new gPlayerRouteData[MAX_PLAYERS][MAX_PLAYER_GPS_ROUTES][E_PLAYER_ROUTE], gDefPlayerRouteData[E_PLAYER_ROUTE];
new gPlayerGangZones[MAX_PLAYERS][MAX_PLAYER_GPS_ROUTES][MAX_GPS_ROUTE_ZONES];
new Float:gGPSRPlayerMarkerPos[MAX_PLAYERS][3], bool:gGPSRPlayerMarkerSet[MAX_PLAYERS];
new bool:gGPSRoutesInitialized = false;
forward GPSR_UpdateRoute(playerid, routeid); // internal
new const bool:GPSR_LandVehicles[212] =
{
true, true, true, true, true, true, true,
true, true, true, true, true, true, true, true,
true, true, false, true, true, true, true, true,
true, true, false, true, true, true, true, false,
true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true,
false, false, true, false, true, true, false,
false, false, true, true, true, true, true, false,
true, true, true, false, false, true, true,
true, false, true, true, false, false, true,
true, false, true, true, true, true, true, true,
true, false, true, true, false, false, true, true,
true, true, false, true, true, true, false, true,
true, true, false, true, true, true, true, true,
true, true, true, true, false, false, false,
true, true, true, true, true, false, false, true,
true, true, true, true, true, true, true, true,
true, true, true, true, true, true, true, false,
false, false, true, true, true, true, true, true,
true, true, false, true, true, true, true, false,
true, true, true, true, true, true, true,
true, true, false, true, true, true, true, true,
false, false, true, true, true, true, true,
true, false, true, true, true, true, true, true,
true, true, true, true, true, true, false,
true, false, false, true, false, true, true,
true, true, true, true, true, true, true, true,
true, true, true, true, true, true
};
///////////////////////////////////////////////////// Forwards for include related callbacks
forward GPSR_OnPlayerRouteFound(playerid, routeid, extra, Float:distance);
forward GPSR_OnPlayerReachDestination(playerid, routeid, extra);
///////////////////////////////////////////////////// Hooks
public OnFilterScriptInit()
{
GPSR_Init();
#if defined GPSR_OnFilterScriptInit
return GPSR_OnFilterScriptInit();
#else
return 1;
#endif
}
#if defined _ALS_OnFilterScriptInit
#undef OnFilterScriptInit
#else
#define _ALS_OnFilterScriptInit
#endif
#define OnFilterScriptInit GPSR_OnFilterScriptInit
#if defined GPSR_OnFilterScriptInit
forward GPSR_OnFilterScriptInit();
#endif
// ----
public OnGameModeInit()
{
GPSR_Init();
#if defined GPSR_OnGameModeInit
return GPSR_OnGameModeInit();
#else
return 1;
#endif
}
#if defined _ALS_OnGameModeInit
#undef OnGameModeInit
#else
#define _ALS_OnGameModeInit
#endif
#define OnGameModeInit GPSR_OnGameModeInit
#if defined GPSR_OnGameModeInit
forward GPSR_OnGameModeInit();
#endif
// ----
GPSR_Init()
{
if(gGPSRoutesInitialized) return 1;
gGPSRoutesInitialized = true;
return 1;
}
// ----
public OnFilterScriptExit()
{
GPSR_Exit();
#if defined GPSR_OnFilterScriptExit
return GPSR_OnFilterScriptExit();
#else
return 1;
#endif
}
#if defined _ALS_OnFilterScriptExit
#undef OnFilterScriptExit
#else
#define _ALS_OnFilterScriptExit
#endif
#define OnFilterScriptExit GPSR_OnFilterScriptExit
#if defined GPSR_OnFilterScriptExit
forward GPSR_OnFilterScriptExit();
#endif
// ----
public OnGameModeExit()
{
#if defined GPSR_OnGameModeExit
return GPSR_OnGameModeExit();
#else
return 1;
#endif
}
#if defined _ALS_OnGameModeExit
#undef OnGameModeExit
#else
#define _ALS_OnGameModeExit
#endif
#define OnGameModeExit GPSR_OnGameModeExit
#if defined GPSR_OnGameModeExit
forward GPSR_OnGameModeExit();
#endif
// ----
GPSR_Exit()
{
if(!gGPSRoutesInitialized) return 1;
for(new i = 0; i < MAX_PLAYERS; i ++) for(new j = 0; j < MAX_PLAYER_GPS_ROUTES; j ++) DisablePlayerRoute(i, j);
gGPSRoutesInitialized = false;
return 1;
}
// ----
public OnPlayerConnect(playerid)
{
for(new i = 0; i < MAX_PLAYER_GPS_ROUTES; i ++) gPlayerRouteData[playerid][i] = gDefPlayerRouteData;
gGPSRPlayerMarkerSet[playerid] = false;
#if defined GPSR_OnPlayerConnect
return GPSR_OnPlayerConnect(playerid);
#else
return 1;
#endif
}
#if defined _ALS_OnPlayerConnect
#undef OnPlayerConnect
#else
#define _ALS_OnPlayerConnect
#endif
#define OnPlayerConnect GPSR_OnPlayerConnect
#if defined GPSR_OnPlayerConnect
forward GPSR_OnPlayerConnect(playerid);
#endif
// ----
public OnPlayerDisconnect(playerid, reason)
{
for(new i = 0; i < MAX_PLAYER_GPS_ROUTES; i ++) DisablePlayerRoute(playerid, i);
#if defined GPSR_OnPlayerDisconnect
return GPSR_OnPlayerDisconnect(playerid, reason);
#else
return 1;
#endif
}
#if defined _ALS_OnPlayerDisconnect
#undef OnPlayerDisconnect
#else
#define _ALS_OnPlayerDisconnect
#endif
#define OnPlayerDisconnect GPSR_OnPlayerDisconnect
#if defined GPSR_OnPlayerDisconnect
forward GPSR_OnPlayerDisconnect(playerid, reason);
#endif
// ----
public OnPlayerClickMap(playerid, Float:fX, Float:fY, Float:fZ)
{
gGPSRPlayerMarkerPos[playerid][0] = fX;
gGPSRPlayerMarkerPos[playerid][1] = fY;
gGPSRPlayerMarkerPos[playerid][2] = fZ;
gGPSRPlayerMarkerSet[playerid] = true;
#if defined GPSR_OnPlayerClickMap
return GPSR_OnPlayerClickMap(playerid, Float:fX, Float:fY, Float:fZ);
#else
return 1;
#endif
}
#if defined _ALS_OnPlayerClickMap
#undef OnPlayerClickMap
#else
#define _ALS_OnPlayerClickMap
#endif
#define OnPlayerClickMap GPSR_OnPlayerClickMap
#if defined GPSR_OnPlayerClickMap
forward GPSR_OnPlayerClickMap(playerid, Float:fX, Float:fY, Float:fZ);
#endif
// ----
ProcessRoute(playerid, routeid, const node_id_array[], amount_of_nodes, Float:distance)
{
if(amount_of_nodes < 3)
{
if(gPlayerRouteData[playerid][routeid][routeCalcState] == ROUTE_CALC_INIT) DisablePlayerRoute(playerid, routeid);
//else ResetPlayerRoute(playerid, routeid);
gPlayerRouteData[playerid][routeid][routeCalcState] = ROUTE_CALC_NONE;
return 1;
}
//else if(amount_of_nodes > MAX_GPS_ROUTE_LEN) amount_of_nodes = MAX_GPS_ROUTE_LEN;
ResetPlayerRoute(playerid, routeid);
gPlayerRouteData[playerid][routeid][routeCalcState] = ROUTE_CALC_NONE;
gPlayerRouteData[playerid][routeid][routeLen] = amount_of_nodes;
DrawPlayerRoute(playerid, routeid, node_id_array);
if(gPlayerRouteData[playerid][routeid][routeInitial] && funcidx("GPSR_OnPlayerRouteFound") != -1)
{
CallLocalFunction("GPSR_OnPlayerRouteFound", "iiif", playerid, routeid, gPlayerRouteData[playerid][routeid][routeExtra], distance);
}
return 1;
}
#if defined _gps_included
forward GPSR_OnRouteCalculated(Path:pathid, playerid, routeid);
public GPSR_OnRouteCalculated(Path:pathid, playerid, routeid)
{
static path[MAX_GPS_ROUTE_LEN];
new size, c, MapNode:nodeid, Float:distance;
GetPathSize(pathid, size);
for(new i = 0; i < size && i < sizeof(path); i ++)
{
GetPathNode(pathid, i, nodeid);
path[i] = _:nodeid;
c ++;
}
GetPathLength(pathid, distance);
ProcessRoute(playerid, routeid, path, c, distance);
}
#else
public GPS_WhenRouteIsCalculated(routeid, const node_id_array[], amount_of_nodes, Float:distance, Float:Polygon[], Polygon_Size, Float:NodePosX[], Float:NodePosY[], Float:NodePosZ[])
{
if(gGPSRoutesInitialized)
{
#if defined GPSR_DEBUG
new tick = GetTickCount();
#endif
routeid -= GPS_ROUTEID_OFFSET;
new playerid = (routeid % MAX_PLAYERS);
routeid = (routeid - playerid) / MAX_PLAYERS;
if(!IsPlayerConnected(playerid) || routeid < 0 || routeid >= MAX_PLAYER_GPS_ROUTES || !gPlayerRouteData[playerid][routeid][routeActivated] || gPlayerRouteData[playerid][routeid][routeCalcState] == ROUTE_CALC_NONE) return 1;
ProcessRoute(playerid, routeid, node_id_array, amount_of_nodes, distance);
#if defined GPSR_DEBUG
printf("Drawing route took %d ms", GetTickCount() - tick);
#endif
}
#if defined GPSR_GPS_WhenRouteIsCalculated
return GPSR_GPS_WhenRouteIsCalculated(routeid, node_id_array[], amount_of_nodes, Float:distance, Float:Polygon[], Polygon_Size, Float:NodePosX[], Float:NodePosY[], Float:NodePosZ[]);
#else
return 1;
#endif
}
#if defined _ALS_GPS_WhenRouteIsCalculated
#undef GPS_WhenRouteIsCalculated
#else
#define _ALS_GPS_WhenRouteIsCalculated
#endif
#define GPS_WhenRouteIsCalculated GPSR_GPS_WhenRouteIsCalculated
#if defined GPSR_GPS_WhenRouteIsCalculated
forward GPSR_GPS_WhenRouteIsCalculated(routeid, node_id_array[], amount_of_nodes, Float:distance, Float:Polygon[], Polygon_Size, Float:NodePosX[], Float:NodePosY[], Float:NodePosZ[]);
#endif
#endif
// ----
public GPSR_UpdateRoute(playerid, routeid)
{
if(gPlayerRouteData[playerid][routeid][routeActivated])
{
new Float:x, Float:y, Float:z;
GetPlayerPos(playerid, x, y, z);
if(VectorSize(x - gPlayerRouteData[playerid][routeid][routeTargetX], y - gPlayerRouteData[playerid][routeid][routeTargetY], 0.0) < gPlayerRouteData[playerid][routeid][routeTargetDistance])
{
CallLocalFunction("GPSR_OnPlayerReachDestination", "iii", playerid, routeid, gPlayerRouteData[playerid][routeid][routeExtra]);
}
else
{
FindPlayerRoute(playerid, gPlayerRouteData[playerid][routeid][routeExtra], gPlayerRouteData[playerid][routeid][routeTargetX], gPlayerRouteData[playerid][routeid][routeTargetY], gPlayerRouteData[playerid][routeid][routeTargetZ], gPlayerRouteData[playerid][routeid][routeTargetDistance], gPlayerRouteData[playerid][routeid][routeColor], true, gPlayerRouteData[playerid][routeid][routeLandVehicles], false);
}
}
return 1;
}
///////////////////////////////////////////////////// Functions
FindPlayerRoute(playerid, extra, Float:x, Float:y, Float:z, Float:target_distance = 25.0, color = 0xFFFF0099, bool:update = false, bool:land_vehicles = false, bool:initial = true)
{
if(!IsPlayerConnected(playerid)) return -1;
new routeid = FindFreeRouteSlot(playerid, extra);
if(routeid == -1) return -1;
/*switch(GetPlayerState(playerid))
{
case PLAYER_STATE_ONFOOT, PLAYER_STATE_DRIVER, PLAYER_STATE_PASSENGER:
{ }
default:
{
return -1;
}
}*/
#if defined _gps_included
new node_start, node_end, Float:px, Float:py, Float:pz;
GetPlayerPos(playerid, px, py, pz);
GetClosestMapNodeToPoint(px, py, pz, MapNode:node_start);
GetClosestMapNodeToPoint(x, y, z, MapNode:node_end);
if(!IsValidMapNode(MapNode:node_start) || !IsValidMapNode(MapNode:node_end)) return -1;
#else
new node_start = NearestPlayerNode(playerid), node_end = NearestNodeFromPoint(x, y, z);
if(!NodeExists(node_start) || !NodeExists(node_end)) return -1;
#endif
if(gPlayerRouteData[playerid][routeid][routeActivated] && gPlayerRouteData[playerid][routeid][routeNode1] == node_start && gPlayerRouteData[playerid][routeid][routeNode2] == node_end)
{
return -1;
}
gPlayerRouteData[playerid][routeid][routeActivated] = true;
gPlayerRouteData[playerid][routeid][routeCalcState] = ROUTE_CALC_INIT;
gPlayerRouteData[playerid][routeid][routeUpdate] = update;
gPlayerRouteData[playerid][routeid][routeInitial] = initial;
gPlayerRouteData[playerid][routeid][routeLandVehicles] = land_vehicles;
if(gPlayerRouteData[playerid][routeid][routeTimerID] != 0)
{
KillTimer(gPlayerRouteData[playerid][routeid][routeTimerID]);
gPlayerRouteData[playerid][routeid][routeTimerID] = 0;
}
if(update) gPlayerRouteData[playerid][routeid][routeTimerID] = SetTimerEx("GPSR_UpdateRoute", GPSR_UPDATE_INTERVAL, 1, "ii", playerid, routeid);
gPlayerRouteData[playerid][routeid][routeNode1] = node_start;
gPlayerRouteData[playerid][routeid][routeNode2] = node_end;
gPlayerRouteData[playerid][routeid][routeExtra] = extra;
gPlayerRouteData[playerid][routeid][routeTargetX] = x;
gPlayerRouteData[playerid][routeid][routeTargetY] = y;
gPlayerRouteData[playerid][routeid][routeTargetZ] = z;
gPlayerRouteData[playerid][routeid][routeTargetDistance] = target_distance;
gPlayerRouteData[playerid][routeid][routeColor] = color;
new bool:calc = true;
if(gPlayerRouteData[playerid][routeid][routeLandVehicles])
{
new pstate = GetPlayerState(playerid);
if((pstate != PLAYER_STATE_DRIVER && pstate != PLAYER_STATE_PASSENGER) || !GPSR_IsLandVehicle(GetPlayerVehicleID(playerid)))
{
ResetPlayerRoute(playerid, routeid);
calc = false;
}
}
if(calc)
{
#if defined _gps_included
FindPathThreaded(MapNode:node_start, MapNode:node_end, "GPSR_OnRouteCalculated", "ii", playerid, routeid);
#else
CalculatePath(node_start, node_end, routeid * MAX_PLAYERS + playerid + GPS_ROUTEID_OFFSET);
#endif
}
return routeid;
}
FindFreeRouteSlot(playerid, extra = cellmin)
{
if(!IsPlayerConnected(playerid)) return -1;
if(extra != cellmin)
{
for(new i = 0; i < MAX_PLAYER_GPS_ROUTES; i ++) if(gPlayerRouteData[playerid][i][routeActivated] && gPlayerRouteData[playerid][i][routeExtra] == extra) return i;
}
for(new i = 0; i < MAX_PLAYER_GPS_ROUTES; i ++) if(!gPlayerRouteData[playerid][i][routeActivated]) return i;
return -1;
}
ResetPlayerRoute(playerid, routeid)
{
if(!IsPlayerConnected(playerid) || routeid < 0 || routeid >= MAX_PLAYER_GPS_ROUTES || !gPlayerRouteData[playerid][routeid][routeActivated]) return 0;
new num = gPlayerRouteData[playerid][routeid][routeNumZones];
for(new i = 0; i < num; i ++)
{
PlayerGangZoneHide(playerid, gPlayerGangZones[playerid][routeid][i]);
PlayerGangZoneDestroy(playerid, gPlayerGangZones[playerid][routeid][i]);
}
gPlayerRouteData[playerid][routeid][routeNumZones] = 0;
gPlayerRouteData[playerid][routeid][routePos] = 0;
gPlayerRouteData[playerid][routeid][routeLen] = 0;
return 1;
}
DisablePlayerRoute(playerid, routeid)
{
if(!IsPlayerConnected(playerid) || routeid < 0 || routeid >= MAX_PLAYER_GPS_ROUTES || !gPlayerRouteData[playerid][routeid][routeActivated]) return 0;
if(gPlayerRouteData[playerid][routeid][routeTimerID] != 0)
{
KillTimer(gPlayerRouteData[playerid][routeid][routeTimerID]);
gPlayerRouteData[playerid][routeid][routeTimerID] = 0;
}
ResetPlayerRoute(playerid, routeid);
gPlayerRouteData[playerid][routeid] = gDefPlayerRouteData;
return 1;
}
DrawPlayerRoute(playerid, routeid, const node_id_array[])
{
if(!IsPlayerConnected(playerid) || routeid < 0 || routeid >= MAX_PLAYER_GPS_ROUTES || !gPlayerRouteData[playerid][routeid][routeActivated] || gPlayerRouteData[playerid][routeid][routeCalcState] != ROUTE_CALC_NONE) return 0;
new len = gPlayerRouteData[playerid][routeid][routeLen], num_zones;
new Float:x, Float:y, Float:tx, Float:ty, Float:w, nodeid, Float:dis, gzid;
for(new i = 0; i < len && num_zones < MAX_GPS_ROUTE_ZONES; i ++)
{
nodeid = node_id_array[i];
#if defined _gps_included
if(!IsValidMapNode(MapNode:nodeid)) continue;
GetMapNodePos(MapNode:nodeid, tx, ty, w);
#else
if(!NodeExists(nodeid)) continue;
GetNodePos(nodeid, tx, ty, w);
#endif
if(i == 0)
{
x = tx;
y = ty;
continue;
}
#if 1 // Interpolation between Nodes (always the same zone size)
while(num_zones < MAX_GPS_ROUTE_ZONES && (dis = VectorSize(tx - x, ty - y, 0.0)) > ROUTE_ZONE_SIZE)
{
x += (tx - x) / dis * ROUTE_ZONE_SIZE;
y += (ty - y) / dis * ROUTE_ZONE_SIZE;
gzid = PlayerGangZoneCreate(playerid, x - ROUTE_ZONE_SIZE/2.0, y - ROUTE_ZONE_SIZE/2.0, x + ROUTE_ZONE_SIZE/2.0, y + ROUTE_ZONE_SIZE/2.0);
if(gzid != -1 /*IsValidPlayerGangZone(playerid, gzid)*/)
{
gPlayerGangZones[playerid][routeid][num_zones] = gzid;
PlayerGangZoneShow(playerid, gzid, gPlayerRouteData[playerid][routeid][routeColor]);
num_zones ++;
}
}
#else // No interpolation, rectangles from Node to Node (test)
new Float:minx, Float:miny, Float:maxx, Float:maxy;
if(tx < x)
{
minx = tx;
maxx = x;
}
else
{
minx = x;
maxx = tx;
}
if(ty < y)
{
miny = ty;
maxy = y;
}
else
{
miny = y;
maxy = ty;
}
if(maxx - minx < 7.0)
{
minx -= 3.5;
maxx += 3.5;
}
if(maxy - miny < 7.0)
{
miny -= 3.5;
maxy += 3.5;
}
//printf("%.1f, %.1f // %.1f, %.1f", minx, miny, maxx, maxy);
gzid = PlayerGangZoneCreate(playerid, minx, miny, maxx, maxy);
if(gzid != -1 /*IsValidPlayerGangZone(playerid, gzid)*/)
{
gPlayerGangZones[playerid][routeid][num_zones] = gzid;
PlayerGangZoneShow(playerid, gzid, gPlayerRouteData[playerid][routeid][routeColor]);
num_zones ++;
}
x = tx;
y = ty;
#endif
gPlayerRouteData[playerid][routeid][routePos] = i;
}
gPlayerRouteData[playerid][routeid][routeNumZones] = num_zones;
#if defined GPSR_DEBUG
printf("Created %d GZ, total nodes: %d", num_zones, len);
#endif
return 1;
}
static GPSR_IsLandVehicle(vehicleid)
{
new modelid = GetVehicleModel(vehicleid);
if(modelid >= 400 && modelid <= 611) return _:GPSR_LandVehicles[modelid - 400];
return 0;
}
// EOF
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment