16.04.2015, 18:02
y_areas
Introduction
This is one of the higher-level YSI libraries that has been in existence for a while, but which I never got around to documenting. It provides two callbacks:
Код:
forward OnPlayerEnterArea(playerid, areaid); forward OnPlayerLeaveArea(playerid, areaid);
Use
This library provides a number of functions for declaring areas. For example, to detect when a player is in Fort Carson do:
Код:
#include <YSI\y_areas> new gAreaFortCarson public OnScriptInit() { gAreaFortCarson = Area_AddCuboid(-376.20, 826.30, -3.00, 123.70, 1220.40, 200.00); } public OnPlayerEnterArea(playerid, areaid) { if (areaid == gAreaFortCarson) SendClientMessage(playerid, X11_GREEN, "Welcome to Fort Carson"); }
Код:
Area_AddPoly(/* First Point */ -100.0, -100.0, /* Second Point */ 100.0, -100.0, /* Third Point */ 0.0, 100.0);
Код:
Area_AddPoly(/* First Point */ -100.0, -100.0, /* Third Point */ 0.0, 100.0, /* Second Point */ 100.0, -100.0);
Код:
Area_AddPoly( -100.0, -100.0, // Bottom left 100.0, -100.0, // Bottom right 100.0, 100.0, // Top right -100.0, 100.0 // Top left );
Код:
Area_AddPoly( -100.0, -100.0, // Bottom left 100.0, 100.0, // Top right -100.0, 100.0, // Top left 100.0, -100.0 // Bottom right );
Код:
#include <YSI\y_areas> new gAreaDowntownLosSantos; public OnScriptInit() { gAreaDowntownLosSantos= Area_AddPoly( 1812.60, -1430.80, 1812.60, -1150.80, 1463.90, -1150.80, 1463.90, - 926.90, 1391.00, - 926.90, 1391.00, -1026.30, 1378.30, -1026.30, 1378.30, -1130.80, 1370.80, -1130.80, 1370.80, -1384.90, 1463.90, -1384.90, 1463.90, -1430.80, 335.90); } public OnPlayerEnterArea(playerid, areaid) { if (areaid == gAreaDowntownLosSantos) SendClientMessage(playerid, X11_GREEN, "Welcome to Downtown Los Santos"); }
- Код:
// Add a box area with upper and lower limits. forward Area_AddCuboid(Float:x0, Float:y0, Float:z0, Float:x1, Float:y1, Float:z1); forward Area_AddCube(Float:x0, Float:y0, Float:z0, Float:x1, Float:y1, Float:z1);
- Код:
// Add a vertically infinite box area. forward Area_AddBox(Float:x0, Float:y0, Float:x1, Float:y1);
- Код:
// Add a circle, with optional height. forward Area_AddCircle(Float:x, Float:y, Float:r, Float:height = FLOAT_INFINITY);
- Код:
// Add an oval (an irregular circle). forward Area_AddOval(Float:x, Float:y, Float:rx, Float:ry, Float:h, Float:d);
- Код:
forward Area_AddSphere(Float:x, Float:y, Float:z, Float:r);
- Код:
forward Area_AddOvoid(Float:x, Float:y, Float:z, Float:rx, Float:ry, Float:rz);
- Код:
forward Area_AddPoly(Float:x1, Float:y1, Float:x2, Float:y2, Float:x3, Float:y3, Float:...);
As well as functions to add areas, there are several helper functions:
-
Код:
forward Area_GetPlayerAreas(playerid, idx);
Код:for (new idx, areaid; (areaid = Area_GetPlayerAreas(playerid, idx)) != NO_AREA; ++idx) { printf("Areaid: %d", areaid); }
- Код:
forward Area_SetPlayer(area, playerid, bool:set);
Код:Group_SetArea(Group:g, areaid, bool:set);
- Код:
forward Area_GetPlayer(area, playerid);
- Код:
forward Area_SetWorld(area, world, bool:set);
- Код:
forward Area_GetWorld(area, world);
- Код:
forward Area_SetAllPlayers(area, bool:set);
- Код:
forward Area_SetAllWorlds(area, bool:set);
- Код:
forward Area_IsValid(area);
- Код:
forward Area_IsEmpty(area);
- Код:
forward Area_Delete(area);
This library, like most of my libraries, is included in YSI. This can be downloaded here:
https://sampforum.blast.hk/showthread.php?tid=321092
Advanced
This system uses a (IMHO) quite complex zoning system to stream areas - only the ones known to be near the player are checked. The world is split in to a grid and areas added to the grid square in which they fit entirely. If an area does not fit in to a single square there are numerous pregressively larger zones in which they can be placed - quarter of the map, half the map, the whole map, several areas outside the map, and the whole world (i.e. everywhere). This means that a player is in several zones at once and only these ones are checked. You can see the (quite ugly and hand-unrolled) code for this zone determination in "Area_DetermineZone".
In addition to this zoning, the more complex zones (i.e. polygons) have extra optimisations in the form of bounding circles. A circle is drawn around the whole area - if the player is not in this circle (which is easy to check) then they can't possibly be in the polygon (which is hard to check). Only if they are in the circle is the polygon checked too.
As already mentioned, a player can be in multiple areas at once. Several of these areas are stored in an array for fast access - if they are in a great number of areas at once (which is unlikely) then the extras are stored in slower but more dynamic PVars. The limit at which this happens is defined by "AREAS_MAX_OVERLAPS" and defaults to 3.
Finally, this code contains the most complex "enum" that I've ever written! An area has certain pieces of data such as players and worlds that all areas need, but some areas need more storage for co-ordinates than others. To accommodate this, multiple areas are used to store a single one, but in those cases the space used to store things like player permissions is not needed and is instead used for extra co-ordinate space. This is done through this enum:
Код:
// ============================================================================= // ============================================================================= // IMPORTANT NOTE: The order of elements in this enum is INCREDIBLY important. // Because this is three enums in one, they share some pieces of data that must // not get overridden. // ============================================================================= // ============================================================================= // This might just be the most complex enum I've ever made! enum E_AREA { //union //{ // struct // { #if YSIM_HAS_MASTER E_AREA_MASTER, #endif // This has increased from 4 to 6, so we can store cubes and ovoids // in a single slot. Float:E_AREA_POS[6], PlayerArray:E_AREA_PLAYERS<MAX_PLAYERS>, // This MUST go between "E_AREA_PLAYERS" and "E_AREA_FLAGS" to // remain constant in subsequent unions. //#if defined AREA_VERY_FAST // Float:E_AREA_BOUNDING[4], //#endif // As must this. #if AREA_WORLDS > 0 BitArray:E_AREA_WORLDS<AREA_WORLDS>, #endif // ALWAYS last (actually used by EVERY type). e_AREA_FLAGS:E_AREA_FLAGS, // } // // Start of poly data. // struct // { // Reset the enum counter to 0 (on the next item). _E_AREA_RESET_@1 = -1, // Now restart. #if YSIM_HAS_MASTER // Skip the master flag if it exists. _E_AREA_MASTER_@1, #endif // This is where polys differ from all others. E_AREA_POLY_COUNT, E_AREA_POLY_NEXT, //Float:E_AREA_POLY_POS[4], // This slot holds ONLY the polygon bounding data, it doesn't hold // any co-ordinates AT ALL. //Float:E_AREA_BOUNDING[4], Float:E_AREA_POLY_BOUND_X, Float:E_AREA_POLY_BOUND_Y, Float:E_AREA_POLY_BOUND_R, Float:E_AREA_POLY_BOUND_H, // Skip all the array and flag data, they stay the same. _E_AREA_ARRAYS_@1[E_AREA_FLAGS - E_AREA_PLAYERS], // Skip the flags. E_AREA_UNUSED_NEXT, // } // // Start of poly child data. // struct // { // Reset the enum counter to 0 (on the next item). _E_AREA_RESET_@2 = -1, // Now restart. #if YSIM_HAS_MASTER // Skip the master flag if it exists. _E_AREA_MASTER_@2, #endif // Store a link to the parent of this poly child. E_AREA_CHILD_PARENT, // And the next one in VERY rare cases (I hope). // THIS SHARES A SLOT WITH "E_AREA_POLY_NEXT", so we can always just // use that slot when iterating. #if _:(E_AREA_FLAGS - E_AREA_CHILD_PARENT) & 1 // There are an ODD number of available slots. We can store an // extra pair iff we don't need to use "NEXT". //_E_AREA_SKIP_@2, E_AREA_CHILD_OPT_Y, #endif E_AREA_CHILD_OPT_X, // Just store a vast number of X/Y pairs in this child. Float:E_AREA_CHILD_ELEMS[_:E_AREA_FLAGS - _:E_AREA_CHILD_OPT_X - 1], // Skip the flags. _E_AREA_CHILD_ELEMS_END // } } #define CHILD_AREA_SLOTS ((_:_E_AREA_CHILD_ELEMS_END - _:E_AREA_CHILD_ELEMS))