| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553 |
- /**--------------------------------------------------------------------------**\
- =======================================
- y_playerset - Collections of players!
- =======================================
- Description:
- This code is a huge abstraction of collections of players. It allows you to
- define functions which take one or more players, specified in a number of
- formats, and perform the code for all those player. Essentially it is an
- abstraction of loops over players.
- 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 playerset include.
-
- 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:
- ZeeX, koolk, JoeBullet/Google63, g_aSlice/Slice
-
- 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.
- 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.
-
- Version:
- 1.0
- Changelog:
- 30/04/11:
- First version
- </remarks>
- \**--------------------------------------------------------------------------**/
- #include <a_samp>
- #include "y_iterate"
- #include "y_playerarray"
- #include "y_debug"
- #if !defined MAX_PLAYER_SET_STACK
- #define MAX_PLAYER_SET_STACK (3)
- #endif
- #define ALL_PLAYERS (0x7FFFFFF1)
- #define ALL_HUMANS (0x7FFFFFF1)
- #define ALL_CHARACTERS (0x7FFFFFF2)
- #define ALL_BOTS (0x7FFFFFF3)
- enum e_PLAYER_SET_HACK
- {
- e_PLAYER_SET_HACK_PA[bits<MAX_PLAYERS> + 1] = 0,
- e_PLAYER_SET_HACK_DATA[MAX_PLAYERS] = 0,
- e_PLAYER_SET_HACK_EXCESS[MAX_PLAYERS - (bits<MAX_PLAYERS> + 1)]
- }
- enum e_PLAYER_SET_TYPE
- {
- e_PLAYER_SET_TYPE_NONE = 0,
- // "GROUP" is a YSI group.
- e_PLAYER_SET_TYPE_GROUP,
- // "ID" is just a single player.
- e_PLAYER_SET_TYPE_ID,
- // "PA" is a YSI player array.
- e_PLAYER_SET_TYPE_PA,
- // "BOOL" is an array of true/false.
- e_PLAYER_SET_TYPE_BOOL,
- // "ARRAY" is just an array of elements.
- e_PLAYER_SET_TYPE_ARRAY,
- // "CUSTOM" is used to identify fields in an enum.
- e_PLAYER_SET_TYPE_CUSTOM,
- e_PLAYER_SET_TYPE_PLAYERS,
- e_PLAYER_SET_TYPE_BOTS,
- e_PLAYER_SET_TYPE_CHARACTERS
- }
- // ========================================================================== //
- // ========================================================================== //
- // WARNING: THE CODE BELOW IS VERY FRAGILE - DO NOT TOUCH IT! //
- // ========================================================================== //
- // ========================================================================== //
- // DO NOT change the order of these variables!
- stock
- e_PLAYER_SET_TYPE:__ps_type[MAX_PLAYER_SET_STACK char],
- __ps_stack[MAX_PLAYER_SET_STACK][MAX_PLAYERS],
- __ps_data[e_PLAYER_SET_HACK],
- __ps_pointer = -1;
- //#define @PlayerVar:%0) __ps_addr_t:__ps_addr,__ps_drop_t:%0)for(new %0=-1;(%0=PS@YSII_Ag(__ps_addr,%0))!=-1;)
- #define @PlayerVar:%0) __ps_addr_t:__ps_addr,__ps_drop_t:%0)foreach(new %0:PS(__ps_addr))
- // More than one parameter. This removes the need to redefine "for", which I'm
- // very happy about, by doing all detection in one go.
- //#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;)
- #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))
- // Only one parameter (not caught by the above statement). The one is the
- // variable name we steal for the "foreach" loop.
- #define __ps_addr,__ps_drop_t:%0) __ps_addr)
- // This is not affected by any of the macros above.
- #define @PlayerArray:%0<%1>%2) __ps_addr_t:__ps_addr%2)for(new PlayerArray:%0<MAX_PLAYERS>;__PS_A(__ps_addr,%0); )
- #define @PlayerSet __ps_addr_t
- // This code is now less fragile than before (and I understand it far more
- // having done much more work with this style of macro in the interim).
- // This is the master function, and a long one at that. This function looks at
- // the parameters passed to it and determines what SORT of parameter has been
- // passed from the long list of possibilities. If "cur" is -1 then this is the
- // first call of the function and we need to determine the type. If "cur" is
- // not -1 then we are mid-loop and we can just use the stored determined type
- // and use "cur" (as the last player done) to figure out the next player. This
- // code only loops through connected players.
- stock PS@YSII_Ag(__ps_addr_t:addr, cur)
- {
- if (cur == -1)
- {
- P:3("__PS_S called: %i", _:addr);
- // Increment the "stack" pointer.
- if (__ps_pointer == MAX_PLAYER_SET_STACK - 1)
- {
- P:E("y_playerset stack overflow - increase \"MAX_PLAYER_SET_STACK\"");
- return -1;
- }
- ++__ps_pointer;
- new
- begin = __ps_data[e_PLAYER_SET_HACK_DATA][0];
- // Is this a single value element (group or ID).
- if (_:addr == begin)
- {
- // Increase the stack pointer for recursive/multi-layered calls.
- // Should really add error-checking code for overflows.
- __ps_stack[__ps_pointer][0] = _:addr;
- // Single value - either a playerid or a groupid.
- #if defined _YSI_HAS_GROUP_SYSTEM
- if (Group:addr & GROUP_MASK)
- {
- // Use the pre-made iterator functionality.
- __ps_type{__ps_pointer} = e_PLAYER_SET_TYPE_GROUP;
- cur = Group@YSII_Ag(Group:addr, -1);
- if (cur == -1)
- {
- --__ps_pointer;
- }
- return cur;
- }
- #endif
- // May not always want this check - tough, they can't really have one
- // inside the function because that's just silly.
- switch (_:addr)
- {
- case ALL_PLAYERS:
- {
- // Uses the new "foreach" format of the infinate loop.
- __ps_type{__ps_pointer} = e_PLAYER_SET_TYPE_PLAYERS;
- cur = Player@YSII_Ag[sizeof (Player@YSII_Ag) - 1];
- if (cur == sizeof (Player@YSII_Ag) - 1)
- {
- --__ps_pointer;
- cur = -1;
- }
- return cur;
- }
- #if defined _FOREACH_BOT && !defined FOREACH_NO_BOTS
- case ALL_BOTS:
- {
- __ps_type{__ps_pointer} = e_PLAYER_SET_TYPE_BOTS;
- cur = Bot@YSII_Ag[sizeof (Bot@YSII_Ag) - 1];
- if (cur == sizeof (Bot@YSII_Ag) - 1)
- {
- --__ps_pointer;
- cur = -1;
- }
- return cur;
- }
- case ALL_CHARACTERS:
- {
- __ps_type{__ps_pointer} = e_PLAYER_SET_TYPE_CHARACTERS;
- cur = Character@YSII_Ag[sizeof (Character@YSII_Ag) - 1];
- if (cur == sizeof (Character@YSII_Ag) - 1)
- {
- --__ps_pointer;
- cur = -1;
- }
- return cur;
- }
- #endif
- default:
- {
- __ps_type{__ps_pointer} = e_PLAYER_SET_TYPE_ID;
- if (PS_IS_PLAYER_CONNECTED(_:addr))
- {
- return _:addr;
- }
- else
- {
- --__ps_pointer;
- return -1;
- }
- }
- }
- }
- else
- {
- // It's an array - _:addr contains the address of the target.
- memcpy(__ps_stack[__ps_pointer], __ps_data[e_PLAYER_SET_HACK_DATA], 0, MAX_PLAYERS * 4);
- // Try to determine what sort of array it is. Note that there are three
- // possible types.
- if (begin == PA_TYPE_PA)
- {
- // Easy to handle - the systems were designed for each other.
- // This one needs work... Err - what work?
- __ps_type{__ps_pointer} = e_PLAYER_SET_TYPE_PA;
- cur = PA@YSII_Ag(Bit:__ps_stack[__ps_pointer], -1);
- if (cur == -1)
- {
- --__ps_pointer;
- }
- return cur;
- }
- else if (begin & 0xFF0000FF == 0x0F0000F0)
- {
- __ps_type{__ps_pointer} = e_PLAYER_SET_TYPE_CUSTOM;
- cur = begin >>> 8 & 0x0000FFFF;
- if (cur == INVALID_PLAYER_ID & 0x0000FFFF)
- {
- --__ps_pointer;
- return -1;
- }
- if (PS_IS_PLAYER_CONNECTED(cur))
- {
- return cur;
- }
- addr = __ps_addr_t:0;
- while (++_:addr != MAX_PLAYERS)
- {
- cur = __ps_stack[__ps_pointer][_:addr];
- if (cur == INVALID_PLAYER_ID)
- {
- --__ps_pointer;
- return -1;
- }
- if (PS_IS_PLAYER_CONNECTED(cur))
- {
- return cur;
- }
- }
- --__ps_pointer;
- return -1;
- }
- else if (begin > 1 || __ps_data[e_PLAYER_SET_HACK_DATA][1] > 1 || __ps_data[e_PLAYER_SET_HACK_DATA][2] > 1)
- {
- // List of players. One of the first three will normally be greater
- // than 1 in a list of players.
- __ps_type{__ps_pointer} = e_PLAYER_SET_TYPE_ARRAY;
- cur = begin;
- if (cur == INVALID_PLAYER_ID)
- {
- --__ps_pointer;
- return -1;
- }
- if (PS_IS_PLAYER_CONNECTED(cur))
- {
- return cur;
- }
- addr = __ps_addr_t:0;
- while (++_:addr != MAX_PLAYERS)
- {
- cur = __ps_stack[__ps_pointer][_:addr];
- if (cur == INVALID_PLAYER_ID)
- {
- --__ps_pointer;
- return -1;
- }
- if (PS_IS_PLAYER_CONNECTED(cur))
- {
- return cur;
- }
- }
- --__ps_pointer;
- return -1;
- }
- else
- {
- // Boolean array.
- __ps_type{__ps_pointer} = e_PLAYER_SET_TYPE_BOOL;
- // Find the first set player.
- foreach (cur : Player)
- {
- // Now ANY true value is true.
- if (__ps_data[e_PLAYER_SET_HACK_DATA][cur])
- {
- //_:addr = i;
- return cur;
- }
- }
- // No players specified.
- --__ps_pointer;
- return -1;
- }
- }
- // Will have returned by this point.
- }
- P:3("__PS_N called: %i, %i", _:addr, cur);
- // Each mode has a different end condition.
- switch (__ps_type{__ps_pointer})
- {
- #if defined _YSI_HAS_GROUP_SYSTEM
- case e_PLAYER_SET_TYPE_GROUP:
- {
- cur = Group@YSII_Ag(Group:_:addr, cur);
- if (cur == -1)
- {
- --__ps_pointer;
- }
- return cur;
- }
- #endif
- case e_PLAYER_SET_TYPE_PLAYERS:
- {
- cur = Player@YSII_Ag[cur];
- if (cur == sizeof (Player@YSII_Ag) - 1)
- {
- --__ps_pointer;
- cur = -1;
- }
- return cur;
- }
- #if defined _FOREACH_BOT && !defined FOREACH_NO_BOTS
- case e_PLAYER_SET_TYPE_BOTS:
- {
- cur = Bot@YSII_Ag[cur];
- if (cur == sizeof (Bot@YSII_Ag) - 1)
- {
- --__ps_pointer;
- cur = -1;
- }
- return cur;
- }
- case e_PLAYER_SET_TYPE_CHARACTERS:
- {
- cur = Character@YSII_Ag[cur];
- if (cur == sizeof (Character@YSII_Ag) - 1)
- {
- --__ps_pointer;
- cur = -1;
- }
- return cur;
- }
- #endif
- case e_PLAYER_SET_TYPE_PA:
- {
- cur = PA@YSII_Ag(Bit:__ps_stack[__ps_pointer], cur);
- if (cur == -1)
- {
- --__ps_pointer;
- }
- return cur;
- }
- case e_PLAYER_SET_TYPE_BOOL:
- {
- for ( ; ; )
- {
- cur = Player@YSII_Ag[cur];
- if (cur == sizeof (Player@YSII_Ag) - 1)
- {
- --__ps_pointer;
- return -1;
- }
- if (__ps_stack[__ps_pointer][cur])
- {
- // Don't need to check if they're connected as the data
- // comes directly from "foreach".
- break;
- }
- // Could add extra late checks here (Error, not Warning, now).
- }
- return cur;
- }
- case e_PLAYER_SET_TYPE_ID:
- {
- --__ps_pointer;
- return -1;
- }
- case e_PLAYER_SET_TYPE_ARRAY:
- {
- addr = __ps_addr_t:-1;
- while (++_:addr != MAX_PLAYERS)
- {
- if (__ps_stack[__ps_pointer][_:addr] == cur)
- {
- break;
- }
- }
- if (_:addr != MAX_PLAYERS)
- {
- while (++_:addr != MAX_PLAYERS)
- {
- cur = __ps_stack[__ps_pointer][_:addr];
- if (cur == INVALID_PLAYER_ID)
- {
- --__ps_pointer;
- return -1;
- }
- if (PS_IS_PLAYER_CONNECTED(cur))
- {
- return cur;
- }
- }
- }
- --__ps_pointer;
- return -1;
- }
- case e_PLAYER_SET_TYPE_CUSTOM:
- {
- if (cur == __ps_stack[__ps_pointer][0] >>> 8 & 0x0000FFFF)
- {
- addr = __ps_addr_t:0;
- while (++_:addr != MAX_PLAYERS)
- {
- cur = __ps_stack[__ps_pointer][_:addr];
- if (cur == INVALID_PLAYER_ID)
- {
- --__ps_pointer;
- return -1;
- }
- if (PS_IS_PLAYER_CONNECTED(cur))
- {
- return cur;
- }
- }
- --__ps_pointer;
- return -1;
- }
- else
- {
- addr = __ps_addr_t:0;
- while (++_:addr != MAX_PLAYERS)
- {
- if (__ps_stack[__ps_pointer][_:addr] == cur)
- {
- break;
- }
- }
- if (_:addr != MAX_PLAYERS)
- {
- while (++_:addr != MAX_PLAYERS)
- {
- cur = __ps_stack[__ps_pointer][_:addr];
- if (cur == INVALID_PLAYER_ID)
- {
- --__ps_pointer;
- return -1;
- }
- if (PS_IS_PLAYER_CONNECTED(cur))
- {
- return cur;
- }
- }
- }
- --__ps_pointer;
- return -1;
- }
- }
- }
- --__ps_pointer;
- return -1;
- }
- // This function gets the required data from custom format (enum) arrays.
- stock __PS_C(source[MAX_PLAYERS][], idx)
- {
- static
- sFake[MAX_PLAYERS] = {(INVALID_PLAYER_ID << 8) | 0x0F0000F0};
- new
- ret = (__ps_data[e_PLAYER_SET_HACK_DATA] = sFake),
- e_PLAYER_SET_HACK:i = e_PLAYER_SET_HACK:0;
- foreach (new playerid : Player)
- {
- if (source[playerid][idx])
- {
- if (i)
- {
- __ps_data[i++] = playerid;
- }
- else
- {
- __ps_data[i++] = (playerid << 8) | 0x0F0000F0;
- }
- }
- }
- if (i < e_PLAYER_SET_HACK:MAX_PLAYERS)
- {
- __ps_data[i] = INVALID_PLAYER_ID;
- }
- return ret;
- }
- stock bool:__PS_A(@PlayerSet:addr, PlayerArray:ret<MAX_PLAYERS>)
- {
- if (ret[0])
- {
- return false;
- }
- PA_FastInit(ret);
- foreach (new a : PS(addr))
- {
- PA_Let(ret, a);
- }
- return true;
- }
- // This SHOULD handle excess parameters correctly, simply because I left out the
- // extra brackets.
- #define PSF:%0(%1) %0(__ps_addr_t:__ps_data[e_PLAYER_SET_HACK_PA]=__ps_addr_t:%1)
- // This redefines e_PLAYER_SET_HACK_DATA in the case of passing player arrays.
- #define e_PLAYER_SET_HACK_PA]=__ps_addr_t:@%0) e_PLAYER_SET_HACK_DATA]=__ps_addr_t:%0)
- // This redefines __ps_data in the case of custom arrays.
- //#define __ps_data[e_PLAYER_SET_HACK_DATA]=%0<%1> __ps_addr_t:__PS_Z(%0,%1)
- //#define __PS_Z(@%0,%1) __PS_C(%0,%1)
- // Don't actually need the "@".
- #define __ps_data[e_PLAYER_SET_HACK_DATA]=%0<%1> __ps_addr_t:__PS_C(%0,%1)
- #define __PS_C(@%0,%1) __PS_C(%0,%1)
|