-- Exclude the following functions from automatic documentation generation 
 
--[[DocGenExclude: 
 
character_is_ready_to_speak 
 
group_create_check_done_loop 
 
helicopter_fly_to_internal 
 
]] 
 
 
 
-------------------- 
 
-- Script Globals -- 
 
-------------------- 
 
 
 
-- floating_values_types in autil_hud.h 
 
FLOATING_VALUE_CASH				= 0 
 
FLOATING_VALUE_POINTS			= 1 
 
FLOATING_VALUE_TIME				= 2 
 
FLOATING_VALUE_BONUS_TIME		= 3 
 
FLOATING_VALUE_CUSTOM_TEXT		= 4 
 
 
 
 
 
MINIMAP_ICON_KILL								= "icon_class_kill" 
 
MINIMAP_ICON_PROTECT_ACQUIRE				= "icon_class_protectacquire" 
 
MINIMAP_ICON_LOCATION						= "icon_class_location" 
 
MINIMAP_ICON_USE								= "icon_class_use" 
 
MINIMAP_ICON_SAINTS							= "icon_class_saints" 
 
MINIMAP_ICON_SYNDICATE						= "icon_class_syndicate" 
 
MINIMAP_ICON_STOMP							= "icon_class_stomp" 
 
MINIMAP_ICON_BLAST							= "icon_class_blast" 
 
MINIMAP_ICON_BUFF								= "icon_class_buff" 
 
MINIMAP_ICON_TELE								= "icon_class_tele" 
 
MINIMAP_ICON_SUMMON								= MINIMAP_ICON_TELE -- Since we replaced tk, we'll just assign it the same value, in case there are any other scripts referencing this. 
 
MINIMAP_ICON_DLC_XMAS_GIFT						= "icon_class_xmas_gift" 
 
MINIMAP_ICON_DLC_XMAS_NAUGHTY					= "icon_class_xmas_naughty" 
 
 
 
INGAME_EFFECT_LOCATION						= "vfx_missioncheckPointicon" 
 
INGAME_EFFECT_CUTSCENE						= "mission_purchase" 
 
INGAME_EFFECT_MP_TUTORIAL					= "mission_complete_mp" 
 
 
 
INGAME_EFFECT_VEHICLE_INTERACT			= "Icon_lg_b" 
 
INGAME_EFFECT_VEHICLE_PROTECT_ACQUIRE	= "Icon_lg_c" 
 
INGAME_EFFECT_VEHICLE_KILL					= "Icon_lg_d" 
 
 
 
INGAME_EFFECT_VEHICLE_LOCATION			= "vfx_missioncheckPointCar" 
 
INGAME_EFFECT_VEHICLE_CUTSCENE			= "vfx_missioncheckPointCar" 
 
 
 
INGAME_EFFECT_CHECKPOINT					= "vfx_missioncheckPointicon" 
 
 
 
-- Object Indicator Assets 
 
OI_ASSET_INVALID	  = -1 
 
OI_ASSET_KILL		  = 0 
 
OI_ASSET_DEFEND	  = 1 
 
OI_ASSET_USE		  = 2 
 
OI_ASSET_REVIVE	  = 3 
 
OI_ASSET_LOCATION	  = 4 
 
OI_ASSET_COOP		  = 5 
 
OI_ASSET_KILL_FULL  = 6 
 
OI_ASSET_FINSHER    = 7 -- Not used in lua, just here so we stay in sync with C. 
 
OI_ASSET_KILL_TIRES = 8 
 
OI_ASSET_HITMAN     = 9 
 
OI_ASSET_CHOP_SHOP  = 10 
 
OI_ASSET_ALL        = 11 -- Not used in lua, just here so we stay in sync with C. 
 
OI_ASSET_SYNDICATE  = 12 
 
OI_ASSET_GRENADE 		= 13 
 
OI_ASSET_FINISHER_WARDEN 	= 14 -- Not used in lua, just here so we stay in sync with C. 
 
OI_ASSET_GT_HUMAN 	= 15 -- Not used in lua, just here so we stay in sync with C. 
 
OI_ASSET_GT_MOVER 	= 16 -- Not used in lua, just here so we stay in sync with C. 
 
OI_ASSET_GT_VEHICLE 	= 17 -- Not used in lua, just here so we stay in sync with C. 
 
OI_ASSET_POWER_TELE	= 18 
 
OI_ASSET_POWER_SUMMON = OI_ASSET_POWER_TELE -- Since we replaced tk, we'll just assign it the same value, in case there are any other scripts referencing this. 
 
OI_ASSET_CID = 19 
 
OI_ASSET_POWER_STOMP = 20 
 
OI_ASSET_POWER_FREEZE = 21 
 
OI_ASSET_POWER_BLAST	= 22 
 
OI_ASSET_POWER_BUFF	= 23 
 
OI_ASSET_DLC_XMAS_GIFT    = 24 
 
OI_ASSET_DLC_XMAS_NAUGHTY = 25 
 
 
 
-- This must match the flags defined in object_indicators.h 
 
OI_FLAG_NONE					= 0x00 
 
OI_FLAG_STICKY					= 0x01 
 
OI_FLAG_DISPLAY_DISTANCE	= 0x02 
 
OI_FLAG_PULSE					= 0x04 
 
OI_FLAG_FADE					= 0x08 
 
OI_FLAG_PARTIAL_HIDE			= 0x10 
 
OI_FLAGS_DEFAULT				= OI_FLAG_STICKY + OI_FLAG_DISPLAY_DISTANCE + OI_FLAG_PARTIAL_HIDE 
 
OI_FLAGS_LOCATION				= OI_FLAG_STICKY + OI_FLAG_DISPLAY_DISTANCE + OI_FLAG_PULSE 
 
OI_FLAGS_FULL					= OI_FLAG_STICKY + OI_FLAG_DISPLAY_DISTANCE 
 
 
 
SYNC_LOCAL	= 1 
 
SYNC_REMOTE = 2 
 
SYNC_ALL		= 3 
 
 
 
VAULT_SEQUENCE = 0 
 
BALL_SEQUENCE = 1 
 
 
 
MAX_NOTORIETY_LEVEL	= 6 
 
MAX_NOTORIETY_NO_WARDENS = 5 
 
INVALID_THREAD_HANDLE = -1 
 
INVALID_CONVERSATION_HANDLE = -1 
 
INVALID_PERSONA_HANDLE = 0 
 
INVALID_MESSAGE_HANDLE = -1  
 
INVALID_HUD_DISPLAY_STATE = -1 
 
INVALID_EFFECT_HANDLE = -1 
 
 
 
MISSION_START_CHECKPOINT = "mission start" 
 
 
 
LOCAL_PLAYER	= "#PLAYER1#" 
 
REMOTE_PLAYER	= "#PLAYER2#" 
 
CLOSEST_PLAYER = "#CLOSEST_PLAYER#" 
 
CLOSEST_TEAM1	= "#CLOSEST_TEAM1#" 
 
CLOSEST_TEAM2	= "#CLOSEST_TEAM2#" 
 
FOLLOWER1		= "#FOLLOWER1#" 
 
FOLLOWER2		= "#FOLLOWER2#" 
 
FOLLOWER3		= "#FOLLOWER3#" 
 
 
 
PLAYER_TAG_LIST = {  
	"#PLAYER1#", 
 
	"#PLAYER2#", 
 
	"#PLAYER3#", 
 
	"#PLAYER4#", 
 
	"#PLAYER5#", 
 
	"#PLAYER6#", 
 
	"#PLAYER7#", 
 
	"#PLAYER8#" 
 
} 
 
 
 
WEAPON_SLOT_NONE			= -1 
 
WEAPON_SLOT_UNARMED		= 0 
 
WEAPON_SLOT_MELEE			= 1 
 
WEAPON_SLOT_PISTOL		= 2 
 
WEAPON_SLOT_SMG			= 3 
 
WEAPON_SLOT_SHOTGUN		= 4 
 
WEAPON_SLOT_RIFLE			= 5 
 
WEAPON_SLOT_EXPLOSIVE	= 6 
 
WEAPON_SLOT_SPECIAL		= 7 
 
 
 
Mission_waypoint		= -1 
 
 
 
VT_AUTOMOBILE	= 0 
 
VT_MOTORCYCLE	= 1 
 
VT_AIRPLANE		= 2 
 
VT_HELICOPTER	= 3 
 
VT_VTOL			= 4 
 
VT_WATERCRAFT	= 5 
 
-- This means the player isn't in a vehicle. 
 
VT_NONE			= 6 
 
 
 
IS_MOVER		= 1 
 
IS_ITEM			= 2 
 
INDETERMINATE	= 3 
 
 
 
VST_AMBULANCE = 0 
 
VST_BUS = 1 
 
VST_FBI = 2 
 
VST_FIRETRUCK = 3 
 
VST_INDUSTRIAL = 4 
 
VST_METER_MAID = 5 
 
VST_NEWS_VAN = 6 
 
VST_POLICE = 7 
 
VST_SWAT_VAN = 8 
 
VST_TAXI = 9 
 
VST_TOW_TRUCK = 10 
 
VST_LIMO = 11 
 
VST_ATV = 12 
 
VST_TANK = 14 
 
-- This player's vehicle isn't special or 
 
-- this player isn't in a vehicle. 
 
VST_NONE = 13 
 
 
 
-- Gender types 
 
GT_NONE = 0 
 
GT_MALE = 1 
 
GT_FEMALE = 2 
 
 
 
-- Enable/Disable 
 
ENABLE = true 
 
DISABLE = false 
 
 
 
-- Trigger event state tracker globals 
 
UPDATE_DEACTIVATED = 1 
 
ALL_PLAYERS_DEACTIVATED = 2 
 
ANY_PLAYER_DEACTIVATED = 3 
 
UPDATE_ACTIVATED = 4 
 
ALL_PLAYERS_ACTIVATED = 5 
 
ALL_PLAYERS_ACTIVATED_RESET = 6 
 
TRIGGER_MULTIPLE_TIMES = 7 
 
TRIGGER_ONCE_FIRST_TRIGGERER = 8 
 
TRIGGER_ONCE_PER_PLAYER = 9 
 
 
 
-- Parameters for use with npc_set_boss_ai 
 
AI_BOSS_JYUNICHI = "Jyunichi" 
 
AI_BOSS_AKUJI = "Akuji" 
 
 
 
START_FADE_OUT_TIME = 1.0 
 
START_FADE_IN_TIME = 2.0 
 
DEFAULT_END_DELAY_TIME = 2.0 
 
DEFAULT_END_FADE_OUT_TIME = 3.0 
 
 
 
-- Persona override types 
 
POT_ATTACK				= 1 
 
POT_TAKE_DAMAGE		= 2 
 
-- POT_TAUNT_NEGATIVE	= 3 (PA 7-14-08. There is no longer code-side support for this override type.) 
 
POT_CUSTOM_1			= 4 
 
POT_CUSTOM_2			= 5 
 
POT_PRAISED_BY_PC		= 6 
 
POT_TAUNTED_BY_PC		= 7 
 
POT_BARTER				= 8 
 
POT_GRATS_PC			= 9 
 
POT_GRATS_SELF			= 10 
 
POT_HIT_CAR				= 11 
 
POT_HIT_OBJ				= 12 
 
POT_HIT_PED				= 13 
 
 
 
-- Build types 
 
BUILD_TYPE_NORMAL			= 1 
 
BUILD_TYPE_AUSTRALIAN	= 2 
 
BUILD_TYPE_GERMAN			= 3 
 
BUILD_TYPE_JAPANESE_360	= 4 
 
BUILD_TYPE_JAPANESE_PS3	= 5 
 
 
 
-- Persona situations associated w/ each override type 
 
POT_SITUATIONS = {  
	[POT_ATTACK]			=	{"threat - alert (group attack)",  
									 "threat - alert (solo attack)"}, 
 
	[POT_TAKE_DAMAGE]		=	{"take damage",   
									 "threat - damage received (firearm)", 
 
									 "threat - damage received (melee)", 
 
									 "threat - damage received (vehicle)"}, 
 
	[POT_CUSTOM_1]			=	{"custom line 1"},  
	[POT_CUSTOM_2]			=	{"custom line 2"},  
	[POT_PRAISED_BY_PC]	=	{"observe - praised by pc"},  
	[POT_TAUNTED_BY_PC]	=	{"misc - respond to player taunt w/taunt"},  
	[POT_BARTER]			=	{"hostage - barters"},  
	[POT_GRATS_PC]			=	{"combat - congratulate player"},  
	[POT_GRATS_SELF]		=	{"combat - congratulate self"},  
	[POT_HIT_CAR]			=	{"observe - passenger when driver hits cars"},  
	[POT_HIT_OBJ]			=	{"observe - passenger when driver hits object"},  
	[POT_HIT_PED]			=	{"observe - passenger when driver hits peds"}  
} 
 
 
 
-- Persona situation overrides, correlates to enum persona_situation_override 
 
PS_OVERRIDE_MISSION01 = 1 
 
PS_OVERRIDE_MISSION07 = 2 
 
PS_OVERRIDE_MISSION17 = 3 
 
PS_OVERRIDE_HELI_ACTIVITY = 4 
 
PS_OVERRIDE_MISSION03 = 5 
 
PS_OVERRIDE_SH02 = 6 
 
PS_OVERRIDE_ESCORT = 7 
 
PS_OVERRIDE_SH02_BRUTE = 8 
 
 
 
-- Constants use by the audio_play_conversation function. 
 
-- These indices are the indices of each segment of a dialog stream 
 
-- in the dialog stream table. 
 
DIALOG_STREAM_AUDIO_NAME_INDEX = 1 
 
DIALOG_STREAM_CHAR_NAME_INDEX = 2 
 
DIALOG_STREAM_DELAY_SECONDS_INDEX = 3 
 
DIALOG_STREAM_ANIM_ACTION_INDEX = 4 
 
 
 
-- Types of conversations 
 
NOT_CALL = 1 
 
OUTGOING_CALL = 2 
 
INCOMING_CALL = 3 
 
 
 
-- Used in a dialog stream to denote that this is a character speaking through the cellphone 
 
CELLPHONE_CHARACTER  = "cellphone character" 
 
 
 
-- Ring sounds 
 
CELLPHONE_INCOMING = "SYS_CELL_RING_1" 
 
CELLPHONE_OUTGOING = "SYS_CELL_RING_OTHER" 
 
 
 
-- Screen fade styles 
 
SFX_FADE_LEFT_TO_RIGHT = 0 
 
SFX_FADE_ALPHA_FADE = 1 
 
 
 
-- Screen fade types 
 
SFX_FADE_TYPE_DEFAULT = 0 
 
SFX_FADE_TYPE_CRIB = 1 
 
SFX_FADE_TYPE_WORLD = 2 
 
SFX_FADE_TYPE_NIGHTMARE = 3 
 
 
 
-- Damaged by types (from damaged_by_types enum) 
 
DBT_NONE			= -1 
 
DBT_CAR				= 0		-- collision with vehicle 
 
DBT_EXPLOSION		= 1		-- explosion-type weapon 
 
DBT_BULLET			= 2		-- bullet-type weapon 
 
DBT_LEVEL_OBJECT	= 3		-- collision with mover 
 
DBT_MELEE			= 4		-- melee-type weapon 
 
DBT_WORLD			= 5		-- collision with world 
 
DBT_HUMAN			= 6		-- collision with human 
 
DBT_FIRE			= 7		-- fire or flame-type weapon 
 
DBT_WIND			= 8		-- helicopter downdraft 
 
DBT_SONIC			= 9		-- sonic-type weapon 
 
DBT_LASER			= 10	-- laser-type weapon 
 
DBT_KNEECAPPER		= 11	-- vehicle kneecapper 
 
DBT_FORCED			= 12	-- hard-coded or scripted damage 
 
DBT_DFA				= 13	-- death from above 
 
DBT_STOMP			= 14	-- super stomp 
 
DBT_ENERGY_BLAST	= 15	-- energy blast 
 
 
 
-- Gang persona tables 
 
-- 
 
--	The key of each entry is the persona, and the value is the tag prfix of its situation triggers. 
 
-- 
 
-- These tables are used in conjunction with persona_override_group_start and persona_override_group_stop. 
 
 
 
BROTHERHOOD_PERSONAS = {  
	["HM_Bro1"]	=	"HMBRO1", 
 
	["HM_Bro2"]	=	"HMBRO2", 
 
	["HM_Bro3"]	=	"HMBRO3", 
 
 
 
	["HF_Bro1"]	=	"HFBRO1", 
 
	["HF_Bro2"]	=	"HFBRO2", 
 
 
 
	["WM_Bro1"]	=	"WMBRO1", 
 
	["WM_Bro2"]	=	"WMBRO2", 
 
	["WM_Bro3"]	=	"WMBRO3", 
 
 
 
	["WF_Bro1"]	=	"WFBRO1", 
 
	["WF_Bro2"]	=	"WFBRO2", 
 
} 
 
 
 
RONIN_PERSONAS	= {  
	["AM_Ron1"]	=	"AMRON1", 
 
	["AM_Ron2"]	=	"AMRON2", 
 
	["AM_Ron3"]	=	"AMRON3", 
 
 
 
	["AF_Ron1"]	=	"AFRON1", 
 
	["AF_Ron2"]	=	"AFRON2", 
 
	["AF_Ron3"]	=	"AFRON3", 
 
 
 
	["WM_Ron1"]	=	"WMRON1", 
 
	["WM_Ron2"]	=	"WMRON2", 
 
 
 
	["WF_Ron1"]	=	"WFRON1", 
 
	["WF_Ron2"]	=	"WFRON2", 
 
} 
 
 
 
SAINTS_PERSONAS = {  
	["AM_TSS1"]	=	"AMTSS1", 
 
	["AM_TSS2"]	=	"AMTSS2", 
 
	["AM_TSS3"]	=	"AMTSS3", 
 
 
 
	["AF_TSS1"]	=	"AFTSS1", 
 
	["AF_TSS2"]	=	"AFTSS2", 
 
	["AF_TSS3"]	=	"AFTSS3", 
 
 
 
	["BM_TSS1"]	=	"BMTSS1", 
 
	["BM_TSS2"]	=	"BMTSS2", 
 
	["BM_TSS3"]	=	"BMTSS3", 
 
 
 
	["BF_TSS1"]	=	"BFTSS1", 
 
	["BF_TSS2"]	=	"BFTSS2", 
 
	["BF_TSS3"]	=	"BFTSS3", 
 
 
 
	["HM_TSS1"]	=	"HMTSS1", 
 
	["HM_TSS2"]	=	"HMTSS2", 
 
	["HM_TSS3"]	=	"HMTSS3", 
 
 
 
	["HF_TSS1"]	=	"HFTSS1", 
 
	["HF_TSS2"]	=	"HFTSS2", 
 
	["HF_TSS3"]	=	"HFTSS3", 
 
 
 
	["WM_TSS1"]	=	"WMTSS1", 
 
	["WM_TSS2"]	=	"WMTSS2", 
 
	["WM_TSS3"]	=	"WMTSS3", 
 
 
 
	["WF_TSS1"]	=	"WFTSS1", 
 
	["WF_TSS2"]	=	"WFTSS2", 
 
	["WF_TSS3"]	=	"WFTSS3", 
 
} 
 
 
 
SAMEDI_PERSONAS = {  
	["BM_SoS2"]	=	"BMSOS2", 
 
 
 
	["BF_SoS1"]	=	"BFSOS1", 
 
	["BF_SoS2"]	=	"BFSOS2", 
 
	["BF_SoS3"]	=	"BFSOS3", 
 
 
 
	["WM_SoS2"]	=	"WMSOS2", 
 
 
 
	["WF_SoS1"]	=	"WFSOS1", 
 
} 
 
 
 
COP_PERSONAS = {  
--	["AM_Cop"]	=	"AMCOP",		Currently has no mission specific lines 
 
	["AF_Cop"]	=	"AFCOP", 
 
	 
 
	["BM_Cop"]	=	"BMCOP", 
 
	["BF_Cop"]	=	"BFCOP", 
 
	 
 
	["HM_Cop"]	=	"HMCOP", 
 
	["HF_Cop"]	=	"HFCOP", 
 
	 
 
	["WM_Cop"]	=	"WMCOP", 
 
	["WF_Cop"]	=	"WFCOP", 
 
} 
 
 
 
BUM_PERSONAS = {  
	["BM_Hobo"]		=	"BMHOBO1", 
 
	["BF_Hobo"]		=	"BFHOBO1", 
 
	 
 
	["HM_Hobo"]		=	"HMHOBO1", 
 
	["HF_Hobo"]		=	"HFHOBO1", 
 
	 
 
	["WM_Hobo1"]	=	"WMHOBO1", 
 
	["WM_Hobo2"]	=	"WMHOBO2", 
 
	 
 
	["WF_Hobo1"]	=	"WFHOBO1", 
 
	["WF_Hobo2"]	=	"WFHOBO2", 
 
} 
 
 
 
-- Human teams ( gangs ) 
 
HUMAN_TEAM_BROTHERHOOD		= 1 
 
HUMAN_TEAM_RONIN				= 2 
 
HUMAN_TEAM_SAMEDI				= 3 
 
 
 
-- AI Atttack modes 
 
ATTACK_ON_SIGHT = 0 
 
ATTACK_NOW = 1 
 
ATTACK_NOW_NEVER_LOSE = 2 
 
 
 
-- Level light toggle styles 
 
LEVEL_LIGHT_TOGGLE_STYLE_POP = 0 
 
LEVEL_LIGHT_TOGGLE_STYLE_FADE = 1 
 
LEVEL_LIGHT_TOGGLE_STYLE_FLICKER = 2 
 
 
 
-- Spawn Region Filters 
 
SRF_DEFAULT = 1 
 
SRF_1 = 2 
 
SRF_2 = 4 
 
SRF_3 = 8 
 
SRF_4 = 16 
 
SRF_5 = 32 
 
SRF_6 = 64 
 
SRF_7 = 128 
 
SRF_ALL = 255 
 
 
 
-- Global Variables -- 
 
Player_controls_disabled_by_mission_start_fadeout = false 
 
 
 
-- HUD Elements -- 
 
HUD_ELEM_GSI 					= 0 
 
HUD_ELEM_MINIMAP 				= 1 
 
HUD_ELEM_RETICLE 				= 2 
 
HUD_ELEM_HIT_INDICATORS 		= 3 
 
HUD_ELEM_WEAPONS 				= 4 
 
HUD_ELEM_CASH_RESPECT_HOMEY 	= 5 
 
HUD_ELEM_WEAPON_SWAP_MESSAGE 	= 6 
 
HUD_ELEM_RADIO_STATION 			= 7 
 
HUD_ELEM_VEHICLE_NAME 			= 8 
 
HUD_ELEM_DIVERSIONS 			= 9 
 
HUD_ELEM_MESSAGES 				= 10 
 
HUD_ELEM_TUTORIAL_HELP 			= 11 
 
HUD_ELEM_AMMO_PICKUP			= 12 
 
HUD_ELEM_COLLECTION				= 13 
 
HUD_ELEM_SUPER_REWARD			= 14 
 
HUD_ELEM_MECH					= 15 
 
 
 
-- Must be explicitly hidden 
 
HUD_ELEM_SCREEN_FX 				= 16 
 
HUD_ELEM_SUBTITLES 				= 17 
 
HUD_ELEM_OI 					= 18 
 
HUD_ELEM_RADIAL					= 19 
 
 
 
HUD_ALL_ELEM 					= 20 
 
 
 
 
 
-- HUD Fade Levels -- 
 
HUD_FADE_HIDDEN = 0 
 
HUD_FADE_TRANSPARENT = 1 
 
HUD_FADE_VISIBLE = 2 
 
HUD_FADE_NO_CHANGE = 3 
 
 
 
-- Helicopter pathfinding orientations 
 
HELI_PF_FACE_ALONG_PATH = 0 
 
HELI_PF_FACE_TARGET = 1 
 
HELI_PF_LEFT_SIDE_FACE_TARGET = 2 
 
HELI_PF_RIGHT_SIDE_FACE_TARGT = 3 
 
HELI_PF_FACE_DOWN_AT_TARGET = 4 
 
HELI_PF_PATH_PITCH = 5 
 
HELI_PF_LEFT_WING_UP = 6 
 
 
 
-- Upgraded weapon levels 
 
WEAPON_LEVEL1 = 0x0		-- no upgrades 
 
WEAPON_LEVEL2 = 0x1		-- first upgrade 
 
WEAPON_LEVEL3 = 0x3		-- first and second upgrades 
 
WEAPON_LEVEL4 = 0x7		-- first second and third upgrades 
 
 
 
-- Character Gender Types 
 
GENDER_TYPE_MALE = 0 
 
GENDER_TYPE_FEMALE = 1 
 
 
 
-- Hit Locations 
 
HIT_NONE 			= 0 
 
HIT_GENERAL 		= 1 
 
HIT_TORSO 			= 2 
 
HIT_PELVIS 			= 3 
 
HIT_LEFT_ARM 		= 4 
 
HIT_RIGHT_ARM 		= 5 
 
HIT_HEAD 			= 6 
 
HIT_LEFT_LEG 		= 7 
 
HIT_RIGHT_LEG 		= 8 
 
HIT_RIOT_SHIELD 	= 9 
 
HIT_ARMOR 			= 10 
 
HIT_LOWER_BODY		= 11 
 
HIT_EXPLOSIVE		= 12 
 
 
 
-- Shop types 
 
SHOP_TYPE_CLOTHING 			= 0 
 
SHOP_TYPE_WEAPON 			= 1 
 
SHOP_TYPE_PLASTIC_SURGEON 	= 2 
 
SHOP_TYPE_JEWELRY 			= 3 
 
SHOP_TYPE_TATTOO 			= 4 
 
SHOP_TYPE_MECHANIC 			= 5 
 
SHOP_TYPE_VEHICLE_DEALER 	= 6 
 
SHOP_TYPE_MISC_PROPERTY 	= 7 
 
 
 
-- player superpowers 
 
SUPERPOWER_SUMMON = 0 
 
SUPERPOWER_STOMP = 1 
 
SUPERPOWER_BUFF = 2 
 
SUPERPOWER_BLAST = 3 
 
 
 
-- elements 
 
PLAYER_SUPERPOWER_ELEMENT_NONE = -1 
 
 
 
PLAYER_SUPERPOWER_SUMMON_IMP = 0 
 
PLAYER_SUPERPOWER_SUMMON_TOWER = 1 
 
PLAYER_SUPERPOWER_SUMMON_TITAN = 2 
 
 
 
PLAYER_SUPERPOWER_STOMP_FORCE = 3 
 
PLAYER_SUPERPOWER_STOMP_VACUUM = 4 
 
PLAYER_SUPERPOWER_STOMP_FLASH = 5 
 
 
 
PLAYER_SUPERPOWER_BLAST_STONE = 6 
 
PLAYER_SUPERPOWER_BLAST_SOUL = 7 
 
PLAYER_SUPERPOWER_BLAST_SHADOW = 8 
 
 
 
PLAYER_SUPERPOWER_AURA_COLDFIRE = 9 
 
PLAYER_SUPERPOWER_AURA_VAMP = 10 
 
PLAYER_SUPERPOWER_AURA_WORSHIP = 11 
 
 
 
--PLAYER_SUPERPOWER_TK_FORCE = 12 
 
--PLAYER_SUPERPOWER_TK_LIGHTNING = 13 
 
--PLAYER_SUPERPOWER_TK_LIFE_STEAL = 14 
 
 
 
PLAYER_SUPERPOWER_TK_EXPLODE 		= 12 
 
PLAYER_SUPERPOWER_STOMP_EXPLODE 	= 13 
 
PLAYER_SUPERPOWER_BLAST_EXPLODE 	= 14 
 
PLAYER_SUPERPOWER_BUFF_EXPLODE 		= 15 
 
 
 
PLAYER_SUPERPOWER_TK_BLING 			= 16 
 
PLAYER_SUPERPOWER_STOMP_BLING 		= 17 
 
PLAYER_SUPERPOWER_BLAST_BLING 		= 18 
 
PLAYER_SUPERPOWER_BUFF_BLING 		= 19 
 
 
 
 
 
 
 
-- Rail table  
 
Rail_data = { }  
 
 
-- 
 
Generator_data = { }  
Generator_objective = "" 
 
 
 
------------------------ 
 
-- Internal functions -- 
 
------------------------ 
 
 
 
function character_is_ready_to_speak( speaking_character ) 
 
   -- Check for a character that does not exist first 
 
   if (not character_exists(speaking_character)) then 
 
		return false 
 
	end 
 
 
 
	if (character_is_dead(speaking_character)) then 
 
		return false 
 
	end 
 
 
 
	if (character_is_ragdolled(speaking_character)) then 
 
		return false 
 
	end 
 
	 
 
	if (character_is_on_fire(speaking_character)) then 
 
      return false 
 
   end 
 
 
 
	-- The character can speak 
 
   return true 
 
end 
 
 
 
function group_create_check_done_loop(group) 
 
	local group_name = group 
 
 
 
	if (type(group) == "table" and type(group.name) == "string") then 
 
		group_name = group.name 
 
	end 
 
 
 
	while (not(group_create_check_done(group_name))) do 
 
		thread_yield() 
 
	end 
 
end 
 
 
 
function group_create_unsynced_check_done_loop(group) 
 
	local group_name = group 
 
 
 
	if (type(group) == "table" and type(group.name) == "string") then 
 
		group_name = group.name 
 
	end 
 
 
 
	while (not(group_create_unsynced_check_done(group_name))) do 
 
		thread_yield() 
 
	end 
 
	 
 
	group_unsynced_do(group_name) 
 
end 
 
 
 
-- Internal use: generalized helicopter_fly_to function 
 
-- 
 
-- name:				name of the helicopter 
 
-- speed:				speed at which the helicopter will fly 
 
-- direct:				if true, move directly, ignoring heightmap 
 
-- follow:				if non-empty, target vehicle to follow 
 
-- continue_at_goal:	if true, continue forward at goal 
 
-- path:				name of scripted_path or single navpoint 
 
-- follow_dist:			distance at which to follow 
 
-- directly_above:		whether the helicopter should be directly above the target vehicle 
 
-- loop_path:			If true, the path should loop back on itself 
 
-- start_full_speed:	if true, the helicopter will start the path at the full speed specified 
 
-- reverse_sp:			fly in the reverse direction of the spline 
 
-- 
 
function helicopter_fly_to_internal(name, speed, direct, follow, continue_at_goal, path, follow_dist, directly_above, loop_path, start_full_speed, reverse_sp) 
 
	local override_player_control = false -- very specific use case for path while player is in control - DO NOT USE 
 
	if (helicopter_fly_to_do(name, speed, direct, follow, continue_at_goal, path, follow_dist, directly_above, loop_path, start_full_speed, reverse_sp, override_player_control)) then 
 
		local check_done = vehicle_pathfind_check_done(name) 
 
		 
 
		while ( check_done == 0) do 
 
			thread_yield() 
 
			check_done = vehicle_pathfind_check_done(name) 
 
		end 
 
		 
 
		return check_done == 1 
 
	else 
 
		return false 
 
	end 
 
end 
 
 
 
-------------------- 
 
-- Script Actions -- 
 
-------------------- 
 
 
 
-- Make a human play an animation and optional morph, blocking until the animation is done. 
 
-- 
 
-- name:			(string) name of character 
 
-- anim_name:		(string) name of animation to play (valid action names can be found in anim_actions.xtbl, in data/tables) 
 
-- morph_name:		(string, optional) name of morph to use (defaults to the animation name) 
 
-- force_play:		(boolean, optional) if true, forces the animation to play on the character, even if dead (defaults to false) 
 
-- percentage:		(float, optional) percentage done to check for (defaults to 0.8) 
 
-- stand_still:		(boolean, optional) if true, the character is reset to a standing state before the animation is played (defaults to false) 
 
-- zero_movement:	(boolean, optional) if ture, the charaters movement speed it set to zero (defaults to false) 
 
-- navpoint:		(string, optional) slide into navpoint if provided 
 
-- dest_nav:		(string, optional) destination navpoint to slide into if provided 
 
-- layered:			(boolean, optional) TRUE if the action should be layered ontop of other scripted actions, FALSE otherwise [layered anims cannot be querried with action_play_is_finished()] (defaults to false) 
 
-- hold_last_frame:	(boolean, optional) TRUE if the action should be hold its last frame. (defaults to false) 
 
-- no_script_control:	(boolean, optional) TRUE if we should skip the script control mode for the player. 
 
-- navpoint_orient_only:(boolean, optional) TRUE if we only want to  
 
-- 
 
function action_play(name, anim_name, morph_name, force_play, percentage, stand_still, zero_movement, navpoint, dest_nav, hold_last_frame, no_script_control, navpoint_orient_only) 
 
	action_play_non_blocking(name, anim_name, morph_name, force_play, stand_still, zero_movement, navpoint, dest_nav, nil, hold_last_frame, no_script_control, navpoint_orient_only) 
 
	repeat 
 
		thread_yield() 
 
	until action_play_is_finished(name, percentage) 
 
end 
 
 
 
-- Make a human play a custom animation, blocking until the animation is done. 
 
-- 
 
-- name:				(string) name of character 
 
-- anim_name:		(string) name of animation to play (valid action names can be found in anim_actions.xtbl, in data/tables) 
 
-- percentage:		(float, optional) percentage done to check for (defaults to 0.8) 
 
-- 
 
-- If possible, the action_play script action should be used instead where possible. 
 
-- 
 
function action_play_custom(name, anim_name, percentage) 
 
	while not action_play_custom_do(name, anim_name) do 
 
		thread_yield() 
 
	end 
 
 
 
	repeat 
 
		thread_yield() 
 
	until action_play_is_finished(name, percentage) 
 
end 
 
 
 
-- Make a human play a direction stumble, blocking until the animation is done 
 
-- 
 
-- name:			(string) name of character 
 
-- nav_name:		(string) name of navpoint indicating the direction to stumble 
 
-- percentage:		(float, optional) percentage done to check for (defaults to 0.8) 
 
-- do_flinch:		(boolean, optional) true if a flinch should be in place of the stumble animations 
 
-- 
 
-- If possible, the action_play script action should be used instead where possible. 
 
-- 
 
function action_play_directional_stumble(name, nav_name, percentage, do_flinch) 
 
	while not action_play_directional_stumble_do(name, nav_name, do_flinch) do 
 
		thread_yield() 
 
	end 
 
	 
 
	repeat 
 
		thread_yield() 
 
	until action_play_is_finished(name, percentage) 
 
end 
 
 
 
-- Make a human play an animation without blocking. 
 
-- 
 
-- name:			(string) name of character 
 
-- anim_name:		(string) name of animation to play (valid action names can be found in anim_actions.xtbl, in data/tables) 
 
-- morph_name:		(string, optional) name of morph to use (defaults to the animation name) 
 
-- force_play:		(boolean, optional) if true, forces the animation to play on the character, even if dead (defaults to false) 
 
-- stand_still:		(boolean, optional) if true, the character is reset to a standing state before the animation is played (defaults to false) 
 
-- zero_movement:	(boolean, optional) if ture, the charaters movement speed it set to zero (defaults to false)	 
 
-- navpoint:		(string, optional) slide into navpoint if provided 
 
-- dest_nav:		(string, optional) slide into destination navpoint if provided 
 
-- layered:			(boolean, optional) TRUE if the action should be layered ontop of other scripted actions, FALSE otherwise [layered anims cannot be querried with action_play_is_finished()] (defaults to false) 
 
-- hold_last_frame:	(boolean, optional) TRUE if the action should be hold its last frame. (defaults to false) 
 
-- 
 
-- NB: This function will still block until the animation actually starts playing. 
 
-- 
 
function action_play_non_blocking(name, anim_name, morph_name, force_play, stand_still, zero_movement, navpoint, dest_nav, layered, hold_last_frame, no_script_control, navpoint_orient_only) 
 
	while not action_play_do(name, anim_name, morph_name, force_play, stand_still, zero_movement, navpoint, dest_nav, layered, hold_last_frame, no_script_control, navpoint_orient_only) do 
 
		thread_yield() 
 
	end 
 
end 
 
 
 
-- Make a pair of humans play an animation.  This function blocks 
 
-- 
 
-- attacker:			(string) name of attacker 
 
-- victim: 				(string) name of victim 
 
-- anim_name:			(string) name of synced animation 
 
-- start_nav_name:      (string, optional) name of the nav point with which to orient the synced animation 
 
-- warp_to_nav:			(boolean, optional) Set to true to warp the attacker and victim to the start nav point (Generally should be false, navpoint must be in loaded zone) (defaults to false) 
 
-- sync_type:			(integer, optional) synchronization type (SYNC_LOCAL = affects local player, SYNC_REMOTE = affects remote player, or SYNC_ALL = affects both players; defaults to SYNC_ALL) 
 
-- percentage:			(float, optional) Percentage of animation playback before returning (defaults to 1.0) 
 
-- 
 
function action_play_synced(attacker, victim, anim_name, start_nav, warp_to_nav, sync_type, percentage) 
 
	while not action_play_synced_do(attacker, victim, anim_name, start_nav, warp_to_nav, sync_type) do 
 
		thread_yield() 
 
	end 
 
	 
 
	repeat 
 
		thread_yield() 
 
	until action_play_synced_is_finished(attacker, victim, percentage) 
 
end 
 
 
 
-- Make a pair of humans play an animation.  This function blocks 
 
-- 
 
-- attacker:			(string) name of attacker 
 
-- victim: 				(string) name of victim 
 
-- anim_name:			(string) name of synced animation 
 
-- start_nav_name:      (string, optional) name of the nav point with which to orient the synced animation 
 
-- sync_type:			(integer, optional) synchronization type (SYNC_LOCAL = affects local player, SYNC_REMOTE = affects remote player, or SYNC_ALL = affects both players; defaults to SYNC_ALL) 
 
-- 
 
-- NB: This function will still block until the animation actually starts playing. 
 
-- 
 
function action_play_synced_non_blocking(attacker, victim, anim_name, start_nav, sync_type) 
 
	while not action_play_synced_do(attacker, victim, anim_name, start_nav, nil, sync_type) do 
 
		thread_yield() 
 
	end 
 
end 
 
 
 
-- Make an airplane fly through a series of navpoints 
 
--  
 
-- name:		(string) name of the airplane 
 
-- speed:		(float) speed in m/s 
 
-- path:		(string) name of a scripted_path, or single navpoint  
 
-- 
 
function airplane_fly_to(name, speed, path) 
 
	-- Wait until the resource is loaded. 
 
	-- character_wait_for_loaded_resource(name) 
 
			 
 
	if (airplane_fly_to_do(name, speed, path)) then 
 
		local check_done = vehicle_pathfind_check_done(name) 
 
		 
 
		while ( check_done == 0) do 
 
			thread_yield() 
 
			check_done = vehicle_pathfind_check_done(name) 
 
		end 
 
		 
 
		return check_done == 1 
 
	else 
 
		return false 
 
	end 
 
end 
 
 
 
 
 
-- Make an airplane land on a runway. 
 
-- 
 
-- name:				(string) name of the airplane 
 
-- runway_start:	(string) name of the navpoint for the start of the runway (must be oriented in the direction of the runway) 
 
-- 
 
-- Note that the plane must already be heading in approximately the correct direction of the runway. 
 
-- The plane will land after the runway_start position. 
 
-- 
 
function airplane_land(name, runway_start) 
 
	if (not airplane_land_do( name, runway_start )) then 
 
		return 
 
	end 
 
	 
 
	while( vehicle_pathfind_check_done(name) == 0) do 
 
		thread_yield() 
 
	end 
 
 
 
end 
 
 
 
-- Make an airplane take off straight in the direction they are currently facing. 
 
-- 
 
-- name:	(string) name of the airplane 
 
-- 
 
function airplane_takeoff(name) 
 
	if (not airplane_takeoff_do( name )) then 
 
		return 
 
	end 
 
	 
 
	while( vehicle_pathfind_check_done(name) == 0) do 
 
		thread_yield() 
 
	end 
 
 
 
end 
 
 
 
-- Make an NPC attack the closest player. 
 
-- 
 
-- name:		(string) name of the NPC 
 
-- 
 
-- This script action will silently fail if the NPC is dead. 
 
-- 
 
function attack_closest_player( npc_name ) 
 
   local distance, closest_player = get_dist_closest_player_to_object( npc_name ) 
 
   attack_safe( npc_name, closest_player ) 
 
end 
 
 
 
-- Make a character attack another character, but checks to see if either character is dead first. 
 
-- 
 
-- attacker:	(string) name of the attacker 
 
-- target:		(string, optional) name of the target (defaults to the player) 
 
-- 
 
-- If either the attacker of target is dead, the attack command is ignored. 
 
-- 
 
function attack_safe( npc_name, target ) 
 
	if target == nil then 
 
		target = CLOSEST_PLAYER 
 
	end 
 
 
 
	if ( ( not character_is_dead( npc_name ) ) and ( ( not character_is_dead( target ) ) ) ) then 
 
		attack( npc_name, target )  
 
	end 
 
end 
 
 
 
-- Play a 2D sound (by name). 
 
-- 
 
-- audio_name:		(string) name of sound to play 
 
-- type_name:		(string, optional) name of audio source (can be "foley", "voice", "music", or "ambient"; defaults to "foley") 
 
-- blocking:		(boolean, optional) set to true to block until the sound starts playing, else set to false to return immediately (defaults to false) 
 
-- ignore_fade:	(boolean, optional) set to true to ignore the volume throttling that occurs with screen fades (defaults to false) 
 
-- 
 
-- returns:			(integer) audio instance handle, or -1 on error 
 
-- 
 
-- Example: 
 
-- 
 
--		audio_play("Some Foley Sound", "foley")  
-- 
 
-- Plays the foley sound, "Some Foley Sound", in 2D. 
 
-- 
 
function audio_play(audio_name, type_name, blocking, ignore_fade) 
 
	local handle = audio_play_do(audio_name, type_name, ignore_fade) 
 
	 
 
	if (not blocking) then 
 
		return handle; 
 
	end 
 
	 
 
	while (audio_is_playing(handle)) do 
 
		thread_yield() 
 
	end 
 
	 
 
	return -1; 
 
end 
 
 
 
function audio_conversation_wait_for_end(handle) 
 
	while audio_conversation_playing(handle) do 
 
		thread_yield() 
 
	end 
 
	audio_conversation_end(handle) 
 
end 
 
 
 
-- Play a conversation, which consists of a series of lines played by characters. 
 
-- 
 
-- dialog_stream:		(table) dialog stream segments that consist of a speaking character name, the name of the audio to play, and a delay in seconds between each dialog segment (character name is not necessary for cellphone calls) 
 
-- cellphone_call:	(enumeration, optional) type of cellphone call (can be NOT_CALL, OUTGOING_CALL, or INCOMING_CALL; defaults to NOT_CALL) 
 
-- 
 
-- For cellphone calls, this function also takes care of cellphone animations. 
 
-- 
 
-- Example: 
 
-- 
 
--		FLEEING_LIEUTENANTS_DIALOG_STREAM = {  
--			{ "SOS3_FLEE_L1", TOBIAS_NAME, 0 },  
--			{ "SOS3_FLEE_L2", LOCAL_PLAYER, 0 }  
--		} 
 
--		audio_play_conversation( FLEEING_LIEUTENANTS_DIALOG_STREAM, NOT_CALL ) 
 
-- 
 
-- Plays "SOS3_FLEE_L1" from Tobias and then "SOS3_FLEE_L2" from the local player. 
 
-- 
 
function audio_play_conversation( dialog_stream, cellphone_call ) 
 
   if ( cellphone_call == nil ) then 
 
      cellphone_call = NOT_CALL 
 
   end 
 
 
 
   -- Open with the cellphone, if this is a cellphone 
 
   -- conversation 
 
   if ( cellphone_call == INCOMING_CALL ) then 
 
      cellphone_animate_start_do() 
 
      delay(0.5) 
 
   elseif ( cellphone_call == OUTGOING_CALL ) then 
 
      cellphone_animate_start_do() 
 
      audio_play( CELLPHONE_OUTGOING, "foley", true ) 
 
      delay(0.5) 
 
   end 
 
 
 
	-- Build a list of characters that will be tested for readyness before any line in the 
 
	-- conversation is played. We only use this list for NOT_CALL conversations. 
 
	local character_list = {}  
	if (cellphone_call == NOT_CALL) then 
 
		for segment_index, dialog_segment in pairs( dialog_stream ) do 
 
			local speaking_character = dialog_segment[DIALOG_STREAM_CHAR_NAME_INDEX] 
 
			character_list[speaking_character] = speaking_character 
 
		end 
 
	end 
 
 
 
	local function dialog_characters_ready() 
 
		for i,character in pairs(character_list) do 
 
			if (not character_is_ready_to_speak(character)) then 
 
				return false 
 
			end 
 
		end 
 
		return true 
 
	end 
 
 
 
   for segment_index, dialog_segment in pairs( dialog_stream ) do 
 
      local audio_name = dialog_segment[DIALOG_STREAM_AUDIO_NAME_INDEX] 
 
      local speaking_character = dialog_segment[DIALOG_STREAM_CHAR_NAME_INDEX] 
 
      local delay_seconds = dialog_segment[DIALOG_STREAM_DELAY_SECONDS_INDEX] 
 
		local anim_action = dialog_segment[DIALOG_STREAM_ANIM_ACTION_INDEX] 
 
 
 
      -- Play the dialog stream for each character 
 
      if ( cellphone_call == NOT_CALL ) then 
 
         repeat 
 
            thread_yield() 
 
         until ( dialog_characters_ready() ) 
 
 
 
			local playing_action = (	(anim_action ~= nil) 
 
												and (not character_is_in_vehicle(speaking_character))  
 
												and (not character_is_combat_ready(speaking_character)) 
 
												and (not mesh_mover_wielding(speaking_character)) 
 
												and (vehicle_exit_check_done(speaking_character)) 
 
												and (vehicle_enter_check_done(speaking_character)) 
 
											) 
 
 
 
			if (playing_action ) then	 
 
				inv_item_equip(nil,speaking_character) 
 
				action_play(speaking_character, anim_action, anim_action, true, 0.0, true) 
 
			end 
 
         audio_play_for_character( audio_name, speaking_character, "voice", false, true) 
 
         delay( delay_seconds ) 
 
      -- Cellphone calls are different - just play the audio, don't use the character function unless 
 
      -- it's the player. 
 
      else 
 
         -- For players, use audio_play_for_character so that the tag can be correctly translated 
 
         if (	speaking_character ~= nil and character_is_player( speaking_character ) ) then 
 
            -- Don't play lines unless and until the player is alive and in a state to deliver them 
 
            repeat 
 
               thread_yield() 
 
            until ( character_is_ready_to_speak( speaking_character ) ) 
 
            audio_play_for_character( audio_name, speaking_character, "voice", false, true) 
 
         elseif ( speaking_character == CELLPHONE_CHARACTER or speaking_character == nil ) then 
 
            -- for_cutscene = false, blocking = true, variant = nil, voice_distance = nil, cellphone_line = true 
 
            audio_play_for_character( audio_name, LOCAL_PLAYER, "voice", false, true, nil, nil, true ) 
 
         else 
 
            script_assert( false, "You must specify CELLPHONE_CHARACTER as the speaking character ( or leave it at nil ) for the other side of a cellphone conversation." ) 
 
         	audio_play( audio_name, "voice", true ) 
 
         end 
 
         delay( delay_seconds ) 
 
      end 
 
   end 
 
 
 
   -- Close the cellphone, if this was a cellphone 
 
   -- conversation 
 
   if ( cellphone_call ~= NOT_CALL ) then 
 
      cellphone_animate_stop_do() 
 
   end 
 
end 
 
 
 
-- Play a conversation while the speaker is in a vehicle. 
 
-- 
 
-- dialog_stream:		(table) dialog stream segments that consist of a speaking character name, the name of the audio to play, and a delay in seconds between each dialog segment (character name is not necessary for cellphone calls) 
 
-- 
 
-- Behaves in much the same way as audio_play_conversation, but blocks while the speaking character is not in a vehicle. 
 
-- 
 
function audio_play_conversation_in_vehicle(dialog_stream) 
 
	-- Loop through the table conversation... 
 
   for segment_index, dialog_segment in pairs( dialog_stream ) do 
 
      local audio_name = dialog_segment[DIALOG_STREAM_AUDIO_NAME_INDEX] 
 
      local speaking_character = dialog_segment[DIALOG_STREAM_CHAR_NAME_INDEX] 
 
      local delay_seconds = dialog_segment[DIALOG_STREAM_DELAY_SECONDS_INDEX] 
 
 
 
		-- Conversation requires them to be in a vehicle 
 
		while (not character_is_in_vehicle(speaking_character)) do 
 
			thread_yield() 
 
		end 
 
 
 
      -- Play the dialog stream for each character 
 
		audio_play_for_character(audio_name, speaking_character, "voice", false, true) 
 
		delay(delay_seconds) 
 
   end 
 
end 
 
 
 
-- Play a cellphone ring and then a conversation. 
 
-- 
 
-- ring_name:	(string) name of the ring sound (from foley.xtbl) 
 
-- conv_name:	(string) name of the conversation (from voice.xtbl) 
 
-- 
 
-- The ring sound plays twice before the conversation occurs. This function also player cellphone animations. 
 
-- 
 
-- Note: this function does not display a "Press Y to ..." message on the screen. 
 
-- 
 
function audio_play_for_cellphone_force(ring_name, conv_name ) 
 
 
 
	audio_play(ring_name, "foley", false, true) 
 
	delay(0.5) 
 
	audio_play(ring_name, "foley", false, true) 
 
	delay(0.5) 
 
	cellphone_animate_start_do() 
 
 
 
	-- RCS: I think these should probably play in 2D...so audio_play would be more appropriate... 
 
	audio_play_for_character(conv_name, LOCAL_PLAYER, "voice", false, true) 
 
	cellphone_animate_stop_do() 
 
 
 
end 
 
 
 
-- Play a 3D sound (by name) on a character. 
 
-- 
 
-- audio_name:			(string) name of sound 
 
-- human_name:			(string) name of character 
 
-- type_name:			(string, optional) name of audio source (can be "foley", "voice", "music", or "ambient"; defaults to "foley") 
 
-- for_cutscene:		(boolean, optional) no longer used (NIQ 4/2/09: this parameter needs to be removed) 
 
-- blocking:			(boolean, optional) set to true if the function should block until the sound has finished playing (defaults to false) 
 
-- variant:				(integer, optional) index of the specific variant of the voice line to play (or -1 for a random selection; defaults to -1) 
 
-- voice_distance:	(integer, optional) distance that the foley can be heard from; only used for voices (0 = short, 1 = normal, 2 = long, 3 = extreme; defaults to -1, which is normal distance for peds, and extreme distance for passengers in cars) 
 
-- cellphone_line:	(boolean, optional) set to true if the audio is for a cellphone line (defaults to false) 
 
-- ignore_fade:		(boolean, optional) set to true to ignore the volume throttling that occurs with screen fades (defaults to false) 
 
-- 
 
-- returns:				(integer) audio instance handle, or -1 on error 
 
-- 
 
-- Example: 
 
-- 
 
--		audio_play_for_character("Woot", "#PLAYER1#", "foley")  
-- 
 
-- Plays the sound "Woot" on the local player. 
 
--  
 
function audio_play_for_character(audio_name, human_name, type_name, for_cutscene, blocking, variant, voice_distance, cellphone_line, ignore_fade)	 
 
	if (character_is_dead(human_name)) then 
 
		return -1, 0 
 
	end 
 
   if ( cellphone_line == nil ) then 
 
      cellphone_line = false 
 
   end 
 
 
 
	-- If we're playing a voice line, update the audio name to reflect the player's persona. 
 
	local new_audio_name = audio_name 
 
	if ( (type_name == "voice") and (character_is_player(human_name)) and cellphone_line == false ) then 
 
		new_audio_name = persona_trigger_get_player_prefix(human_name) .. audio_name 
 
	end 
 
 
 
	local handle, play_time = audio_play_for_character_do(new_audio_name, human_name, type_name, blocking, variant, voice_distance, cellphone_line, ignore_fade) 
 
 
 
	if (not(blocking)) then 
 
		return handle, play_time; 
 
	end 
 
 
 
	-- While playing blocking audio, prevent random persona lines from playing 
 
	if (character_is_player(human_name)) then 
 
		audio_suppress_ambient_player_lines(true) 
 
	else 
 
		npc_suppress_persona(human_name, true) 
 
	end 
 
	 
 
	if (handle > 0) then 
 
		while (audio_is_playing(handle)) do 
 
			thread_yield() 
 
		end 
 
	else 
 
		delay( play_time ) 
 
	end 
 
 
 
	-- Allow random persona lines to play again 
 
	if (character_is_player(human_name)) then 
 
		audio_suppress_ambient_player_lines(false) 
 
	else 
 
		npc_suppress_persona(human_name, true) 
 
	end	 
 
 
 
	return -1, 0 
 
end 
 
 
 
-- Play a 3D sound (by name) on a character's weapon. 
 
-- 
 
-- audio_name:	(string) name of sound 
 
-- human_name:	(string) name of character 
 
-- type_name:	(string, optional) name of audio source (can be "foley", "voice", "music", or "ambient"; defaults to "foley") 
 
-- blocking:	(boolean, optional) set to true if the function should block until the sound has finished playing (defaults to false) 
 
-- 
 
-- returns:		(integer) audio instance handle, or -1 on error 
 
-- 
 
function audio_play_for_character_weapon( audio_name, character, type_name, blocking)	 
 
	local handle = audio_play_for_character_weapon_do( audio_name, character, type_name) 
 
	 
 
	if not blocking then 
 
		return handle 
 
	end 
 
	 
 
	if handle >= 0 then 
 
		repeat  
 
			thread_yield() 
 
		until not audio_is_playing(handle) 
 
		return 0 
 
	end 
 
 
 
	return -1 
 
end 
 
 
 
-- Play a 3D sound (by name) on a mover. 
 
-- 
 
-- audio_name:		(string) name of sound 
 
-- script_mover:	(string) name of mover 
 
-- type_name:		(string, optional) name of audio source (can be "foley", "voice", "music", or "ambient"; defaults to "foley") 
 
-- blocking:		(boolean, optional) set to true if the function should block until the sound has finished playing (defaults to false) 
 
-- 
 
-- returns:			(integer) audio instance handle, or -1 on error 
 
-- 
 
function audio_play_for_mover( audio_name, script_mover, type_name, blocking ) 
 
 
 
	local handle = audio_play_for_mover_do( audio_name, script_mover, type_name ) 
 
	 
 
	if (not blocking) then 
 
		return handle 
 
	end 
 
	 
 
	if (handle > 0) then 
 
		while (audio_is_playing(handle)) do 
 
			thread_yield() 
 
		end 
 
	end 
 
	 
 
	return -1 
 
end 
 
 
 
-- Play a 2D sound (by ID). 
 
-- 
 
-- audio_id:	(integer) ID of sound to play 
 
-- type_name:	(string, optional) name of audio source (can be "foley", "voice", "music", or "ambient"; defaults to "foley") 
 
-- blocking:	(boolean, optional) set to true if the function should block until the sound has finished playing (defaults to false) 
 
-- 
 
-- returns:		(integer) audio instance handle, or -1 on error 
 
-- 
 
function audio_play_id(audio_id, type, blocking) 
 
	local handle = audio_play_id_do(audio_id, type) 
 
	 
 
	if (not blocking) then 
 
		return handle; 
 
	end 
 
	 
 
	while (audio_is_playing(handle)) do 
 
		thread_yield() 
 
	end 
 
	 
 
	return -1; 
 
end 
 
 
 
-- Play a 3D sound (by ID) on a character. 
 
-- 
 
-- human_name:			(string) name of character 
 
-- audio_id:			(integer) ID of sound to play 
 
-- type_name:			(string, optional) name of audio source (can be "foley", "voice", "music", or "ambient"; defaults to "foley") 
 
-- blocking:			(boolean, optional) set to true if the function should block until the sound has finished playing (defaults to false) 
 
-- variant:				(integer, optional) index of the specific variant of the voice line to play (or -1 for a random selection; defaults to -1) 
 
-- voice_distance:	(integer, optional) distance that the foley can be heard from; only used for voices (0 = short, 1 = normal, 2 = long, 3 = extreme; defaults to -1, which is normal distance for peds, and extreme distance for passengers in cars) 
 
-- 
 
-- returns:				(integer) audio instance handle, or -1 on error 
 
-- 
 
function audio_play_id_for_character(human_name, audio_id, type, blocking, variant, voice_distance) 
 
	if (character_is_dead(human_name)) then 
 
		return -1 
 
	end 
 
	 
 
	local	handle = audio_play_id_for_character_do(human_name, audio_id, type, blocking, variant, voice_distance) 
 
 
 
	if (not blocking) then 
 
		return handle 
 
	end 
 
	 
 
	while (audio_is_playing(handle)) do 
 
		thread_yield() 
 
	end 
 
	 
 
	return -1 
 
end 
 
 
 
-- Play a 3D sound (by ID) on a navpoint. 
 
-- 
 
-- navpoint_name:		(string) name of the navpoint 
 
-- audio_id:			(integer) ID of sound to play 
 
-- type_name:			(string, optional) name of audio source (can be "foley", "voice", "music", or "ambient"; defaults to "foley") 
 
-- blocking:			(boolean, optional) set to true if the function should block until the sound has finished playing (defaults to false) 
 
-- 
 
-- returns:				(integer) audio instance handle, or -1 on error 
 
--  
 
function audio_play_id_for_navpoint(navpoint_name, audio_id, type, blocking) 
 
	local handle = audio_play_id_for_navpoint_do(navpoint_name, audio_id, type) 
 
 
 
	if (not blocking) then 
 
		return handle 
 
	end 
 
 
 
	while (audio_is_playing(handle)) do 
 
		thread_yield() 
 
	end 
 
 
 
	return -1 
 
end 
 
 
 
-- Call the given function on each member of the given table 
 
-- 
 
-- function_ref:	(function reference - NOT a string) the function to call for each member of the table 
 
-- table:			(table) the table whose members the function should be called on 
 
-- ...:				(list) list of arguments to send to the function 
 
-- 
 
function call_function_on_each_member(function_ref, table, ...) 
 
	if (type(function_ref) ~= "function") then 
 
		return 
 
	end 
 
 
 
	if (type(table) ~= "table") then 
 
		function_ref(table, unpack(arg)) 
 
	else 
 
		for i,member in pairs(table) do 
 
			call_function_on_each_member(function_ref, member, unpack(arg)) 
 
		end 
 
	end 
 
end 
 
 
 
-- Moves the camera from its current position and orientation to the specified navpoint's position and orientation. 
 
-- 
 
-- navpoint_name:		(string) navpoint to look through 
 
-- duration:			(float, optional) duration of movement, in seconds; if set to 0, camera will instantly jump to the end position and orientation (defaults to 0) 
 
-- yield:				(boolean, optional) set to true to block until the camera movement is complete (defaults to false) 
 
-- hide_hud:			(boolean, option) set to false to not disable the hud when using this camera (defaults to true) 
 
-- sync_flag:			(enum, optional) SYNC_LOCAL, SYNC_REMOTE, SYNC_ALL (defaults to SYNC_ALL) 
 
-- 
 
-- NB: the camera will not return to its default behavior unless a camera_end_script() action is issued. If the camera script finishes execution before the 
 
-- camera_end_script() action is issued, the camera will hold the last position of the camera script. 
 
-- 
 
-- Example: 
 
-- 
 
--		camera_look_through("$npc000", 5.0, true)  
--		camera_end_script() 
 
-- 
 
-- Move the camera over a period of five seconds so that it is looking through navpoint "$npc000".  
 
-- 
 
function camera_look_through(navp, duration, yield, hide_hud, sync_flag) 
 
	camera_look_through_do(navp, duration, hide_hud, sync_flag) 
 
 
 
	if (yield) then	 
 
		while (not(camera_script_is_finished())) do 
 
			thread_yield() 
 
		end 
 
	end 
 
end 
 
 
 
-- Make a character take another character as a human shield. 
 
-- 
 
-- attacker:	(string) name of attacker 
 
-- victim:		(string) name of target 
 
-- bypass_ai:	(bool, optional) bypass AI and take shield immediately 
 
-- 
 
function character_take_human_shield( character, victim, ... ) 
 
	local bypass_ai = false 
 
	if (arg.n > 0) then 
 
		bypass_ai = arg[1] 
 
	end 
 
	 
 
   character_take_human_shield_do( character, victim, bypass_ai ) 
 
 
 
   -- loop as long as grabber and grabee are alive until we've succeeded 
 
   while ( character_is_dead( character ) == false and 
 
           character_is_dead( victim ) == false and 
 
	   character_take_human_shield_check_done( character, victim) == false) do 
 
      thread_yield(); 
 
   end 
 
end 
 
 
 
-- Blocks until the specified character's resources have loaded. 
 
-- 
 
-- name:	(string) name of the character to wait on 
 
-- 
 
function character_wait_for_loaded_resource(name) 
 
	while (not(character_check_resource_loaded(name))) do 
 
		thread_yield() 
 
	end 
 
end 
 
 
 
-- Recursively cleanup all the callbacks for an object or table of objects. 
 
-- 
 
-- objects:	(string or table) object, table of objects, table of tables of objects, etc. 
 
-- 
 
function cleanup_callbacks(objects) 
 
	if (type(objects) == "string") then 
 
		clear_callbacks_for_obj(objects) 
 
	elseif (type(objects) == "table") then 
 
		for i,obj in pairs(objects) do 
 
			cleanup_callbacks(obj) 
 
		end 
 
	end 
 
end 
 
 
 
-- Recursively cleanup all the audio conversations that may still be playing 
 
-- 
 
-- convos:	(table) conversation, table of conversations, table of tables of conversations, etc. 
 
-- 
 
function cleanup_conversations(convos) 
 
	local ret_val = convos 
 
 
 
	-- An audio conversation is a table, so if we've come to something that's not a table, there is no conversation here 
 
	if (type(convos) ~= "table") then 
 
		return ret_val 
 
	end 
 
 
 
	-- Conversations must have a name and a handle to the conversation as it plays, so having both should be a good sign that this is a conversation 
 
	if (convos.name ~= nil and convos.handle ~= nil) then 
 
		if (convos.handle ~= INVALID_CONVERSATION_HANDLE) then 
 
			audio_conversation_end(convos.handle) 
 
			ret_val.handle = INVALID_CONVERSATION_HANDLE 
 
		end 
 
	else 
 
		-- The table didn't have a member called "handle", so this table isn't a conversation (though could be a table of conversations) 
 
		for i,convo in pairs(convos) do 
 
			ret_val[i] = cleanup_conversations(convo) 
 
		end 
 
	end 
 
 
 
	-- Return a copy with the ended conversation handles set to INVALID_CONVERSATION_HANDLE 
 
	return ret_val 
 
end 
 
 
 
-- Recursively clean up a script group or table of script groups. 
 
-- 
 
-- group:	(string or table) a group, table of groups, table of tables of groups, etc. 
 
-- 
 
function cleanup_groups(group) 
 
	if (type(group) == "string") then 
 
		if (group_is_loaded(group)) then 
 
			group_destroy_do(group) 
 
		end 
 
	elseif (type(group) == "table") then 
 
		if (type(group.name) == "string") then 
 
			if (group_is_loaded(group.name)) then 
 
				group_destroy_do(group.name) 
 
			end 
 
		else 
 
			for i,group_member in pairs(group) do 
 
				cleanup_groups(group_member) 
 
			end 
 
		end 
 
	end 
 
end 
 
 
 
-- Recursively cleanup all the spawn groups (i.e. stop their spawning) 
 
-- 
 
-- group:	(string or table) a spawn group, table of spawn groups, table of tables of spawn groups, etc. 
 
-- 
 
function cleanup_spawn_groups(group) 
 
	if (type(group) == "string") then 
 
		continuous_spawn_stop(group, true) 
 
	elseif (type(group) == "table") then 
 
		-- See if the spawn group is within the regular group table 
 
		if (type(group.name) == "string") then 
 
			continuous_spawn_stop(group.name, true) 
 
		else 
 
			for i, member in pairs(group) do 
 
				cleanup_spawn_groups(member) 
 
			end 
 
		end 
 
	end 
 
end 
 
 
 
-- Recursively disable all spawn regions in a table 
 
-- 
 
-- regions:	(string or table) spawn region, table of spawn regions, table of tables of spawn regions, etc. 
 
-- 
 
function cleanup_spawn_regions(regions) 
 
	continuous_spawn_regions_enable(regions, false) 
 
end 
 
 
 
-- Recursively remove temporary weapons from player inventory 
 
-- 
 
-- weapons:		(string or table) weapon, table of weapons, table of tables of weapons, etc. 
 
-- sync_type:	(enum, optional) SYNC_LOCAL, SYNC_REMOTE, SYNC_ALL (defaults to SYNC_ALL) 
 
-- 
 
function cleanup_temporary_weapons(weapons, sync_type) 
 
	if (sync_type == nil) then 
 
		sync_type = SYNC_ALL 
 
	end 
 
 
 
	if (type(weapons) == "string") then 
 
		if (SYNC_LOCAL or SYNC_ALL) then 
 
			inv_weapon_remove_temporary(LOCAL_PLAYER, weapons) 
 
		end 
 
		if (coop_is_active()) then 
 
			if (SYNC_REMOTE or SYNC_ALL) then 
 
				inv_weapon_remove_temporary(REMOTE_PLAYER, weapons) 
 
			end 
 
		end 
 
	elseif (type(weapons) == "table") then 
 
		for i,weapon in pairs(weapons) do 
 
			cleanup_temporary_weapons(weapon, sync_type) 
 
		end 
 
	end 
 
end 
 
 
 
-- Iterates recursively through a table of threads, cleaning up any thread it finds. 
 
-- 
 
-- threads:	(string or table) a single thread, table of threads, table of tables of threads, etc. 
 
-- 
 
function cleanup_threads(threads) 
 
	local ret_val = threads 
 
 
 
	if (type(threads) == "number") then 
 
		if (threads ~= INVALID_THREAD_HANDLE) then 
 
			thread_kill(threads) 
 
			ret_val = INVALID_THREAD_HANDLE 
 
		end 
 
	elseif (type(threads) == "table") then 
 
		for i,thread in pairs(threads) do 
 
			ret_val[i] = cleanup_threads(thread) 
 
		end 
 
	end 
 
 
 
	-- Returns a copy with killed threads set to INVALID_THREAD_HANDLE 
 
	return ret_val 
 
end 
 
 
 
-- Debug function for making a Lua function executable through the console. 
 
-- 
 
-- function_name:	(string) name of function 
 
-- 
 
function console_wrapper( function_name ) 
 
	_UGGlobals[function_name]() 
 
end 
 
 
 
-- Recursively dig down into a spawn region table and enable/disable each region found 
 
-- 
 
-- regions:	(table or string) spawn region, table of spawn regions, table of tables of spawn regions, etc. 
 
-- enable:	(bool) whether to enable or disable these regions 
 
-- 
 
function continuous_spawn_regions_enable(regions, enable) 
 
	if (type(regions) == "string") then 
 
		spawn_region_enable(regions, enable) 
 
	elseif (type(regions) == "table") then 
 
		for i,region in pairs(regions) do 
 
			continuous_spawn_regions_enable(region, enable) 
 
		end 
 
	end 
 
end 
 
 
 
-- Play a cutscene. 
 
-- 
 
-- name:				(string) name of cutscene to play 
 
-- group_name:		(string, or table of strings; optional) group(s) for the cutscene to load before returning (defaults to nil) 
 
-- teleport_name:	(table of strings, optional) names of navpoints to teleport the local and remote players to after the cutscene (defaults to nil) 
 
-- fade_in_after:	(boolean, optional) set to true to automatically fade in after the cutscene is finished (defaults to true) 
 
-- wait_for_spawning:   (boolean, optional) set to false to have the cutscene drop immediately into the world without waiting for spawning to finish up (defaults to true) 
 
-- 
 
-- This function blocks until the cutscene has finished playing. 
 
-- 
 
function cutscene_play(name, group_name, teleport_name, fade_in_after, wait_for_spawning, restore_players_vehicle) 
 
	while (not zscene_is_loaded(name)) do 
 
		thread_yield() 
 
	end 
 
	 
 
	local function convert_to_table( var ) 
 
		if (var == "" or var == nil) then 
 
			return nil 
 
		elseif (type(var) == "table") then		 
 
			return var 
 
		else 
 
			return { var, n = 1 }  
		end 
 
	end 
 
	 
 
	local converted_group = convert_to_table( group_name ) 
 
	local converted_teleports = convert_to_table( teleport_name ) 
 
	 
 
	cutscene_play_do(name, converted_group, converted_teleports, fade_in_after, wait_for_spawning, restore_players_vehicle) 
 
 
 
	while (not(cutscene_play_check_done())) do 
 
		thread_yield() 
 
	end 
 
end 
 
 
 
-- Play a fake conversation by displaying consecutive lines of text on the screen 
 
-- 
 
-- conversation:	(table) character name, audio tag, delay between lines  
 
-- 
 
-- NOTE: conversation should be formatted the way it is for audio_play_conversation { "text", "name", duration }  
-- 
 
-- convo = {  
--		{ "This is the first line of the convo", "Speaker's Name", 3 },  
--		{ "This is the second line of the convo", "Other Speaker's Name", 2 },  
-- } 
 
-- 
 
-- This will display on the screen for 3 seconds: "Speaker's Name: This is the first line of the convo" 
 
-- This will display on the screen for 2 seconds: "Other Speaker's Name: This is the second line of the convo" 
 
-- 
 
function debug_conversation(conversation) 
 
	if (conversation == nil) then 
 
		return 
 
	end 
 
 
 
	for i,line in pairs(conversation) do 
 
		local current_handle = message(line[DIALOG_STREAM_CHAR_NAME_INDEX] .. ": " .. line[DIALOG_STREAM_AUDIO_NAME_INDEX], line[DIALOG_STREAM_DELAY_SECONDS_INDEX]) 
 
		delay(line[DIALOG_STREAM_DELAY_SECONDS_INDEX]) 
 
		message_remove(current_handle) 
 
		current_handle = INVALID_MESSAGE_HANDLE 
 
	end 
 
end 
 
 
 
-- Block until the screen is completely faded in. 
 
-- sync_type:	(enum, optional) SYNC_LOCAL, SYNC_REMOTE, SYNC_ALL (defaults to SYNC_ALL) 
 
-- 
 
function fade_in_block(sync_type) 
 
	if (sync_type == nil) then 
 
		sync_type = SYNC_ALL 
 
	end 
 
	 
 
	while( not fade_is_fully_faded_in(sync_type) ) do 
 
		thread_yield() 
 
	end 
 
	 
 
	thread_yield() 
 
end 
 
 
 
-- Block until the screen is completely faded out. 
 
-- sync_type:	(enum, optional) SYNC_LOCAL, SYNC_REMOTE, SYNC_ALL (defaults to SYNC_ALL) 
 
-- 
 
function fade_out_block(sync_type) 
 
	if (sync_type == nil) then 
 
		sync_type = SYNC_ALL 
 
	end 
 
	 
 
	while( not fade_is_fully_faded_out(sync_type) ) do 
 
		thread_yield() 
 
	end 
 
	 
 
	thread_yield() 
 
end 
 
 
 
-- Force an NPC to fire at a navpoint. 
 
-- 
 
-- name:					(string) name of the NPC 
 
-- fire_at_navpoint:	(string) name of navpoitn to fire at 
 
-- fire_once:			(boolean, optional) set to true to only fire once, or false to fire continuously (defaults to false) 
 
-- 
 
function force_fire(name, fire_at_navpoint, fire_once) 
 
	if (force_fire_do(name, fire_at_navpoint, fire_once)) then 
 
		while (not(force_ai_mode_check_done(name))) do 
 
			thread_yield() 
 
		end 
 
	end 
 
end 
 
 
 
-- Force an NPC to fire at a character. 
 
-- 
 
-- name:					(string) name of the NPC 
 
-- fire_at_target:	(string) name of the target character 
 
-- fire_once:			(boolean, optional) set to true to only fire once, or false to fire continuously (defaults to false) 
 
-- 
 
function force_fire_target(name, fire_at_target, fire_once) 
 
	if (force_fire_target_do(name, fire_at_target, fire_once)) then 
 
		while (not(force_ai_mode_check_done(name))) do 
 
			thread_yield() 
 
		end 
 
	end 
 
end 
 
 
 
-- Force an NPC to toss a thrown weapon at a navpoint. 
 
-- 
 
-- name:				(string) name of the NPC 
 
-- target_navp:	(string) name of the target navpoint 
 
-- throw_pitch:	(float, optional) angle of elevation to throw at, in degrees (defaults to 45 degrees) 
 
-- throw_speed:	(float, optional) speed to throw at, in m/s (defaults to -1, which means use the default throw speed of the weapon) 
 
-- 
 
function force_throw(name, target_navp, throw_pitch, throw_speed) 
 
	if (force_throw_do(name, target_navp, throw_pitch, throw_speed)) then 
 
		while (not(force_ai_mode_check_done(name))) do 
 
			thread_yield() 
 
		end 
 
	end 
 
end 
 
 
 
-- Force an NPC to toss a thrown weapon at a character. 
 
-- 
 
-- name:		(string) name of the NPC 
 
-- target:	(string) name of the target character 
 
-- 
 
function force_throw_char(name, target) 
 
	if (force_throw_char_do(name, target)) then 
 
		while (not(force_ai_mode_check_done(name))) do 
 
			thread_yield() 
 
		end 
 
	end 
 
end 
 
 
 
-- Get the distance from an object to the closest player, and also returns the closest player. 
 
-- 
 
-- object:			(string) name of the object 
 
-- player_list:	(table of strings, optional) list of players to test against, or nil to test against all players (defaults to nil) 
 
-- 
 
-- returns:			(float) distance from the object to the closest player 
 
-- returns:			(string) name of closest player 
 
-- 
 
function get_dist_closest_player_to_object(object, player_list) 
 
	if player_list == nil then 
 
		player_list = player_names_get_all() 
 
	end 
 
	 
 
	if sizeof_table( player_list ) == 0 then 
 
		return 
 
	end 
 
	 
 
	local closest_dist = get_dist(player_list[1], object) 
 
	local closest_player = player_list[1] 
 
	 
 
	-- Spin through the available players 
 
	for i, p in pairs(player_list) do 
 
		local current_dist = get_dist(p, object) 
 
		if current_dist < closest_dist then 
 
			closest_dist = current_dist 
 
			closest_player = p 
 
		end 
 
	end 
 
	 
 
	return closest_dist, closest_player 
 
end 
 
 
 
-- Get a random entry from a lua table. 
 
-- 
 
-- returns:		(any type) a random entry from the table passed in (can be any type) 
 
-- returns:		(nil) if the table passed in is not a table or has no entries, returns nil 
 
-- 
 
function get_random_table_entry(input_table) 
 
	if (type(input_table) ~= "table") then 
 
		return nil 
 
	end 
 
 
 
	if (sizeof_table(input_table) == 0) then 
 
		return nil 
 
	end 
 
 
 
	return input_table[rand_int(1, sizeof_table(input_table))] 
 
end 
 
 
 
-- Gets a random player. If the game's not in coop, always returns the local player. 
 
-- Created this to select a random player for the AI to target. 
 
-- 
 
-- returns: LOCAL_PLAYER or REMOTE_PLAYER. In non-coop games, returns LOCAL_PLAYER. 
 
-- 
 
function get_random_target_player() 
 
	if ( coop_is_active() == false ) then 
 
		return LOCAL_PLAYER 
 
	end 
 
 
 
	local players_table = { LOCAL_PLAYER, REMOTE_PLAYER }  
 
 
	return get_random_table_entry( players_table ) 
 
end 
 
 
 
-- Get a child table from a parent table by its "name" variable. 
 
-- 
 
-- parent_table:	(table) Table that contains child tables with a "name" variable. 
 
-- child_name:		(string) Name of the table that we want from the parent table. 
 
-- 
 
function get_table_by_name(parent_table, child_name) 
 
	local found_table = nil 
 
	for i, child_table in pairs(parent_table) do 
 
		if child_table.name == child_name then 
 
			found_table = child_table 
 
			break 
 
		end 
 
	end 
 
	return found_table 
 
end 
 
 
 
-- Creates all script objects belonging to the specified script group. 
 
-- 
 
-- group:	(string or table) name of the script group, or a group table (should have a "name" field that contains the name of the group) 
 
-- block:	(boolean, optional) set to true to block until everything in the group has finished streaming in (defaults to false) 
 
-- 
 
-- If the script object is already created, it will reset the script object to initial position and state. The objects will be unhidden by default. 
 
-- 
 
-- Example: 
 
-- 
 
--		group_create("mission 1 characters", true)  
-- 
 
-- Creates all objects in the "mission 1 characters" group and waits for them to stream in. 
 
-- 
 
-- Notes: 
 
--  - before group_create() is called, script objects in the group don�t exist, so they don�t need to be hidden 
 
--  - also, this means that any script commands using those script objects must come AFTER the group_create() call; otherwise those commands will do nothing 
 
--  - group_create() call should probably go in mission_start() rather than mission_setup(), as in the final game, the player might have several missions open at once 
 
-- 
 
function group_create(group, block) 
 
	local group_name = group 
 
 
 
	-- With the new setup of groups in mission scripts, each group should have a "name" field that contains the name of the group 
 
	if (type(group) == "table" and type(group.name) == "string") then 
 
		group_name = group.name 
 
	end 
 
 
 
	-- pause npc spawning. we are starting in an area where we can't see peds and cars. so don't bother waiting for them 
 
	-- object_spawn_pause(true, "group_create") 
 
	 
 
	group_create_do(group_name) 
 
 
 
	local handle = thread_new("group_create_check_done_loop", group_name)  
 
 
	if (block == true) then 
 
		while (not thread_check_done(handle)) do 
 
			thread_yield() 
 
		end 
 
		thread_yield() 
 
	end 
 
 
 
	-- pause npc spawning. we are starting in an area where we can't see peds and cars. so don't bother waiting for them 
 
	-- object_spawn_pause(false, "group_create") 
 
end 
 
 
 
-- Creates all script objects belonging to the specified script group, and makes them all hidden. 
 
-- 
 
-- group:	(string or table) name of the script group, or a group table (should have a "name" field that contains the name of the group) 
 
-- block:	(boolean, optional) set to true to block until everything in the group has finished streaming in (defaults to false) 
 
-- 
 
-- If the script object is already created, it will reset the script object to initial position and state. 
 
-- 
 
-- This function behaves the same as the group_create script action, except that all members of the group remain hidden until manually made visible. 
 
-- 
 
function group_create_hidden(group, block) 
 
	local group_name = group 
 
 
 
	-- With the new setup of groups in mission scripts, each group should have a "name" field that contains the name of the group 
 
	if (type(group) == "table" and type(group.name) == "string") then 
 
		group_name = group.name 
 
	end 
 
	 
 
	group_create_hidden_do(group_name) 
 
 
 
	local handle = thread_new("group_create_check_done_loop", group_name)  
 
 
	if (block == true) then 
 
		while (not thread_check_done(handle)) do 
 
			thread_yield() 
 
		end 
 
		thread_yield() 
 
	end 
 
end 
 
 
 
-- Creates all script objects belonging to the specified script group, and makes them unsynced 
 
-- 
 
-- group:	(string or table) name of the script group, or a group table (should have a "name" field that contains the name of the group) 
 
-- block:	(boolean, optional) set to true to block until everything in the group has finished streaming in (defaults to false) 
 
-- 
 
-- WARNING: Only use with groups that contain only script_items 
 
-- 
 
function group_create_unsynced(group, block) 
 
	local group_name = group 
 
		-- With the new setup of groups in mission scripts, each group should have a "name" field that contains the name of the group 
 
	if (type(group) == "table" and type(group.name) == "string") then 
 
		group_name = group.name 
 
	end 
 
	 
 
	group_create_do(group_name) 
 
 
 
	local handle = thread_new("group_create_unsynced_check_done_loop", group_name)  
 
 
	if (block == true) then 
 
		while (not thread_check_done(handle)) do 
 
			thread_yield() 
 
		end 
 
		thread_yield() 
 
	end 
 
end 
 
 
 
-- Spawn a list of groups 
 
-- 
 
-- groups_table:		(table) list of groups to spawn 
 
-- block_override:		(optional, boolean) If set, the value will override the invidual .block variables for each group 
 
-- 
 
function group_create_list(groups_table, block_override) 
 
	for i, group in pairs(groups_table) do 
 
		local blocking = true 
 
		if (block_override ~= nil) then 
 
			blocking = block_override 
 
		else 
 
			if group.block == false then 
 
				blocking = false 
 
			end 
 
		end 
 
		 
 
		if group.hidden == true then 
 
			group_create_hidden(group.name, blocking) 
 
		else 
 
			group_create(group.name, blocking) 
 
		end 
 
	end 
 
end 
 
 
 
-- Destroys all objects belonging to the specified script group 
 
-- 
 
-- group:	(string or table) name of a script group, or a script group table with a "name" field 
 
-- 
 
function group_destroy(group) 
 
	if (type(group) == "table" and type(group.name) == "string") then 
 
		group_destroy_do(group.name) 
 
	else 
 
		group_destroy_do(group) 
 
	end 
 
end 
 
 
 
-- Destroys all objects belonging to the specified unsynced script group 
 
-- 
 
-- group:	(string or table) name of a script group, or a script group table with a "name" field 
 
-- 
 
function group_destroy_unsynced(group) 
 
	if (type(group) == "table" and type(group.name) == "string") then 
 
		group_destroy_unsynced_do(group.name) 
 
	else 
 
		group_destroy_unsynced_do(group) 
 
	end 
 
end 
 
-- Hide a group of script objects 
 
-- 
 
-- group:	(string or table) name of a script group, or a script group table with a "name" field 
 
-- 
 
function group_hide(group) 
 
	if (type(group) == "table" and type(group.name) == "string") then 
 
		group_hide_do(group.name) 
 
	else 
 
		group_hide_do(group) 
 
	end 
 
end 
 
 
 
-- Check if a group is loaded 
 
-- 
 
-- group:	(string or table) name of a script group, or a script group table with a "name" field 
 
-- 
 
-- returns:	(boolean) true if the group is loaded, false otherwise 
 
-- 
 
function group_is_loaded(group) 
 
	if (type(group) == "table" and type(group.name) == "string") then 
 
		return group_is_loaded_internal(group.name) 
 
	else 
 
		return group_is_loaded_internal(group) 
 
	end 
 
end 
 
 
 
-- run a function on each NPC in a provided group. 
 
-- 
 
-- group: (string or table) the name of the script group or a script group table with a "name" field. 
 
-- fn: (function reference) the funciton you wish to execute on each NPC.  Should be protoyped like so - my_func(npc_name) 
 
-- 
 
function group_foreach_npc(group, fn) 
 
	if (type(group) == "table" and type(group.name) == "string") then 
 
		group = group.name 
 
	end 
 
	 
 
	-- If it's not a function, that's no good. 
 
	if (type(fn) == "function") then 
 
		local np = group_get_first_npc(group) 
 
		while (np) do 
 
			fn(np) 
 
			np = group_get_next_npc(group, np) 
 
		end 
 
	end 
 
end 
 
 
 
-- Unhide a group of script objects 
 
-- 
 
-- group:	(string or table) name of a script group, or a script group table with a "name" field 
 
-- 
 
function group_show(group) 
 
	if (type(group) == "table" and type(group.name) == "string") then 
 
		group_show_do(group.name) 
 
	else 
 
		group_show_do(group) 
 
	end 
 
end 
 
 
 
-- Make a helicopter fly through one or more navpoints. 
 
-- 
 
-- name:		(string) name of the helicopter 
 
-- speed:		(float) spead to fly at, in m/s (-1 to use default movement speed) 
 
-- path:		(string) name of a scripted_path, or single navpoint  
 
-- 
 
-- The helicopter will pathfind through the specified path or to the specified point, 
 
-- avoiding buildings and other obstacles along the way. 
 
-- 
 
function helicopter_fly_to(name, speed, path, reverse_sp) 
 
	local direct = false 
 
	local follow = "" 
 
	local continue_at_goal = false 
 
	local follow_dist = 0.0 
 
	local directly_above = false 
 
	local loop_path = false 
 
	local start_full_speed = false 
 
	return helicopter_fly_to_internal(name, speed, direct, follow, continue_at_goal, path, follow_dist, directly_above, loop_path, start_full_speed, reverse_sp) 
 
end 
 
 
 
-- Make a helicopter fly through one or more navpoints, ignoring any obstacles along the way. 
 
-- 
 
-- name:		(string) name of the helicopter 
 
-- speed:		(float) spead to fly at, in m/s (-1 to use default movement speed) 
 
-- path:		(string) name of a scripted_path, or single navpoint  
 
-- 
 
-- Use the function only when you are absolutely sure there are no obstacles between waypoints. The helicopter will not move around them when using this function. 
 
-- 
 
function helicopter_fly_to_direct(name, speed, path,reverse_sp) 
 
	local direct = true 
 
	local follow = "" 
 
	local continue_at_goal = false 
 
	local follow_dist = 0.0 
 
	local directly_above = false 
 
	local loop_path = false 
 
	local start_full_speed = false 
 
	return helicopter_fly_to_internal(name, speed, direct, follow, continue_at_goal, path, follow_dist, directly_above, loop_path, start_full_speed, reverse_sp) 
 
end 
 
 
 
-- Make a helicopter fly through one or more navpoints, ignoring any obstacles along the way. 
 
-- 
 
-- name:		(string) name of the helicopter 
 
-- speed:		(float) spead to fly at, in m/s (-1 to use default movement speed) 
 
-- path:		(string) name of a scripted_path NOTE:  SHOULD BE A PATH!!! 
 
-- 
 
-- Use the function only when you are absolutely sure there are no obstacles between waypoints. 
 
--	The helicopter will not move around them when using this function. 
 
--	WARNING:  This function will return immediately unlike other similar functions. (when do you end running in a loop?) 
 
-- 
 
function helicopter_fly_to_direct_loop(name, speed, path) 
 
	local direct = true 
 
	local follow = "" 
 
	local continue_at_goal = false 
 
	local follow_dist = 0.0 
 
	local directly_above = false 
 
	local loop_path = true 
 
 
 
	-- direct call to helicopter_fly_to_do because we don't want to wait for completion 
 
	if (helicopter_fly_to_do(name, speed, direct, follow, continue_at_goal, path, follow_dist, directly_above, loop_path)) then 
 
		-- immediate return 
 
		return true 
 
	else 
 
		return false 
 
	end 
 
end 
 
 
 
-- Make a helicopter fly through one or more navpoints, ignoring any obstacles along the way and without stopping. 
 
-- 
 
-- name:			(string) name of the helicopter 
 
-- speed:		(float) spead to fly at, in m/s (-1 to use default movement speed) 
 
-- path:		(string) name of a scripted_path, or single navpoint  
 
-- 
 
-- Use the function only when you are absolutely sure there are no obstacles between waypoints. The helicopter will not move around them when using this function. 
 
-- 
 
function helicopter_fly_to_direct_dont_stop(name, speed, path) 
 
	local direct = true 
 
	local follow = "" 
 
	local continue_at_goal = true 
 
	local follow_dist = 0.0 
 
	local directly_above = false 
 
	local loop_path = false 
 
	local start_full_speed = false 
 
	local reverse_sp = false 
 
	return helicopter_fly_to_internal(name, speed, direct, follow, continue_at_goal, path, follow_dist, directly_above, loop_path, start_full_speed, reverse_sp) 
 
end 
 
 
 
-- Make a helicopter fly through one or more navpoints while following a target, ignoring obstacles along the way. 
 
-- 
 
-- name:				(string) name of the helicopter 
 
-- speed:			(float) spead to fly at, in m/s (-1 to use default movement speed) 
 
-- target:			(string) name of target to follow 
 
-- path:				(string) name of a scripted_path, or single navpoint 
 
-- follow_dist:	(number) distance at which to follow 
 
-- directly_above:(bool) whether the helicopter should be directly above the target vehicle 
 
-- 
 
-- Use the function only when you are absolutely sure there are no obstacles between waypoints. The helicopter will not move around them when using this function. 
 
-- 
 
function helicopter_fly_to_direct_follow(name, speed, target, path, follow_dist, directly_above) 
 
	local direct = true 
 
	local follow = target 
 
	local continue_at_goal = false 
 
	local loop_path = false 
 
	local start_full_speed = false 
 
	local reverse_sp = false 
 
	return helicopter_fly_to_internal(name, speed, direct, follow, continue_at_goal, path, follow_dist, directly_above, loop_path, start_full_speed, reverse_sp) 
 
end 
 
 
 
-- Make a helicopter fly through one or more navpoints while following a target, ignoring obstacles along the way and without stopping. 
 
-- 
 
-- name:				(string) name of the helicopter 
 
-- speed:			(float) spead to fly at, in m/s (-1 to use default movement speed) 
 
-- target:			(string) name of target to follow 
 
-- path:				(string) name of a scripted_path, or single navpoint  
 
-- follow_dist:	(number) distance at which to follow 
 
-- directly_above:(bool) whether the helicopter should be directly above the target vehicle 
 
-- 
 
-- Use the function only when you are absolutely sure there are no obstacles between waypoints. The helicopter will not move around them when using this function. 
 
-- 
 
function helicopter_fly_to_direct_follow_dont_stop(name, speed, target, path, follow_dist, directly_above) 
 
	local direct = true 
 
	local follow = target 
 
	local continue_at_goal = true 
 
	local loop_path = false 
 
	local start_full_speed = false 
 
	local reverse_sp = false 
 
	return helicopter_fly_to_internal(name, speed, direct, follow, continue_at_goal, path, follow_dist, directly_above, loop_path, start_full_speed, reverse_sp) 
 
end 
 
 
 
-- Make a helicopter fly through one or more navpoints without stopping 
 
-- 
 
-- name:			(string) name of the helicopter 
 
-- speed:		(float) spead to fly at, in m/s (-1 to use default movement speed) 
 
-- path:		(string) name of a scripted_path, or single navpoint  
 
-- 
 
-- The helicopter will pathfind through the specified points, avoiding buildings and other obstacles along the way. 
 
-- 
 
function helicopter_fly_to_dont_stop(name, speed, path) 
 
	local direct = false 
 
	local follow = "" 
 
	local continue_at_goal = true 
 
	local follow_dist = 0.0 
 
	local directly_above = false 
 
	local loop_path = false 
 
	local start_full_speed = false 
 
	local reverse_sp = false 
 
	return helicopter_fly_to_internal(name, speed, direct, follow, continue_at_goal, path, follow_dist, directly_above, loop_path, start_full_speed, reverse_sp) 
 
end 
 
 
 
-- Add an item to a character's inventory. 
 
-- 
 
-- item_name:	(string) name of the item 
 
-- count:		(integer, optional) number of the item to give, or ammount of reserve ammo to give in the case of weapons (default = 1) 
 
-- character:	(string, optional) character to add item to (defaults to the local player) 
 
-- equip_now:	(boolean, optional) set to true to force the item to be immediately equipped (defaults to false) 
 
-- upgrade_flags: (integer, optional) upgrade flags to be applied to the weaopn (WEAPON_LEVEL2, WEAPON_LEVEL3, WEAPON_LEVEL4) (defaults to 0) 
 
--  
 
-- 
 
function inv_item_add(item_name, count, character, equip_now, upgrade_flags)	 
 
	if (type(item_name) == "table" ) then 
 
		local size = sizeof_table(item_name) 
 
		 
 
		for x=1, size, 1 do 
 
			inv_item_add_do(item_name[x], count[x], character, equip_now, upgrade_flags[x]) 
 
		end	 
 
	else 
 
		inv_item_add_do(item_name, count, character, equip_now, upgrade_flags) 
 
	end 
 
end 
 
 
 
-- Check if a script tag refers to a player. 
 
-- 
 
-- tag:		(string) tag to check 
 
-- 
 
-- returns:	(boolean) true if the tag refers to a player, else false 
 
-- 
 
function is_player_tag(tag) 
 
	-- Search the "closest" tags 
 
	if tag == CLOSEST_PLAYER or tag == CLOSEST_TEAM1 or tag == CLOSEST_TEAM2 then 
 
		return true 
 
	end 
 
	 
 
	-- Loop through the player tags 
 
	for i, player in pairs(PLAYER_TAG_LIST) do 
 
		if tag == player then 
 
			return true 
 
		end 
 
	end 
 
	 
 
	return false 
 
end 
 
 
 
-- Toggle level lights on or off 
 
-- 
 
-- enable:			(bool) whether to enable or disable all level lights 
 
-- toggle_style:	(enumeration, optional) method by which to toggle the level lights 
 
-- 
 
function level_lights_toggle_all(enable, toggle_style) 
 
	if (toggle_style == nil) then 
 
		toggle_style = LEVEL_LIGHT_TOGGLE_STYLE_FADE 
 
	end 
 
 
 
	level_light_enable_all_do(enable, toggle_style) 
 
end 
 
 
 
-- Check line of sight between two human-types 
 
-- 
 
-- name_one, name_two:  Names of the people to check los between 
 
-- 
 
 
 
-- Check for line-of-sight between two characters. 
 
-- 
 
-- name_one:	(string) name of the first character 
 
-- name_two:	(string) name of the second character 
 
-- 
 
-- returns:		(boolean) true if there is line-of-sight between the characters, or false if there is not 
 
-- 
 
-- Note that this function may take up to a 100 frames before returning a result and will block in the meantime. 
 
-- 
 
function los_check(name_one, name_two) 
 
	local retval = false 
 
	local current_los_result = -1 
 
	local num_retries = 0 
 
	-- It's possible for the los check to continually get deleted intead of processed 
 
	-- For example, if one of the humans is hidden.  So only try re-issuing 100 times. 
 
	while (current_los_result == -1 and num_retries < 100) do 
 
		current_los_result = los_check_do(name_one, name_two) 
 
		thread_yield() 
 
		num_retries = num_retries + 1 
 
	end 
 
	if (current_los_result == 0) then 
 
		return false 
 
	end 
 
	if (current_los_result == 1) then 
 
		return true 
 
	end 
 
	return false 
 
end 
 
 
 
-- Add a minimap marker and object indicator to a table (or single object) of navpoints, players, or script objects. 
 
-- 
 
-- object_name:				(table or string) table of objects (or a single object) to add object indicators to 
 
-- minimap_icon_name:		(string) name of the minimap icon 
 
-- object_indicator_id:		(integer) ID of the object indicator asset to use 
 
-- object_indicator_flags:	(integer, optional) flags for the object indicator (defaults to OI_FLAGS_DEFAULT) 
 
-- sync_type:				(enumeration, optional) synchronization type (SYNC_LOCAL = affects local player, SYNC_REMOTE = affects remote player, or SYNC_ALL = affects both players; defaults to SYNC_ALL) 
 
-- fade_dist:				(float, optional) distance to start fading if the OI_FLAG_FADE flag is specified (default, 100.0) 
 
-- tag_name:				(string, optional) name of the object tag to use to position the object indicator, or nil to use default obj position (default, nil) 
 
-- 
 
-- Returns the number of objects that were found (markers successfully added) 
 
-- 
 
function marker_add(object_name, minimap_icon_name, object_indicator_id, object_indicator_flags, sync_type, fade_dist, tag_name) 
 
	local num_added = 0 
 
 
 
	if (type(object_name) == "table") then 
 
		for i,obj in pairs(object_name) do 
 
			num_added = num_added + marker_add(obj, minimap_icon_name, object_indicator_id, object_indicator_flags, sync_type, tag_name) 
 
		end 
 
	elseif (type(object_name) == "string") then 
 
		if (object_indicator_flags == nil) then 
 
			object_indicator_flags = OI_FLAGS_DEFAULT 
 
		end 
 
 
 
		if (sync_type == nil) then 
 
			sync_type = SYNC_ALL 
 
		end 
 
		 
 
		if (fade_dist == nil) then 
 
			fade_dist = 100.0 
 
		end 
 
 
 
		minimap_icon_add_do( object_name, minimap_icon_name, "", 0, sync_type ) 
 
		-- If the object was found and the object indicator was added, increase the number by 1 
 
		if (object_indicator_add_do( object_name, object_indicator_id, object_indicator_flags, sync_type, fade_dist, tag_name ) == true) then 
 
			num_added = num_added + 1 
 
		end 
 
	end 
 
 
 
	return num_added 
 
end 
 
 
 
-- Add a minimap marker and ingame effect to all objects in a script group. 
 
-- 
 
-- group:						(string or table) name of a script group, or a script group table with a "name" field 
 
-- minimap_icon_name:		(string) name of the minimap icon 
 
-- object_indicator_id:		(integer) ID of the object indicator asset to use 
 
-- object_indicator_flags:	(integer, optional) flags for the object indicator (defaults to OI_FLAGS_DEFAULT) 
 
-- sync_type:					(enumeration, optional) synchronization type (SYNC_LOCAL = affects local player, SYNC_REMOTE = affects remote player, or SYNC_ALL = affects both players; defaults to SYNC_ALL) 
 
-- 
 
function marker_add_script_group( group, minimap_icon_name, object_indicator_id, object_indicator_flags, sync_type ) 
 
	if (object_indicator_flags == nil) then 
 
		object_indicator_flags = OI_FLAGS_DEFAULT 
 
	end 
 
 
 
	if (sync_type == nil) then 
 
		sync_type = SYNC_ALL 
 
	end 
 
 
 
	local group_name = group 
 
 
 
	if (type(group) == "table" and type(group.name) == "string") then 
 
		group_name = group.name 
 
	end 
 
 
 
	minimap_icon_add_script_group_do( group_name, minimap_icon_name, "", 0, sync_type ) 
 
	object_indicator_add_script_group_do( group_name, object_indicator_id, object_indicator_flags, sync_type ) 
 
end 
 
 
 
-- Add an object indicator to the npc in the script group closest to the target. 
 
-- 
 
-- target_name:				(string) name of the target to find the npc closest to 
 
-- group:						(string or table) name of a script group, or a script group table with a "name" field 
 
-- object_indicator_id:		(integer) ID of the object indicator asset to use 
 
-- object_indicator_flags:	(integer, optional) flags for the object indicator (defaults to OI_FLAGS_DEFAULT) 
 
-- sync_type:					(enumeration, optional) synchronization type (SYNC_LOCAL = affects local player, SYNC_REMOTE = affects remote player, or SYNC_ALL = affects both players; defaults to SYNC_ALL) 
 
-- 
 
-- returns:						(string) the name of the npc that got the marker, or "" if none. 
 
-- 
 
function marker_add_closest_to_target( target_name, group, object_indicator_id, object_indicator_flags, sync_type ) 
 
	if (object_indicator_flags == nil) then 
 
		object_indicator_flags = OI_FLAGS_DEFAULT 
 
	end 
 
 
 
	if (sync_type == nil) then 
 
		sync_type = SYNC_ALL 
 
	end 
 
 
 
	local group_name = group 
 
 
 
	if (type(group) == "table" and type(group.name) == "string") then 
 
		group_name = group.name 
 
	end 
 
	 
 
	return add_object_indicator_to_closest_npc( target_name, group_name, object_indicator_id, object_indicator_flags, sync_type ) 
 
end 
 
 
 
-- Add a minimap marker and ingame effect to a script trigger. 
 
-- 
 
-- trigger_name:				(string) name of the script trigger 
 
-- minimap_icon_name:		(string) name of the minimap icon 
 
-- ingame_effect_name:		(string, optional) name of ingame effect (defaults to nil) 
 
-- object_indicator_id:		(integer, optional) ID of the object indicator asset to use (defaults to nil) 
 
-- object_indicator_flags:	(integer, optional) flags for the object indicator (defaults to OI_FLAGS_LOCATION) 
 
-- sync_type:					(enumeration, optional) synchronization type (SYNC_LOCAL = affects local player, SYNC_REMOTE = affects remote player, or SYNC_ALL = affects both players; defaults to SYNC_ALL) 
 
-- fade_dist:					(float, optional) distance to start fading if the OI_FLAG_FADE flag is specified (default, 100.0) 
 
-- 
 
function marker_add_trigger( trigger_name, minimap_icon_name, ingame_effect_name, object_indicator_id, object_indicator_flags, sync_type, fade_dist ) 
 
	if (sync_type == nil) then 
 
		sync_type = SYNC_ALL 
 
	end 
 
	 
 
	if (fade_dist == nil) then 
 
		fade_dist = 100.0 
 
	end 
 
 
 
	minimap_icon_add_do(trigger_name, minimap_icon_name, "", 0, sync_type) 
 
 
 
	if (ingame_effect_name ~= nil) then 
 
		ingame_effect_add_trigger(trigger_name, ingame_effect_name, sync_type) 
 
	end 
 
 
 
	if (object_indicator_id ~= nil) then 
 
		if (object_indicator_flags == nil) then 
 
			object_indicator_flags = OI_FLAGS_LOCATION 
 
		end 
 
		object_indicator_add_do(trigger_name, object_indicator_id, object_indicator_flags, sync_type, fade_dist) 
 
	end 
 
end 
 
 
 
-- Remove a minimap marker and ingame effect from a table of objects or a single object 
 
-- 
 
-- object_name:(table or string) table of objects (or a single object) to remove markers and effects from 
 
-- sync_type:	(enumeration, optional) synchronization type (SYNC_LOCAL = affects local player, SYNC_REMOTE = affects remote player, or SYNC_ALL = affects both players; defaults to SYNC_ALL) 
 
-- 
 
function marker_remove(object_name, sync_type) 
 
		 
 
	if (type(object_name) == "table") then 
 
		for i,obj in pairs(object_name) do 
 
			marker_remove(obj, sync_type) 
 
		end 
 
	elseif (type(object_name) == "string") then 
 
		if (sync_type == nil) then 
 
			sync_type = SYNC_ALL 
 
		end 
 
 
 
		if object_name ~= nil and object_name ~= "" then 
 
			minimap_icon_remove_do( object_name, sync_type ) 
 
			object_indicator_remove_do( object_name, sync_type ) 
 
		end 
 
	end 
 
		 
 
end 
 
 
 
-- Remove a minimap marker and ingame effect from a script group. 
 
-- 
 
-- group:		(string or table) name of a script group, or a script group table with a "name" field 
 
-- sync_type:	(enumeration, optional) synchronization type (SYNC_LOCAL = affects local player, SYNC_REMOTE = affects remote player, or SYNC_ALL = affects both players; defaults to SYNC_ALL) 
 
-- 
 
function marker_remove_script_group( group, sync_type ) 
 
	if (sync_type == nil) then 
 
		sync_type = SYNC_ALL 
 
	end 
 
 
 
	local group_name = group 
 
 
 
	if (type(group) == "table" and type(group.name) == "string") then 
 
		group_name = group.name 
 
	end 
 
	 
 
	minimap_icon_remove_script_group_do( group_name, sync_type ) 
 
	object_indicator_remove_script_group_do( group_name, sync_type ) 
 
end 
 
 
 
-- Remove a minimap marker and ingame effect from a script trigger. 
 
-- 
 
-- trigger_name:	(string) name of the script trigger 
 
-- sync_type:		(enumeration, optional) synchronization type (SYNC_LOCAL = affects local player, SYNC_REMOTE = affects remote player, or SYNC_ALL = affects both players; defaults to SYNC_ALL) 
 
-- 
 
function marker_remove_trigger( trigger_name, sync_type ) 
 
	if (sync_type == nil) then 
 
		sync_type = SYNC_ALL 
 
	end 
 
	 
 
	minimap_icon_remove_do(trigger_name, sync_type) 
 
	ingame_effect_remove_trigger(trigger_name, sync_type) 
 
	object_indicator_remove_do(trigger_name, sync_type) 
 
end 
 
 
 
-- Add a minimap marker to a trigger, navpoint, player, or script object, with an optional radius around the marker. 
 
-- 
 
-- object_name:			(string or table) name of the object or a nested table of names 
 
-- minimap_icon_name:	(string) name of the minimap icon 
 
-- bitmap_glow_name:		(string, optional) name of minimap icon glow (defaults to nil) 
 
-- radius:					(float, optional) radius of the minimap marker, in meters (defaults to 0.0) 
 
-- sync_type:				(enumeration, optional) synchronization type (SYNC_LOCAL = affects local player, SYNC_REMOTE = affects remote player, or SYNC_ALL = affects both players; defaults to SYNC_ALL) 
 
-- 
 
function minimap_icon_add(object_name, minimap_icon_name, bitmap_glow_name, radius, sync_type) 
 
	if (type(object_name) == "string") then 
 
		minimap_icon_add_do( object_name, minimap_icon_name, bitmap_glow_name, radius, sync_type ) 
 
	elseif (type(object_name) == "table") then 
 
		for i,obj in pairs(object_name) do 
 
			minimap_icon_add(obj, minimap_icon_name, bitmap_glow_name, radius, sync_type) 
 
		end 
 
	end 
 
end 
 
 
 
-- Add a minimap radius around an object. 
 
-- 
 
-- object_name:(string or table) name of the object (can be a navpoint, trigger, or mover) or a nested table of names 
 
-- radius:		(float, optional) radius of the minimap marker, in meters (defaults to 0.0) 
 
-- sync_type:	(enumeration, optional) synchronization type (SYNC_LOCAL = affects local player, SYNC_REMOTE = affects remote player, or SYNC_ALL = affects both players; defaults to SYNC_ALL) 
 
-- 
 
function minimap_icon_add_radius(object_name, radius, sync_type) 
 
	if (type(object_name) == "string") then 
 
		minimap_icon_add_radius_do( object_name, radius, sync_type ) 
 
	elseif (type(object_name) == "table") then 
 
		for i,obj in pairs(object_name) do 
 
			minimap_icon_add_radius(obj, radius, sync_type) 
 
		end 
 
	end 
 
end 
 
 
 
-- Add a minimap marker to all objects in a script group, with an optional radius around the marker. 
 
-- 
 
-- group:					(string or table) name of a script group, or a script group table with a "name" field 
 
-- minimap_icon_name:	(string) name of the minimap icon 
 
-- bitmap_glow_name:		(string, optional) name of minimap icon glow (defaults to nil) 
 
-- radius:					(float, optional) radius of the minimap marker, in meters (defaults to 0.0) 
 
-- sync_type:				(enumeration, optional) synchronization type (SYNC_LOCAL = affects local player, SYNC_REMOTE = affects remote player, or SYNC_ALL = affects both players; defaults to SYNC_ALL) 
 
-- 
 
function minimap_icon_add_script_group(group, minimap_icon_name, bitmap_glow_name, radius, sync_type) 
 
	local group_name = group 
 
 
 
	if (type(group) == "table" and type(group.name) == "string") then 
 
		group_name = group.name 
 
	end 
 
 
 
	minimap_icon_add_script_group_do(group_name, minimap_icon_name, bitmap_glow_name, radius, sync_type) 
 
end 
 
 
 
-- Remove the minimap marker from a trigger, navpoint, player, or script object. 
 
-- 
 
-- object_name:	(string or table) name of the object or a nested table of names 
 
-- sync_type:		(enumeration, optional) synchronization type (SYNC_LOCAL = affects local player, SYNC_REMOTE = affects remote player, or SYNC_ALL = affects both players; defaults to SYNC_ALL) 
 
-- 
 
function minimap_icon_remove(object_name, sync_type) 
 
	if (type(object_name) == "string") then 
 
		minimap_icon_remove_do(object_name, sync_type) 
 
	elseif (type(object_name) == "table") then 
 
		for i,obj in pairs(object_name) do 
 
			minimap_icon_remove(obj, sync_type) 
 
		end 
 
	end 
 
end 
 
 
 
-- Remove a minimap radius from an object. 
 
-- 
 
-- object_name:	(string or table) name of the object (can be a navpoint, trigger, or mover) or a nested table of names 
 
-- sync_type:		(enumeration, optional) synchronization type (SYNC_LOCAL = affects local player, SYNC_REMOTE = affects remote player, or SYNC_ALL = affects both players; defaults to SYNC_ALL) 
 
-- 
 
function minimap_icon_remove_radius(object_name, sync_type) 
 
	if (type(object_name) == "string") then 
 
		minimap_icon_remove_radius_do( object_name, sync_type ) 
 
	elseif (type(object_name) == "table") then 
 
		for i,obj in pairs(object_name) do 
 
			minimap_icon_remove_radius(obj, sync_type) 
 
		end 
 
	end 
 
end 
 
 
 
-- Remove a minimap marker from all objects in a script group. 
 
-- 
 
-- group:		(string or table) name of a script group, or a script group table with a "name" field 
 
-- sync_type:	(enumeration, optional) synchronization type (SYNC_LOCAL = affects local player, SYNC_REMOTE = affects remote player, or SYNC_ALL = affects both players; defaults to SYNC_ALL) 
 
-- 
 
function minimap_icon_remove_script_group(group, sync_type) 
 
	local group_name = group 
 
 
 
	if (type(group) == "table" and type(group.name) == "string") then 
 
		group_name = group.name 
 
	end 
 
 
 
	minimap_icon_remove_script_group_do(group_name, sync_type) 
 
end 
 
 
 
-- Re-enable player controls if they have been disabled by a mission start fade-in. 
 
-- 
 
-- Should be called in mission cleanup by all missions that use mission_start_fade_in / mission_start_fade_out. 
 
-- 
 
function mission_cleanup_maybe_reenable_player_controls() 
 
   if ( Player_controls_disabled_by_mission_start_fadeout ) then 
 
      player_controls_enable( LOCAL_PLAYER ) 
 
      if ( coop_is_active() ) then 
 
         player_controls_enable( REMOTE_PLAYER ) 
 
      end 
 
      Player_controls_disabled_by_mission_start_fadeout = false 
 
   end 
 
end 
 
 
 
-- Generates a mission help message and updates the Objectives screen. 
 
-- 
 
-- tag:		(string) name of help text to display (from mission_help.xtbl) 
 
-- string1:	(string, optional) first replacement string 
 
-- string2:	(string, optional) second replacement string 
 
-- sync:		(enumeration, optional) synchronization type (SYNC_LOCAL = affects local player, SYNC_REMOTE = affects remote player, or SYNC_ALL = affects both players; defaults to SYNC_ALL) 
 
-- 
 
-- Displays a localized help string in the help text area of the HUD. 
 
-- There are two replacement strings available which will replace up to 
 
-- instances of %s in the table text. 
 
-- 
 
-- The duration of the text is specified in the mission_help.xtbl file.  If 
 
-- a value of 0.0 is specified, the default duration is 5 seconds. 
 
-- 
 
-- Example: 
 
-- 
 
-- Entry in the mission_help.xtbl file has name of "KillGangsta" and text of 
 
-- "Kill %s more %s members!" 
 
-- 
 
-- Executing the lua statement 'mission_help_table("KillGangsta", 5, "Vice King")'  
-- results in the help output "Kill 5 more Vice King members!"  
 
-- 
 
function mission_help_table(tag, string1, string2, sync) 
 
	mission_help_table_do(tag, true, false, string1, string2, sync, 0) 
 
end 
 
 
 
-- Generates a mission help nag message, but does not update the Objectives screen. 
 
-- 
 
-- tag:		(string) name of help text to display (from mission_help.xtbl) 
 
-- string1:	(string, optional) first replacement string 
 
-- string2:	(string, optional) second replacement string 
 
-- sync:		(enumeration, optional) synchronization type (SYNC_LOCAL = affects local player, SYNC_REMOTE = affects remote player, or SYNC_ALL = affects both players; defaults to SYNC_ALL) 
 
-- 
 
function mission_help_table_nag(tag, string1, string2, sync) 
 
	mission_help_table_do(tag, false, false, string1, string2, sync, 0) 
 
end 
 
 
 
-- Enables player controls and fades in the screen. 
 
-- 
 
-- Should be used when the mission is loading groups at start. Prevents the player from moving or seeing anything happening. Use in conjunction with mission_start_fade_out. 
 
-- 
 
-- unlock_controls:		(boolean, optional) Set to TRUE to unlock player controls, FALSE to keep player controls locked (must be unlocked manually) [defaults to true] 
 
-- 
 
function mission_start_fade_in(unlock_controls) 
 
	if (unlock_controls == nil) then 
 
		unlock_controls = true 
 
	end 
 
 
 
	mip_streaming_pause(false, "mission_start_fade_in") 
 
	object_spawn_pause(false, "mission_start_fade_in") 
 
     
 
	fade_in( START_FADE_IN_TIME ) 
 
	fade_in_block() 
 
	 
 
	if (unlock_controls) then 
 
		player_controls_enable( LOCAL_PLAYER ) 
 
		if ( coop_is_active() ) then 
 
			player_controls_enable( REMOTE_PLAYER ) 
 
		end 
 
		Player_controls_disabled_by_mission_start_fadeout = false 
 
	end 
 
end 
 
 
 
-- Disables player controls and instantly fades out the screen. 
 
-- 
 
-- Should be used when the mission is loading groups at start. Prevents the player from moving or seeing anything happening. Use in conjunction with mission_start_fade_in. 
 
-- 
 
function mission_start_fade_out(fade_time) 
 
	if (fade_time == nil) then 
 
		fade_time = START_FADE_OUT_TIME 
 
	end 
 
	 
 
	fade_out( fade_time ) 
 
	player_controls_disable( LOCAL_PLAYER ) 
 
	if ( coop_is_active() ) then 
 
		player_controls_disable( REMOTE_PLAYER ) 
 
	end 
 
	Player_controls_disabled_by_mission_start_fadeout = true 
 
	fade_out_block() 
 
 
 
	mip_streaming_pause(true, "mission_start_fade_out") 
 
	object_spawn_pause(true, "mission_start_fade_out") 
 
end 
 
 
 
-- Add a mission waypoint at a navpoint. 
 
-- 
 
-- navpoint:	(string) name of navpoint, NPC, vehicle, or trigger 
 
-- sync_type:	(enumeration, optional) synchronization type (SYNC_LOCAL = affects local player, SYNC_REMOTE = affects remote player, or SYNC_ALL = affects both players; defaults to SYNC_ALL) 
 
-- 
 
-- If a mission waypoint currently exists, it will be removed and replaced by this call. 
 
-- 
 
function mission_waypoint_add(navpoint, sync_type) 
 
	mission_waypoint_remove(sync_type) 
 
	Mission_waypoint = waypoint_add(navpoint, sync_type) 
 
end 
 
 
 
-- Remove the current mission waypoint. 
 
-- 
 
-- sync_type:	(enumeration, optional) synchronization type (SYNC_LOCAL = affects local player, SYNC_REMOTE = affects remote player, or SYNC_ALL = affects both players; defaults to SYNC_ALL) 
 
-- 
 
function mission_waypoint_remove(sync_type) 
 
	if (Mission_waypoint ~= -1) then	 
 
		waypoint_remove(sync_type) 
 
		Mission_waypoint = -1 
 
	end 
 
end 
 
 
 
-- move_to speed defines 
 
MOVE_TO_WALK = 1 
 
MOVE_TO_RUN = 2 
 
MOVE_TO_SPRINT = 3 
 
 
 
-- Make a character pathfind through one or more navpoints. 
 
--  
 
-- name:					(string) name of the character 
 
-- dest:					(string) name of the scripted_path spline to follow, or navpoint to go to 
 
-- speed:				(integer, optional) movement speed (1 = walk, 2 = run, 3 = sprint; defaults to 1) 
 
-- retry_on_failure:	(boolean, optional) set to true to keep retrying if path calculation fails (defaults to false) 
 
-- move_and_fire:		(boolean, optional) set to true to allow the NPC to fire on the move (defaults to false) 
 
-- 
 
-- This function blocks until the character has finished pathfinding. 
 
 
 
-- 
 
function move_to(name, dest, ...) 
 
	local num_args, speed, retry_on_failure, move_and_fire, path_index 
 
 
 
	-- Wait until the resource is loaded. 
 
	character_wait_for_loaded_resource(name) 
 
			 
 
	num_args = arg.n 
 
 
 
	-- get move and fire 
 
	if ( (num_args >= 3) and (type(arg[num_args]) == "boolean") ) then 
 
		move_and_fire = arg[num_args] 
 
		num_args = num_args - 1 
 
	else 
 
		move_and_fire = false 
 
	end 
 
 
 
	-- get retry 
 
	if ( (num_args >= 2) and (type(arg[num_args]) == "boolean") ) then 
 
		retry_on_failure = arg[num_args] 
 
		num_args = num_args - 1 
 
	else 
 
		retry_on_failure = false 
 
	end 
 
	 
 
	-- get speed 
 
	if ( (num_args >= 1) and (type(arg[num_args]) == "number") ) then 
 
		speed = arg[num_args] 
 
	else 
 
		speed = 1 
 
	end 
 
	 
 
	while( not character_is_ready( name ) ) do 
 
		thread_yield() 
 
	end 
 
	 
 
	if (type(dest) == "string") then 
 
		-- Is it a scripted_path or a single navpoint? 
 
		if ( path_name_is_path(dest)) then 
 
			local	idx 
 
 
 
			idx = 0 
 
			while (1) do 
 
				path_index = move_to_do(name, dest, speed, retry_on_failure, move_and_fire, true, idx) 
 
				if (not (path_index == 0)) then 
 
					-- Keep checking for done until the character dies or reaches the destination 
 
					while ( character_is_dead( name ) == false and 
 
						not( move_to_check_done(path_index,  name, dest, speed, retry_on_failure, move_and_fire, true, idx ) ) ) do 
 
						thread_yield() 
 
					end 
 
 
 
					idx = idx + 1 
 
				else 
 
					return false 
 
				end 
 
			end 
 
		else 
 
			path_index = move_to_do(name, dest, speed, retry_on_failure, move_and_fire, false, 0) 
 
			if (not (path_index == 0)) then 
 
			-- Keep checking for done until the character dies or reaches the destination 
 
				while ( character_is_dead( name ) == false and 
 
					not( move_to_check_done(path_index,  name, dest, speed, retry_on_failure, move_and_fire, false, 0 ) ) ) do 
 
					thread_yield() 
 
				end 
 
			else 
 
				return false 
 
			end 
 
		end 
 
	end 
 
	 
 
	return (not character_is_dead( name )) 
 
end 
 
 
 
-- Make a character pathfind through one or more navpoints. Returns if at any point the character dies. 
 
--  
 
-- name:				(string) name of the character 
 
-- dest:				(string) name of the scripted_path spline to follow, or navpoint to go to 
 
-- speed:				(integer, optional) movement speed (1 = walk, 2 = run, 3 = sprint; defaults to 1) 
 
-- retry_on_failure:	(boolean, optional) set to true to keep retrying if path calculation fails (defaults to false) 
 
-- move_and_fire:		(booelan, optional) set to true to allow the NPC to fire on the move (defaults to false) 
 
-- 
 
-- This function blocks until the character has finished pathfinding, or until the character dies. This function otherwise behaves exactly like move_to. 
 
-- 
 
function move_to_safe(name, dest, ...) 
 
 
 
   -- If the character is dead, then forget about pathfinding 
 
   if ( character_is_dead( name ) == true ) then 
 
      return false 
 
   end 
 
 
 
	local num_args, speed, retry_on_failure, move_and_fire, path_index 
 
 
 
	-- Wait until the resource is loaded. 
 
	character_wait_for_loaded_resource(name) 
 
			 
 
	num_args = arg.n 
 
 
 
	-- get move and fire 
 
	if ( (num_args >= 3) and (type(arg[num_args]) == "boolean") ) then 
 
		move_and_fire = arg[num_args] 
 
		num_args = num_args - 1 
 
	else 
 
		move_and_fire = false 
 
	end 
 
 
 
	-- get retry 
 
	if ( (num_args >= 2) and (type(arg[num_args]) == "boolean") ) then 
 
		retry_on_failure = arg[num_args] 
 
		num_args = num_args - 1 
 
	else 
 
		retry_on_failure = false 
 
	end 
 
	 
 
	-- get speed 
 
	if ( (num_args >= 1) and (type(arg[num_args]) == "number") ) then 
 
		speed = arg[num_args] 
 
	else 
 
		speed = 1 
 
	end 
 
	 
 
	while( not character_is_ready( name )) do 
 
		thread_yield() 
 
 
 
		-- Don't pathfind if character is dead or entered a vehicle 
 
		if (character_is_dead(name) or character_is_in_vehicle( name )) then 
 
			return false 
 
		end 
 
	end 
 
 
 
	-- Character may have entered a vehicle in the same frame that it became ready. 
 
		if (character_is_in_vehicle( name )) then 
 
			return false 
 
		end 
 
 
 
	if (type(dest) == "string") then 
 
		-- Is it a path list of navpoints are a single navpoint? 
 
		if (path_name_is_path(dest)) then 
 
			local	idx 
 
 
 
			idx = 0 
 
			while (1) do 
 
				path_index = move_to_do(name, dest, speed, retry_on_failure, move_and_fire, true, idx) 
 
				if (not (path_index == 0)) then 
 
					while(not(move_to_check_done(path_index, name, dest, speed, retry_on_failure, move_and_fire, true, idx))) do 
 
						thread_yield() 
 
 
 
						-- Don't pathfind if character is dead or entered a vehicle 
 
						if (character_is_dead(name) or character_is_in_vehicle( name )) then 
 
							return false 
 
						end 
 
					end 
 
 
 
					idx = idx + 1 
 
				else 
 
					return false 
 
				end 
 
			end 
 
		else 
 
			path_index = move_to_do(name, dest, speed, retry_on_failure, move_and_fire, false, 0) 
 
			if (not (path_index == 0)) then 
 
				while(not(move_to_check_done(path_index, name, dest, speed, retry_on_failure, move_and_fire, false, 0))) do 
 
					thread_yield() 
 
 
 
					-- Don't pathfind if character is dead or entered a vehicle 
 
					if (character_is_dead(name) or character_is_in_vehicle( name )) then 
 
						return false 
 
					end 
 
				end 
 
			else 
 
				return false 
 
			end 
 
		end 
 
	end 
 
	 
 
	return true 
 
end 
 
 
 
-- Add object indicators to a table (or single object) of NPCs, vehicles, items, or anything else that's set up in object_indicator.h 
 
-- 
 
-- object_name:(table or string) table of names of the objects to add indicators to (or a single object, table of tables, etc.) 
 
-- asset_id:	(int) ID of the indicator asset to use 
 
-- flags:		(int, optional) flags for the object indicator (defaults to OI_FLAGS_DEFAULT) 
 
-- sync_type:	(enumeration, optional) synchronization type (SYNC_LOCAL = affects local player, SYNC_REMOTE = affects remote player, or SYNC_ALL = affects both players; defaults to SYNC_ALL) 
 
-- fade_dist:					(float, optional) distance to start fading if the OI_FLAG_FADE flag is specified (default, 100.0) 
 
-- 
 
-- Returns the number of object indicators that were added 
 
-- 
 
function object_indicator_add(object_name, asset_id, flags, sync_type, fade_dist) 
 
	local num_added = 0 
 
	if (type(object_name) == "table") then 
 
		for i,obj in pairs(object_name) do 
 
			num_added = num_added + object_indicator_add(obj, asset_id, flags, sync_type) 
 
		end 
 
	elseif (type(object_name) == "string") then 
 
		if (sync_type == nil) then 
 
			sync_type = SYNC_ALL 
 
		end 
 
		 
 
		if (fade_dist == nil) then 
 
			fade_dist = 100.0 
 
		end 
 
 
 
		if (flags == nil) then 
 
			flags = OI_FLAGS_DEFAULT 
 
		end 
 
 
 
		-- If the object was found, and the indicator was added, increase the number by 1 
 
		if (object_indicator_add_do(object_name, asset_id, flags, sync_type, fade_dist) == true) then 
 
			num_added = num_added + 1 
 
		end 
 
	end 
 
 
 
	return num_added 
 
end 
 
 
 
-- Add an object indicator to each object in a group of script objects 
 
-- 
 
-- group:		(string or table) name of a script group, or a script group table with a "name" field 
 
-- asset_id:	(integer) ID of the object indicator asset to use 
 
-- flags:		(integer, optional) flags for the object indicator (defaults to OI_FLAGS_DEFAULT) 
 
-- sync_type:	(enumeration, optional) synchronization type (SYNC_LOCAL = affects local player, SYNC_REMOTE = affects remote player, or SYNC_ALL = affects both players; defaults to SYNC_ALL) 
 
-- 
 
function object_indicator_add_script_group(group, asset_id, flags, sync_type) 
 
	local group_name = group 
 
 
 
	if (type(group) == "table" and type(group.name) == "string") then 
 
		group_name = group.name 
 
	end 
 
 
 
	if (flags == nil) then 
 
		flags = OI_FLAGS_DEFAULT 
 
	end 
 
 
 
	if (sync_type == nil) then 
 
		sync_type = SYNC_ALL 
 
	end 
 
	 
 
	object_indicator_add_script_group_do(group_name, asset_id, flags, sync_type) 
 
end 
 
 
 
-- Remove object indicators from a table (or single object) of NPCs, vehicles, items, or anything else that's set up in object_indicator.h 
 
-- 
 
-- object_name:(table or string) table of names of the objects to remove indicators from (or a single object, table of tables, etc.) 
 
-- sync_type:	(enumeration, optional) synchronization type (SYNC_LOCAL = affects local player, SYNC_REMOTE = affects remote player, or SYNC_ALL = affects both players; defaults to SYNC_ALL) 
 
-- 
 
function object_indicator_remove(object_name, sync_type) 
 
	if (type(object_name) == "table") then 
 
		for i,obj in pairs(object_name) do 
 
			object_indicator_remove(obj, sync_type) 
 
		end 
 
	elseif (type(object_name) == "string") then 
 
		if (sync_type == nil) then 
 
			sync_type = SYNC_ALL 
 
		end 
 
 
 
		object_indicator_remove_do(object_name, sync_type) 
 
	end 
 
end 
 
 
 
-- Removes an object indicator from each object in a group of script objects 
 
-- 
 
-- group:		(string or table) name of a script group, or a script group table with a "name" field 
 
-- sync_type:	(enumeration, optional) synchronization type (SYNC_LOCAL = affects local player, SYNC_REMOTE = affects remote player, or SYNC_ALL = affects both players; defaults to SYNC_ALL) 
 
-- 
 
function object_indicator_remove_script_group(group, sync_type) 
 
	local group_name = group 
 
 
 
	if (type(group) == "table" and type(group.name) == "string") then 
 
		group_name = group.name 
 
	end 
 
 
 
	if (sync_type == nil) then 
 
		sync_type = SYNC_ALL 
 
	end 
 
	 
 
	object_indicator_remove_script_group_do(group_name, sync_type) 
 
end 
 
 
 
-- Register a callback function for when any human is killed 
 
-- 
 
-- function_name:	(string) name of the function 
 
-- 
 
-- When the callback is executed, the following parameters are passed to it: 
 
--  - (string) name of the attacker 
 
--  - (string) name of the team of the victim 
 
-- 
 
function on_random_human_killed( function_name ) 
 
	on_random_obj_killed_do( function_name, 1 ) 
 
end 
 
 
 
-- Register a callback function for when any mover is killed 
 
-- 
 
-- function_name:	(string) name of the function 
 
-- 
 
-- When the callback is executed, the following parameters are passed to it: 
 
--  - (string) name of the attacker 
 
--  - (string) property name (from levels.xtbl) of the mover destroyed 
 
-- 
 
function on_random_mover_killed( function_name, mission_name ) 
 
	on_random_obj_killed_do( function_name, 2 ) 
 
end 
 
 
 
-- Register a callback function for when any object in an object destroyed script is destroyed during a mission. 
 
-- 
 
-- function_name:	(string) name of the function 
 
-- 
 
-- The object destroyed script is generated by Art. It contains the chunk number (all three digits, including leading zeroes) 
 
-- and the object name. The file is named exactly the same as the mission/stronghold/activity and has a ".ods" file extension. 
 
-- 
 
function on_random_ods_killed( function_name ) 
 
	on_random_obj_killed_do( function_name, 4 ) 
 
end 
 
 
 
-- Register a callback function for when any vehicle is destroyed 
 
-- 
 
-- function_name:	(string) name of the function 
 
-- 
 
-- When the callback is executed, the following parameters are passed to it: 
 
--  - (string) name of the attacker 
 
--  - (string) name of the team of the vehicle's driver 
 
-- 
 
function on_random_vehicle_killed( function_name ) 
 
	on_random_obj_killed_do( function_name, 3 ) 
 
end 
 
 
 
-- Open an interface dialog box. 
 
-- 
 
-- title_tag:	(string) identifying tag for the title text 
 
-- body_tag:	(string) identifying tag for the body text 
 
-- tag1:		(string) identifying tag for selection 1 text 
 
-- tag2:		(string) identifying tag for selection 2 text 
 
-- 
 
-- returns:		(integer) index corresponding to which option was selected (0 corresponds to the first option, 1 corresponds to the second option, or -1 if the dialog was forced closed) 
 
-- 
 
function open_vint_dialog(title_tag, body_tag, tag1, tag2) 
 
	open_vint_dialog_do(title_tag, body_tag, tag1, tag2) 
 
 
 
	local		check_value = open_vint_dialog_check_done() 
 
 
 
	while (check_value == -2) do 
 
		thread_yield() 
 
		check_value = open_vint_dialog_check_done() 
 
	end 
 
 
 
	return check_value 
 
end 
 
 
 
-- Add members to the player's party. 
 
-- 
 
-- names:	(one or more strings) names of NPCs to add to the player's party 
 
-- player:	(string, optional) name of the player to add party members to (defaults to closest player) 
 
-- 
 
-- This function will automatically dismiss followers to make room for the specified NPCs. 
 
-- 
 
-- Example: 
 
-- 
 
--		party_add("NPC1", "NPC2", "NPC3")  
-- 
 
-- Adds three NPCs to the closeset player�s party, "NPC1", "NPC2", and "NPC3". 
 
-- 
 
function party_add(...) 
 
	local player = CLOSEST_PLAYER 
 
	 
 
	if is_player_tag(arg[arg.n]) then 
 
		player = arg[arg.n] 
 
		arg.n = arg.n - 1 
 
	end 
 
		 
 
	party_add_do(player, arg, false) 
 
end 
 
 
 
-- Add members to the player's party, but only if there is room. 
 
-- 
 
-- names:	(one or more strings) names of NPCs to add to the player's party 
 
-- player:	(string, optional) name of the player to add party members to (defaults to closest player) 
 
-- 
 
function party_add_optional(...) 
 
	local player = CLOSEST_PLAYER 
 
	 
 
	if is_player_tag(arg[arg.n]) then 
 
		player = arg[arg.n] 
 
		arg.n = arg.n - 1 
 
	end 
 
		 
 
	party_add_do(player, arg, true) 
 
end 
 
 
 
function party_add_ignore_limits(...) 
 
	local player = CLOSEST_PLAYER 
 
	 
 
	if is_player_tag(arg[arg.n]) then 
 
		player = arg[arg.n] 
 
		arg.n = arg.n - 1 
 
	end 
 
		 
 
	party_add_do(player, arg, false, true) 
 
end 
 
 
 
 
 
-- Dismiss members from the player's party 
 
-- 
 
-- names:	(one or more strings) names of NPCs to remove from the player's party 
 
-- 
 
-- Example: 
 
-- 
 
--		party_dismiss("NPC1", "NPC2", "NPC3")  
-- 
 
-- Dismisses three NPCs from the player�s party, "NPC1", "NPC2", and "NPC3". 
 
-- 
 
function party_dismiss(...) 
 
	party_dismiss_do(arg) 
 
end 
 
 
 
-- Override an NPC's persona on a per-situation basis. 
 
-- 
 
-- name:			(string) name of the NPC 
 
-- situation:	(string, or table of strings) persona situation(s) to override (as defined in persona_situations.xtbl) 
 
-- audio:		(string, optional) audio file to force play, or "" to override with silence (defaults to "") 
 
-- count:		(integer, optional) number of times the override should occur, or -1 to override indefinitely (defaults to -1) 
 
-- 
 
-- The specified audio will play in the specified situation instead of the default voice line(s) defined in persona.xtbl. 
 
-- 
 
function persona_override_character_start(character, situation, audio, count) 
 
 
 
	if type(situation) == "table" then 
 
		for i, persona_situation in pairs(situation) do 
 
			local trigger_prefix = persona_trigger_get_player_prefix(character) 
 
			persona_override_character_start_do(character, persona_situation, trigger_prefix .. audio, count) 
 
		end 
 
	else 
 
		local trigger_prefix = persona_trigger_get_player_prefix(character) 
 
		persona_override_character_start_do(character, situation, trigger_prefix .. audio, count) 
 
	end 
 
 
 
end 
 
 
 
-- Reset the situational persona overrides for an NPC. 
 
-- 
 
-- name:			(string) name of the NPC 
 
-- situation:	(string, or table of strings) persona situation(s) to reset back to normal (as defined in persona_situations.xtbl) 
 
-- 
 
function persona_override_character_stop(character, situation) 
 
 
 
	if type(situation) == "table" then 
 
		for i, persona_situation in pairs(situation) do 
 
			persona_override_character_stop_do(character, persona_situation) 
 
		end 
 
	else 
 
		persona_override_character_stop_do(character, situation) 
 
	end 
 
 
 
end 
 
 
 
-- Override a group of personas for a single situation. 
 
-- 
 
-- persona_list:	(string, or table of strings) names of personas to override 
 
-- situation:		(string, or table of strings) persona situation(s) to override (as defined in persona_situations.xtbl) 
 
-- tag_suffix:		(string) common trigger suffix 
 
-- 
 
-- Example: 
 
-- 
 
--	The following 3 lines: 
 
-- 
 
--		EX_PERSONAS	=	{	["AM_Gang1"]	=	"AMGNG1";  
--								["AM_Gang2"]	=	"AMGNG2"; 
 
--							} 
 
--		persona_override_group(EX_PERSONAS, POT_SITUATIONS[POT_ATTACK], "EX01_ATTACK") 
 
-- 
 
--	Are equivalent to: 
 
-- 
 
--		persona_override_persona_start("AM_Gang1", "threat - alert (group attack)",	"AMGNG1_EX01_ATTACK")  
--		persona_override_persona_start("AM_Gang1", "threat - alert (solo attack)",		"AFGNG1_EX01_ATTACK")  
--		persona_override_persona_start("AM_Gang2", "threat - alert (group attack)",	"AMGNG2_EX01_ATTACK")  
--		persona_override_persona_start("AM_Gang2", "threat - alert (solo attack)",		"AFGNG2_EX01_ATTACK")  
-- 
 
-- Generally this function will be used with one of 4 predefined gang persona tables: 
 
-- 
 
--		BROTHERHOOD_PERSONAS 
 
--		RONIN_PERSONAS 
 
--		SAINTS_PERSONAS 
 
--		SAMEDI_PERSONAS 
 
-- 
 
function persona_override_group_start(persona_list, situation, tag_suffix) 
 
	for persona, tag_prefix in pairs(persona_list) do 
 
		persona_override_persona_start(persona, situation, tag_prefix .. "_" .. tag_suffix) 
 
	end 
 
end 
 
 
 
-- Reset the situational override for a group of personas. 
 
-- 
 
-- persona_list:	(string, or table of strings) names of personas to override 
 
-- situation:		(string, or table of strings) persona situation(s) to override (as defined in persona_situations.xtbl) 
 
-- 
 
function persona_override_group_stop(persona_list, situation) 
 
	for persona, tag_prefix in pairs(persona_list) do 
 
		persona_override_persona_stop(persona, situation) 
 
	end 
 
end 
 
 
 
-- Override all script NPCs with a particular persona in a given situation (or situations). 
 
-- 
 
-- persona:		(string) name of the persona 
 
-- situation:	(string, or table of strings) persona situation(s) to override (as defined in persona_situations.xtbl) 
 
-- audio:		(string, optional) audio file to force play, or "" to override with silence (defaults to "") 
 
-- count:		(integer, optional) number of times the override should occur, or -1 to override indefinitely (defaults to -1) 
 
-- 
 
function persona_override_persona_start_old(persona, situation, audio, count) 
 
 
 
	if type(situation) == "table" then 
 
		for i, persona_situation in pairs(situation) do 
 
			persona_override_persona_start_do(persona, persona_situation, audio, count) 
 
		end 
 
	else 
 
		persona_override_persona_start_do(persona, situation) 
 
	end 
 
 
 
end 
 
 
 
-- Reset all script NPCs with a particular persona in a given situation (or situations). 
 
-- 
 
-- persona:		(string) name of the persona 
 
-- situation:	(string, or table of strings) persona situation(s) to reset (as defined in persona_situations.xtbl) 
 
-- 
 
function persona_override_persona_stop_old(persona, situation) 
 
 
 
	if type(situation) == "table" then 
 
		for i, persona_situation in pairs(situation) do 
 
			persona_override_persona_stop_do(persona, persona_situation) 
 
		end 
 
	else 
 
		persona_override_persona_stop_do(persona, situation) 
 
	end 
 
 
 
end 
 
 
 
-- Get the prefix that should be prepended to the character's persona triggers. 
 
-- 
 
-- name:		(string) name of the character 
 
-- 
 
--	returns:	(string) prefix to prepend to the trigger 
 
--  
 
function persona_trigger_get_player_prefix(name) 
 
 
 
	local voice_prefixes = {[0] = "WM", [1] = "BM", [2] = "WMA", [3] = "WF", [4] = "BF", [5] = "HF", [6] = "Z"}  
	local trigger_prefix = "" 
 
 
 
	if(character_is_player(name)) then 
 
		local player_voice_prefix = voice_prefixes[player_get_custom_voice(name)] 
 
		if(player_voice_prefix ~= nil) then 
 
			trigger_prefix = player_voice_prefix 
 
		end 
 
	end 
 
 
 
	return trigger_prefix 
 
 
 
end 
 
 
 
-- Make a player take another character as a human shield. 
 
-- 
 
-- player:		(string) name of the player that will take a human shield 
 
-- victim:		(string) name of target 
 
-- tele_victim: (bool, optional) Teleport the victim in range if necessary? (default true) 
 
-- 
 
-- returns:		(bool) true if the grab worked, false if it failed. 
 
function player_take_human_shield( player, victim, tele_victim ) 
 
	 
 
	if (player == nil or player == "" or victim == nil or victim == "") then 
 
		return false 
 
	end 
 
	 
 
	if not player_take_human_shield_do( player, victim, tele_victim ) then 
 
		return false 
 
	end 
 
 
 
   -- loop as long as grabber and grabee are alive until we've succeeded 
 
   while ( character_is_dead( player ) == false and 
 
           character_is_dead( victim ) == false and 
 
	   character_take_human_shield_check_done( player, victim) == false) do 
 
      thread_yield(); 
 
   end 
 
    
 
   return character_has_specific_human_shield( player, victim ) 
 
end 
 
 
 
-- Take a screenshot and stores the output file on the kit. 
 
-- 
 
-- filename:	(string, optional) filename to use for screenshot, excluding the file extension (default filename is MMDD_screenNNN, where MM is the month, DD is the date, and NNN is an incrementing count) 
 
-- 
 
function screenshot(...) 
 
	if (arg.n > 0) then 
 
		screenshot_do(arg[1]) 
 
	else 
 
		screenshot_do("")  
	end 
 
 
 
	while (not(screenshot_check_done())) do 
 
		thread_yield() 
 
	end 
 
end 
 
 
 
-- Determine the synchronization type from a player name. 
 
-- 
 
-- player_name:	(string) name of the player 
 
-- 
 
-- returns:			(enumeration) SYNC_REMOTE, if the player is REMOTE_PLAYER; SYNC_LOCAL, if the player is LOCAL_PLAYER; else SYNC_ALL 
 
-- 
 
function sync_from_player(player_name) 
 
	if player_name == REMOTE_PLAYER then 
 
		return SYNC_REMOTE 
 
	end 
 
	if player_name == LOCAL_PLAYER then 
 
		return SYNC_LOCAL 
 
	end 
 
	 
 
	return SYNC_ALL 
 
end 
 
 
 
-- Teleport both the local and remote players. 
 
-- 
 
-- local_player_nav:		(string) name of the navpoint to teleport the local player to 
 
-- remote_player_nav:	(string) name of the navpoint to teleport the remote player to 
 
-- exit_vehicles:			(boolean, optional) set to true to force the players to exit from whatever vehicle they are in (defaults to false) 
 
-- check_for_obstacles:	(boolean, optional) set to true to check for obstacles in the way at the spawn location, false otherwise. Only valid for NPCs. (IF SET TO FALSE, THE HUMAN MAY BE TELEPORTED INTERSECTING WITH ANOTHER OBJECT) (defaults to true)  
 
-- stick_to_ground:		(boolean, optional) Set to true if the character (NPC only) should be stuck to the ground when teleporting. Only set to false in special case situations, if you're unsure you probably want to use the default. (defaults to true) 
 
-- 
 
function teleport_coop( local_player_nav, remote_player_nav, exit_vehicles, check_for_obstacles, stick_to_ground ) 
 
 
 
	--assert_screen_is_faded_out() -- Assert that the screen is completely faded out at this point - mission responsibility. 
 
	 
 
	local reset_camera_orient = true 
 
	local offset_x = 0 
 
	local offset_y = 0 
 
	local offset_z = 0 
 
 
 
	if (coop_is_active()) then 
 
		teleport( REMOTE_PLAYER, remote_player_nav, exit_vehicles, reset_camera_orient, offset_x, 
 
			offset_y, offset_z, check_for_obstacles, stick_to_ground ) 
 
	end 
 
 
 
	teleport( LOCAL_PLAYER, local_player_nav, exit_vehicles, reset_camera_orient, offset_x, 
 
			offset_y, offset_z, check_for_obstacles, stick_to_ground ) 
 
 
 
	if (coop_is_active()) then 
 
		waiting_for_player_dialog( true ) 
 
		repeat thread_yield() until (teleport_check_done(REMOTE_PLAYER) or coop_is_active() == false) 
 
		waiting_for_player_dialog( false ) 
 
	end		 
 
	 
 
	repeat thread_yield() until teleport_check_done(LOCAL_PLAYER) 
 
end 
 
 
 
-- Block until a single thread or table of threads has completed processing 
 
-- 
 
-- thread_handle:	(number or table) handle to a thread, table of handles to a thread, table of tables, etc... 
 
-- 
 
function threads_wait_for_completion(thread_handle) 
 
	local ret_val = thread_handle 
 
 
 
	if (type(thread_handle) == "number") then 
 
		while (not thread_check_done(thread_handle)) do 
 
			thread_yield() 
 
		end 
 
		ret_val = INVALID_THREAD_HANDLE 
 
	elseif (type(thread_handle) == "table") then 
 
		for i,handle in pairs(thread_handle) do 
 
			ret_val[i] = threads_wait_for_completion(handle) 
 
		end 
 
	end 
 
 
 
	-- Returns a copy with the completed threads set to INVALID_THREAD_HANDLE 
 
	return ret_val 
 
end 
 
 
 
-- Make a character turn to face an object (or turn to face the same direction as the object). 
 
-- 
 
-- name:		(string) name of the character 
 
-- target:	(string) name of the object (can be a navpoint, or character, or other scripted object) 
 
-- orient:	(boolean, optional) set to true to make the character face the same direction that the object is facing, or false to make the character face the object (defaults to false) 
 
-- 
 
-- This function blocks until the character has finished turning. 
 
-- 
 
function turn_to(name, target, orient) 
 
	while (not(character_is_ready(name))) do 
 
		thread_yield() 
 
	end 
 
	 
 
	turn_to_do(name, target, orient) 
 
 
 
	while (not(turn_to_check_done(name))) do 
 
		thread_yield() 
 
	end 
 
end 
 
 
 
-- Make a character enter vehicle. 
 
-- 
 
-- name:							(string) name of the character 
 
-- vehicle_name:				(string) name of the vehicle 
 
-- seat:							(integer, optional) index of the seat to enter (defaults to 0, the driver's seat) 
 
-- block:						(boolean, optional) set to true to block until vehicle entry is finished (defaults to true) 
 
-- force_hijack_success:	(boolean, optional) set to true to force any hijacks to automatically succeed (defaults to false) 
 
-- block_until_visual:		(boolean, optional) if blocking, only block until the human is visualy in vehicle instead of waiting until the enter is completely finished (defaults to false) 
 
-- override_action:			(string, optional) action to play instead of standard vehicle entry actions 
 
-- special_entry:				(boolean, optional) if this is set, the special_entry flag is required for the seat used.  This can be used to differentiate seats that should only be used in scripted situations. 
 
-- skip_ai_entry_mode:		(boolean, optional) defaults to false 
 
-- skip_pathfind:			(boolean, optional) defaults to false 
 
-- hop_over:				(boolean, optional) defaults to false 
 
function vehicle_enter(name, vehicle_name, seat, block, force_hijack_success, block_until_visual, override_action, special_entry, skip_ai_entry_mode, skip_pathfind, hop_over) 
 
   if ( force_hijack_success == nil ) then 
 
      force_hijack_success = false 
 
   end 
 
	local s = vehicle_enter_do(name, vehicle_name, false, seat, force_hijack_success, override_action, special_entry, skip_ai_entry_mode, skip_pathfind, hop_over) 
 
	local r 
 
	 
 
	-- don't bother checking if it was successful unless the request was successful  
 
	if (s and (block or (block == nil))) then 
 
		repeat 
 
			thread_yield() 
 
			r = vehicle_enter_check_done(name, block_until_visual, vehicle_name) 
 
		until r ~= 0 
 
	else 
 
		return s 
 
	end 
 
	 
 
	if r == 2 then 
 
		return false 
 
	else 
 
		return true 
 
	end 
 
end 
 
 
 
-- Make a group of NPCs enter a vehicle. 
 
-- 
 
-- names:			(string, or table of strings) names of characters entering the vehicle 
 
-- vehicle_name:	(string) name of vehicle to enter 
 
--  
 
function vehicle_enter_group(...) 
 
	local npcs = {}  
	local vehicle_name 
 
 
 
	if type(arg[1]) == "table" then 
 
		npcs = { unpack(arg[1]) }  
		npcs[ "n" ] = sizeof_table( npcs ) 
 
		 
 
		vehicle_name = arg[2] 
 
	else		 
 
		vehicle_name = arg[ arg.n ] 
 
		 
 
		npcs = arg; 
 
		npcs.n = npcs.n - 1 
 
	end 
 
 
 
	if (vehicle_enter_group_do(vehicle_name, false, npcs)) then 
 
		while (not(vehicle_enter_group_check_done(vehicle_name, false, npcs))) do 
 
			thread_yield() 
 
		end 
 
	end 
 
end 
 
 
 
-- Teleport a group of NPCs directly into a vehicle 
 
-- 
 
-- names:			(string, or table of strings) names of characters to teleport 
 
-- vehicle_name:	(string) name of vehicle 
 
-- 
 
-- This function blocks until all the NPCs have finished teleporting. 
 
--  
 
function vehicle_enter_group_teleport(...) 
 
	local npcs = {}  
	local vehicle_name 
 
 
 
	if type(arg[1]) == "table" then 
 
		npcs = { unpack(arg[1]) }  
		npcs[ "n" ] = sizeof_table( npcs ) 
 
		 
 
		vehicle_name = arg[2] 
 
	else		 
 
		vehicle_name = arg[ arg.n ] 
 
		 
 
		npcs = arg; 
 
		npcs.n = npcs.n - 1 
 
	end 
 
	 
 
	if (vehicle_enter_group_do(vehicle_name, true, npcs)) then 
 
		while (not(vehicle_enter_group_check_done(vehicle_name, true, npcs))) do 
 
			thread_yield() 
 
		end 
 
	end 
 
end 
 
 
 
-- Teleport a character directly into a vehicle. 
 
-- 
 
-- name:				(string) name of the character 
 
-- vehicle_name:	(string) name of the vehicle 
 
-- seat:				(integer, optional) index of the seat to enter (defaults to 0, the driver's seat) 
 
-- block:			(boolean, optional) set to true to block until vehicle entry is finished (defaults to true) 
 
-- exit_current:	(boolean, optional) set to true if the human should vacate their current vehicle (requires blocking) (defaults to true) 
 
-- special_entry:	(boolean, optional) if this is set, the special_entry flag is required for the seat used.  This can be used to differentiate seats that should only be used in scripted situations. 
 
-- 
 
function vehicle_enter_teleport(name, vehicle_name, seat, block, exit_current, special_entry) 
 
	if (block == nil) then 
 
		block = true 
 
	end 
 
 
 
	if (exit_current == nil) then 
 
		exit_current = true 
 
	end 
 
 
 
	if (exit_current and block) then 
 
		if character_is_entering_vehicle(name) then 
 
			vehicle_enter_cancel(name) 
 
			while character_is_entering_vehicle(name) do 
 
				thread_yield() 
 
			end 
 
		elseif character_is_exiting_vehicle(name) then 
 
			while (not(vehicle_exit_check_done(name))) do 
 
				thread_yield() 
 
			end 
 
		end 
 
	 
 
		local current_name = get_char_vehicle_name(name) 
 
		if (current_name ~= vehicle_name) then 
 
			-- this blocks 
 
			vehicle_exit_teleport(name, false) 
 
		end 
 
	end 
 
 
 
	local s = vehicle_enter_do(name, vehicle_name, true, seat, false, nil, special_entry) 
 
	local r 
 
	 
 
	-- don't bother checking if it was successful unless the request was successful 
 
	if (s and block) then 
 
		repeat 
 
			thread_yield() 
 
			r = vehicle_enter_check_done(name, false, vehicle_name) 
 
		until r ~= 0 
 
	else 
 
		return s 
 
	end 
 
	 
 
	if r == 2 then 
 
		return false 
 
	else 
 
		return true 
 
	end 
 
end 
 
 
 
-- Make a character exit a vehicle. 
 
-- 
 
-- name:				(string) name of the character 
 
-- not_enterable:		(boolean, optional) set to true to make the vehicle unenterable (defaults to false) 
 
-- override_action:		(string, optional) action to play instead of standard vehicle entry actions 
 
-- 
 
-- This function blocks until the character has exited the vehicle. 
 
-- 
 
function vehicle_exit(name, not_enterable, override_action) 
 
	if (vehicle_exit_do(name, false, not_enterable, false, override_action)) then 
 
		while (not(vehicle_exit_check_done(name))) do 
 
			thread_yield() 
 
		end 
 
	end 
 
end 
 
 
 
-- Make a character exit a vehicle by diving. 
 
-- 
 
-- name:				(string) name of the character 
 
-- not_enterable:	(boolean, optional) set to true to make the vehicle unenterable (defaults to false) 
 
-- 
 
-- This function blocks until the character has exited the vehicle. 
 
-- 
 
function vehicle_exit_dive(name, not_enterable) 
 
	if (vehicle_exit_do(name, false, not_enterable, true)) then 
 
		while (not(vehicle_exit_check_done(name))) do 
 
			thread_yield() 
 
		end 
 
	end 
 
end 
 
 
 
-- Make a character exit a vehicle by teleporting. 
 
-- 
 
-- name:				(string) name of the character 
 
-- not_enterable:	(boolean, optional) set to true to make the vehicle unenterable (defaults to false) 
 
-- 
 
-- This function blocks until the character has exited the vehicle. 
 
-- 
 
function vehicle_exit_teleport(name, not_enterable) 
 
	if (vehicle_exit_do(name, true, not_enterable, false)) then 
 
		while (not(vehicle_exit_check_done(name))) do 
 
			thread_yield() 
 
		end 
 
	end 
 
end 
 
 
 
-- Make a group of NPCs exit a vehicle. 
 
-- 
 
-- npc_group:	(table of strings) names of characters exiting the vehicle 
 
--  
 
function vehicle_exit_group(npc_group, not_enterable) 
 
	if (vehicle_exit_group_do(false, false, not_enterable, npc_group)) then 
 
		while (not(vehicle_exit_group_check_done(npc_group))) do 
 
			thread_yield() 
 
		end 
 
	end 
 
end 
 
 
 
-- Make a group of NPCs dive out of a vehicle. 
 
-- 
 
-- npc_group:	(table of strings) names of characters exiting the vehicle 
 
--  
 
function vehicle_exit_group_dive(npc_group, not_enterable) 
 
	if (vehicle_exit_group_do(false, true, not_enterable, npc_group)) then 
 
		while (not(vehicle_exit_group_check_done(npc_group))) do 
 
			thread_yield() 
 
		end 
 
	end 
 
end 
 
 
 
-- Teleport a group of NPCs directly out of a vehicle 
 
-- 
 
-- npc_group:	(table of strings) names of characters to teleport 
 
--  
 
function vehicle_exit_group_teleport(npc_group, not_enterable) 
 
	if (vehicle_exit_group_do(true, false, not_enterable, npc_group)) then 
 
		while (not(vehicle_exit_group_check_done(npc_group))) do 
 
			thread_yield() 
 
		end 
 
	end 
 
end 
 
 
 
-- Make a vehicle pathfind through a series of navpoints. 
 
-- 
 
-- name:					(string) name of the vehicle 
 
-- path:					(string) name of scripted path or navpoint to pathfind through 
 
-- use_navmesh:		(boolean, optional) set to true if the car should pathfind using the navmesh, or false if it should pathfind using traffic splines (defaults to false) 
 
-- stop_at_goal:		(boolean, optional) set to true if the vehicle should stop upon reaching the final destination (defaults to true) 
 
-- force_path:			(boolean, optional) set to true to ignore the current position to support looping (defaults to false) 
 
-- suppress_errors:	(boolean, optional) set to true to suppress errors (defaults to false) 
 
-- 
 
function vehicle_pathfind_to(name, path, use_navmesh, stop_at_goal, force_path, suppress_errors) 
 
	if (use_navmesh == nil) then 
 
		use_navmesh = false; 
 
	end 
 
 
 
	if (stop_at_goal == nil) then 
 
		stop_at_goal = true 
 
	end 
 
	 
 
	if (force_path == nil) then 
 
		force_path = false 
 
	end 
 
	 
 
	if (suppress_errors == nil) then 
 
		suppress_errors = false; 
 
	end 
 
 
 
	if (use_navmesh) then 
 
		if (not vehicle_pathfind_navmesh_do(name, path, force_path, stop_at_goal, suppress_errors, 0)) then 
 
			return false 
 
		end 
 
	else 
 
		if (not vehicle_pathfind_to_do(name, path, stop_at_goal, suppress_errors)) then 
 
			return false 
 
		end 
 
	end 
 
 
 
	local check_done = vehicle_pathfind_check_done(name) 
 
 
 
	while ( check_done == 0) do 
 
		thread_yield() 
 
		check_done = vehicle_pathfind_check_done(name) 
 
	end 
 
 
 
	return check_done == 1 
 
end 
 
 
 
-- Make a vehicle pathfind through a series of navpoints (using the navmesh), starting from a specific point along the path. 
 
-- 
 
-- name:					(string) name of the vehicle 
 
-- start_index:		(integer) index into the path to start from 
 
-- path:					(string) name of scripted path or navpoint to pathfind through 
 
-- stop_at_goal:		(boolean, optional) set to true if the vehicle should stop upon reaching the final destination (defaults to true) 
 
-- force_path:			(boolean, optional) set to true to ignore the current position to support looping (defaults to false) 
 
-- suppress_errors:	(boolean, optional) set to true to suppress errors (defaults to false) 
 
-- 
 
function vehicle_navmesh_pathfind_to_starting_from(name, start_index, path, stop_at_goal, force_path, suppress_errors) 
 
 
 
	if (stop_at_goal == nil) then 
 
		stop_at_goal = true 
 
	end 
 
 
 
	if (force_path == nil) then 
 
		force_path = false 
 
	end 
 
 
 
	if (suppress_errors == nil) then 
 
		suppress_errors = false 
 
	end 
 
 
 
	if (not vehicle_pathfind_navmesh_do(name, path, force_path, stop_at_goal, suppress_errors, start_index)) then 
 
		return false 
 
	end 
 
 
 
	local check_done = vehicle_pathfind_check_done(name) 
 
 
 
	while ( check_done == 0) do 
 
		thread_yield() 
 
		check_done = vehicle_pathfind_check_done(name) 
 
	end 
 
 
 
	return check_done == 1 
 
end 
 
 
 
-- Make the vehicle follow the given path 
 
-- 
 
-- vehicle:				(string) name of vehicle 
 
-- destination:			(string) navpoint name 
 
-- use_navmesh:			(boolean, optional) if true navmesh will be used for pathfinding, if false will only use traffic splines (defaults to false)  
 
-- mission_critical: 	(boolean, optional) if true vehicle will be teleported to destination if pathfinding fails (defaults to true) 
 
-- 
 
function vehicle_drive_path(vehicle, destination, use_navmesh, mission_critical) 
 
	local success = true 
 
	local stop_at_goal = false 
 
	local pathfind_success = false 
 
	 
 
	if use_navmesh then 
 
		pathfind_success = vehicle_pathfind_to(vehicle, destination, use_navmesh, stop_at_goal) 
 
	else 
 
		pathfind_success = vehicle_turret_base_to(vehicle, destination, stop_at_goal) 
 
	end 
 
	 
 
	if pathfind_success == false then 
 
		local number_of_failures = 1 
 
		local max_number_of_failures = 3 
 
		-- if the first pathfind fails lets try a few more times 
 
		while number_of_failures < max_number_of_failures do 
 
			if use_navmesh then 
 
				pathfind_success = vehicle_pathfind_to(vehicle, destination, use_navmesh, stop_at_goal) 
 
			else 
 
				pathfind_success = vehicle_turret_base_to(vehicle, destination, stop_at_goal) 
 
			end 
 
			 
 
			if pathfind_success == true then 
 
				break 
 
			else 
 
				number_of_failures = number_of_failures + 1 
 
				delay(1.0) 
 
			end 
 
		end 
 
		if number_of_failures >= max_number_of_failures then 
 
			if mission_critical == nil or mission_critical then 
 
				 -- pathfind failed so just teleport to the next stop 
 
				teleport_vehicle(vehicle, destination) 
 
			else 
 
				-- we couldn't pathfind to the destination and we didn't teleport, this isn't good 
 
				success = false  
 
			end 
 
		end 
 
	end 
 
	return success 
 
end 
 
 
 
-- Make a vehicle come to a stop. 
 
-- 
 
-- name:			(string) name of the vehicle 
 
-- dont_block:	(boolean, optional) set to true if the function should not block while the vehicle is coming to a stop (defaults to false) 
 
-- 
 
function vehicle_stop( name, dont_block ) 
 
 
 
	if( (name == nil) or (not vehicle_exists(name)) or  (vehicle_is_destroyed(name)) ) then 
 
		return 
 
	end 
 
 
 
	vehicle_stop_do( name ) 
 
	 
 
	while( (not dont_block) and get_vehicle_speed(name) > 0 ) do 
 
		thread_yield() 
 
	end 
 
end 
 
 
 
-- Make a vehicle use turret mode to move along a path or to a navpoint. 
 
--  
 
-- name:			(string) name of the vehicle 
 
-- path:			(string) name of scripted_path to follow or navpoint to go to 
 
-- stop_at_goal:	(boolean, optional) set to true if the vehicle should stop upon reaching the final destination (defaults to true) 
 
-- 
 
-- A driver must be added to the car before calling vehicle_turret_base_to(). If the vehicle does not start on a rail, 
 
-- it will transition to rail and continue pathing from that position. 
 
-- 
 
-- Example: 
 
-- 
 
--		vehicle_turret_base_to("car", "heli_path 001", true)  
-- 
 
-- Sets "car" driving to navp1, and then on to navp2, stopping at goal. 
 
-- 
 
function vehicle_turret_base_to(name, path, stop_at_goal) 
 
	 
 
	-- Wait until the resource is loaded. 
 
	-- character_wait_for_loaded_resource(name) 
 
				 
 
	if (vehicle_turret_base_to_do(name, path, stop_at_goal)) then 
 
		local check_done = vehicle_pathfind_check_done(name) 
 
		 
 
		while ( check_done == 0) do 
 
			thread_yield() 
 
			check_done = vehicle_pathfind_check_done(name) 
 
		end 
 
		 
 
		return check_done == 1 
 
	else 
 
		return false 
 
	end 
 
end 
 
 
 
-- This function blocks until all player have entered the specified vehicle 
 
-- 
 
-- vehicle_name:	(string) Name of the vehicle to wait for the players to enter 
 
-- lock_players_in:	(boolean, optional) Set to TRUE if the players should not be allowed to exit the vehicle once both players are inside. 
 
-- 
 
-- NOTE:	If lock_players_in is set to TRUE, all players will be flagged as not able to exit vehicles when  
 
--			this function returns. 
 
--			- Make sure to re-enable vehicle exiting for players before mission exit, with set_player_can_exit_vehicles() 
 
--					 
 
function vehicle_wait_for_players_to_enter(vehicle_name, lock_players_in) 
 
	while(vehicle_is_destroyed(vehicle_name) == false) do 
 
		-- Check if all players are in the vehicle 
 
		local all_players_in_vehicle = true 
 
		if (not character_is_in_vehicle(LOCAL_PLAYER, vehicle_name)) then 
 
			all_players_in_vehicle = false 
 
		else 
 
			if (coop_is_active() and not character_is_in_vehicle(REMOTE_PLAYER, vehicle_name)) then 
 
				all_players_in_vehicle = false 
 
			end 
 
		end 
 
		 
 
		-- If all players are in the vehicle, return 
 
		if (all_players_in_vehicle) then 
 
			break 
 
		end 
 
		 
 
		thread_yield() 
 
	end 
 
	 
 
	-- Maybe lock the players into the vehicle 
 
	if (lock_players_in == true) then 
 
		local seat = nil 
 
		local block = false 
 
		local exit_current = true 
 
	 
 
		-- Disable vehicle exit for the players and teleport them into the vehicle to ensure they are indeed in the vehicle 
 
		set_player_can_exit_vehicles(LOCAL_PLAYER, false) 
 
		vehicle_enter_teleport(LOCAL_PLAYER, vehicle_name, seat, block, exit_current) 
 
		if (coop_is_active()) then 
 
			set_player_can_exit_vehicles(REMOTE_PLAYER, false) 
 
			vehicle_enter_teleport(REMOTE_PLAYER, vehicle_name, seat, block, exit_current) 
 
		end 
 
	end 
 
end 
 
 
 
--[[ 
 
	process enemy set (depricated - use kill_list) 
 
	Ver. 1.1 
 
			- moved setup into its own function.  This can be called independently for greater flexability. 
 
			- added cleanup call.  This will cleanup callbacks and kill markers.  This can stop calls in progress. 
 
	Ver. 1.00 
 
	 
 
	Mark all enemies with target markers. (not yet implemented) 
 
	Keep a count of total enemies and enemies killed. 
 
	Continue when all enemies are killed. 
 
	 
 
	Setup: 
 
	a list of all of the enemies that need to be killed 
 
		_GROUP_TABLE	= {	"Script_NPC 001", "Script_NPC 002", "Script_NPC 003", 
 
													"Script_NPC 004" } 
 
	 
 
	After the enemies are spawned, call the function (runs until all are killed) 
 
		Process_enemy_set(_GROUP_TABLE) 
 
	 
 
	Cleanup enemies on your own 
 
]]-- 
 
 
 
-- *** Defines 
 
	Process_enemy_set_cleared		= false 
 
	Num_enemies_alive					= 0 
 
	Num_enemies_to_kill				= 0 
 
	Process_enemy_set_objective_helptext	= "" 
 
	 
 
-- *** Functions 
 
 
 
-- Wait until a group of enemies is killed before continuing 
 
-- 
 
-- enemy_table:			(strings, or table of strings) list of enemies to kill 
 
-- mission_helptext:		(string, optional) mission help to display 
 
-- objective_helptext:	(string, optional) X/Y helptext to display 
 
-- 
 
function process_enemy_set(enemy_table, mission_helptext, objective_helptext) 
 
	 
 
	enemy_set_setup(enemy_table, true) 
 
 
 
	-- Display the help text 
 
	if(mission_helptext) then 
 
		mission_help_table(mission_helptext) 
 
	end 
 
	 
 
	-- Display the objective text 
 
	if(objective_helptext) then 
 
		Process_enemy_set_objective_helptext = objective_helptext 
 
		objective_text(0, Process_enemy_set_objective_helptext, Num_enemies_to_kill - Num_enemies_alive, Num_enemies_to_kill) 
 
	end 
 
	 
 
	while (not Process_enemy_set_cleared) do 
 
		thread_yield() 
 
	end 
 
	 
 
	-- make sure everything is cleaned up 
 
	enemy_set_cleanup(enemy_table) 
 
 
 
end 
 
 
 
-- Setup all of the enemies to be killed 
 
-- 
 
-- enemy_table:				(strings, or table of strings) list of enemies to kill 
 
-- target_closest_player	(bool)	if true, then every enemy in the set will target the closest player 
 
-- 
 
function enemy_set_setup(enemy_table, target_closest_player) 
 
	Process_enemy_set_cleared = false 
 
 
 
	-- Assign enemy callbacks 
 
	Num_enemies_alive = 0 
 
	for i, enemy in pairs(enemy_table) do 
 
		if(not character_is_dead(enemy)) then 
 
			-- set a callback to know when the enemy is killed 
 
			on_death("process_enemy_set_killed", enemy)  
			marker_add(enemy, MINIMAP_ICON_KILL, OI_ASSET_KILL, OI_FLAGS_DEFAULT, SYNC_ALL) 
 
			if (target_closest_player) then 
 
				ai_add_enemy_target(enemy, CLOSEST_PLAYER, 2) 
 
			end 
 
			Num_enemies_alive = Num_enemies_alive + 1 
 
		end 
 
	end 
 
	 
 
	-- Check the edge condition if all the enemies are already dead 
 
	if (Num_enemies_alive == 0) then 
 
		Process_enemy_set_cleared = true 
 
	end 
 
 
 
	-- Setup kill tracking numbers 
 
	Num_enemies_to_kill = sizeof_table(enemy_table) 
 
end 
 
 
 
-- Remove callbacks and markers on the enemy set 
 
-- 
 
-- enemy_table:			(strings, or table of strings) list of enemies to kill 
 
-- 
 
function enemy_set_cleanup(enemy_table) 
 
	-- Clear enemy callbacks 
 
	for i, enemy in pairs(enemy_table) do 
 
		-- cleanup the callback 
 
		on_death("", enemy)  
		marker_remove(enemy, SYNC_ALL) 
 
	end 
 
	Process_enemy_set_cleared = false 
 
end 
 
 
 
-- *** Callbacks 
 
-- Enemy killed callback (counts dead enemies) 
 
-- 
 
-- enemy:	(string) name of killed NPC 
 
-- 
 
function process_enemy_set_killed(enemy) 
 
	marker_remove(enemy) 
 
	on_death("",enemy)  
	Num_enemies_alive = Num_enemies_alive - 1 
 
	if (Num_enemies_alive < 1) then 
 
		Process_enemy_set_cleared = true 
 
		if (Process_enemy_set_objective_helptext ~= "") then 
 
			objective_text_clear(0) 
 
		end 
 
		Process_enemy_set_objective_helptext = "" 
 
	else 
 
		if (Process_enemy_set_objective_helptext ~= "") then 
 
			objective_text(0, Process_enemy_set_objective_helptext, Num_enemies_to_kill - Num_enemies_alive, Num_enemies_to_kill) 
 
		end 
 
	end 
 
end 
 
 
 
--[[ 
 
	END process enemy set 
 
]]-- 
 
 
 
-------------------------------------------------------------------------------------------------- 
 
								--[[**********************]]-- 
 
								--[[                      ]]-- 
 
								--[[  Trigger functions   ]]-- 
 
								--[[                      ]]-- 
 
								--[[ NOTE:                ]]-- 
 
								--[[   uses convo system  ]]-- 
 
								--[[**********************]]-- 
 
				 
 
	--[[	Trigger Data template 
 
	_trigger = { 
 
		-- = { 
 
		--	name = "_trigger", 
 
		--	hit = false, 
 
		-- 	last_hit_by = nil (set to the last human to enter the trigger in the default trigger callback) 
 
		--  (optional)callback = "_function_name_cb" or "" for no callback 
 
		--	(optional)exit_callback = "_function_name_cb" (defaults to no callback) 
 
		--	(optional)marker = TRIGGER_LOCATION or TRIGGER_USE or custom or don't include for no marker 
 
		--  (optional)waypoint = true, 
 
		--  (optional)teleport_to = {  
		--						host = "host_nav", 
 
		--						client = "client_nav"  
 
		--					},		 
 
		--	(optional)conversation = _convo.convo_name  -- play a conversation 
 
		--	(optional)next_trigger = "next_trigger_name" sets up next trigger when triggered (breadcrumbs) 
 
		--}, 
 
	} 
 
	]] 
 
		 
 
	TRIGGER_LOCATION = {   
		minimap_icon_name = MINIMAP_ICON_LOCATION, 
 
		ingame_effect_name = INGAME_EFFECT_LOCATION, 
 
		object_indicator_id = OI_ASSET_LOCATION, 
 
		object_indicator_flags = OI_FLAGS_LOCATION, 
 
		sync_type = SYNC_ALL, 
 
		-- (nil for default) fade_dist 
 
	} 
 
	TRIGGER_USE = {   
		minimap_icon_name = MINIMAP_ICON_USE, 
 
		ingame_effect_name = INGAME_EFFECT_CHECKPOINT, 
 
		object_indicator_id = OI_ASSET_USE, 
 
		object_indicator_flags = OI_FLAGS_FULL, 
 
		sync_type = SYNC_ALL, 
 
		-- (nil for default) fade_dist 
 
	} 
 
Current_trigger_list = {}  
 
 
-- Setup the list of triggers 
 
function triggers_startup(trigger_list) 
 
	Current_trigger_list = trigger_list 
 
end 
 
-- Shut down all triggers and clear the list 
 
function triggers_shutdown() 
 
	trigger_clear_all() 
 
	Current_trigger_list = {}  
end 
 
 
 
-- Enable a trigger and set any markers/waypoints needed 
 
-- 
 
-- trigger_table:		(table) information about how to setup this trigger 
 
--							 
 
function trigger_setup( trigger_table ) 
 
 
 
	-- clear hit flag 
 
	trigger_table.hit = false 
 
	trigger_table.last_hit_by = nil 
 
	 
 
	-- set callback (explicitly setting "" will serve as no callback) 
 
	if trigger_table.callback ~= nil then 
 
		on_trigger(trigger_table.callback, trigger_table.name) 
 
	else 
 
		on_trigger("trigger_cb", trigger_table.name)	  
	end 
 
	 
 
	-- set exit callback (if there is one) 
 
	if trigger_table.exit_callback ~= nil and trigger_table.exit_callback ~= "" then 
 
		on_trigger_exit(trigger_table.exit_callback, trigger_table.name) 
 
	end 
 
	 
 
	-- enable the trigger 
 
	trigger_enable(trigger_table.name, true) 
 
	 
 
	-- set marker information 
 
	if trigger_table.marker ~= nil then 
 
		marker_add_trigger(trigger_table.name, trigger_table.marker.minimap_icon_name, trigger_table.marker.ingame_effect_name, trigger_table.marker.object_indicator_id, trigger_table.marker.object_indicator_flags, trigger_table.marker.sync_type, trigger_table.marker.fade_dist) 
 
	end 
 
	 
 
	-- set a waypoint 
 
	if trigger_table.waypoint == true then 
 
		mission_waypoint_add(trigger_table.name) 
 
	end 
 
end 
 
 
 
-- Disable trigger and cleanup any markers on the trigger 
 
-- 
 
-- trigger:		(string) name of trigger to cleanup 
 
-- 
 
function trigger_clear(trigger) 
 
	on_trigger("", trigger)  
	on_trigger_exit("", trigger)  
	trigger_enable(trigger, false) 
 
	marker_remove_trigger(trigger, SYNC_ALL) 
 
end 
 
 
 
-- clear all triggers 
 
function trigger_clear_all() 
 
	for i,trigger_table in pairs(Current_trigger_list) do 
 
		trigger_clear(trigger_table.name) 
 
	end 
 
	mission_waypoint_remove() 
 
end 
 
 
 
-- get the trigger table from the trigger name 
 
-- 
 
-- trigger:		(string) trigger name 
 
-- 
 
function trigger_get_table(trigger) 
 
	local trigger_table 
 
	for i,trig in pairs(Current_trigger_list) do 
 
		if trig.name == trigger then 
 
			trigger_table = trig 
 
		end 
 
	end 
 
	return trigger_table 
 
end 
 
 
 
-- basic trigger callback 
 
-- NOTE:  linked to conversation system 
 
-- 
 
-- human:		(string) name of human that tripped the trigger 
 
-- trigger:		(string) name of the trigger 
 
-- 
 
function trigger_cb(human, trigger) 
 
	-- This may be called multiple times in the same frame.  Never create groups in a trigger callback.  Use show group instead. 
 
	 
 
	-- find this trigger 
 
	local trigger_table = trigger_get_table(trigger) 
 
	 
 
	if trigger_table ~= nil then 
 
		if trigger_table.hit == true then 
 
			return 
 
		end 
 
		trigger_table.hit = true 
 
		trigger_table.last_hit_by = human 
 
		 
 
		trigger_clear(trigger_table.name) 
 
		 
 
		if trigger_table.teleport_to ~= nil then 
 
			mission_start_fade_out() 
 
			teleport_coop( trigger_table.teleport_to.host, trigger_table.teleport_to.client, true )	 
 
			mission_start_fade_in()			 
 
		end 
 
		 
 
		if trigger_table.waypoint == true then 
 
			mission_waypoint_remove() 
 
		end 
 
		 
 
		-- Conversation system link 
 
		if trigger_table.conversation ~= nil and trigger_table.conversation ~= "" then 
 
			convo_start(trigger_table.conversation) 
 
		end 
 
		 
 
		-- setup the next trigger (breadcrumbs) 
 
		if trigger_table.next_trigger ~= nil then 
 
			-- get the next trigger 
 
			local next_trigger_table = trigger_get_table(trigger_table.next_trigger) 
 
			 
 
			if next_trigger_table ~= nil then 
 
				-- setup the next trigger 
 
				trigger_setup(next_trigger_table) 
 
			end 
 
		end 
 
	end 
 
end 
 
 
 
-------------------------------------------------------------------------------------------------- 
 
								--[[**********************]]-- 
 
								--[[                      ]]-- 
 
								--[[  Persona functions   ]]-- 
 
								--[[                      ]]-- 
 
								--[[**********************]]-- 
 
	--[[	Conversation data template 
 
	_persona = { 
 
		 = { 
 
			persona_name = "", 
 
			persona_id = INVALID_PERSONA_HANDLE, 
 
		}, 
 
	} 
 
	]]-- 
 
	 
 
Persona_list = {}  
								 
 
-- load a 2d persona 
 
-- 
 
-- persona_data:	(table) table containing persona_name and persona_id to load 
 
-- 
 
function load_persona(persona_data) 
 
	persona_data.persona_id = audio_persona_load_2d(persona_data.persona_name) 
 
	Persona_list[persona_data.persona_name] = persona_data 
 
end 
 
 
 
-- clear a specific persona 
 
-- 
 
-- persona_data:	(table) table containing persona_name and persona_id to clear 
 
-- 
 
function clear_persona(persona_data) 
 
	if (persona_data.persona_id ~= INVALID_PERSONA_HANDLE) then 
 
		audio_persona_remove_2d(persona_data.persona_id) 
 
		persona_data.persona_id = INVALID_PERSONA_HANDLE 
 
	end 
 
	Persona_list[persona_data.persona_name] = nil 
 
end 
 
 
 
-- clear all loaded personas 
 
function clear_all_personas() 
 
	for i, persona_data in pairs( Persona_list ) do 
 
		clear_persona(persona_data) 
 
	end 
 
end 
 
 
 
-------------------------------------------------------------------------------------------------- 
 
								--[[**********************]]-- 
 
								--[[                      ]]-- 
 
								--[[Conversation functions]]-- 
 
								--[[                      ]]-- 
 
								--[[**********************]]-- 
 
								 
 
	--[[	Conversation data template 
 
	_convo = { 
 
		 = { 
 
			name = "file_name without voice (_bm, _wm, _bf...", 
 
			player_talks = true or false, 
 
			handle = INVALID_CONVERSATION_HANDLE, 
 
			convo_thread = INVALID_THREAD_HANDLE, 
 
			max_wait_seconds = 60,	-- if the conversation doesn't complete before this time, kill it 
 
			timer_thread = INVALID_THREAD_HANDLE, 
 
			priority = CONVO_PRIORITY_HIGH or CONVO_PRIORITY_NORMAL or CONVO_PRIORITY_OPTIONAL 
 
			(optional) persona_line = true,	-- this is a single line 
 
			(required if persona_line) speaker_name = "name of speaker",	-- single line speaker (might be set before call to play is made?) 
 
			(optional) persona_2d_line = true,	-- this is a single line played in 2d (disembodied voice)  NOTE:  Always waits for max_wait_seconds and persona must be loaded 
 
			(required if persona_2d_line) speaker_persona_table = persona,	-- persona table of speaker 
 
			(optional)phone_call = true,  -- this is a phone conversation (phone persona must be preloaded) 
 
			(required if phone_call) receiving_call = true or false  -- auto-answer is always true in missions (until proven otherwise) 
 
			 
 
		}, 
 
	} 
 
	]] 
 
	 
 
	--[[	Conversation queue data template 
 
		_convo_queue = { 
 
		start_drive_queue = {   
			{ delay = 2.0, convo = _convo.goto_ },  
			{ delay = 4.0, convo = _convo.goto_2 },  
			{ delay = 3.0, convo = _convo.goto_3 },  
			{ delay = 5.0, convo = _convo.goto_4 }  
		}, 
 
		 
 
	} 
 
	]] 
 
								 
 
Current_convo_list = {}  
convo_queue_thread = INVALID_THREAD_HANDLE 
 
convo_phone_complete = false 
 
convo_playing = ""	-- name of currently playing conversation 
 
 
 
-- DEFINED VALUES 
 
CONVO_PRIORITY_HIGH = 1 
 
CONVO_PRIORITY_NORMAL = 2 
 
CONVO_PRIORITY_OPTIONAL = 3 
 
 
 
-- setup conversation list 
 
function convo_startup(convo_list) 
 
	convo_queue_thread = INVALID_THREAD_HANDLE 
 
	convo_phone_complete = false 
 
	convo_playing = "" 
 
	Current_convo_list = convo_list 
 
end 
 
-- end all conversations and clear the list 
 
function convo_shutdown() 
 
	convo_end_all() 
 
	Current_convo_list = {}  
end 
 
 
 
-- spawn the convo play thread for this conversation 
 
-- 
 
-- convo_table:		(table) conversation information 
 
-- 
 
function convo_start(convo_table) 
 
	convo_table.convo_thread = thread_new("convo_play_thread", convo_table)  
end 
 
 
 
-- end the conversation and kill any threads playing the conversation 
 
-- 
 
-- convo_table:		(table) conversation information 
 
-- 
 
function convo_end(convo_table) 
 
	-- kill any threads that might be running 
 
	thread_kill(convo_table.convo_thread) 
 
	thread_kill(convo_table.timer_thread) 
 
	 
 
	if convo_table.handle ~= INVALID_CONVERSATION_HANDLE and (convo_table.sync == nil or convo_table.sync == SYNC_LOCAL or convo_table.sync ==SYNC_ALL)  then 
 
		audio_conversation_end(convo_table.handle,convo_table.sync) 
 
		convo_table.handle = INVALID_CONVERSATION_HANDLE 
 
		convo_playing = "" 
 
	end 
 
	if convo_table.sync ~= nil and convo_table.sync == SYNC_REMOTE and convo_table.handle_remote  ~= nil and convo_table.handle_remote ~= INVALID_CONVERSATION_HANDLE then 
 
		audio_conversation_end(convo_table.handle_remote,convo_table.sync) 
 
		convo_table.handle_remote = INVALID_CONVERSATION_HANDLE 
 
	end 
 
	 
 
	if convo_table.phone_call == true then 
 
		audio_remove_mission_cellphone(convo_table.name) 
 
	end 
 
	 
 
	if convo_table.persona_2d_line == true or convo_table.persona_line == true then 
 
		if (convo_table.play_id ~= nil) then 
 
			audio_stop(convo_table.play_id) 
 
		end 
 
	end 
 
	 
 
end 
 
 
 
-- end all of the conversations 
 
function convo_end_all() 
 
	-- kill queue first so we don't start new conversations 
 
	thread_kill(convo_queue_thread) 
 
	convo_queue_thread = INVALID_THREAD_HANDLE 
 
	 
 
	for i,convo_table in pairs(Current_convo_list) do 
 
		convo_end(convo_table) 
 
	end 
 
	convo_phone_complete = false 
 
	 
 
	convo_playing = "" 
 
end 
 
 
 
-- callback for when a phone call conversation is complete 
 
function call_ended_cb() 
 
	convo_phone_complete = true 
 
end 
 
 
 
-- Determine if a conversation is playing 
 
-- 
 
-- returns:		true if playing or false if not 
 
-- 
 
function convo_is_playing() 
 
	local playing_conversation = false 
 
	if convo_playing ~= nil and convo_playing ~= "" then 
 
		playing_conversation = true 
 
	end 
 
	return playing_conversation 
 
end 
 
 
 
-- Wait for conversation to complete 
 
-- (this may not work as desired with multiple conversations queued) 
 
-- 
 
function convo_wait_for_end() 
 
	while convo_is_playing() do 
 
		delay(1.0) 
 
	end 
 
end 
 
 
 
-- Start a list of conversations 
 
-- 
 
-- queue_table:		(table) list of times and conversations to play in order 
 
-- 
 
function convo_queue_start( queue_table ) 
 
	-- Multiple queues complicates things.  For now, this is not supported 
 
	if convo_queue_thread ~= INVALID_THREAD_HANDLE then 
 
		return 
 
	end 
 
	 
 
	convo_queue_thread = thread_new("convo_queue_play_thread", queue_table)  
end 
 
 
 
-- stall until all conversations in the convo queue have been played 
 
function convo_queue_wait_for_end() 
 
	while not thread_check_done(convo_queue_thread) do 
 
		delay(1.0) 
 
	end 
 
end 
 
 
 
-- Thread to play all of the items in a conversation queue 
 
-- 
 
-- queue_table:		(table) list of times and conversations to play in order 
 
-- 
 
function convo_queue_play_thread(queue_table) 
 
	for i, conversation in pairs(queue_table) do 
 
		delay(conversation.delay) 
 
		convo_start(conversation.convo) 
 
		convo_wait_for_end() 
 
	end 
 
	convo_queue_thread = INVALID_THREAD_HANDLE 
 
end 
 
 
 
-- Thread to play the conversation with emergency kill timer 
 
-- 
 
-- convo_table:		(table) conversation information 
 
-- 
 
function convo_play_thread(convo_table) 
 
	-- determine priority 
 
	if not(convo_table.sync == SYNC_REMOTE) then 
 
		if convo_table.priority == CONVO_PRIORITY_HIGH then 
 
			-- priority conversation 
 
			-- kill any running conversation 
 
			if convo_is_playing() then 
 
				convo_end(convo_playing) 
 
			end 
 
		elseif convo_table.priority == CONVO_PRIORITY_NORMAL then 
 
			-- normal conversation 
 
			-- wait for currently playing conversation 
 
			while convo_is_playing() do 
 
				thread_yield() 
 
			end 
 
		else -- optional conversation 
 
			-- if another conversation is playing, exit 
 
			if convo_is_playing() then 
 
				return 
 
			end 
 
		end 
 
	 
 
	 
 
	-- Set conversation as playing 
 
	convo_playing = convo_table 
 
	end 
 
	-- start the backup timer thread 
 
	convo_table.timer_thread = thread_new("delay", convo_table.max_wait_seconds)  
	 
 
	if convo_table.persona_2d_line == true then 
 
		-- play the line 
 
		convo_table.play_id = audio_play_persona_line_2d(convo_table.speaker_persona_table.persona_id, convo_table.name) 
 
		thread_yield() 
 
		-- we don't have a good check for done, so just wait out the timer 
 
		delay(convo_table.max_wait_seconds) 
 
	elseif convo_table.persona_line == true then 
 
		-- play the line 
 
		convo_table.play_id = audio_play_persona_line(convo_table.speaker_name, convo_table.name) 
 
		thread_yield() 
 
		while audio_persona_line_playing(convo_table.speaker_name) and (not thread_check_done(convo_table.timer_thread)) do 
 
			thread_yield() 
 
		end 
 
	elseif convo_table.phone_call == true then 
 
		-- do a phone call 
 
		local auto_answer = true  -- missions always auto-answer 
 
		convo_phone_complete = false 
 
		audio_play_for_mission_cellphone(convo_table.name, convo_table.receiving_call, auto_answer, "", "call_ended_cb") 
 
		 
 
		while not convo_phone_complete and (not thread_check_done(convo_table.timer_thread)) do 
 
			thread_yield() 
 
		end 
 
		 
 
		audio_remove_mission_cellphone(convo_table.name) 
 
	else -- conversation 
 
		if(convo_table.sync == SYNC_REMOTE) then 
 
			convo_table.handle_remote = audio_conversation_load_direct(convo_table.name, convo_table.player_talks, convo_table.sync) 
 
			audio_conversation_play(convo_table.handle_remote, convo_table.sync) 
 
		else 
 
			convo_table.handle = audio_conversation_load_direct(convo_table.name, convo_table.player_talks, convo_table.sync) 
 
			audio_conversation_play(convo_table.handle, convo_table.sync) 
 
		end 
 
		 
 
		 
 
		thread_yield() 
 
		 
 
		-- Wait for the conversation to end 
 
		while audio_conversation_playing(convo_table.handle) and (not thread_check_done(convo_table.timer_thread)) do 
 
			thread_yield() 
 
		end 
 
		if convo_table.sync ~= nil and (convo_table.sync == SYNC_LOCAL or convo_table.sync == SYNC_ALL) then 
 
			audio_conversation_end(convo_table.handle) 
 
		end 
 
		convo_table.handle = INVALID_CONVERSATION_HANDLE 
 
	end 
 
	 
 
	-- clear the playing conversation 
 
	convo_playing = "" 
 
end 
 
 
 
-- attempt to play all loaded conversations 
 
function convo_test_all() 
 
	for i, convo in pairs(Current_convo_list) do 
 
		message("PLAYING "..convo.name, 3.0, false)  
		convo_start(convo) 
 
		convo_wait_for_end() 
 
	end 
 
end 
 
 
 
-------------------------------------------------------------------------------------------------- 
 
								--[[**********************]]-- 
 
								--[[                      ]]-- 
 
								--[[ Kill List functions  ]]-- 
 
								--[[                      ]]-- 
 
								--[[**********************]]-- 
 
 
 
Current_kill_list = {}  
Current_vehicle_kill_list = {}  
 
 
-- setup kill list 
 
function kill_list_startup() 
 
	Current_kill_list = {}  
	Current_vehicle_kill_list = {}  
end 
 
-- shut down kill list and clear list 
 
function kill_list_shutdown() 
 
	kill_list_clear_all() 
 
	Current_kill_list = {}  
	Current_vehicle_kill_list = {}  
end 
 
 
 
 
 
function kill_list_get_members() 
 
	return Current_kill_list 
 
end 
 
 
 
-- Add a single person or a table of people to list to be killed 
 
-- 
 
-- npc_name:		(table or string) table of names or single name of character to add to the kill_list 
 
-- icon_type:		(string) OI_ASSET_KILL or OI_ASSET_KILL_FULL 
 
-- 
 
function kill_list_add( npc_name, icon_type ) 
 
	-- Recursive call to add individual members 
 
	if (type(npc_name) == "table") then 
 
		for i,member in pairs(npc_name) do 
 
			kill_list_add( member, icon_type ) 
 
		end 
 
		return 
 
	end 
 
	 
 
	-- make sure it is still alive 
 
	if character_is_dead(npc_name) then 
 
		return 
 
	end 
 
		 
 
	if icon_type == OI_ASSET_KILL then 
 
		marker_add(npc_name, MINIMAP_ICON_KILL, OI_ASSET_KILL, OI_FLAGS_DEFAULT, SYNC_ALL) 
 
	elseif icon_type == OI_ASSET_KILL_FULL then 
 
		marker_add(npc_name, MINIMAP_ICON_KILL, OI_ASSET_KILL_FULL, OI_FLAGS_FULL, SYNC_ALL) 
 
	else 
 
		-- FAIL!  We won't know which guy to kill 
 
		return 
 
	end 
 
	on_death("kill_list_member_dead_cb", npc_name)  
	 
 
	-- add the name to the kill_list 
 
	Current_kill_list[ npc_name ] = npc_name 
 
end 
 
 
 
-- Add a single vehicle to list to be killed 
 
-- NOTE: Passengers should be teleported into the vehicle before calling this. 
 
--		Passengers will be made invulnerable and not allowed to leave.  If an NPC does manage to get out, he will forever be invulnerable. 
 
--		The vehicle will be set to unjackable. 
 
--		All passengers will be killed on destruction. Don't fill the vehicle with NPCs (too many ragdolls). 
 
--		Do not add the NPCs to the kill list. 
 
-- 
 
-- vehicle_name:	(string) single name of vehicle to add to the kill_list (no recursive call) 
 
-- icon_type:		(string) OI_ASSET_KILL or OI_ASSET_KILL_FULL 
 
-- 
 
function kill_list_vehicle_add( vehicle_name, icon_type ) 
 
	 
 
	-- make sure it is still alive 
 
	if vehicle_is_destroyed(vehicle_name) then 
 
		return 
 
	end 
 
	 
 
	vehicle_suppress_npc_exit(vehicle_name) 
 
	set_unjackable_flag(vehicle_name, true) 
 
	 
 
	-- loop through each of the seats and set character as invulnerable 
 
	local num_seats = vehicle_get_num_seats(vehicle_name) 
 
	for seat = 0, num_seats-1, 1 do 
 
		local npc = get_char_in_vehicle(vehicle_name, seat) 
 
		if npc ~= nil then 
 
			turn_invulnerable(npc, true) 
 
			set_seatbelt_flag(npc, true)	-- don't go through windshield 
 
		end 
 
	end 
 
		 
 
	if icon_type == OI_ASSET_KILL then 
 
		marker_add(vehicle_name, MINIMAP_ICON_KILL, OI_ASSET_KILL, OI_FLAGS_DEFAULT, SYNC_ALL) 
 
	elseif icon_type == OI_ASSET_KILL_FULL then 
 
		marker_add(vehicle_name, MINIMAP_ICON_KILL, OI_ASSET_KILL_FULL, OI_FLAGS_FULL, SYNC_ALL) 
 
	else 
 
		-- FAIL!  We won't know which vehicle to kill 
 
		return 
 
	end 
 
	on_vehicle_destroyed("kill_list_vehicle_dead_cb", vehicle_name)  
	on_vehicle_enter_water("kill_list_vehicle_dead_cb", vehicle_name)  -- in water is same as dead  
	 
 
	-- add the name to the kill_list 
 
	Current_vehicle_kill_list[ vehicle_name ] = vehicle_name 
 
end 
 
 
 
-- remove an npc from the kill list and make sure it has markers and callbacks cleaned up 
 
-- 
 
-- npc_name:		(string) name of npc to remove from the kill_list 
 
-- 
 
function kill_list_clear(npc_name) 
 
	marker_remove(npc_name) 
 
	on_death("", npc_name)  
	Current_kill_list[ npc_name ] = nil 
 
end 
 
 
 
-- remove a vehicle from the kill list and make sure it has markers and callbacks cleaned up 
 
-- 
 
-- vehicle_name:		(string) name of vehicle to remove from the kill_list 
 
-- 
 
function kill_list_vehicle_clear(vehicle_name) 
 
	marker_remove(vehicle_name) 
 
	on_vehicle_destroyed("", vehicle_name)  
	on_vehicle_enter_water("", vehicle_name)  
	Current_vehicle_kill_list[ vehicle_name ] = nil 
 
	 
 
	-- turn passengers vulnerable again (may have been cleared without being destroyed) 
 
	local num_seats = vehicle_get_num_seats(vehicle_name) 
 
	for seat = 0, num_seats, 1 do 
 
		local npc = get_char_in_vehicle(vehicle_name, seat) 
 
		if npc ~= nil then 
 
			turn_vulnerable(npc, true) 
 
		end 
 
	end 
 
end 
 
 
 
-- remove all entries in the kill list and make sure all markers/callbacks are cleaned up 
 
function kill_list_clear_all() 
 
	for i, npc_name in pairs( Current_kill_list ) do 
 
		kill_list_clear(npc_name) 
 
	end 
 
	for i, vehicle_name in pairs( Current_vehicle_kill_list ) do 
 
		kill_list_vehicle_clear(vehicle_name) 
 
	end 
 
end 
 
 
 
-- Return number of npcs in the kill list 
 
function kill_list_get_count() 
 
	local kill_list_size = sizeof_table(Current_kill_list) 
 
	local kill_list_vehicle_size = sizeof_table(Current_vehicle_kill_list) 
 
	return kill_list_size + kill_list_vehicle_size 
 
end 
 
 
 
-- wait for everyone in the kill list to be killed (don't call this until everyone you want to include has been spawned and added) 
 
function kill_list_wait_for_complete() 
 
	local npcs_alive = true 
 
	 
 
	while npcs_alive do 
 
		npcs_alive = false 
 
		for i, npc_name in pairs( Current_kill_list ) do 
 
			if character_is_dead(npc_name) then 
 
				-- remove it 
 
				kill_list_clear(npc_name) 
 
			else 
 
				npcs_alive = true 
 
				thread_yield() 
 
			end 
 
		end 
 
		for j, vehicle_name in pairs( Current_vehicle_kill_list ) do 
 
			if vehicle_is_destroyed(vehicle_name) then 
 
				-- remove it 
 
				kill_list_vehicle_clear(vehicle_name) 
 
			else 
 
				npcs_alive = true 
 
				thread_yield() 
 
			end 
 
		end 
 
		thread_yield() 
 
	end 
 
	 
 
end 
 
 
 
-- callback for kill_list member 
 
-- 
 
-- npc_killed:	(string) name of killed NPC 
 
-- 
 
function kill_list_member_dead_cb(npc_killed) 
 
	kill_list_clear(npc_killed) 
 
end 
 
 
 
-- callback for kill_list_vehicle member 
 
-- 
 
-- vehicle_killed:	(string) name of killed vehicle 
 
-- 
 
function kill_list_vehicle_dead_cb(vehicle_killed)	 
 
	kill_list_vehicle_clear(vehicle_killed) 
 
	 
 
	-- kill our passengers 
 
	local num_seats = vehicle_get_num_seats(vehicle_killed) 
 
	for seat = 0, num_seats, 1 do 
 
		local npc = get_char_in_vehicle(vehicle_killed, seat) 
 
		if npc ~= nil then 
 
			character_kill(npc) 
 
		end 
 
	end 
 
end 
 
 
 
-------------------------------------------------------------------------------------------------- 
 
								--[[**********************]]-- 
 
								--[[                      ]]-- 
 
								--[[  Interrogate Target  ]]-- 
 
								--[[      functions       ]]-- 
 
								--[[                      ]]--								 
 
								--[[**********************]]-- 
 
								 
 
	--[[	Interrogate Target data template 
 
	_interrogate = { 
 
		leader = {  
			target = _group.friendly_fire.owner, 
 
			persona = "Interrogation", 
 
			objective = "_INTERROGATE_OWNER", 
 
		--	(optional)conversation = _convo.convo_name  -- play a conversation while interrogating 
 
		}, 
 
	} 
 
	]] 
 
							 
 
Current_interrogate_list = {}  
-- Startup interrogation system (not automatic) 
 
function interrogate_startup(interrogate_list) 
 
	Current_interrogate_list = interrogate_list 
 
end 
 
-- Shutdown interrogation 
 
function interrogate_shutdown() 
 
	interrogate_cleanup() 
 
end 
 
							 
 
-- Set all necessary flags and mark target for interrogation 
 
-- Returns when interrogation is complete 
 
-- 
 
-- interrogate_data:	(table) information about person to interrogate 
 
-- 
 
function interrogate_target(interrogate_data) 
 
	-- set persona (defaults to interrogate) 
 
	if interrogate_data.persona ~= nil then 
 
		persona_override_persona_start(interrogate_data.target, interrogate_data.persona) 
 
	end 
 
	-- set flags on the target 
 
	character_set_counter_on_grabbed_by_player(interrogate_data.target, false) 
 
	-- set icon 
 
	objective_text_clear(0) 
 
	marker_remove(interrogate_data.target, SYNC_ALL) 
 
	marker_add(interrogate_data.target, MINIMAP_ICON_USE, OI_ASSET_USE, OI_FLAG_STICKY + OI_FLAG_DISPLAY_DISTANCE, SYNC_ALL) 
 
 
 
	-- set objective 
 
	objective_text(0, interrogate_data.objective, "", "", SYNC_ALL, OI_ASSET_USE) 
 
	 
 
	-- wait for grab 
 
	local hostage_taken = false 
 
	local attacker = LOCAL_PLAYER 
 
	while not hostage_taken do 
 
		if character_has_specific_human_shield(LOCAL_PLAYER, interrogate_data.target) then 
 
			hostage_taken = true 
 
		elseif (coop_is_active() and character_has_specific_human_shield(REMOTE_PLAYER, interrogate_data.target)) then 
 
			hostage_taken = true 
 
			attacker = REMOTE_PLAYER 
 
		end 
 
		thread_yield() 
 
	end 
 
	 
 
	-- make player and target invulnerable 
 
	turn_invulnerable(interrogate_data.target) 
 
	turn_invulnerable(attacker) 
 
	 
 
	-- no ragdolling 
 
	character_allow_ragdoll(attacker, false) 
 
	character_allow_ragdoll(interrogate_data.target, false) 
 
	character_prevent_flinching(attacker, true) 
 
	character_prevent_flinching(interrogate_data.target, true) 
 
 
 
	-- Block controls until the interrogation is complete 
 
	player_human_shield_lock(attacker, true) 
 
	delay(1) 
 
	-- play a synced interrogate animation 
 
	action_play_synced_state(attacker, interrogate_data.target, "Gat Interrogation State") 
 
	action_play_synced(attacker, interrogate_data.target, "Gat Interrogation Entry") 
 
	 
 
	-- Play the interrogation conversation 
 
	if interrogate_data.conversation ~= nil then 
 
		convo_start(interrogate_data.conversation) 
 
		convo_wait_for_end() 
 
	else 
 
		-- eh...just wait a few seconds 
 
		delay(3.0) 
 
	end 
 
	 
 
	-- end interrogate animation 
 
	action_play_synced_state(attacker, interrogate_data.target, "HS_Stand") 
 
	action_play_synced(attacker, interrogate_data.target, "Gat Interrogation Return") 
 
		 
 
	-- Restore control 
 
	player_human_shield_lock(attacker, false) 
 
	action_stop_synced_state(attacker, true) 
 
	 
 
	if interrogate_data.persona ~= nil then 
 
		persona_override_persona_stop(interrogate_data.target) 
 
	end 
 
 
 
	-- Clear invulnerability 
 
	turn_vulnerable(interrogate_data.target) 
 
	turn_vulnerable(attacker) 
 
	character_prevent_flinching(attacker, false) 
 
	character_prevent_flinching(interrogate_data.target, false) 
 
	character_allow_ragdoll(attacker, true) 
 
	character_allow_ragdoll(interrogate_data.target, true) 
 
	character_prevent_explosion_fling(interrogate_data.target, false) 
 
 
 
	-- Clear objective 
 
	marker_remove(interrogate_data.target, SYNC_ALL) 
 
	objective_text_clear(0) 
 
	 
 
end 
 
 
 
-- clear any icons and make sure player states are restored 
 
function interrogate_cleanup() 
 
	for i,data in pairs(Current_interrogate_list) do 
 
		-- remove any icons 
 
		marker_remove(data.target, SYNC_ALL) 
 
	end 
 
	 
 
	-- clear possible player states 
 
	turn_vulnerable(LOCAL_PLAYER) 
 
	character_prevent_flinching(LOCAL_PLAYER, false) 
 
	character_allow_ragdoll(LOCAL_PLAYER, true) 
 
	 
 
	if coop_is_active() then 
 
		turn_vulnerable(REMOTE_PLAYER) 
 
		character_prevent_flinching(REMOTE_PLAYER, false) 
 
		character_allow_ragdoll(REMOTE_PLAYER, true) 
 
	end 
 
end 
 
 
 
-------------------------------------------------------------------------------------------------- 
 
								--[[**********************]]-- 
 
								--[[                      ]]-- 
 
								--[[    Common Mission    ]]-- 
 
								--[[      Functions       ]]-- 
 
								--[[                      ]]--								 
 
								--[[**********************]]-- 
 
 
 
-- Prep global systems for mission use 
 
-- 
 
-- trigger_list:		(table) List of table data 
 
-- convo_list:			(table) List of conversation data 
 
-- 
 
function mission_startup(trigger_list, convo_list) 
 
	triggers_startup(trigger_list) 
 
	convo_startup(convo_list) 
 
	kill_list_startup() 
 
	-- not common - requires manual setup in script interrogate_startup(interrogate_list) 
 
end 
 
 
 
-- Critical shutdown of common systems. This should always be called on cleanup. 
 
-- 
 
function mission_shutdown() 
 
	triggers_shutdown() 
 
	convo_shutdown() 
 
	kill_list_shutdown() 
 
	 
 
	-- even though we don't auto-setup, we can auto-shutdown 
 
	interrogate_shutdown() 
 
	rail_shutdown() 
 
end 
 
 
 
-- Initializes a specific checkpoint during mission start. 
 
-- 
 
-- mission_checkpoints: (table) The table containing the mission's checkpoints. 
 
-- checkpoint_name:     (string) The name of the checkpoint to initialize. 
 
-- 
 
function checkpoint_init(mission_checkpoints, checkpoint_name) 
 
	local checkpoint_table = get_table_by_name(mission_checkpoints, checkpoint_name) 
 
	if checkpoint_table ~= nil then	 
 
		-- move the player's vehicles if needed 
 
		if checkpoint_table.p1_car_nav ~= nil and checkpoint_table.p1_car_nav ~= "" then 
 
			teleport_player_vehicles(checkpoint_table.p1_car_nav, checkpoint_table.p2_car_nav) 
 
		end 
 
 
 
		-- spawn any starting groups 
 
		group_create_list(checkpoint_table.start_groups) 
 
		group_create_list(checkpoint_table.cp_only_groups) 
 
 
 
		-- teleport players if not at starting checkpoint 
 
		if checkpoint_table.name ~= MISSION_START_CHECKPOINT and checkpoint_table.no_cp_teleport ~= true then 
 
			local exit_vehicles = true 
 
			local check_for_obstacles = true 
 
			teleport_coop(checkpoint_table.host_start, checkpoint_table.client_start, exit_vehicles, check_for_obstacles, checkpoint_table.check_up) 
 
		end 
 
 
 
		-- Execute the checkpoint's init function. 
 
		local mission_start = true 
 
		if checkpoint_table.init ~= nil then 
 
			_G[checkpoint_table.init](mission_start) 
 
		end 
 
	end 
 
end 
 
 
 
-- Processes a mission's checkpoints in the order defined in the checkpoint table  
 
-- until the mission ends. 
 
-- 
 
-- mission_checkpoints:   (table) The table containing the mission's checkpoints. 
 
-- first_checkpoint_name: (string) The name of the checkpoint to start the mission at. 
 
-- 
 
function checkpoint_run_mission(mission_checkpoints, first_checkpoint_name) 
 
	local cur_checkpoint = get_table_by_name(mission_checkpoints, first_checkpoint_name) 
 
	local prev_checkpoint = nil 
 
	 
 
	while cur_checkpoint ~= nil do 
 
		-- Initialize the checkpoint, but only if it's not our first checkpoint, that's 
 
		-- initialized by the mission start. 
 
		if cur_checkpoint.name ~= first_checkpoint_name then 
 
			if cur_checkpoint.init ~= nil then 
 
				_G[cur_checkpoint.init](false) 
 
			end 
 
		end 
 
	 
 
		-- Execute the checkpoint's run function. 
 
		_G[cur_checkpoint.run]() 
 
		 
 
		-- Move to the next checkpoint or exit the mission. 
 
		prev_checkpoint = cur_checkpoint 
 
		cur_checkpoint = get_table_by_name(mission_checkpoints, cur_checkpoint.next_checkpoint) 
 
		if cur_checkpoint == nil then 
 
			break 
 
		end 
 
		 
 
		-- Set the mission checkpoint, if this isn't a script debug checkpoint 
 
		if (cur_checkpoint.debug_checkpoint ~= true) then 
 
			mission_set_checkpoint(cur_checkpoint.name, cur_checkpoint.ignore_vehicles, cur_checkpoint.ignore_notoriety) 
 
		end 
 
 
 
		-- Create any groups for the next checkpoint 
 
		group_create_list(cur_checkpoint.start_groups) 
 
		 
 
		-- Clean up the previous checkpoint. 
 
		if prev_checkpoint.cleanup ~= nil then 
 
			_G[prev_checkpoint.cleanup](false) 
 
		end 
 
	end 
 
end				 
 
								 
 
								 
 
-- Run the cleanup function for each checkpoint listed starting with the MISSION_START_CHECKPOINT 
 
-- NOTE: Out of order checkpoints/branching checkpoints need to call their own cleanups 
 
-- 
 
-- checkpoints:		(table) table of checkpoint information 
 
-- 
 
function checkpoint_cleanup_mission(checkpoints)	 
 
	local mission_exit = true 
 
	 
 
	for i, checkpoint in pairs(checkpoints) do 
 
		if checkpoint.cleanup ~= nil then 
 
			_G[checkpoint.cleanup](mission_exit) 
 
		end 
 
	end 
 
end 
 
 
 
 
 
-------------------------------------------------------------------------------------------------- 
 
 
 
--[[ 
 
	temp weapon loadout 
 
	Ver. 1.00 
 
	 
 
	Equip all players with temporary loadout 
 
	Equip the last weapon provided 
 
	 
 
	Setup: 
 
	a list of the weapons to equip the player with 
 
		_PLAYER_LOADOUT	= {	"Gal43", "m16" } 
 
	 
 
	Equipment goes to the player and coop player 
 
		inv_add_temp_loadout( _PLAYER_LOADOUT ) 
 
		 
 
	Make sure you cleanup (will only run if add was called) 
 
		inv_remove_temp_loadout( _PLAYER_LOADOUT ) 
 
]]-- 
 
 
 
-- *** Defines 
 
	Players_have_temp_loadout		= false 
 
	 
 
-- *** Functions 
 
-- Add temp weapons with unlimited ammo 
 
-- 
 
-- weapon_table:	(strings, or table of strings) list of weapons to equip 
 
-- restrict_to:	(string, optional) player to restrict loadout to (defaults to both players (if available)) 
 
-- 
 
function inv_add_temp_loadout(weapon_table, restrict_to) 
 
	local equip_local = true 
 
	local equip_remote = true 
 
	if (restrict_to == LOCAL_PLAYER) then 
 
		equip_remote = false 
 
	end 
 
	if (restrict_to == REMOTE_PLAYER) then 
 
		equip_local = false 
 
	end 
 
		 
 
	local last_weapon = "" 
 
	local in_coop = equip_remote and coop_is_active() 
 
	-- Assign weapons 
 
	for i, weapon in pairs(weapon_table) do 
 
		if (equip_local) then 
 
			inv_weapon_add_temporary(LOCAL_PLAYER, weapon, 1, true) 
 
		end 
 
		if (in_coop) then 
 
			inv_weapon_add_temporary(REMOTE_PLAYER, weapon, 1, true) 
 
		end 
 
		last_weapon = weapon 
 
	end 
 
	 
 
	if (last_weapon ~= "") then 
 
		if (equip_local) then 
 
			inv_item_equip( last_weapon, LOCAL_PLAYER ) 
 
		end 
 
		if (in_coop) then 
 
			inv_item_equip( last_weapon, REMOTE_PLAYER ) 
 
		end 
 
	end 
 
	 
 
	Players_have_temp_loadout = true 
 
 
 
end 
 
 
 
 
 
-- remove temp weapons 
 
-- 
 
-- weapon_table:		(strings, or table of strings) list of weapons to remove 
 
-- restrict_to:		(string, optional) player to restrict loadout to (defaults to both players (if available)) 
 
-- 
 
function inv_remove_temp_loadout(weapon_table, restrict_to) 
 
	local equip_local = true 
 
	local equip_remote = true 
 
	if (restrict_to == LOCAL_PLAYER) then 
 
		equip_remote = false 
 
	end 
 
	if (restrict_to == REMOTE_PLAYER) then 
 
		equip_local = false 
 
	end 
 
	 
 
	local in_coop = equip_remote and coop_is_active() 
 
	if (Players_have_temp_loadout) then 
 
		-- remove weapons 
 
		for i, weapon in pairs(weapon_table) do 
 
			if (equip_local) then 
 
				inv_weapon_remove_temporary(LOCAL_PLAYER, weapon) 
 
			end 
 
			if (in_coop) then 
 
				inv_weapon_remove_temporary(REMOTE_PLAYER, weapon) 
 
			end 
 
		end 
 
	end 
 
	 
 
	Players_have_temp_loadout = false 
 
end 
 
 
 
--[[ 
 
	END temp weapon loadout 
 
]]-- 
 
 
 
-------------------------------------------------------------------------------------------------- 
 
								--[[**********************]]-- 
 
								--[[                      ]]-- 
 
								--[[   Rail functions     ]]-- 
 
								--[[                      ]]-- 
 
								--[[**********************]]-- 
 
	--[[	Rail data template 
 
		_rail = { 
 
			_rail = { 
 
				vehicle = "", 
 
			 
 
				(optional)mission_critical = true defaults to true 
 
				 
 
				(optional)npcs_enter_vehicle_timeout = 10.0, this timeout is how long we will wait for npcs to get into the vehicle after the players before teleporting them 
 
				seats = {  
					{ character = "", seat_idx = 0 },  
				}, 
 
				coop_seats = {  
					{ character = "", seat_idx = 0 },  
				}, 
 
			}, 
 
		} 
 
	--]] 
 
	--[[	Path data template 
 
		_path = { 
 
			 = { 
 
				data = {"", ""} This can be Navpoints  
				use_navmesh = false, 
 
				callback = "", function to call when this segment is completed 
 
				}, 
 
			}, 
 
		} 
 
	--]] 
 
								 
 
-- Setup the vehicle, npcs, and players to start a rail sequence. This includes setting all of the neccassary vehicle 
 
-- flags, npcs flags, player flags, and telling the player and npcs to get in the vehicle or teleport them in if teleport is true. 
 
-- 
 
-- rail_table:		(table) rail information 
 
-- teleport:		(boolean) if true teleports the player and npcs into the vehicle  
 
-- 
 
-- NOTE:		Call rail_vehicle_setup or your own vehicle setup function before calling this function.  
 
--				-If the rail vehicle is vulnerable set the on_destroyed callback before calling this function as  
 
--				the vehicle could be destroyed during this function. 
 
--				- Any speed overrides will be cleared during this function so set those after this returns.  
 
--				this was done to prevent vehicles from driving off. 
 
-- 
 
function rail_setup(rail_table, teleport) 
 
	if not teleport then 
 
		marker_add(rail_table.vehicle, MINIMAP_ICON_LOCATION, OI_ASSET_LOCATION) 
 
	end 
 
	 
 
	vehicle_speed_cancel(rail_table.vehicle) 
 
	 
 
	local seats = nil 
 
	if coop_is_active() then 
 
		seats = rail_table.coop_seats 
 
	else 
 
		seats = rail_table.seats 
 
	end 
 
	 
 
	local npcs = { }  
	local npc_count = 0 
 
	 
 
	for index, seat in pairs(seats) do 
 
		if seat.character == LOCAL_PLAYER or seat.character == REMOTE_PLAYER then -- setup player 
 
			player_force_vehicle_seat(seat.character, seat.seat_idx, rail_table.vehicle) 
 
		else -- setup npc 
 
			local npc_is_independent = true 
 
			follower_make_independent(seat.character, npc_is_independent) 
 
			local npc_stay_in_car = true 
 
			follower_remain_in_car(seat.character, npc_stay_in_car) 
 
			set_seatbelt_flag(seat.character) 
 
			if not teleport then 
 
				local block = false 
 
				vehicle_enter(seat.character, rail_table.vehicle, seat.seat_idx, block) 
 
			end 
 
			-- set ignore ai while we wait for the player to get in, otherwise the npc could take off 
 
			set_ignore_ai_flag(seat.character, true) 
 
			npc_count = npc_count + 1 
 
			npcs[npc_count] = seat.character 
 
		end 
 
		if teleport then 
 
			local block = false 
 
			local exit_current = true 
 
			vehicle_enter_teleport(seat.character, rail_table.vehicle, seat.seat_idx, block, exit_current) 
 
		end 
 
	end 
 
	 
 
	local lock_players_in = true 
 
	vehicle_wait_for_players_to_enter(rail_table.vehicle, lock_players_in) 
 
	 
 
	if npc_count > 0 then 
 
		-- turn ai back on 
 
		for index = 1, npc_count, 1 do 
 
			set_ignore_ai_flag(npcs[index], false) 
 
		end 
 
	end 
 
	 
 
	--Make sure every npc is in the car if they are not in after max_wait_time teleport them 
 
	--Players are already in the car and locked in by this point 
 
	local ready = false 
 
	local elapsed_time = 0.0 
 
	local max_wait_time = 10.0 
 
	if rail_table.npcs_enter_vehicle_timeout ~= nil and rail_table.npcs_enter_vehicle_timeout > 0 then 
 
		max_wait_time = rail_table.npcs_enter_vehicle_timeout 
 
	end	 
 
	while(ready == false) do 
 
		ready = true 
 
		-- Check if any characters are not in the vehicle 
 
		for index, seat in pairs(seats) do 
 
			if (character_is_in_vehicle(seat.character, rail_table.vehicle) == false) then 
 
				if elapsed_time >= max_wait_time then 
 
					local block = true 
 
					local exit_current = true 
 
					vehicle_enter_teleport(seat.character, rail_table.vehicle, seat.seat_idx, block, exit_current) 
 
				else 
 
					ready = false 
 
					break 
 
				end 
 
			end 
 
		end 
 
		thread_yield() 
 
		elapsed_time = elapsed_time + get_frame_time() 
 
	end 
 
	 
 
	--Make the npcs invincible since they are in the car 
 
	for index, seat in pairs(seats) do 
 
		if seat.character ~= LOCAL_PLAYER and seat.character ~= REMOTE_PLAYER then 
 
			turn_invulnerable(seat.character) 
 
		end 
 
	end 
 
	 
 
	set_unjackable_flag(rail_table.vehicle, true) 
 
	 
 
	marker_remove(rail_table.vehicle) 
 
end 
 
 
 
-- Start the rail segment 
 
-- 
 
-- rail_table:		(table) rail information 
 
-- car_path:		(table) holds list of navs to pathfind to, if navmesh should be used, and a callback for when pathfinding is complete 
 
-- 
 
function rail_play(rail_table, car_path) 
 
	if Rail_data[rail_table.vehicle] ~= nil then 
 
		rail_segment_cleanup(rail_table) 
 
	end 
 
	 
 
	Rail_data[rail_table.vehicle] = {}  
	Rail_data[rail_table.vehicle].index = 1 
 
	Rail_data[rail_table.vehicle].path = car_path 
 
	Rail_data[rail_table.vehicle].rail = rail_table 
 
	Rail_data[rail_table.vehicle].paused = false 
 
	Rail_data[rail_table.vehicle].path_thread = thread_new("rail_drive_thread", rail_table, Rail_data[rail_table.vehicle].path)  
	Rail_data[rail_table.vehicle].callback_thread = INVALID_THREAD_HANDLE 
 
end 
 
 
 
-- Pause the rail segment 
 
-- 
 
-- rail_table:		(table) rail information 
 
-- 
 
function rail_pause(rail_table) 
 
	if Rail_data[rail_table.vehicle] ~= nil and Rail_data[rail_table.vehicle].path_thread ~= INVALID_THREAD_HANDLE then 
 
		thread_kill(Rail_data[rail_table.vehicle].path_thread) 
 
		Rail_data[rail_table.vehicle].path_thread = INVALID_THREAD_HANDLE 
 
		local dont_block = true 
 
		vehicle_stop(rail_table.vehicle, dont_block) 
 
		Rail_data[rail_table.vehicle].paused = true 
 
	end 
 
end 
 
 
 
-- Resume the rail segment 
 
-- 
 
-- rail_table:		(table) rail information 
 
-- 
 
function rail_resume(rail_table) 
 
	if Rail_data[rail_table.vehicle] ~= nil and Rail_data[rail_table.vehicle].paused == true then 
 
		Rail_data[rail_table.vehicle].paused = false 
 
		Rail_data[rail_table.vehicle].path_thread = thread_new("rail_drive_thread", rail_table, Rail_data[rail_table.vehicle].path)  
	end 
 
end 
 
 
 
-- Cleanup any global data used by a rail segment 
 
-- 
 
-- rail_table:		(table) rail information 
 
-- 
 
function rail_segment_cleanup(rail_table) 
 
	if Rail_data[rail_table.vehicle] ~= nil then 
 
		thread_kill(Rail_data[rail_table.vehicle].path_thread) 
 
		thread_kill(Rail_data[rail_table.vehicle].callback_thread) 
 
		Rail_data[rail_table.vehicle] = nil 
 
	end 
 
end 
 
 
 
-- Cleanup the entire rail including segments and undo any player changes 
 
-- 
 
-- rail_table:		(table) rail information 
 
-- 
 
function rail_cleanup(rail_table) 
 
	rail_segment_cleanup(rail_table) 
 
	marker_remove(rail_table.vehicle) 
 
	 
 
	local seats = nil 
 
	if coop_is_active() then 
 
		seats = rail_table.coop_seats 
 
	else 
 
		seats = rail_table.seats 
 
	end 
 
	 
 
	for index, seat in pairs(seats) do 
 
		if seat.character == LOCAL_PLAYER or seat.character == REMOTE_PLAYER then 
 
			player_force_vehicle_seat(seat.character, -1) 
 
			set_player_can_exit_vehicles(seat.character, true) 
 
		else 
 
			local npc_is_independent = false 
 
			follower_make_independent(seat.character, npc_is_independent) 
 
			turn_vulnerable(seat.character) 
 
			local npc_stay_in_car = false 
 
			follower_remain_in_car(seat.character, npc_stay_in_car) 
 
			local uses_seatbelt = false 
 
			set_seatbelt_flag(seat.character, uses_seatbelt) 
 
		end 
 
	end 
 
end 
 
 
 
-- Cleanup any data that is still left the global Rail_data table 
 
-- 
 
function rail_shutdown() 
 
	for index, rail_info in pairs(Rail_data) do 
 
		rail_cleanup(rail_info.rail) 
 
	end 
 
end 
 
 
 
-- Setup the rail vehicle, this is the defualt function if one is not given in rail_table 
 
-- 
 
-- vehicle:		(string) name of vehicle 
 
-- 
 
function rail_vehicle_setup(vehicle) 
 
	vehicle_disable_chase(vehicle, true) 
 
	vehicle_disable_flee(vehicle, true) 
 
	vehicle_ignore_repulsors(vehicle, true) 
 
	-- Don't make tanks have infinite mass, it upsets the Havok programmers. 
 
	if not vehicle_is_tank(vehicle) then 
 
		vehicle_infinite_mass(vehicle, true) 
 
	end 
 
	vehicle_prevent_explosion_fling(vehicle, true) 
 
	vehicle_prevent_transition_to_ambient(vehicle, true) 
 
	vehicle_set_allow_ram_ped(vehicle, true) 
 
	vehicle_set_ignore_rail_obstacles(vehicle, true) 
 
	vehicle_set_obey_traffic_lights(vehicle, false) 
 
	vehicle_set_use_short_cuts(vehicle, true) 
 
	vehicle_set_invulnerable(vehicle) 
 
	vehicle_suppress_flipping(vehicle, true) 
 
	vehicle_set_invulnerable_to_telekinesis(vehicle, true)	 
 
end 
 
 
 
-- Make the vehicle follow all of the rail's paths 
 
-- 
 
-- rail_table:		(table) rail information 
 
-- path:			(table) holds list of navs to pathfind to, if navmesh should be used, and a callback for when pathfinding is complete 
 
-- 
 
function rail_drive_thread(rail_table, path) 
 
	local success = true 
 
	while path.data[Rail_data[rail_table.vehicle].index] ~= nil do 
 
		success = vehicle_drive_path(rail_table.vehicle, path.data[Rail_data[rail_table.vehicle].index], path.use_navmesh, rail_table.mission_critical) 
 
		if not success then 
 
			break 
 
		end 
 
		Rail_data[rail_table.vehicle].index = Rail_data[rail_table.vehicle].index + 1 
 
	end 
 
	Rail_data[rail_table.vehicle].callback_thread = thread_new(path.callback, success) 
 
end 
 
 
 
-------------------------------------------End of Rail functions------------------------------------------------------- 
 
 
 
-------------------------------------------------------------------------------------------------- 
 
								--[[**************************************]]-- 
 
								--[[                     				                            ]]-- 
 
								--[[   Disable Generator functions         ]]-- 
 
								--[[                                                               ]]-- 
 
								--[[**************************************]]-- 
 
--[[ Disable Generator data template 
 
	_generators = { 
 
		{  
			trigger = trigger_table, table for the trigger that should be used to disable a generator(warning  callback set in this  table will be overwritten) 
 
			time_to_disable = 4, time in seconds it should take for generator to be disabled 
 
			objective = "", objective message that should be displayed next to progress bar 
 
			disabled = false, wether or not this generator has been disabled 
 
			(optional)anim_nav = "", navpoint that  anim should be orientied to 
 
		}, 
 
	} 
 
 
 
]]--								 
 
-- Handles setting up triggers for disabling generators 
 
-- 
 
-- generators:	(table) table of generators that should be setup 
 
-- objective:	(string, optional) objective msg that should be used when not disabling generators(defualts to "" which means no objective will show when not disabling generators) 
 
-- 
 
function disable_generators_run(generators, objective) 
 
	Generator_data = generators 
 
	Generator_objective = objective 
 
	 
 
	for i, generator in pairs(Generator_data) do 
 
		-- Add some data to generator table 
 
		generator.progress = 0 
 
		generator.player = nil 
 
		generator.monitor_thread = INVALID_THREAD_HANDLE 
 
		 
 
		-- override trigger callback  
 
		generator.trigger.callback = "disable_generators_cb" 
 
		 
 
		--setup trigger 
 
		trigger_setup(generator.trigger) 
 
		-- HVS_EC  Changed trigger rate to 100 ms to better handle low-framerate issues. 
 
		-- HVS_EC  (The ideal fix would be to make everything run off the real-time clock...) 
 
		trigger_set_delay_between_activations(generator.trigger.name, 100) 
 
		trigger_set_hold_to_repeat(generator.trigger.name) 
 
	end 
 
	 
 
	hud_gsi_supress_popout(true) 
 
	if Generator_objective ~= nil and Generator_objective ~= "" then 
 
		objective_text(0, Generator_objective, nil, nil, SYNC_ALL, OI_ASSET_USE) 
 
	end 
 
end 
 
 
 
-- Cleanup any triggers and threads being used  for diabling generators 
 
-- 
 
function disable_generators_cleanup() 
 
	 
 
	hud_gsi_supress_popout(false) 
 
	 
 
	-- no table has been set so just return 
 
	if sizeof_table(Generator_data) == 0 then 
 
		return 
 
	end 
 
	 
 
	for i, generator in pairs(Generator_data) do 
 
		trigger_clear(generator.trigger.name) 
 
		 
 
		if generator.monitor_thread ~= INVALID_THREAD_HANDLE then 
 
			thread_kill(generator.monitor_thread) 
 
		end 
 
		generator.progress = 0 
 
		generator.player = nil 
 
		generator.monitor_thread = INVALID_THREAD_HANDLE 
 
	end 
 
	 
 
	Generator_data = { }  
	Generator_objective = "" 
 
end 
 
 
 
-- Callback for entering trigger 
 
-- 
 
-- char_name:	(string) name of character that entered trigger 
 
-- trigger_name:	(string) name of trigger that was entered 
 
-- 
 
function disable_generators_cb(char_name, trigger_name) 
 
	local generator = diable_generators_get_generator_by_trigger(trigger_name) 
 
	 
 
	-- if we didn't find a generator something went wrong just return 
 
	if generator == nil then 
 
		return 
 
	end 
 
	 
 
	-- the character that acivated the trigger isn't the local or remote player... what happened? 
 
	if not (char_name == LOCAL_PLAYER or char_name == REMOTE_PLAYER) then 
 
		return 
 
	end 
 
	 
 
	if generator.player == nil then 
 
		generator.player = char_name 
 
		generator.progress = 0 
 
	end 
 
	 
 
	if generator.player ~= char_name then 
 
		return 
 
	end 
 
	 
 
	local force_play = false 
 
	local percent_done = 1.0 
 
	local stand_still = true 
 
	local zero_movement = true 
 
	 
 
	local start_anim = "disable tech start" 
 
	local state_anim = "disable tech state" 
 
	local exit_anim = "disable tech exit" 
 
	 
 
	local hud_index = 0 
 
	local sync = SYNC_LOCAL 
 
	-- HVS_EC  Variables for scaling the generator ticks to display ticks. 
 
	local last_progress_display = 0 
 
	local next_progress_display = 0 
 
	if generator.player == REMOTE_PLAYER then 
 
		hud_index = 1 
 
		sync = SYNC_REMOTE 
 
	end 
 
	 
 
	-- HVS_EC  Changed trigger cycle from 500ms to 100ms 
 
	-- we are updating every 100ms so target progress should be 10x the amount of seconds we want this to last 
 
	local target_progress =  generator.time_to_disable*10 
 
	 
 
	if generator.progress == 0 then 
 
		camera_set_hide_ui_during_animated(false) 
 
		action_play_non_blocking(char_name, start_anim, nil, force_play, stand_still, zero_movement, generator.anim_nav,  generator.anim_nav) 
 
		set_animation_state(char_name, state_anim,  generator.anim_nav) 
 
		repeat 
 
			thread_yield() 
 
		until action_play_is_finished(char_name, percent_done) 
 
		 
 
		-- only continue if the player is still in the disable tech state 
 
		if check_animation_state(generator.player, "disable tech state") then 
 
			player_movement_disable(char_name) 
 
			generator.monitor_thread = thread_new("disable_generators_monitor_thread", generator)  
			generator.progress = generator.progress + 1 
 
		 
 
		else 
 
			clear_animation_state(char_name) 
 
			generator.progress = 0 
 
			generator.player = nil 
 
			generator.monitor_thread = INVALID_THREAD_HANDLE 
 
		end 
 
		 
 
		 
 
	elseif generator.progress >= target_progress then 
 
		trigger_cb(char_name, trigger_name) 
 
		trigger_clear(trigger_name) 
 
		thread_kill(generator.monitor_thread) 
 
		generator.monitor_thread = INVALID_THREAD_HANDLE 
 
		 
 
		action_play_non_blocking(char_name, exit_anim, nil, force_play, stand_still, zero_movement, generator.anim_nav,  generator.anim_nav) 
 
		clear_animation_state(char_name) 
 
		repeat 
 
			thread_yield() 
 
		until action_play_is_finished(char_name, percent_done) 
 
		 
 
		player_movement_enable(char_name) 
 
		generator.disabled = true 
 
		camera_set_hide_ui_during_animated(true) 
 
		hud_bar_off(hud_index) 
 
		if Generator_objective ~= nil and Generator_objective ~= "" then 
 
			objective_text(0, Generator_objective, nil, nil, sync, OI_ASSET_USE) 
 
		end 
 
	 
 
	else 
 
		-- HVS_EC  The HUD updates every 500 ms.  This callback updates every 100 ms.  So we update the HUD once every 5 ticks. 
 
		last_progress_display = generator.progress / 5 
 
		next_progress_display = (generator.progress + 1) / 5 
 
		generator.progress = generator.progress + 1 
 
		if generator.progress <= target_progress then 
 
			if last_progress_display < next_progress_display then 
 
				hud_bar_set_value(hud_index,  next_progress_display, sync) 
 
			end 
 
		end 
 
	end 
 
	 
 
end	 
 
 
 
-- Monitors disable progress and updates hud bar as neccassary 
 
-- generator:	(table) generator to monitor 
 
-- 
 
function disable_generators_monitor_thread(generator)					 
 
	local prev_progress = generator.progress 
 
	 
 
	objective_text_clear(0) 
 
	local hud_index = 0 
 
	local sync = SYNC_LOCAL 
 
	if generator.player == REMOTE_PLAYER then 
 
		hud_index = 1 
 
		sync = SYNC_REMOTE 
 
		if Generator_objective ~= nil and Generator_objective ~= "" then 
 
			objective_text(0, Generator_objective, nil, nil, SYNC_LOCAL, OI_ASSET_USE) 
 
		end 
 
	elseif Generator_objective ~= nil and Generator_objective ~= "" then 
 
		objective_text(0, Generator_objective, nil, nil, SYNC_REMOTE, OI_ASSET_USE) 
 
	end 
 
	 
 
	-- we are updating every 500ms so target progress should be double the amount of seconds we want this to last 
 
	local target_progress =  generator.time_to_disable*2 
 
	 
 
	hud_bar_on(hud_index, "Default", generator.objective, target_progress, sync, OI_ASSET_USE) 
 
	 
 
	while true do 
 
		delay(0.5) 
 
		 
 
		if generator.progress <= prev_progress then 
 
			-- no progress was made so break out and kill it 
 
			break 
 
		end 
 
		 
 
		if not check_animation_state(generator.player, "disable tech state") then 
 
			-- the player got knocked out of the state fail the disable 
 
			-- also disable the trigger for 2seconds so the player doesn't instatnly re-enter animation if they are holding down y when they get hit 
 
			hud_bar_off(hud_index) 
 
			trigger_enable(generator.trigger.name, false) 
 
			delay(2.0) 
 
			trigger_enable(generator.trigger.name) 
 
			break 
 
		end 
 
		prev_progress = generator.progress 
 
	end 
 
	 
 
	hud_bar_off(hud_index) 
 
	if Generator_objective ~= nil and Generator_objective ~= "" then 
 
		objective_text(0, Generator_objective, nil, nil, sync, OI_ASSET_USE) 
 
	end 
 
	 
 
	if generator.player ~= nil then 
 
		player_movement_enable(generator.player) 
 
		clear_animation_state(generator.player) 
 
	end 
 
	 
 
	generator.progress = 0 
 
	generator.player = nil 
 
	generator.monitor_thread = INVALID_THREAD_HANDLE 
 
end 
 
 
 
-- Get the generator table from the trigger name 
 
-- 
 
-- tirgger:	(string) name of the trigger to look for 
 
-- 
 
-- returns: (table) generator table with matching trigger 
 
-- 
 
function diable_generators_get_generator_by_trigger(trigger) 
 
	for i, generator in pairs(Generator_data) do 
 
		if generator.trigger.name == trigger then 
 
			return generator 
 
		end 
 
	end 
 
	return nil 
 
end		 
 
								 
 
								 
 
-------------------------------------------End of disable generator functions-------------------------------------------------------								 
 
 
 
function crib_ship_nemo_chair_exit_setup( ) 
 
	crib_ship_nemo_chair_exit_setup_do( ) 
 
 
 
	while (crib_ship_nemo_chair_enter_check_done() == false) do 
 
		thread_yield( ) 
 
	end 
 
end 
 
 
 
function crib_ship_nemo_chair_exit( ) 
 
	crib_ship_nemo_chair_exit_do( ) 
 
 
 
	while (crib_ship_nemo_chair_exit_check_done() == false) do 
 
		thread_yield( ) 
 
	end 
 
end 
 
 
 
function crib_ship_nemo_chair_enter( ) 
 
	crib_ship_nemo_chair_enter_do( ) 
 
 
 
	while (crib_ship_nemo_chair_exit_check_done() == false) do 
 
		thread_yield( ) 
 
	end 
 
end 
 
 
 
--This function should correctly apply the ship suit for any players in the player list 
 
function mission_apply_ship_suit_to_players() 
 
	local player_list = player_names_get_all() 
 
	local add_to_wardrobe = false 
 
	 
 
	--Remove all clothing before we put on the correct clothes 
 
	players_naked(true) 
 
	 
 
	-- Wait until everyone is good and nekkid before we proceed 
 
	for i, player in pairs(player_list) do 
 
		while player_customization_is_finalized(player) == false do 
 
			thread_yield() 
 
		end 
 
	end 
 
	 
 
	-- Put on your clothes. 
 
	for i, player in pairs(player_list) do 
 
		local sync_flags = SYNC_LOCAL 
 
		if player == REMOTE_PLAYER then 
 
			sync_flags = SYNC_REMOTE 
 
		end 
 
		 
 
		if character_get_gender(player) == GENDER_TYPE_MALE then 
 
			local item_name = "cm_suit_jumpsuit01" 
 
			local wear_option = "cm_suit_jumpsuit01.cmeshx" 
 
			local variant = "defaultMat" 
 
			local clear_facewear = true 
 
			customization_item_wear(item_name, wear_option, variant, add_to_wardrobe, sync_flags, clear_facewear) 
 
		else 
 
			local item_name = "cf_suit_jumpsuit02" 
 
			local wear_option = "cm_suit_jumpsuit02.cmeshx" 
 
			local variant = "defaultMat" 
 
			local clear_facewear = true 
 
			customization_item_wear(item_name, wear_option, variant, add_to_wardrobe, sync_flags, clear_facewear) 
 
		end 
 
	end 
 
	 
 
	-- Wait until everything is ready to show. 
 
	for i, player in pairs(player_list) do 
 
		while player_customization_is_finalized(player) == false do 
 
			delay(0.25) 
 
		end 
 
	end 
 
end