05.11.2016, 21:49
Alguem sabe resolver esse erros na GameMode ScavengeSurive.?
olhe a imagem do link
http://imgur.com/a/KotCr
olhe a imagem do link
http://imgur.com/a/KotCr
/*============================================================================== # Southclaw's Interactivity Framework (SIF) ## Overview SIF is a collection of high-level include scripts to make the development of interactive features easy for the developer while maintaining quality front-end gameplay for players. ## Description A simple framework using streamer areas and key checks to give the in-game effect of physical buttons that players must press instead of using a command. It was created as an alternative to the default GTA:SA spinning pickups for a few reasons: 1. A player might want to stand where a pickup is but not use it (if the pickup is a building entrance or interior warp, he might want to stand in the doorway without being teleported.) 2. Making hidden doors or secrets that can only be found by walking near the button area and seeing the textdraw. (or spamming F!) 3. Spinning objects don't really add immersion to a role-play environment! ## Dependencies - Streamer Plugin - YSI\y_hooks - YSI\y_timers ## Hooks - OnScriptInit: Zero initialised array cells. - OnPlayerConnect: Zero y_iterator and some player array data. - OnPlayerKeyStateChange: Catch F/Enter key presses for button interaction. - OnPlayerEnterDynamicArea: Monitor what buttons the player has walked near. - OnPlayerLeaveDynamicArea: Detect when a player has walked away from a button. ## Credits - SA:MP Team: Amazing mod! - SA:MP Community: Inspiration and support - Incognito: Very useful streamer plugin - ******: YSI framework ==============================================================================*/ #if defined _SIF_BUTTON_INCLUDED #endinput #endif #if !defined _SIF_DEBUG_INCLUDED #include <SIF\Debug.pwn> #endif #if !defined _SIF_CORE_INCLUDED #include <SIF\Core.pwn> #endif #if defined DEBUG_LABELS_BUTTON #include <SIF\extensions\DebugLabels.pwn> #endif #include <YSI\y_iterate> #include <YSI\y_timers> #include <YSI\y_hooks> #include <streamer> #define _SIF_BUTTON_INCLUDED /*============================================================================== Constant Definitions, Function Declarations and Documentation ==============================================================================*/ // Maximum amount of buttons that can be created. #if !defined BTN_MAX #define BTN_MAX (8192) #endif // Maximum string length for labels and action-text strings. #if !defined BTN_MAX_TEXT #define BTN_MAX_TEXT (128) #endif // Default maximum stream range for button label text. #if !defined BTN_DEFAULT_STREAMDIST #define BTN_DEFAULT_STREAMDIST (4.0) #endif // Maximum amount of buttons to record the player being near to. #if !defined BTN_MAX_INRANGE #define BTN_MAX_INRANGE (8) #endif // A value to identify streamer object EXTRA_ID data array type. #if !defined BTN_STREAMER_AREA_IDENTIFIER #define BTN_STREAMER_AREA_IDENTIFIER (100) #endif // Time in milliseconds to freeze a player upon using a linked teleport button. #if !defined BTN_TELEPORT_FREEZE_TIME #define BTN_TELEPORT_FREEZE_TIME (1000) #endif // Validity check constant #define INVALID_BUTTON_ID (-1) DEFINE_HOOK_REPLACEMENT(Button , Btn); // Functions forward CreateButton(Float:x, Float:y, Float:z, text[], world = 0, interior = 0, Float:areasize = 1.0, label = 0, labeltext[] = "", labelcolour = 0xFFFF00FF, Float:streamdist = BTN_DEFAULT_STREAMDIST, testlos = true); /* # Description Creates an interactive button players can activate by pressing F. # Parameters - x, y, z: World position. - text: Message box text for when the player approaches the button. - world: The virtual world to show the button in. - interior: The interior world to show the button in. - areasize: Size of the button's detection area. - label: Determines whether a 3D Text Label should be at the button. - labeltext: The text that the label should show. - labelcolour: The colour of the label. - streamdist: Stream distance of the label. - testlos: test line of sight for the label. # Returns Button ID handle of the newly created button or INVALID_BUTTON_ID if another button cannot be created due to BTN_MAX limit. */ forward DestroyButton(buttonid); /* # Description Destroys a button. # Returns Boolean to indicate success or failure. */ forward LinkTP(buttonid1, buttonid2); /* # Description Links two buttons to be teleport buttons, if a user presses buttonid1 he will be teleported to the position of buttonid2 and vice versa. # Returns Boolean to indicate success or failure. */ forward UnLinkTP(buttonid1, buttonid2); /* # Description Un-links two linked buttons # Returns -1 if buttons are not linked at all. -2 if either is linked to another button. */ forward IsValidButton(buttonid); /* # Description Checks if buttonid is a valid button ID handle. */ forward GetButtonArea(buttonid); /* # Description Returns the streamer area ID used by a button. */ forward SetButtonArea(buttonid, areaid); /* # Description Updates a button's streamer area ID. Note that this does not remove the existing area from memory. # Returns Boolean to indicate success or failure. */ forward SetButtonLabel(buttonid, text[], colour = 0xFFFF00FF, Float:range = BTN_DEFAULT_STREAMDIST, testlos = true); /* # Description Creates a 3D Text Label at the specified button ID handle, if a label already exists it updates the text, colour and range. */ forward DestroyButtonLabel(buttonid); /* # Description Removes the label from a button. # Returns Boolean to indicate success or failure. -1 if the button did not have a label. */ forward GetButtonPos(buttonid, &Float:x, &Float:y, &Float:z); /* # Description Stores the button's X, Y and Z into parameters. # Returns Boolean to indicate success or failure. */ forward SetButtonPos(buttonid, Float:x, Float:y, Float:z); /* # Description Changes the button position (area, label, etc). # Returns Boolean to indicate success or failure. */ forward Float:GetButtonSize(buttonid); /* # Description Returns the size of the specified button's dynamic area. # Returns 0.0 if the specified button ID handle is invalid. */ forward SetButtonSize(buttonid, Float:size); /* # Description Sets a button's detection area size. # Returns Boolean to indicate success or failure. */ forward GetButtonWorld(buttonid); /* # Description Returns the virtual world that a button exists in. */ forward SetButtonWorld(buttonid, world); /* # Description Updates a button's virtual world. Moves all streamer entities to the world too. # Returns Boolean to indicate success or failure. */ forward GetButtonInterior(buttonid); /* # Description Returns the interior that a button exists in. */ forward SetButtonInterior(buttonid, interior); /* # Description Updates a button's interior. Moves all streamer entities to the interior too. # Returns Boolean to indicate success or failure. */ forward GetButtonLinkedID(buttonid); /* # Description Returns the linked button of buttonid. */ forward GetButtonText(buttonid, text[]); /* # Description Returns the text assigned to a button that appears on-screen when a player walks near it. */ forward SetButtonText(buttonid, text[]); /* # Description Updates the text that appears on-screen when a player walks near the button. # Returns Boolean to indicate success or failure. */ forward SetButtonExtraData(buttonid, data); /* # Description Sets the button's extra data field, this is one cell of blank space allocated for each button. # Returns Boolean to indicate success or failure. */ forward GetButtonExtraData(buttonid); /* # Description Retrieves the integer assigned to the button set with SetButtonExtraData. */ forward GetPlayerPressingButton(playerid); /* # Description Returns the ID of the button that the player is currently pressing. This will only return a value while playerid is holding down the interact key at a button. */ forward GetPlayerButtonID(playerid); /* # Description Returns the closest button that a player is standing within the area of. */ forward GetPlayerButtonList(playerid, list[], &size, bool:validate = false); /* # Description Returns a list of buttons that a player is standing in the areas of. # Parameters - playerid: Player to get a list of buttons from. - list: Array to store the buttons in (Must be BTN_MAX_INRANGE size) - size: Stores the amount of buttons in the list. - validate: If true, the function will check if the player is actually in each button in the list. This is a small workaround for a larger problem that is currently unknown that results in a button not being removed from a player's list when they leave the area for it. # Returns Boolean to indicate success or failure. */ forward Float:GetPlayerAngleToButton(playerid, buttonid); /* # Description Returns the angle in degrees from a player to a button. */ forward Float:GetButtonAngleToPlayer(playerid, buttonid); /* # Description Returns the angle in degrees from a button to a player. */ // Events forward OnButtonPress(playerid, buttonid); /* # Called When a player presses the F/Enter key while at a button. # Returns If the button is a linked teleporter, return 1 to prevent teleporting. */ forward OnButtonRelease(playerid, buttonid); /* # Called When a player releases the F/Enter key after pressing it while at a button. */ forward OnPlayerEnterButtonArea(playerid, buttonid); /* # Called When a player enters the dynamic streamed area of a button. */ forward OnPlayerLeaveButtonArea(playerid, buttonid); /* # Called When a player leaves the dynamic streamed area of a button. */ /*============================================================================== Setup ==============================================================================*/ enum E_BTN_DATA { btn_area, Text3D: btn_label, Float: btn_posX, Float: btn_posY, Float: btn_size, btn_world, btn_interior, btn_link, btn_text[BTN_MAX_TEXT], btn_exData } enum e_button_range_data { btn_buttonId, Float: btn_distance } new btn_Data[BTN_MAX][E_BTN_DATA], Iterator:btn_Index<BTN_MAX> #if defined DEBUG_LABELS_BUTTON , btn_DebugLabelType, btn_DebugLabelID[BTN_MAX] #endif ; static btn_Near[MAX_PLAYERS][BTN_MAX_INRANGE], Iterator:btn_NearIndex[MAX_PLAYERS]<BTN_MAX_INRANGE>, btn_Pressing[MAX_PLAYERS]; static BUTTON_DEBUG = -1; /*============================================================================== Zeroing ==============================================================================*/ hook OnScriptInit() { BUTTON_DEBUG = sif_debug_register_handler("SIF/Button"); sif_d:SIF_DEBUG_LEVEL_CALLBACKS:BUTTON_DEBUG("[OnScriptInit]"); Iter_Init(btn_NearIndex); for(new i; i < MAX_PLAYERS; i++) { btn_Pressing[i] = INVALID_BUTTON_ID; } #if defined DEBUG_LABELS_BUTTON btn_DebugLabelType = DefineDebugLabelType("BUTTON", 0xFF0000FF); #endif } hook OnPlayerConnect(playerid) { sif_dp:SIF_DEBUG_LEVEL_CALLBACKS:BUTTON_DEBUG("[OnPlayerConnect]")<playerid>; Iter_Clear(btn_NearIndex[playerid]); btn_Pressing[playerid] = INVALID_BUTTON_ID; } /*============================================================================== Core Functions ==============================================================================*/ stock CreateButton(Float:x, Float:y, Float:z, text[], world = 0, interior = 0, Float:areasize = 1.0, label = 0, labeltext[] = "", labelcolour = 0xFFFF00FF, Float:streamdist = BTN_DEFAULT_STREAMDIST, testlos = true) { sif_d:SIF_DEBUG_LEVEL_CORE:BUTTON_DEBUG("[CreateButton]"); new id = Iter_Free(btn_Index); if(id == -1) { print("ERROR: BTN_MAX reached, please increase this constant!"); return INVALID_BUTTON_ID; } btn_Data[id][btn_area] = CreateDynamicSphere(x, y, z, areasize, world, interior); strcpy(btn_Data[id][btn_text], text, BTN_MAX_TEXT); btn_Data[id][btn_posX] = x; btn_Data[id][btn_posY] = y; btn_Data[id][btn_posZ] = z; btn_Data[id][btn_size] = areasize; btn_Data[id][btn_world] = world; btn_Data[id][btn_interior] = interior; btn_Data[id][btn_link] = INVALID_BUTTON_ID; if(label) btn_Data[id][btn_label] = CreateDynamic3DTextLabel(labeltext, labelcolour, x, y, z, streamdist, .testlos = testlos, .worldid = world, .interiorid = interior, .streamdistance = streamdist); else btn_Data[id][btn_label] = Text3D:INVALID_3DTEXT_ID; new data[2]; data[0] = BTN_STREAMER_AREA_IDENTIFIER; data[1] = id; Streamer_SetArrayData(STREAMER_TYPE_AREA, btn_Data[id][btn_area], E_STREAMER_EXTRA_ID, data, 2); Iter_Add(btn_Index, id); #if defined DEBUG_LABELS_BUTTON btn_DebugLabelID[id] = CreateDebugLabel(btn_DebugLabelType, id, x, y, z); UpdateButtonDebugLabel(id); #endif return id; } stock DestroyButton(buttonid) { sif_d:SIF_DEBUG_LEVEL_CORE:BUTTON_DEBUG("[DestroyButton]"); if(!Iter_Contains(btn_Index, buttonid)) return 0; sif_d:SIF_DEBUG_LEVEL_CORE_DEEP:BUTTON_DEBUG("[DestroyButton] process_LeaveDynamicArea calls"); foreach(new i : Player) { if(IsPlayerInDynamicArea(i, btn_Data[buttonid][btn_area])) process_LeaveDynamicArea(i, btn_Data[buttonid][btn_area]); } sif_d:SIF_DEBUG_LEVEL_CORE_DEEP:BUTTON_DEBUG("[DestroyButton] Destroying dynamic area"); DestroyDynamicArea(btn_Data[buttonid][btn_area]); sif_d:SIF_DEBUG_LEVEL_CORE_DEEP:BUTTON_DEBUG("[DestroyButton] Destroying 3D text label"); if(IsValidDynamic3DTextLabel(btn_Data[buttonid][btn_label])) DestroyDynamic3DTextLabel(btn_Data[buttonid][btn_label]); sif_d:SIF_DEBUG_LEVEL_CORE_DEEP:BUTTON_DEBUG("[DestroyButton] Clearing array data"); btn_Data[buttonid][btn_area] = -1; btn_Data[buttonid][btn_label] = Text3D:INVALID_3DTEXT_ID; btn_Data[buttonid][btn_posX] = 0.0; btn_Data[buttonid][btn_posY] = 0.0; btn_Data[buttonid][btn_posZ] = 0.0; btn_Data[buttonid][btn_size] = 0.0; btn_Data[buttonid][btn_world] = 0; btn_Data[buttonid][btn_interior] = 0; btn_Data[buttonid][btn_link] = INVALID_BUTTON_ID; btn_Data[buttonid][btn_text][0] = EOS; sif_d:SIF_DEBUG_LEVEL_CORE_DEEP:BUTTON_DEBUG("[DestroyButton] Removing from button index"); Iter_Remove(btn_Index, buttonid); sif_d:SIF_DEBUG_LEVEL_CORE_DEEP:BUTTON_DEBUG("[DestroyButton] Destroying debug label"); #if defined DEBUG_LABELS_BUTTON DestroyDebugLabel(btn_DebugLabelID[buttonid]); #endif sif_d:SIF_DEBUG_LEVEL_CORE_DEEP:BUTTON_DEBUG("[DestroyButton] End of function"); return 1; } stock LinkTP(buttonid1, buttonid2) { sif_d:SIF_DEBUG_LEVEL_CORE:BUTTON_DEBUG("[LinkTP]"); if(!Iter_Contains(btn_Index, buttonid1) || !Iter_Contains(btn_Index, buttonid2)) return 0; btn_Data[buttonid1][btn_link] = buttonid2; btn_Data[buttonid2][btn_link] = buttonid1; #if defined DEBUG_LABELS_BUTTON UpdateButtonDebugLabel(buttonid1); UpdateButtonDebugLabel(buttonid2); #endif return 1; } stock UnLinkTP(buttonid1, buttonid2) { sif_d:SIF_DEBUG_LEVEL_CORE:BUTTON_DEBUG("[UnLinkTP]"); if(!Iter_Contains(btn_Index, buttonid1) || !Iter_Contains(btn_Index, buttonid2)) return 0; if(btn_Data[buttonid1][btn_link] == INVALID_BUTTON_ID || btn_Data[buttonid1][btn_link] == INVALID_BUTTON_ID) return -1; if(btn_Data[buttonid1][btn_link] != buttonid2 || btn_Data[buttonid2][btn_link] != buttonid1) return -2; btn_Data[buttonid1][btn_link] = INVALID_BUTTON_ID; btn_Data[buttonid2][btn_link] = INVALID_BUTTON_ID; #if defined DEBUG_LABELS_BUTTON UpdateButtonDebugLabel(buttonid1); UpdateButtonDebugLabel(buttonid2); #endif return 1; } stock IsValidButton(buttonid) { sif_d:SIF_DEBUG_LEVEL_INTERFACE:BUTTON_DEBUG("[IsValidButton]"); if(!Iter_Contains(btn_Index, buttonid)) return 0; return 1; } // btn_area stock GetButtonArea(buttonid) { sif_d:SIF_DEBUG_LEVEL_INTERFACE:BUTTON_DEBUG("[GetButtonWorld]"); if(!Iter_Contains(btn_Index, buttonid)) return -1; return btn_Data[buttonid][btn_area]; } stock SetButtonArea(buttonid, areaid) { sif_d:SIF_DEBUG_LEVEL_INTERFACE:BUTTON_DEBUG("[GetButtonWorld]"); if(!Iter_Contains(btn_Index, buttonid)) return 0; btn_Data[buttonid][btn_area] = areaid; return 1; } // btn_label stock SetButtonLabel(buttonid, text[], colour = 0xFFFF00FF, Float:range = BTN_DEFAULT_STREAMDIST, testlos = true) { sif_d:SIF_DEBUG_LEVEL_INTERFACE:BUTTON_DEBUG("[SetButtonLabel]"); if(!Iter_Contains(btn_Index, buttonid)) return 0; if(IsValidDynamic3DTextLabel(btn_Data[buttonid][btn_label])) { UpdateDynamic3DTextLabelText(btn_Data[buttonid][btn_label], colour, text); return 2; } btn_Data[buttonid][btn_label] = CreateDynamic3DTextLabel(text, colour, btn_Data[buttonid][btn_posX], btn_Data[buttonid][btn_posY], btn_Data[buttonid][btn_posZ], range, _, _, testlos, btn_Data[buttonid][btn_world], btn_Data[buttonid][btn_interior], _, range); return 1; } stock DestroyButtonLabel(buttonid) { sif_d:SIF_DEBUG_LEVEL_INTERFACE:BUTTON_DEBUG("[DestroyButtonLabel]"); if(!Iter_Contains(btn_Index, buttonid)) return 0; if(!IsValidDynamic3DTextLabel(btn_Data[buttonid][btn_label])) return -1; DestroyDynamic3DTextLabel(btn_Data[buttonid][btn_label]); btn_Data[buttonid][btn_label] = Text3D:INVALID_3DTEXT_ID; return 1; } // btn_posX // btn_posY // btn_posZ stock GetButtonPos(buttonid, &Float:x, &Float:y, &Float:z) { sif_d:SIF_DEBUG_LEVEL_INTERFACE:BUTTON_DEBUG("[GetButtonPos] %d", buttonid); if(!Iter_Contains(btn_Index, buttonid)) return 0; x = btn_Data[buttonid][btn_posX]; y = btn_Data[buttonid][btn_posY]; z = btn_Data[buttonid][btn_posZ]; return 1; } stock SetButtonPos(buttonid, Float:x, Float:y, Float:z) { sif_d:SIF_DEBUG_LEVEL_INTERFACE:BUTTON_DEBUG("[SetButtonPos] %d, %f, %f, %f", buttonid, x, y, z); if(!Iter_Contains(btn_Index, buttonid)) return 0; Streamer_SetFloatData(STREAMER_TYPE_AREA, btn_Data[buttonid][btn_area], E_STREAMER_X, x); Streamer_SetFloatData(STREAMER_TYPE_AREA, btn_Data[buttonid][btn_area], E_STREAMER_Y, y); Streamer_SetFloatData(STREAMER_TYPE_AREA, btn_Data[buttonid][btn_area], E_STREAMER_Z, z); if(IsValidDynamic3DTextLabel(btn_Data[buttonid][btn_label])) { sif_d:SIF_DEBUG_LEVEL_INTERFACE_DEEP:BUTTON_DEBUG("[SetButtonPos] Updating button label"); Streamer_SetFloatData(STREAMER_TYPE_3D_TEXT_LABEL, btn_Data[buttonid][btn_label], E_STREAMER_X, x); Streamer_SetFloatData(STREAMER_TYPE_3D_TEXT_LABEL, btn_Data[buttonid][btn_label], E_STREAMER_Y, y); Streamer_SetFloatData(STREAMER_TYPE_3D_TEXT_LABEL, btn_Data[buttonid][btn_label], E_STREAMER_Z, z); } sif_d:SIF_DEBUG_LEVEL_INTERFACE_DEEP:BUTTON_DEBUG("[SetButtonPos] Updating variables"); btn_Data[buttonid][btn_posX] = x; btn_Data[buttonid][btn_posY] = y; btn_Data[buttonid][btn_posZ] = z; sif_d:SIF_DEBUG_LEVEL_INTERFACE_DEEP:BUTTON_DEBUG("[SetButtonPos] Updated to: %f, %f, %f", btn_Data[buttonid][btn_posX], btn_Data[buttonid][btn_posY], btn_Data[buttonid][btn_posZ]); return 1; } // btn_size stock Float:GetButtonSize(buttonid) { sif_d:SIF_DEBUG_LEVEL_INTERFACE:BUTTON_DEBUG("[GetButtonSize] %d", buttonid); if(!Iter_Contains(btn_Index, buttonid)) return 0.0; return btn_Data[buttonid][btn_size]; } stock SetButtonSize(buttonid, Float:size) { sif_d:SIF_DEBUG_LEVEL_INTERFACE:BUTTON_DEBUG("[SetButtonSize] %d, %f", buttonid, size); if(!Iter_Contains(btn_Index, buttonid)) return 0; Streamer_SetFloatData(STREAMER_TYPE_AREA, btn_Data[buttonid][btn_area], E_STREAMER_SIZE, size); btn_Data[buttonid][btn_size] = size; #if defined DEBUG_LABELS_BUTTON UpdateButtonDebugLabel(buttonid); #endif return 1; } // btn_world stock GetButtonWorld(buttonid) { sif_d:SIF_DEBUG_LEVEL_INTERFACE:BUTTON_DEBUG("[GetButtonWorld]"); if(!Iter_Contains(btn_Index, buttonid)) return -1; return btn_Data[buttonid][btn_world]; } stock SetButtonWorld(buttonid, world) { sif_d:SIF_DEBUG_LEVEL_INTERFACE:BUTTON_DEBUG("[SetButtonWorld]"); if(!Iter_Contains(btn_Index, buttonid)) return 0; if(btn_Data[buttonid][btn_world] != world) { Streamer_SetIntData(STREAMER_TYPE_AREA, btn_Data[buttonid][btn_area], E_STREAMER_WORLD_ID, world); if(IsValidDynamic3DTextLabel(btn_Data[buttonid][btn_label])) { Streamer_SetIntData(STREAMER_TYPE_3D_TEXT_LABEL, btn_Data[buttonid][btn_label], E_STREAMER_WORLD_ID, world); } btn_Data[buttonid][btn_world] = world; } return 1; } // btn_interior stock GetButtonInterior(buttonid) { sif_d:SIF_DEBUG_LEVEL_INTERFACE:BUTTON_DEBUG("[GetButtonInterior]"); if(!Iter_Contains(btn_Index, buttonid)) return -1; return btn_Data[buttonid][btn_interior]; } stock SetButtonInterior(buttonid, interior) { sif_d:SIF_DEBUG_LEVEL_INTERFACE:BUTTON_DEBUG("[SetButtonInterior]"); if(!Iter_Contains(btn_Index, buttonid)) return 0; if(btn_Data[buttonid][btn_interior] != interior) { Streamer_SetIntData(STREAMER_TYPE_AREA, btn_Data[buttonid][btn_area], E_STREAMER_INTERIOR_ID, interior); if(IsValidDynamic3DTextLabel(btn_Data[buttonid][btn_label])) { Streamer_SetIntData(STREAMER_TYPE_3D_TEXT_LABEL, btn_Data[buttonid][btn_label], E_STREAMER_INTERIOR_ID, interior); } btn_Data[buttonid][btn_interior] = interior; } return 1; } // btn_link stock GetButtonLinkedID(buttonid) { sif_d:SIF_DEBUG_LEVEL_INTERFACE:BUTTON_DEBUG("[GetButtonLinkedID]"); if(!Iter_Contains(btn_Index, buttonid)) return INVALID_BUTTON_ID; return btn_Data[buttonid][btn_link]; } // btn_text stock GetButtonText(buttonid, text[]) { sif_d:SIF_DEBUG_LEVEL_INTERFACE:BUTTON_DEBUG("[GetButtonText]"); if(!Iter_Contains(btn_Index, buttonid)) return 0; text[0] = EOS; strcat(text, btn_Data[buttonid][btn_text], BTN_MAX_TEXT); return 1; } stock SetButtonText(buttonid, text[]) { sif_d:SIF_DEBUG_LEVEL_INTERFACE:BUTTON_DEBUG("[SetButtonText]"); if(!Iter_Contains(btn_Index, buttonid)) return 0; btn_Data[buttonid][btn_text][0] = EOS; strcat(btn_Data[buttonid][btn_text], text, BTN_MAX_TEXT); return 1; } // btn_exData stock SetButtonExtraData(buttonid, data) { if(!Iter_Contains(btn_Index, buttonid)) return 0; btn_Data[buttonid][btn_exData] = data; #if defined DEBUG_LABELS_BUTTON UpdateButtonDebugLabel(buttonid); #endif return 1; } stock GetButtonExtraData(buttonid) { if(!Iter_Contains(btn_Index, buttonid)) return 0; return btn_Data[buttonid][btn_exData]; } // btn_Pressing stock GetPlayerPressingButton(playerid) { sif_dp:SIF_DEBUG_LEVEL_INTERFACE:BUTTON_DEBUG("[GetPlayerPressingButton]")<playerid>; if(!(0 <= playerid < MAX_PLAYERS)) return -1; return btn_Pressing[playerid]; } stock GetPlayerButtonID(playerid) { sif_dp:SIF_DEBUG_LEVEL_INTERFACE:BUTTON_DEBUG("[GetPlayerButtonID]")<playerid>; if(!IsPlayerConnected(playerid)) return INVALID_BUTTON_ID; if(Iter_Count(btn_NearIndex[playerid]) == 0) return INVALID_BUTTON_ID; new Float:x, Float:y, Float:z, curid, closestid, Float:curdistance, Float:closetsdistance = 99999.9; GetPlayerPos(playerid, x, y, z); foreach(new i : btn_NearIndex[playerid]) { curid = btn_Near[playerid][i]; curdistance = sif_Distance(x, y, z, btn_Data[curid][btn_posX], btn_Data[curid][btn_posY], btn_Data[curid][btn_posZ]); if(curdistance < closetsdistance) { closetsdistance = curdistance; closestid = curid; } } return closestid; } stock GetPlayerButtonList(playerid, list[], &size, bool:validate = false) { if(!IsPlayerConnected(playerid)) return 0; if(Iter_Count(btn_NearIndex[playerid]) == 0) return 0; // Validate whether or not the player is actually inside the areas. // Caused by a bug that hasn't been found yet, this is the quick workaround. if(validate) { foreach(new i : btn_NearIndex[playerid]) { if(!IsPlayerInDynamicArea(playerid, btn_Data[btn_Near[playerid][i]][btn_area])) { printf("ERROR: Player %d incorrectly flagged as inside button %d area, removing.", playerid, btn_Near[playerid][i]); Iter_SafeRemove(btn_NearIndex[playerid], i, i); continue; } list[size++] = btn_Near[playerid][i]; } } else { foreach(new i : btn_NearIndex[playerid]) list[size++] = btn_Near[playerid][i]; } return 1; } stock Float:GetPlayerAngleToButton(playerid, buttonid) { sif_dp:SIF_DEBUG_LEVEL_INTERFACE:BUTTON_DEBUG("[GetPlayerAngleToButton]")<playerid>; if(!Iter_Contains(btn_Index, buttonid)) return 0.0; if(!IsPlayerConnected(playerid)) return 0.0; new Float:x, Float:y, Float:z; GetPlayerPos(playerid, x, y, z); return sif_GetAngleToPoint(x, y, btn_Data[buttonid][btn_posX], btn_Data[buttonid][btn_posY]); } stock Float:GetButtonAngleToPlayer(playerid, buttonid) { sif_dp:SIF_DEBUG_LEVEL_INTERFACE:BUTTON_DEBUG("[GetButtonAngleToPlayer]")<playerid>; if(!Iter_Contains(btn_Index, buttonid)) return 0.0; if(!IsPlayerConnected(playerid)) return 0.0; new Float:x, Float:y, Float:z; GetPlayerPos(playerid, x, y, z); return sif_GetAngleToPoint(btn_Data[buttonid][btn_posX], btn_Data[buttonid][btn_posY], x, y); } /*============================================================================== Internal Functions and Hooks ==============================================================================*/ hook OnPlayerKeyStateChange(playerid, newkeys, oldkeys) { sif_dp:SIF_DEBUG_LEVEL_CALLBACKS:BUTTON_DEBUG("[OnPlayerKeyStateChange]")<playerid>; if(newkeys & 16) { if(!IsPlayerInAnyVehicle(playerid) && Iter_Count(btn_NearIndex[playerid]) > 0) { if(!IsPlayerInAnyDynamicArea(playerid)) { printf("[WARNING] Player %d is not in areas but list isn't empty. Purging list.", playerid); Iter_Clear(btn_NearIndex[playerid]); } new id, Float:x, Float:y, Float:z, Float:distance, list[BTN_MAX_INRANGE][e_button_range_data], index; GetPlayerPos(playerid, x, y, z); foreach(new i : btn_NearIndex[playerid]) { if(index >= BTN_MAX_INRANGE - 1) break; id = btn_Near[playerid][i]; distance = sif_Distance(x, y, z, btn_Data[id][btn_posX], btn_Data[id][btn_posY], btn_Data[id][btn_posZ]); if(distance > btn_Data[id][btn_size]) continue; if(!(btn_Data[id][btn_posZ] - btn_Data[id][btn_size] <= z <= btn_Data[id][btn_posZ] + btn_Data[id][btn_size])) continue; list[index][btn_buttonId] = id; list[index][btn_distance] = distance; index++; } _btn_SortButtons(list, 0, index); for(new i = index - 1; i >= 0; i--) { if(Internal_OnButtonPress(playerid, list[i][btn_buttonId])) break; } } if(oldkeys & 16) { if(btn_Pressing[playerid] != INVALID_BUTTON_ID) { CallLocalFunction("OnButtonRelease", "dd", playerid, btn_Pressing[playerid]); btn_Pressing[playerid] = INVALID_BUTTON_ID; } } } return 1; } _btn_SortButtons(array[][e_button_range_data], left, right) { new tmp_left = left, tmp_right = right, Float:pivot = array[(left + right) / 2][btn_distance], buttonid, Float:distance; while(tmp_left <= tmp_right) { while(array[tmp_left][btn_distance] > pivot) tmp_left++; while(array[tmp_right][btn_distance] < pivot) tmp_right--; if(tmp_left <= tmp_right) { buttonid = array[tmp_left][btn_buttonId]; array[tmp_left][btn_buttonId] = array[tmp_right][btn_buttonId]; array[tmp_right][btn_buttonId] = buttonid; distance = array[tmp_left][btn_distance]; array[tmp_left][btn_distance] = array[tmp_right][btn_distance]; array[tmp_right][btn_distance] = distance; tmp_left++; tmp_right--; } } if(left < tmp_right) _btn_SortButtons(array, left, tmp_right); if(tmp_left < right) _btn_SortButtons(array, tmp_left, right); } Internal_OnButtonPress(playerid, buttonid) { sif_dp:SIF_DEBUG_LEVEL_INTERNAL:BUTTON_DEBUG("[Internal_OnButtonPress]")<playerid>; if(!Iter_Contains(btn_Index, buttonid)) return 0; new id = btn_Data[buttonid][btn_link]; if(Iter_Contains(btn_Index, id)) { if(CallLocalFunction("OnButtonPress", "dd", playerid, buttonid)) return 1; TogglePlayerControllable(playerid, false); defer btn_Unfreeze(playerid); Streamer_UpdateEx(playerid, btn_Data[id][btn_posX], btn_Data[id][btn_posY], btn_Data[id][btn_posZ], btn_Data[id][btn_world], btn_Data[id][btn_interior]); SetPlayerVirtualWorld(playerid, btn_Data[id][btn_world]); SetPlayerInterior(playerid, btn_Data[id][btn_interior]); SetPlayerPos(playerid, btn_Data[id][btn_posX], btn_Data[id][btn_posY], btn_Data[id][btn_posZ]); } else { btn_Pressing[playerid] = buttonid; if(CallLocalFunction("OnButtonPress", "dd", playerid, buttonid)) return 1; } return 0; } timer btn_Unfreeze[BTN_TELEPORT_FREEZE_TIME](playerid) { TogglePlayerControllable(playerid, true); } hook OnPlayerEnterDynArea(playerid, areaid) { sif_dp:SIF_DEBUG_LEVEL_CALLBACKS:BUTTON_DEBUG("[OnPlayerEnterDynamicArea]")<playerid>; if(!IsPlayerInAnyVehicle(playerid) && Iter_Count(btn_NearIndex[playerid]) < BTN_MAX_INRANGE) { sif_dp:SIF_DEBUG_LEVEL_CALLBACKS_DEEP:BUTTON_DEBUG("[OnPlayerEnterDynamicArea] player is valid")<playerid>; new data[2]; Streamer_GetArrayData(STREAMER_TYPE_AREA, areaid, E_STREAMER_EXTRA_ID, data, 2); // Due to odd streamer behavior reversing data arrays: // Only use this if you are using streamer v2.7.1 or lower // new tmp = data[0]; // data[0] = data[1]; // data[1] = tmp; // end if(data[0] == BTN_STREAMER_AREA_IDENTIFIER) { sif_dp:SIF_DEBUG_LEVEL_CALLBACKS_DEEP:BUTTON_DEBUG("[OnPlayerEnterDynamicArea] area is valid")<playerid>; if(Iter_Contains(btn_Index, data[1])) { sif_dp:SIF_DEBUG_LEVEL_CALLBACKS_DEEP:BUTTON_DEBUG("[OnPlayerEnterDynamicArea] in index")<playerid>; new cell = Iter_Free(btn_NearIndex[playerid]); btn_Near[playerid][cell] = data[1]; Iter_Add(btn_NearIndex[playerid], cell); ShowActionText(playerid, btn_Data[data[1]][btn_text]); CallLocalFunction("OnPlayerEnterButtonArea", "dd", playerid, data[1]); } } } sif_dp:SIF_DEBUG_LEVEL_CALLBACKS_DEEP:BUTTON_DEBUG("[OnPlayerEnterDynamicArea] end")<playerid>; } hook OnPlayerLeaveDynArea(playerid, areaid) { process_LeaveDynamicArea(playerid, areaid); } process_LeaveDynamicArea(playerid, areaid) { sif_dp:SIF_DEBUG_LEVEL_CALLBACKS:BUTTON_DEBUG("[OnPlayerLeaveDynamicArea]")<playerid>; if(!IsValidDynamicArea(areaid)) { sif_dp:SIF_DEBUG_LEVEL_CALLBACKS_DEEP:BUTTON_DEBUG("[OnPlayerLeaveDynamicArea] area ID is invalid")<playerid>; return 1; } if(IsPlayerInAnyVehicle(playerid)) { sif_dp:SIF_DEBUG_LEVEL_CALLBACKS_DEEP:BUTTON_DEBUG("[OnPlayerLeaveDynamicArea] player is in vehicle")<playerid>; return 1; } if(Iter_Count(btn_NearIndex[playerid]) == 0) { sif_dp:SIF_DEBUG_LEVEL_CALLBACKS_DEEP:BUTTON_DEBUG("[OnPlayerLeaveDynamicArea] player nearindex is empty")<playerid>; return 2; } new data[2]; Streamer_GetArrayData(STREAMER_TYPE_AREA, areaid, E_STREAMER_EXTRA_ID, data, 2); // Due to odd streamer behavior reversing data arrays: // Only use this if you are using streamer v2.7.1 or lower // new tmp = data[0]; // data[0] = data[1]; // data[1] = tmp; // end if(data[0] != BTN_STREAMER_AREA_IDENTIFIER) { sif_dp:SIF_DEBUG_LEVEL_CALLBACKS_DEEP:BUTTON_DEBUG("[OnPlayerLeaveDynamicArea] area is not a button area")<playerid>; return 3; } if(!Iter_Contains(btn_Index, data[1])) { sif_dp:SIF_DEBUG_LEVEL_CALLBACKS_DEEP:BUTTON_DEBUG("[OnPlayerLeaveDynamicArea] button ID is invalid")<playerid>; return 4; } HideActionText(playerid); CallLocalFunction("OnPlayerLeaveButtonArea", "dd", playerid, data[1]); foreach(new i : btn_NearIndex[playerid]) { sif_dp:SIF_DEBUG_LEVEL_LOOPS:BUTTON_DEBUG("[OnPlayerLeaveDynamicArea] looping player list")<playerid>; if(btn_Near[playerid][i] == data[1]) { sif_dp:SIF_DEBUG_LEVEL_CALLBACKS_DEEP:BUTTON_DEBUG("[OnPlayerLeaveDynamicArea] removing from player list")<playerid>; Iter_Remove(btn_NearIndex[playerid], i); break; } } return 0; } #if defined DEBUG_LABELS_BUTTON UpdateButtonDebugLabel(buttonid) { new string[64]; format(string, sizeof(string), "EXDATA: %d SIZE: %.1f LINK: %d", btn_Data[buttonid][btn_exData], btn_Data[buttonid][btn_size], btn_Data[buttonid][btn_link]); UpdateDebugLabelString(btn_DebugLabelID[buttonid], string); } #endif /*============================================================================== Testing ==============================================================================*/ #if defined RUN_TESTS #include <SIF\testing/Button.pwn> #endif
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: 2.0 Changelog: 29/04/11: Added version 2 of the code with more advanced options. 21/03/11: Added debug printing to timer functions. Uses "P:C" in compiling. 26/10/10: Officially added simple calling. Added "delay" functions. 12/10/10: Rewrote for YSI 1.0 using y_scripting. 11/08/07: Removed millions of defines to reduce pre-processing. Added pickups. 03/08/07: First version. </remarks> \**--------------------------------------------------------------------------**/ // Disable this version! static stock Alloc:YSI_g_sLastSlot = NO_ALLOC, Alloc:YSI_g_sFirstSlot = NO_ALLOC, YSI_g_sPlayerTimers = -1; hook OnScriptInit() { P:1("hook Timers_OnScriptInit called"); new pointer, time, idx, entry; while ((idx = AMX_GetPublicEntryPrefix(idx, entry, _A<@yT_>))) //while ((idx = AMX_GetPublicPointerSuffix(idx, pointer, _A<@yT_>))) { P:6("Timer_OnScriptInit: entry: %d", entry); #emit LREF.S.pri entry #emit STOR.S.pri pointer //YSI_g_sCurFunc = pointer; // Don't bother with the real name, call the function by address to get // the time the function runs for. P:7("Timer_OnScriptInit: pointer: %d", pointer); // Push the address of the current function. #emit PUSH.S pointer #emit PUSH.C 0xFFFFFFFF #emit PUSH.C 8 #emit LCTRL 6 #emit ADD.C 28 #emit PUSH.pri #emit LOAD.S.pri pointer #emit SCTRL 6 #emit STOR.S.pri time //YSI_g_sCurFunc = 0; P:7("Timer_OnScriptInit: time: %d", time); if (time != -1) { // Find all the functions with the same time. This is less // efficient than previous implementations (it is O(N^2)), but also // more robust as it won't fail no matter how many different times // there are - old ones relied on an array with a finite size. new pointer2, time2, idx2, total, pre; while ((idx2 = AMX_GetPublicPointerPrefix(idx2, pointer2, _A<@yT_>))) { // Call the functions a second time to guarantee getting #emit PUSH.C 0 #emit PUSH.C 0xFFFFFFFF #emit PUSH.C 8 #emit LCTRL 6 #emit ADD.C 28 #emit PUSH.pri #emit LOAD.S.pri pointer2 #emit SCTRL 6 #emit STOR.S.pri time2 // Check if the new time is a FACTOR, SAME, or MULTIPLE of this // task, so we don't start different timers together. if (time2 == time || time / time2 * time2 == time || time2 / time * time == time2) { ++total; if (idx2 < idx) { ++pre; } } } P:7("Timer_OnScriptInit: total: %d, time: %d, pre: %d", total, time, pre); // Now we know what time this function has, how many others have // that time and how many have already been started. new buffer[32]; entry += 4; #emit LREF.S.pri entry #emit STOR.S.pri pointer AMX_ReadString(AMX_BASE_ADDRESS + pointer, buffer); P:7("Timer_OnScriptInit: %s", unpack(buffer)); // Get the time offset for the current call. This should mean that // all the functions are nicely spread out. SetTimerEx(buffer, time * pre / total, 0, "ii", 1, -1); } } P:1("hook Timers_OnScriptInit ended"); return 1; } hook OnPlayerConnect(playerid) { P:1("hook Timers_OnPlayerConnect called: %d", playerid); // Loop through all the per-player timers. Correctly finds them all from a // linked list hidden in static variables (which are really global). new cur = YSI_g_sPlayerTimers, data; while (cur != -1) { #emit LREF.S.pri cur #emit STOR.S.pri data P:6("Timers_OnPlayerConnect: func: %x", data); // Start this timer for this player. #emit PUSH.S playerid #emit PUSH.C 1 // Push the parameter count (in bytes). This is actually passed to // native functions directly. #emit PUSH.C 8 // Call the function currently in the list to trigger the repeating // timer. This involves getting the current "cip" address, modifying it // to get the return address then modifying "cip" to call the function. #emit LCTRL 6 #emit ADD.C 28 #emit PUSH.pri #emit LOAD.S.pri data #emit SCTRL 6 // Returned, get the next list element. cur += 4; #emit LREF.S.pri cur #emit STOR.S.pri cur } P:1("hook Timers_OnPlayerConnect ended"); return 1; } hook OnPlayerDisconnect(playerid, reason) { P:1("hook Timers_OnPlayerDisconnect called: %d, %d, playerid, reason"); // Loop through all the per-player timers. Correctly finds them all from a // linked list hidden in static variables (which are really global). new cur = YSI_g_sPlayerTimers, data; while (cur != -1) { #emit LREF.S.pri cur #emit STOR.S.pri data P:6("Timers_OnPlayerDisconnect: func: %x", data); // End this timer for this player. #emit PUSH.S playerid #emit PUSH.C 0 // Push the parameter count (in bytes). This is actually passed to // native functions directly. #emit PUSH.C 8 // Call the function currently in the list to trigger the repeating // timer. This involves getting the current "cip" address, modifying it // to get the return address then modifying "cip" to call the function. #emit LCTRL 6 #emit ADD.C 28 #emit PUSH.pri #emit LOAD.S.pri data #emit SCTRL 6 // Returned, get the next list element. cur += 4; #emit LREF.S.pri cur #emit STOR.S.pri cur } P:1("hook Timers_OnPlayerDisconnect ended"); return 1; } stock _Timer_I(func[], interval, action, &result) { P:3("_Timer_I called"); switch (action) { case 0: { if (result != -1) { KillTimer(result), result =- 1; } } case 1: { if (result == -1) { result = SetTimer(func, interval, 1); } } } return interval; } // Attempt to stop or start a task, possibly for a single player. stock _Timer_D(func[], interval, const action, who, results[MAX_PLAYERS], a[2]) { P:3("_Timer_D called"); switch (action) { case -1: { if (who) { // Add this timer to the global linked list. a[0] = who; a[1] = YSI_g_sPlayerTimers; // Store the address of the global array. #emit LOAD.S.pri a #emit STOR.pri YSI_g_sPlayerTimers } } case 0: { // Stop the timer. if (who == -1) { foreach (who : Player) { if (results[who] != -1) { KillTimer(results[who]); results[who] = -1; } } } else if (results[who] != -1) { KillTimer(results[who]); results[who] = -1; } } case 1: { // Start the timer. if (who == -1) { foreach (who : Player) { if (results[who] == -1) { results[who] = SetTimerEx(func, interval, true, "i", who); } } } else if (results[who] == -1) { results[who] = SetTimerEx(func, interval, true, "i", who); } } } // No global interval for per-player timers. return -1; } static stock Alloc:Timer_GetSingleSlot(len) { // Allocates memory and secretly appends data to the start. P:4("Timer_GetSingleSlot called: %d", len); new Alloc:slot = malloc(len + 1); if (slot == NO_ALLOC) { return NO_ALLOC; } P:5("Timer_GetSingleSlot: %d, %d, %d", _:YSI_g_sFirstSlot, _:YSI_g_sLastSlot, _:slot); // Standard linked list. if (YSI_g_sFirstSlot == NO_ALLOC) { YSI_g_sFirstSlot = slot; } else { mset(YSI_g_sLastSlot, 0, _:slot); } YSI_g_sLastSlot = slot; mset(YSI_g_sLastSlot, 0, -1); return slot;// + Alloc:1; } // Allocate memory to store a string. stock _Timer_S(string:str[]) { P:3("_Timer_S called"); new len = strlen(str); if (len & 0x0F) { len = (len & ~0x0F) + 32; } new Alloc:slot = Timer_GetSingleSlot(len + 1); if (slot != NO_ALLOC) { msets(slot, 1, str); } P:5("str: %d", _:slot); return _:slot + 1; } // Allocate memory to store an array. stock _Timer_A(str[], len) { P:3("_Timer_A called"); new Alloc:slot = Timer_GetSingleSlot(len); if (slot != NO_ALLOC) { mseta(slot, 1, str, len); } P:5("str: %d", _:slot); return _:slot + 1; } //stock // I@ = -1; // Create the timer setup. stock _Timer_C(tt, g) { P:3("_Timer_C called: %d, %d", tt, g); //P:3("_Timer_C called: %d", tt); // This is done here for convenience. I@ = -1; // Only repeating timers are freed like this. // UPDATE: Now all timers with array parameters, regardless of repeat status // are freed like this. Only timers with no malloc aren't. if (g) { new Alloc:slot = Timer_GetSingleSlot(1); P:5("_Timer_C: slot = %d", _:slot); if (slot == NO_ALLOC) { // Not a graceful fail! return 0; } mset(slot, 1, tt); // Just so it isn't a real timer ID (or possibly isn't). slot = ~YSI_g_sFirstSlot;// ^ Alloc:-1; YSI_g_sFirstSlot = NO_ALLOC; YSI_g_sLastSlot = NO_ALLOC; return _:slot; } // Reset these variables on all timers, including self-cleaning ones. YSI_g_sFirstSlot = NO_ALLOC; YSI_g_sLastSlot = NO_ALLOC; return tt; } // Free all timer resources. stock _Timer_F(slot) { P:3("_Timer_F called"); // This is done here for convenience. if (slot & 0x80000000) { new next; slot = ~slot; //^= -1; for ( ; ; ) { next = mget(Alloc:slot, 0); P:6("_Timer_F: slot = %d, next = %d", slot, next); // Out of stored strings and arrays. if (next == -1) { KillTimer(mget(Alloc:slot, 1)); free(Alloc:slot); break; } free(Alloc:slot); slot = next; } } else { KillTimer(slot); } return 1; } stock _Timer_H(slot) { _Timer_F(~(slot - 1)); } #define task%0[%1](%2) @yT_%0(g,p);@yT_%0(g,p){static s=-1;return _Timer_I(#%0,%1,g,s);}%0();public%0() // #define ptask%0[%1](%2) @yT_%0(g,p);@yT_%0(g,p){static s[MAX_PLAYERS]={-1,...},a[2];return _Timer_D(#%0,%1,g,p,s,a);}%0(%2);public%0(%2) #define @yT_%0\32;%1(%2) @yT_%0%1(%2) //#define @_yT%0;\32%1(%2) @_yT%0%1(%2) #define pause%0; {J@=_:@Ym:@yT_%0(0,-1);} #define resume%0; {J@=_:@Ym:@yT_%0(1,-1);} #define @Ym:%0[%1](%2,-1) %0(%2,%1) #define timerfunc YSI_timer #if !defined YSI_NO_timer #define timer YSI_timer #endif