/* _ _ _ | | | | | | | |__| | ___ __ _ __| | ___ _ __ | __ |/ _ \/ _` |/ _` |/ _ \ '__| | | | | __/ (_| | (_| | __/ | |_| |_|\___|\__,_|\__,_|\___|_| Fire system by Fred Heinrich (oneOCT3T) WARNING: you should not have to edit this file. Instead, you should be editing the files that use the functions defined below. This file contains the internal components of the fire system. External files should only be using the above fire type declarations, or the public functions defined below. If there's anything missing, feel free to add it to the list below. The aim of this structure is to create a system such that developers may extend without editing this file. San Andreas Role Play 2017 */ #define FIRE_RADIUS_IN_VEHICLE 15.0 #define FIRE_RADIUS_ON_FOOT 4.0 #define MAX_FIRES 200 #define INVALID_FIRE_SLOT MAX_FIRES+1 #define FIRE_SMALL_HEALTH 5 #define FIRE_MED_HEALTH 15 #define FIRE_LARGE_HEALTH 30 enum { FIRE_CAMP, FIRE_SMALL, FIRE_MED, FIRE_LARGE } /* Functions */ forward IsValidFireType(type); forward IsValidFire(fid); forward IsPlayerOnFire(playerid); forward IsPlayerPuttingOutFire(playerid); forward IsPlayerInRangeOfFire(playerid, fid, Float:range); forward IsAFiremen(playerid); //firemen skin? forward CreateFire(type, Float:x, Float:y, Float:z, Float:offset, vw, int); forward DestroyFire(fid); forward GetFireHealth(fid); forward GetFireVirtualWorld(fid); forward GetFireInterior(fid); forward GetFirePos(fid, &Float:x, &Float:y, &Float:z); forward GetFireOwner(fid); forward GetFireType(fid); forward GetFirePoolSize(); forward SetFireHealth(fid, health); forward SetFireVirtualWorld(fid, vw); forward SetFireOwner(fid, playerid); forward SetPlayerOnFire(playerid); forward RemovePlayerFire(playerid); forward GetPlayerClosestFire(playerid); /* Global variables */ enum FireInfo { fireObject, fireWoodObject, fireHealth, fireType, fireVW, fireInt, fireOwner } /* fire internal core related vars */ static Fires[MAX_FIRES][FireInfo]; /* player putting out fires vars */ //max fires that may be put out at once by a single player #define MAX_PUTOUT 5 static PlayerPuttingOutFire[MAX_PLAYERS][MAX_PUTOUT+1]; static FireTimer[MAX_PLAYERS][MAX_PUTOUT+1]; //how many fires player is putting out static puttingoutSlots[MAX_PLAYERS]; //aim assist new Text:aim_hud; new Text:aim_hud_green; /* player on fire vars */ //remaining time on fire new pFireTime[MAX_PLAYERS]; new pFireObject[MAX_PLAYERS] = {INVALID_OBJECT_ID, ...}; /* ______ _ _ | ____| | | (_) | |__ _ _ _ __ ___| |_ _ ___ _ __ ___ | __| | | | '_ \ / __| __| |/ _ \| '_ \/ __| | | | |_| | | | | (__| |_| | (_) | | | \__ \ |_| \__,_|_| |_|\___|\__|_|\___/|_| |_|___/ */ //Initalize some arrays, aim assist Hook:Fire_OnGameModeInit() { for(new i; i < MAX_PLAYERS; i++) { for(new j; j < MAX_PUTOUT; j++) { PlayerPuttingOutFire[i][j] = INVALID_FIRE_SLOT; } } //Putting out HUD aim_hud = TextDrawCreate(314.000000, 207.000000, "X"); TextDrawBackgroundColor(aim_hud, 255); TextDrawFont(aim_hud, 1); TextDrawLetterSize(aim_hud, 0.500000, 1.399999); TextDrawColor(aim_hud, 0xFF000044); TextDrawSetOutline(aim_hud, 0); TextDrawSetProportional(aim_hud, 1); TextDrawSetShadow(aim_hud, 0); TextDrawSetSelectable(aim_hud, 0); aim_hud_green = TextDrawCreate(314.000000, 207.000000, "X"); TextDrawBackgroundColor(aim_hud_green, 255); TextDrawFont(aim_hud_green, 1); TextDrawLetterSize(aim_hud_green, 0.500000, 1.399999); TextDrawColor(aim_hud_green, 0x00FF0044); TextDrawSetOutline(aim_hud_green, 0); TextDrawSetProportional(aim_hud_green, 1); TextDrawSetShadow(aim_hud_green, 0); TextDrawSetSelectable(aim_hud_green, 0); return 1; } /* GETTERS: -------- only store getters here. Getters are functions that allows a developer to access internal variables. */ public GetFireOwner(fid) { if(!IsValidFire(fid)) return INVALID_PLAYER_ID; return Fires[fid][fireOwner]; } public GetFireType(fid) { return Fires[fid][fireType]; } public GetFireHealth(fid) { return Fires[fid][fireHealth]; } public GetFireVirtualWorld(fid) { return Fires[fid][fireVW]; } public GetFireInterior(fid) { return Fires[fid][fireInt]; } public GetFirePos(fid, &Float:x, &Float:y, &Float:z) { GetDynamicObjectPos(Fires[fid][fireObject], x, y, z); return; } public GetFirePoolSize() { return MAX_FIRES; } /* SETTERS: -------- Setters allows a developer to set internal variables. */ public SetFireOwner(fid, playerid) { Fires[fid][fireOwner] = playerid; } public SetFireHealth(fid, health) { if(IsValidFire(fid)) { if( health <= 0 ) { DestroyFire(fid); } else Fires[fid][fireHealth] = health; return 1; } return 0; } /* Checking Related Functions -------------------------- Only store checking related functions here, that is to be accessiable outside of this file. */ public IsValidFireType(type) { if(type == FIRE_SMALL || type == FIRE_MED || type == FIRE_LARGE) return true; return false; } public IsValidFire(fid) { if(fid == INVALID_FIRE_SLOT) return false; if(!Fires[fid][fireObject]) return false; return true; } public IsPlayerPuttingOutFire(playerid) { return puttingoutSlots[playerid]; } public IsPlayerOnFire(playerid) { return IsValidObject(pFireObject[playerid]) && pFireTime[playerid]; } public IsPlayerInRangeOfFire(playerid, fid, Float:range) { new vw = GetPlayerVirtualWorld(playerid); new int = GetPlayerInterior(playerid); new Float: fx, Float:fy, Float:fz; GetFirePos(fid, fx, fy, fz); if( vw == GetFireVirtualWorld(fid) ) { if( int == GetFireInterior(fid) ) { if( IsPlayerInRangeOfPoint(playerid, range, fx, fy, fz ) ) { return true; } } } return false; } /* Creating/Destroying fires ------------------------- Only related functions please. */ public CreateFire(type, Float:x, Float:y, Float:z, Float:offset, vw, int) { new i = GetFreeFireSlot(); if( i != INVALID_FIRE_SLOT ) { switch(type) { case FIRE_CAMP: { Fires[i][fireObject] = CreateDynamicObject( 18688, x, y, (z + offset), 0.0, 0.0, 0.0, vw, int ); Fires[i][fireWoodObject] = CreateDynamicObject(841, x, y, (z + offset + 1.5), 0.0, 0.0, 0.0); Fires[i][fireHealth] = FIRE_SMALL_HEALTH; Fires[i][fireType] = FIRE_SMALL; } case FIRE_SMALL: { Fires[i][fireObject] = CreateDynamicObject( 18688, x, y, (z + offset), 0.0, 0.0, 0.0, vw, int ); Fires[i][fireHealth] = FIRE_SMALL_HEALTH; Fires[i][fireType] = FIRE_SMALL; } case FIRE_MED: { Fires[i][fireObject] = CreateDynamicObject( 18692, x, y, (z + offset), 0.0, 0.0, 0.0, vw, int ); Fires[i][fireHealth] = FIRE_MED_HEALTH; Fires[i][fireType] = FIRE_MED; } case FIRE_LARGE: { Fires[i][fireObject] = CreateDynamicObject( 18691, x, y, (z + offset), 0.0, 0.0, 0.0, vw, int ); Fires[i][fireHealth] = FIRE_LARGE_HEALTH; Fires[i][fireType] = FIRE_LARGE; } } //Invalid fire type provided. if(!IsValidFire(i)) return INVALID_FIRE_SLOT; Fires[i][fireVW] = vw; Fires[i][fireInt] = int; Fires[i][fireOwner] = INVALID_PLAYER_ID; return i; } printf("FIRE WARNING: max fires has been reached !"); return INVALID_FIRE_SLOT; } public DestroyFire(fid) { if( !( IsValidFire(fid) ) ) { printf("FIRE WARNING: destroying invalid fire ID."); return 0; } if( Fires[fid][fireObject] ) DestroyDynamicObjectEx( Fires[fid][fireObject] ); if( Fires[fid][fireWoodObject] ) DestroyDynamicObjectEx( Fires[fid][fireWoodObject] ); for( new i; FireInfo:i < FireInfo; i++ ) { Fires[fid][FireInfo:i] = 0; } return 1; } /* DANGER: ------- You are entering the deep internal area. Only adjust things here, if you know what you're doing! */ static Float:Get3DDistance( Float:x, Float:y, Float:z, Float:x2, Float:y2, Float:z2 ) { return floatsqroot( floatpower(x - x2, 2) + floatpower(y - y2, 2) + floatpower(z - z2, 2) ); } public GetPlayerClosestFire( playerid ) { new Float:x, Float:y, Float:z, Float:dist = 99999.0, Float:px, Float:py, Float:pz, fid; GetPlayerPos( playerid, px, py, pz ); for( new i; i < GetFirePoolSize(); i++ ) { if( i >= MAX_FIRES - 1 ) break; if( IsValidFire(i) ) { GetFirePos(i, x, y, z); new Float: cmpDist = Get3DDistance( x, y, z, px, py, pz ); if( cmpDist < dist || i == INVALID_FIRE_SLOT ) { dist = cmpDist; fid = i; } } } return fid; } GetFreeFireSlot() { for(new i; i < MAX_FIRES; i++) { if(!IsValidFire(i)) { return i; } } return INVALID_FIRE_SLOT; } /* If the player is burning (on fire)... */ //is player inside a fire or is on fire? OnFirePlayerSecondSync( playerid ) { new Float:fx, Float:fy, Float:fz, Float:radius; if(IsAFiremen(playerid)) { if(IsPlayerOnFire(playerid)) RemovePlayerFire(playerid); return 1; } //bikes or onfoot may catch fire new biking = IsABike(GetPlayerVehicleID(playerid)); if(IsPlayerInAnyVehicle(playerid) && !biking) { if(IsPlayerOnFire(playerid)) RemovePlayerFire(playerid); return 1; } if(IsPlayerOnFire(playerid)) { OnPlayerBurn( playerid ); return 1; } //set the player on fire for( new i; GetFirePoolSize() > i; i++ ) { if( IsValidFire(i) ) { //radius player must be within new type = GetFireType(i); if( type == FIRE_SMALL ) continue; //cancel camp fires from lighting players on fire if( type == FIRE_MED ) radius = 1.5; if( type == FIRE_LARGE ) radius = 2.5; GetFirePos(i, fx, fy, fz); if( GetFireVirtualWorld(i) != GetPlayerVirtualWorld( playerid ) ) continue; if( GetFireInterior(i) != GetPlayerInterior(playerid) ) continue; if( IsPlayerInRangeOfPoint( playerid, radius, fx, fy, fz + 1 ) ) { SetPlayerOnFire(playerid); //set the player on fire return 1; } } } return 1; } /* Setting players on fire... ------------------------ Again, Internal. */ public RemovePlayerFire(playerid) { DestroyObject( pFireObject[playerid] ); HideBloodForPlayer(playerid); pFireObject[playerid] = INVALID_OBJECT_ID; pFireTime[playerid] = 0; return 1; } //set the player on fire public SetPlayerOnFire(playerid) { if(PlayerInfo[playerid][pAdmin] > 1 && PlayerInfo[playerid][pStealthed] != 1) return 1; if(IsPlayerOnFire(playerid)) return 1; pFireObject[playerid] = CreateObject(18688, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0); AttachObjectToPlayer( pFireObject[playerid], playerid, 0.0, 0.0, -1.5, 0.0, 0.0, 0.0 ); pFireTime[playerid] = 10; ShowBloodForPlayer(playerid, 4, 2.0); SetPlayerArmedWeapon( playerid, 0 ); return 1; } //fire damage effect on players //Only called if the player is on fire static OnPlayerBurn( playerid ) { if( gPlayerLogged[playerid] != 1 ) return 1; //is swimming? if( GetPlayerAnimationIndex( playerid ) ) { new animlib[40], animname[40]; GetAnimationName( GetPlayerAnimationIndex(playerid), animlib, 40, animname, 40 ); if( strcmp( animlib, "SWIM", true ) == 0) { pFireTime[playerid] = 0; RemovePlayerFire(playerid); HideBloodForPlayer(playerid); } } //effects pFireTime[playerid] -= 1; //new Float:health; //GetPlayerHealth(playerid, health); //SetPlayerHealth(playerid, health - 2 ); if(pFireTime[playerid] <= 0) { RemovePlayerFire(playerid); } return 1; } //Illegal actions while burning... Hook:Burn_OnPlayerChangeWeapon( playerid, oldweapon, newweapon ) { if(IsPlayerOnFire(playerid)) SetPlayerArmedWeapon( playerid, 0 ); return 1; } Hook:Burn_OnPlayerEnterVehicle( playerid, vehicleid, ispassenger ) { if(IsPlayerOnFire(playerid)) ClearAnimations( playerid ); return 1; } //Putting the player out? Hook:Burn_OnPlayerTakeDamage(playerid, issuerid, Float:amount, weaponid, bodypart) { if( !IsPlayerOnFire(playerid) ) return 1; if( weaponid == 42 ) { //fire ext RemovePlayerFire(playerid); return 0; //return 0 to stop Fire Est issuing damage } return 1; } Hook:Burn_OnPlayerDisconnect(playerid, reason) { if(IsPlayerOnFire(playerid)) { RemovePlayerFire(playerid); } return 1; } Hook:Burn_OnPlayerDeath(playerid, killerid, reason) { if(IsPlayerOnFire(playerid)) { RemovePlayerFire(playerid); } return 1; } /* Ways of putting out fire -------------------------- Again, Internal. */ // Using fire truck/estingisher to put it out //Checks for an empty slot in the queue then sets a timer function for the helper function, OnPlayerPutOutFireEx // puttingOutSlots is already checked in its parent callee. static OnPlayerPutOutFire(playerid, fid, Float:offset, time, Float:fx, Float:fy, Float:fz) { //It's important the queue is maintained at all times. if(!IsValidFire(fid)) return 1; for(new i; i < MAX_PUTOUT; i++) { if(PlayerPuttingOutFire[playerid][i] == fid) { return 0; } } for(new i; i < MAX_PUTOUT; i++) { if(!FireTimer[playerid][i]) { FireTimer[playerid][i] = SetTimerEx("OnPlayerPutOutFireEx", time, true, "ifffii", playerid, fx, fy, fz+offset, fid, i); PlayerPuttingOutFire[playerid][i] = fid; puttingoutSlots[playerid]++; break; } } return 1; } //Helper function - INTERNAL, highly recommended not touching this unless you know mathematics. forward OnPlayerPutOutFireEx(playerid, Float:fx, Float:fy, Float:fz, fid, queueid); public OnPlayerPutOutFireEx(playerid, Float:fx, Float:fy, Float:fz, fid, queueid) { new Float:camx, Float:camy, Float:camz, Float:camvx, Float:camvy, Float:camvz, Float:fcx, Float:fcy, Float:fcz, Float:fire_distance, keys, updown, leftright; GetPlayerKeys(playerid, keys, updown, leftright); if(keys & KEY_FIRE) { GetPlayerCameraFrontVector(playerid, camvx, camvy, camvz); //third dimensional camera look at vectors GetPlayerCameraPos(playerid, camx, camy, camz); //existing camera position to use with vectors fire_distance = Get3DDistance( camx, camy, camz, fx, fy, fz ); fcx = camvx * fire_distance + camx; fcy = camvy * fire_distance + camy; fcz = camvz * fire_distance + camz; new Float: camDistToFire = Get3DDistance( fcx, fcy, fcz, fx, fy, fz ); if( camDistToFire < 1.3) { new firehealth = GetFireHealth(fid) -5; TextDrawShowForPlayer(playerid, aim_hud_green); SetFireHealth(fid, firehealth); if(firehealth <= 0) { KillTimer( FireTimer[playerid][queueid] ); FireTimer[playerid][queueid] = 0; PlayerPuttingOutFire[playerid][queueid] = INVALID_FIRE_SLOT; puttingoutSlots[playerid]--; //no longer aiming at destroyed fire if(!puttingoutSlots[playerid]) { TextDrawHideForPlayer(playerid, aim_hud_green); } } return 1; } } else { KillTimer( FireTimer[playerid][queueid] ); FireTimer[playerid][queueid] = 0; PlayerPuttingOutFire[playerid][queueid] = INVALID_FIRE_SLOT; puttingoutSlots[playerid]--; //no longer firing TextDrawHideForPlayer(playerid, aim_hud); } //player didn't manage to reduce fire health TextDrawHideForPlayer(playerid, aim_hud_green); return 1; } stock IsAFiremen(playerid) { new skin = GetPlayerSkin(playerid); if( skin == 277 || skin == 278 || skin == 279) return 1; return 0; } //The trigger for putting out fire Hook:Fire_OnPlayerKeyStateChange(playerid, newkeys, oldkeys) { if(HOLDING(KEY_FIRE)) { if(puttingoutSlots[playerid] == MAX_PUTOUT) return 1; new Float:fire_radius; new Float:offset = 2.0; new time = 2000; if(GetPlayerState(playerid) == PLAYER_STATE_ONFOOT) { new weapon = GetPlayerWeapon(playerid); if(weapon != 42) return 1; fire_radius = FIRE_RADIUS_ON_FOOT; } else if(GetPlayerState(playerid) == PLAYER_STATE_DRIVER) { if(GetVehicleModel(GetPlayerVehicleID(playerid)) != 407) return 1; fire_radius = FIRE_RADIUS_IN_VEHICLE; offset = 2.5; //vector fix time = 1000; //puts out fire quicker } else return 1; if(!puttingoutSlots[playerid]) { TextDrawShowForPlayer(playerid, aim_hud); } new Float:fx, Float:fy, Float:fz; for(new i; i < GetFirePoolSize(); i++) { //if(GetFireType(i) == FIRE_SMALL) continue; if(GetFireVirtualWorld(i) != GetPlayerVirtualWorld(playerid)) continue; if(GetFireInterior(i) != GetPlayerInterior(playerid)) continue; GetFirePos(i, fx, fy, fz); if(IsPlayerInRangeOfPoint(playerid, fire_radius, fx, fy, fz) ) { OnPlayerPutOutFire(playerid, i, offset, time, fx, fy, fz); } } } else { TextDrawHideForPlayer(playerid, aim_hud); } return 1; } #include "inc\fire_system\commands\putout.inc" #include "inc\fire_system\commands\create.inc" #include "inc\fire_system\camp.inc"