y_playerset.inc 15 KB

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