| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509 |
- /*
- Legal:
- Version: MPL 1.1
-
- The contents of this file are subject to the Mozilla Public License Version
- 1.1 the "License"; you may not use this file except in compliance with
- the License. You may obtain a copy of the License at
- http://www.mozilla.org/MPL/
-
- Software distributed under the License is distributed on an "AS IS" basis,
- WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- for the specific language governing rights and limitations under the
- License.
-
- The Original Code is the YSI framework.
-
- The Initial Developer of the Original Code is Alex "Y_Less" Cole.
- Portions created by the Initial Developer are Copyright C 2011
- the Initial Developer. All Rights Reserved.
- Contributors:
- Y_Less
- koolk
- JoeBullet/Google63
- g_aSlice/Slice
- Misiur
- samphunter
- tianmeta
- maddinat0r
- spacemud
- Crayder
- Dayvison
- Ahmad45123
- Zeex
- irinel1996
- Yiin-
- Chaprnks
- Konstantinos
- Masterchen09
- Southclaws
- PatchwerkQWER
- m0k1
- paulommu
- udan111
- Thanks:
- JoeBullet/Google63 - Handy arbitrary ASM jump code using SCTRL.
- ZeeX - Very productive conversations.
- koolk - IsPlayerinAreaEx code.
- TheAlpha - Danish translation.
- breadfish - German translation.
- Fireburn - Dutch translation.
- yom - French translation.
- 50p - Polish translation.
- Zamaroht - Spanish translation.
- Los - Portuguese translation.
- Dracoblue, sintax, mabako, Xtreme, other coders - Producing other modes for
- me to strive to better.
- Pixels^ - Running XScripters where the idea was born.
- Matite - Pestering me to release it and using it.
- Very special thanks to:
- Thiadmer - PAWN, whose limits continue to amaze me!
- Kye/Kalcor - SA:MP.
- SA:MP Team past, present and future - SA:MP.
- Optional plugins:
- Gamer_Z - GPS.
- Incognito - Streamer.
- Me - sscanf2, fixes2, Whirlpool.
- */
- #define _GROUP_STOP_INCLUDES
- /*-------------------------------------------------------------------------*//**
- * <param name="%9">Check if the function exists?</param>
- * <param name="%0">Variable holding the function.</param>
- * <param name="%1">Parameters.</param>
- * <remarks>
- * Calls the correct function to chain a function with the given number of
- * parameters, calls the function stored in a variable and passes parameters
- * BY REFERENCE - ALL of them!
- * </remarks>
- *//*------------------------------------------------------------------------**/
- /*
- New groups check:
- if (ALL A == ALL B)
- {
- if (ALL A == P)
- {
- // Add.
- }
- else
- {
- // Remove.
- }
- }
- else
- {
- if (ANY A == P && NO B == P)
- {
- // Add.
- }
- else
- {
- // Remove.
- }
- }
- */
- enum e_GROUP_FLAGS (<<= 1)
- {
- e_GROUP_FLAGS_NONE = 0,
- e_GROUP_FLAGS_GANG = 1,
- // I can't remember why I had this!
- //e_GROUP_FLAGS_CHAT,
- e_GROUP_FLAGS_ACTIVE,
- // Only ever set for one group.
- e_GROUP_FLAGS_GLOBAL,
- // Has no name.
- e_GROUP_FLAGS_TEMP,
- e_GROUP_FLAGS_COLOR = 0xFFFFFF00
- }
- enum E_GROUP_DATA
- {
- E_GROUP_DATA_NAME[MAX_GROUP_NAME char],
- //E_GROUP_DATA_COUNT,
- E_GROUP_DATA_HASH,
- e_GROUP_FLAGS:E_GROUP_DATA_FLAGS,
- }
- stock PlayerGroups(a, b)
- {
- return 0;
- }
- // Define the groups iterator in terms of the underlying bits, but without the
- // need for users to know about the "YSI_gGroupPlayers" array, just the player.
- #define Iter_Func@PlayerGroups(%1,%0) GROUP_MANGLE(Iter_Func@Bits(_:%1,YSI_gGroupPlayers[%0]))
- #define Iterator@PlayerGroups Iterator@Bits
- static
- // Function pointers for chaining.
- YSI_g_sNextInitFunc,
- YSI_g_sNextUpdFunc,
- YSI_g_sNextAddFunc,
- YSI_g_sGroupCount;
- static stock const
- YSI_g_scGlobalName[] = "__GLOBAL",
- BitArray:RG@<_MAX_GROUPS_G>;
- MASTER_DATA<_MAX_GROUPS_G>
- static stock __declspec(dist_master) YSI_gGroupData[_MAX_GROUPS_G][E_GROUP_DATA];
- static stock __declspec(dist_tagged) Bit:YSI_g_sChildGroups[_MAX_GROUPS_G][bits<_MAX_GROUPS_G>];
- static stock
- BitArray:YSI_g_sDefaultGroups<_MAX_GROUPS_G>,
- // Create group hierarchys.
- Iterator:GroupPlayers[_MAX_GROUPS_G]<MAX_PLAYERS>;
- //#define GROUP_BITS<%0> BitArray:%0<_MAX_GROUPS_G>,
- #define _Group_HasPlayer(%0,%1) \
- Bit_Get(YSI_gGroupPlayers[(%1)],(%0)) //, _MAX_GROUPS_G)
- //PA_Get(YSI_gGroupPlayers[(%0)],(%1))
- //Bit_Get(YSI_gGroupPlayers[(%0)], (%1), MAX_PLAYERS)
- #define _Group_GetColor(%0) \
- (_:(YSI_gGroupData[_:(%0)][E_GROUP_DATA_FLAGS] & e_GROUP_FLAGS_COLOR) | 0xAA)
- #define _Group_SetColor(%0,%1) \
- (YSI_gGroupData[_:(%0)][E_GROUP_DATA_FLAGS] = (YSI_gGroupData[_:(%0)][E_GROUP_DATA_FLAGS] & ~e_GROUP_FLAGS_COLOR) | (e_GROUP_FLAGS:(%1) & e_GROUP_FLAGS_COLOR))
- #define _Group_GetGang(%0) \
- (bool:(YSI_gGroupData[_:(%0)][E_GROUP_DATA_FLAGS] & e_GROUP_FLAGS_GANG))
- #define _Group_LetGang(%0) \
- (YSI_gGroupData[_:(%0)][E_GROUP_DATA_FLAGS] |= e_GROUP_FLAGS_GANG)
- #define _Group_VetGang(%0) \
- (YSI_gGroupData[_:(%0)][E_GROUP_DATA_FLAGS] &= ~e_GROUP_FLAGS_GANG)
- #define _Group_IsActive(%0) \
- (YSI_gGroupData[_:(%0)][E_GROUP_DATA_FLAGS] & e_GROUP_FLAGS_ACTIVE)
- //#define _Group_IsValid(%0)
- // (0 <= (%0) < _MAX_GROUPS && Group_IsActive(%0))
- #define _Group_IsValid(%0) \
- ((_:GROUP_MASK<=_:(%0)<=_:GROUP_GLOBAL)&&(_Group_IsActive(GROUP_TEMP_FIX(%0))))
- //#define Group_IsActive(%0)
- // (YSI_gGroupData[_:GROUP_FIX(%0)][E_GROUP_DATA_FLAGS] & e_GROUP_FLAGS_ACTIVE)
- foreign void:_Group_ReInitPlayers();
- global void:_Group_ReInitPlayers()
- {
- Iter_Init(GroupPlayers);
- for (new i = 0; i != _MAX_GROUPS_G; ++i) if (_Group_IsActive(i))
- {
- foreach (new playerid : Player)
- {
- if (_Group_HasPlayer(i, playerid)) Iter_Add(GroupPlayers[i], playerid);
- }
- }
- }
- HANDOFF()
- {
- P:1("y_groups HANDOFF called");
- DISTRIBUTE(YSI_gGroupData);
- DISTRIBUTE(YSI_g_sChildGroups);
- _Group_ReInitPlayers();
- P:1("y_groups HANDOFF ended");
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="g">Group to add the following items to.</param>
- * <remarks>
- * This sets up a temporary environment, during which all items are ONLY added
- * to the specified group and no others, with no complex extra code required.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- #define GROUP_ADD<%0> for(J@=_Group_AddInternal(1,(%0));J@;J@=_Group_AddInternal(0,(%0)))
- static remotefunc void:__Group_AddInternal(a,g)
- {
- GROUP_CHAIN?<YSI_g_sNextInitFunc>(-1, a, g);
- }
- // Can't use "stock remotefunc".
- #pragma unused __Group_AddInternal
- stock _Group_AddInternal(a, Group:g)
- {
- //printf("_Group_AddInternal %d %d", a, _:g);
- if (_Group_IsValid(g))
- {
- broadcastfunc __Group_AddInternal(a, _:GROUP_TEMP_FIX(g));
- return a;
- }
- else
- {
- return 0;
- }
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="p">Player who connected.</param>
- * <param name="g">The player's new groups.</param>
- * <param name="s">Size of "g".</param>
- * <remarks>
- * Pretend the player is in all the groups they won't be in shortly so that
- * they have a complete blank slate when they connect - the system KNOWS they
- * are not in some groups and are in others.
- *
- * Uses an unusual size in "g" to reduce the string length.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- static remotefunc void:_Group_FakePlayer(p,Bit:g[sizeof RG@],s)
- {
- for (new i = 0; i != sizeof (g); ++i)
- {
- // Set their current groups to the inverse of what they are now in.
- YSI_gGroupPlayers[p][i] = ~g[i];
- }
- _Group_SetPlayer(p, g, s);
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="g">Group to check.</param>
- * <returns>
- * bool: - Is the group active and valid?
- * </returns>
- *//*------------------------------------------------------------------------**/
- foreign bool:Group_IsValid(Group:g);
- global bool:Group_IsValid(Group:g)
- {
- //GROUP_FIX(g);
- return _Group_IsValid(g);
- }
- static stock Group_DefineStates_() <YSI_has_groups : y>
- {
- }
- static stock Group_DefineStates_() <YSI_has_groups : n>
- {
- }
- static stock Group_DefineStates_() <YSI_has_groups : other>
- {
- }
- /*-------------------------------------------------------------------------*//**
- * <remarks>
- * Finds three functions by prefix:
- *
- * _yGI - An init function to set up a script using groups.
- * _yGA - An add function called when a new group is created.
- * _yGU - An update function called when a player's groups change.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- hook OnScriptInit()
- {
- P:1("Group_OnScriptInit called");
- state YSI_has_groups : y;
- // Set up the global groups.
- #if !(YSIM_HAS_MASTER && (_YSIM_IS_STUB || _YSIM_IS_CLIENT))
- #if _YSIM_IS_CLOUD
- if (_YCM@y)
- #endif
- {
- P:5("Group_OnScriptInit: Master");
- Iter_Init(GroupPlayers);
- strpack(YSI_gGroupData[_MAX_GROUPS][E_GROUP_DATA_NAME], YSI_g_scGlobalName, MAX_GROUP_NAME char);
- YSI_gGroupData[_MAX_GROUPS][E_GROUP_DATA_FLAGS] = e_GROUP_FLAGS_ACTIVE | e_GROUP_FLAGS_GLOBAL;
- YSI_gGroupData[_MAX_GROUPS][E_GROUP_DATA_HASH] = YHash(YSI_g_scGlobalName);
- // Store the default groups away from the actual global group.
- // The default groups include the global group, but it itself
- // doesn't.
- Bit_Let(YSI_g_sDefaultGroups, _:_MAX_GROUPS);
- }
- #endif
- // Call the other group init functions.
- new
- ni = AMX_GetPublicPointerPrefix(0, YSI_g_sNextInitFunc, _A<_yGI>),
- na = AMX_GetPublicPointerPrefix(0, YSI_g_sNextAddFunc, _A<_yGA>),
- nu = AMX_GetPublicPointerPrefix(0, YSI_g_sNextUpdFunc, _A<_yGU>);
- //printf("Group_OnScriptInit: %d, %d, %d", ni, na, nu);
- P:4("Group_OnScriptInit: %d, %d, %d", ni, na, nu);
- P:4("Group_OnScriptInit: %d, %d, %d", AMX_HEADER_COD - AMX_BASE_ADDRESS, YSI_g_sNextInitFunc, AMX_HEADER_DAT - AMX_BASE_ADDRESS);
- GROUP_CHAIN?<YSI_g_sNextInitFunc>(ni, na, nu);
- //CallLocalFunction("_Group_SpecialInit", "");
- heapspace(); // I'm sure there's a reason this is here. But I forgot it!
- #if defined _GROUP_INCLUDE_ALL_PREVIOUS
- if (FALSE)
- {
- // Include, but never call, this function.
- _GROUP_UNIQUE_FUNCTION();
- }
- #endif
- P:1("Group_OnScriptInit end");
- return 1;
- }
- /*-------------------------------------------------------------------------*//**
- * <remarks>
- * Destroy all this script's groups. This is an instance of "_YSI_SpecialExit"
- * which can't be y_hooked and is called after (almost) every other callback,
- * at least after every one controlled via y_scriptinit.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- public OnScriptExit()
- {
- #if defined _Group_SpecialExit
- _Group_SpecialExit();
- #endif
- _Group_TryRemove();
- return 1;
- }
- #undef OnScriptExit
- #define OnScriptExit _Group_SpecialExit
- #if defined _Group_SpecialExit
- forward _Group_SpecialExit();
- #endif
- /*-------------------------------------------------------------------------*//**
- * <param name="group">Group to destroy from the system.</param>
- *//*------------------------------------------------------------------------**/
- static remotefunc void:_Group_Destroy(g, Iterator@gp[MAX_PLAYERS + 1], s)
- {
- #pragma unused s
- new
- ps = Bit_Slot(g),
- Bit:pm = ~Bit_Mask(g);
- if (YSI_g_sNextUpdFunc)
- {
- // Update all players who were in this group as they may have lost
- // some permissions. This is the simplest update because we don't
- // need to worry about the effect of removing this group on the
- // players' memberships in other groups. Maybe they were added to
- // that other group as a direct result of being added to this group,
- // but maybe they weren't - there's no way to know!
- // Check it! Local parameter iterator!
- foreach (new p : gp)
- {
- YSI_gTempGroups = YSI_gGroupPlayers[p];
- // If these parameters were the other way round I could do this:
- //GROUP_CHAIN<YSI_g_sNextUpdFunc>(p, (YSI_gGroupPlayers[p], YSI_gGroupPlayers[p][ps] &= pm), YSI_gGroupPlayers[p]);
- // But they're currently not so I can't.
- GROUP_CHAIN<YSI_g_sNextUpdFunc>(p, YSI_gTempGroups, YSI_gGroupPlayers[p]);
- YSI_gGroupPlayers[p][ps] &= pm;
- }
- }
- else
- {
- foreach (new p : gp)
- {
- // Remove all players from this group.
- YSI_gGroupPlayers[p][ps] &= pm;
- }
- }
- }
- // Added for the fun of it.
- foreign void:Group_Destroy(Group:group);
- global void:Group_Destroy(Group:group)
- {
- P:2("Group_Destroy called: %i", _:group);
- P:2("Group_Destroy called in %d", _@);
- // You can't destroy the global group.
- if (_Group_IsValid(group) && group != GROUP_GLOBAL)
- {
- GROUP_FIX(group);
- broadcastfunc _Group_Destroy(_:group, Iter_TrueArray(GroupPlayers[_:group]), sizeof (Iter_TrueArray(GroupPlayers[])));
- YSI_gGroupData[_:group][E_GROUP_DATA_FLAGS] = e_GROUP_FLAGS:0;
- YSI_gGroupData[_:group][E_GROUP_DATA_HASH] = 0;
- Iter_Clear(GroupPlayers[_:group]);
- MASTER_RESET<_:group>
- // This group no longer exists, so can't be the child of another group.
- new
- ps = Bit_Slot(_:group),
- Bit:pm = ~Bit_Mask(_:group);
- for (new i = 0; i != _MAX_GROUPS; ++i)
- {
- YSI_g_sChildGroups[i][ps] &= pm;
- }
- // Nor can it have groups (saves some later checks).
- YSI_g_sChildGroups[_: group] = YSI_g_cEmptyGroups;
- }
- }
- /*-------------------------------------------------------------------------*//**
- * <remarks>
- * Removes all groups purely owned by the calling script.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- @foreign void:_Group_TryRemove();
- @global void:_Group_TryRemove()
- {
- for (new i = 0; i != _MAX_GROUPS; ++i) if (_Group_IsActive(i))
- {
- MASTER_REMOVE<i>
- MASTER_EMPTY<i>
- {
- //printf("removing i");
- broadcastfunc _Group_Destroy(i, Iter_TrueArray(GroupPlayers[i]), sizeof (Iter_TrueArray(GroupPlayers[])));
- YSI_gGroupData[i][E_GROUP_DATA_FLAGS] = e_GROUP_FLAGS:0;
- YSI_gGroupData[i][E_GROUP_DATA_HASH] = 0;
- Iter_Clear(GroupPlayers[i]);
- }
- }
- //return 1;
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="playerid">The player that left.</param>
- * <param name="reason">Why they left (unused).</param>
- * <remarks>
- * Removes this player from all groups. Unfortunately there's no way to
- * combine multiple iterator removals to improve their efficiency - currently
- * they have to loop through all previous ones to find the one to modify to
- * skip over that player. I did debate updating foreach to doubly-linked
- * lists for this reason - that would make reverse traversal and removal faster
- * by doubling memory consumption but only very slightly affecting adds.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- hook OnPlayerDisconnect(playerid, reason)
- {
- #pragma unused reason
- P:2("hook OnPlayerDisconnect called: %d, %d", playerid, reason);
- // Can't - can't what? Don't stop typing mid comment!
- //foreach (new Group:g : PlayerGroups(playerid))
- #if !(YSIM_HAS_MASTER && (_YSIM_IS_STUB || _YSIM_IS_CLIENT))
- #if _YSIM_IS_CLOUD
- if (_YCM@y)
- #endif
- {
- foreach (new g : Bits(YSI_gGroupPlayers[playerid]))
- {
- Iter_Remove(GroupPlayers[g], playerid);
- Bit_Vet(YSI_gGroupPlayers[playerid], g);
- }
- }
- #endif
- // Remove them from everything ever.
- //static const
- // BitArray:scEmpty<_MAX_GROUPS_G>;
- //GROUP_CHAIN?<YSI_g_sNextUpdFunc>(playerid, YSI_gGroupPlayers[p], scEmpty);
- YSI_gGroupPlayers[playerid] = YSI_g_cEmptyGroups;
- return 1;
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="playerid">The player that joined.</param>
- * <remarks>
- * The player may not have ACTUALLY joined the server, they may have just been
- * added to this newly loaded script. In that case we need to initialise the
- * locally stored group data to the new data. Of course, if this script is the
- * group master, then we need to do significantly more! This is more complex
- * than other scripts with master as they don't have some things to do in non-
- * master scripts as well, whereas this one does.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- hook OnPlayerConnect@9(playerid)
- {
- P:1("Groups_OnPlayerConnect called: %d, %d", playerid, !(YSIM_HAS_MASTER && (_YSIM_IS_STUB || _YSIM_IS_CLIENT)));
- #if !(YSIM_HAS_MASTER && (_YSIM_IS_STUB || _YSIM_IS_CLIENT))
- #if _YSIM_IS_CLOUD
- if (_YCM@y)
- #endif
- {
- //printf("0");
- P:6("Group_OnPlayerConnect: Master %d", playerid);
- // Is master. ALWAYS reset all players - if they start screwing
- // around with loading orders then that's their own fault!
- broadcastfunc _Group_FakePlayer(playerid, YSI_g_sDefaultGroups, bits<_MAX_GROUPS_G>);
- //printf("1");
- }
- #if _YSIM_IS_CLOUD
- else
- #endif
- #endif
- #if YSIM_HAS_MASTER
- {
- P:6("Group_OnPlayerConnect: Slave %d", playerid);
- static const
- BitArray:scF<_MAX_GROUPS_G> = {Bit:-1, ...};
- _GROUPS_CHECK_ANY(scF, YSI_gGroupPlayers[playerid])
- {
- // Already been set up - return.
- return 1;
- }
- //_Group_
- _Group_InitPlayer(playerid, _@);
- }
- #endif
- return 1;
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="p">The player that joined.</param>
- * <param name="master">The script that just started.</param>
- * <remarks>
- * Request all a player's groups from the master system.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- foreign void:_Group_InitPlayer(p, master);
- global void:_Group_InitPlayer(p, master)
- {
- targetfunc _Group_FakePlayer<master>(p, YSI_gGroupPlayers[p], bits<_MAX_GROUPS_G>);
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="playerid">Player to add to multiple groups.</param>
- * <param name="group">Parent group to add to.</param>
- * <returns>
- * A new array of groups.
- * </returns>
- * <remarks>
- * Takes a group and adds a player to that group and every child group of
- * which they are not already a member.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- // If you overflow this stack there is something VERY wrong! The manual stack
- // is intended to help us combat very long inheritance chains. This code also
- // neatly ignores recursion by only doing any one group if the player doesn't
- // already have it.
- static
- YSI_g_sRecursionStack[_MAX_GROUPS_G][2];
- static stock Bit:_Group_SetSome(playerid, group)
- {
- new
- g = -1,
- s = 0;
- YSI_gTempGroups = YSI_gGroupPlayers[playerid];
- Bit_Let(YSI_gTempGroups, group);
- for ( ; ; )
- {
- g = Iter_Func@Bits(g, YSI_g_sChildGroups[group], bits<_MAX_GROUPS_G>);
- if (g == -1)
- {
- // Finished this child group, pop off the stack.
- if (s--)
- {
- g = YSI_g_sRecursionStack[s][0];
- group = YSI_g_sRecursionStack[s][1];
- }
- else break;
- }
- else if (!Bit_Get(YSI_gTempGroups, g))
- {
- // This player isn't in this group, add them.
- Bit_Let(YSI_gTempGroups, g);
- YSI_g_sRecursionStack[s][0] = g;
- YSI_g_sRecursionStack[s][1] = group;
- ++s;
- group = g;
- g = -1;
- }
- }
- return YSI_gTempGroups;
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="g">Group to modify.</param>
- * <param name="p">Player to modify the group for.</param>
- * <param name="s">Add the player (true) or remove them (false).</param>
- * <remarks>
- * Will in some cases update all settings, unless they are being added through
- * a recursive call due to being added to child groups.
- *
- * There is an internal version that ONLY adds them to the group and DOES NOT
- * update any of their other settings. As a result it has less checks in it.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- foreign void:Group_SetPlayer(Group:g,p,bool:s);
- global void:Group_SetPlayer(Group:g,p,bool:s)
- {
- if (_Group_IsValid(g) && VALID_PLAYERID(p))
- {
- GROUP_FIX(g);
- if (s)
- {
- if (!_Group_HasPlayer(g, p))
- {
- // Added to a group they don't already have. Make a new list of
- // groups they're in, taking in to account the new groups
- // hierarchy and pass that new array about.
- broadcastfunc _Group_SetPlayer(p, _Group_SetSome(p, _:g), sizeof (YSI_gTempGroups));
- return; //1;
- }
- }
- else
- {
- if (_Group_HasPlayer(g, p))
- {
- YSI_gTempGroups = YSI_gGroupPlayers[p];
- Bit_Vet(YSI_gTempGroups, _:g);
- broadcastfunc _Group_SetPlayer(p, YSI_gTempGroups, sizeof (YSI_gTempGroups));
- return; //1;
- }
- }
- }
- //return 0;
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="p">Player to set.</param>
- * <param name="g">The player's new groups.</param>
- * <param name="s">Size of "g".</param>
- * <remarks>
- * Pass a list of groups that the player is now in or not in. This is a
- * complete replacement for their existing list of groups, and so multiple
- * updates can be done at once.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- remotefunc void:_Group_SetPlayer(p, Bit:g[sizeof RG@], s)
- {
- #pragma unused s
- #if !(YSIM_HAS_MASTER && (_YSIM_IS_STUB || _YSIM_IS_CLIENT))
- #if _YSIM_IS_CLOUD
- if (_YCM@y)
- #endif
- {
- P:2("_Group_SetPlayer called: %i, %s, %s, %i", p, Bit_Display(YSI_gGroupPlayers[p]), Bit_Display(g), s);
- // <a href="http://supertech.csail.mit.edu/papers/debruijn.pdf" />
- new
- Bit:cur,
- Bit:exor,
- Bit:bit,
- group;
- for (new i = 0; i != sizeof (g); ++i)
- {
- // Get the difference between the old and new groups.
- cur = YSI_gGroupPlayers[p][i];
- exor = g[i] ^ cur;
- P:6("_Group_SetPlayer: Loop %d 0x%04x%04x 0x%04x%04x", i, _:cur >>> 16, _:cur & 0xFFFF, _:exor >>> 16, _:exor & 0xFFFF);
- // Low-level bit twiddling.
- while ((bit = Bit:Cell_GetLowestComponent(exor)))
- {
- // group = i * cellbits + (scDeBruijn[(_:bit * 0x077CB531) >>> 27]);
- group = i * cellbits + Cell_GetLowestBit(exor);
- P:7("_Group_SetPlayer: %d %s %d", p, _:(cur & bit) ? ("remove from") : ("add to"), group);
- if (cur & bit)
- {
- // Used to have this group, now don't.
- Iter_Remove(GroupPlayers[group], p);
- }
- else
- {
- // Didn't have this group, now do.
- Iter_Add(GroupPlayers[group], p);
- }
- exor ^= bit;
- }
- }
- }
- #endif
- #pragma tabsize 4
- GROUP_CHAIN?<YSI_g_sNextUpdFunc>(p, YSI_gGroupPlayers[p], g);
- // NOW save the new version, after everything else has had a chance to
- // update according to the changes.
- YSI_gGroupPlayers[p] = g;
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="p">Player to add to the smallest group.</param>
- * <param name="gs">An array of possible groups.</param>
- * <param name="c">The number of USED items in the array.</param>
- * <returns>
- * The group they have been added to.
- * </returns>
- * <remarks>
- * Chains the call with "Group_SetPlayer" to use its hierarchy code.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- foreign Group:_Group_SetBalancedInternal(p,Group:gs[],c);
- global Group:_Group_SetBalancedInternal(p,Group:gs[],c)
- {
- // Find which of the listed groups has the least players in.
- new
- count = cellmax,
- Group:id = INVALID_GROUP;
- for (new i = 0; i != c; ++i)
- {
- // Find the group with the least players.
- new
- gi = _:gs[i];
- if (_Group_IsValid(Group:gi))
- {
- new
- g2 = _:GROUP_TEMP_FIX(Group:gi),
- cc = Iter_Count(GroupPlayers[g2]);
- if (_Group_HasPlayer(g2, p))
- {
- // The player is already in this group - just return.
- return Group:gi;
- }
- else if (cc < count)
- {
- count = cc,
- id = Group:gi;
- }
- }
- }
- if (id == INVALID_GROUP)
- {
- P:W("Group_SetBalanced(Array) requires at least 1 valid group.");
- }
- else
- {
- Group_SetPlayer(id, p, true);
- }
- return id;
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="p">Player to add to the smallest group.</param>
- * <param name="gs">An array of possible groups.</param>
- * <param name="c">The number of USED items in the array.</param>
- * <returns>
- * The group they have been added to.
- * </returns>
- * <remarks>
- * Puts a player in whichever of the given groups currently has the least
- * players.
- *
- * Now ONLY takes a list - arrays should use "Group_SetBalancedArray". As a
- * result, has more error-checking.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- stock Group:Group_SetBalancedArray(p, Group:gs[], c = sizeof (gs))
- {
- if (!(0 <= p < MAX_PLAYERS)) return INVALID_GROUP;
- return _Group_SetBalancedInternal(p, gs, c);
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="playerid">Player to put in one of a number of groups</param>
- * <param name="">A list of groups.</param>
- * <remarks>
- * Puts a player in whichever of the given groups currently has the least
- * players.
- *
- * Now ONLY takes a list - arrays should use "Group_SetBalancedArray". As a
- * result, has more error-checking.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- stock Group:Group_SetBalanced(playerid, Group:g0, Group:g1, Group:...)
- {
- new
- Group:possible[_MAX_GROUPS_G],
- count = min(_MAX_GROUPS_G + 1, numargs()),
- i = 2,
- j = 3;
- possible[0] = g0,
- possible[1] = g1;
- while (j != count)
- {
- possible[i++] = Group:getarg(j++);
- }
- return Group_SetBalancedArray(playerid, possible, i);
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="g">Group to check.</param>
- * <param name="p">Player to check.</param>
- * <returns>
- * Is this player in this group?
- * </returns>
- *//*------------------------------------------------------------------------**/
- foreign bool:Group_GetPlayer(Group:g,p);
- global bool:Group_GetPlayer(Group:g,p)
- {
- P:2("bool:Group_GetPlayer called: %i, %i", _:g, p);
- if (_Group_IsValid(g) && VALID_PLAYERID(p))
- {
- GROUP_FIX(g);
- return Bit_Get(YSI_gGroupPlayers[p], _:g);
- //Bit_Set(YSI_gGroupPlayers[g], p, s, bits<MAX_PLAYERS>);
- }
- return false;
- }
- /*-------------------------------------------------------------------------*//**
- * <summary></summary>
- * <param name="g">Group to set the name of.</param>
- * <param name="n">The new name of the group.</param>
- * <remarks>
- * Sets the name of a group.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- foreign void:Group_SetName(Group:g,string:n[]);
- global void:Group_SetName(Group:g,string:n[])
- {
- P:2("Group_SetName called: %i, \"%s\"", _:g, n);
- if (_Group_IsValid(g))
- {
- GROUP_FIX(g);
- if (isnull(n))
- {
- YSI_gGroupData[_:g][E_GROUP_DATA_NAME][0] = 0;
- YSI_gGroupData[_:g][E_GROUP_DATA_HASH] = 0;
- }
- else
- {
- strpack(YSI_gGroupData[_:g][E_GROUP_DATA_NAME], n, MAX_GROUP_NAME char);
- YSI_gGroupData[_:g][E_GROUP_DATA_HASH] = YHash(n);
- }
- }
- //return 1;
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="g">Group to get the name of.</param>
- * <returns>
- * string:
- * </returns>
- * <remarks>
- * Gets the name of a group.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- foreign string:Group_GetName(Group:g);
- global string:Group_GetName(Group:g)
- {
- //printf("Group_GetName called: %04x%04x", _:g >>> 16, _:g & 0xFFFF);
- P:2("Group_GetName called: %04x%04x", _:g >>> 16, _:g & 0xFFFF);
- static
- ret[YSI_MAX_STRING];
- if (_Group_IsValid(g))
- {
- //printf("Is Valid");
- //return Bit_Get(YSI_gGroupPlayers[g], p);
- //Bit_Set(YSI_gGroupPlayers[g], p, s, bits<MAX_PLAYERS>);
- strunpack(ret, YSI_gGroupData[_:GROUP_TEMP_FIX(g)][E_GROUP_DATA_NAME], YSI_MAX_STRING);
- //printf("Is Valid: %s", ret);
- }
- return ret;
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="name">Name of a group to get.</param>
- * <returns>
- * Group: - The ID of the group with this name.
- * </returns>
- *//*------------------------------------------------------------------------**/
- foreign Group:Group_GetID(string:name[]);
- global Group:Group_GetID(string:name[])
- {
- P:2("Group_GetID called: %s", name);
- new
- i,
- hash = YHash(name);
- while (i != _MAX_GROUPS)
- {
- if (_Group_IsActive(i) && YSI_gGroupData[i][E_GROUP_DATA_HASH] == hash)
- {
- break;
- }
- ++i;
- }
- if (i == _MAX_GROUPS) return INVALID_GROUP;
- return GROUP_MANGLE(i);
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="g">Group to set for.</param>
- * <param name="n">Set or not?</param>
- * <remarks>
- * I actually can't remember what the "Gang" setting on a group does - I don't
- * think it actually does anything in the latest code version!
- * </remarks>
- *//*------------------------------------------------------------------------**/
- foreign void:Group_SetGang(Group:g,bool:n);
- global void:Group_SetGang(Group:g,bool:n)
- {
- P:2("Group_SetGang called: %i, %i", _:g, n);
- if (_Group_IsValid(g))
- {
- if (n)
- {
- _Group_LetGang(GROUP_TEMP_FIX(g));
- }
- else
- {
- _Group_VetGang(GROUP_TEMP_FIX(g));
- }
- }
- //return 1;
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="g">Group to get the gang status of.</param>
- * <returns>
- * bool:
- * </returns>
- * <remarks>
- * I still don't remember what this once did!
- * </remarks>
- *//*------------------------------------------------------------------------**/
- foreign bool:Group_GetGang(Group:g);
- global bool:Group_GetGang(Group:g)
- {
- P:2("bool:Group_GetGang called: %i", _:g);
- if (_Group_IsValid(g))
- {
- return _Group_GetGang(GROUP_TEMP_FIX(g));
- }
- return false;
- }
- /*-------------------------------------------------------------------------*//**
- * <summary>
- * Group_SetColor
- * Group_SetColour
- * </summary>
- * <param name="g">The group to set the colour of.</param>
- * <param name="c">An RGBA colour.</param>
- * <remarks>
- * This colour is not actually currently used anywhere.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- foreign void:Group_SetColour(Group:g,c);
- global void:Group_SetColour(Group:g,c)
- {
- P:2("Group_SetColour called: %i, %i", _:g, c);
- if (_Group_IsValid(g))
- {
- GROUP_FIX(g);
- _Group_SetColor(g, c);
- }
- //return 1;
- }
- /*-------------------------------------------------------------------------*//**
- * <summary>
- * Group_GetColor
- * Group_GetColour
- * </summary>
- * <param name="g">The group to get the colour of.</param>
- * <returns>
- * An RGBA colour.
- * </returns>
- * <remarks>
- * This colour is not actually currently used anywhere.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- foreign Group_GetColour(Group:g);
- global Group_GetColour(Group:g)
- {
- P:2("Group_GetColour called: %i", _:g);
- if (_Group_IsValid(g))
- {
- return _Group_GetColor(GROUP_TEMP_FIX(g));
- //Bit_Set(YSI_gGroupPlayers[g], p, s, bits<MAX_PLAYERS>);
- }
- return 0;
- }
- #define Group_SetColor Group_SetColour
- #define Group_GetColor Group_GetColour
- /*-------------------------------------------------------------------------*//**
- * <param name="g">The group to get the membership for.</param>
- * <returns>
- * The number of players in the group.
- * </returns>
- *//*------------------------------------------------------------------------**/
- foreign Group_GetCount(Group:g);
- global Group_GetCount(Group:g)
- {
- P:2("Group_GetCount called: %i", _:g);
- if (_Group_IsValid(g))
- {
- GROUP_FIX(g);
- return Iter_Count(GroupPlayers[_:g]);
- }
- return 0;
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="name">The optional name of the new group.</param>
- * <returns>
- * The group ID with a tag of "Group:", or "INVALID_GROUP".
- * </returns>
- * <remarks>
- * Group_Create - Local function to detect and "NULL"ify empty strings.
- * _Group_Create - Global function that does most of the work.
- * _Group_CreateChain - Remote function that updates all master scripts with
- * the new group's existence.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- static remotefunc void:_Group_CreateChain(Group:g)
- {
- GROUP_CHAIN?<YSI_g_sNextAddFunc>(g);
- }
- @foreign Group:_Group_Create(string:name[]);
- @global Group:_Group_Create(string:name[])
- {
- P:2("Group:Group_Create called: \"%s\"", name);
- P:2("Group_Create called in %d", _@);
- new
- i;
- if (!isnull(name))
- {
- // Get a group of the same name if it already exists.
- i = _:Group_GetID(name);
- if (i != _:INVALID_GROUP)
- {
- MASTER_ADD<_:GROUP_TEMP_FIX(Group:i)>
- return Group:i;
- }
- }
- i = 0;
- while (i != _MAX_GROUPS && _Group_IsActive(i))
- {
- ++i;
- }
- if (i == _MAX_GROUPS) return INVALID_GROUP;
- if (isnull(name))
- {
- YSI_gGroupData[i][E_GROUP_DATA_NAME][0] = 0;
- YSI_gGroupData[i][E_GROUP_DATA_FLAGS] = e_GROUP_FLAGS_TEMP | e_GROUP_FLAGS_ACTIVE;
- YSI_gGroupData[i][E_GROUP_DATA_HASH] = 0;
- }
- else
- {
- strpack(YSI_gGroupData[i][E_GROUP_DATA_NAME], name, MAX_GROUP_NAME char);
- YSI_gGroupData[i][E_GROUP_DATA_FLAGS] = e_GROUP_FLAGS_ACTIVE;
- if (!(YSI_gGroupData[i][E_GROUP_DATA_HASH] = YHash(name)))
- {
- P:E("Group %d has hash 0.", name);
- }
- }
- MASTER_SET<i>
- ++YSI_g_sGroupCount;
- // Set the child groups to only this group.
- //Bit_Let(YSI_g_sChildGroups[i], i);
- // Add this group to other groups.
- broadcastfunc _Group_CreateChain(Group:i);
- return GROUP_MANGLE(i);
- }
- stock Group:Group_Create(string:name[] = "")
- {
- if (name[0])
- {
- return _Group_Create(name);
- }
- else
- {
- return _Group_Create(NULL);
- }
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="group">Group to loop over.</param>
- * <param name="start">Last value.</param>
- * <returns>
- * The next player.
- * </returns>
- * <remarks>
- * Internal implementation of the "Group()" iterator for "foreach". Returns
- * all the players in a group one at a time. Now just wraps the "GroupPlayers"
- * iterator that is only stored in the GROUP master script.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- #pragma deprecated use "GroupMember"
- stock Iter_Func@Group(start, Group:group)
- {
- static
- sOnce = true;
- if (sOnce)
- {
- P:I("The \"Group\" iterator is deprecated, please use \"GroupMember\" instead.");
- sOnce = false;
- }
- return Iter_Func@GroupMember(start, Group:group);
- }
- // Else. VERY similar to the `iterstart` macro, but without the first `J@`,
- // which is used both for `sizeof` (we already gave it `I@`) and for detecting
- // state functions, which this is not one of (so doesn't need the detection -
- // and even if it was a state function, we would know that here and could
- // adapt).
- #define Iterator@GroupElse_(%1)%9$%9))-1);_:(%9=Iterator@%0$[_:%2]%9; ))-1+(%1));_:(%1)!=_:F@k:F@l:(%2=Iter_Func@%0$(_:%2));
- foreign Iter_Func@GroupMember(start, Group:group);
- global Iter_Func@GroupMember(start, Group:group)
- {
- GROUP_FIX(group);
- if (start == -1)
- {
- //start = Iter_Begin(GroupPlayers[]);
- start = Iter_First(GroupPlayers[_:GROUP_TEMP_FIX(group)]);
- }
- else
- {
- start = Iter_Next(GroupPlayers[_:GROUP_TEMP_FIX(group)], start);
- }
- if (start == Iter_End(GroupPlayers[]))
- {
- return -1;
- }
- return start;
- }
- #define Iterator@GroupMember iterstart(-1)
- /*-------------------------------------------------------------------------*//**
- * <param name="start">Last value.</param>
- * <returns>
- * The next group.
- * </returns>
- * <remarks>
- * Internal implementation of the "CreatedGroup()" iterator for "foreach".
- * Returns all the groups that exist.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- // Bypass y_iterate restrictions.
- foreign Group:Iter_Func@CreatedGroup(group);
- global Group:Iter_Func@CreatedGroup(group)
- {
- // In most iterators the inital value is implicitly handled by the first
- // "++", but it isn't here because we need to correct for the "Group" flag.
- if (Group:group != INVALID_GROUP) GROUP_FIX(Group:group);
- while (++group < (_MAX_GROUPS_G - 1))
- {
- // DO NOT include the global group in this loop!
- if (_Group_IsActive(Group:group)) return GROUP_MANGLE(Group:group);
- }
- return INVALID_GROUP;
- }
- #define Iterator@CreatedGroup iterstart(-1)
- /*-------------------------------------------------------------------------*//**
- * <param name="parent">Group to get the children of.</param>
- * <param name="start">Last value.</param>
- * <returns>
- * The next group.
- * </returns>
- * <remarks>
- * Internal implementation of the "ChildGroup()" iterator for "foreach".
- * Returns all the groups that are a child of the provided group.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- foreign Group:Iter_Func@GroupChild(start, Group:parent);
- global Group:Iter_Func@GroupChild(start, Group:parent)
- {
- if (!_Group_IsValid(parent)) return INVALID_GROUP;
- // In most iterators the inital value is implicitly handled by the first
- // "++", but it isn't here because we need to correct for the "Group" flag.
- if (Group:start != INVALID_GROUP) GROUP_FIX(Group:start);
- // Use the existing "Bits" iterator with fixed identifiers.
- return GROUP_MANGLE(Iter_Func@Bits(start, YSI_g_sChildGroups[_:GROUP_TEMP_FIX(parent)], bits<_MAX_GROUPS_G>));
- }
- #define Iterator@GroupChild Iterator@Bits
- /*-------------------------------------------------------------------------*//**
- * <summary>
- * _Y_G@C_0
- * _Y_G@C_1
- * _Y_G@C_2
- * _Y_G@C_3
- * </summary>
- * <param name="func">Function to call.</param>
- * <param name="">0, 1, 2, or 3 parameters.</param>
- * <returns>
- * 0 - ALWAYS zero to make "_gchain" work properly.
- * </returns>
- * <remarks>
- * Basically function indirection, call a function through a pointer and use
- * the compiler to figure out the assembly to generate instead of having a
- * run-time loop inside a single instance of this function. Using just one
- * macro ("GROUP_CHAIN") one of these four functions are selected and called.
- * Adding more is fairly trivial too, the parameters are just:
- *
- * #emit PUSH.S <16 + n * 4>
- * #emit PUSH.S ...
- * #emit PUSH.S 20
- * #emit PUSH.S 16
- * #emit PUSH.C <n * 4>
- *
- * </remarks>
- *//*------------------------------------------------------------------------**/
- stock _Y_G@C_0(func)
- {
- #emit PUSH.C 0
- #emit LCTRL 6
- #emit ADD.C 28
- #emit PUSH.pri
- #emit LOAD.S.pri func
- #emit SCTRL 6
- // ALWAYS return 0;
- return 0;
- }
- stock _Y_G@C_1(func, GLOBAL_TAG_TYPES:...)
- {
- #emit PUSH.S 16
- #emit PUSH.C 4
- #emit LCTRL 6
- #emit ADD.C 28
- #emit PUSH.pri
- #emit LOAD.S.pri func
- #emit SCTRL 6
- //#emit RETN
- return 0;
- }
- stock _Y_G@C_2(func, GLOBAL_TAG_TYPES:...)
- {
- #emit PUSH.S 20
- #emit PUSH.S 16
- #emit PUSH.C 8
- #emit LCTRL 6
- #emit ADD.C 28
- #emit PUSH.pri
- #emit LOAD.S.pri func
- #emit SCTRL 6
- //#emit RETN
- return 0;
- }
- stock _Y_G@C_3(func, GLOBAL_TAG_TYPES:...)
- {
- #emit PUSH.S 24
- #emit PUSH.S 20
- #emit PUSH.S 16
- #emit PUSH.C 12
- #emit LCTRL 6
- #emit ADD.C 28
- #emit PUSH.pri
- #emit LOAD.S.pri func
- #emit SCTRL 6
- //#emit RETN
- return 0;
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="g">Group to set for.</param>
- * <param name="el">Element to set.</param>
- * <param name="s">Set or unset?</param>
- * <remarks>
- * IMPORTANT NOTE: Groups are SLIGHTLY different to other systems - if you
- * REMOVE a group from another group then players WILL NOT be removed from
- * that second group, or any child groups.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- foreign void:Group_SetGroup(Group:g,Group:el,bool:s);
- global void:Group_SetGroup(Group:g,Group:el,bool:s)
- {
- P:2(#Group_SetGroup " called: %i, %i, %i", _:g, _:el, s);
- // Set wether a group can use this item.
- if (g != el && _Group_IsValid(g) && _Group_IsValid(el))
- {
- GROUP_FIX(g);
- GROUP_FIX(el);
- if (s)
- {
- if (Bit_Get(YSI_g_sChildGroups[_:g], _:el))
- {
- // Child is already part of parent.
- return;
- }
- Bit_Let(YSI_g_sChildGroups[_:g], _:el);
- if (g == Group:_MAX_GROUPS) Bit_Let(YSI_g_sDefaultGroups, _:el);
- foreach (new p : GroupPlayers[_:g])
- {
- if (!_Group_HasPlayer(el, p))
- {
- broadcastfunc _Group_SetPlayer(p, _Group_SetSome(p, _:el), bits<_MAX_GROUPS_G>);
- }
- }
- }
- else
- {
- if (Bit_Get(YSI_g_sChildGroups[_:g], _:el))
- {
- // Child is a part of parent (don't remove players though).
- Bit_Vet(YSI_g_sChildGroups[_:g], _:el);
- if (g == Group:_MAX_GROUPS) Bit_Vet(YSI_g_sDefaultGroups, _:el);
- }
- }
- }
- }
- /*-------------------------------------------------------------------------*//**
- * <param name="el">Element to set.</param>
- * <param name="s">Set or unset?</param>
- * <remarks>
- * If "s" is true, then one element is added to the global group. False it is
- * removed.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- foreign void:Group_SetGlobalGroup(Group:el,bool:s);
- global void:Group_SetGlobalGroup(Group:el,bool:s)
- {
- P:2(#Group_SetGlobalGroup " called: %i, %i", _:el, s);
- Group_SetGroup(GROUP_GLOBAL, el, s);
- }
- /*-------------------------------------------------------------------------*//**
- * <summary>Group_Get...</summary>
- * <param name="g">Group to get from.</param>
- * <param name="el">Element to get.</param>
- * <returns>
- * bool: Does the group have the element?
- * </returns>
- * <remarks>
- * This has no "active" checks on the groups as if they aren't active but are
- * in range, then "YSI_g_sChildGroups" will return false anyway. Extra checks
- * are therefore just a waste of time.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- foreign bool:Group_GetGroup(Group:g,Group:el);
- global bool:Group_GetGroup(Group:g,Group:el)
- {
- P:2(#Group_GetGroup " called: %i, %i", _:g, _:el);
- return (GROUP_MASK <= el <= GROUP_GLOBAL && GROUP_MASK <= g <= GROUP_GLOBAL && Bit_Get(YSI_g_sChildGroups[_:GROUP_TEMP_FIX(g)], _:GROUP_TEMP_FIX(el)));
- }
- /*-------------------------------------------------------------------------*//**
- * <summary>Group_GetGlobal...</summary>
- * <param name="el">Element to get.</param>
- * <returns>
- * bool: Does the global group have the element?
- * </returns>
- *//*------------------------------------------------------------------------**/
- foreign Group_GetGlobalGroup(Group:el);
- global Group_GetGlobalGroup(Group:el)
- {
- P:2(#Group_GetGlobalGroup " called: %i", _:el);
- return Group_GetGroup(GROUP_GLOBAL, el);
- }
- #define Group_AddChild(%0,%1) Group_SetGroup((%0), (%1), true)
- #define Group_RemoveChild(%0,%1) Group_SetGroup((%0), (%1), false)
- #define Group_IsChild(%0,%1) Group_GetGroup((%0), (%1))
- /*-------------------------------------------------------------------------*//**
- * <param name="p">Ancestor group to check.</param>
- * <param name="c">Other group to check.</param>
- * <returns>
- * bool: Is the second group related to the first?
- * </returns>
- * <remarks>
- * Now uses implicit, not explicit, recursion.
- * </remarks>
- *//*------------------------------------------------------------------------**/
- static stock
- BitArray:YSI_g_sGroupDone<_MAX_GROUPS_G>;
- foreign bool:Group_IsDescendant(Group:p, Group:c);
- global bool:Group_IsDescendant(Group:p, Group:c)
- {
- // Groups that don't exist CAN'T be related, even if they're the same group.
- if (_Group_IsValid(p) && _Group_IsValid(c))
- {
- if (p == c) return true; // Any group is related to itself.
- GROUP_FIX(p);
- GROUP_FIX(c);
- // "g" = current child. "s" = current stack position. "c" = target
- // child group. "p" = current parent.
- Bit_SetAll(YSI_g_sGroupDone, false);
- Bit_Let(YSI_g_sGroupDone, _:p);
- new
- g = -1,
- s = 0;
- for ( ; ; )
- {
- g = Iter_Func@Bits(g, YSI_g_sChildGroups[_:p], bits<_MAX_GROUPS_G>);
- if (g == -1)
- {
- // Finished this child group, pop off the stack.
- if (s--)
- {
- g = YSI_g_sRecursionStack[s][0];
- p = Group:YSI_g_sRecursionStack[s][1];
- }
- else return false;
- }
- else if (!Bit_Get(YSI_g_sGroupDone, g))
- {
- if (Group:g == c) return true;
- // We haven't tested this group yet.
- Bit_Let(YSI_g_sGroupDone, g);
- YSI_g_sRecursionStack[s][0] = g;
- YSI_g_sRecursionStack[s][1] = _:p;
- ++s;
- p = Group:g;
- g = -1;
- }
- }
- }
- return false;
- }
|