samp-distance
samp-distance copied to clipboard
SA-MP Distance Functions
This library offers a bunch of functions for getting the distance between various entities (2D/3D points, players, vehicles), performing proximity/distance checks between them and finding the closest entities to other entities. The idea for this library came after seeing a discussion on this topic on the forums and realising that there is no widely adopted library for performing these tasks and the homebrewed implementations often have major issues. Some of the issues this library addresses:
- Incorrect return values - If a distance check fails because an entity does not exist, the result of the check is
NaN, not0.0as the standard library would return,9999.9999,-1.0or any other nonsense. As you might know, comparingNaNto any value, even itself, always returnsfalse, making it easy to distinguish if the function failed and avoiding incorrect behaviour in code using those functions. - Inconsistent and non-descriptive function names - A function name I saw in the topic mentioned above is
IsCoordNearCoord, which has a similar behaviour toIsPlayerInRangeOfPointfrom the standard library, but a completely different name structure. Some other examples areGetClosestPlayerandGetClosestVehicle, commonly used names for functions returning the closest player/vehicle to a player. The more you read the previous sentence, the more you will realise that the names are missing something...
Huge thanks to Y_Less for his contributions to this library and the productive conversations with him!
Installation
Simply install to your project:
sampctl package install kristoisberg/samp-distance
Include in your code and begin using the library:
#include <samp-distance>
Changes to the standard library
This library hooks the GetPlayerDistanceFromPoint and GetVehicleDistanceFromPoint functions to return NaN instead of 0.0 when the functions fail (the player/vehicle does not exist). These changes should not affect any existing code since it should be checking for invalid players/vehicles before using these functions anyway.
Function Naming
The functions in this library have several basic forms:
Get<A>DistanceTo<B>- Get the distance betweenAandB.Is<A>InRangeOf<B>- Is the distance betweenAandBlower than some threshold?GetClosest<A>To<B>- Get the closestAtoB.
Plus a few "Point" functions which take an x/y pair or x/y/z triple instead of another entity:
Get<A>DistanceToPoint2D- Get the distance betweenAand anx/ypoint.Is<A>InRangeOfPoint2D- Is the distance betweenAand anx/ypoint lower than some threshold?Get<A>DistanceToPoint3D- Get the distance betweenAand anx/y/zpoint.Is<A>InRangeOfPoint3D- Is the distance betweenAand anx/y/zpoint lower than some threshold?
And the overloaded versions that are determined by parameter count:
Get<A>DistanceToPoint- Get the distance betweenAand anx/y(/z)point.Is<A>InRangeOfPoint- Is the distance betweenAand anx/y(/z)point lower than some threshold?
For each of these functions A and B can be any of the following entity types:
PlayerVehicleObjectDynObject(with the streamer plugin).Actor
So for example, to check if a vehicle (A) is near a given streamer object (B) use:
if (IsVehicleInRangeOfDynObject(vehicleid, objectid, 50.0))
{
// Yes.
}
There are also two generic point functions:
stock Float:GetPointDistanceToPoint(Float:x1, Float:y1, Float:z1, Float:x2, Float:y2 = FLOAT_NAN, Float:z2 = FLOAT_NAN);
stock bool:IsPointInRangeOfPoint(Float:range, Float:x1, Float:y1, Float:z1, Float:x2, Float:y2 = FLOAT_NAN, Float:z2 = FLOAT_NAN);
These can be invoked as 2D (with four/five parameters) or 3D (with six/seven parameters).
In many cases a function is overloaded on the number of parameters:
if (IsVehicleInRangeOfPoint(vehicleid, 4.0, 5.0, 10.0)) // 2D check.
{
}
if (IsVehicleInRangeOfPoint(vehicleid, 4.0, 5.0, 6.0, 10.0)) // 3D check.
{
}
You can specify which with IsVehicleInRangeOfPoint2D and IsVehicleInRangeOfPoint3D, the underlying implementations, as listed below.
All Functions
Functions that return Float: will return FLOAT_NAN if they fail. If, for example, one of the input entities doesn't exist.
Point - Point
Float:GetPointDistanceToPoint(Float:x1, Float:y1, Float:z1, Float:x2, Float:y2 = FLOAT_NAN, Float:z2 = FLOAT_NAN);
bool:IsPointInRangeOfPoint(Float:range, Float:x1, Float:y1, Float:z1, Float:x2, Float:y2 = FLOAT_NAN, Float:z2 = FLOAT_NAN);
Player - Point
Float:GetPlayerDistanceToPoint2D(playerid, Float:x, Float:y);
bool:IsPlayerInRangeOfPoint2D(playerid, Float:range, Float:x, Float:y);
Float:GetPlayerDistanceToPoint3D(playerid, Float:x, Float:y, Float:z);
bool:IsPlayerInRangeOfPoint3D(playerid, Float:range, Float:x, Float:y, Float:z);
Player - Player
Float:GetPlayerDistanceToPlayer(playerid, targetid);
bool:IsPlayerInRangeOfPlayer(playerid, targetid, Float:range, bool:ignoreVW = false, bool:ignoreInterior = false);
GetClosestPlayerToPlayer(playerid, bool:ignoreVW = false, bool:ignoreInterior = false);
Vehicle - Point
Float:GetVehicleDistanceToPoint2D(vehicleid, Float:x, Float:y);
bool:IsVehicleInRangeOfPoint2D(vehicleid, Float:range, Float:x, Float:y);
Float:GetVehicleDistanceToPoint3D(vehicleid, Float:x, Float:y, Float:z);
bool:IsVehicleInRangeOfPoint3D(vehicleid, Float:range, Float:x, Float:y, Float:z);
Vehicle - Vehicle
Float:GetVehicleDistanceToVehicle(vehicleid, targetid);
bool:IsVehicleInRangeOfVehicle(vehicleid, targetid, Float:range, bool:ignoreVW = false);
GetClosestVehicleToVehicle(vehicleid, bool:ignoreVW = false);
Object - Point
Float:GetObjectDistanceToPoint2D(objectid, Float:x, Float:y);
bool:IsObjectInRangeOfPoint2D(objectid, Float:range, Float:x, Float:y);
Float:GetObjectDistanceToPoint3D(objectid, Float:x, Float:y, Float:z);
bool:IsObjectInRangeOfPoint3D(objectid, Float:range, Float:x, Float:y, Float:z);
Object - Object
Float:GetObjectDistanceToObject(objectid, targetid);
bool:IsObjectInRangeOfObject(objectid, targetid, Float:range);
GetClosestObjectToObject(objectid);
DynObject - Point
Float:GetDynObjectDistanceToPoint2D(STREAMER_TAG_OBJECT:objectid, Float:x, Float:y);
bool:IsDynObjectInRangeOfPoint2D(STREAMER_TAG_OBJECT:objectid, Float:range, Float:x, Float:y);
Float:GetDynObjectDistanceToPoint3D(STREAMER_TAG_OBJECT:objectid, Float:x, Float:y, Float:z);
bool:IsDynObjectInRangeOfPoint3D(STREAMER_TAG_OBJECT:objectid, Float:range, Float:x, Float:y, Float:z);
DynObject - DynObject
Float:GetDynObjectDistanceToDynObject(STREAMER_TAG_OBJECT:objectid, STREAMER_TAG_OBJECT:targetid);
bool:IsDynObjectInRangeOfDynObject(STREAMER_TAG_OBJECT:objectid, STREAMER_TAG_OBJECT:targetid, Float:range);
STREAMER_TAG_OBJECT:GetClosestDynObjectToDynObject(STREAMER_TAG_OBJECT:objectid);
Player - Vehicle
Float:GetVehicleDistanceToPlayer(vehicleid, playerid);
bool:IsVehicleInRangeOfPlayer(vehicleid, playerid, Float:range, bool:ignoreVW = false);
GetClosestVehicleToPlayer(playerid, bool:ignoreVW = false);
Float:GetPlayerDistanceToVehicle(playerid, vehicleid);
bool:IsPlayerInRangeOfVehicle(playerid, vehicleid, Float:range, bool:ignoreVW = false);
GetClosestPlayerToVehicle(vehicleid, bool:ignoreVW = false);
Player - Object
Float:GetPlayerDistanceToObject(playerid, objectid);
bool:IsPlayerInRangeOfObject(playerid, objectid, Float:range);
GetClosestPlayerToObject(objectid);
Float:GetObjectDistanceToPlayer(objectid, playerid);
bool:IsObjectInRangeOfPlayer(objectid, playerid, Float:range);
GetClosestObjectToPlayer(playerid);
Object - Vehicle
Float:GetObjectDistanceToVehicle(objectid, vehicleid);
bool:IsObjectInRangeOfVehicle(objectid, vehicleid, Float:range);
GetClosestObjectToVehicle(vehicleid);
Float:GetVehicleDistanceToObject(vehicleid, objectid);
bool:IsVehicleInRangeOfObject(vehicleid, objectid, Float:range);
GetClosestVehicleToObject(objectid);
DynObject - Player
Float:GetDynObjectDistanceToPlayer(STREAMER_TAG_OBJECT:objectid, playerid);
bool:IsDynObjectInRangeOfPlayer(STREAMER_TAG_OBJECT:objectid, playerid, Float:range);
STREAMER_TAG_OBJECT:GetClosestDynObjectToPlayer(playerid);
Float:GetPlayerDistanceToDynObject(playerid, STREAMER_TAG_OBJECT:objectid);
bool:IsPlayerInRangeOfDynObject(playerid, STREAMER_TAG_OBJECT:objectid, Float:range);
GetClosestPlayerToDynObject(STREAMER_TAG_OBJECT:objectid);
DynObject - Object
Float:GetDynObjectDistanceToObject(STREAMER_TAG_OBJECT:objectid, targetid);
bool:IsDynObjectInRangeOfObject(STREAMER_TAG_OBJECT:objectid, targetid, Float:range);
STREAMER_TAG_OBJECT:GetClosestDynObjectToObject(objectid);
Float:GetObjectDistanceToDynObject(objectid, STREAMER_TAG_OBJECT:targetid);
bool:IsObjectInRangeOfDynObject(objectid, STREAMER_TAG_OBJECT:targetid, Float:range);
GetClosestObjectToDynObject(STREAMER_TAG_OBJECT:objectid);
DynObject - Vehicle
Float:GetDynObjectDistanceToVehicle(STREAMER_TAG_OBJECT:objectid, vehicleid);
bool:IsDynObjectInRangeOfVehicle(STREAMER_TAG_OBJECT:objectid, vehicleid, Float:range);
STREAMER_TAG_OBJECT:GetClosestDynObjectToVehicle(vehicleid);
Float:GetVehicleDistanceToDynObject(vehicleid, STREAMER_TAG_OBJECT:objectid);
bool:IsVehicleInRangeOfDynObject(vehicleid, STREAMER_TAG_OBJECT:objectid, Float:range);
GetClosestVehicleToDynObject(STREAMER_TAG_OBJECT:objectid);
Actor - Point
Float:GetActorDistanceToPoint2D(actorid, Float:x, Float:y);
bool:IsActorInRangeOfPoint2D(actorid, Float:range, Float:x, Float:y);
Float:GetActorDistanceToPoint3D(actorid, Float:x, Float:y, Float:z);
bool:IsActorInRangeOfPoint3D(actorid, Float:range, Float:x, Float:y, Float:z);
Actor - Actor
Float:GetActorDistanceToActor(actorid, targetid);
bool:IsActorInRangeOfActor(actorid, targetid, Float:range, bool:ignoreVW = false);
GetClosestActorToActor(actorid, bool:ignoreVW = false);
Player - Actor
Float:GetActorDistanceToPlayer(actorid, playerid);
bool:IsActorInRangeOfPlayer(actorid, playerid, Float:range, bool:ignoreVW = false);
GetClosestActorToPlayer(playerid, bool:ignoreVW = false);
Float:GetPlayerDistanceToActor(playerid, actorid);
bool:IsPlayerInRangeOfActor(playerid, actorid, Float:range, bool:ignoreVW = false);
GetClosestPlayerToActor(actorid, bool:ignoreVW = false);
Object - Actor
Float:GetObjectDistanceToActor(objectid, actorid);
bool:IsObjectInRangeOfActor(objectid, actorid, Float:range);
GetClosestObjectToActor(actorid);
Float:GetActorDistanceToObject(actorid, objectid);
bool:IsActorInRangeOfObject(actorid, objectid, Float:range);
GetClosestActorToObject(objectid);
Actor - Vehicle
Float:GetActorDistanceToVehicle(vehicleid, targetid);
bool:IsActorInRangeOfVehicle(vehicleid, targetid, Float:range);
GetClosestActorToVehicle(vehicleid);
Float:GetVehicleDistanceToActor(vehicleid, targetid);
bool:IsVehicleInRangeOfActor(vehicleid, targetid, Float:range);
GetClosestVehicleToActor(vehicleid);
DynObject - Actor
Float:GetDynObjectDistanceToActor(STREAMER_TAG_OBJECT:objectid, actorid);
bool:IsDynObjectInRangeOfActor(STREAMER_TAG_OBJECT:objectid, actorid, Float:range);
STREAMER_TAG_OBJECT:GetClosestDynObjectToActor(actorid);
Float:GetActorDistanceToDynObject(actorid, STREAMER_TAG_OBJECT:objectid);
bool:IsActorInRangeOfDynObject(actorid, STREAMER_TAG_OBJECT:objectid, Float:range);
GetClosestActorToDynObject(STREAMER_TAG_OBJECT:objectid);
Note: The functions with an asterisk (*) next to them only support the ignoreInterior argument if the GetVehicleInterior function is available (added by YSF, some other library or by a new SA-MP version (yeah, right)).
Examples
CMD:pay(playerid, params[])
{
new playerid2, amount;
if (sscanf(params, "ui", playerid2, amount))
{
return SendClientMessage(playerid, COLOR_WHITE, "USAGE: /pay <Player name/ID> <Amount>");
}
if (!IsPlayerInRangeOfPlayer(playerid, playerid2, 5.0))
{
// fails if the other player is not connected or not near enough to the player
return SendClientMessage(playerid, COLOR_RED, "The specified player is not near you!");
}
GivePlayerMoney(playerid, -amount);
GivePlayerMoney(playerid2, amount);
return 1;
}
CMD:fixtires(playerid)
{
new vehicleid = GetClosestVehicleToPlayer(playerid);
if (!IsPlayerInRangeOfVehicle(playerid, vehicleid, 10.0))
{
// fails if no vehicle was found in the same interior and virtual world as the player or the closest one is not near enough to the player
return SendClientMessage(playerid, COLOR_RED, "You are not near any vehicle!");
}
new panels, doors, lights, tires;
GetVehicleDamageStatus(vehicleid, panels, doors, lights, tires);
SetVehicleDamageStatus(vehicleid, panels, doors, lights, 0);
return 1;
}
Testing
The library is not fully tested as of now because there is no fully working version of ut_mock_player/y_mock right now (I will be working on one soon). Right now, the tests only check whether the library compiles and the functions in the Point-Point section return the correct values. To test, simply run the package:
sampctl package run