multiclass.inc 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973
  1. /**--------------------------------------------------------------------------**\
  2. ======================================
  3. y_classes - Advanced class selection
  4. ======================================
  5. Description:
  6. Allows greater control over classes so not everyone has every class. Uses
  7. a form of compression for locations.
  8. Legal:
  9. Version: MPL 1.1
  10. The contents of this file are subject to the Mozilla Public License Version
  11. 1.1 (the "License"); you may not use this file except in compliance with
  12. the License. You may obtain a copy of the License at
  13. http://www.mozilla.org/MPL/
  14. Software distributed under the License is distributed on an "AS IS" basis,
  15. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  16. for the specific language governing rights and limitations under the
  17. License.
  18. The Original Code is the YSI classes include.
  19. The Initial Developer of the Original Code is Alex "Y_Less" Cole.
  20. Portions created by the Initial Developer are Copyright (C) 2011
  21. the Initial Developer. All Rights Reserved.
  22. Contributors:
  23. ZeeX, koolk, JoeBullet/Google63, g_aSlice/Slice
  24. Thanks:
  25. JoeBullet/Google63 - Handy arbitrary ASM jump code using SCTRL.
  26. ZeeX - Very productive conversations.
  27. koolk - IsPlayerinAreaEx code.
  28. TheAlpha - Danish translation.
  29. breadfish - German translation.
  30. Fireburn - Dutch translation.
  31. yom - French translation.
  32. 50p - Polish translation.
  33. Zamaroht - Spanish translation.
  34. Dracoblue, sintax, mabako, Xtreme, other coders - Producing other modes
  35. for me to strive to better.
  36. Pixels^ - Running XScripters where the idea was born.
  37. Matite - Pestering me to release it and using it.
  38. Very special thanks to:
  39. Thiadmer - PAWN, whose limits continue to amaze me!
  40. Kye/Kalcor - SA:MP.
  41. SA:MP Team past, present and future - SA:MP.
  42. Version:
  43. 0.1
  44. Changelog:
  45. 14/04/12:
  46. Readded OnRequestSpawn logic.
  47. Fixed Class_AddWithGroupSet.
  48. Improved GM/FS interaction.
  49. 02/01/08:
  50. First '08 edit - Fixed minus numbers in spawn points.
  51. 18/11/07:
  52. Improved location compression to allow larger areas.
  53. Moved position code to Class_OnPlayerSpawn to reduce overhead.
  54. 10/10/07:
  55. Fixed spawn data problem.
  56. 31/08/07:
  57. Added cheap master system - YSI_SLAVE_CLASSs can't be master.
  58. 05/08/07:
  59. Fixed a few bugs with repeated selection.
  60. 04/08/07:
  61. First version.
  62. Functions:
  63. Public:
  64. Class_AddRemote - Adds a class to the remote master.
  65. Class_Remote - Updates settings remotely.
  66. Core:
  67. Class_Class - Sets up the system.
  68. Class_OnPlayerRequestSpawn - Called when a player requests a spawn.
  69. Class_OnPlayerRequestClass - Called when a player requests a class.
  70. Class_OnPlayerConnect - Called when a player connects.
  71. Stock:
  72. Class_Delete - Removes a class.
  73. Class_SetPlayer - Sets whether or not a player can use a class.
  74. Class_Disable - Disables a class.
  75. Class_Enable - Enables a disabled class.
  76. Class_AddForGroup - Adds a class to the system for only one group.
  77. Class_Add - Adds a class to the system.
  78. Class_AddEx - Adds a class to the system with group selection and setting.
  79. Class_AddWithGroupSet - Adds a class and sets their group on selection.
  80. Class_AllowReselection - Allows or disallows people to return to reselect a class.
  81. Class_RequireSpawnLogin - Require people to login before spawning.
  82. Class_RequireSelectionLogin - Require people to login before selecting.
  83. Class_RequireSelectionReg - Require people to register before selecting.
  84. Class_RequireSpawnReg - Require people to register before spawning.
  85. Static:
  86. Class_AddClass - Adds a class, wrapped by API and remote functions.
  87. Inline:
  88. _Class_IsActive - Checks a class is active.
  89. _Class_Enabled - Checks a class is enabled.
  90. _Class_IsValid - Checks a class is valid.
  91. Class_X - Gets a classes x position.
  92. Class_Y - Gets a classes y position.
  93. Class_Z - Gets a classes z position.
  94. Class_A - Gets a classes angle.
  95. Class_Skin - Gets a classes skin.
  96. API:
  97. -
  98. Callbacks:
  99. -
  100. Definitions:
  101. MAX_CLASSES - Maximum number of classes storeable by the system.
  102. CLASS_LEFT - Flag for last internal class viewed.
  103. CLASS_MIDDLE - Flag for last internal class viewed.
  104. CLASS_RIGHT - Flag for last internal class viewed.
  105. Enums:
  106. e_CLASS_FLAGS - Small data for individual classes.
  107. E_CLASS - Class data structure.
  108. Macros:
  109. -
  110. Tags:
  111. -
  112. Variables:
  113. Global:
  114. -
  115. Static:
  116. YSI_g_sClasses - Data for classes.
  117. YSI_g_sPlayerClass - Player's current classes.
  118. YSI_g_sLeft - Handle for the first internal class.
  119. YSI_g_sMiddle - Handle for the second internal class.
  120. YSI_g_sRight - Handle for the third internal class.
  121. YSI_g_sClassCount - Number of classes stored.
  122. Commands:
  123. -
  124. Compile options:
  125. -
  126. Operators:
  127. -
  128. \**--------------------------------------------------------------------------**/
  129. PRE_HOOK(Class)
  130. #undef CHAIN_ORDER
  131. #define CHAIN_ORDER @CO_Class
  132. forward Class_ResolveGroups(class, Group:forgroup, bool:cp);
  133. enum e_CLASS_FLAGS (<<= 1)
  134. {
  135. e_CLASS_FLAGS_SKIN = 0x0000FFFF,
  136. e_CLASS_FLAGS_ENABLED = 0x00010000,
  137. e_CLASS_FLAGS_ACTIVE
  138. }
  139. enum E_CLASS
  140. {
  141. e_CLASS_FLAGS:E_CLASS_FLAGS,
  142. Float:E_CLASS_X,
  143. Float:E_CLASS_Y,
  144. Float:E_CLASS_Z,
  145. Float:E_CLASS_A,
  146. E_CLASS_WEAPONS[MAX_CLASS_SPAWN_WEAPONS + 1],
  147. Group:E_CLASS_GROUP,
  148. PlayerArray:E_CLASS_PLAYERS<MAX_PLAYERS>
  149. }
  150. // enum e_PLAYER_CLASS (<<= 1)
  151. // {
  152. // e_PLAYER_CLASS_NONE = 0,
  153. // e_PLAYER_CLASS_SKIN = 0x000FFFFF,
  154. // e_PLAYER_CLASS_IN_SELECTION
  155. // }
  156. static stock
  157. YSI_g_sClasses[MAX_CLASSES][E_CLASS],
  158. YSI_g_sPlayerClass[MAX_PLAYERS],
  159. YSI_g_sInternalClass[MAX_PLAYERS],
  160. YSI_g_sClassCount,
  161. YSI_g_sLastRefuse[MAX_PLAYERS];
  162. stock Class_ResolveGroups(class, Group:forgroup, bool:cp) <YSI_has_groups : n>
  163. {
  164. #pragma unused forgroup
  165. P:2("Class_ResolveGroups<n>: call Resolve %d %d %d", class, _:forgroup, cp);
  166. if (!cp) PA_Init(YSI_g_sClasses[class][E_CLASS_PLAYERS], true);
  167. }
  168. stock Class_ResolveGroups(class, Group:forgroup, bool:cp) <>
  169. {
  170. #pragma unused forgroup
  171. P:2("Class_ResolveGroups<>: call Resolve %d %d %d", class, _:forgroup, cp);
  172. if (!cp) PA_Init(YSI_g_sClasses[class][E_CLASS_PLAYERS], true);
  173. }
  174. /**--------------------------------------------------------------------------**\
  175. <summary>_Class_IsActive</summary>
  176. <param name="classid">Class to check if active.</param>
  177. <returns>
  178. -
  179. </returns>
  180. <remarks>
  181. -
  182. </remarks>
  183. \**--------------------------------------------------------------------------**/
  184. #define _Class_IsActive(%1) (YSI_g_sClasses[(%1)][E_CLASS_FLAGS] & e_CLASS_FLAGS_ACTIVE)
  185. /**--------------------------------------------------------------------------**\
  186. <summary>_Class_IsValid</summary>
  187. <param name="classid">Class to check if valid.</param>
  188. <returns>
  189. -
  190. </returns>
  191. <remarks>
  192. -
  193. </remarks>
  194. \**--------------------------------------------------------------------------**/
  195. #define _Class_IsValid(%1) (IS_IN_RANGE((%1), 0, MAX_CLASSES) && _Class_IsActive(%1))
  196. /**--------------------------------------------------------------------------**\
  197. <summary>_Class_Enabled</summary>
  198. <param name="classid">Class to check.</param>
  199. <returns>
  200. -
  201. </returns>
  202. <remarks>
  203. -
  204. </remarks>
  205. \**--------------------------------------------------------------------------**/
  206. #define _Class_Enabled(%1) (YSI_g_sClasses[(%1)][E_CLASS_FLAGS] & e_CLASS_FLAGS_ENABLED)
  207. /**--------------------------------------------------------------------------**\
  208. <summary>Class_X</summary>
  209. <param name="classid">Class to get X location for.</param>
  210. <returns>
  211. -
  212. </returns>
  213. <remarks>
  214. -
  215. </remarks>
  216. \**--------------------------------------------------------------------------**/
  217. #define Class_X(%1) YSI_g_sClasses[(%1)][E_CLASS_X]
  218. /**--------------------------------------------------------------------------**\
  219. <summary>Class_Y</summary>
  220. <param name="classid">Class to get Y location for.</param>
  221. <returns>
  222. -
  223. </returns>
  224. <remarks>
  225. -
  226. </remarks>
  227. \**--------------------------------------------------------------------------**/
  228. #define Class_Y(%1) YSI_g_sClasses[(%1)][E_CLASS_Y]
  229. /**--------------------------------------------------------------------------**\
  230. <summary>Class_Z</summary>
  231. <param name="classid">Class to get Z location for.</param>
  232. <returns>
  233. -
  234. </returns>
  235. <remarks>
  236. -
  237. </remarks>
  238. \**--------------------------------------------------------------------------**/
  239. #define Class_Z(%1) YSI_g_sClasses[(%1)][E_CLASS_Z]
  240. /**--------------------------------------------------------------------------**\
  241. <summary>Class_A</summary>
  242. <param name="classid">Class to get angle for.</param>
  243. <returns>
  244. -
  245. </returns>
  246. <remarks>
  247. -
  248. </remarks>
  249. \**--------------------------------------------------------------------------**/
  250. #define Class_A(%1) YSI_g_sClasses[(%1)][E_CLASS_A]
  251. /**--------------------------------------------------------------------------**\
  252. <summary>Class_Skin</summary>
  253. <param name="classid">Class to get skin for.</param>
  254. <returns>
  255. -
  256. </returns>
  257. <remarks>
  258. -
  259. </remarks>
  260. \**--------------------------------------------------------------------------**/
  261. #define Class_Skin(%1) (YSI_g_sClasses[(%1)][E_CLASS_FLAGS] & e_CLASS_FLAGS_SKIN)
  262. /**--------------------------------------------------------------------------**\
  263. <summary>Class_Class</summary>
  264. <returns>
  265. -
  266. </returns>
  267. <remarks>
  268. Creates three real player classes so you can scroll correctly with the
  269. direction being detected.
  270. </remarks>
  271. \**--------------------------------------------------------------------------**/
  272. hook OnScriptInit()
  273. {
  274. #if !_YSIM_IS_CLIENT
  275. if (!YSI_FILTERSCRIPT)
  276. {
  277. // This code placement is not generic.
  278. new
  279. classLeft = AddPlayerClass(0, 1958.0, 1343.0, 15.0, 269.0, 0, 0, 0, 0, 0, 0),
  280. classMiddle = AddPlayerClass(0, 1958.0, 1343.0, 15.0, 269.0, 0, 0, 0, 0, 0, 0),
  281. classRight = AddPlayerClass(0, 1958.0, 1343.0, 15.0, 269.0, 0, 0, 0, 0, 0, 0);
  282. if (classLeft != 0 || classMiddle != 1 || classRight != 2)
  283. {
  284. P:E("y_classes assumptions failed. Do you have \"AddPlayerClass\" in your modes?");
  285. }
  286. //printf("==========================");
  287. P:4("Class_OnScriptInit: classes = %d %d %d", classLeft, classMiddle, classRight);
  288. //printf("==========================");
  289. #if !defined YSI_NO_MASTER
  290. if (_Master_Get(#_YCM, true))
  291. {
  292. // Nothing changed, or we took it without force.
  293. state _YCM:y;
  294. _YCM@ = _E_YCM@y;
  295. }
  296. else
  297. {
  298. // Something changed, tell other scripts.
  299. state _YCM:u;
  300. _YCM@ = _E_YCM@u;
  301. // Determine the next unique name.
  302. CallRemoteFunction(#_YCM, "");
  303. // There is a note in y_master about this being safe
  304. // because servers always get the data. This is the server
  305. // code, but may be in use by cloud systems. This, however,
  306. // is still not a problem because we never fully pass off.
  307. }
  308. #endif
  309. }
  310. #endif
  311. return 1;
  312. }
  313. static stock Class_FindNew(playerid, playerclass, dir)
  314. {
  315. P:4("Class_FindNew called: %i, %i, %i", playerid, playerclass, dir);
  316. if (dir == 0)
  317. {
  318. // No change, check the current skin is still valid. There are plenty
  319. // of reasons for this: Removed skin, returned -1, disabled, returned to
  320. // class selection. This used to not be handled explicitly, but now it
  321. // is.
  322. if (_Class_Enabled(playerclass) && PA_Get(YSI_g_sClasses[playerclass][E_CLASS_PLAYERS], playerid)) return playerclass;
  323. ++dir;
  324. }
  325. new
  326. old = playerclass % YSI_g_sClassCount;
  327. do
  328. {
  329. playerclass = (playerclass + dir) % YSI_g_sClassCount;
  330. P:4("Class_FindNew: %d %d", playerclass, playerid);
  331. }
  332. while (playerclass != old && (!_Class_Enabled(playerclass) || !PA_Get(YSI_g_sClasses[playerclass][E_CLASS_PLAYERS], playerid)));
  333. // (!(playerclass == old || (_Class_Enabled(playerclass) && PA_Get(YSI_g_sClasses[playerclass][E_CLASS_PLAYERS], playerid))))
  334. return playerclass;
  335. }
  336. /**--------------------------------------------------------------------------**\
  337. <summary>Class_Goto</summary>
  338. <param name="playerid">Player who is changing class.</param>
  339. <param name="playerclass">The internal class they are switching to.</param>
  340. <returns>
  341. -
  342. </returns>
  343. <remarks>
  344. Does the visual faking through "SetPlayerSkin", and also calls
  345. "SetSpawnInfo" to avoid any lag from "SetPlayerPos" under "OnPlayerSpawn".
  346. </remarks>
  347. \**--------------------------------------------------------------------------**/
  348. foreign void:Class_Goto(playerid, playerclass);
  349. global void:Class_Goto(playerid, playerclass)
  350. {
  351. P:2("Class_Goto called: %i, %i", playerid, playerclass);
  352. // This now sets the REAL spawn information, including spawn location.
  353. SetSpawnInfo(playerid, NO_TEAM, Class_Skin(e_PLAYER_CLASS:playerclass), Class_X(e_PLAYER_CLASS:playerclass), Class_Y(e_PLAYER_CLASS:playerclass), Class_Z(e_PLAYER_CLASS:playerclass), Class_A(e_PLAYER_CLASS:playerclass), 0, 0, 0, 0, 0, 0);
  354. SetPlayerSkin(playerid, Class_Skin(e_PLAYER_CLASS:playerclass));
  355. YSI_g_sPlayerClass[playerid] = playerclass; //(YSI_g_sPlayerClass[playerid] & ~e_PLAYER_CLASS_SKIN) | (e_PLAYER_CLASS:playerclass);
  356. }
  357. /**--------------------------------------------------------------------------**\
  358. <summary>Class_OnPlayerRequestSpawn</summary>
  359. <param name="playerid">Player who selected a spawn.</param>
  360. <returns>
  361. -
  362. </returns>
  363. <remarks>
  364. Has inbuilt protection for a bug where selections aren't correctly
  365. debounced so you can press shift twice at once which can mess up some
  366. scripts (e.g. the example team selection script). Calls
  367. OnPlayerRequestSpawnEx with an additional class parameter.
  368. </remarks>
  369. \**--------------------------------------------------------------------------**/
  370. #if !defined YSI_NO_MASTER && (_YSIM_IS_CLIENT || _YSIM_IS_STUB)
  371. public OnPlayerRequestSpawn(playerid)
  372. {
  373. return 1;
  374. }
  375. #else
  376. #if !defined YSI_NO_MASTER && _YSIM_IS_CLOUD
  377. public OnPlayerRequestSpawn(playerid) <>
  378. {
  379. return 1;
  380. }
  381. public OnPlayerRequestSpawn(playerid) <_YCM:y>
  382. #else
  383. public OnPlayerRequestSpawn(playerid)
  384. #endif
  385. {
  386. P:1("Class_OnPlayerRequestSpawn called: %d", playerid);
  387. new
  388. time = GetTickCount();
  389. if ((time - YSI_g_sLastRefuse[playerid]) >= 500)
  390. {
  391. new
  392. ret = CallRemoteFunction("Class_OnPlayerRequestSpawn", "i", playerid);
  393. P:4("Class_OnPlayerRequestSpawn() return: %d", ret);
  394. if (ret)
  395. {
  396. new
  397. Group:newgroup = YSI_g_sClasses[YSI_g_sPlayerClass[playerid]][E_CLASS_GROUP];
  398. P:4("Class_OnPlayerRequestSpawn() newgroup: %d", _:newgroup);
  399. if (newgroup != Group:-1) Class_ResolveGroups(playerid, newgroup, true);
  400. if (ret == -1)
  401. {
  402. // Send the player back to class selection.
  403. OnPlayerRequestClass(playerid, YSI_g_sInternalClass[playerid]);
  404. }
  405. else
  406. {
  407. // YSI_g_sPlayerClass[playerid] &= ~e_PLAYER_CLASS_IN_SELECTION;
  408. return 1;
  409. }
  410. }
  411. }
  412. P:5("Class_OnPlayerRequestSpawn: Return 0");
  413. return
  414. YSI_g_sLastRefuse[playerid] = time,
  415. 0;
  416. }
  417. #endif
  418. HOOK_FORWARD:Class_OnPlayerRequestSpawn(playerid);
  419. #if defined _ALS_OnPlayerRequestSpawn
  420. #undef OnPlayerRequestSpawn
  421. #else
  422. #define _ALS_OnPlayerRequestSpawn
  423. #endif
  424. #define OnPlayerRequestSpawn(%0) Class_OnPlayerRequestSpawn(%0) <_ALS : _ALS_go>
  425. /**--------------------------------------------------------------------------**\
  426. <summary>Class_OnPlayerSpawn</summary>
  427. <param name="playerid">Player who spawned.</param>
  428. <returns>
  429. -
  430. </returns>
  431. <remarks>
  432. Sets a player's position based on skin.
  433. </remarks>
  434. \**--------------------------------------------------------------------------**/
  435. mhook OnPlayerSpawn(playerid)
  436. {
  437. P:2("Class_OnPlayerSpawn called: %d", playerid);
  438. new
  439. playerclass = YSI_g_sPlayerClass[playerid],
  440. weapon;
  441. for (new i = 0; i != MAX_CLASS_SPAWN_WEAPONS; ++i)
  442. {
  443. weapon = YSI_g_sClasses[playerclass][E_CLASS_WEAPONS][i];
  444. if (weapon)
  445. {
  446. GivePlayerWeapon(playerid, weapon & 0xFF, weapon >>> 8);
  447. }
  448. else
  449. {
  450. break;
  451. }
  452. }
  453. weapon = YSI_g_sClasses[playerclass][E_CLASS_WEAPONS][MAX_CLASS_SPAWN_WEAPONS];
  454. P:5("Class_OnPlayerSpawn: Armour %d %d %d", weapon, weapon & 0xFF, WEAPON_ARMOUR);
  455. if ((weapon & 0xFF) == WEAPON_ARMOUR)
  456. {
  457. weapon >>>= 8;
  458. if (weapon == 0x00800000)
  459. {
  460. // INFINITY
  461. SetPlayerArmour(playerid, Float:0x7F800000);
  462. }
  463. else
  464. {
  465. SetPlayerArmour(playerid, weapon);
  466. }
  467. }
  468. return 1;
  469. }
  470. /**--------------------------------------------------------------------------**\
  471. <summary>Class_OnPlayerConnect</summary>
  472. <param name="playerid">Player who joined the server.</param>
  473. <returns>
  474. -
  475. </returns>
  476. <remarks>
  477. Reset all the data on this player's current classes.
  478. </remarks>
  479. \**--------------------------------------------------------------------------**/
  480. mhook OnPlayerConnect(playerid)
  481. {
  482. YSI_g_sPlayerClass[playerid] = -1,
  483. YSI_g_sInternalClass[playerid] = -1;
  484. NO_GROUPS()
  485. {
  486. new
  487. slot = PA_Slot(playerid),
  488. Bit:mask = PA_Mask(playerid);
  489. for (new i = 0; i != MAX_CLASSES; ++i)
  490. {
  491. YSI_g_sClasses[i][E_CLASS_PLAYERS][slot] |= mask;
  492. }
  493. }
  494. return 1;
  495. }
  496. /**--------------------------------------------------------------------------**\
  497. <summary>_CLASS_WEAPON_CODE</summary>
  498. <returns>
  499. -
  500. </returns>
  501. <remarks>
  502. Converts the variable arguments to an array.
  503. </remarks>
  504. \**--------------------------------------------------------------------------**/
  505. // #define _CLASS_WEAPON_CODE if((n-w)&0x01)while(w!=n&&s<MAX_CLASS_SPAWN_WEAPONS){cw=getarg(w++);if(cw==WEAPON_ARMOUR)weapons[MAX_CLASS_SPAWN_WEAPONS]=WEAPON_ARMOUR|(100<<8);else if(w==n)P:E(": Insufficient class weapon data.");else weapons[s++]=(cw&0xFF)|(getarg(w++)<<8);}else while(w!=n&&s<MAX_CLASS_SPAWN_WEAPONS){cw=getarg(w++);if(cw==WEAPON_ARMOUR)weapons[MAX_CLASS_SPAWN_WEAPONS]=WEAPON_ARMOUR|(getarg(w++)<<8);else weapons[s++]=(cw&0xFF)|(getarg(w++)<<8);}
  506. #define _CLASS_WEAPON_CODE \
  507. {if((n-w+1)/2>MAX_CLASS_SPAWN_WEAPONS) P:E("Excessive class weapon data."); \
  508. if((n-w)&0x01){while(w!=n)if((cw=getarg(w++))==WEAPON_ARMOUR)weapons[MAX_CLASS_SPAWN_WEAPONS]=WEAPON_ARMOUR|(100<<8);else if(w==n)P:E(": Insufficient class weapon data.");else weapons[s++]=(cw&0xFF)|(getarg(w++)<<8);} \
  509. else{while(w!=n)weapons[((cw=getarg(w++))==WEAPON_ARMOUR)?MAX_CLASS_SPAWN_WEAPONS:(s++)]=(cw&0xFF)|(getarg(w++)<<8);}}
  510. // cw=getarg(w++);if(cw==WEAPON_ARMOUR)weapons[MAX_CLASS_SPAWN_WEAPONS]=WEAPON_ARMOUR|(getarg(w++)<<8);else weapons[s++]=(cw&0xFF)|(getarg(w++)<<8);
  511. // cw=getarg(w++);
  512. // if((getarg(w++))==WEAPON_ARMOUR)
  513. // weapons[MAX_CLASS_SPAWN_WEAPONS]=WEAPON_ARMOUR|(100<<8);
  514. // else if(w==n)
  515. // P:E(": Insufficient class weapon data.");
  516. // else
  517. // weapons[s++]=(cw&0xFF)|(getarg(w++)<<8);
  518. /**--------------------------------------------------------------------------**\
  519. <summary>Class_Add</summary>
  520. <param name="skin">Skin of the class.</param>
  521. <param name="Float:x">X spawn location.</param>
  522. <param name="Float:y">Y spawn location.</param>
  523. <param name="Float:z">Z spawn location.</param>
  524. <param name="Float:a">A spawn location.</param>
  525. <param name="...">Spawn weapons and ammo (weapon then ammo)</param>
  526. <returns>
  527. -
  528. </returns>
  529. <remarks>
  530. Pretty much AddPlayerClass but allows greater control over the classes.
  531. Now has infinate (MAX_CLASS_SPAWN_WEAPONS) spawn weapons. This is one of
  532. the few API functions which is not entirely remote. This is because it has
  533. variable parameters which is need to collect in to a single array to pass to
  534. the remote function.
  535. </remarks>
  536. \**--------------------------------------------------------------------------**/
  537. stock Class_Add(skin, Float:x, Float:y, Float:z, Float:a, ...)
  538. {
  539. P:3("Class_Add called: %i, %f, %f, %f, %f, (+%i)", skin, x, y, z, a, numargs() - 5);
  540. new
  541. n = numargs(),
  542. w = 5,
  543. s,
  544. weapons[MAX_CLASS_SPAWN_WEAPONS + 1],
  545. cw;
  546. _CLASS_WEAPON_CODE
  547. return Class_AddClass(skin, x, y, z, a, weapons, MAX_CLASS_SPAWN_WEAPONS + 1, Group:-1, Group:-1);
  548. }
  549. /**--------------------------------------------------------------------------**\
  550. <summary>Class_AddEx</summary>
  551. <param name="forgroup">Group that can use the skin.</param>
  552. <param name="setgroup">Group to add the player to on selection.</param>
  553. <param name="skin">Skin of the class.</param>
  554. <param name="Float:x">X spawn location.</param>
  555. <param name="Float:y">Y spawn location.</param>
  556. <param name="Float:z">Z spawn location.</param>
  557. <param name="Float:a">A spawn location.</param>
  558. <param name="...">Spawn weapons and ammo (weapon then ammo)</param>
  559. <returns>
  560. -
  561. </returns>
  562. <remarks>
  563. Pretty much AddPlayerClass but allows greater control over the classes.
  564. Now has infinate (MAX_CLASS_SPAWN_WEAPONS) spawn weapons.
  565. </remarks>
  566. \**--------------------------------------------------------------------------**/
  567. stock Class_AddEx(Group:forgroup, Group:setgroup, skin, Float:x, Float:y, Float:z, Float:a, ...)
  568. {
  569. P:3("Class_AddEx called: %i, %i, %i, %f, %f, %f, %f (+%i)", _:forgroup, _:setgroup, skin, x, y, z, a, numargs() - 7);
  570. new
  571. n = numargs(),
  572. w = 7,
  573. s,
  574. weapons[MAX_CLASS_SPAWN_WEAPONS + 1],
  575. cw;
  576. _CLASS_WEAPON_CODE
  577. return Class_AddClass(skin, x, y, z, a, weapons, MAX_CLASS_SPAWN_WEAPONS + 1, forgroup, setgroup);
  578. }
  579. /**--------------------------------------------------------------------------**\
  580. <summary>Class_AddForGroup</summary>
  581. <param name="group">Group to allow to use the class.</param>
  582. <param name="skin">Skin of the class.</param>
  583. <param name="Float:x">X spawn location.</param>
  584. <param name="Float:y">Y spawn location.</param>
  585. <param name="Float:z">Z spawn location.</param>
  586. <param name="Float:a">A spawn location.</param>
  587. <param name="...">Weapon data.</param>
  588. <returns>
  589. -
  590. </returns>
  591. <remarks>
  592. Adds a class only people in the specified group can use.
  593. </remarks>
  594. \**--------------------------------------------------------------------------**/
  595. stock Class_AddForGroup(Group:group, skin, Float:x, Float:y, Float:z, Float:a, ...)
  596. {
  597. P:3("Class_AddForGroup called: %i, %i, %f, %f, %f, %f (+%i)", _:group, skin, x, y, z, a, numargs() - 6);
  598. new
  599. n = numargs(),
  600. w = 6,
  601. s,
  602. weapons[MAX_CLASS_SPAWN_WEAPONS + 1],
  603. cw;
  604. _CLASS_WEAPON_CODE
  605. return Class_AddClass(skin, x, y, z, a, weapons, MAX_CLASS_SPAWN_WEAPONS + 1, group, Group:-1);
  606. }
  607. /**--------------------------------------------------------------------------**\
  608. <summary>Class_AddWithGroupSet</summary>
  609. <param name="group">Group to make players who use this group.</param>
  610. <param name="skin">Skin of the class.</param>
  611. <param name="Float:x">X spawn location.</param>
  612. <param name="Float:y">Y spawn location.</param>
  613. <param name="Float:z">Z spawn location.</param>
  614. <param name="Float:a">A spawn location.</param>
  615. <param name="...">Spawn weapons.</param>
  616. <returns>
  617. -
  618. </returns>
  619. <remarks>
  620. Adds a class which puts you in the specified group when selected.
  621. </remarks>
  622. \**--------------------------------------------------------------------------**/
  623. stock Class_AddWithGroupSet(Group:group, skin, Float:x, Float:y, Float:z, Float:a, ...)
  624. {
  625. P:3("Class_AddWithGroupSet called: %i, %i, %f, %f, %f, %f (+%i)", _:group, skin, x, y, z, a, numargs() - 6);
  626. new
  627. n = numargs(),
  628. w = 6,
  629. s,
  630. weapons[MAX_CLASS_SPAWN_WEAPONS + 1],
  631. cw;
  632. _CLASS_WEAPON_CODE
  633. return Class_AddClass(skin, x, y, z, a, weapons, MAX_CLASS_SPAWN_WEAPONS + 1, Group:-1, group);
  634. }
  635. /**--------------------------------------------------------------------------**\
  636. <summary>Class_AddClass</summary>
  637. <param name="skin">Skin of the class.</param>
  638. <param name="Float:x">X spawn location.</param>
  639. <param name="Float:y">Y spawn location.</param>
  640. <param name="Float:z">Z spawn location.</param>
  641. <param name="Float:a">A spawn location.</param>
  642. <param name="weapons[]">Array of spawn weapon data.</param>
  643. <param name="count">Number of weapons added.</param>
  644. <param name="forgroup">Group that can use the class.</param>
  645. <param name="asgroup">Group to assign people to with this class.</param>
  646. <returns>
  647. -
  648. </returns>
  649. <remarks>
  650. Does the hard work. This took a long time to get working correctly with the
  651. new master system, infact getting just this one function to compile took a
  652. major re-working of the macros to reduce the length of intermediate stages.
  653. </remarks>
  654. \**--------------------------------------------------------------------------**/
  655. foreign Class_AddClass(s,Float:x,Float:y,Float:z,Float:a,w[],c,Group:f,Group:g);
  656. global Class_AddClass(s,Float:x,Float:y,Float:z,Float:a,w[],c,Group:f,Group:g)
  657. {
  658. #pragma unused c
  659. P:2("Class_AddClass called: %i, %f, %f, %f, %f, %s, %i, %i, %i", s, x, y, z, a, Debug_PrintArray(w, c), c, _:f, _:g);
  660. new
  661. i;
  662. while (i != MAX_CLASSES && _Class_IsActive(i)) ++i;
  663. if (i == MAX_CLASSES) return -1;
  664. YSI_g_sClasses[i][E_CLASS_FLAGS] = e_CLASS_FLAGS_ACTIVE | e_CLASS_FLAGS_ENABLED | e_CLASS_FLAGS:s,
  665. YSI_g_sClasses[i][E_CLASS_X] = x,
  666. YSI_g_sClasses[i][E_CLASS_Y] = y,
  667. YSI_g_sClasses[i][E_CLASS_Z] = z,
  668. YSI_g_sClasses[i][E_CLASS_A] = a,
  669. memcpy(YSI_g_sClasses[i][E_CLASS_WEAPONS], w, 0, (MAX_CLASS_SPAWN_WEAPONS + 1) * 4, MAX_CLASS_SPAWN_WEAPONS + 1);
  670. // P:7("Class_AddClass: weapon %d %d %d", j, c, i);
  671. PA_FastInit(YSI_g_sClasses[i][E_CLASS_PLAYERS]);
  672. // P:7("Class_AddClass: weapon %d %d %d", j, c, f);
  673. Class_ResolveGroups(i, f, false);
  674. // P:7("Class_AddClass: weapon %d %d %d", j, c, g);
  675. YSI_g_sClasses[i][E_CLASS_GROUP] = g;
  676. YSI_g_sClassCount++;
  677. // P:7("Class_AddClass: weapon %d %d %d", j, c, YSI_g_sClassCount);
  678. return i;
  679. }
  680. /**--------------------------------------------------------------------------**\
  681. <summary>Class_HasGroup</summary>
  682. <param name="classid">The class to test.</param>
  683. <param name="Group:group">The group to test setting of.</param>
  684. <returns>
  685. Does this class add players to the given group on spawn?
  686. </returns>
  687. <remarks>
  688. -
  689. </remarks>
  690. \**--------------------------------------------------------------------------**/
  691. foreign Class_HasGroup(classid, Group:group);
  692. global Class_HasGroup(classid, Group:group)
  693. {
  694. return YSI_g_sClasses[classid][E_CLASS_GROUP] == group;
  695. }
  696. /**--------------------------------------------------------------------------**\
  697. <summary>Class_Enable</summary>
  698. <returns>
  699. -
  700. </returns>
  701. <remarks>
  702. -
  703. </remarks>
  704. \**--------------------------------------------------------------------------**/
  705. foreign Class_Enable(classid, bool:toggle);
  706. global Class_Enable(classid, bool:toggle)
  707. {
  708. if (_Class_IsValid(classid))
  709. {
  710. if (toggle)
  711. {
  712. YSI_g_sClasses[classid][E_CLASS_FLAGS] |= e_CLASS_FLAGS_ENABLED;
  713. }
  714. else
  715. {
  716. YSI_g_sClasses[classid][E_CLASS_FLAGS] &= ~e_CLASS_FLAGS_ENABLED;
  717. // Hide the class for anyone currently viewing it.
  718. // foreach (new playerid : Player)
  719. // {
  720. // P:5("Class_Enable: %d %d %d", playerid, _:YSI_g_sPlayerClass[playerid], _:e_PLAYER_CLASS_IN_SELECTION);
  721. // if (YSI_g_sPlayerClass[playerid] & e_PLAYER_CLASS_IN_SELECTION)
  722. // {
  723. // OnPlayerRequestClass(playerid, YSI_g_sInternalClass[playerid]);
  724. // }
  725. // }
  726. }
  727. return 1;
  728. }
  729. return 0;
  730. }
  731. /**--------------------------------------------------------------------------**\
  732. <summary>Class_Delete</summary>
  733. <param name="classid">Class to delete.</param>
  734. <returns>
  735. -
  736. </returns>
  737. <remarks>
  738. Completely removes a class from the system.
  739. </remarks>
  740. \**--------------------------------------------------------------------------**/
  741. foreign void:Class_Delete(classid);
  742. global void:Class_Delete(classid)
  743. {
  744. P:2("Class_Delete called: %i", classid);
  745. if (Class_Enable(classid, false))
  746. {
  747. YSI_g_sClasses[classid][E_CLASS_FLAGS] = e_CLASS_FLAGS:0;
  748. }
  749. }
  750. /**--------------------------------------------------------------------------**\
  751. <summary>Class_SetPlayer</summary>
  752. <param name="classid">Class to set permissions for.</param>
  753. <param name="playerid">Player to set for.</param>
  754. <param name="set">Whether or not they can use this class.</param>
  755. <returns>
  756. -
  757. </returns>
  758. <remarks>
  759. -
  760. </remarks>
  761. \**--------------------------------------------------------------------------**/
  762. foreign void:Class_SetPlayer(cl, pid, bool:s);
  763. global void:Class_SetPlayer(cl, pid, bool:s)
  764. {
  765. PA_Set(YSI_g_sClasses[cl][E_CLASS_PLAYERS], pid, s);
  766. }
  767. /**--------------------------------------------------------------------------**\
  768. <summary>Class_GetPlayer</summary>
  769. <param name="classid">Class to set permissions for.</param>
  770. <param name="playerid">Player to set for.</param>
  771. <returns>
  772. -
  773. </returns>
  774. <remarks>
  775. -
  776. </remarks>
  777. \**--------------------------------------------------------------------------**/
  778. foreign bool:Class_GetPlayer(cl,pid);
  779. global bool:Class_GetPlayer(cl,pid)
  780. {
  781. return PA_Get(YSI_g_sClasses[cl][E_CLASS_PLAYERS], pid);
  782. }
  783. /**--------------------------------------------------------------------------**\
  784. <summary>Class_Get</summary>
  785. <param name="playerid">Player to get the current class of.</param>
  786. <returns>
  787. -
  788. </returns>
  789. <remarks>
  790. -
  791. </remarks>
  792. \**--------------------------------------------------------------------------**/
  793. foreign Class_Get(playerid);
  794. global Class_Get(playerid)
  795. {
  796. if (VALID_PLAYERID(playerid))
  797. {
  798. return YSI_g_sPlayerClass[playerid];
  799. }
  800. return -1;
  801. }
  802. #if defined _ALS_AddPlayerClass
  803. #undef AddPlayerClass
  804. #else
  805. #define _ALS_AddPlayerClass
  806. #endif
  807. #define AddPlayerClass Class_Add
  808. /**--------------------------------------------------------------------------**\
  809. <summary>Class_OnPlayerRequestClass</summary>
  810. <param name="playerid">Player who requested a class.</param>
  811. <param name="class">Class they requested.</param>
  812. <returns>
  813. -
  814. </returns>
  815. <remarks>
  816. The input is one of the three real classes used to detect selected
  817. direction of alteration. Scans for a class the player is allowed to use
  818. and hot swaps it out. Uses SetPlayerSkin AND SetSpawnInfo to combat bugs
  819. with calling this from OnPlayerRequestSpawn (e.g. the example team script).
  820. Calls OnPlayerRequestClassEx with the current internal class not the real
  821. one.
  822. Last thing in the file to be simpler to call directly. If we ever have a
  823. more complex ALS hook on this callback the main body of this code will have
  824. to be moved in to its own function.
  825. </remarks>
  826. \**--------------------------------------------------------------------------**/
  827. // Pre-empt y_hooks.
  828. #if !defined YSI_NO_MASTER && _YSIM_IS_CLIENT
  829. public OnPlayerRequestClass(playerid, classid)
  830. {
  831. return 1;
  832. }
  833. #else
  834. #if !defined YSI_NO_MASTER && _YSIM_IS_CLOUD
  835. public OnPlayerRequestClass(playerid, classid) <>
  836. {
  837. return 1;
  838. }
  839. public OnPlayerRequestClass(playerid, classid) <_YCM:y>
  840. #else
  841. public OnPlayerRequestClass(playerid, classid)
  842. #endif
  843. {
  844. P:1("Class_OnPlayerRequestClass called: %d %d", playerid, classid);
  845. //YSI_g_sPlayerClass[playerid] |= e_PLAYER_CLASS_IN_SELECTION;
  846. // Find which direction they pressed.
  847. if (YSI_g_sClassCount)
  848. {
  849. // Find the next available skin for this player. I'm still not
  850. // sure how this handles the case where you can't use any skin.
  851. // I'll have to look in to that - don't want to get stuck
  852. // constantly adding "0".
  853. new
  854. cid = classid;
  855. classid = Class_FindNew(playerid, YSI_g_sPlayerClass[playerid], ((classid - YSI_g_sInternalClass[playerid]) + 1) % 3 - 1),
  856. YSI_g_sInternalClass[playerid] = cid,
  857. Class_Goto(playerid, classid);
  858. P:5("Class_OnPlayerRequestClass() selected: %d", classid);
  859. }
  860. else
  861. {
  862. P:E("No YSI classes found");
  863. SetSpawnInfo(playerid, NO_TEAM, 0, 1958.0, 1343.0, 15.0, 269.0, 0, 0, 0, 0, 0, 0),
  864. SetPlayerSkin(playerid, 0),
  865. classid = -1;
  866. }
  867. return CallRemoteFunction("Class_OnPlayerRequestClass", "ii", playerid, classid);
  868. }
  869. #endif
  870. // Use the special pre-y_hooks hook method.
  871. HOOK_FORWARD:Class_OnPlayerRequestClass(playerid, classid);
  872. #if defined _ALS_OnPlayerRequestClass
  873. #undef OnPlayerRequestClass
  874. #else
  875. #define _ALS_OnPlayerRequestClass
  876. #endif
  877. #define OnPlayerRequestClass(%0) Class_OnPlayerRequestClass(%0) <_ALS : _ALS_go>
  878. // foreign bool:Class_InSelection(playerid);
  879. // global bool:Class_InSelection(playerid)
  880. // {
  881. // return bool:(YSI_g_sPlayerClass[playerid] & e_PLAYER_CLASS_IN_SELECTION);
  882. // }