sfcnr/gamemodes/irresistible/physics_dynamic.pwn
2023-12-29 01:48:17 +03:00

1065 lines
32 KiB
Plaintext

/* <SA-MP Objects Physics - Handle collisions and more.>
Copyright (C) <2013> <Peppe, Lorenc_>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
#include <modelsizes>
#pragma compress 1
#include <YSI_Data/y_iterate>
#if !defined PHY_TIMER_INTERVAL
#define PHY_TIMER_INTERVAL (50.0)
#endif
#if !defined PHY_MAX_OBJECTS
#define PHY_MAX_OBJECTS (1024)
#endif
#if !defined PHY_MAX_WALLS
#define PHY_MAX_WALLS (512)
#endif
#if !defined PHY_MAX_CYLINDERS
#define PHY_MAX_CYLINDERS (512)
#endif
#define PHY_MODE_3D (0)
#define PHY_MODE_2D (1)
#if !defined FLOAT_INFINITY
#define FLOAT_INFINITY (Float:0x7F800000)
#endif
#if !defined FLOAT_NEG_INFINITY
#define FLOAT_NEG_INFINITY (Float:0xFF800000)
#endif
#if !defined FLOAT_NAN
#define FLOAT_NAN (Float:0xFFFFFFFF)
#endif
/* Callbacks */
forward PHY_OnObjectUpdate(handleid);
forward PHY_OnObjectCollideWithObject(handleid_a, handleid_b);
forward PHY_OnObjectCollideWithZBound(handleid, lowhigh); // low bound = 0, high bound = 1
forward PHY_OnObjectCollideWithWall(handleid, wallid);
forward PHY_OnObjectCollideWithCylinder(handleid, cylinderid);
forward PHY_OnObjectCollideWithPlayer(handleid, playerid);
enum E_PHY_OBJECT
{
PHY_Created,
PHY_DynamicObject,
PHY_World,
Float:PHY_Size,
Float:PHY_Mass,
Float:PHY_VX,
Float:PHY_VY,
Float:PHY_VZ,
Float:PHY_AX,
Float:PHY_AY,
Float:PHY_AZ,
Float:PHY_Friction,
Float:PHY_AirResistance,
Float:PHY_Gravity,
Float:PHY_LowZBound,
/*Float:PHY_LowZRX,
Float:PHY_LowZRY,*/
Float:PHY_HighZBound,
Float:PHY_BoundConst,
Float:PHY_PlayerConst,
Float:PHY_PlayerDist,
Float:PHY_PlayerLowZ,
Float:PHY_PlayerHighZ,
PHY_Mode,
bool:PHY_Rolling,
bool:PHY_Object_Ghost_Objects,
bool:PHY_Object_Ghost_Walls,
bool:PHY_Object_Ghost_Cylinders,
bool:PHY_Object_Player_Collision
}
new
PHY_Object[PHY_MAX_OBJECTS][E_PHY_OBJECT],
Iterator:ITER_Object<PHY_MAX_OBJECTS>;
enum E_PHY_WALL
{
PHY_Created,
PHY_World,
Float:PHY_X1,
Float:PHY_Y1,
Float:PHY_X2,
Float:PHY_Y2,
Float:PHY_Z1,
Float:PHY_Z2,
Float:PHY_BounceConst,
Float:PHY_ANG
}
new
PHY_Wall[PHY_MAX_WALLS][E_PHY_WALL],
Iterator:ITER_Wall<PHY_MAX_WALLS>;
enum E_PHY_CYLINDER
{
PHY_Created,
PHY_World,
Float:PHY_X,
Float:PHY_Y,
Float:PHY_Z1,
Float:PHY_Z2,
Float:PHY_Size,
Float:PHY_BounceConst
}
new
PHY_Cylinder[PHY_MAX_CYLINDERS][E_PHY_CYLINDER],
Iterator:ITER_Cylinder<PHY_MAX_CYLINDERS>;
enum E_PHY_PLAYER
{
PHY_World
}
new
PHY_Player[MAX_PLAYERS][E_PHY_PLAYER];
/* Macros are self-explanatory */
#define PHY_IsHandleValid(%1) (%1 != ITER_NONE && Iter_Contains(ITER_Object, %1))
#define PHY_IsHandleUsing3D(%1) (PHY_Object[%1][PHY_Mode] == PHY_MODE_3D)
#define PHY_IsHandleGhostWithObjects(%1) (PHY_Object[%1][PHY_Object_Ghost_Objects])
#define PHY_IsHandleGhostWithWalls(%1) (PHY_Object[%1][PHY_Object_Ghost_Walls])
#define PHY_IsHandleGhostWithCylinders(%1) (PHY_Object[%1][PHY_Object_Ghost_Cylinders])
#define PHY_IsHandleCollidingWithPlayers(%1) (PHY_Object[%1][PHY_Object_Player_Collision])
#define PHY_IsHandleMoving(%1) (PHY_Object[%1][PHY_VX] != 0.0 || PHY_Object[%1][PHY_VY] != 0.0 || PHY_Object[%1][PHY_VZ] != 0.0)
#define PHY_IsHandleRolling(%1) (PHY_Object[%1][PHY_Rolling])
#define PHY_GetHandleFriction(%1) (PHY_Object[%1][PHY_Friction])
#define PHY_GetHandleAirResistance(%1) (PHY_Object[%1][PHY_AirResistance])
#define PHY_GetHandleGravity(%1) (PHY_Object[%1][PHY_Gravity])
#define PHY_GetHandleMode(%1) (PHY_Object[%1][PHY_Mode])
#define PHY_GetHandleObject(%1) (PHY_Object[%1][PHY_DynamicObject])
static
cb_Connect;
public OnGameModeInit()
{
SetTimer("PHY_CoreTimer", floatround(PHY_TIMER_INTERVAL), true);
cb_Connect = funcidx("PHY_OnPlayerConnect") != -1;
if(funcidx("PHY_OnGameModeInit") != -1)
return CallLocalFunction("PHY_OnGameModeInit", "");
return 1;
}
public OnPlayerConnect(playerid)
{
PHY_Player[playerid][PHY_World] = 0;
if(cb_Connect)
return CallLocalFunction("PHY_OnPlayerConnect", "i", playerid);
return 1;
}
forward PHY_CoreTimer();
public PHY_CoreTimer()
{
new
Float:x,
Float:y,
Float:z,
Float:x1,
Float:y1,
Float:z1,
Float:xclosest,
Float:yclosest,
Float:x2,
Float:y2,
Float:z2,
Float:speed,
Float:dx,
Float:dy,
Float:dz,
Float:dist,
Float:maxdist,
Float:angle,
Float:moveangle,
Float:dvx,
Float:dvy,
Float:dvz,
Float:mag,
Float:tmpvx,
Float:tmpvy,
Float:tmpvz,
Float:newvy1,
Float:newvy2,
Float:newvx1,
Float:newvx2,
Float:newvz1,
Float:newvz2;
foreach(new a : ITER_Object)
{
if (!Iter_Count(ITER_Object)) {
printf("\n\n\n[DEBUG A] !!!! INFINITE LOOP %d %d !!!!\n\n\n", GetTickCount( ), Iter_Count(ITER_Object));
return 1;
}
if(PHY_Object[a][PHY_Created])
{
new
obj_a = PHY_Object[a][PHY_DynamicObject];
GetDynamicObjectPos(obj_a, x, y, z);
x1 = x + PHY_Object[a][PHY_VX] * (PHY_TIMER_INTERVAL/1000.0);
y1 = y + PHY_Object[a][PHY_VY] * (PHY_TIMER_INTERVAL/1000.0);
// 3D Mode
if(PHY_GetHandleMode(a) == PHY_MODE_3D)
{
z1 = z + PHY_Object[a][PHY_VZ] * (PHY_TIMER_INTERVAL/1000.0);
if(z1 > PHY_Object[a][PHY_HighZBound])
{
if(PHY_Object[a][PHY_VZ] > 0.0)
{
PHY_Object[a][PHY_VZ] = -PHY_Object[a][PHY_VZ] * PHY_Object[a][PHY_BoundConst];
CallLocalFunction("PHY_OnObjectCollideWithZBound", "dd", a, 1);
}
z1 = PHY_Object[a][PHY_HighZBound];
}
else if(z1 < PHY_Object[a][PHY_LowZBound])
{
if(PHY_Object[a][PHY_VZ] < 0.0)
{
PHY_Object[a][PHY_VZ] = -PHY_Object[a][PHY_VZ] * PHY_Object[a][PHY_BoundConst];
CallLocalFunction("PHY_OnObjectCollideWithZBound", "dd", a, 0.0);
}
z1 = PHY_Object[a][PHY_LowZBound];
}
if(PHY_GetHandleGravity(a) != 0.0)
{
if(PHY_Object[a][PHY_VZ] > 0.0)
{
PHY_Object[a][PHY_VZ] -= PHY_Object[a][PHY_Gravity] * (PHY_TIMER_INTERVAL/1000.0);
if(PHY_Object[a][PHY_VZ] < 0.0)
PHY_Object[a][PHY_VZ] = 0.0;
}
else
PHY_Object[a][PHY_VZ] -= PHY_Object[a][PHY_Gravity] * (PHY_TIMER_INTERVAL/1000.0);
}
}
else
z1 = z;
// 2D mode
if(PHY_IsHandleMoving(a))
{
if(!PHY_IsHandleGhostWithObjects(a))
{
foreach(new b : ITER_Object)
{
if (!Iter_Count(ITER_Object)) {
printf("\n\n\n[DEBUG B] !!!! INFINITE LOOP %d %d !!!!\n\n\n", GetTickCount( ), Iter_Count(ITER_Object));
return 1;
}
if(PHY_Object[b][PHY_Created])
{
if(a != b && !PHY_IsHandleGhostWithObjects(b) && (!PHY_Object[a][PHY_World] || !PHY_Object[b][PHY_World] || PHY_Object[a][PHY_World] == PHY_Object[b][PHY_World]))
{
new
obj_b = PHY_Object[b][PHY_DynamicObject];
GetDynamicObjectPos(obj_b, x2, y2, z2);
dx = x1 - x2;
dy = y1 - y2;
dz = (PHY_GetHandleMode(a) == PHY_MODE_3D && PHY_GetHandleMode(b) == PHY_MODE_3D) ? (z1 - z2) : (0.0);
dist = (dx * dx) + (dy * dy) + (dz * dz);
maxdist = PHY_Object[a][PHY_Size] + PHY_Object[b][PHY_Size];
if(dist < (maxdist * maxdist))
{
dvx = PHY_Object[a][PHY_VX] - PHY_Object[b][PHY_VX];
dvy = PHY_Object[a][PHY_VY] - PHY_Object[b][PHY_VY];
dvz = PHY_Object[a][PHY_VZ] - PHY_Object[b][PHY_VZ];
mag = dvx * dx + dvy * dy + dvz * dz;
if(mag < 0.0)
{
newvx1 = PHY_Object[a][PHY_VX];
newvy1 = PHY_Object[a][PHY_VY];
newvz1 = PHY_Object[a][PHY_VZ];
newvx2 = PHY_Object[b][PHY_VX];
newvy2 = PHY_Object[b][PHY_VY];
newvz2 = PHY_Object[b][PHY_VZ];
projectVonU(PHY_Object[a][PHY_VX], PHY_Object[a][PHY_VY], PHY_Object[a][PHY_VZ], dx, dy, dz, tmpvx, tmpvy, tmpvz);
newvx1 -= tmpvx;
newvy1 -= tmpvy;
newvz1 -= tmpvz;
projectVonU(PHY_Object[b][PHY_VX], PHY_Object[b][PHY_VY], PHY_Object[b][PHY_VZ], -dx, -dy, -dz, tmpvx, tmpvy, tmpvz);
tmpvx = ((PHY_Object[a][PHY_Mass] - PHY_Object[b][PHY_Mass]) * PHY_Object[a][PHY_VX] + 2 * PHY_Object[b][PHY_Mass] * tmpvx) / (PHY_Object[a][PHY_Mass] + PHY_Object[b][PHY_Mass]);
tmpvy = ((PHY_Object[a][PHY_Mass] - PHY_Object[b][PHY_Mass]) * PHY_Object[a][PHY_VY] + 2 * PHY_Object[b][PHY_Mass] * tmpvy) / (PHY_Object[a][PHY_Mass] + PHY_Object[b][PHY_Mass]);
tmpvz = ((PHY_Object[a][PHY_Mass] - PHY_Object[b][PHY_Mass]) * PHY_Object[a][PHY_VZ] + 2 * PHY_Object[b][PHY_Mass] * tmpvz) / (PHY_Object[a][PHY_Mass] + PHY_Object[b][PHY_Mass]);
newvx1 += tmpvx;
newvy1 += tmpvy;
newvz1 += tmpvz;
projectVonU(PHY_Object[b][PHY_VX], PHY_Object[b][PHY_VY], PHY_Object[b][PHY_VZ], dx, dy, dz, tmpvx, tmpvy, tmpvz);
newvx2 -= tmpvx;
newvy2 -= tmpvy;
newvz2 -= tmpvz;
projectVonU(PHY_Object[a][PHY_VX], PHY_Object[a][PHY_VY], PHY_Object[a][PHY_VZ], -dx, -dy, -dz, tmpvx, tmpvy, tmpvz);
tmpvx = ((PHY_Object[b][PHY_Mass] - PHY_Object[a][PHY_Mass]) * PHY_Object[b][PHY_VX] + 2 * PHY_Object[a][PHY_Mass] * tmpvx) / (PHY_Object[a][PHY_Mass] + PHY_Object[b][PHY_Mass]);
tmpvy = ((PHY_Object[b][PHY_Mass] - PHY_Object[a][PHY_Mass]) * PHY_Object[b][PHY_VY] + 2 * PHY_Object[a][PHY_Mass] * tmpvy) / (PHY_Object[a][PHY_Mass] + PHY_Object[b][PHY_Mass]);
tmpvz = ((PHY_Object[b][PHY_Mass] - PHY_Object[a][PHY_Mass]) * PHY_Object[b][PHY_VZ] + 2 * PHY_Object[a][PHY_Mass] * tmpvz) / (PHY_Object[a][PHY_Mass] + PHY_Object[b][PHY_Mass]);
newvx2 += tmpvx;
newvy2 += tmpvy;
newvz2 += tmpvz;
PHY_Object[a][PHY_VX] = newvx1;
PHY_Object[a][PHY_VY] = newvy1;
PHY_Object[a][PHY_VZ] = newvz1;
PHY_Object[b][PHY_VX] = newvx2;
PHY_Object[b][PHY_VY] = newvy2;
PHY_Object[b][PHY_VZ] = newvz2;
CallLocalFunction("PHY_OnObjectCollideWithObject", "dd", a, b);
}
}
}
}
else Iter_SafeRemove(ITER_Object, b, b); //, printf("SAFE REMOVED B %d", b);
}
}
if(!PHY_IsHandleGhostWithWalls(a))
{
foreach(new w : ITER_Wall)
{
if(PHY_Wall[w][PHY_Created])
{
if((!PHY_Object[a][PHY_World] || !PHY_Wall[w][PHY_World] || PHY_Object[a][PHY_World] == PHY_Wall[w][PHY_World]))
{
//dist = (y1 - PHY_Wall[w][PHY_M] * x1 - PHY_Wall[w][PHY_Q])/floatsqroot(1 + PHY_Wall[w][PHY_M] * PHY_Wall[w][PHY_M]);
//dist = (PHY_Wall[w][PHY_A] * x1 + PHY_Wall[w][PHY_B] * y1 + PHY_Wall[w][PHY_C])/floatsqroot(PHY_Wall[w][PHY_A] * PHY_Wall[w][PHY_A] + PHY_Wall[w][PHY_B] * PHY_Wall[w][PHY_B]);
angle = PHY_Wall[w][PHY_ANG];
if(PHY_Wall[w][PHY_Z1] - PHY_Object[a][PHY_Size] < z1 < PHY_Wall[w][PHY_Z2] + PHY_Object[a][PHY_Size] &&
(check_segment_intersection(PHY_Wall[w][PHY_X1], PHY_Wall[w][PHY_Y1], PHY_Wall[w][PHY_X2], PHY_Wall[w][PHY_Y2], x1, y1, PHY_Object[a][PHY_Size], xclosest, yclosest) || /* && floatabs(dist) < PHY_Object[a][PHY_Size]*/
check_segment_intersection(PHY_Wall[w][PHY_X1], PHY_Wall[w][PHY_Y1], PHY_Wall[w][PHY_X2], PHY_Wall[w][PHY_Y2], (x + x1)/2, (y + y1)/2, PHY_Object[a][PHY_Size], xclosest, yclosest)))
{
//mag = y1 + PHY_Object[a][PHY_Size] * floatcos(-moveangle, degrees) - (x1 + PHY_Object[a][PHY_Size] * floatsin(-moveangle, degrees)) * PHY_Wall[w][PHY_M] - PHY_Wall[w][PHY_Q];
//mag = PHY_Wall[w][PHY_A] * (x1 + PHY_Object[a][PHY_Size] * floatsin(-moveangle, degrees)) + PHY_Wall[w][PHY_B] * (y1 + PHY_Object[a][PHY_Size] * floatcos(-moveangle, degrees)) + PHY_Wall[w][PHY_C];
//if((dist >= 0) ? (mag <= 0) : (mag >= 0))
newvx1 = PHY_Wall[w][PHY_BounceConst] * (PHY_Object[a][PHY_VX] * floatcos(angle, degrees) - PHY_Object[a][PHY_VY] * floatsin(angle, degrees));
newvy1 = -PHY_Wall[w][PHY_BounceConst] * (PHY_Object[a][PHY_VX] * floatsin(angle, degrees) + PHY_Object[a][PHY_VY] * floatcos(angle, degrees));
angle = -angle;
PHY_Object[a][PHY_VX] = newvx1 * floatcos(angle, degrees) - newvy1 * floatsin(angle, degrees);
PHY_Object[a][PHY_VY] = newvx1 * floatsin(angle, degrees) + newvy1 * floatcos(angle, degrees);
angle = angle + (newvy1 > 0.0 ? 90.0 : -90.0);
x1 = xclosest + (PHY_Object[a][PHY_Size] + 0.001) * floatcos(angle, degrees);
y1 = yclosest + (PHY_Object[a][PHY_Size] + 0.001) * floatsin(angle, degrees);
CallLocalFunction("PHY_OnObjectCollideWithWall", "dd", a, w);
}
}
}
else
Iter_SafeRemove(ITER_Wall, w, w);
}
}
if(!PHY_IsHandleGhostWithCylinders(a))
{
foreach(new c : ITER_Cylinder)
{
if(PHY_Cylinder[c][PHY_Created])
{
if((!PHY_Object[a][PHY_World] || !PHY_Cylinder[c][PHY_World] || PHY_Object[a][PHY_World] == PHY_Cylinder[c][PHY_World]))
{
if(PHY_Cylinder[c][PHY_Z1] - PHY_Object[a][PHY_Size] < z1 < PHY_Cylinder[c][PHY_Z2] + PHY_Object[a][PHY_Size])
{
x2 = PHY_Cylinder[c][PHY_X];
y2 = PHY_Cylinder[c][PHY_Y];
dx = x1 - x2;
dy = y1 - y2;
dist = (dx * dx) + (dy * dy);
maxdist = PHY_Object[a][PHY_Size] + PHY_Cylinder[c][PHY_Size];
if(dist < (maxdist * maxdist))
{
mag = PHY_Object[a][PHY_VX] * dx + PHY_Object[a][PHY_VY] * dy;
if(mag < 0.0)
{
angle = -atan2(dy, dx);
newvx1 = -PHY_Cylinder[c][PHY_BounceConst] * (PHY_Object[a][PHY_VX] * floatcos(angle, degrees) - PHY_Object[a][PHY_VY] * floatsin(angle, degrees));
newvy1 = PHY_Cylinder[c][PHY_BounceConst] * (PHY_Object[a][PHY_VX] * floatsin(angle, degrees) + PHY_Object[a][PHY_VY] * floatcos(angle, degrees));
angle = -angle;
PHY_Object[a][PHY_VX] = newvx1 * floatcos(angle, degrees) - newvy1 * floatsin(angle, degrees);
PHY_Object[a][PHY_VY] = newvx1 * floatsin(angle, degrees) + newvy1 * floatcos(angle, degrees);
CallLocalFunction("PHY_OnObjectCollideWithCylinder", "dd", a, c);
}
}
}
}
}
else
Iter_SafeRemove(ITER_Cylinder, c, c);
}
}
if(PHY_IsHandleCollidingWithPlayers(a))
{
foreach(new i : Player)
{
if((!PHY_Object[a][PHY_World] || !PHY_Player[i][PHY_World] || PHY_Object[a][PHY_World] == PHY_Player[i][PHY_World]))
{
GetPlayerPos(i, x2, y2, z2);
if(z2 - PHY_Object[a][PHY_PlayerLowZ] - PHY_Object[a][PHY_Size] < z1 < z2 + PHY_Object[a][PHY_PlayerHighZ] + PHY_Object[a][PHY_Size])
{
dx = x1 - x2;
dy = y1 - y2;
dist = (dx * dx) + (dy * dy);
maxdist = PHY_Object[a][PHY_Size] + PHY_Object[a][PHY_PlayerDist];
if(dist < (maxdist * maxdist))
{
mag = PHY_Object[a][PHY_VX] * dx + PHY_Object[a][PHY_VY] * dy;
if(mag < 0.0)
{
angle = -atan2(dy, dx);
newvx1 = -PHY_Object[a][PHY_PlayerConst] * (PHY_Object[a][PHY_VX] * floatcos(angle, degrees) - PHY_Object[a][PHY_VY] * floatsin(angle, degrees));
newvy1 = PHY_Object[a][PHY_PlayerConst] * (PHY_Object[a][PHY_VX] * floatsin(angle, degrees) + PHY_Object[a][PHY_VY] * floatcos(angle, degrees));
angle = -angle;
PHY_Object[a][PHY_VX] = newvx1 * floatcos(angle, degrees) - newvy1 * floatsin(angle, degrees);
PHY_Object[a][PHY_VY] = newvx1 * floatsin(angle, degrees) + newvy1 * floatcos(angle, degrees);
CallLocalFunction("PHY_OnObjectCollideWithPlayer", "dd", a, i);
}
}
}
}
}
}
moveangle = atan2(PHY_Object[a][PHY_VY], PHY_Object[a][PHY_VX]) - 90.0;
speed = floatsqroot(PHY_Object[a][PHY_VX] * PHY_Object[a][PHY_VX] + PHY_Object[a][PHY_VY] * PHY_Object[a][PHY_VY]);
if(PHY_GetHandleFriction(a) != 0.0 && z1 == PHY_Object[a][PHY_LowZBound])
{
speed -= PHY_Object[a][PHY_Friction] * (PHY_TIMER_INTERVAL/1000.0);
if(speed < 0.001)
speed = 0.0;
PHY_Object[a][PHY_VX] = speed * floatsin(-moveangle, degrees);
PHY_Object[a][PHY_VY] = speed * floatcos(-moveangle, degrees);
}
if(PHY_GetHandleAirResistance(a) != 0.0)
{
PHY_Object[a][PHY_VX] -= PHY_Object[a][PHY_VX] * PHY_Object[a][PHY_AirResistance] * (PHY_TIMER_INTERVAL/1000.0);
PHY_Object[a][PHY_VY] -= PHY_Object[a][PHY_VY] * PHY_Object[a][PHY_AirResistance] * (PHY_TIMER_INTERVAL/1000.0);
PHY_Object[a][PHY_VZ] -= PHY_Object[a][PHY_VZ] * PHY_Object[a][PHY_AirResistance] * (PHY_TIMER_INTERVAL/1000.0);
}
if(PHY_IsHandleRolling(a) && speed > 0.0)
PHY_ApplyRotation(a, speed, moveangle);
}
PHY_Object[a][PHY_VX] += PHY_Object[a][PHY_AX];
PHY_Object[a][PHY_VY] += PHY_Object[a][PHY_AY];
PHY_Object[a][PHY_VZ] += PHY_Object[a][PHY_AZ];
SetDynamicObjectPos(obj_a, x1, y1, z1);
CallLocalFunction("PHY_OnObjectUpdate", "d", a);
}
else
{
Iter_SafeRemove(ITER_Object, a, a); //, printf("SAFE REMOVED A %d", a);
}
}
return 1;
}
/* Starts using physics for objectid.
modelid - object's modelid, used to get its size with modelsizes include.
mass - object's mass, it is like its weight and is used in collisions.
size - object's sphere radius, taken from modelsizes.inc by default.
mode - PHY_MODE_3D or PHY_MODE_2D. */
stock PHY_InitObject(objectid, modelid = 0, Float:mass = 1.0, Float:size = FLOAT_NAN, mode = PHY_MODE_3D)
{
new
handleid = Iter_Free(ITER_Object);
if(handleid != ITER_NONE)
{
new
Float:z;
GetDynamicObjectPos(objectid, z, z, z);
PHY_Object[handleid][PHY_DynamicObject] = objectid;
PHY_Object[handleid][PHY_Mode] = mode;
PHY_Object[handleid][PHY_Mass] = mass;
PHY_Object[handleid][PHY_World] = 0;
PHY_Object[handleid][PHY_VX] = 0.0;
PHY_Object[handleid][PHY_VY] = 0.0;
PHY_Object[handleid][PHY_VZ] = 0.0;
PHY_Object[handleid][PHY_Gravity] = 0.0;
PHY_Object[handleid][PHY_LowZBound] = z;
PHY_Object[handleid][PHY_HighZBound] = FLOAT_INFINITY;
PHY_Object[handleid][PHY_BoundConst] = 0.0;
PHY_Object[handleid][PHY_Created] = 1;
if(size != size)
{
if(modelid)
PHY_Object[handleid][PHY_Size] = GetColSphereRadius(modelid);
}
else
PHY_Object[handleid][PHY_Size] = size;
Iter_Add(ITER_Object, handleid);
}
return handleid;
}
/* Stops using physics for objectid (doesn't destroy it). */
stock PHY_DeleteHandle(handle)
{
if(PHY_IsHandleValid(handle)) {
PHY_Object[handle][PHY_Created] = 0;
}
return 1;
}
/* Moves the object with vx, vy, vz velocities. */
stock PHY_SetHandleVelocity(objectid, Float:vx, Float:vy, Float:vz = 0.0)
{
if(PHY_IsHandleValid(objectid))
{
PHY_Object[objectid][PHY_VX] = vx;
PHY_Object[objectid][PHY_VY] = vy;
PHY_Object[objectid][PHY_VZ] = vz;
return 1;
}
return 0;
}
/* Self-explanatory */
stock PHY_GetHandleVelocity(objectid, &Float:vx, &Float:vy, &Float:vz)
{
if(PHY_IsHandleValid(objectid))
{
vx = PHY_Object[objectid][PHY_VX];
vy = PHY_Object[objectid][PHY_VY];
vz = PHY_Object[objectid][PHY_VZ];
return 1;
}
return 0;
}
/* Sets the object's acceleration. */
stock PHY_SetHandleAcceleration(objectid, Float:ax, Float:ay, Float:az = 0.0)
{
if(PHY_IsHandleValid(objectid))
{
PHY_Object[objectid][PHY_AX] = ax;
PHY_Object[objectid][PHY_AY] = ay;
PHY_Object[objectid][PHY_AZ] = az;
return 1;
}
return 0;
}
/* Self-explanatory */
stock PHY_GetHandleAcceleration(objectid, &Float:ax, &Float:ay, &Float:az)
{
if(PHY_IsHandleValid(objectid))
{
ax = PHY_Object[objectid][PHY_AX];
ay = PHY_Object[objectid][PHY_AY];
az = PHY_Object[objectid][PHY_AZ];
return 1;
}
return 0;
}
/* Self-explanatory */
stock PHY_GetHandleSpeed(objectid, &Float:speed, _3D = 0)
{
if(PHY_IsHandleValid(objectid))
{
speed = floatsqroot(PHY_Object[objectid][PHY_VX] * PHY_Object[objectid][PHY_VX] + PHY_Object[objectid][PHY_VY] * PHY_Object[objectid][PHY_VY] + _3D ? (PHY_Object[objectid][PHY_VZ] * PHY_Object[objectid][PHY_VZ]) : 0.0);
return 1;
}
return 0;
}
/* Self-explanatory */
stock PHY_GetHandleMoveAngle(objectid, &Float:moveangle)
{
if(PHY_IsHandleValid(objectid))
{
moveangle = atan2(PHY_Object[objectid][PHY_VY], PHY_Object[objectid][PHY_VX]) - 90.0;
return 1;
}
return 0;
}
/* Starts rolling the object when it moves of toggle = 1 or stops if toggle = 0. */
stock PHY_RollObject(objectid, bool: toggle = true)
{
if(PHY_IsHandleValid(objectid))
{
PHY_Object[objectid][PHY_Rolling] = toggle;
return 1;
}
return 0;
}
/* Applies friction to the object when it moves on the floor. */
stock PHY_SetHandleFriction(objectid, Float:friction)
{
if(PHY_IsHandleValid(objectid))
{
if(friction >= 0.0)
PHY_Object[objectid][PHY_Friction] = friction;
return 1;
}
return 0;
}
/* Applies air resistance to the object when it moves. */
stock PHY_SetHandleAirResistance(objectid, Float:resistance)
{
if(PHY_IsHandleValid(objectid))
{
if(0.0 <= resistance <= 1.0)
PHY_Object[objectid][PHY_AirResistance] = resistance;
return 1;
}
return 0;
}
/* Limits the object's Z position.
low - The lowest Z that the object can have (you can use FLOAT_NEG_INFINITY). If it is set to NaN it doesn't change.
high - The highest Z that the object can have (you can use FLOAT_INFINITY). If it is set to NaN it doesn't change.
(When you use PHY_InitObject lowest Z is set to the current object's Z and highest Z to FLOAT_INFINITY.
constant - It should be from 0.0 to 1.0. If it is 1.0 the object doesn't lose velocity,
if it is 0.0 the object stops when it bounces. It could be a middle ground.*/
stock PHY_SetHandleZBound(objectid, Float:low = FLOAT_NAN, Float:high = FLOAT_NAN, Float:constant = 0.0)
{
if(PHY_IsHandleValid(objectid))
{
if(low == low)
PHY_Object[objectid][PHY_LowZBound] = low;
if(high == high)
PHY_Object[objectid][PHY_HighZBound] = high;
PHY_Object[objectid][PHY_BoundConst] = constant;
return 1;
}
return 0;
}
/* Sets the gravity's acceleration that the object is subjected to. */
stock PHY_SetHandleGravity(objectid, Float:gravity)
{
if(PHY_IsHandleValid(objectid))
{
PHY_Object[objectid][PHY_Gravity] = gravity;
return 1;
}
return 0;
}
/* Object and walls collide only if the are in the same world or one of them is in the world 0 (default). */
stock PHY_SetHandleWorld(objectid, world)
{
if(PHY_IsHandleValid(objectid))
{
PHY_Object[objectid][PHY_World] = world;
return 1;
}
return 0;
}
/* Toggles object's collisions with players.
- constant - It should be from 0.0 to 1.0. If it is 1.0 the object doesn't lose velocity,
if it is 0.0 the object stops when it bounces. It could be a middle ground.
- distoffset - The distance at which the object collides with the player.
- zoffsetlow/zoffsethigh - The max Z distance (downward/upward) at which the object collides with the player. */
stock PHY_ToggleObjectPlayerColls(objectid, bool: toggle = true, Float:constant = 1.0, Float:distoffset = 0.8, Float:zoffsetlow = 1.0, Float:zoffsethigh = 1.0)
{
if(PHY_IsHandleValid(objectid))
{
PHY_Object[objectid][PHY_Object_Player_Collision] = toggle;
if(toggle)
{
PHY_Object[objectid][PHY_PlayerConst] = constant;
PHY_Object[objectid][PHY_PlayerDist] = distoffset;
PHY_Object[objectid][PHY_PlayerLowZ] = zoffsetlow;
PHY_Object[objectid][PHY_PlayerHighZ] = zoffsethigh;
}
return 1;
}
return 0;
}
/* Used internally to rotate the objects */
stock PHY_ApplyRotation(handleid, Float:speed, Float:moveangle)
{
new objectid = PHY_Object[handleid][PHY_DynamicObject];
new Float:rx, Float:ry, Float:rz;
GetDynamicObjectRot(objectid, rx, ry, rz);
rx -= speed * (PHY_TIMER_INTERVAL/1000.0) * (180.0/3.14159) / PHY_Object[handleid][PHY_Size];
if(rx < 0.0)
rx += 360.0;
rz = moveangle;
//FixRot(rx, ry, rz);
//PHY_Roll(rx, ry, rz, PHY_Object[handleid][PHY_VX], PHY_Object[handleid][PHY_VY], ((speed * PHY_TIMER_INTERVAL)/1000) * (180/3.14159) / PHY_Object[handleid][PHY_Size]);
SetDynamicObjectRot(objectid, rx, ry, rz);
return 1;
}
/* Creates a collision wall (straight line) from A(x1, y1) to B(x2, y2).
constant should be from 0.0 to 1.0. If it is 1.0 the object doesn't lose velocity,
if it is 0.0 the object stops when it collides.
low is the lowest wall's Z, high is the highest. If they're set to default the wall is like infinitely high.*/
stock PHY_CreateWall(Float:x1, Float:y1, Float:x2, Float:y2, Float:constant = 1.0, Float:low = FLOAT_NEG_INFINITY, Float:high = FLOAT_INFINITY)
{
new
i = Iter_Free(ITER_Wall);
if(i != ITER_NONE)
{
if(!PHY_Wall[i][PHY_Created])
{
PHY_Wall[i][PHY_Created] = 1;
PHY_Wall[i][PHY_World] = 0;
PHY_Wall[i][PHY_X1] = x1;
PHY_Wall[i][PHY_Y1] = y1;
PHY_Wall[i][PHY_X2] = x2;
PHY_Wall[i][PHY_Y2] = y2;
PHY_Wall[i][PHY_Z1] = low;
PHY_Wall[i][PHY_Z2] = high;
PHY_Wall[i][PHY_BounceConst] = constant;
PHY_Wall[i][PHY_ANG] = atan2(y1 - y2, x1 - x2);
/*PHY_Wall[i][PHY_A] = -(y2 - y1);
PHY_Wall[i][PHY_B] = (x2 - x1);
PHY_Wall[i][PHY_C] = (y2 - y1) * x1 - (x2 - x1) * y1;*/
//PHY_Wall[i][PHY_Q] = -((y2 - y1) * x1)/(x2 - x1) + y1;
Iter_Add(ITER_Wall, i);
}
}
return i;
}
/* Creates four walls that form an area. Works like IsPlayerInArea. */
stock PHY_CreateArea(Float:minX, Float:minY, Float:maxX, Float:maxY, Float:constant = 1.0, Float:low = FLOAT_NEG_INFINITY, Float:high = FLOAT_INFINITY)
{
PHY_CreateWall(minX, minY, minX, maxY, constant, low, high);
PHY_CreateWall(minX, maxY, maxX, maxY, constant, low, high);
PHY_CreateWall(maxX, maxY, maxX, minY, constant, low, high);
PHY_CreateWall(maxX, minY, minX, minY, constant, low, high);
}
/* Self-explanatory */
stock PHY_DestroyWall(wallid)
{
PHY_Wall[wallid][PHY_Created] = 0;
// Iter_Remove(ITER_Wall, wallid);
return 1;
}
/* See PHY_SetHandleWorld(objectid, world). */
stock PHY_SetWallWorld(wallid, world)
{
if(PHY_Wall[wallid][PHY_Created])
{
PHY_Wall[wallid][PHY_World] = world;
return 1;
}
return 0;
}
/* Creates a collision cylinder at position x, y.
constant should be from 0.0 to 1.0. If it is 1.0 the object doesn't lose velocity,
if it is 0.0 the object stops when it collides.
low is the lowest cylinder's Z, high is the highest. If they're set to default the cylinder is like infinitely high.*/
stock PHY_CreateCylinder(Float:x, Float:y, Float:size, Float:constant = 1.0, Float:low = FLOAT_NEG_INFINITY, Float:high = FLOAT_INFINITY)
{
new
i = Iter_Free(ITER_Cylinder);
if(i != ITER_NONE)
{
if(!PHY_Cylinder[i][PHY_Created])
{
PHY_Cylinder[i][PHY_Created] = 1;
PHY_Cylinder[i][PHY_World] = 0;
PHY_Cylinder[i][PHY_X] = x;
PHY_Cylinder[i][PHY_Y] = y;
PHY_Cylinder[i][PHY_Size] = size;
PHY_Cylinder[i][PHY_Z1] = low;
PHY_Cylinder[i][PHY_Z2] = high;
PHY_Cylinder[i][PHY_BounceConst] = constant;
Iter_Add(ITER_Cylinder, i);
}
}
return i;
}
/* Self-explanatory */
stock PHY_DestroyCylinder(cylinderid)
{
PHY_Cylinder[cylinderid][PHY_Created] = 0;
// Iter_Remove(ITER_Cylinder, cylinderid);
return 1;
}
/* See PHY_SetHandleWorld(objectid, world). */
stock PHY_SetCylinderWorld(cylinderid, world)
{
if(PHY_Cylinder[cylinderid][PHY_Created])
{
PHY_Cylinder[cylinderid][PHY_World] = world;
return 1;
}
return 0;
}
/* See PHY_SetHandleWorld(objectid, world). */
stock PHY_SetPlayerWorld(playerid, world)
{
PHY_Player[playerid][PHY_World] = world;
return 1;
}
/* Privates */
stock Float:vectordotp(Float:v1x, Float:v1y, Float:v1z, Float:v2x, Float:v2y, Float:v2z)
return (v1x * v2x + v1y * v2y + v1z * v2z);
stock projectVonU(Float:vx, Float:vy, Float:vz, Float:ux, Float:uy, Float:uz, &Float:x, &Float:y, &Float:z)
{
new Float:fac = vectordotp(vx, vy, vz, ux, uy, uz) / vectordotp(ux, uy, uz, ux, uy, uz);
x = ux * fac;
y = uy * fac;
z = uz * fac;
}
stock check_segment_intersection(Float:x1, Float:y1, Float:x2, Float:y2, Float:xc, Float:yc, Float:r, &Float:x, &Float:y)
{
new Float:v1[2];
new Float:v2[2];
v1[0] = x2 - x1;
v1[1] = y2 - y1;
v2[0] = xc - x1;
v2[1] = yc - y1;
new Float:v1_len = floatsqroot(v1[0] * v1[0] + v1[1] * v1[1]);
v1[0] /= v1_len;
v1[1] /= v1_len;
new Float:proj = vectordotp(v2[0], v2[1], 0.0, v1[0], v1[1], 0.0);
x = proj * v1[0] + x1;
y = proj * v1[1] + y1;
return ((0 - r < proj < v1_len + r) && ((x - xc) * (x - xc) + (y - yc) * (y - yc) < (r * r)));
}
stock FixRot(&Float:x, &Float:y, &Float:z)
{
FixAngle(x);
FixAngle(y);
FixAngle(z);
}
stock FixAngle(&Float:x)
{
while(x < 0.0)
x += 360.0;
while(x > 360.0)
x -= 360.0;
if(x < 0.01)
x = 0.01;
else if(89.99 < x <= 90.0)
x = 89.99;
else if(90.0 < x < 90.01)
x = 90.01;
else if(179.99 < x <= 180.0)
x = 179.99;
else if(180.0 < x < 180.01)
x = 180.01;
else if(269.99 < x <= 270.0)
x = 269.99;
else if(270.0 < x < 270.01)
x = 270.01;
else if(x > 359.99)
x = 359.99;
}
/* Function to make a better ball rolling, not used because it doesn't work well. */
stock PHY_Roll(&Float:x, &Float:y, &Float:z, Float:dx, Float:dy, Float:speed)
{
new Float:q_roll[4];
new Float:vx, Float:vy, Float:vz;
vectorcrossp(dx, dy, 0.0, 0.0, 0.0, 1.0, vx, vy, vz);
QuatFromAxisAngle(vx, vy, vz, -speed, q_roll[0], q_roll[1], q_roll[2], q_roll[3]);
new Float:q_rot[4];
EulerToQuaternion(x, y, z, q_rot[0], q_rot[1], q_rot[2], q_rot[3]);
new Float:q_res[4];
QuatMultiply(q_rot[0], q_rot[1], q_rot[2], q_rot[3], q_roll[0], q_roll[1], q_roll[2], q_roll[3], q_res[0], q_res[1], q_res[2], q_res[3]);
QuaternionToEuler(q_res[0], q_res[1], q_res[2], q_res[3], x, y, z);
}
stock QuatMultiply(Float:w1, Float:x1, Float:y1, Float:z1, Float:w2, Float:x2, Float:y2, Float:z2, &Float:q_w, &Float:q_x, &Float:q_y, &Float:q_z)
{
q_w = w1*w2 - x1*x2 - y1*y2 - z1*z2;
q_x = w1*x2 + x1*w2 + y1*z2 - z1*y2;
q_y = w1*y2 - x1*z2 + y1*w2 + z1*x2;
q_z = w1*z2 + x1*y2 - y1*x2 + z1*w2;
QuatNormalize(q_w, q_x, q_y, q_z);
}
stock QuatFromAxisAngle(Float:x, Float:y, Float:z, Float:angle, &Float:q_w, &Float:q_x, &Float:q_y, &Float:q_z)
{
new
Float:omega,
Float:s;
s = floatsqroot(x*x + y*y + z*z);
if (s > 0.01)
{
x /= s;
y /= s;
z /= s;
omega = 0.5 * angle;
s = floatsin(omega, degrees);
q_x = s*x;
q_y = s*y;
q_z = s*z;
q_w = floatcos(omega, degrees);
}
else
{
q_x = 0.0;
q_y = 0.0;
q_z = 0.0;
q_w = 1.0;
}
QuatNormalize(q_w, q_x, q_y, q_z);
}
stock QuatNormalize(&Float:q_w, &Float:q_x, &Float:q_y, &Float:q_z)
{
new Float:magnitude = floatsqroot(q_w*q_w + q_x*q_x + q_y*q_y + q_z*q_z);
if (magnitude == 0.0)
{
q_w = 1.0;
q_x = 0.0;
q_y = 0.0;
q_z = 0.0;
}
else
{
q_w /= magnitude;
q_x /= magnitude;
q_y /= magnitude;
q_z /= magnitude;
}
}
stock EulerToQuaternion(Float:z, Float:x, Float:y, &Float:q_w, &Float:q_x, &Float:q_y, &Float:q_z)
{
new Float:c1 = floatcos(x/2.0, degrees);
new Float:s1 = floatsin(x/2.0, degrees);
new Float:c2 = floatcos(y/2.0, degrees);
new Float:s2 = floatsin(y/2.0, degrees);
new Float:c3 = floatcos(z/2.0, degrees);
new Float:s3 = floatsin(z/2.0, degrees);
new Float:c1c2 = c1 * c2;
new Float:s1s2 = s1 * s2;
q_w = c1c2 * c3 - s1s2 * s3;
q_x = c1c2 * s3 + s1s2 * c3;
q_y = s1*c2*c3 + c1*s2*s3;
q_z = c1*s2*c3 - s1*c2*s3;
QuatNormalize(q_w, q_x, q_y, q_z);
}
stock QuaternionToEuler(Float:q_w, Float:q_x, Float:q_y, Float:q_z, &Float:z, &Float:x, &Float:y)
{
new Float:test = q_x*q_y + q_z*q_w;
if (test > 0.499) { // singularity at north pole
x = 2 * atan2(q_x,q_w);
y = 90.0;
z = 0;
return;
}
if (test < -0.499) { // singularity at south pole
x = -2 * atan2(q_x,q_w);
y = -90.0;
z = 0;
return;
}
new Float:sqx = q_x*q_x;
new Float:sqy = q_y*q_y;
new Float:sqz = q_z*q_z;
x = atan2(2*q_y*q_w-2*q_x*q_z , 1 - 2*sqy - 2*sqz);
y = asin(2*test);
z = atan2(2*q_x*q_w-2*q_y*q_z , 1 - 2*sqx - 2*sqz);
}
stock vectorcrossp(Float:v1x, Float:v1y, Float:v1z, Float:v2x, Float:v2y, Float:v2z, &Float:c1, &Float:c2, &Float:c3)
{
c1 = (v1y * v2z) - (v1z * v2y),
c2 = (v1z * v2x) - (v1x * v2z),
c3 = (v1x * v2y) - (v1y * v2x);
}
#if defined _ALS_OnGameModeInit
#undef OnGameModeInit
#else
#define _ALS_OnGameModeInit
#endif
#define OnGameModeInit PHY_OnGameModeInit
forward OnGameModeInit();
#if defined _ALS_OnPlayerConnect
#undef OnPlayerConnect
#else
#define _ALS_OnPlayerConnect
#endif
#define OnPlayerConnect PHY_OnPlayerConnect
forward OnPlayerConnect(playerid);