impl.inc 42 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248
  1. /*
  2. Legal:
  3. Version: MPL 1.1
  4. The contents of this file are subject to the Mozilla Public License Version
  5. 1.1 the "License"; you may not use this file except in compliance with
  6. the License. You may obtain a copy of the License at
  7. http://www.mozilla.org/MPL/
  8. Software distributed under the License is distributed on an "AS IS" basis,
  9. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  10. for the specific language governing rights and limitations under the
  11. License.
  12. The Original Code is the YSI framework.
  13. The Initial Developer of the Original Code is Alex "Y_Less" Cole.
  14. Portions created by the Initial Developer are Copyright C 2011
  15. the Initial Developer. All Rights Reserved.
  16. Contributors:
  17. Y_Less
  18. koolk
  19. JoeBullet/Google63
  20. g_aSlice/Slice
  21. Misiur
  22. samphunter
  23. tianmeta
  24. maddinat0r
  25. spacemud
  26. Crayder
  27. Dayvison
  28. Ahmad45123
  29. Zeex
  30. irinel1996
  31. Yiin-
  32. Chaprnks
  33. Konstantinos
  34. Masterchen09
  35. Southclaws
  36. PatchwerkQWER
  37. m0k1
  38. paulommu
  39. udan111
  40. Thanks:
  41. JoeBullet/Google63 - Handy arbitrary ASM jump code using SCTRL.
  42. ZeeX - Very productive conversations.
  43. koolk - IsPlayerinAreaEx code.
  44. TheAlpha - Danish translation.
  45. breadfish - German translation.
  46. Fireburn - Dutch translation.
  47. yom - French translation.
  48. 50p - Polish translation.
  49. Zamaroht - Spanish translation.
  50. Los - Portuguese translation.
  51. Dracoblue, sintax, mabako, Xtreme, other coders - Producing other modes for
  52. me to strive to better.
  53. Pixels^ - Running XScripters where the idea was born.
  54. Matite - Pestering me to release it and using it.
  55. Very special thanks to:
  56. Thiadmer - PAWN, whose limits continue to amaze me!
  57. Kye/Kalcor - SA:MP.
  58. SA:MP Team past, present and future - SA:MP.
  59. Optional plugins:
  60. Gamer_Z - GPS.
  61. Incognito - Streamer.
  62. Me - sscanf2, fixes2, Whirlpool.
  63. */
  64. #define MAX_RACE_WINNERS 3
  65. #if !defined MAX_RACE_CHECKPOINTS
  66. #define MAX_RACE_CHECKPOINTS 1024
  67. #endif
  68. #if !defined MAX_RACE_STARTS
  69. #define MAX_RACE_STARTS 32
  70. #endif
  71. #define NO_RACE -1
  72. #define RACE_NO_CHECKPOINT -1
  73. #define RACE_LOOP_GRANULARITY 5
  74. #define RACE_PLAYER_OUT 0x80000000
  75. enum e_RACE_FLAGS (<<= 1)
  76. {
  77. e_RACE_FLAGS_NONE = 0,
  78. e_RACE_FLAGS_EXIT_TIME = 0xFFFF,
  79. e_RACE_FLAGS_ACTIVE = 0x10000,
  80. e_RACE_FLAGS_ARIAL,
  81. e_RACE_FLAGS_REL_WIN,
  82. e_RACE_FLAGS_STARTED,
  83. e_RACE_FLAGS_RACING,
  84. e_RACE_FLAGS_CD_JOIN,
  85. e_RACE_FLAGS_RESTART
  86. }
  87. enum E_RACE
  88. {
  89. e_RACE_FLAGS:E_RACE_FLAGS,
  90. E_RACE_LAPS,
  91. E_RACE_CHECKPOINTS,
  92. E_RACE_CP_START,
  93. E_RACE_RACER_COUNT,
  94. E_RACE_RACER_MAX,
  95. E_RACE_COUNTDOWN,
  96. E_RACE_FINISHED,
  97. E_RACE_ENTRY,
  98. E_RACE_VW,
  99. E_RACE_INT,
  100. E_RACE_PRIZES[MAX_RACE_WINNERS]
  101. }
  102. enum E_RACE_PLAYER
  103. {
  104. E_RACE_PLAYER_RACE,
  105. E_RACE_PLAYER_LAP,
  106. E_RACE_PLAYER_CP,
  107. E_RACE_PLAYER_TIME,
  108. #if defined RACE_POSITION
  109. E_RACE_PLAYER_POSITION,
  110. #endif
  111. E_RACE_PLAYER_TOUT,
  112. Float:E_RACE_PLAYER_X,
  113. Float:E_RACE_PLAYER_Y,
  114. Float:E_RACE_PLAYER_Z,
  115. Float:E_RACE_PLAYER_A,
  116. E_RACE_PLAYER_INT,
  117. E_RACE_PLAYER_WORLD
  118. }
  119. #define RACE_POS_DONE 0x80000000
  120. enum E_RACE_POS
  121. {
  122. E_RACE_POS_CP,
  123. Float:E_RACE_POS_TOGO
  124. }
  125. _Y_RACES_STATIC stock
  126. PlayerArray:YSI_g_sRacers[MAX_RACES]<MAX_PLAYERS>,
  127. YSI_g_sRaceData[MAX_RACES][E_RACE],
  128. Float:YSI_g_sRaceCheckpoints[MAX_RACE_CHECKPOINTS][3],
  129. YSI_g_sPlayerRace[MAX_PLAYERS][E_RACE_PLAYER],
  130. Float:YSI_g_sRaceStarts[MAX_RACES][MAX_RACE_STARTS][4],
  131. YSI_g_sCPIndex,
  132. Iterator:YSI_g_sRacePeople[MAX_RACES]<MAX_PLAYERS>;
  133. forward Race_Countdown(race, time);
  134. forward Race_Timeout(playerid);
  135. loadtext core[ysi_race];
  136. /*-------------------------------------------------------------------------*//**
  137. * <param name="from">Which slot to start shifting up.</param>
  138. * <param name="add">How to adjust the slots.</param>
  139. * Notes:
  140. * Find all races whose checkpoint blocks start above the current index and
  141. * increment them.
  142. *//*------------------------------------------------------------------------**/
  143. _Y_RACES_STATIC stock RaceArray_IndexShift(from, add)
  144. {
  145. for (new i = 0; i != MAX_RACES; ++i)
  146. {
  147. if (YSI_g_sRaceData[i][E_RACE_CP_START] >= from) YSI_g_sRaceData[i][E_RACE_CP_START] += add;
  148. }
  149. }
  150. /*-------------------------------------------------------------------------*//**
  151. * <param name="from">Which slot to start shifting up.</param>
  152. * <param name="end">Slot to end the shifting on.</param>
  153. * <param name="to">Slot to shift to.</param>
  154. * Notes:
  155. * Shifts checkpoints up in the main array to make room for new ones.
  156. *//*------------------------------------------------------------------------**/
  157. _Y_RACES_STATIC stock RaceArray_Shift(from, &end, to)
  158. {
  159. // Fast version.
  160. end -= from;
  161. if (end + to >= MAX_RACE_CHECKPOINTS)
  162. {
  163. return false;
  164. }
  165. memcpy(_:YSI_g_sRaceCheckpoints[to], _:YSI_g_sRaceCheckpoints[from], 0, end * 4 * 3, end * 3);
  166. //++to;
  167. end += to;
  168. return true;
  169. }
  170. /*-------------------------------------------------------------------------*//**
  171. * <param name="slot">Slot to add to.</param>
  172. * <param name="x">Position data.</param>
  173. * <param name="y">Position data.</param>
  174. * <param name="z">Position data.</param>
  175. * Notes:
  176. * Adds a new checkpoint to the checkpoints array.
  177. *//*------------------------------------------------------------------------**/
  178. _Y_RACES_STATIC stock RaceArray_Add(slot, Float:x, Float:y, Float:z)
  179. {
  180. if (slot < YSI_g_sCPIndex)
  181. {
  182. if (!RaceArray_Shift(slot, YSI_g_sCPIndex, slot + 1)) return false;
  183. RaceArray_IndexShift(slot, 1);
  184. }
  185. YSI_g_sRaceCheckpoints[slot][0] = x;
  186. YSI_g_sRaceCheckpoints[slot][1] = y;
  187. YSI_g_sRaceCheckpoints[slot][2] = z;
  188. return true;
  189. }
  190. /*-------------------------------------------------------------------------*//**
  191. * <param name="raceid">Race to check.</param>
  192. * Notes:
  193. * Checks a race is active.
  194. *//*------------------------------------------------------------------------**/
  195. P:D(bool:Race__IsActive(raceid));
  196. #define Race__IsActive(%1) \
  197. (YSI_g_sRaceData[(%1)][E_RACE_FLAGS] & e_RACE_FLAGS_ACTIVE)
  198. /*-------------------------------------------------------------------------*//**
  199. * <param name="raceid">Race to check.</param>
  200. * Notes:
  201. * Checks an id is valid and active.
  202. *//*------------------------------------------------------------------------**/
  203. P:D(bool:Race_IsValid(raceid));
  204. #define Race_IsValid(%1) \
  205. (0 <= (%1) < MAX_RACES && Race__IsActive((%1)))
  206. /*-------------------------------------------------------------------------*//**
  207. * <param name="laps">Number of laps to race for.</param>
  208. * <param name="entry">Cost of entry.</param>
  209. * <param name="countdown">Time to count down from for start.</param>
  210. * <param name="arial">Use arial checkpoints instead.</param>
  211. * <param name="fixedPrize">Set prize amounts manually.</param>
  212. * <param name="exitTime">Time allowed out a vehicle before fail.</param>
  213. * <param name="interior">The interior of the race.</param>
  214. * <param name="world">The world of the race.</param>
  215. * <param name="restart">Don't destroy the race on completion.</param>
  216. * Notes:
  217. * Disables default group settings when groups are used.
  218. *//*------------------------------------------------------------------------**/
  219. stock Races_SetupGroups() <YSI_has_groups:n>
  220. {
  221. }
  222. stock Races_SetupGroups() <>
  223. {
  224. }
  225. /*-------------------------------------------------------------------------*//**
  226. * <param name="laps">Number of laps to race for.</param>
  227. * <param name="entry">Cost of entry.</param>
  228. * <param name="countdown">Time to count down from for start.</param>
  229. * <param name="arial">Use arial checkpoints instead.</param>
  230. * <param name="fixedPrize">Set prize amounts manually.</param>
  231. * <param name="exitTime">Time allowed out a vehicle before fail.</param>
  232. * <param name="interior">The interior of the race.</param>
  233. * <param name="world">The world of the race.</param>
  234. * <param name="restart">Don't destroy the race on completion.</param>
  235. * <returns>raceid - ID of the race for reference or -1.</returns>
  236. * Notes:
  237. * Finds an empty slot and sets the race up for use. All
  238. * parameters are optional and can be set separately aswell.
  239. *//*------------------------------------------------------------------------**/
  240. foreign _Race_Create(l,e,c,a,f,x,i,w,r);
  241. stock Race_Create(laps = 0, entry = 0, countdown = 3, bool:arial = false, bool:fixedPrize = true, exitTime = 0, interior = 0, world = 0, bool:restart = false)
  242. {
  243. return _Race_Create(laps, entry, countdown, _:arial, _:fixedPrize, exitTime, interior, world, _:restart);
  244. }
  245. global _Race_Create(l,e,c,a,f,x,i,w,r)
  246. {
  247. new
  248. raceid;
  249. for (raceid = 0; raceid < MAX_RACES; raceid++) if (!Race__IsActive(raceid)) break;
  250. if (raceid == MAX_RACES) return NO_RACE;
  251. YSI_g_sRaceData[raceid][E_RACE_FLAGS] = e_RACE_FLAGS_ACTIVE |
  252. (f ? e_RACE_FLAGS_NONE : e_RACE_FLAGS_REL_WIN) |
  253. (a ? e_RACE_FLAGS_ARIAL : e_RACE_FLAGS_NONE ) |
  254. (r ? e_RACE_FLAGS_RESTART : e_RACE_FLAGS_NONE ) |
  255. (e_RACE_FLAGS:x & e_RACE_FLAGS_EXIT_TIME);
  256. //
  257. // Old functions.
  258. //
  259. //Race_SetLaps(raceid, l);
  260. YSI_g_sRaceData[raceid][E_RACE_LAPS] = l;
  261. //Race_SetEntry(raceid, e);
  262. YSI_g_sRaceData[raceid][E_RACE_ENTRY] = e;
  263. if (f)
  264. {
  265. //for (new p = 1; p <= MAX_RACE_WINNERS; p++) Race_SetPrize(raceid, (MAX_RACE_WINNERS + 1) - p, e * p);
  266. for (new p = 1; p <= MAX_RACE_WINNERS; p++) YSI_g_sRaceData[raceid][E_RACE_PRIZES][MAX_RACE_WINNERS - p] = e * p;
  267. //Race_SetFixedWin(raceid, _:f);
  268. }
  269. //Race_SetArial(raceid, _:a);
  270. //Race_SetActive(raceid, 1);
  271. //Race_SetCountdown(raceid, c);
  272. YSI_g_sRaceData[raceid][E_RACE_COUNTDOWN] = c;
  273. //Race_SetExitTime(raceid, x);
  274. //Race_SetInterior(raceid, i);
  275. YSI_g_sRaceData[raceid][E_RACE_INT] = i;
  276. //Race_SetWorld(raceid, w);
  277. YSI_g_sRaceData[raceid][E_RACE_VW] = w;
  278. //Race_SetRestart(raceid, _:r);
  279. //
  280. // Remainder.
  281. //
  282. YSI_g_sRaceData[raceid][E_RACE_CHECKPOINTS] = 0;
  283. YSI_g_sRaceData[raceid][E_RACE_RACER_COUNT] = 0;
  284. YSI_g_sRaceData[raceid][E_RACE_FINISHED] = 0;
  285. YSI_g_sRaceData[raceid][E_RACE_RACER_MAX] = 0;
  286. PA_Init(YSI_g_sRacers[raceid]);
  287. Iter_Clear(YSI_g_sRacePeople[raceid]);
  288. YSI_g_sRaceData[raceid][E_RACE_CP_START] = YSI_g_sCPIndex;
  289. NO_GROUPS(raceid)
  290. {
  291. }
  292. return raceid;
  293. }
  294. /*-------------------------------------------------------------------------*//**
  295. * <param name="slot">Race to destroy.</param>
  296. * <param name="refund">Should entrants get their money back?</param>
  297. * Notes:
  298. * Called whenever a player leaves the race, checks the
  299. * number of remaining racers and if none ends the race.
  300. *//*------------------------------------------------------------------------**/
  301. foreign void:_Race_Destroy(slot, refund);
  302. stock Race_Destroy(slot, bool:refund = false)
  303. {
  304. _Race_Destroy(slot, _:refund);
  305. }
  306. global void:_Race_Destroy(slot, refund)
  307. {
  308. if (Race_IsValid(slot))
  309. {
  310. if (YSI_g_sRaceData[slot][E_RACE_FLAGS] & e_RACE_FLAGS_STARTED)
  311. {
  312. foreach (new i : Player)
  313. {
  314. if (PA_Get(YSI_g_sRacers[slot], i))
  315. {
  316. DisablePlayerRaceCheckpoint(i);
  317. Race_PlayerDone(i);
  318. }
  319. }
  320. }
  321. else
  322. {
  323. foreach (new i : Player)
  324. {
  325. if (PA_Get(YSI_g_sRacers[slot], i))
  326. {
  327. _Race_PlayerLeave(i, refund);
  328. }
  329. }
  330. }
  331. YSI_g_sRaceData[slot][E_RACE_RACER_COUNT] = 0;
  332. YSI_g_sRaceData[slot][E_RACE_FLAGS] = e_RACE_FLAGS:0;
  333. new
  334. count = YSI_g_sRaceData[slot][E_RACE_CHECKPOINTS],
  335. start = YSI_g_sRaceData[slot][E_RACE_CP_START];
  336. RaceArray_Shift(start + count, YSI_g_sCPIndex, start);
  337. RaceArray_IndexShift(start, -count);
  338. }
  339. }
  340. /*-------------------------------------------------------------------------*//**
  341. * <param name="raceid">Race to add to.</param>
  342. * <param name="x">X position.</param>
  343. * <param name="y">Y position.</param>
  344. * <param name="z">Z position.</param>
  345. * <returns>position or -1.</returns>
  346. *//*------------------------------------------------------------------------**/
  347. global Race_AddCheckpoint(raceid, Float:x, Float:y, Float:z)
  348. {
  349. if (!Race_IsValid(raceid)) return RACE_NO_CHECKPOINT;
  350. new
  351. count = YSI_g_sRaceData[raceid][E_RACE_CHECKPOINTS];
  352. RaceArray_Add(YSI_g_sRaceData[raceid][E_RACE_CP_START] + count, x, y, z);
  353. YSI_g_sRaceData[raceid][E_RACE_CHECKPOINTS]++;
  354. return count;
  355. }
  356. /*-------------------------------------------------------------------------*//**
  357. * <param name="raceid">Race to add to.</param>
  358. * <param name="x">X position.</param>
  359. * <param name="y">Y position.</param>
  360. * <param name="z">Z position.</param>
  361. * <param name="a">Angle.</param>
  362. * <returns>position or -1.</returns>
  363. * Notes:
  364. * Adds a starting point to a race and increases the max
  365. * number of racers for the race.
  366. *//*------------------------------------------------------------------------**/
  367. global Race_AddStart(raceid, Float:x, Float:y, Float:z, Float:a)
  368. {
  369. if (!Race_IsValid(raceid)) return RACE_NO_CHECKPOINT;
  370. new
  371. count = YSI_g_sRaceData[raceid][E_RACE_RACER_MAX];
  372. if (count >= MAX_RACE_STARTS) return RACE_NO_CHECKPOINT;
  373. YSI_g_sRaceStarts[raceid][count][0] = x;
  374. YSI_g_sRaceStarts[raceid][count][1] = y;
  375. YSI_g_sRaceStarts[raceid][count][2] = z;
  376. YSI_g_sRaceStarts[raceid][count][3] = a;
  377. YSI_g_sRaceData[raceid][E_RACE_RACER_MAX]++;
  378. return count;
  379. }
  380. /*-------------------------------------------------------------------------*//**
  381. * <param name="raceid">Race to set for.</param>
  382. * <param name="set">1/0.</param>
  383. * Notes:
  384. * Sets wether or not a race has fixed prizes for the
  385. * winners. If not the prizes are calculated at race start
  386. * based on the number of entrants and the entry fee.
  387. *//*------------------------------------------------------------------------**/
  388. global void:Race_SetFixedWin(raceid, set)
  389. {
  390. if (!Race_IsValid(raceid)) return;
  391. if (set) YSI_g_sRaceData[raceid][E_RACE_FLAGS] &= ~e_RACE_FLAGS_REL_WIN;
  392. else YSI_g_sRaceData[raceid][E_RACE_FLAGS] |= e_RACE_FLAGS_REL_WIN;
  393. }
  394. /*-------------------------------------------------------------------------*//**
  395. * <param name="raceid">Race to set for.</param>
  396. * <param name="set">1/0.</param>
  397. * Notes:
  398. * Sets wether or not a race is destroyed after completion.
  399. *//*------------------------------------------------------------------------**/
  400. global void:Race_SetRestart(raceid, set)
  401. {
  402. if (!Race_IsValid(raceid)) return;
  403. if (set) YSI_g_sRaceData[raceid][E_RACE_FLAGS] |= e_RACE_FLAGS_RESTART;
  404. else YSI_g_sRaceData[raceid][E_RACE_FLAGS] &= ~e_RACE_FLAGS_RESTART;
  405. }
  406. /*-------------------------------------------------------------------------*//**
  407. * <param name="raceid">Race to set for.</param>
  408. * <param name="set">1/0.</param>
  409. * Notes:
  410. * Toggles the use of arial checkpoints.
  411. *//*------------------------------------------------------------------------**/
  412. global void:Race_SetArial(raceid, set)
  413. {
  414. if (!Race_IsValid(raceid)) return;
  415. if (set) YSI_g_sRaceData[raceid][E_RACE_FLAGS] |= e_RACE_FLAGS_ARIAL;
  416. else YSI_g_sRaceData[raceid][E_RACE_FLAGS] &= ~e_RACE_FLAGS_ARIAL;
  417. }
  418. /*-------------------------------------------------------------------------*//**
  419. * <param name="raceid">Race to set for.</param>
  420. * <param name="set">1/0.</param>
  421. * Notes:
  422. * Activates the race for entry and use.
  423. *//*------------------------------------------------------------------------**/
  424. _Y_RACES_STATIC stock Race_SetActive(raceid, set)
  425. {
  426. if (!Race_IsValid(raceid)) return;
  427. if (set) YSI_g_sRaceData[raceid][E_RACE_FLAGS] |= e_RACE_FLAGS_ACTIVE;
  428. else YSI_g_sRaceData[raceid][E_RACE_FLAGS] &= ~e_RACE_FLAGS_ACTIVE;
  429. }
  430. /*-------------------------------------------------------------------------*//**
  431. * <param name="raceid">Race to set for.</param>
  432. * <param name="countdown">Number to count down from.</param>
  433. *//*------------------------------------------------------------------------**/
  434. global void:Race_SetCountdown(raceid, countdown)
  435. {
  436. if (!Race_IsValid(raceid)) return;
  437. YSI_g_sRaceData[raceid][E_RACE_COUNTDOWN] = countdown;
  438. }
  439. /*-------------------------------------------------------------------------*//**
  440. * <param name="raceid">Race to set for.</param>
  441. * <param name="interior">Interior where race is located.</param>
  442. * Notes:
  443. * AFAIK you can't drive between interiors so all the
  444. * checkpoints must be located in the same interior.
  445. *//*------------------------------------------------------------------------**/
  446. global void:Race_SetInterior(raceid, interior)
  447. {
  448. if (!Race_IsValid(raceid)) return;
  449. YSI_g_sRaceData[raceid][E_RACE_INT] = interior;
  450. }
  451. /*-------------------------------------------------------------------------*//**
  452. * <param name="raceid">Race to set for.</param>
  453. * <param name="world">World to run race in.</param>
  454. *//*------------------------------------------------------------------------**/
  455. global void:Race_SetWorld(raceid, world)
  456. {
  457. if (!Race_IsValid(raceid)) return;
  458. YSI_g_sRaceData[raceid][E_RACE_VW] = world;
  459. }
  460. /*-------------------------------------------------------------------------*//**
  461. * <param name="raceid">Race to set for.</param>
  462. * <param name="position">Winning position to set for/.</param>
  463. * <param name="amount">Amount for that position to win.</param>
  464. * Notes:
  465. * If this is used after the race has started it will over-
  466. * write prizes set by relative winnings.
  467. *//*------------------------------------------------------------------------**/
  468. global void:Race_SetPrize(raceid, position, amount)
  469. {
  470. if (!Race_IsValid(raceid) || position >= MAX_RACE_WINNERS) return;
  471. YSI_g_sRaceData[raceid][E_RACE_PRIZES][position - 1] = amount;
  472. }
  473. /*-------------------------------------------------------------------------*//**
  474. * <param name="raceid">Race to set for.</param>
  475. * <param name="time">Time to set.</param>
  476. * Notes:
  477. * Sets the time you are allowed out a vehicle before you
  478. * fail the race. 0 means a vehicle exit is an instant fail.
  479. *
  480. * Useful for long races where cars may well be destroyed.
  481. *//*------------------------------------------------------------------------**/
  482. global void:Race_SetExitTime(raceid, time)
  483. {
  484. if (!Race_IsValid(raceid)) return;
  485. YSI_g_sRaceData[raceid][E_RACE_FLAGS] = (YSI_g_sRaceData[raceid][E_RACE_FLAGS] & (~e_RACE_FLAGS_EXIT_TIME)) | (e_RACE_FLAGS:time & e_RACE_FLAGS_EXIT_TIME);
  486. }
  487. /*-------------------------------------------------------------------------*//**
  488. * <param name="raceid">Race to check.</param>
  489. * Notes:
  490. * Wrapper for Race_IsValid.
  491. *//*------------------------------------------------------------------------**/
  492. global Race_IsActive(raceid)
  493. {
  494. return Race_IsValid(raceid);
  495. }
  496. /*-------------------------------------------------------------------------*//**
  497. * <param name="raceid">Race to set for.</param>
  498. * <param name="laps">Number of laps to set.</param>
  499. *//*------------------------------------------------------------------------**/
  500. global void:Race_SetLaps(raceid, laps)
  501. {
  502. if (!Race_IsValid(raceid)) return;
  503. YSI_g_sRaceData[raceid][E_RACE_LAPS] = laps;
  504. }
  505. /*-------------------------------------------------------------------------*//**
  506. * <param name="raceid">Race to set for.</param>
  507. * <param name="cost">Cost of entry to the race.</param>
  508. *//*------------------------------------------------------------------------**/
  509. global void:Race_SetEntry(raceid, cost)
  510. {
  511. if (!Race_IsValid(raceid)) return;
  512. YSI_g_sRaceData[raceid][E_RACE_ENTRY] = cost;
  513. }
  514. /*-------------------------------------------------------------------------*//**
  515. * <param name="playerid">Player dropping out.</param>
  516. * Notes:
  517. * Called when a player exits their vehicle.
  518. *//*------------------------------------------------------------------------**/
  519. _Y_RACES_STATIC Race_Dropout(playerid)
  520. {
  521. SetTimerEx("Race_Timeout", (YSI_g_sRaceData[(YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_RACE])][E_RACE_FLAGS] & e_RACE_FLAGS_EXIT_TIME), 0, "i", playerid);
  522. YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_RACE] |= RACE_PLAYER_OUT;
  523. YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_TOUT] = GetTickCount();
  524. }
  525. /*-------------------------------------------------------------------------*//**
  526. * <param name="playerid">Player rejoining.</param>
  527. * Notes:
  528. * Called when a player gets in a new vehicle if not timmed
  529. * out..
  530. *//*------------------------------------------------------------------------**/
  531. _Y_RACES_STATIC Race_Rejoin(playerid)
  532. {
  533. YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_RACE] &= ~RACE_PLAYER_OUT;
  534. }
  535. /*-------------------------------------------------------------------------*//**
  536. * <param name="playerid">Player to check.</param>
  537. * Notes:
  538. * Called from Race_Dropout after the race's exit time. If
  539. * the player still isn't in a vehicle (set by Race_Rejoin) the
  540. * player fails the race.
  541. *//*------------------------------------------------------------------------**/
  542. public Race_Timeout(playerid)
  543. {
  544. if (YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_RACE] & RACE_PLAYER_OUT) Race_Exit(playerid);
  545. }
  546. /*-------------------------------------------------------------------------*//**
  547. * <param name="playerid">Player who left.</param>
  548. * Notes:
  549. * Called when a player leaves a race prematurly.
  550. *//*------------------------------------------------------------------------**/
  551. _Y_RACES_STATIC Race_Exit(playerid)
  552. {
  553. DisablePlayerRaceCheckpoint(playerid);
  554. new
  555. slot = YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_RACE];
  556. Race_PlayerDone(playerid);
  557. CallRemoteFunction("OnPlayerExitRace", "ii", playerid, slot);
  558. Race_CheckEnd(slot);
  559. }
  560. /*-------------------------------------------------------------------------*//**
  561. * <param name="slot">Race to check.</param>
  562. * Notes:
  563. * Called whenever a player leaves the race, checks the
  564. * number of remaining racers and if none ends the race.
  565. *//*------------------------------------------------------------------------**/
  566. _Y_RACES_STATIC Race_CheckEnd(slot)
  567. {
  568. // This shouldn't be here...
  569. //YSI_g_sRaceData[slot][E_RACE_RACER_COUNT]--;
  570. if (!(--YSI_g_sRaceData[slot][E_RACE_RACER_COUNT]))
  571. {
  572. CallRemoteFunction("OnRaceEnd", "i", slot);
  573. if (YSI_g_sRaceData[slot][E_RACE_FLAGS] & e_RACE_FLAGS_RESTART)
  574. {
  575. YSI_g_sRaceData[slot][E_RACE_FLAGS] &= ~e_RACE_FLAGS_STARTED;
  576. YSI_g_sRaceData[slot][E_RACE_FINISHED] = 0;
  577. new
  578. count = YSI_g_sRaceData[slot][E_RACE_CHECKPOINTS],
  579. start = YSI_g_sRaceData[slot][E_RACE_CP_START];
  580. RaceArray_Shift(start + count, YSI_g_sCPIndex, start);
  581. RaceArray_IndexShift(start, -count);
  582. // Reset groups.
  583. NO_GROUPS(slot)
  584. {
  585. }
  586. }
  587. else
  588. {
  589. YSI_g_sRaceData[slot][E_RACE_FLAGS] = e_RACE_FLAGS:0;
  590. }
  591. }
  592. }
  593. /*-------------------------------------------------------------------------*//**
  594. * <param name="playerid">Player done.</param>
  595. * Notes:
  596. * Generic cleanup for anyone who has left a race. Sets
  597. * the player back to their old position and sets a few other
  598. * variables.
  599. *//*------------------------------------------------------------------------**/
  600. _Y_RACES_STATIC Race_PlayerDone(playerid)
  601. {
  602. new
  603. race = YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_RACE];
  604. YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_RACE] = NO_RACE;
  605. YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_TIME] = GetTickCount() - YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_TIME];
  606. #if defined RACE_POSITION
  607. YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_POSITION] = 0;
  608. #endif
  609. if (Race_IsValid(race))
  610. {
  611. PA_Set(YSI_g_sRacers[race], playerid, false);
  612. Iter_Remove(YSI_g_sRacePeople[race], playerid);
  613. if (IsPlayerConnected(playerid))
  614. {
  615. //SetPlayerPos(playerid, YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_X], YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_Y], YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_Z]);
  616. //SetPlayerFacingAngle(playerid, YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_A]);
  617. new
  618. vehicleid;
  619. if ((vehicleid = GetPlayerVehicleID(playerid)))
  620. {
  621. SetVehiclePos(vehicleid, YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_X], YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_Y], YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_Z]);
  622. SetVehicleZAngle(vehicleid, YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_A]);
  623. LinkVehicleToInterior(vehicleid, YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_INT]);
  624. SetVehicleVirtualWorld(vehicleid, YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_WORLD]);
  625. }
  626. else
  627. {
  628. SetPlayerPos(playerid, YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_X], YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_Y], YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_Z]);
  629. SetPlayerFacingAngle(playerid, YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_A]);
  630. }
  631. SetPlayerInterior(playerid, YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_INT]);
  632. SetPlayerVirtualWorld(playerid, YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_WORLD]);
  633. }
  634. }
  635. }
  636. /*-------------------------------------------------------------------------*//**
  637. * <param name="playerid">Player to get time of.</param>
  638. * <returns>The time a player has been out a vehicle.</returns>
  639. *//*------------------------------------------------------------------------**/
  640. global Race_GetPlayerExitedTime(playerid)
  641. {
  642. return GetTickCount() - YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_TOUT];
  643. }
  644. /*-------------------------------------------------------------------------*//**
  645. * <param name="playerid">Player to add.</param>
  646. * <param name="race">Race to add to.</param>
  647. * Notes:
  648. * Checks if a player is valid to join a race and if the race
  649. * is valid to be joined to and if so adds them to it.
  650. *//*------------------------------------------------------------------------**/
  651. global Race_PlayerJoin(playerid, race)
  652. {
  653. if (YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_RACE] != NO_RACE ||
  654. !Race_IsValid(race)) return 0;
  655. new
  656. e_RACE_FLAGS:flags = YSI_g_sRaceData[race][E_RACE_FLAGS];
  657. if ((flags & e_RACE_FLAGS_STARTED) ||
  658. YSI_g_sRaceData[race][E_RACE_RACER_COUNT] >= YSI_g_sRaceData[race][E_RACE_RACER_MAX] ||
  659. GetPlayerMoney(playerid) < YSI_g_sRaceData[race][E_RACE_ENTRY]) return 0;
  660. GivePlayerMoney(playerid, 0 - YSI_g_sRaceData[race][E_RACE_ENTRY]);
  661. YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_RACE] = race;
  662. YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_LAP] = 0;
  663. YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_CP] = 0;
  664. PA_Set(YSI_g_sRacers[race], playerid, true);
  665. ++YSI_g_sRaceData[race][E_RACE_RACER_COUNT];
  666. Iter_Add(YSI_g_sRacePeople[race], playerid);
  667. #if defined RACE_POSITION
  668. YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_POSITION] = 0;
  669. #endif
  670. return 1;
  671. }
  672. /*-------------------------------------------------------------------------*//**
  673. * <param name="playerid">Player to leave the race.</param>
  674. * <param name="refund">Wether or not to give them their entry fee back.</param>
  675. * Notes:
  676. * Called if a player leaves a race before the race has
  677. * started.
  678. *//*------------------------------------------------------------------------**/
  679. foreign _Race_PlayerLeave(playerid, refund);
  680. stock Race_PlayerLeave(playerid, bool:refund = false)
  681. {
  682. return _Race_PlayerLeave(playerid, _:refund);
  683. }
  684. global _Race_PlayerLeave(playerid, refund)
  685. {
  686. new
  687. race = YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_RACE];
  688. if (!Race_IsValid(race) || YSI_g_sRaceData[race][E_RACE_FLAGS] & e_RACE_FLAGS_STARTED) return 0;
  689. if (refund) GivePlayerMoney(playerid, YSI_g_sRaceData[race][E_RACE_ENTRY]);
  690. YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_RACE] = NO_RACE;
  691. PA_Set(YSI_g_sRacers[race], playerid, false);
  692. --YSI_g_sRaceData[race][E_RACE_RACER_COUNT];
  693. Iter_Remove(YSI_g_sRacePeople[race], playerid);
  694. return 1;
  695. }
  696. /*-------------------------------------------------------------------------*//**
  697. * <param name="playerid">Player who finished the race.</param>
  698. * Notes:
  699. * Called when a player completes a race.
  700. *//*------------------------------------------------------------------------**/
  701. _Y_RACES_STATIC Race_Finish(playerid)
  702. {
  703. new
  704. slot = YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_RACE],
  705. winners = YSI_g_sRaceData[slot][E_RACE_FINISHED];
  706. YSI_g_sRaceData[slot][E_RACE_FINISHED]++;
  707. Race_PlayerDone(playerid);
  708. new
  709. prize;
  710. if (winners < MAX_RACE_WINNERS) prize = YSI_g_sRaceData[slot][E_RACE_PRIZES][winners];
  711. GivePlayerMoney(playerid, prize);
  712. CallRemoteFunction("OnPlayerFinishRace", "iiiii", playerid, slot, winners + 1, prize, YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_TIME]);
  713. Race_CheckEnd(slot);
  714. }
  715. /*-------------------------------------------------------------------------*//**
  716. * <param name="race">Race to start.</param>
  717. * Notes:
  718. * Loops through all players who have entered the race and
  719. * moves them to their respective start points. If the prize
  720. * is set as relative the prizes are calculated here based on
  721. * number of entrants, number of possible winners and entry
  722. * fee.
  723. *//*------------------------------------------------------------------------**/
  724. global Race_Start(race)
  725. {
  726. if (!Race_IsValid(race)) return 0;
  727. new
  728. bool:exited = true,
  729. j;
  730. for (new i = Iter_First(YSI_g_sRacePeople[race]), k; (k = Iter_Next(YSI_g_sRacePeople[race], i)), i != Iter_End(YSI_g_sRacePeople[race]); i = k)
  731. //foreach (new i : YSI_g_sRacePeople[race])
  732. {
  733. new
  734. vehicleid;
  735. GetPlayerPos(i, YSI_g_sPlayerRace[i][E_RACE_PLAYER_X], YSI_g_sPlayerRace[i][E_RACE_PLAYER_Y], YSI_g_sPlayerRace[i][E_RACE_PLAYER_Z]);
  736. if ((vehicleid = GetPlayerVehicleID(i)))
  737. {
  738. GetPlayerFacingAngle(i, YSI_g_sPlayerRace[i][E_RACE_PLAYER_A]);
  739. YSI_g_sPlayerRace[i][E_RACE_PLAYER_INT] = GetPlayerInterior(i);
  740. YSI_g_sPlayerRace[i][E_RACE_PLAYER_WORLD] = GetPlayerVirtualWorld(i);
  741. LinkVehicleToInterior(vehicleid, YSI_g_sRaceData[race][E_RACE_INT]);
  742. SetVehicleVirtualWorld(vehicleid, YSI_g_sRaceData[race][E_RACE_VW]);
  743. SetPlayerInterior(i, YSI_g_sRaceData[race][E_RACE_INT]);
  744. SetPlayerVirtualWorld(i, YSI_g_sRaceData[race][E_RACE_VW]);
  745. SetVehiclePos(vehicleid, YSI_g_sRaceStarts[race][j % MAX_RACE_STARTS][0], YSI_g_sRaceStarts[race][j % MAX_RACE_STARTS][1], YSI_g_sRaceStarts[race][j % MAX_RACE_STARTS][2]);
  746. SetVehicleZAngle(vehicleid, YSI_g_sRaceStarts[race][j % MAX_RACE_STARTS][3]);
  747. SetCameraBehindPlayer(i);
  748. TogglePlayerControllable(i, 0);
  749. ++j;
  750. }
  751. else
  752. {
  753. exited = false;
  754. Race_Exit(i);
  755. }
  756. }
  757. if (j)
  758. {
  759. YSI_g_sRaceData[race][E_RACE_FLAGS] |= e_RACE_FLAGS_STARTED;
  760. Race_Countdown(race, YSI_g_sRaceData[race][E_RACE_COUNTDOWN]);
  761. if (YSI_g_sRaceData[race][E_RACE_FLAGS] & e_RACE_FLAGS_REL_WIN)
  762. {
  763. new
  764. prize;
  765. if (j < MAX_RACE_WINNERS) prize = ((j * j) + j) / 2;
  766. else prize = (MAX_RACE_WINNERS * (MAX_RACE_WINNERS + 1)) / 2;
  767. new
  768. count = (YSI_g_sRaceData[race][E_RACE_ENTRY] * j) / prize;
  769. for (new i = 0; i < MAX_RACE_WINNERS; i++) YSI_g_sRaceData[race][E_RACE_PRIZES][i] = (MAX_RACE_WINNERS - i) * count;
  770. }
  771. return 1;
  772. }
  773. else if (exited)
  774. {
  775. // No players were in to begin with.
  776. YSI_g_sRaceData[race][E_RACE_RACER_COUNT] = 1;
  777. Race_CheckEnd(race);
  778. }
  779. return 0;
  780. }
  781. /*-------------------------------------------------------------------------*//**
  782. * <param name="race">Race to do countdown for.</param>
  783. * <param name="time">Seconds remaining.</param>
  784. *//*------------------------------------------------------------------------**/
  785. public Race_Countdown(race, time)
  786. {
  787. if (!(YSI_g_sRaceData[race][E_RACE_FLAGS] & e_RACE_FLAGS_ACTIVE)) return;
  788. if (time)
  789. {
  790. Text_Send(YSI_g_sRacers[race], $YSI_RACE_COUNTDOWN, time);
  791. SetTimerEx("Race_Countdown", 1000, 0, "ii", race, time - 1);
  792. }
  793. else
  794. {
  795. Text_Send(YSI_g_sRacers[race], $YSI_RACE_GO);
  796. new
  797. startTime = GetTickCount(),
  798. vehicleid,
  799. pos;
  800. //for (new i = Iter_First(YSI_g_sRacePeople[race]), j; j = Iter_Next(YSI_g_sRacePeople[race], i), i != Iter_End(YSI_g_sRacePeople[race]); i = j)
  801. foreach (new i : YSI_g_sRacePeople[race])
  802. {
  803. if ((vehicleid = GetPlayerVehicleID(i)))
  804. {
  805. TogglePlayerControllable(i, 1);
  806. Race_DoEnterRaceCP(i);
  807. SetVehiclePos(vehicleid, YSI_g_sRaceStarts[race][pos % MAX_RACE_STARTS][0], YSI_g_sRaceStarts[race][pos % MAX_RACE_STARTS][1], YSI_g_sRaceStarts[race][pos % MAX_RACE_STARTS][2] + 0.1);
  808. SetVehicleZAngle(vehicleid, YSI_g_sRaceStarts[race][pos % MAX_RACE_STARTS][3]);
  809. YSI_g_sPlayerRace[i][E_RACE_PLAYER_TIME] = startTime;
  810. pos++;
  811. }
  812. else Race_Exit(i);
  813. }
  814. }
  815. }
  816. /*-------------------------------------------------------------------------*//**
  817. * <param name="playerid">Player who entered.</param>
  818. * Sets up important variables
  819. *//*------------------------------------------------------------------------**/
  820. mhook OnScriptInit()
  821. {
  822. for (new i = 0; i < MAX_PLAYERS; i++)
  823. {
  824. YSI_g_sPlayerRace[i][E_RACE_PLAYER_RACE] = NO_RACE;
  825. }
  826. Iter_Init(YSI_g_sRacePeople);
  827. Races_SetupGroups();
  828. return 1;
  829. }
  830. /*-------------------------------------------------------------------------*//**
  831. * <param name="playerid">Player who entered.</param>
  832. * Checks a players position in the race and displays the corresponding next
  833. * checkpoint or calls Race_Finish.
  834. *//*------------------------------------------------------------------------**/
  835. mhook OnPlayerEnterRaceCP(playerid)
  836. {
  837. return Race_DoEnterRaceCP(playerid);
  838. }
  839. _Y_RACES_STATIC Race_DoEnterRaceCP(playerid)
  840. {
  841. new
  842. race = YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_RACE];
  843. if (race & RACE_PLAYER_OUT) return 1;
  844. // Race_IsValid check mixed with running check.
  845. if ((0 <= race < MAX_RACES) && (YSI_g_sRaceData[race][E_RACE_FLAGS] & (e_RACE_FLAGS_ACTIVE | e_RACE_FLAGS_STARTED) == (e_RACE_FLAGS_ACTIVE | e_RACE_FLAGS_STARTED)))
  846. {
  847. DisablePlayerRaceCheckpoint(playerid);
  848. new
  849. start = YSI_g_sRaceData[race][E_RACE_CP_START],
  850. check = YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_CP],
  851. checks = YSI_g_sRaceData[race][E_RACE_CHECKPOINTS],
  852. laps = YSI_g_sRaceData[race][E_RACE_LAPS],
  853. type = _:(YSI_g_sRaceData[race][E_RACE_FLAGS] & e_RACE_FLAGS_ARIAL);
  854. if (laps)
  855. {
  856. new
  857. lap = YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_LAP];
  858. if (lap == laps)
  859. {
  860. Race_Finish(playerid);
  861. return 1;
  862. }
  863. else
  864. {
  865. if (check + 1 == checks)
  866. {
  867. if (lap + 1 == laps)
  868. {
  869. SetPlayerRaceCheckpoint(playerid, type ? 4 : 1, YSI_g_sRaceCheckpoints[start][0], YSI_g_sRaceCheckpoints[start][1], YSI_g_sRaceCheckpoints[start][2], 0.0, 0.0, 0.0, 5.0);
  870. }
  871. else
  872. {
  873. check += start;
  874. SetPlayerRaceCheckpoint(playerid, type ? 3 : 0, YSI_g_sRaceCheckpoints[check][0], YSI_g_sRaceCheckpoints[check][1], YSI_g_sRaceCheckpoints[check][2], YSI_g_sRaceCheckpoints[start][0], YSI_g_sRaceCheckpoints[start][1], YSI_g_sRaceCheckpoints[start][2], 5.0);
  875. }
  876. YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_CP] = 0;
  877. ++YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_LAP];
  878. return 1;
  879. }
  880. else
  881. {
  882. check += start;
  883. SetPlayerRaceCheckpoint(playerid, type ? 3 : 0, YSI_g_sRaceCheckpoints[check][0], YSI_g_sRaceCheckpoints[check][1], YSI_g_sRaceCheckpoints[check][2], YSI_g_sRaceCheckpoints[check + 1][0], YSI_g_sRaceCheckpoints[check + 1][1], YSI_g_sRaceCheckpoints[check + 1][2], 5.0);
  884. ++YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_CP];
  885. return 1;
  886. }
  887. }
  888. }
  889. else
  890. {
  891. switch (checks - check)
  892. {
  893. case 0:
  894. {
  895. Race_Finish(playerid);
  896. }
  897. case 1:
  898. {
  899. check += start;
  900. SetPlayerRaceCheckpoint(playerid, type ? 4 : 1, YSI_g_sRaceCheckpoints[check][0], YSI_g_sRaceCheckpoints[check][1], YSI_g_sRaceCheckpoints[check][2], 0.0, 0.0, 0.0, 5.0);
  901. }
  902. default:
  903. {
  904. check += start;
  905. SetPlayerRaceCheckpoint(playerid, type ? 3 : 0, YSI_g_sRaceCheckpoints[check][0], YSI_g_sRaceCheckpoints[check][1], YSI_g_sRaceCheckpoints[check][2], YSI_g_sRaceCheckpoints[check + 1][0], YSI_g_sRaceCheckpoints[check + 1][1], YSI_g_sRaceCheckpoints[check + 1][2], 5.0);
  906. }
  907. }
  908. ++YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_CP];
  909. return 1;
  910. }
  911. }
  912. return 1;
  913. }
  914. /*-------------------------------------------------------------------------*//**
  915. * <param name="playerid">Player who's state changed.</param>
  916. * <param name="newstate">Their new state.</param>
  917. * <param name="oldstate">Their last state.</param>
  918. * Notes:
  919. * Processes a players vehicle exit or entry mid race and
  920. * calls the relevant functions.
  921. *//*------------------------------------------------------------------------**/
  922. mhook OnPlayerStateChange(playerid, newstate, oldstate)
  923. {
  924. new
  925. race = YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_RACE];
  926. if (!Race_IsValid(race) || !(YSI_g_sRaceData[race][E_RACE_FLAGS] & e_RACE_FLAGS_STARTED)) return 0;
  927. if (oldstate == PLAYER_STATE_DRIVER)
  928. {
  929. if (!(YSI_g_sRaceData[race][E_RACE_FLAGS] & e_RACE_FLAGS_EXIT_TIME) || newstate != PLAYER_STATE_EXIT_VEHICLE) Race_Exit(playerid);
  930. else Race_Dropout(playerid);
  931. }
  932. else if (newstate == PLAYER_STATE_DRIVER) Race_Rejoin(playerid);
  933. else if (newstate != PLAYER_STATE_ONFOOT && newstate != PLAYER_STATE_ENTER_VEHICLE_DRIVER) Race_Exit(playerid);
  934. return 1;
  935. }
  936. /*-------------------------------------------------------------------------*//**
  937. * <param name="playerid">Player who left.</param>
  938. * <param name="reason">Why they left.</param>
  939. * Notes:
  940. * Similar to the Race_OnPlayerStateChange function but
  941. * instantly exits them from the race as they're not there
  942. * anymore.
  943. *//*------------------------------------------------------------------------**/
  944. mhook OnPlayerDisconnect(playerid, reason)
  945. {
  946. new
  947. race = YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_RACE];
  948. if (!Race_IsValid(race)) return 0;
  949. if (YSI_g_sRaceData[race][E_RACE_FLAGS] & e_RACE_FLAGS_STARTED)
  950. {
  951. Race_Exit(playerid);
  952. }
  953. else
  954. {
  955. Race_PlayerLeave(playerid);
  956. }
  957. return 1;
  958. #pragma unused reason
  959. }
  960. /*-------------------------------------------------------------------------*//**
  961. * <param name="playerid">Player who joined.</param>
  962. * Just a fix for IsPlayerInRaceCheckpoint.
  963. *//*------------------------------------------------------------------------**/
  964. mhook OnPlayerConnect(playerid)
  965. {
  966. DisablePlayerRaceCheckpoint(playerid);
  967. return 1;
  968. }
  969. #if defined RACE_POSITION
  970. /*-------------------------------------------------------------------------*//**
  971. * If compiled with RACE_POSITION this function will keep track of all player's
  972. * positions in their race. It uses current lap, checkpoint and distance from next
  973. * checkpoint to approximate position.
  974. *
  975. * Note: If a race doubles back between two checkpoints you may be closer than
  976. * another player thus show as a higher position when you are infact behind them.
  977. *//*------------------------------------------------------------------------**/
  978. #if YSIM_HAS_MASTER && (_YSIM_IS_CLIENT || _YSIM_IS_STUB)
  979. stock Race_Loop()
  980. #else
  981. task Race_Loop[500 / MAX_RACES]()
  982. #endif
  983. {
  984. static
  985. race = -1;
  986. // Process a different race every call.
  987. race = (race + 1) % MAX_RACES;
  988. static
  989. racePos[MAX_PLAYERS][E_RACE_POS];
  990. if (Race__IsActive(race))
  991. {
  992. new
  993. start = YSI_g_sRaceData[race][E_RACE_CP_START],
  994. lap,
  995. cp = Iter_Count(YSI_g_sRacePeople[race]),
  996. Float:togo,
  997. pos;
  998. if (!cp)
  999. {
  1000. return;
  1001. }
  1002. foreach (new playerid : YSI_g_sRacePeople[race])
  1003. {
  1004. lap = YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_LAP],
  1005. cp = YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_CP] + start;
  1006. static
  1007. Float:x,
  1008. Float:y,
  1009. Float:z;
  1010. GetPlayerPos(playerid, x, y, z);
  1011. // Get the player's (squared) distance to the next checkpoint.
  1012. x -= YSI_g_sRaceCheckpoints[cp][0];
  1013. y -= YSI_g_sRaceCheckpoints[cp][1];
  1014. z -= YSI_g_sRaceCheckpoints[cp][2];
  1015. togo = ((x * x) + (y * y) + (z * z)),
  1016. pos = 1;
  1017. racePos[playerid][E_RACE_POS_CP] = cp;
  1018. racePos[playerid][E_RACE_POS_TOGO] = togo;
  1019. for (new i = Iter_Begin(YSI_g_sRacePeople[race]); (i = Iter_Next(YSI_g_sRacePeople[race], i)) != playerid; )
  1020. {
  1021. // This checks "checkpoint + start", but both players have
  1022. // that offset so it is fine.
  1023. if (YSI_g_sPlayerRace[i][E_RACE_PLAYER_LAP] > lap ||
  1024. (YSI_g_sPlayerRace[i][E_RACE_PLAYER_LAP] == lap &&
  1025. (racePos[i][E_RACE_POS_CP] > cp ||
  1026. (racePos[i][E_RACE_POS_CP] == cp && racePos[i][E_RACE_POS_TOGO] < togo)))) ++pos;
  1027. // This player is on a higher lap, or on a higher
  1028. // checkpoint, or less distance from their next checkpoint.
  1029. else ++YSI_g_sPlayerRace[i][E_RACE_PLAYER_POSITION];
  1030. }
  1031. YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_POSITION] = pos;
  1032. }
  1033. #if defined RACE_SHOW_POS
  1034. // Race time.
  1035. lap = Race_GetPlayerRaceTime(Iter_First(YSI_g_sRacePeople[race])) / 1000;
  1036. // Racer count.
  1037. cp = Iter_Count(YSI_g_sRacePeople[race]);
  1038. new
  1039. time[8],
  1040. th[3];
  1041. format(time, sizeof (time), "%d:%02d", lap / 60, lap % 60);
  1042. foreach (new playerid : YSI_g_sRacePeople[race])
  1043. {
  1044. pos = YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_POSITION];
  1045. switch (pos % 20)
  1046. {
  1047. case 1: th = "ST";
  1048. case 2: th = "ND";
  1049. case 3: th = "RD";
  1050. case 11: if (pos % 100 > 20) th = "TH"; else th = "ST";
  1051. case 12: if (pos % 100 > 20) th = "TH"; else th = "ND";
  1052. case 13: if (pos % 100 > 20) th = "TH"; else th = "RD";
  1053. default: th = "TH";
  1054. }
  1055. // Show the background.
  1056. // Show the position.
  1057. // Show the "th".
  1058. // Show the races count.
  1059. // Show the time.
  1060. }
  1061. #endif
  1062. }
  1063. }
  1064. /*-------------------------------------------------------------------------*//**
  1065. * <param name="playerid">Player to get position of.</param>
  1066. * <returns>Dynamic position in race.</returns>
  1067. *//*------------------------------------------------------------------------**/
  1068. global Race_GetPlayerPosition(playerid)
  1069. {
  1070. return YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_POSITION];
  1071. }
  1072. #endif
  1073. /*-------------------------------------------------------------------------*//**
  1074. * <param name="playerid">Player to get time for.</param>
  1075. * <returns>Time in race so far.</returns>
  1076. *//*------------------------------------------------------------------------**/
  1077. global Race_GetPlayerRaceTime(playerid)
  1078. {
  1079. return GetTickCount() - YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_TIME];
  1080. }
  1081. /*-------------------------------------------------------------------------*//**
  1082. * <param name="playerid">Player to get race of.</param>
  1083. * <returns>Player's race.</returns>
  1084. *//*------------------------------------------------------------------------**/
  1085. global Race_GetPlayerRace(playerid)
  1086. {
  1087. if (VALID_PLAYERID(playerid)) return YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_RACE];
  1088. return NO_RACE;
  1089. }
  1090. /*-------------------------------------------------------------------------*//**
  1091. * <param name="race">Which race to add them to.</param>
  1092. * <param name="playerid">Player to set in the race.</param>
  1093. * <param name="set">Add or remove them.</param>
  1094. *//*------------------------------------------------------------------------**/
  1095. global Race_SetPlayer(race, playerid, bool:set)
  1096. {
  1097. if (Race_IsValid(race))
  1098. {
  1099. if (set)
  1100. {
  1101. if (!(YSI_g_sRaceData[race][E_RACE_FLAGS] & e_RACE_FLAGS_STARTED))
  1102. {
  1103. Race_PlayerJoin(playerid, race);
  1104. return 1;
  1105. }
  1106. }
  1107. else if (race == YSI_g_sPlayerRace[playerid][E_RACE_PLAYER_RACE])
  1108. {
  1109. if (YSI_g_sRaceData[race][E_RACE_FLAGS] & e_RACE_FLAGS_STARTED)
  1110. {
  1111. Race_Exit(playerid);
  1112. }
  1113. else
  1114. {
  1115. Race_PlayerLeave(playerid);
  1116. }
  1117. return 1;
  1118. }
  1119. }
  1120. return 0;
  1121. }
  1122. // Called when a player finishes the race.
  1123. forward OnPlayerFinishRace(playerid, race, position, prize, time);
  1124. // Called when a player drops out of the race.
  1125. forward OnPlayerExitRace(playerid, race);
  1126. // Called when the race is over.
  1127. forward OnRaceEnd(race);