/* 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. */ /* 88 88 ,d ,d 88 88 88 88 MM88MMM ,adPPYba, 8b,dPPYba, ,adPPYYba, MM88MMM ,adPPYba, 8b,dPPYba, ,adPPYba, 88 88 a8P_____88 88P' "Y8 "" `Y8 88 a8" "8a 88P' "Y8 I8[ "" 88 88 8PP""""""" 88 ,adPPPPP88 88 8b d8 88 `"Y8ba, 88 88, "8b, ,aa 88 88, ,88 88, "8a, ,a8" 88 aa ]8I 88 "Y888 `"Ybbd8"' 88 `"8bbdP"Y8 "Y888 `"YbbdP"' 88 `"YbbdP"' */ /*-------------------------------------------------------------------------*//** * Create the internal iterators. *//*------------------------------------------------------------------------**/ #if _FOREACH_PLAYERS new Iterator:Player; #endif #if _FOREACH_BOTS new Iterator:Bot, Iterator:Character; #define Iter_Single@NPC Iter_Single@Bot #define Iterator@NPC Iterator@Bot #endif #if _FOREACH_ACTORS new #if _FOREACH_LOCALS Iterator:LocalActor, #endif Iterator:Actor; #endif #if _FOREACH_VEHICLES new #if _FOREACH_LOCALS Iterator:LocalVehicle, #endif Iterator:Vehicle; #endif iterfunc stock Range[cellmin](cur, min, max, step = 1) { if (cur == cellmin) cur = min; else cur += step; return (min <= cur < max || max < cur <= min) ? cur : cellmin; } iterfunc stock Powers(&iterstate, cur, base) { // Returns all the powers of the given number that can be stored in a PAWN // cell. // // foreach (new i : Powers(3)) // { // // 3^0, 3^1, 3^2, 3^3, etc... // } // if (cur) { return iterstate = base * cur, _:(iterstate > cur) * iterstate; } return 1; } #define iterstart@Powers iterstate(0, 0) iterfunc stock Fib(&iterstate, cur) { // Returns every number in the Fibaonacci sequence that can be stored in a // PAWN cell. // // foreach (new i : Fib()) // { // } // switch (cur) { case -1: return 0; case 1836311903: // End point (statically calculated largest Fibaonacci number that can // be stored in a signed 32-bit integer. Does make this not totally // portable, because it can't be used in the 64-bit version quickly. return -1; } // Based on the "+--" swap method (like "^^^"), but without the last one. return (iterstate = iterstate + cur) - cur; } #define iterstart@Fib iterstate(-1, 1) iterfunc stock Random(&iterstate, cur, count, min = cellmax, max = 0) { // Return a given count of random numbers: // // foreach (new i : Random(5)) // { // // 5 random numbers. // } // // foreach (new i : Random(12, 10)) // { // // 12 random numbers between 0 and 10 (0 to 9 inclusive). // } // // foreach (new i : Random(100, -10, 10)) // { // // 100 random numbers between -10 and 10 (-10 to 9 inclusive). // } // // Note that this function has internal state, so you cannot call this in a // nested manner. This will probably fail: // // foreach (new i : Random(10, 70)) // { // foreach (new j : Random(10, 80)) // { // // Will NOT get 100 randoms 0 to 80, plus 10 randoms 0 to 70. // } // } // if (cur == cellmin) { iterstate = 0; } if (++iterstate > count) { return cellmin; } if (min >= max) { return random(min); } else { return random(max - min) + min; } } #define iterstart@Random iterstate(cellmin, 0) iterfunc stock Null(cur, arr[], size = sizeof (arr)) { // Loop over all the indexes of this array that are zero. // // new array[] = { ... }; // foreach (new i : Null(array)) // { // } // while (++cur < size) { if (!arr[cur]) { return cur; } } return -1; } iterfunc stock NonNull(cur, arr[], size = sizeof (arr)) { // Loop over all the indexes of this array that are not zero. // // new array[] = { ... }; // foreach (new i : NonNull(array)) // { // } // while (++cur < size) { if (arr[cur]) { return cur; } } return -1; } iterfunc stock Until(cur, val, arr[], size = sizeof (arr)) { // Loop over all the indexes of this array until one equals the given value: // // new array[] = { ... }; // foreach (new i : Until(5, array)) // { // } // return (++cur >= size || arr[cur] == val) ? -1 : cur; } iterfunc stock Filter(cur, val, arr[], size = sizeof (arr)) { while (++cur < size) { if (arr[cur] == val) { return cur; } } return -1; } #define Iter_Func@None(%0,%1) _ITER(%1,%0) #define Iterator@None iterstart(-1) #define Iter_None_InternalA(%0,%1,%9) Iter_None_Internal(%1,F@s(%1)-1,%9) #define Iter_None_InternalB(%0,%2,%1,%9) Iter_None_Internal(%1,F@s(%1)-F@s(%0),%9) stock Iter_None_Internal(array[], size, value) { // Loop over all values NOT in any iterator. Similar to repeatedly calling // "Iter_Free", though that will return the same value twice if called twice // in a row. Instead, this function will loop through the missing ones. while (++value < size) { if (array[value] <= value) { return value; } } return -1; } #define Iter_Func@All(%0,%1) _ITER(%1,%0) #define Iterator@All iterstart(-1) #define Iter_All_InternalA(%0,%1,%9) Iter_All_Internal(%1,F@s(%1)-1,%9) #define Iter_All_InternalB(%0,%2,%1,%9) Iter_All_Internal(%1,F@s(%1)-F@s(%0),%9) stock Iter_All_Internal(array[], size, value) { // Loop over all values in any iterator. This is different to looping over // the iterator normally for multi-dimensional iterators, since it will // return all values in ANY iterator in their numerical order. For single- // dimensional iterators it is exactly the same, just a little slower. while (++value < size) { if (array[value] > value) { return value; } } return -1; } /* 88 88 88 88 88 88 88 88 88 88aaaaaaaa88 ,adPPYba, ,adPPYba, 88 ,d8 ,adPPYba, 88""""""""88 a8" "8a a8" "8a 88 ,a8" I8[ "" 88 88 8b d8 8b d8 8888[ `"Y8ba, 88 88 "8a, ,a8" "8a, ,a8" 88`"Yba, aa ]8I 88 88 `"YbbdP"' `"YbbdP"' 88 `Y8a `"YbbdP"' */ /*-------------------------------------------------------------------------*//** * * Sets up all existing iterators. Does nothing for "XXLocal" ones, since they * are by definition empty when a script starts. * *//*------------------------------------------------------------------------**/ #if _FOREACH_CHARACTERS || _FOREACH_VEHICLES || _FOREACH_ACTORS hook OnScriptInit() { #if _FOREACH_VEHICLES Iter_Clear(Vehicle); for (new i = 1; i <= MAX_VEHICLES; ++i) { if (GetVehicleModel(i)) { Iter_Add(Vehicle, i); } } #endif #if _FOREACH_ACTORS Iter_Clear(Actor); for (new i = 0; i != MAX_ACTORS; ++i) { if (IsValidActor(i)) { Iter_Add(Actor, i); } } #endif #if _FOREACH_CHARACTERS #if _FOREACH_BOTS Iter_Clear(Bot); Iter_Clear(Character); #endif #if _FOREACH_PLAYERS Iter_Clear(Player); #endif for (new i = 0; i != MAX_PLAYERS; ++i) { if (IsPlayerConnected(i)) { #if _FOREACH_BOTS Iter_Add(Character, i); if (IsPlayerNPC(i)) { Iter_Add(Bot, i); } else #endif { #if _FOREACH_PLAYERS Iter_Add(Player, i); #endif } } } #endif return 1; } #endif /* 88888888ba 88 88 "8b 88 88 ,8P 88 88aaaaaa8P' 88 ,adPPYYba, 8b d8 ,adPPYba, 8b,dPPYba, ,adPPYba, 88""""""' 88 "" `Y8 `8b d8' a8P_____88 88P' "Y8 I8[ "" 88 88 ,adPPPPP88 `8b d8' 8PP""""""" 88 `"Y8ba, 88 88 88, ,88 `8b,d8' "8b, ,aa 88 aa ]8I 88 88 `"8bbdP"Y8 Y88' `"Ybbd8"' 88 `"YbbdP"' d8' d8' */ /*-------------------------------------------------------------------------*//** * Player who joined. * * Adds a player to the loop data. Now sorts the list too. Note that I found * the most bizzare bug ever (I *think* it may be a compiler but, but it * requires further investigation), basically it seems that multiple variables * were being treated as the same variable (namely @YSII_EgotS and * @YSII_CgharacterS were the same and @YSII_EgotC and @YSII_CgharacterC were the * same). Adding print statements which reference these variables seem to fix * the problem, and I've tried to make sure that the values will never actually * get printed. * *//*------------------------------------------------------------------------**/ #if _FOREACH_CHARACTERS hook OnPlayerConnect(playerid) { P:1("Iter_OnPlayerConnect called: %d, %d", playerid, IsPlayerNPC(playerid)); #if _FOREACH_BOTS Iter_Add(Character, playerid); if (IsPlayerNPC(playerid)) { Iter_Add(Bot, playerid); } else #endif { #if _FOREACH_PLAYERS Iter_Add(Player, playerid); #endif } P:2("Iter_OnPlayerConnect end"); return 1; } /*---------------------------------------------------------------------*//** * * Player who left. * * Removes a player from the loop data. No longer uses "hook" to ENSURE * that this is always last. Previously I think that the order of * evaluation in y_hooks meant that this got called before the user * "OnPlayerDisconnect". * *//*--------------------------------------------------------------------**/ hook OnPlayerDisconnect(playerid, reason) { SetTimerEx("Iter_OPDCInternal", 0, false, "i", playerid); return 1; } /*---------------------------------------------------------------------*//** * * Player who left. * * Called AFTER "OnPlayerDisconnect" so that using "Kick" inside a * "foreach" loop doesn't crash the server due to an OOB error. * *//*--------------------------------------------------------------------**/ public Iter_OPDCInternal(playerid) { if (IsPlayerConnected(playerid)) { return; } #if _FOREACH_BOTS Iter_Remove(Character, playerid); if (IsPlayerNPC(playerid)) { Iter_Remove(Bot, playerid); } else #endif { #if _FOREACH_PLAYERS Iter_Remove(Player, playerid); #endif } } #endif /* db d88b ,d d8'`8b 88 d8' `8b ,adPPYba, MM88MMM ,adPPYba, 8b,dPPYba, ,adPPYba, d8YaaaaY8b a8" "" 88 a8" "8a 88P' "Y8 I8[ "" d8""""""""8b 8b 88 8b d8 88 `"Y8ba, d8' `8b "8a, ,aa 88, "8a, ,a8" 88 aa ]8I d8' `8b `"Ybbd8"' "Y888 `"YbbdP"' 88 `"YbbdP"' */ #if _FOREACH_ACTORS remotefunc void:Iter_ActorDo(bool:add, actorid) { // Because there may be multiple scripts running, we need to tell all of // them when an actor is created or destroyed. if (add) Iter_Add(Actor, actorid); else Iter_Remove(Actor, actorid); } stock Iter_CreateActor(modelid, Float:X, Float:Y, Float:Z, Float:Rotation) { new ret = CreateActor(modelid, X, Y, Z, Rotation); broadcastfunc Iter_ActorDo(true, ret); #if _FOREACH_LOCALS Iter_Add(LocalActor, ret); #endif return ret; } #if defined _ALS_CreateActor #undef CreateActor #else #define _ALS_CreateActor #endif #define CreateActor Iter_CreateActor stock Iter_DestroyActor(actorid) { broadcastfunc Iter_ActorDo(false, actorid); #if _FOREACH_LOCALS Iter_Remove(LocalActor, actorid); #endif return DestroyActor(actorid); } #if defined _ALS_DestroyActor #undef DestroyActor #else #define _ALS_DestroyActor #endif #define DestroyActor Iter_DestroyActor #endif /* 8b d8 88 88 88 `8b d8' 88 "" 88 `8b d8' 88 88 `8b d8' ,adPPYba, 88,dPPYba, 88 ,adPPYba, 88 ,adPPYba, ,adPPYba, `8b d8' a8P_____88 88P' "8a 88 a8" "" 88 a8P_____88 I8[ "" `8b d8' 8PP""""""" 88 88 88 8b 88 8PP""""""" `"Y8ba, `888' "8b, ,aa 88 88 88 "8a, ,aa 88 "8b, ,aa aa ]8I `8' `"Ybbd8"' 88 88 88 `"Ybbd8"' 88 `"Ybbd8"' `"YbbdP"' */ #if _FOREACH_VEHICLES remotefunc void:Iter_VehicleDo(bool:add, vehicleid) { // Because there may be multiple scripts running, we need to tell all of // them when a vehicle is created or destroyed. if (add) Iter_Add(Vehicle, vehicleid); else Iter_Remove(Vehicle, vehicleid); } stock Iter_CreateVehicle(modelid, Float:x, Float:y, Float:z, Float:angle, color1, color2, respawn_delay, addsiren = 0) { #if _FOREACH_ACTORS new ret = CreateVehicle(modelid, x, y, z, angle, color1, color2, respawn_delay, addsiren); #else #pragma unused addsiren new ret = CreateVehicle(modelid, x, y, z, angle, color1, color2, respawn_delay); #endif broadcastfunc Iter_VehicleDo(true, ret); #if _FOREACH_LOCALS Iter_Add(LocalVehicle, ret); #endif return ret; } #if defined _ALS_CreateVehicle #undef CreateVehicle #else #define _ALS_CreateVehicle #endif #define CreateVehicle Iter_CreateVehicle stock Iter_AddStaticVehicle(modelid, Float:spawn_x, Float:spawn_y, Float:spawn_z, Float:angle, color1, color2) { new ret = AddStaticVehicle(modelid, spawn_x, spawn_y, spawn_z, angle, color1, color2); broadcastfunc Iter_VehicleDo(true, ret); #if _FOREACH_LOCALS Iter_Add(LocalVehicle, ret); #endif return ret; } #if defined _ALS_AddStaticVehicle #undef AddStaticVehicle #else #define _ALS_AddStaticVehicle #endif #define AddStaticVehicle Iter_AddStaticVehicle stock Iter_AddStaticVehicleEx(modelid, Float:spawn_x, Float:spawn_y, Float:spawn_z, Float:angle, color1, color2, respawn_delay, addsiren = 0) { #if _FOREACH_ACTORS new ret = AddStaticVehicleEx(modelid, spawn_x, spawn_y, spawn_z, angle, color1, color2, respawn_delay, addsiren); #else #pragma unused addsiren new ret = AddStaticVehicleEx(modelid, spawn_x, spawn_y, spawn_z, angle, color1, color2, respawn_delay); #endif broadcastfunc Iter_VehicleDo(true, ret); #if _FOREACH_LOCALS Iter_Add(LocalVehicle, ret); #endif return ret; } #if defined _ALS_AddStaticVehicleEx #undef AddStaticVehicleEx #else #define _ALS_AddStaticVehicleEx #endif #define AddStaticVehicleEx Iter_AddStaticVehicleEx stock Iter_DestroyVehicle(vehicleid) { broadcastfunc Iter_VehicleDo(false, vehicleid); #if _FOREACH_LOCALS Iter_Remove(LocalVehicle, vehicleid); #endif return DestroyVehicle(vehicleid); } #if defined _ALS_DestroyVehicle #undef DestroyVehicle #else #define _ALS_DestroyVehicle #endif #define DestroyVehicle Iter_DestroyVehicle #endif