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.0
as the standard library would return,9999.9999
,-1.0
or any other nonsense. As you might know, comparingNaN
to 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 toIsPlayerInRangeOfPoint
from the standard library, but a completely different name structure. Some other examples areGetClosestPlayer
andGetClosestVehicle
, 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!
Simply install to your project:
sampctl package install kristoisberg/samp-distance
Include in your code and begin using the library:
#include <samp-distance>
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.
The functions in this library have several basic forms:
Get<A>DistanceTo<B>
- Get the distance betweenA
andB
.Is<A>InRangeOf<B>
- Is the distance betweenA
andB
lower than some threshold?GetClosest<A>To<B>
- Get the closestA
toB
.
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 betweenA
and anx/y
point.Is<A>InRangeOfPoint2D
- Is the distance betweenA
and anx/y
point lower than some threshold?Get<A>DistanceToPoint3D
- Get the distance betweenA
and anx/y/z
point.Is<A>InRangeOfPoint3D
- Is the distance betweenA
and anx/y/z
point lower than some threshold?
And the overloaded versions that are determined by parameter count:
Get<A>DistanceToPoint
- Get the distance betweenA
and anx/y(/z)
point.Is<A>InRangeOfPoint
- Is the distance betweenA
and anx/y(/z)
point lower than some threshold?
For each of these functions A
and B
can be any of the following entity types:
Player
Vehicle
Object
DynObject
(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.
Functions that return Float:
will return FLOAT_NAN
if they fail. If, for example, one of the input entities doesn't exist.
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);
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);
Float:GetPlayerDistanceToPlayer(playerid, targetid);
bool:IsPlayerInRangeOfPlayer(playerid, targetid, Float:range, bool:ignoreVW = false, bool:ignoreInterior = false);
GetClosestPlayerToPlayer(playerid, bool:ignoreVW = false, bool:ignoreInterior = false);
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);
Float:GetVehicleDistanceToVehicle(vehicleid, targetid);
bool:IsVehicleInRangeOfVehicle(vehicleid, targetid, Float:range, bool:ignoreVW = false);
GetClosestVehicleToVehicle(vehicleid, bool:ignoreVW = false);
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);
Float:GetObjectDistanceToObject(objectid, targetid);
bool:IsObjectInRangeOfObject(objectid, targetid, Float:range);
GetClosestObjectToObject(objectid);
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);
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);
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);
Float:GetPlayerDistanceToObject(playerid, objectid);
bool:IsPlayerInRangeOfObject(playerid, objectid, Float:range);
GetClosestPlayerToObject(objectid);
Float:GetObjectDistanceToPlayer(objectid, playerid);
bool:IsObjectInRangeOfPlayer(objectid, playerid, Float:range);
GetClosestObjectToPlayer(playerid);
Float:GetObjectDistanceToVehicle(objectid, vehicleid);
bool:IsObjectInRangeOfVehicle(objectid, vehicleid, Float:range);
GetClosestObjectToVehicle(vehicleid);
Float:GetVehicleDistanceToObject(vehicleid, objectid);
bool:IsVehicleInRangeOfObject(vehicleid, objectid, Float:range);
GetClosestVehicleToObject(objectid);
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);
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);
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);
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);
Float:GetActorDistanceToActor(actorid, targetid);
bool:IsActorInRangeOfActor(actorid, targetid, Float:range, bool:ignoreVW = false);
GetClosestActorToActor(actorid, bool:ignoreVW = false);
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);
Float:GetObjectDistanceToActor(objectid, actorid);
bool:IsObjectInRangeOfActor(objectid, actorid, Float:range);
GetClosestObjectToActor(actorid);
Float:GetActorDistanceToObject(actorid, objectid);
bool:IsActorInRangeOfObject(actorid, objectid, Float:range);
GetClosestActorToObject(objectid);
Float:GetActorDistanceToVehicle(vehicleid, targetid);
bool:IsActorInRangeOfVehicle(vehicleid, targetid, Float:range);
GetClosestActorToVehicle(vehicleid);
Float:GetVehicleDistanceToActor(vehicleid, targetid);
bool:IsVehicleInRangeOfActor(vehicleid, targetid, Float:range);
GetClosestVehicleToActor(vehicleid);
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)).
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;
}
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