1
0

projectile.inc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. //projectile.inc - By Gammix (originally by PeppeAC) - v1.3.5 - updated: 9 April,2017
  2. #if defined projectile_included
  3. #endinput
  4. #endif
  5. #define projectile_included
  6. #include <colandreas>
  7. #if !defined MAX_PROJECTILES
  8. #define MAX_PROJECTILES \
  9. 100
  10. #endif
  11. #if !defined PROJECTILE_TIMER_INTERVAL
  12. #define PROJECTILE_TIMER_INTERVAL \
  13. 20
  14. #endif
  15. #if !defined FLOAT_INFINITY
  16. #define FLOAT_INFINITY \
  17. Float:0x7F800000
  18. #endif
  19. #if defined OnProjectileUpdate
  20. forward OnProjectileUpdate(projid);
  21. #endif
  22. #if defined OnProjectileStop
  23. forward OnProjectileStop(projid);
  24. #endif
  25. #if defined OnProjectileCollide
  26. forward OnProjectileCollide(projid, type, Float:x, Float:y, Float:z, extraid);
  27. #endif
  28. /*
  29. native Projectile(Float:x, Float:y, Float:z, Float:vx, Float:vy, Float:vz, Float:rx = 0.0, Float:ry = 0.0, Float:rz = 0.0, Float:sphere_radius = 1.0, Float:ground_friction = 5.0, Float:collision_friction = 0.2, Float:air_resistance = 0.5, Float:gravity = 10.0, Float:playercol_radius = 0.8);
  30. native IsValidProjectile(projid);
  31. native StopProjectile(projid);
  32. native PushProjectile(projid, Float:vx, Float:vy, Float:vz);
  33. native GetProjectilePos(projid, &Float:x, &Float:y, &Float:z);
  34. native GetProjectileRot(projid, &Float:rx, &Float:ry, &Float:rz);
  35. native GetProjectileVelocity(projid, &Float:vx, &Float:vy, &Float:vz);
  36. native GetProjectilePoolSize();
  37. */
  38. static iInitializetimer;
  39. enum e_PROJECTILE
  40. {
  41. bool:e_PROJECTILE_VALID,
  42. Float:e_PROJECTILE_X,
  43. Float:e_PROJECTILE_Y,
  44. Float:e_PROJECTILE_Z,
  45. Float:e_PROJECTILE_RX,
  46. Float:e_PROJECTILE_RY,
  47. Float:e_PROJECTILE_RZ,
  48. Float:e_PROJECTILE_VX,
  49. Float:e_PROJECTILE_VY,
  50. Float:e_PROJECTILE_VZ,
  51. Float:e_PROJECTILE_RADIUS,
  52. Float:e_PROJECTILE_MASS,
  53. Float:e_PROJECTILE_GROUND_FRICTION,
  54. Float:e_PROJECTILE_COLLISION_FRICTION,
  55. Float:e_PROJECTILE_GRAVITY,
  56. Float:e_PROJECTILE_AIR_RESISTANCE,
  57. Float:e_PROJECTILE_PLAYERCOL_RADIUS
  58. };
  59. static eProjectile[MAX_PROJECTILES][e_PROJECTILE];
  60. static iProjectilePoolSize;
  61. stock Projectile(Float:x, Float:y, Float:z, Float:vx, Float:vy, Float:vz, Float:rx = 0.0, Float:ry = 0.0, Float:rz = 0.0, Float:sphere_radius = 1.0, Float:ground_friction = 5.0, Float:collision_friction = 0.2, Float:air_resistance = 0.5, Float:gravity = 10.0, Float:playercol_radius = 0.8)
  62. {
  63. if (sphere_radius <= 0.0)
  64. {
  65. print("[projectile.inc] - Error: \"sphere_radius\" in function \"Projectile(..)\" cannot be negative or 0.");
  66. return -1;
  67. }
  68. if (playercol_radius < 0.0)
  69. {
  70. print("[projectile.inc] - Error: \"playercol_radius\" in function \"Projectile(..)\" cannot be negative; use 0.0 to disable player collision.");
  71. return -1;
  72. }
  73. if (iInitializetimer == 0)
  74. {
  75. iInitializetimer = SetTimer("Internal_OnProjectilesUpdate", PROJECTILE_TIMER_INTERVAL, true);
  76. }
  77. new id = -1;
  78. for (new i; i < MAX_PROJECTILES; i++)
  79. {
  80. if (!eProjectile[i][e_PROJECTILE_VALID])
  81. {
  82. id = i;
  83. break;
  84. }
  85. }
  86. if (id == -1)
  87. {
  88. printf("[projectile.inc] - Error: Cannot create more than \"%i\" active projectiles. Increase the value of \"MAX_PROJECTILES\" in your script.", MAX_PROJECTILES);
  89. return -1;
  90. }
  91. if (id > iProjectilePoolSize)
  92. {
  93. iProjectilePoolSize = id;
  94. }
  95. eProjectile[id][e_PROJECTILE_VALID] = true;
  96. eProjectile[id][e_PROJECTILE_X] = x;
  97. eProjectile[id][e_PROJECTILE_Y] = y;
  98. eProjectile[id][e_PROJECTILE_Z] = z;
  99. eProjectile[id][e_PROJECTILE_RX] = rx;
  100. eProjectile[id][e_PROJECTILE_RY] = ry;
  101. eProjectile[id][e_PROJECTILE_RZ] = rz;
  102. eProjectile[id][e_PROJECTILE_VX] = vx;
  103. eProjectile[id][e_PROJECTILE_VY] = vy;
  104. eProjectile[id][e_PROJECTILE_VZ] = vz;
  105. eProjectile[id][e_PROJECTILE_RADIUS] = sphere_radius;
  106. eProjectile[id][e_PROJECTILE_GROUND_FRICTION] = ground_friction;
  107. eProjectile[id][e_PROJECTILE_COLLISION_FRICTION] = collision_friction;
  108. eProjectile[id][e_PROJECTILE_AIR_RESISTANCE] = air_resistance;
  109. eProjectile[id][e_PROJECTILE_GRAVITY] = gravity;
  110. eProjectile[id][e_PROJECTILE_PLAYERCOL_RADIUS] = playercol_radius;
  111. return id;
  112. }
  113. stock IsValidProjectile(projid)
  114. {
  115. if (projid < 0 || projid > iProjectilePoolSize)
  116. return 0;
  117. return eProjectile[projid][e_PROJECTILE_VALID];
  118. }
  119. stock StopProjectile(projid)
  120. {
  121. if (projid < 0 || projid > iProjectilePoolSize)
  122. return 0;
  123. if (!eProjectile[projid][e_PROJECTILE_VALID])
  124. return 0;
  125. eProjectile[projid][e_PROJECTILE_VALID] = false;
  126. if (projid == iProjectilePoolSize)
  127. {
  128. for (new i = iProjectilePoolSize; i != -1; i--)
  129. {
  130. if (eProjectile[i][e_PROJECTILE_VALID])
  131. {
  132. iProjectilePoolSize = i;
  133. break;
  134. }
  135. }
  136. }
  137. if (iProjectilePoolSize == 0)
  138. {
  139. KillTimer(iInitializetimer);
  140. iInitializetimer = 0;
  141. }
  142. return 1;
  143. }
  144. stock PushProjectile(projid, Float:vx, Float:vy, Float:vz)
  145. {
  146. if (projid < 0 || projid > iProjectilePoolSize)
  147. return 0;
  148. eProjectile[projid][e_PROJECTILE_VX] += vx;
  149. eProjectile[projid][e_PROJECTILE_VY] += vy;
  150. eProjectile[projid][e_PROJECTILE_VZ] += vz;
  151. return 1;
  152. }
  153. stock GetProjectilePos(projid, &Float:x, &Float:y, &Float:z)
  154. {
  155. if (projid < 0 || projid > iProjectilePoolSize)
  156. return 0;
  157. x = eProjectile[projid][e_PROJECTILE_X];
  158. y = eProjectile[projid][e_PROJECTILE_Y];
  159. z = eProjectile[projid][e_PROJECTILE_Z];
  160. return 1;
  161. }
  162. stock GetProjectileRot(projid, &Float:rx, &Float:ry, &Float:rz)
  163. {
  164. if (projid < 0 || projid > iProjectilePoolSize)
  165. return 0;
  166. rx = eProjectile[projid][e_PROJECTILE_RX];
  167. ry = eProjectile[projid][e_PROJECTILE_RY];
  168. rz = eProjectile[projid][e_PROJECTILE_RZ];
  169. return 1;
  170. }
  171. stock GetProjectileVelocity(projid, &Float:vx, &Float:vy, &Float:vz)
  172. {
  173. if (projid < 0 || projid > iProjectilePoolSize)
  174. return 0;
  175. vx = eProjectile[projid][e_PROJECTILE_VX];
  176. vy = eProjectile[projid][e_PROJECTILE_VY];
  177. vz = eProjectile[projid][e_PROJECTILE_VZ];
  178. return 1;
  179. }
  180. stock GetProjectilePoolSize()
  181. {
  182. return iProjectilePoolSize;
  183. }
  184. forward Internal_OnProjectilesUpdate();
  185. public Internal_OnProjectilesUpdate()
  186. {
  187. new Float:unused,
  188. Float:new_x,
  189. Float:new_y,
  190. Float:new_z,
  191. Float:max_height,
  192. Float:min_height,
  193. Float:cx,
  194. Float:cy,
  195. Float:cz,
  196. Float:crx,
  197. Float:cry,
  198. extraid,
  199. Float:dx,
  200. Float:dy,
  201. Float:moveangle,
  202. Float:new_vx,
  203. Float:new_vy,
  204. Float:speed,
  205. bool:collision;
  206. for (new i; i <= iProjectilePoolSize; i++)
  207. {
  208. if (!eProjectile[i][e_PROJECTILE_VALID])
  209. continue;
  210. collision = false;
  211. // calculate next position at timestep
  212. new_x = eProjectile[i][e_PROJECTILE_X] + eProjectile[i][e_PROJECTILE_VX] * (PROJECTILE_TIMER_INTERVAL / 1000.0);
  213. new_y = eProjectile[i][e_PROJECTILE_Y] + eProjectile[i][e_PROJECTILE_VY] * (PROJECTILE_TIMER_INTERVAL / 1000.0);
  214. new_z = eProjectile[i][e_PROJECTILE_Z] + eProjectile[i][e_PROJECTILE_VZ] * (PROJECTILE_TIMER_INTERVAL / 1000.0);
  215. // calculate max & min height the sphere can reach
  216. min_height = 0.0;
  217. if (CA_RayCastLine(eProjectile[i][e_PROJECTILE_X], eProjectile[i][e_PROJECTILE_Y], eProjectile[i][e_PROJECTILE_Z], new_x, new_y, new_z - 1000.0, unused, unused, min_height) != 0)
  218. {
  219. min_height += eProjectile[i][e_PROJECTILE_RADIUS];
  220. }
  221. else
  222. {
  223. min_height = eProjectile[i][e_PROJECTILE_Z] - 1.0;
  224. }
  225. max_height = 0.0;
  226. if (CA_RayCastLine(eProjectile[i][e_PROJECTILE_X], eProjectile[i][e_PROJECTILE_Y], eProjectile[i][e_PROJECTILE_Z], new_x, new_y, new_z + 1000.0, unused, unused, max_height) != 0)
  227. {
  228. if (max_height > min_height)
  229. max_height -= eProjectile[i][e_PROJECTILE_RADIUS];
  230. else
  231. max_height = FLOAT_INFINITY;
  232. }
  233. else
  234. {
  235. max_height = FLOAT_INFINITY;
  236. }
  237. if (new_z > max_height)
  238. {
  239. if (eProjectile[i][e_PROJECTILE_VZ] > 0)
  240. eProjectile[i][e_PROJECTILE_VZ] = -eProjectile[i][e_PROJECTILE_VZ] * 0.8;
  241. #if defined OnProjectileCollide
  242. OnProjectileCollide(i, 1, new_x, new_y, new_z, 0); // COLLIDE_HIGH_Z_BOUND
  243. #endif
  244. new_z = max_height;
  245. }
  246. else if (new_z < min_height)
  247. {
  248. if (eProjectile[i][e_PROJECTILE_VZ] < 0)
  249. eProjectile[i][e_PROJECTILE_VZ] = -eProjectile[i][e_PROJECTILE_VZ] * 0.8;
  250. #if defined OnProjectileCollide
  251. OnProjectileCollide(i, 2, new_x, new_y, new_z, 0); // COLLIDE_LOW_Z_BOUND
  252. #endif
  253. new_z = min_height;
  254. }
  255. // apply gravitation force
  256. if (eProjectile[i][e_PROJECTILE_GRAVITY] != 0.0)
  257. {
  258. if (eProjectile[i][e_PROJECTILE_VZ] > 0)
  259. {
  260. eProjectile[i][e_PROJECTILE_VZ] -= eProjectile[i][e_PROJECTILE_GRAVITY] * (PROJECTILE_TIMER_INTERVAL / 1000.0);
  261. if (eProjectile[i][e_PROJECTILE_VZ] < 0)
  262. eProjectile[i][e_PROJECTILE_VZ] = 0;
  263. }
  264. else
  265. eProjectile[i][e_PROJECTILE_VZ] -= eProjectile[i][e_PROJECTILE_GRAVITY] * (PROJECTILE_TIMER_INTERVAL / 1000.0);
  266. }
  267. // collision check with SA-World
  268. cx = cy = cz = crx = cry = 0.0;
  269. if ((extraid = CA_RayCastLineAngle(eProjectile[i][e_PROJECTILE_X], eProjectile[i][e_PROJECTILE_Y], eProjectile[i][e_PROJECTILE_Z], new_x, new_y, new_z, cx, cy, cz, crx, cry, unused)) != 0)
  270. {
  271. moveangle = atan2(-cry, crx);
  272. new_vx = ((eProjectile[i][e_PROJECTILE_VX] * floatcos(moveangle, degrees)) - (eProjectile[i][e_PROJECTILE_VY] * floatsin(moveangle, degrees)));
  273. new_vy = -((eProjectile[i][e_PROJECTILE_VX] * floatsin(moveangle, degrees)) + (eProjectile[i][e_PROJECTILE_VY] * floatcos(moveangle, degrees)));
  274. moveangle *= -1;
  275. eProjectile[i][e_PROJECTILE_VX] = ((new_vx * floatcos(moveangle, degrees)) - (new_vy * floatsin(moveangle, degrees)));
  276. eProjectile[i][e_PROJECTILE_VY] = ((new_vx * floatsin(moveangle, degrees)) + (new_vy * floatcos(moveangle, degrees)));
  277. moveangle += ((new_vy > 0) ? (90.0) : (-90.0));
  278. new_x = (cx + (eProjectile[i][e_PROJECTILE_RADIUS] * floatcos(moveangle, degrees)));
  279. new_y = (cy + (eProjectile[i][e_PROJECTILE_RADIUS] * floatsin(moveangle, degrees)));
  280. collision = true;
  281. #if defined OnProjectileCollide
  282. OnProjectileCollide(i, 3, cx, cy, cz, extraid); // COLLIDE_SA_WORLD
  283. #else
  284. #pragma unused extraid
  285. #endif
  286. }
  287. // collision check with players
  288. if (eProjectile[i][e_PROJECTILE_PLAYERCOL_RADIUS] != 0.0)
  289. {
  290. for (new a, b = GetPlayerPoolSize(); a <= b; a++)
  291. {
  292. if (IsPlayerConnected(a))
  293. {
  294. GetPlayerPos(a, cx, cy, cz);
  295. if ((cz - (min_height - eProjectile[i][e_PROJECTILE_RADIUS])) < new_z < (cz + (max_height + eProjectile[i][e_PROJECTILE_RADIUS])))
  296. {
  297. dx = new_x - cx;
  298. dy = new_y - cy;
  299. if (((dx * dx) + (dy * dy)) < ((eProjectile[i][e_PROJECTILE_RADIUS] + eProjectile[i][e_PROJECTILE_PLAYERCOL_RADIUS]) * (eProjectile[i][e_PROJECTILE_RADIUS] + eProjectile[i][e_PROJECTILE_PLAYERCOL_RADIUS])))
  300. {
  301. if (((eProjectile[i][e_PROJECTILE_VX] * dx) + (eProjectile[i][e_PROJECTILE_VY] * dy)) < 0.0)
  302. {
  303. moveangle = -atan2(dy, dx);
  304. new_vx = ((eProjectile[i][e_PROJECTILE_VX] * floatcos(moveangle, degrees)) - (eProjectile[i][e_PROJECTILE_VY] * floatsin(moveangle, degrees)));
  305. new_vy = ((eProjectile[i][e_PROJECTILE_VX] * floatsin(moveangle, degrees)) + (eProjectile[i][e_PROJECTILE_VY] * floatcos(moveangle, degrees)));
  306. moveangle *= -1;
  307. eProjectile[i][e_PROJECTILE_VX] = ((new_vx * floatcos(moveangle, degrees)) - (new_vy * floatsin(moveangle, degrees)));
  308. eProjectile[i][e_PROJECTILE_VY] = ((new_vx * floatsin(moveangle, degrees)) + (new_vy * floatcos(moveangle, degrees)));
  309. collision = true;
  310. #if defined OnProjectileCollide
  311. OnProjectileCollide(i, 4, cx, cy, cz, a); // COLLIDE_PLAYER
  312. #endif
  313. }
  314. }
  315. }
  316. }
  317. }
  318. }
  319. // apply collision friction
  320. moveangle = (atan2(eProjectile[i][e_PROJECTILE_VY], eProjectile[i][e_PROJECTILE_VX]) - 90.0);
  321. speed = floatsqroot((eProjectile[i][e_PROJECTILE_VX] * eProjectile[i][e_PROJECTILE_VX]) + (eProjectile[i][e_PROJECTILE_VY] * eProjectile[i][e_PROJECTILE_VY]));
  322. if (eProjectile[i][e_PROJECTILE_COLLISION_FRICTION] != 0.0 && speed > 0.0 && collision)
  323. {
  324. speed -= eProjectile[i][e_PROJECTILE_COLLISION_FRICTION];
  325. if (speed < 0.001)
  326. speed = 0;
  327. eProjectile[i][e_PROJECTILE_VX] = speed * floatsin(-moveangle, degrees);
  328. eProjectile[i][e_PROJECTILE_VY] = speed * floatcos(-moveangle, degrees);
  329. }
  330. // apply ground friction
  331. if (eProjectile[i][e_PROJECTILE_GROUND_FRICTION] != 0.0 && speed > 0.0 && new_z == min_height)
  332. {
  333. speed -= eProjectile[i][e_PROJECTILE_GROUND_FRICTION] * (PROJECTILE_TIMER_INTERVAL / 1000.0);
  334. if (speed < 0.001)
  335. speed = 0;
  336. eProjectile[i][e_PROJECTILE_VX] = speed * floatsin(-moveangle, degrees);
  337. eProjectile[i][e_PROJECTILE_VY] = speed * floatcos(-moveangle, degrees);
  338. }
  339. // apply air resistance
  340. if (eProjectile[i][e_PROJECTILE_AIR_RESISTANCE] != 0.0)
  341. {
  342. if ((new_z == min_height && floatabs(eProjectile[i][e_PROJECTILE_AIR_RESISTANCE]) > eProjectile[i][e_PROJECTILE_GROUND_FRICTION]) || (collision && floatabs(eProjectile[i][e_PROJECTILE_AIR_RESISTANCE]) > eProjectile[i][e_PROJECTILE_COLLISION_FRICTION]) || new_z > min_height)
  343. {
  344. eProjectile[i][e_PROJECTILE_VX] -= eProjectile[i][e_PROJECTILE_VX] * eProjectile[i][e_PROJECTILE_AIR_RESISTANCE] * (PROJECTILE_TIMER_INTERVAL / 1000.0);
  345. eProjectile[i][e_PROJECTILE_VY] -= eProjectile[i][e_PROJECTILE_VY] * eProjectile[i][e_PROJECTILE_AIR_RESISTANCE] * (PROJECTILE_TIMER_INTERVAL / 1000.0);
  346. }
  347. }
  348. // update rotation
  349. speed = floatsqroot((eProjectile[i][e_PROJECTILE_VX] * eProjectile[i][e_PROJECTILE_VX]) + (eProjectile[i][e_PROJECTILE_VY] * eProjectile[i][e_PROJECTILE_VY]));
  350. if (speed > 0.0)
  351. {
  352. eProjectile[i][e_PROJECTILE_RX] -= ((speed * (PROJECTILE_TIMER_INTERVAL / 1000.0)) * ((180.0 / 3.14159) / eProjectile[i][e_PROJECTILE_RADIUS]));
  353. if (eProjectile[i][e_PROJECTILE_RX] < 0.0)
  354. {
  355. eProjectile[i][e_PROJECTILE_RX] += 360.0;
  356. }
  357. eProjectile[i][e_PROJECTILE_RZ] = moveangle;
  358. }
  359. // update position
  360. eProjectile[i][e_PROJECTILE_X] = new_x;
  361. eProjectile[i][e_PROJECTILE_Y] = new_y;
  362. eProjectile[i][e_PROJECTILE_Z] = new_z;
  363. #if defined OnProjectileUpdate
  364. OnProjectileUpdate(i);
  365. #endif
  366. // if velocity is 0, stop simulation (KillTimer)
  367. if (eProjectile[i][e_PROJECTILE_VX] == 0.0 && eProjectile[i][e_PROJECTILE_VY] == 0.0 && ((new_z == min_height && eProjectile[i][e_PROJECTILE_VZ] <= 0.0) || (new_z == max_height && eProjectile[i][e_PROJECTILE_VZ] >= 0.0)))
  368. {
  369. #if defined OnProjectileStop
  370. OnProjectileStop(i);
  371. #endif
  372. StopProjectile(i);
  373. return;
  374. }
  375. }
  376. }