open.mp forum
[Library] Map Zones - Let's end a decade of bad practices... - Printable Version

+ open.mp forum (https://forum.open.mp)
-- Forum: SA-MP (https://forum.open.mp/forumdisplay.php?fid=3)
--- Forum: Releases (https://forum.open.mp/forumdisplay.php?fid=13)
---- Forum: Libraries (https://forum.open.mp/forumdisplay.php?fid=31)
---- Thread: [Library] Map Zones - Let's end a decade of bad practices... (/showthread.php?tid=252)



Map Zones - Let's end a decade of bad practices... - kristo - 2019-04-15

SA-MP Map Zones



[Image: sampctl-samp--map--zones-2f2f2f.svg?style=for-the-badge]



This library does not bring anything gamechanging to the table, it?s created to stop a decade long era of bad practices regarding map zones. An array of ~350 zones dumped (or manually converted?) from the game has been around for such a long time, but in that time I?ve never seen a satisfactory API for them. Let?s look at an implementation from Emmet_?s South Central Roleplay.



PHP Code:
stock GetLocation(Float:fXFloat:fYFloat:fZ)

{

? ? 
enum e_ZoneData

? ? {

? ? ? ? 
e_ZoneName[32 char],

? ? ? ? 
Float:e_ZoneArea[6]

? ? };

? ? new const 
g_arrZoneData[][e_ZoneData] =

? ? {

? ? ? ? 
// ...

? ? };

? ? new

? ? ? ? 
name[32] = "San Andreas";



? ? for (new 
0!= sizeof(g_arrZoneData); )

? ? {

? ? ? ? if (

? ? ? ? ? ? (
fX >= g_arrZoneData[i][e_ZoneArea][0] && fX <= g_arrZoneData[i][e_ZoneArea][3]) &&

? ? ? ? ? ? (
fY >= g_arrZoneData[i][e_ZoneArea][1] && fY <= g_arrZoneData[i][e_ZoneArea][4]) &&

? ? ? ? ? ? (
fZ >= g_arrZoneData[i][e_ZoneArea][2] && fZ <= g_arrZoneData[i][e_ZoneArea][5]))

? ? ? ? {

? ? ? ? ? ? 
strunpack(nameg_arrZoneData[i][e_ZoneName]);



? ? ? ? ? ? break;

? ? ? ? }

? ? }

? ? return 
name;

}



stock GetPlayerLocation(playerid)

{

? ? new

? ? ? ? 
Float:fX,

? ? ? ? 
Float:fY,

? ? ? ? 
Float:fZ,

? ? ? ? 
string[32],

? ? ? ? 
id = -1;



? ? if ((
id House_Inside(playerid)) != -1)

? ? {

? ? ? ? 
fX HouseData[id][housePos][0];

? ? ? ? 
fY HouseData[id][housePos][1];

? ? ? ? 
fZ HouseData[id][housePos][2];

? ? }

? ? 
// ...

? ? else GetPlayerPos(playeridfXfYfZ);



? ? 
format(string32GetLocation(fXfYfZ));

? ? return 
string;





[Image: cyUdlu4.png]



If you didn?t get the reference, you should probably check out this repository. GetPlayerLocation most likely uses format to prevent this bug from occurring, but the risk is still there and arrays should never be returned in PAWN. Let?s take a look at another implementation that even I used a long time ago.



PHP Code:
stock GetPointZone(Float:xFloat:yFloat:zzone[] = "San Andreas"len sizeof(zone))

{

? ? for (new 
isizeof(Zones); ji)

? ? {

? ? ? ? if (
>= Zones[i][zArea][0] && <= Zones[i][zArea][3] && >= Zones[i][zArea][1] && <= Zones[i][zArea][4] && >= Zones[i][zArea][2] && <= Zones[i][zArea][5])

? ? ? ? {

? ? ? ? ? ? 
strunpack(zoneZones[i][zName], len);

? ? ? ? ? ? return 
1;

? ? ? ? }

? ? }

? ? return 
1;

}



stock GetPlayerZone(playeridzone[], len sizeof(zone))

{

? ? new 
Float:pos[3];

? ? 
GetPlayerPos(playeridpos[0], pos[1], pos[2]);



? ? for (new 
isizeof(Zones); ji)

? ? {

? ? ? ? if (
>= Zones[i][zArea][0] && <= Zones[i][zArea][3] && >= Zones[i][zArea][1] && <= Zones[i][zArea][4] && >= Zones[i][zArea][2] && <= Zones[i][zArea][5])

? ? ? ? {

? ? ? ? ? ? 
strunpack(zoneZones[i][zName], len);

? ? ? ? ? ? return 
1;

? ? ? ? }

? ? }

? ? return 
1;





First of all, what do we see? A lot of code repetition. That?s easy to fix in this case, but what if we also needed either the min/max position of the zone? We?d have to loop through the zones again or take a different approach. Which approach does this library take? Functions like GetMapZoneAtPoint and GetPlayerMapZone do not return the name of the zone, they return an identificator of it. The name or positions of the zone must be fetched using another function. In addition to that, I rebuilt the array of zones myself since the one used basically everywhere seems to be faulty according to this post.



Installation



Simply install to your project:



Code:
sampctl package install kristoisberg/samp-map-zones



Include in your code and begin using the library:



PHP Code:
#include <map-zones> 



Usage



Constants


  • INVALID_MAP_ZONE_ID = MapZone:-1


    • The return value of several functions when no map zone was matching the

      criteria.


  • MAX_MAP_ZONE_NAME = 27


    • The length of the longest map zone name including the null character.


  • MAX_MAP_ZONE_AREAS = 13


    • The most areas associated with a map zone.



Functions


  • MapZone:GetMapZoneAtPoint(Float:x, Float:y, Float:z)


    • Returns the ID of the map zone the point is in or INVALID_MAP_ZONE_ID if

      it isn?t in any. Alias: GetMapZoneAtPoint3D.


  • MapZone:GetPlayerMapZone(playerid)


    • Returns the ID of the map zone the player is in or INVALID_MAP_ZONE_ID if

      it isn?t in any. Alias: GetPlayerMapZone3D.


  • MapZone:GetVehicleMapZone(vehicleid)


    • Returns the ID of the map zone the vehicle is in or INVALID_MAP_ZONE_ID if

      it isn?t in any. Alias: GetVehicleMapZone3D.


  • MapZone:GetMapZoneAtPoint2D(Float:x, Float:y)


    • Returns the ID of the map zone the point is in or INVALID_MAP_ZONE_ID if

      it isn?t in any. Does not check the Z-coordinate.


  • MapZone:GetPlayerMapZone2D(playerid)


    • Returns the ID of the map zone the player is in or INVALID_MAP_ZONE_ID if

      it isn?t in any. Does not check the Z-coordinate.


  • MapZone:GetVehicleMapZone2D(vehicleid)


    • Returns the ID of the map zone the vehicle is in or INVALID_MAP_ZONE_ID if

      it isn?t in any. Does not check the Z-coordinate.


  • bool:IsValidMapZone(MapZone:id)


    • Returns true or false depending on if the map zone is valid or not.


  • bool:GetMapZoneName(MapZone:id, name[], size = sizeof(name))


    • Retrieves the name of the map zone. Returns true or false depending on

      if the map zone is valid or not.


  • bool:GetMapZoneSoundID(MapZone:id, &soundid)


    • Retrieves the sound ID of the map zone. Returns true or false depending

      on if the map zone is valid or not.


  • bool:GetMapZoneAreaCount(MapZone:id, &count)


    • Retrieves the count of areas associated with the map zone. Returns true or

      false depending on if the map zone is valid or not.


  • GetMapZoneAreaPos(MapZone:id, &Float:minX = 0.0, &Float:minY = 0.0, &Float:minZ = 0.0, &Float:maxX = 0.0, &Float:maxY = 0.0, &Float:maxZ = 0.0, start = 0)


    • Retrieves the coordinates of an area associated with the map zone. Returns

      the array index for the area or -1 if none were found. See the usage in

      in the examples section.


  • GetMapZoneCount()


    • Returns the count of map zones in the array. Could be used for iteration

      purposes.



Examples



Retrieving the location of a player



PHP Code:
CMD:whereami(playerid) {

? ? new 
MapZone:zone GetPlayerMapZone(playerid);



? ? if (
zone == INVALID_MAP_ZONE_ID) {

? ? ? ? return 
SendClientMessage(playerid0xFFFFFFFF"probably in the ocean, mate");

? ? }



? ? new 
name[MAX_MAP_ZONE_NAME], soundid;

? ? 
GetMapZoneName(zonename);

? ? 
GetMapZoneSoundID(zonesoundid);



? ? new 
string[128];

? ? 
format(stringsizeof(string), "you are in %s"name);



? ? 
SendClientMessage(playerid0xFFFFFFFFstring);

? ? 
PlayerPlaySound(playeridsoundid0.00.00.0);

? ? return 
1;





Iterating through areas associated with a map zone



PHP Code:
new zone ZONE_RICHMANindex = -1Float:minXFloat:minYFloat:minZFloat:maxXFloat:maxYFloat:maxZ;



while ((
index GetMapZoneAreaPos(zoneminXminYminZmaxXmaxYmaxZindex  1) != -1) {

? ? 
printf("%f %f %f %f %f %f"minXminYminZmaxXmaxYmaxZ);





Extending



PHP Code:
stock MapZone:GetPlayerOutsideMapZone(playerid) {

? ? new 
House:houseid GetPlayerHouseID(playerid), Float:xFloat:yFloat:z;



? ? if (
houseid != INVALID_HOUSE_ID) { // if the player is inside a house, get the exterior location of the house

? ? ? ? GetHouseExteriorPos(houseidxyz);

? ? } else if (!
GetPlayerPos(playeridxyz)) { // the player isn't connected, presuming that GetPlayerHouseID returns INVALID_HOUSE_ID in that case?

? ? ? ? return INVALID_MAP_ZONE_ID;

? ? }



? ? return 
GetMapZoneAtPoint(xyz);





Testing



To test, simply run the package:



Code:
sampctl package run



RE: Map Zones - Let's end a decade of bad practices... - Gravityfalls - 2019-04-15

This is as good as my french fries. 10/10.


RE: Map Zones - Let's end a decade of bad practices... - Codeah - 2019-04-15

Neat! Was looking for something like this yesterday!