1
0

y_playerset.inc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582
  1. #if defined _INC_y_playerset
  2. #endinput
  3. #endif
  4. #define _INC_y_playerset
  5. /**
  6. * <library name="y_playerset">
  7. * <section>
  8. * Description
  9. * </section>
  10. * This code is a huge abstraction of collections of players. It allows you to
  11. * define functions which take one or more players, specified in a number of
  12. * formats, and perform the code for all those player. Essentially it is an
  13. * abstraction of loops over players.
  14. * <section>
  15. * Version
  16. * </section>
  17. * 1.0
  18. * </library>
  19. *//** *//*
  20. Legal:
  21. Version: MPL 1.1
  22. The contents of this file are subject to the Mozilla Public License Version
  23. 1.1 the "License"; you may not use this file except in compliance with
  24. the License. You may obtain a copy of the License at
  25. http://www.mozilla.org/MPL/
  26. Software distributed under the License is distributed on an "AS IS" basis,
  27. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  28. for the specific language governing rights and limitations under the
  29. License.
  30. The Original Code is the YSI framework.
  31. The Initial Developer of the Original Code is Alex "Y_Less" Cole.
  32. Portions created by the Initial Developer are Copyright C 2011
  33. the Initial Developer. All Rights Reserved.
  34. Contributors:
  35. Y_Less
  36. koolk
  37. JoeBullet/Google63
  38. g_aSlice/Slice
  39. Misiur
  40. samphunter
  41. tianmeta
  42. maddinat0r
  43. spacemud
  44. Crayder
  45. Dayvison
  46. Ahmad45123
  47. Zeex
  48. irinel1996
  49. Yiin-
  50. Chaprnks
  51. Konstantinos
  52. Masterchen09
  53. Southclaws
  54. PatchwerkQWER
  55. m0k1
  56. paulommu
  57. udan111
  58. Thanks:
  59. JoeBullet/Google63 - Handy arbitrary ASM jump code using SCTRL.
  60. ZeeX - Very productive conversations.
  61. koolk - IsPlayerinAreaEx code.
  62. TheAlpha - Danish translation.
  63. breadfish - German translation.
  64. Fireburn - Dutch translation.
  65. yom - French translation.
  66. 50p - Polish translation.
  67. Zamaroht - Spanish translation.
  68. Los - Portuguese translation.
  69. Dracoblue, sintax, mabako, Xtreme, other coders - Producing other modes for
  70. me to strive to better.
  71. Pixels^ - Running XScripters where the idea was born.
  72. Matite - Pestering me to release it and using it.
  73. Very special thanks to:
  74. Thiadmer - PAWN, whose limits continue to amaze me!
  75. Kye/Kalcor - SA:MP.
  76. SA:MP Team past, present and future - SA:MP.
  77. Optional plugins:
  78. Gamer_Z - GPS.
  79. Incognito - Streamer.
  80. Me - sscanf2, fixes2, Whirlpool.
  81. */
  82. #include <a_samp>
  83. #include "..\YSI_Data\y_iterate"
  84. #include "..\YSI_Data\y_playerarray"
  85. #include "..\YSI_Core\y_debug"
  86. #if !defined MAX_PLAYER_SET_STACK
  87. #define MAX_PLAYER_SET_STACK (3)
  88. #endif
  89. #define ALL_PLAYERS (0x7FFFFFF1)
  90. #define ALL_HUMANS (0x7FFFFFF1)
  91. #define ALL_CHARACTERS (0x7FFFFFF2)
  92. #define ALL_BOTS (0x7FFFFFF3)
  93. enum e_PLAYER_SET_HACK
  94. {
  95. e_PLAYER_SET_HACK_PA[bits<MAX_PLAYERS> + 1] = 0,
  96. e_PLAYER_SET_HACK_DATA[MAX_PLAYERS] = 0,
  97. e_PLAYER_SET_HACK_EXCESS[MAX_PLAYERS - (bits<MAX_PLAYERS> + 1)]
  98. }
  99. enum e_PLAYER_SET_TYPE
  100. {
  101. e_PLAYER_SET_TYPE_NONE = 0,
  102. // "GROUP" is a YSI group.
  103. e_PLAYER_SET_TYPE_GROUP,
  104. // "ID" is just a single player.
  105. e_PLAYER_SET_TYPE_ID,
  106. // "PA" is a YSI player array.
  107. e_PLAYER_SET_TYPE_PA,
  108. // "BOOL" is an array of true/false.
  109. e_PLAYER_SET_TYPE_BOOL,
  110. // "ARRAY" is just an array of elements.
  111. e_PLAYER_SET_TYPE_ARRAY,
  112. // "CUSTOM" is used to identify fields in an enum.
  113. e_PLAYER_SET_TYPE_CUSTOM,
  114. e_PLAYER_SET_TYPE_PLAYERS,
  115. e_PLAYER_SET_TYPE_BOTS,
  116. e_PLAYER_SET_TYPE_CHARACTERS
  117. }
  118. // ========================================================================== //
  119. // ========================================================================== //
  120. // WARNING: THE CODE BELOW IS VERY FRAGILE - DO NOT TOUCH IT! //
  121. // ========================================================================== //
  122. // ========================================================================== //
  123. // DO NOT change the order of these variables!
  124. stock
  125. e_PLAYER_SET_TYPE:__ps_type[MAX_PLAYER_SET_STACK char],
  126. __ps_stack[MAX_PLAYER_SET_STACK][MAX_PLAYERS],
  127. __ps_data[e_PLAYER_SET_HACK],
  128. __ps_pointer = -1;
  129. #define @PlayerVar:%0) __ps_addr_t:__ps_addr,__ps_drop_t:%0)foreach(new %0:PS(__ps_addr))
  130. // More than one parameter. This removes the need to redefine "for", which I'm
  131. // very happy about, by doing all detection in one go.
  132. #define __ps_addr_t:__ps_addr,__ps_drop_t:%0,%1)foreach(%2,%3:PS(__ps_addr)) __ps_addr_t:__ps_addr,%1)foreach(%2:PS(__ps_addr))
  133. // Only one parameter (not caught by the above statement). The one is the
  134. // variable name we steal for the "foreach" loop.
  135. #define __ps_addr,__ps_drop_t:%0) __ps_addr)
  136. // This is not affected by any of the macros above.
  137. #define @PlayerArray:%0<%1>%2) __ps_addr_t:__ps_addr%2)for(new PlayerArray:%0<MAX_PLAYERS>;__PS_A(__ps_addr,%0); )
  138. #define @PlayerSet __ps_addr_t
  139. // This code is now less fragile than before (and I understand it far more
  140. // having done much more work with this style of macro in the interim).
  141. // This is the master function, and a long one at that. This function looks at
  142. // the parameters passed to it and determines what SORT of parameter has been
  143. // passed from the long list of possibilities. If "cur" is -1 then this is the
  144. // first call of the function and we need to determine the type. If "cur" is
  145. // not -1 then we are mid-loop and we can just use the stored determined type
  146. // and use "cur" (as the last player done) to figure out the next player. This
  147. // code only loops through connected players.
  148. stock Iter_Func@PS(cur, __ps_addr_t:addr)
  149. {
  150. if (cur == -1)
  151. {
  152. P:3("__PS_S called: %i", _:addr);
  153. // Increment the "stack" pointer.
  154. if (__ps_pointer == MAX_PLAYER_SET_STACK - 1)
  155. {
  156. P:E("y_playerset stack overflow - increase \"MAX_PLAYER_SET_STACK\"");
  157. return -1;
  158. }
  159. ++__ps_pointer;
  160. new
  161. begin = __ps_data[e_PLAYER_SET_HACK_DATA][0];
  162. // Is this a single value element (group or ID).
  163. if (_:addr == begin)
  164. {
  165. // Increase the stack pointer for recursive/multi-layered calls.
  166. // Should really add error-checking code for overflows.
  167. __ps_stack[__ps_pointer][0] = _:addr;
  168. // Single value - either a playerid or a groupid.
  169. #if defined _YSI_HAS_y_groups
  170. if (Group:addr & (Group:0x80000000))//GROUP_MASK)
  171. {
  172. // Use the pre-made iterator functionality.
  173. __ps_type{__ps_pointer} = e_PLAYER_SET_TYPE_GROUP;
  174. cur = Iter_Func@GroupMember(-1, Group:addr);
  175. if (cur == -1)
  176. {
  177. --__ps_pointer;
  178. }
  179. return cur;
  180. }
  181. #endif
  182. // May not always want this check - tough, they can't really have one
  183. // inside the function because that's just silly.
  184. switch (_:addr)
  185. {
  186. case ALL_PLAYERS:
  187. {
  188. // Uses the new "foreach" format of the infinate loop.
  189. __ps_type{__ps_pointer} = e_PLAYER_SET_TYPE_PLAYERS;
  190. cur = Iter_First(Player);
  191. if (cur == Iter_End(Player))
  192. {
  193. --__ps_pointer;
  194. cur = -1;
  195. }
  196. return cur;
  197. }
  198. #if defined _FOREACH_BOT && !defined FOREACH_NO_BOTS
  199. case ALL_BOTS:
  200. {
  201. __ps_type{__ps_pointer} = e_PLAYER_SET_TYPE_BOTS;
  202. cur = Iter_First(Bot);
  203. if (cur == Iter_End(Bot))
  204. {
  205. --__ps_pointer;
  206. cur = -1;
  207. }
  208. return cur;
  209. }
  210. case ALL_CHARACTERS:
  211. {
  212. __ps_type{__ps_pointer} = e_PLAYER_SET_TYPE_CHARACTERS;
  213. cur = Iter_First(Character);
  214. if (cur == Iter_End(Character))
  215. {
  216. --__ps_pointer;
  217. cur = -1;
  218. }
  219. return cur;
  220. }
  221. #endif
  222. default:
  223. {
  224. __ps_type{__ps_pointer} = e_PLAYER_SET_TYPE_ID;
  225. if (PS_IS_PLAYER_CONNECTED(_:addr))
  226. {
  227. return _:addr;
  228. }
  229. else
  230. {
  231. --__ps_pointer;
  232. return -1;
  233. }
  234. }
  235. }
  236. }
  237. else
  238. {
  239. // It's an array - _:addr contains the address of the target.
  240. memcpy(__ps_stack[__ps_pointer], __ps_data[e_PLAYER_SET_HACK_DATA], 0, MAX_PLAYERS * 4);
  241. // Try to determine what sort of array it is. Note that there are three
  242. // possible types.
  243. if (begin == PA_TYPE_PA)
  244. {
  245. // Easy to handle - the systems were designed for each other.
  246. // This one needs work... Err - what work?
  247. __ps_type{__ps_pointer} = e_PLAYER_SET_TYPE_PA;
  248. cur = Iter_Func@PA(-1, Bit:__ps_stack[__ps_pointer]);
  249. if (cur == -1)
  250. {
  251. --__ps_pointer;
  252. }
  253. return cur;
  254. }
  255. else if (begin & 0xFF0000FF == 0x0F0000F0)
  256. {
  257. __ps_type{__ps_pointer} = e_PLAYER_SET_TYPE_CUSTOM;
  258. cur = begin >>> 8 & 0x0000FFFF;
  259. if (cur == INVALID_PLAYER_ID & 0x0000FFFF)
  260. {
  261. --__ps_pointer;
  262. return -1;
  263. }
  264. if (PS_IS_PLAYER_CONNECTED(cur))
  265. {
  266. return cur;
  267. }
  268. addr = __ps_addr_t:0;
  269. while (++_:addr != MAX_PLAYERS)
  270. {
  271. cur = __ps_stack[__ps_pointer][_:addr];
  272. if (cur == INVALID_PLAYER_ID)
  273. {
  274. --__ps_pointer;
  275. return -1;
  276. }
  277. if (PS_IS_PLAYER_CONNECTED(cur))
  278. {
  279. return cur;
  280. }
  281. }
  282. --__ps_pointer;
  283. return -1;
  284. }
  285. else if (begin > 1 || __ps_data[e_PLAYER_SET_HACK_DATA][1] > 1 || __ps_data[e_PLAYER_SET_HACK_DATA][2] > 1)
  286. {
  287. // List of players. One of the first three will normally be greater
  288. // than 1 in a list of players.
  289. __ps_type{__ps_pointer} = e_PLAYER_SET_TYPE_ARRAY;
  290. cur = begin;
  291. if (cur == INVALID_PLAYER_ID)
  292. {
  293. --__ps_pointer;
  294. return -1;
  295. }
  296. if (PS_IS_PLAYER_CONNECTED(cur))
  297. {
  298. return cur;
  299. }
  300. addr = __ps_addr_t:0;
  301. while (++_:addr != MAX_PLAYERS)
  302. {
  303. cur = __ps_stack[__ps_pointer][_:addr];
  304. if (cur == INVALID_PLAYER_ID)
  305. {
  306. --__ps_pointer;
  307. return -1;
  308. }
  309. if (PS_IS_PLAYER_CONNECTED(cur))
  310. {
  311. return cur;
  312. }
  313. }
  314. --__ps_pointer;
  315. return -1;
  316. }
  317. else
  318. {
  319. // Boolean array.
  320. __ps_type{__ps_pointer} = e_PLAYER_SET_TYPE_BOOL;
  321. // Find the first set player.
  322. foreach (cur : Player)
  323. {
  324. // Now ANY true value is true.
  325. if (__ps_data[e_PLAYER_SET_HACK_DATA][cur])
  326. {
  327. //_:addr = i;
  328. return cur;
  329. }
  330. }
  331. // No players specified.
  332. --__ps_pointer;
  333. return -1;
  334. }
  335. }
  336. // Will have returned by this point.
  337. }
  338. P:3("__PS_N called: %i, %i", _:addr, cur);
  339. // Each mode has a different end condition.
  340. switch (__ps_type{__ps_pointer})
  341. {
  342. #if defined _YSI_HAS_y_groups
  343. case e_PLAYER_SET_TYPE_GROUP:
  344. {
  345. cur = Iter_Func@GroupMember(cur, Group:_:addr);
  346. if (cur == -1)
  347. {
  348. --__ps_pointer;
  349. }
  350. return cur;
  351. }
  352. #endif
  353. case e_PLAYER_SET_TYPE_PLAYERS:
  354. {
  355. cur = Iter_Next(Player, cur);
  356. if (cur == Iter_End(Player))
  357. {
  358. --__ps_pointer;
  359. cur = -1;
  360. }
  361. return cur;
  362. }
  363. #if defined _FOREACH_BOT && !defined FOREACH_NO_BOTS
  364. case e_PLAYER_SET_TYPE_BOTS:
  365. {
  366. cur = Iter_Next(Bot, cur);
  367. if (cur == Iter_End(Bot))
  368. {
  369. --__ps_pointer;
  370. cur = -1;
  371. }
  372. return cur;
  373. }
  374. case e_PLAYER_SET_TYPE_CHARACTERS:
  375. {
  376. cur = Iter_Next(Character, cur);
  377. if (cur == Iter_End(Character))
  378. {
  379. --__ps_pointer;
  380. cur = -1;
  381. }
  382. return cur;
  383. }
  384. #endif
  385. case e_PLAYER_SET_TYPE_PA:
  386. {
  387. cur = Iter_Func@PA(cur, Bit:__ps_stack[__ps_pointer]);
  388. if (cur == -1)
  389. {
  390. --__ps_pointer;
  391. }
  392. return cur;
  393. }
  394. case e_PLAYER_SET_TYPE_BOOL:
  395. {
  396. for ( ; ; )
  397. {
  398. cur = Iter_Next(Player, cur);
  399. if (cur == Iter_End(Player))
  400. {
  401. --__ps_pointer;
  402. return -1;
  403. }
  404. if (__ps_stack[__ps_pointer][cur])
  405. {
  406. // Don't need to check if they're connected as the data
  407. // comes directly from "foreach".
  408. break;
  409. }
  410. // Could add extra late checks here (Error, not Warning, now).
  411. }
  412. return cur;
  413. }
  414. case e_PLAYER_SET_TYPE_ID:
  415. {
  416. --__ps_pointer;
  417. return -1;
  418. }
  419. case e_PLAYER_SET_TYPE_ARRAY:
  420. {
  421. addr = __ps_addr_t:-1;
  422. while (++_:addr != MAX_PLAYERS)
  423. {
  424. if (__ps_stack[__ps_pointer][_:addr] == cur)
  425. {
  426. break;
  427. }
  428. }
  429. if (_:addr != MAX_PLAYERS)
  430. {
  431. while (++_:addr != MAX_PLAYERS)
  432. {
  433. cur = __ps_stack[__ps_pointer][_:addr];
  434. if (cur == INVALID_PLAYER_ID)
  435. {
  436. --__ps_pointer;
  437. return -1;
  438. }
  439. if (PS_IS_PLAYER_CONNECTED(cur))
  440. {
  441. return cur;
  442. }
  443. }
  444. }
  445. --__ps_pointer;
  446. return -1;
  447. }
  448. case e_PLAYER_SET_TYPE_CUSTOM:
  449. {
  450. if (cur == __ps_stack[__ps_pointer][0] >>> 8 & 0x0000FFFF)
  451. {
  452. addr = __ps_addr_t:0;
  453. while (++_:addr != MAX_PLAYERS)
  454. {
  455. cur = __ps_stack[__ps_pointer][_:addr];
  456. if (cur == INVALID_PLAYER_ID)
  457. {
  458. --__ps_pointer;
  459. return -1;
  460. }
  461. if (PS_IS_PLAYER_CONNECTED(cur))
  462. {
  463. return cur;
  464. }
  465. }
  466. --__ps_pointer;
  467. return -1;
  468. }
  469. else
  470. {
  471. addr = __ps_addr_t:0;
  472. while (++_:addr != MAX_PLAYERS)
  473. {
  474. if (__ps_stack[__ps_pointer][_:addr] == cur)
  475. {
  476. break;
  477. }
  478. }
  479. if (_:addr != MAX_PLAYERS)
  480. {
  481. while (++_:addr != MAX_PLAYERS)
  482. {
  483. cur = __ps_stack[__ps_pointer][_:addr];
  484. if (cur == INVALID_PLAYER_ID)
  485. {
  486. --__ps_pointer;
  487. return -1;
  488. }
  489. if (PS_IS_PLAYER_CONNECTED(cur))
  490. {
  491. return cur;
  492. }
  493. }
  494. }
  495. --__ps_pointer;
  496. return -1;
  497. }
  498. }
  499. }
  500. --__ps_pointer;
  501. return -1;
  502. }
  503. #define Iterator@PS iterstart(-1)
  504. // This function gets the required data from custom format (enum) arrays.
  505. stock __PS_C(source[MAX_PLAYERS][], idx)
  506. {
  507. static
  508. sFake[MAX_PLAYERS] = {(INVALID_PLAYER_ID << 8) | 0x0F0000F0};
  509. new
  510. ret = (__ps_data[e_PLAYER_SET_HACK_DATA] = sFake),
  511. e_PLAYER_SET_HACK:i = e_PLAYER_SET_HACK:0;
  512. foreach (new playerid : Player)
  513. {
  514. if (source[playerid][idx])
  515. {
  516. if (i)
  517. {
  518. __ps_data[i++] = playerid;
  519. }
  520. else
  521. {
  522. __ps_data[i++] = (playerid << 8) | 0x0F0000F0;
  523. }
  524. }
  525. }
  526. if (i < e_PLAYER_SET_HACK:MAX_PLAYERS)
  527. {
  528. __ps_data[i] = INVALID_PLAYER_ID;
  529. }
  530. return ret;
  531. }
  532. stock bool:__PS_A(@PlayerSet:addr, PlayerArray:ret<MAX_PLAYERS>)
  533. {
  534. if (ret[0])
  535. {
  536. return false;
  537. }
  538. PA_FastInit(ret);
  539. foreach (new a : PS(addr))
  540. {
  541. PA_Let(ret, a);
  542. }
  543. return true;
  544. }
  545. // This SHOULD handle excess parameters correctly, simply because I left out the
  546. // extra brackets.
  547. #define PSF:%0(%1) %0(__ps_addr_t:__ps_data[e_PLAYER_SET_HACK_PA]=__ps_addr_t:%1)
  548. // This redefines e_PLAYER_SET_HACK_DATA in the case of passing player arrays.
  549. #define e_PLAYER_SET_HACK_PA]=__ps_addr_t:@%0) e_PLAYER_SET_HACK_DATA]=__ps_addr_t:%0)
  550. // Don't actually need the "@".
  551. #define __ps_data[e_PLAYER_SET_HACK_DATA]=%0<%1> __ps_addr_t:__PS_C(%0,%1)
  552. #define __PS_C(@%0,%1) __PS_C(%0,%1)