├── LICENSE ├── README.md ├── client ├── anim-post-fx │ └── cAnimPostFX.lua ├── asset-requester │ └── cAssetRequester.lua ├── blip │ └── cBlip.lua ├── camera │ ├── cCamera.lua │ └── cFilter.lua ├── entity │ └── cEntity.lua ├── enums │ └── ControlEnum.lua ├── explosion │ └── cExplosion.lua ├── hud │ └── cHud.lua ├── keypress │ ├── cKeymap.lua │ └── cKeypress.lua ├── light │ └── cLight.lua ├── localplayer │ └── cLocalPlayer.lua ├── localplayer_behaviors │ └── cBulletDetectionLocalPlayerBehavior.lua ├── map │ ├── cImap.lua │ └── cIpl.lua ├── marker │ └── cMarker.lua ├── network │ └── cNetwork.lua ├── object │ ├── cObject.lua │ └── cObjectManager.lua ├── particle-effect │ └── cParticleEffect.lua ├── pause-menu │ └── cPauseMenu.lua ├── ped │ └── cPed.lua ├── physics │ └── cPhysics.lua ├── player │ ├── cPlayer.lua │ ├── cPlayerManager.lua │ └── cPlayers.lua ├── prompt │ └── cPrompt.lua ├── render │ ├── cRender.lua │ └── cTexture.lua ├── screen-effects │ └── cScreenEffects.lua ├── sound │ └── cSound.lua ├── spectate │ └── cSpectateMode.lua ├── typecheck │ └── cTypeCheck.lua ├── ui │ ├── events.js │ ├── index.html │ ├── jquery.js │ ├── reset.css │ └── ui.lua ├── volume │ └── cVolume.lua ├── water │ └── cWater.lua ├── weapons │ └── cWeapons.lua └── world │ └── cWorld.lua ├── example_fxmanifest.lua ├── server ├── entity │ └── sEntity.lua ├── fs-additions │ └── exists.lua ├── json │ ├── sJSONUtils.lua │ └── sJsonOOF.lua ├── key-value-store │ └── sKeyValueStore.lua ├── mysql-async │ ├── LICENSE │ ├── MySQLAsync.net.dll │ ├── MySqlConnector.dll │ ├── System.Buffers.dll │ ├── System.Threading.Tasks.Extensions.dll │ ├── lib │ │ ├── MySQL.lua │ │ └── init.lua │ └── src │ │ ├── AssemblyInfo.cs │ │ ├── Execute.cs │ │ ├── FetchAll.cs │ │ ├── FetchScalar.cs │ │ ├── FiveMMySQLAsync.csproj │ │ ├── FiveMMySQLAsync.sln │ │ ├── Insert.cs │ │ ├── MySQLAsync.cs │ │ ├── MySQLThread.cs │ │ ├── Operation.cs │ │ ├── Transaction.cs │ │ └── app.config ├── mysql │ └── MySQL.lua ├── network │ └── sNetwork.lua ├── ped │ └── sPed.lua ├── player │ ├── sPlayer.lua │ ├── sPlayerManager.lua │ └── sPlayers.lua ├── sConfig.lua └── world │ └── sWorld.lua └── shared ├── color └── Color.lua ├── csv └── CSV.lua ├── enums ├── shEntityTypeEnum.lua ├── shPedBoneEnum.lua ├── shVehicleEnum.lua ├── shWeaponEnum.lua └── shWeaponTypeEnum.lua ├── events ├── shEvent.lua └── shEvents.lua ├── game ├── IsFiveM.lua └── IsRedM.lua ├── lua-additions └── lua-additions.lua ├── lua-overloads ├── assert.lua ├── error.lua ├── lua-overloads.lua ├── math-randomseed.lua ├── print.lua └── tostring.lua ├── math ├── shVector3Math.lua ├── shXorCipher.lua └── standardized-distance.lua ├── object-oriented ├── LOAD_ABSOLUTELY_LAST.lua ├── class.lua ├── shGetterSetter.lua └── shObjectOrientedUtilities.lua ├── standalone-data-structures ├── shDeque.lua ├── shEnum.lua └── shIdPool.lua ├── timer └── Timer.lua ├── value-storage └── ValueStorage.lua └── xml ├── SLAXML.lua └── XML.lua /client/anim-post-fx/cAnimPostFX.lua: -------------------------------------------------------------------------------- 1 | AnimPostFX = class() 2 | 3 | function AnimPostFX:__init() 4 | 5 | end 6 | 7 | function AnimPostFX:Play(name) 8 | AnimpostfxPlay(name) 9 | end 10 | 11 | function AnimPostFX:Stop(name) 12 | AnimpostfxStop(name) 13 | end 14 | 15 | if IsRedM then 16 | AnimPostFX = AnimPostFX() 17 | end 18 | 19 | -- List of FX names from https://github.com/femga/rdr3_discoveries/blob/master/graphics/animpostfx/animpostfx.lua 20 | --[[local animpostfx = { 21 | 22 | "CameraTakePicture", 23 | "CameraTransitionBlink", 24 | "CameraTransitionFlash", 25 | "CameraTransitionWipe_L", 26 | "CameraTransitionWipe_R", 27 | "CameraViewfinder", 28 | "CamTransition01", 29 | "CamTransitionBlink", 30 | "CamTransitionBlinkSick", 31 | "CamTransitionBlinkSlow", 32 | "ChapterTitle_IntroCh01", 33 | "ChapterTitle_IntroCh02", 34 | "ChapterTitle_IntroCh03", 35 | "ChapterTitle_IntroCh04", 36 | "ChapterTitle_IntroCh05", 37 | "ChapterTitle_IntroCh06", 38 | "ChapterTitle_IntroCh08Epi01", 39 | "ChapterTitle_IntroCh09Epi02", 40 | "cutscene_mar6_train", 41 | "cutscene_rbch2rsc11_bink1", 42 | "cutscene_rbch2rsc11_bink2", 43 | "cutscene_rbch2rsc11_bink3", 44 | "cutscene_rbch2rsc11_bink4", 45 | "cutscene_rbch2rsc11_bink5", 46 | "cutscene_rbch2rsc11_bink6", 47 | "cutscene_rbch2rsc11_bink7", 48 | "cutscene_rbch2rsc11_bink8", 49 | "deadeye", 50 | "DeadEyeEmpty", 51 | "DeathFailMP01", 52 | "Duel", 53 | "EagleEye", 54 | "GunslingerFill", 55 | "killCam", 56 | "KillCamHonorChange", 57 | "KingCastleBlue", 58 | "KingCastleRed", 59 | "MissionChoice", 60 | "MissionFail01", 61 | "Mission_EndCredits", 62 | "Mission_FIN1_RideBad", 63 | "Mission_FIN1_RideGood", 64 | "Mission_GNG0_Ride", 65 | "MP_BountyLagrasSwamp", 66 | "MP_BountySelection", 67 | "MP_CampWipeDown", 68 | "MP_CampWipeL", 69 | "MP_CampWipeR", 70 | "MP_CampWipeUp", 71 | "MP_DM_Annesburg_ReduceDustDensity", 72 | "MP_Downed", 73 | "MP_HealthDrop", 74 | "MP_InRegion_Exit", 75 | "MP_LobbyBW01_Intro", 76 | "MP_OutofAreaDirectional", 77 | "MP_PedKill", 78 | "MP_RaceBoostStart", 79 | "MP_Region", 80 | "MP_RewardsExposureLoop", 81 | "MP_Rhodes_ReduceDustDensity", 82 | "MP_RiderFormation", 83 | "MP_SuddenDeath", 84 | "ODR3_Injured01Loop", 85 | "ODR3_Injured02Loop", 86 | "ODR3_Injured03Loop", 87 | "OJDominoBlur", 88 | "OJDominoValid", 89 | "OJFiveFinger", 90 | "OJPokerPlayerTurn", 91 | "PauseMenuIn", 92 | "PedKill", 93 | "PlayerDrugsPoisonWell", 94 | "PlayerDrunk01", 95 | "PlayerDrunk01_PassOut", 96 | "PlayerDrunkAberdeen", 97 | "PlayerDrunkSaloon1", 98 | "PlayerHealthCrackpot", 99 | "PlayerHealthLow", 100 | "PlayerHealthPoorCS", 101 | "PlayerHealthPoorGuarma", 102 | "PlayerHealthPoorMOB3", 103 | "PlayerHonorChoiceBad", 104 | "PlayerHonorChoiceGood", 105 | "PlayerHonorLevelBad", 106 | "PlayerHonorLevelGood", 107 | "PlayerImpact04", 108 | "PlayerImpactFall", 109 | "PlayerKnockout_SerialKiller", 110 | "PlayerKnockout_WeirdoPat", 111 | "PlayerOverpower", 112 | "PlayerRPGCore", 113 | "PlayerRPGCoreDeadEye", 114 | "PlayerRPGEmptyCoreDeadEye", 115 | "PlayerRPGEmptyCoreHealth", 116 | "PlayerRPGEmptyCoreStamina", 117 | "PlayerRPGWarnHealth", 118 | "PlayerSickDoctorsOpinion", 119 | "PlayerSickDoctorsOpinionOutBad", 120 | "PlayerSickDoctorsOpinionOutGood", 121 | "PlayerWakeUpAberdeen", 122 | "PlayerWakeUpDrunk", 123 | "PlayerWakeUpInterrogation", 124 | "PlayerWakeUpKnockout", 125 | "PoisonDartPassOut", 126 | "POSTFX_CONSUMABLE_STAMINA", 127 | "POSTFX_CONSUMABLE_STAMINA_FORT", 128 | "RespawnEstablish01", 129 | "RespawnMissionCheckpoint", 130 | "RespawnPulse01", 131 | "RespawnSkyWithHonor", 132 | "RespawnWithHonor", 133 | "SkyTimelapse_0600_01", 134 | "skytl_0000_01clear", 135 | "skytl_0600_01clear", 136 | "skytl_0900_01clear", 137 | "skytl_0900_04storm", 138 | "skytl_1200_01clear", 139 | "skytl_1200_03clouds", 140 | "skytl_1500_03clouds", 141 | "skytl_1500_04storm", 142 | "title_ch01_colter", 143 | "Title_Ch03_ClemensPoint", 144 | "Title_GameIntro", 145 | "Title_Gen_coupledayslater", 146 | "Title_Gen_coupledayslater_onblack", 147 | "Title_Gen_couplemonthslater", 148 | "Title_Gen_couplemonthslater_onblack", 149 | "Title_Gen_coupleweekslater", 150 | "Title_Gen_coupleweekslater_onblack", 151 | "Title_Gen_daylater", 152 | "Title_Gen_daylater_onblack", 153 | "Title_Gen_FewDaysLater", 154 | "Title_Gen_FewDaysLater_onblack", 155 | "Title_Gen_FewHoursLater", 156 | "Title_Gen_FewHoursLater_onblack", 157 | "Title_Gen_FewMonthsLater", 158 | "Title_Gen_FewMonthsLater_onblack", 159 | "Title_Gen_FewWeeksLater", 160 | "Title_Gen_FewWeeksLater_onblack", 161 | "Title_Gen_somedaysLater", 162 | "Title_Gen_somedaysLater_onblack", 163 | "Title_Gen_someyearsLater", 164 | "Title_Gen_someyearsLater_onblack", 165 | "UI_PauseTransition", 166 | "UI_PauseTransitionOut", 167 | "WheelHUDIn", 168 | }]] 169 | 170 | --[[ 171 | List of FiveM FX names: https://pastebin.com/dafBAjs0 172 | 173 | LIST OF SCREEN FX 174 | 175 | INFO : Somes effect are directly loop and need to be stop 176 | 177 | START_SCREEN_EFFECT(FxName, 0, false); 178 | STOP_SCREEN_EFFECT(FxName); 179 | 180 | SwitchHUDIn 181 | SwitchHUDOut 182 | FocusIn 183 | FocusOut 184 | MinigameEndNeutral 185 | MinigameEndTrevor 186 | MinigameEndFranklin 187 | MinigameEndMichael 188 | MinigameTransitionOut 189 | MinigameTransitionIn 190 | SwitchShortNeutralIn 191 | SwitchShortFranklinIn 192 | SwitchShortTrevorIn 193 | SwitchShortMichaelIn 194 | SwitchOpenMichaelIn 195 | SwitchOpenFranklinIn 196 | SwitchOpenTrevorIn 197 | SwitchHUDMichaelOut 198 | SwitchHUDFranklinOut 199 | SwitchHUDTrevorOut 200 | SwitchShortFranklinMid 201 | SwitchShortMichaelMid 202 | SwitchShortTrevorMid 203 | DeathFailOut 204 | CamPushInNeutral 205 | CamPushInFranklin 206 | CamPushInMichael 207 | CamPushInTrevor 208 | SwitchOpenMichaelIn 209 | SwitchSceneFranklin 210 | SwitchSceneTrevor 211 | SwitchSceneMichael 212 | SwitchSceneNeutral 213 | MP_Celeb_Win 214 | MP_Celeb_Win_Out 215 | MP_Celeb_Lose 216 | MP_Celeb_Lose_Out 217 | DeathFailNeutralIn 218 | DeathFailMPDark 219 | DeathFailMPIn 220 | MP_Celeb_Preload_Fade 221 | PeyoteEndOut 222 | PeyoteEndIn 223 | PeyoteIn 224 | PeyoteOut 225 | MP_race_crash 226 | SuccessFranklin 227 | SuccessTrevor 228 | SuccessMichael 229 | DrugsMichaelAliensFightIn 230 | DrugsMichaelAliensFight 231 | DrugsMichaelAliensFightOut 232 | DrugsTrevorClownsFightIn 233 | DrugsTrevorClownsFight 234 | DrugsTrevorClownsFightOut 235 | HeistCelebPass 236 | HeistCelebPassBW 237 | HeistCelebEnd 238 | HeistCelebToast 239 | MenuMGHeistIn 240 | MenuMGTournamentIn 241 | MenuMGSelectionIn 242 | ChopVision 243 | DMT_flight_intro 244 | DMT_flight 245 | DrugsDrivingIn 246 | DrugsDrivingOut 247 | SwitchOpenNeutralFIB5 248 | HeistLocate 249 | MP_job_load 250 | RaceTurbo 251 | MP_intro_logo 252 | HeistTripSkipFade 253 | MenuMGHeistOut 254 | MP_corona_switch 255 | MenuMGSelectionTint 256 | SuccessNeutral 257 | ExplosionJosh3 258 | SniperOverlay 259 | RampageOut 260 | Rampage 261 | Dont_tazeme_bro 262 | DeathFailOut 263 | 264 | 265 | ]] -------------------------------------------------------------------------------- /client/asset-requester/cAssetRequester.lua: -------------------------------------------------------------------------------- 1 | function LoadModel(hash, callback) 2 | Citizen.CreateThread(function() 3 | local wait_time = 0 4 | RequestModel(hash) 5 | while not HasModelLoaded(hash) do 6 | Citizen.Wait(100) 7 | wait_time = wait_time + 100 8 | assert(wait_time < 5000, string.format("model with hash %s could not be loaded in time. Did you use the right hash?", hash)) 9 | end 10 | callback() 11 | -- Clear up memory 12 | Citizen.SetTimeout(1000, function() 13 | SetModelAsNoLongerNeeded(hash) 14 | end) 15 | end) 16 | end 17 | 18 | function LoadEffectBank(bank, callback) 19 | Citizen.CreateThread(function() 20 | local wait_time = 0 21 | RequestNamedPtfxAsset(bank) 22 | while not HasNamedPtfxAssetLoaded(bank) do 23 | Citizen.Wait(100) 24 | wait_time = wait_time + 100 25 | assert(wait_time < 5000, string.format("effect bank %s could not be loaded in time. Did you use the right name?", bank)) 26 | end 27 | callback() 28 | -- Clear up memory 29 | Citizen.SetTimeout(1000, function() 30 | RemoveNamedPtfxAsset(bank) 31 | end) 32 | end) 33 | end 34 | 35 | 36 | function LoadAnimSet(anim, callback) 37 | Citizen.CreateThread(function() 38 | local wait_time = 0 39 | RequestAnimSet(anim) 40 | while not HasAnimSetLoaded(anim) do 41 | Citizen.Wait(100) 42 | wait_time = wait_time + 100 43 | assert(wait_time < 5000, string.format("anim set %s could not be loaded in time. Did you use the right name?", anim)) 44 | end 45 | callback() 46 | -- Clear up memory 47 | Citizen.SetTimeout(1000, function() 48 | RemoveAnimSet(anim) 49 | end) 50 | end) 51 | end 52 | 53 | function LoadAnimDict(dict, callback) 54 | Citizen.CreateThread(function() 55 | local wait_time = 0 56 | RequestAnimDict(dict) 57 | while not HasAnimDictLoaded(dict) do 58 | Citizen.Wait(100) 59 | wait_time = wait_time + 100 60 | assert(wait_time < 5000, string.format("anim dict %s could not be loaded in time. Did you use the right name?", dict)) 61 | end 62 | callback() 63 | -- Clear up memory 64 | Citizen.SetTimeout(1000, function() 65 | RemoveAnimDict(dict) 66 | end) 67 | end) 68 | end -------------------------------------------------------------------------------- /client/camera/cCamera.lua: -------------------------------------------------------------------------------- 1 | Camera = class() 2 | 3 | function Camera:__init() 4 | self.cam = GetRenderingCam() 5 | self:FadeIn(0) 6 | end 7 | 8 | CameraViewMode = 9 | { 10 | ThirdPersonClose = 0, 11 | ThirdPersonMiddle = 1, 12 | ThirdPersonFar = 2, 13 | FirstPerson = 4 14 | } 15 | 16 | if IsFiveM then 17 | -- Locks the camera view mode to a specific CameraViewMode 18 | -- Must be called every frame to override manual controls 19 | -- unless you also disable the camera control with: 20 | -- LocalPlayer:RestrictAction(Control.NextCamera, true) 21 | function Camera:LockCameraMode(mode) 22 | if GetFollowPedCamViewMode() ~= mode or 23 | GetFollowVehicleCamViewMode() ~= mode then 24 | SetFollowPedCamViewMode(mode) 25 | SetFollowVehicleCamViewMode(mode) 26 | end 27 | end 28 | end 29 | 30 | -- Interpolates between two positions over duration ms 31 | function Camera:InterpolateBetween(pos1, pos2, rot1, rot2, duration) 32 | assert(self.freecam == nil 33 | and self.from_cam == nil 34 | and self.to_cam == nil, "cannot call Camera:InterpolateBetween multiple times without calling Camera:Reset first") 35 | 36 | ClearFocus() 37 | self.from_cam = CreateCamWithParams("DEFAULT_SCRIPTED_CAMERA", pos1.x, pos1.y, pos1.z, rot1.x, rot1.y, rot1.z, self:GetFOV()) 38 | self.to_cam = CreateCamWithParams("DEFAULT_SCRIPTED_CAMERA", pos2.x, pos2.y, pos2.z, rot2.x, rot2.y, rot2.z, self:GetFOV()) 39 | 40 | SetCamActiveWithInterp(self.to_cam, self.from_cam, duration) 41 | RenderScriptCams(true, false, 0, true, false, true) 42 | SetCamAffectsAiming(self.from_cam, false) 43 | SetCamAffectsAiming(self.to_cam, false) 44 | end 45 | 46 | function Camera:SetFOV(fov) 47 | SetCamFov(self:GetCurrentCam(), tofloat(fov)) 48 | end 49 | 50 | function Camera:DetachFromPlayer(position, rotation, ease, ease_time) 51 | assert(self.freecam == nil 52 | and self.from_cam == nil 53 | and self.to_cam == nil, "cannot call Camera:DetachFromPlayer multiple times without calling Camera:Reset first") 54 | 55 | local pos = position or Camera:GetPosition() 56 | local rot = rotation or Camera:GetRotation() 57 | 58 | ClearFocus() 59 | self.freecam = CreateCamWithParams( 60 | "DEFAULT_SCRIPTED_CAMERA", 61 | pos.x, pos.y, pos.z, 62 | rot.y, rot.y, rot.z, 63 | self:GetFOV()) 64 | SetCamActive(self:GetCurrentCam(), true) 65 | RenderScriptCams(true, ease, ease_time, true, false, true) 66 | SetCamAffectsAiming(self:GetCurrentCam(), false) 67 | end 68 | 69 | --[[ 70 | Attaches the camera to an entity. Must use DetachFromPlayer first. 71 | ]] 72 | function Camera:AttachToEntity(entity, offset) 73 | assert(entity:Exists(), "cannot Camera:AttachToEntity on entity that does not exist") 74 | assert(self:GetCurrentCam() ~= self.cam, "must use Camera:DetachFromPlayer before using Camera:AttachToEntity") 75 | 76 | AttachCamToEntity(self:GetCurrentCam(), entity:GetEntityId(), offset.x, offset.y, offset.z, true) 77 | end 78 | 79 | function Camera:Reset(ease, ease_time) 80 | ClearFocus() 81 | RenderScriptCams(false, ease, ease_time or 0, true, false) 82 | DestroyCam(self.freecam, false) 83 | DestroyCam(self.from_cam, false) 84 | DestroyCam(self.to_cam, false) 85 | self.from_cam = nil 86 | self.to_cam = nil 87 | self.freecam = nil 88 | end 89 | 90 | function Camera:GetFOV() 91 | return GetGameplayCamFov() 92 | end 93 | 94 | function Camera:PointAtEntity(entity) 95 | PointCamAtEntity(self:GetCurrentCam(), entity:GetEntityId(), 0.0, 0.0, 0.0, true) 96 | end 97 | 98 | function Camera:PointAtCoord(pos) 99 | PointCamAtCoord(self:GetCurrentCam(), pos.x, pos.y, pos.z) 100 | end 101 | 102 | function Camera:SetPosition(pos) 103 | SetCamCoord(self:GetCurrentCam(), pos.x, pos.y, pos.z) 104 | end 105 | 106 | function Camera:SetRotation(rot) 107 | SetCamRot(self:GetCurrentCam(), rot.x, rot.y, rot.z, 2) 108 | end 109 | 110 | function Camera:SetGameplayCamShakeAmplitude(amount) 111 | SetGameplayCamShakeAmplitude(tofloat(amount)) 112 | end 113 | 114 | CameraShakeType = 115 | { 116 | DEATH_FAIL_IN_EFFECT_SHAKE = "DEATH_FAIL_IN_EFFECT_SHAKE", 117 | DRUNK_SHAKE = "DRUNK_SHAKE", 118 | FAMILY5_DRUG_TRIP_SHAKE = "FAMILY5_DRUG_TRIP_SHAKE", 119 | HAND_SHAKE = "HAND_SHAKE", 120 | JOLT_SHAKE = "JOLT_SHAKE", 121 | LARGE_EXPLOSION_SHAKE = "LARGE_EXPLOSION_SHAKE", 122 | MEDIUM_EXPLOSION_SHAKE = "MEDIUM_EXPLOSION_SHAKE", 123 | SMALL_EXPLOSION_SHAKE = "SMALL_EXPLOSION_SHAKE", 124 | ROAD_VIBRATION_SHAKE = "ROAD_VIBRATION_SHAKE", 125 | SKY_DIVING_SHAKE = "SKY_DIVING_SHAKE", 126 | VIBRATE_SHAKE = "VIBRATE_SHAKE" 127 | } 128 | --[[ 129 | Shakes the Camera with intensity. 130 | 131 | shakeType is a CameraShakeType enum. Intensity is a number 132 | ]] 133 | function Camera:Shake(shakeType, intensity) 134 | ShakeGameplayCam(shakeType, tofloat(intensity)) 135 | end 136 | 137 | --[[ 138 | Fades in the camera from black over time in ms 139 | ]] 140 | function Camera:FadeIn(time) 141 | DoScreenFadeIn(time) 142 | end 143 | 144 | --[[ 145 | Fades out the camera from black over time in ms 146 | ]] 147 | function Camera:FadeOut(time) 148 | DoScreenFadeOut(time) 149 | end 150 | 151 | --[[ 152 | Returns whether the screen is black or not. 153 | ]] 154 | function Camera:IsFadedOut() 155 | return IsScreenFadedOut() 156 | end 157 | 158 | --[[ 159 | Returns true if the screen isn't faded out to black. 160 | ]] 161 | function Camera:IsFadedIn() 162 | return IsScreenFadedIn() 163 | end 164 | 165 | function Camera:GetPosition() 166 | --return GetCamCoord(self:GetCurrentCam()) 167 | return GetGameplayCamCoord() 168 | end 169 | 170 | function Camera:GetRotation() 171 | --return Vector3Math:RotationToDirection(GetCamRot(self:GetCurrentCam())) 172 | return Vector3Math:RotationToDirection(GetGameplayCamRot(0)) 173 | end 174 | 175 | function Camera:GetCurrentCam() 176 | if self.freecam then return self.freecam end 177 | return self.cam 178 | end 179 | 180 | --[[ 181 | rightVector --[[ vector3, forwardVector --[[ vector3, upVector --[[ vector3, position --[[ vector3 182 | Returns the world matrix of the specified camera. To turn this into a view matrix, calculate the inverse. 183 | ]] 184 | function Camera:GetMatrix() 185 | return GetCamMatrix(self:GetCurrentCam()) 186 | end 187 | 188 | Camera = Camera() -------------------------------------------------------------------------------- /client/explosion/cExplosion.lua: -------------------------------------------------------------------------------- 1 | Explosion = class() 2 | 3 | --[[ 4 | Creates an explosion 5 | 6 | args (in table): 7 | owner (ped id, optional): ped id of the owner of this explosion 8 | position (vector3): position of the explosion 9 | type (ExplosionTypes): explosion type 10 | damageScale (number, optional): damage scale of explosion 11 | isAudible (bool, optional): whether or not this explosion can be heard 12 | isInvisible (bool, optional): whether or not this explosion can be seen 13 | cameraShake (number, optional): amount of camera shake this explosion has 14 | noDamage (bool, optional): if the explosion does damage or not 15 | ]] 16 | function Explosion:Create(args) 17 | if args.owner ~= nil then 18 | AddOwnedExplosion( 19 | args.owner, 20 | args.position.x, args.position.y, args.position.z, 21 | args.type, 22 | args.damageScale == nil and 1.0 or args.damageScale, 23 | args.isAudible == nil and true or args.isAudible, 24 | args.isInvisible == nil and false or args.isInvisible, 25 | args.cameraShake == nil and 1.0 or args.cameraShake, 26 | args.noDamage == nil and false or args.noDamage) 27 | else 28 | AddExplosion( 29 | args.position.x, args.position.y, args.position.z, 30 | args.type, 31 | args.damageScale == nil and 1.0 or args.damageScale, 32 | args.isAudible == nil and true or args.isAudible, 33 | args.isInvisible == nil and false or args.isInvisible, 34 | args.cameraShake == nil and 1.0 or args.cameraShake, 35 | args.noDamage == nil and false or args.noDamage) 36 | end 37 | end 38 | 39 | Explosion = Explosion() 40 | 41 | if IsRedM then 42 | ExplosionTypes = 43 | { 44 | EXP_TAG_DONTCARE = -1, 45 | EXP_TAG_GRENADE = 0, 46 | EXP_TAG_STICKYBOMB = 1, 47 | EXP_TAG_MOLOTOV = 2, 48 | EXP_TAG_MOLOTOV_VOLATILE = 3, 49 | EXP_TAG_HI_OCTANE = 4, 50 | EXP_TAG_CAR = 5, 51 | EXP_TAG_PLANE = 6, 52 | EXP_TAG_PETROL_PUMP = 7, 53 | EXP_TAG_DIR_STEAM = 8, 54 | EXP_TAG_DIR_FLAME = 9, 55 | EXP_TAG_DIR_WATER_HYDRANT = 10, 56 | EXP_TAG_BOAT = 11, 57 | EXP_TAG_BULLET = 12, 58 | EXP_TAG_SMOKEGRENADE = 13, 59 | EXP_TAG_BZGAS = 14, 60 | EXP_TAG_GAS_CANISTER = 15, 61 | EXP_TAG_EXTINGUISHER = 16, 62 | EXP_TAG_TRAIN = 17, 63 | EXP_TAG_DIR_FLAME_EXPLODE = 18, 64 | EXP_TAG_VEHICLE_BULLET = 19, 65 | EXP_TAG_BIRD_CRAP = 20, 66 | EXP_TAG_FIREWORK = 21, 67 | EXP_TAG_TORPEDO = 22, 68 | EXP_TAG_TORPEDO_UNDERWATER = 23, 69 | EXP_TAG_LANTERN = 24, 70 | EXP_TAG_DYNAMITE = 25, 71 | EXP_TAG_DYNAMITESTACK = 26, 72 | EXP_TAG_DYNAMITE_VOLATILE = 27, 73 | EXP_TAG_RIVER_BLAST = 28, 74 | EXP_TAG_PLACED_DYNAMITE = 29, 75 | EXP_TAG_FIRE_ARROW = 30, 76 | EXP_TAG_DYNAMITE_ARROW = 31, 77 | EXP_TAG_PHOSPHOROUS_BULLET = 32, 78 | EXP_TAG_LIGHTNING_STRIKE = 33, 79 | EXP_TAG_TRACKING_ARROW = 34, 80 | EXP_TAG_POISON_BOTTLE = 35 81 | } 82 | end -------------------------------------------------------------------------------- /client/hud/cHud.lua: -------------------------------------------------------------------------------- 1 | HUD = class() 2 | 3 | function HUD:__init() 4 | self.render = Events:Subscribe("Render", function() self:Render() end) 5 | end 6 | 7 | --[[ 8 | Draws a frontend alert, like that seen here: http://www.kronzky.info/fivemwiki/index.php?title=DrawFrontendAlert 9 | 10 | args (in table): 11 | title (string) - title of the message 12 | subtitle (string) - (optional) subtitle of the message 13 | subtitle2 (string) - (optional) second subtitle 14 | no_bg (bool) - (optional) default false, set to true to remove the background 15 | time (number) - how long the alert should display for in seconds 16 | ]] 17 | function HUD:DrawFrontendAlert(args) 18 | assert(type(args.time) == 'number', 'args.time expected to be a number') 19 | Citizen.CreateThread(function() 20 | AddTextEntry("FACES_WARNH2", args.title or '') 21 | AddTextEntry("QM_NO_0", args.subtitle or '') 22 | AddTextEntry("QM_NO_3", args.subtitle2 or '') 23 | local timer = Timer() 24 | while timer:GetSeconds() < args.time do 25 | Citizen.Wait(0) 26 | DrawFrontendAlert("FACES_WARNH2", "QM_NO_0", 3, 3, "QM_NO_3", 2, -1, false, "FM_NXT_RAC", "QM_NO_1", no_bg ~= true, false) 27 | end 28 | end) 29 | end 30 | 31 | --[[ 32 | If the radar/minimap should be enabled or not 33 | ]] 34 | function HUD:SetDisplayRadar(enabled) 35 | DisplayRadar(enabled) 36 | end 37 | 38 | if IsRedM then 39 | function HUD:ShowComponent(hash) 40 | Citizen.InvokeNative(0x8BC7C1F929D07BF3, hash) 41 | end 42 | 43 | function HUD:HideComponent(hash) 44 | Citizen.InvokeNative(0x4CC5F2FC1332577F, hash) 45 | end 46 | elseif IsFiveM then 47 | --[[ 48 | 49 | HUD Component ids 50 | 1 : WANTED_STARS 51 | 2 : WEAPON_ICON 52 | 3 : CASH 53 | 4 : MP_CASH 54 | 5 : MP_MESSAGE 55 | 6 : VEHICLE_NAME 56 | 7 : AREA_NAME 57 | 8 : VEHICLE_CLASS 58 | 9 : STREET_NAME 59 | 10 : HELP_TEXT 60 | 11 : FLOATING_HELP_TEXT_1 61 | 12 : FLOATING_HELP_TEXT_2 62 | 13 : CASH_CHANGE 63 | 14 : RETICLE 64 | 15 : SUBTITLE_TEXT 65 | 16 : RADIO_STATIONS 66 | 17 : SAVING_GAME 67 | 18 : GAME_STREAM 68 | 19 : WEAPON_WHEEL 69 | 20 : WEAPON_WHEEL_STATS 70 | 21 : HUD_COMPONENTS 71 | 22 : HUD_WEAPONS 72 | 73 | ]] 74 | 75 | function HUD:HideComponentThisFrame(id) 76 | HideHudComponentThisFrame(id) 77 | end 78 | 79 | function HUD:HideMinimapExteriorThisFrame() 80 | HideMinimapExteriorMapThisFrame() 81 | end 82 | 83 | function HUD:HideHudAndRadarThisFrame() 84 | HideHudAndRadarThisFrame() 85 | end 86 | end 87 | 88 | --[[ 89 | Toggles the display of the ammo in the top right 90 | ]] 91 | function HUD:SetDisplayAmmo(enabled) 92 | self.display_ammo = enabled 93 | end 94 | 95 | --[[ 96 | Toggles the display of the cash hud in the top right 97 | ]] 98 | function HUD:SetDisplayCash(enabled) 99 | self.display_cash = enabled 100 | end 101 | 102 | --[[ 103 | Toggles the display of the bank hud in the top right 104 | ]] 105 | function HUD:SetDisplayBank(enabled) 106 | self.display_bank = enabled 107 | end 108 | 109 | function HUD:Render() 110 | --DisplayAmmoThisFrame(self.display_ammo) -- native does not exist 111 | --if not self.display_bank then RemoveMultiplayerBankCash() end -- native does not exist 112 | --if not self.display_cash then RemoveMultiplayerHudCash() end -- native does not exist 113 | end 114 | 115 | if IsFiveM then 116 | function HUD:SetVisible(visible) 117 | DisplayHud(visible) 118 | end 119 | end 120 | 121 | HUD = HUD() 122 | 123 | if IsRedM then 124 | HudComponent = { 125 | everything = -1679307491, 126 | minimapHonorAndCards = 724769646, 127 | minimap = 474191950, 128 | forceHonor = 121713391, 129 | onlyActionWheel = 2011163970, 130 | actionWheelWeapons = -1249243147, 131 | skillCards = 1058184710, 132 | onlyMoney = 1920936087, 133 | actionWheelFishing = 100665617, 134 | onlyFishingBait = -859384195, 135 | unkSpMoney = -950624750, 136 | unkSpMoneyReplace = 1670279562, 137 | mpMoney = -66088566, 138 | unkInfoBox = 36547242, 139 | campTraderInfo = -782493871, 140 | honorMoneyCards = -2124237476, 141 | forceSkillCards = 1533515944, 142 | actionWheelItems = -2106452847 143 | } 144 | end -------------------------------------------------------------------------------- /client/keypress/cKeymap.lua: -------------------------------------------------------------------------------- 1 | Keymap = class() 2 | 3 | -- See https://cookbook.fivem.net/2020/01/06/using-the-new-console-key-bindings/ for details 4 | function Keymap:__init() 5 | 6 | self.maps = {} 7 | self.id_pool = IdPool() 8 | 9 | end 10 | 11 | function Keymap:Register(key, keytype, name, cb) 12 | 13 | local id = self.id_pool:GetNextId() 14 | 15 | if not self.maps[key] then 16 | self.maps[key] = {} 17 | 18 | local keymap_name = string.format("keymap_%s", name:gsub(" ", "_")) 19 | RegisterKeyMapping("+" .. keymap_name, name, keytype, key) 20 | 21 | RegisterCommand("+" .. keymap_name, function() 22 | for id, callback in pairs(self.maps[key]) do 23 | callback({down = true}) 24 | end 25 | end) 26 | 27 | RegisterCommand("-" .. keymap_name, function() 28 | for id, callback in pairs(self.maps[key]) do 29 | callback({up = true}) 30 | end 31 | end) 32 | 33 | end 34 | 35 | self.maps[key][id] = cb 36 | 37 | return id 38 | 39 | end 40 | 41 | function Keymap:Unregister(key, id) 42 | if self.maps[key] and self.maps[key][id] then 43 | self.maps[key][id] = nil 44 | end 45 | end 46 | 47 | Keymap = Keymap() -------------------------------------------------------------------------------- /client/light/cLight.lua: -------------------------------------------------------------------------------- 1 | Light = class() 2 | 3 | LightIds = 0 -- Track light ids 4 | 5 | function GenerateLightId() 6 | LightIds = LightIds + 1 7 | return LightIds 8 | end 9 | 10 | --[[ 11 | Creates a light. 12 | 13 | args (in table): 14 | 15 | position - (vector3) position 16 | color - (Color) the color of the light 17 | type - (string) can be either LightTypes.Spot or LightTypes.Point 18 | shadow - (bool) whether or not this light creates shadows 19 | 20 | args required for LightTypes.Spot: 21 | direction - (vector3) which way the light is pointing 22 | very finicky - if you have a 0 as the z component it may not work 23 | brightness - (number) how bright it is 24 | hardness - (number) how hard the edges of the light are 25 | radius - (number) how wide the radius is 26 | falloff - (number) how much the light fades with distance 27 | 28 | args required for LightTypes.Point: 29 | range - (number) range of the light 30 | intensity - (number) how bright/intense the light is 31 | 32 | ]] 33 | function Light:__init(args) 34 | 35 | assert(args.type == LightTypes.Point and args.shadow == false, 36 | "Unsupported light type (currently). Try LightTypes.Point and shadow = false") 37 | 38 | assert(type(args.position) == "vector3", "args.position expected to be a vector3") 39 | TypeCheck:Color(args.color) 40 | assert(args.type == LightTypes.Spot or args.type == LightTypes.Point, "args.type expected to be a LightTypes") 41 | 42 | self.position = args.position 43 | self.color = args.color 44 | self.type = args.type 45 | self.shadow = args.shadow 46 | self.id = GenerateLightId() 47 | 48 | if self.type == LightTypes.Spot then 49 | assert(type(args.direction) == "vector3", "args.direction expected to be a vector3") 50 | TypeCheck:Number(args.distance) 51 | TypeCheck:Number(args.brightness) 52 | TypeCheck:Number(args.hardness) 53 | TypeCheck:Number(args.radius) 54 | TypeCheck:Number(args.falloff) 55 | 56 | self.direction = tofloat(args.direction) 57 | self.distance = tofloat(args.distance) 58 | self.brightness = tofloat(args.brightness) 59 | self.hardness = tofloat(args.hardness) 60 | self.radius = tofloat(args.radius) 61 | self.falloff = tofloat(args.falloff) 62 | else 63 | TypeCheck:Number(args.range) 64 | TypeCheck:Number(args.intensity) 65 | 66 | self.range = tofloat(args.range) 67 | self.intensity = tofloat(args.intensity) 68 | end 69 | 70 | self.enabled = true 71 | 72 | self:Draw() 73 | 74 | end 75 | 76 | function Light:GetPosition() 77 | return self.position 78 | end 79 | 80 | function Light:SetPosition(pos) 81 | assert(type(pos) == "vector3", "pos expected to be a vector3") 82 | self.position = pos 83 | end 84 | 85 | function Light:GetColor() 86 | return self.color 87 | end 88 | 89 | function Light:SetColor(col) 90 | TypeCheck:Color(col) 91 | self.color = col 92 | end 93 | 94 | function Light:SetDirection(dir) 95 | assert(type(dir) == "vector3", "direction expected to be a vector3") 96 | self.direction = dir 97 | end 98 | 99 | function Light:GetDirection() 100 | return self.direction 101 | end 102 | 103 | function Light:GetBrightness() 104 | return self.brightness 105 | end 106 | 107 | function Light:SetBrightness(b) 108 | TypeCheck:Number(b) 109 | self.brightness = b 110 | end 111 | 112 | function Light:GetHardness() 113 | return self.hardness 114 | end 115 | 116 | function Light:SetHardness(h) 117 | TypeCheck:Number(h) 118 | self.hardness = h 119 | end 120 | 121 | function Light:GetRadius() 122 | return self.radius 123 | end 124 | 125 | function Light:SetRadius(radius) 126 | TypeCheck:Number(radius) 127 | self.radius = radius 128 | end 129 | 130 | function Light:SetFalloff(falloff) 131 | TypeCheck:Number(falloff) 132 | self.falloff = falloff 133 | end 134 | 135 | function Light:GetFalloff() 136 | return self.falloff 137 | end 138 | 139 | function Light:GetRange() 140 | return self.range 141 | end 142 | 143 | function Light:SetRange(range) 144 | TypeCheck:Number(range) 145 | self.range = range 146 | end 147 | 148 | function Light:GetIntensity() 149 | return self.intensity 150 | end 151 | 152 | function Light:SetIntensity(intensity) 153 | TypeCheck:Number(intensity) 154 | self.intensity = intensity 155 | end 156 | 157 | function Light:SetEnabled(enabled) 158 | assert(type(enabled) == "bool", "enabled expected to be a bool") 159 | self.enabled = enabled 160 | if self.enabled then 161 | self:Draw() -- Draw call to remake coroutine 162 | end 163 | end 164 | 165 | function Light:GetShadowEnabled() 166 | return self.shadow 167 | end 168 | 169 | function Light:SetShadowEnabled(shadow) 170 | assert(type(shadow) == "bool", "shadow expected to be a bool") 171 | self.shadow = shadow 172 | end 173 | 174 | function Light:Draw() 175 | local type = self.type 176 | local id = self.id 177 | 178 | CreateThread(function() 179 | while self.enabled do 180 | 181 | Wait(1) 182 | 183 | if type == LightTypes.Spot then 184 | if self.shadow then 185 | DrawSpotLightWithShadow( 186 | self.position.x, self.position.y, self.position.z, 187 | self.direction.x, self.direction.y, self.direction.z, 188 | self.color.r, self.color.g, self.color.b, 189 | self.distance, self.brightness, self.hardness, 190 | self.radius, self.falloff, self.id 191 | ) 192 | else 193 | DrawSpotLight( 194 | self.position.x, self.position.y, self.position.z, 195 | self.direction.x, self.direction.y, self.direction.z, 196 | self.color.r, self.color.g, self.color.b, 197 | self.distance, self.brightness, self.hardness, 198 | self.radius, self.falloff 199 | ) 200 | end 201 | else 202 | if self.shadow then 203 | DrawLightWithRangeAndShadow( 204 | self.position.x, self.position.y, self.position.z, 205 | self.color.r, self.color.g, self.color.b, 206 | self.range, self.intensity, self.id 207 | ) 208 | else 209 | DrawLightWithRange( 210 | self.position.x, self.position.y, self.position.z, 211 | self.color.r, self.color.g, self.color.b, 212 | self.range, self.intensity 213 | ) 214 | end 215 | end 216 | end 217 | end) 218 | end 219 | 220 | function Light:Remove() 221 | self.enabled = false 222 | end 223 | 224 | LightTypes = {Spot = 1, Point = 2} -------------------------------------------------------------------------------- /client/localplayer_behaviors/cBulletDetectionLocalPlayerBehavior.lua: -------------------------------------------------------------------------------- 1 | BulletDetectionLocalPlayerBehavior = class() 2 | BulletDetectionLocalPlayerBehavior.name = "BulletDetectionLocalPlayerBehavior" 3 | 4 | --[[ 5 | BulletDetectionLocalPlayerBehavior: 6 | Detects and tracks bullets fired by the LocalPlayer 7 | ]] 8 | 9 | function BulletDetectionLocalPlayerBehavior:__init() 10 | self.active = true 11 | self.stored_clip_ammo = 0 12 | 13 | self:CheckForBulletsFired() 14 | end 15 | 16 | function BulletDetectionLocalPlayerBehavior:CheckForBulletsFired() 17 | Citizen.CreateThread(function() 18 | local stored_clip_ammo, current_clip_ammo 19 | local localplayer_has_weapon, localplayer_weapon_enum 20 | 21 | while self.active do 22 | Wait(1) 23 | 24 | localplayer_has_weapon, localplayer_weapon_enum = LocalPlayer:GetEquippedWeapon() 25 | 26 | if localplayer_has_weapon then 27 | stored_clip_ammo = self.stored_clip_ammo 28 | current_clip_ammo = LocalPlayer:GetCurrentClipAmmoForWeapon(localplayer_weapon_enum) 29 | 30 | if stored_clip_ammo ~= current_clip_ammo then 31 | print("Detected Clip Ammo Change") 32 | if stored_clip_ammo - 1 == current_clip_ammo then 33 | print("Detected Bullet Fired") 34 | end 35 | 36 | local aiming_at_entity, entity_id = GetEntityPlayerIsFreeAimingAt(PlayerId()) 37 | if IsEntityAPed(entity_id) and aiming_at_entity then 38 | Chat:Debug("entity: " .. tostring(entity_id)) 39 | 40 | -- verify that localplayer weapon is the last weapon that hit the ped 41 | --local weapon_damage_validation_passed = HasPedBeenDamagedByWeapon(entity_id, WeaponEnum:GetWeaponHash(localplayer_weapon_enum), 0) 42 | --Chat:Print("HasPedBeenDamagedByWeapon(): " .. tostring(weapon_damage_validation)) 43 | --if weapon_damage_validation_passed then 44 | -- detected damage 45 | --end 46 | end 47 | 48 | -- this comes last 49 | self.stored_clip_ammo = current_clip_ammo 50 | end 51 | end 52 | end 53 | end) 54 | end 55 | -------------------------------------------------------------------------------- /client/marker/cMarker.lua: -------------------------------------------------------------------------------- 1 | Marker = class() 2 | 3 | if IsRedM then 4 | -- Partially from here https://github.com/femga/rdr3_discoveries/blob/master/graphics/markers/marker_types.lua 5 | MarkerTypes = 6 | { 7 | Box = 1857541051, 8 | Cylinder = -1795314153, 9 | Sphere = 0x50638AB9, 10 | Ring = 0xEC032ADD, 11 | Halo = 0x6903B113, 12 | RaceCheckpoint = 0xE60FF3B9, 13 | RaceFinish = 0x664669A6, 14 | CanoePole = 0xE03A92AE, 15 | Buoy = 0x751F27D6 16 | } 17 | elseif IsFiveM then 18 | -- Add more markers from this list as needed: https://docs.fivem.net/docs/game-references/markers/ 19 | MarkerTypes = 20 | { 21 | UpsideDownCone = 0, 22 | VerticalCylinder = 1, 23 | DebugSphere = 28 24 | } 25 | end 26 | 27 | --[[ 28 | Creates a new marker in the world, aka a glowly circle thing. 29 | 30 | args (in table): 31 | type (MarkerTypes): shape/type of the marker 32 | position (vector3): position of the marker in the world 33 | direction (vector3): direction of the marker 34 | rotation (vector3): rotation of the marker 35 | scale (vector3): scale of the marker 36 | color (Color): color of the marker 37 | 38 | -- optional: 39 | bob_up_and_down (bool, default false): if the marker bobs up and down 40 | face_camera (bool, default false): if the marker faces the camera 41 | is_rotating (bool, default false): if the marker rotates 42 | texture_dict (string, default nil): if the marker uses a texture 43 | texture_name (string, default nil): if the marker uses a texture 44 | draw_on_entity (bool, default false): if the marker draws on entities 45 | rotation_order (number 0-2, default 1): rotation order of the marker 46 | 47 | ]] 48 | function Marker:__init(args) 49 | 50 | self.type = args.type 51 | self.position = args.position 52 | self.direction = args.direction 53 | self.rotation = args.rotation 54 | self.scale = args.scale 55 | self.color = Color(args.color.r, args.color.g, args.color.b, args.color.a) 56 | 57 | self.bob_up_and_down = args.bob_up_and_down ~= nil and args.bob_up_and_down or false 58 | self.face_camera = args.face_camera ~= nil and args.face_camera or false 59 | self.is_rotating = args.is_rotating ~= nil and args.is_rotating or false 60 | self.texture_dict = args.texture_dict 61 | self.texture_name = args.texture_name 62 | self.draw_on_entity = args.draw_on_entity ~= nil and args.draw_on_entity or false 63 | 64 | self.rotation_order = args.rotation_order or 1 65 | 66 | self.visible = true 67 | 68 | self.render = Events:Subscribe("Render", self, self.Draw) 69 | end 70 | 71 | function Marker:SetVisible(visible) 72 | self.visible = visible 73 | end 74 | 75 | function Marker:SetColor(color) 76 | self.color = color 77 | end 78 | 79 | function Marker:FadeOut() 80 | self.fadeout = true 81 | end 82 | 83 | function Marker:Draw() 84 | if not self.visible then return end 85 | Citizen.InvokeNative(IsFiveM and 0x28477EC23D892089 or 0x2A32FAA57B937173, 86 | self.type, 87 | self.position.x, self.position.y, self.position.z, 88 | self.direction.x, self.direction.z, self.direction.z, 89 | self.rotation.x, self.rotation.y, self.rotation.z, 90 | self.scale.x, self.scale.y, self.scale.z, 91 | self.color.r, self.color.g, self.color.b, self.color.a, 92 | self.bob_up_and_down, self.face_camera, 93 | self.rotation_order, self.is_rotating, 94 | self.texture_dict, self.texture_name, 95 | self.draw_on_entity 96 | ) 97 | 98 | if self.fadeout then 99 | self.color.a = self.color.a - 1 100 | if self.color.a <= 0 then 101 | self:Remove() 102 | end 103 | end 104 | end 105 | 106 | function Marker:Remove() 107 | if self.render then self.render:Unsubscribe() end 108 | self.render = nil 109 | end -------------------------------------------------------------------------------- /client/network/cNetwork.lua: -------------------------------------------------------------------------------- 1 | local NetworkEvent = class() 2 | 3 | local NetworkEvent_id = 0 4 | function NetworkEvent:__init(name, instance, callback) 5 | self.name = name 6 | self.instance = instance 7 | self.callback = callback 8 | self.id = NetworkEvent_id 9 | NetworkEvent_id = NetworkEvent_id + 1 10 | end 11 | 12 | function NetworkEvent:Unsubscribe() 13 | Network:Unsubscribe(self.name, self.id) 14 | end 15 | 16 | function NetworkEvent:Receive(args, name) 17 | -- source is nil if this is clientside, otherwise it is the player who sent it 18 | local return_args = {} 19 | local is_fetch = false 20 | local fetch_id 21 | 22 | if args then 23 | is_fetch = args.__is_fetch 24 | fetch_id = args.__fetch_id 25 | args.__is_fetch = nil 26 | args.__fetch_id = nil 27 | for k,v in pairs(args) do 28 | return_args[k] = v 29 | end 30 | end 31 | 32 | local return_value 33 | if self.callback then 34 | return_value = self.callback(self.instance, return_args) 35 | else 36 | local callback = self.instance 37 | return_value = callback(return_args) 38 | end 39 | 40 | if is_fetch then 41 | assert(return_value and type(return_value) == "table", "A Network:Fetch handler function must return a table!") 42 | Network:Send(name .. "__FetchCallback" .. tostring(fetch_id), return_value) 43 | end 44 | end 45 | 46 | Network = class() 47 | 48 | function Network:__init() 49 | self.subs = {} 50 | self.handlers = {} 51 | self.current_fetch_id = 1 52 | self.fetch_data = {} 53 | end 54 | 55 | function Network:Send(name, args) 56 | assert(name ~= nil and type(name) == "string", "cannot Network:Send without valid networkevent") 57 | 58 | TriggerServerEvent(name, args) 59 | end 60 | 61 | --[[ 62 | Blocks the current Thread until a response is received from the server 63 | ]] 64 | function Network:Fetch(name, args) 65 | self.current_fetch_id = self.current_fetch_id + 1 66 | local fetch_id = self.current_fetch_id 67 | local fetch_args = args 68 | if not args then 69 | fetch_args = {} 70 | end 71 | fetch_args.__is_fetch = true 72 | fetch_args.__fetch_id = fetch_id 73 | Network:Send(name, fetch_args) 74 | fetch_args.__is_fetch = nil 75 | fetch_args.__fetch_id = nil 76 | 77 | local subscription = Network:Subscribe(name .. "__FetchCallback" .. tostring(fetch_id), function(args) 78 | self.fetch_data[fetch_id] = args 79 | end) 80 | 81 | 82 | local response_timer = Timer() 83 | local fetched_data 84 | while not self.fetch_data[fetch_id] do 85 | Wait(10) 86 | if response_timer:GetSeconds() > 4 then 87 | print("Network:Fetch timed out! No response received from the server within 4 seconds. Did you forget to add a Network:Subscribe for this Fetch in the server-side code?") 88 | break 89 | end 90 | end 91 | fetched_data = self.fetch_data[fetch_id] 92 | self.fetch_data[fetch_id] = nil 93 | subscription:Unsubscribe() 94 | 95 | return fetched_data 96 | end 97 | 98 | --[[ 99 | Subscribe to a network event. 100 | 101 | Example usage: 102 | Network:Subscribe("PlayerLoaded", function(args) 103 | end) 104 | ]] 105 | function Network:Subscribe(name, instance, callback) 106 | assert(name ~= nil, "cannot subscribe networkevent without name") 107 | assert(type(instance) == "table" or type(instance) == "function", "callback function non-existant or no callback instance provided. Function usage is Network:Subscribe(name, instance, callback) or Network:Subscribe(name, callback)") 108 | 109 | if not self.subs[name] then 110 | self.subs[name] = {} 111 | 112 | RegisterNetEvent(name) 113 | self.handlers[name] = AddEventHandler(name, function(args) 114 | for _, networkevent in pairs(self.subs[name]) do 115 | networkevent:Receive(args, name) 116 | end 117 | end) 118 | end 119 | 120 | local networkevent = NetworkEvent(name, instance, callback) 121 | self.subs[name][networkevent.id] = networkevent 122 | 123 | return networkevent 124 | end 125 | 126 | function Network:Unsubscribe(name, id) 127 | assert(name ~= nil, "cannot unsubscribe networkevent without name") 128 | 129 | assert(self.subs[name] ~= nil and self.subs[name][id] ~= nil, "cannot unsubscribe NetworkEvent that does not exist") 130 | self.subs[name][id] = nil 131 | 132 | if count_table(self.subs[name]) == 0 then 133 | RemoveEventHandler(self.handlers[name]) 134 | self.subs[name] = nil 135 | self.handlers[name] = nil 136 | end 137 | end 138 | 139 | Network = Network() -------------------------------------------------------------------------------- /client/object/cObject.lua: -------------------------------------------------------------------------------- 1 | Object = class(Entity) 2 | 3 | Objects = {} -- Global table to keep track of all the objects, indexed by entity id 4 | 5 | --[[ 6 | Spawns a new object. 7 | 8 | args (in table) 9 | 10 | model - string of model name 11 | position - vector3 12 | rotation (optional) - vector3 of rotation 13 | isNetwork (optional) - (bool) whether you want this object to be synced across the network 14 | quaternion (optional) - quat 15 | kinematic (optional) - if the object is kinematic or not 16 | callback (optional) - callback function that is called after the object is spawned 17 | ]] 18 | function Object:__init(args) 19 | 20 | assert(type(args.model) == "string" or type(args.model) == "number", 21 | "args.model expected to be a string or hash (number)") 22 | assert(type(args.position) == "vector3", "args.position expected to be a vector3") 23 | 24 | self.hash = type(args.model) == "string" and GetHashKey(args.model) or args.model 25 | self.model = args.model 26 | 27 | self:InitializeValueStorage(self) 28 | 29 | LoadModel(self.hash, function() 30 | self.entity = CreateObject(self.hash, args.position.x, args.position.y, args.position.z, args.isNetwork == true, true, true, true, true) 31 | self:SetQuaternion(type(args.quaternion) == "quat" and args.quaternion or quat(0,0,0,0)) 32 | self:SetKinematic(args.kinematic or false) 33 | self:SetAsPersistent() 34 | if args.rotation then self:SetRotation(args.rotation) end 35 | 36 | self:SetPosition(args.position) -- Reset position again because sometimes they move 37 | 38 | Objects[self:GetEntityId()] = self 39 | 40 | if args.callback then 41 | args.callback(self) 42 | end 43 | end) 44 | 45 | end 46 | 47 | function Object:PlaceOnGroundProperly() 48 | PlaceObjectOnGroundProperly(self.entity) 49 | end 50 | 51 | function Object:GetModel() 52 | return self.model 53 | end 54 | 55 | function Object:GetHash() 56 | return self.hash 57 | end 58 | 59 | function Object:Slide(to_pos, speed, has_collision) 60 | SlideObject(self:GetEntityId(), to_pos.x, to_pos.y, to_pos.z, speed.x, speed.y, speed.z, has_collision) 61 | end 62 | 63 | function Object:Equals(object) 64 | return object ~= nil and self:GetEntityId() == object:GetEntityId() 65 | end 66 | 67 | function Object:Destroy() 68 | if not self:GetEntityId() then 69 | print("[WARNING] Tried to destroy object without valid entity id. (did you already remove it?)") 70 | return 71 | end 72 | Objects[self:GetEntityId()] = nil 73 | self:Remove() 74 | end -------------------------------------------------------------------------------- /client/object/cObjectManager.lua: -------------------------------------------------------------------------------- 1 | ObjectManager = class() 2 | 3 | function ObjectManager:__init() 4 | Events:Subscribe("onResourceStop", self, self.OnResourceStop) 5 | end 6 | 7 | function ObjectManager:FindObjectByEntityId(id) 8 | return Objects[id] 9 | end 10 | 11 | -- Clean up all spawned objects on resource stop 12 | function ObjectManager:OnResourceStop(resource_name) 13 | if GetCurrentResourceName() == resource_name then 14 | self:RemoveAllObjects() 15 | end 16 | end 17 | 18 | function ObjectManager:RemoveAllObjects() 19 | for id, object in pairs(Objects) do 20 | object:Destroy() 21 | end 22 | Objects = {} 23 | end 24 | 25 | ObjectManager = ObjectManager() 26 | 27 | -------------------------------------------------------------------------------- /client/particle-effect/cParticleEffect.lua: -------------------------------------------------------------------------------- 1 | ParticleEffect = class() 2 | 3 | --[[ 4 | Creates a particle. 5 | 6 | args (in table): 7 | 8 | There are 3 ways you can create a particle: 9 | at a position (ParticleEffectTypes.Position) 10 | attached to an entity (with an offset) (ParticleEffectTypes.Entity) 11 | attached to an entity bone (with an offset) (ParticleEffectTypes.Bone) 12 | 13 | bank - (string) effect bank of the effect you want to play. List below 14 | effect - (string) name of the effect you want to play. List here: https://pastebin.com/N9unUFWY 15 | type - (ParticleEffectTypes) 16 | if ParticleEffectTypes.Position, requires these args: 17 | position - (vector3) the position to create the particle at 18 | if ParticleEffectTypes.Entity, requires these args: 19 | entity - (entity) the entity to attach the effect to 20 | offset - (vector3) the offset from the entity to play the effect 21 | if ParticleEffectTypes.Entity, requires these args: 22 | entity - (entity) the entity to attach the effect to 23 | offset - (vector3) the offset from the entity to play the effect 24 | bone - (number) the index of the bone to play the effect on 25 | 26 | scale - (number) how big the particle is 27 | rotation (optional) - (vector3) the rotation of the particle 28 | loop (optional) - (bool) if you want the effect to loop, default false 29 | axis (optional) - (vector3) if you want to flip axes and get wild (Invert Axis Flags) 30 | callback (optional) - (func) if you want to do something after the effect is spawned 31 | 32 | ]] 33 | function ParticleEffect:__init(args) 34 | assert(args.effect ~= nil, "cannot create ParticleEffect without effect") 35 | assert(args.bank ~= nil, "cannot create ParticleEffect without bank") 36 | assert(args.type ~= nil, "cannot create ParticleEffect without type") 37 | assert(args.scale ~= nil, "cannot create ParticleEffect without scale") 38 | 39 | self.effect = args.effect 40 | self.bank = args.bank 41 | self.type = args.type 42 | self.scale = tofloat(args.scale) 43 | self.rotation = args.rotation or vector3(0, 0, 0) 44 | self.loop = args.loop 45 | self.axis = axis or vector3(0, 0, 0) 46 | self.callback = args.callback 47 | 48 | if self.type == ParticleEffectTypes.Position then 49 | assert(args.position ~= nil, "ParticleEffectTypes.Position specified, but no position given") 50 | self.position = args.position 51 | else 52 | assert(args.entity ~= nil, "ParticleEffectTypes.Entity or Bone specified, but no entity given") 53 | assert(args.offset ~= nil, "ParticleEffectTypes.Entity or Bone specified, but no offset given") 54 | self.offset = args.offset 55 | self.entity = args.entity 56 | if self.type == ParticleEffectTypes.Bone then 57 | assert(args.bone ~= nil, "ParticleEffectTypes.Bone specified, but no bone given") 58 | self.bone = args.bone 59 | end 60 | end 61 | 62 | LoadEffectBank(self.bank, function() self:CreateEffect() end) 63 | 64 | end 65 | 66 | function ParticleEffect:GetEffect() 67 | return self.effect 68 | end 69 | 70 | function ParticleEffect:GetType() 71 | return self.type 72 | end 73 | 74 | function ParticleEffect:GetScale() 75 | return self.scale 76 | end 77 | 78 | function ParticleEffect:GetRotation() 79 | return self.rotation 80 | end 81 | 82 | function ParticleEffect:GetIsLooped() 83 | return self.loop 84 | end 85 | 86 | function ParticleEffect:GetAxis() 87 | return self.axis 88 | end 89 | 90 | function ParticleEffect:GetPosition() 91 | return self.position 92 | end 93 | 94 | function ParticleEffect:GetOffset() 95 | return self.offset 96 | end 97 | 98 | function ParticleEffect:GetEntity() 99 | return self.entity 100 | end 101 | 102 | function ParticleEffect:GetBone() 103 | return self.bone 104 | end 105 | 106 | function ParticleEffect:GetColor() 107 | return self.color 108 | end 109 | 110 | function ParticleEffect:GetHandle() 111 | return self.handle 112 | end 113 | 114 | function ParticleEffect:Exists() 115 | return DoesParticleFxLoopedExist(self.handle) 116 | end 117 | 118 | --[[ 119 | Only works for looped effects 120 | ]] 121 | function ParticleEffect:SetScale(scale) 122 | if self.loop then 123 | self.scale = scale 124 | SetParticleFxLoopedScale(self.handle, scale) 125 | end 126 | end 127 | 128 | --[[ 129 | Only works for looped effects 130 | ]] 131 | function ParticleEffect:SetProperty(propertyName, amount) 132 | if self.loop then 133 | SetParticleFxLoopedEvolution(self.handle, propertyName, amount, 0) 134 | end 135 | end 136 | 137 | --[[ 138 | Only works on some particle effects, and only works on looped effects 139 | ]] 140 | function ParticleEffect:SetColor(color) 141 | if self.loop then 142 | self.color = color 143 | SetParticleFxLoopedColour(self.handle, color.r, color.g, color.b, 0) 144 | end 145 | end 146 | 147 | function ParticleEffect:Remove() 148 | RemoveParticleFx(self.handle, true) 149 | end 150 | 151 | function ParticleEffect:CreateEffect() 152 | 153 | UseParticleFxAssetNextCall(self.bank) 154 | if self.type == ParticleEffectTypes.Position then 155 | if self.loop then 156 | self.handle = StartParticleFxLoopedAtCoord( 157 | self.effect, 158 | self.position.x, self.position.y, self.position.z, 159 | self.rotation.x, self.rotation.y, self.rotation.z, 160 | self.scale, self.axis.x, self.axis.y, self.axis.z, 0 161 | ) 162 | else 163 | self.handle = StartParticleFxNonLoopedAtCoord( 164 | self.effect, 165 | self.position.x, self.position.y, self.position.z, 166 | self.rotation.x, self.rotation.y, self.rotation.z, 167 | self.scale, self.axis.x, self.axis.y, self.axis.z, 0 168 | ) 169 | end 170 | elseif self.type == ParticleEffectTypes.Entity then 171 | if self.loop then 172 | self.handle = StartParticleFxLoopedOnEntity( 173 | self.effect, self.entity, 174 | self.offset.x, self.offset.y, self.offset.z, 175 | self.rotation.x, self.rotation.y, self.rotation.z, 176 | self.scale, self.axis.x, self.axis.y, self.axis.z 177 | ) 178 | else 179 | self.handle = StartParticleFxNonLoopedOnEntity( 180 | self.effect, self.entity, 181 | self.offset.x, self.offset.y, self.offset.z, 182 | self.rotation.x, self.rotation.y, self.rotation.z, 183 | self.scale, self.axis.x, self.axis.y, self.axis.z 184 | ) 185 | end 186 | elseif self.type == ParticleEffectTypes.Bone then 187 | if self.loop then 188 | self.handle = StartParticleFxLoopedOnEntityBone( 189 | self.effect, self.entity, 190 | self.offset.x, self.offset.y, self.offset.z, 191 | self.rotation.x, self.rotation.y, self.rotation.z, self.bone, 192 | self.scale, self.axis.x, self.axis.y, self.axis.z 193 | ) 194 | else 195 | -- IF something breaks, you used loop = false with type = bone on a non ped bone 196 | self.handle = StartParticleFxNonLoopedOnPedBone( 197 | self.effect, self.entity, 198 | self.offset.x, self.offset.y, self.offset.z, 199 | self.rotation.x, self.rotation.y, self.rotation.z, self.bone, 200 | self.scale, self.axis.x, self.axis.y, self.axis.z 201 | ) 202 | end 203 | end 204 | 205 | if self.callback then 206 | self.callback(self) 207 | end 208 | end 209 | 210 | ParticleEffectTypes = {Position = 1, Entity = 2, Bone = 3} -------------------------------------------------------------------------------- /client/pause-menu/cPauseMenu.lua: -------------------------------------------------------------------------------- 1 | PauseMenu = class() 2 | 3 | function PauseMenu:__init() 4 | self.enabled_while_dead = false -- If the pause menu can be opened while the player is dead 5 | end 6 | 7 | function PauseMenu:SetEnabledWhileDead(enabled) 8 | self.enabled_while_dead = enabled 9 | 10 | Citizen.CreateThread(function() 11 | while self.enabled_while_dead do 12 | --N_0xcc3fdded67bcfc63() 13 | Wait(0) 14 | end 15 | end) 16 | end 17 | 18 | function PauseMenu:SetTitle(title) 19 | Citizen.CreateThread(function() 20 | AddTextEntry('FE_THDR_GTAO', title) 21 | end) 22 | end 23 | 24 | function PauseMenu:IsActive() 25 | return IsPauseMenuActive() 26 | end 27 | 28 | PauseMenu = PauseMenu() -------------------------------------------------------------------------------- /client/physics/cPhysics.lua: -------------------------------------------------------------------------------- 1 | Physics = class() 2 | 3 | --[[ 4 | Casts a ray from a point to another point. 5 | 6 | startpos - vector3 7 | endpos - vector3 8 | flags - intersection bit flags of what to hit. -1 presumably hits everything 9 | 1: Intersect with map 10 | 2: Intersect with vehicles (used to be mission entities?) (includes train) 11 | 4: Intersect with peds? (same as 8) 12 | 8: Intersect with peds? (same as 4) 13 | 16: Intersect with objects 14 | 32: Water? 15 | 64: Unknown 16 | 128: Unknown 17 | 256: Intersect with vegetation (plants, coral. trees not included) 18 | NOTE: Raycasts that intersect with mission_entites (flag = 2) has limited range and will not register for far away entites. 19 | The range seems to be about 30 metres. 20 | ignore_entity (optional) - an entity to ignore when raycasting 21 | 22 | returns - table containing: 23 | retval - (int) 24 | hit - (bool) whether it hit anything. False if it reached its destination 25 | position - (vector3) the position hit by the ray. if it does not hit anything, it will be equal to endpos 26 | normal - (vector3) surface normal coords of where the ray hit 27 | entity - (entity) handle of the entity hit by the ray 28 | 29 | ]] 30 | function Physics:Raycast(startpos, endpos, flags, ignore_entity) 31 | TypeCheck:Position3D(startpos) 32 | TypeCheck:Position3D(endpos) 33 | 34 | local result = self:GetShapeTestResult(StartShapeTestRay(startpos.x, startpos.y, startpos.z, endpos.x, endpos.y, endpos.z, flags or -1, ignore_entity or 0, 1)) 35 | result.hit = result.hit == 1 36 | 37 | if not result.hit then 38 | result.position = endpos 39 | end 40 | 41 | return result 42 | end 43 | 44 | --[[ 45 | Casts a ray with radius from a point to another point. Basically a cylinder. 46 | 47 | 48 | startpos - vector3 49 | endpos - vector3 50 | flags - intersection bit flags of what to hit 51 | vehicles=10 52 | peds =12 53 | Iterating through flags yields many ped / vehicle/ object combinations 54 | ignore_entity - an entity to ignore when raycasting 55 | radius - number of the radius of the ray 56 | 57 | returns - table containing: 58 | retval - (int) 59 | hit - (bool) whether it hit anything. False if it reached its destination 60 | position - (vector3) the position hit by the ray 61 | normal - (vector3) surface normal coords of where the ray hit 62 | entity - (entity) handle of the entity hit by the ray 63 | ]] 64 | function Physics:RaycastWithRadius(startpos, endpos, flags, ignore_entity, radius) 65 | TypeCheck:Position(startpos) 66 | TypeCheck:Position(endpos) 67 | TypeCheck:Number(flags) 68 | TypeCheck:Number(radius) 69 | return self:GetShapeTestResult(StartShapeTestCapsule(startpos.x, startpos.y, startpos.z, endpos.x, endpos.y, endpos.z, radius, flags or -1, ignore_entity or 0, 7)) 70 | end 71 | 72 | function Physics:GetShapeTestResult(handle) 73 | local retval, hit, position, normal, entity = GetShapeTestResult(handle) 74 | return {retval = retval, hit = hit, position = position, normal = normal, entity = entity} 75 | end 76 | 77 | Physics = Physics() -------------------------------------------------------------------------------- /client/player/cPlayer.lua: -------------------------------------------------------------------------------- 1 | -- a Player instance represents a single player in the game 2 | Player = class(ValueStorage) 3 | 4 | function Player:__init(source_id, steam_id, server_id, unique_id) 5 | self.source_id = source_id 6 | self.steam_id = steam_id 7 | self.server_id = server_id 8 | self.unique_id = unique_id 9 | self.__is_player_instance = true 10 | 11 | -- Wait until player is valid to get player id 12 | Citizen.CreateThread(function() 13 | while not self.player_id or self.player_id == -1 do 14 | self.player_id = GetPlayerFromServerId(self.source_id) 15 | Wait(500) 16 | end 17 | end) 18 | 19 | self:InitializeValueStorage(self) 20 | self:SetValueStorageNetworkId(self.server_id) 21 | end 22 | 23 | function Player:SetControl(enabled) 24 | SetPlayerControl(self.player_id, enabled, false) 25 | end 26 | 27 | --[[ 28 | Changes the model of the player. 29 | Warning: this function is not a wrapped in a thread 30 | 31 | WARNING: THIS WILL DESTROY THE CURRENT PED AND CREATE A NEW 32 | ONE FOR THIS PLAYER. 33 | ]] 34 | function Player:SetModel(model, cb) 35 | local hashkey = GetHashKey(model) 36 | LoadModel(hashkey, function() 37 | -- change the player model 38 | SetPlayerModel(self.player_id, hashkey) 39 | 40 | -- release the player model 41 | SetModelAsNoLongerNeeded(hashkey) 42 | 43 | -- RDR3 player model bits 44 | if N_0x283978a15512b2fe then 45 | N_0x283978a15512b2fe(PlayerPedId(), true) 46 | end 47 | cb() 48 | end) 49 | end 50 | 51 | --[[ 52 | Updates the Ped class so that the Ped is still valid for this player. 53 | Useful for respawning or changing model. 54 | ]] 55 | function Player:GetPed() 56 | if not self.player_id or self.player_id == -1 then 57 | self.player_id = GetPlayerFromServerId(self.source_id) 58 | end 59 | 60 | local player_ped_id = GetPlayerPed(self.player_id) 61 | 62 | if not self.ped then 63 | self.ped = Ped({ped = player_ped_id}) 64 | end 65 | 66 | if self.ped:GetEntityId() ~= player_ped_id then 67 | self.ped:UpdatePedId(player_ped_id) 68 | end 69 | 70 | if not self.ped:Exists() then 71 | self.ped = Ped({ped = player_ped_id}) 72 | end 73 | 74 | return self.ped 75 | end 76 | 77 | function Player:GetEntity() 78 | return self:GetPed() 79 | end 80 | 81 | function Player:GetEntityId() 82 | return GetPlayerPed(self.player_id) 83 | end 84 | 85 | function Player:GetPedId() 86 | return GetPlayerPed(self.player_id) 87 | end 88 | 89 | --[[ 90 | Freezes the player, including movement controls. 91 | 92 | Same as original scripts. 93 | ]] 94 | function Player:Freeze(freeze) 95 | self:SetControl(not freeze) 96 | 97 | local ped = self:GetPed() 98 | local entity = Entity(ped:GetPedId()) 99 | 100 | if not freeze then 101 | if not entity:IsVisible() then 102 | entity:SetVisible(true) 103 | end 104 | 105 | if not ped:InVehicle() then 106 | entity:ToggleCollision(true) 107 | end 108 | 109 | entity:SetKinematic(false) 110 | entity:SetInvincible(false) 111 | else 112 | if entity:IsVisible() then 113 | entity:SetVisible(false) 114 | end 115 | 116 | entity:ToggleCollision(false) 117 | entity:SetKinematic(true) 118 | entity:SetInvincible(true) 119 | 120 | if not ped:IsFatallyInjured() then 121 | ClearPedTasksImmediately(ped.ped_id) 122 | end 123 | end 124 | end 125 | 126 | function Player:SetWeaponDamageModifier(modifier) 127 | SetPlayerWeaponDamageModifier(self:GetPlayerId(), tofloat(modifier)) 128 | end 129 | 130 | function Player:SetMeleeWeaponDamageModifier(modifier) 131 | SetPlayerMeleeWeaponDamageModifier(self:GetPlayerId(), tofloat(modifier)) 132 | end 133 | 134 | function Player:SetRunSprintMultiplier(multiplier) 135 | SetRunSprintMultiplierForPlayer(self:GetPlayerId(), tofloat(multiplier)) 136 | end 137 | 138 | function Player:ResetStamina() 139 | ResetPlayerStamina(self.player_id) 140 | end 141 | 142 | function Player:SetMinFallDistance(distance) 143 | SetPlayerFallDistance(self.player_id, tofloat(distance)) 144 | end 145 | 146 | function Player:DisableFiring(disabled) 147 | self.firing_disabled = disabled 148 | 149 | Citizen.CreateThread(function() 150 | while self.firing_disabled do 151 | DisablePlayerFiring(self.player_id, true) 152 | Wait(1) 153 | end 154 | end) 155 | 156 | end 157 | 158 | function Player:GetPosition() 159 | return self:GetPed():GetPosition() 160 | end 161 | 162 | function Player:SetName(name) 163 | self.name = name 164 | end 165 | 166 | function Player:GetName() 167 | return self.name 168 | end 169 | 170 | function Player:GetId() 171 | return self.server_id 172 | end 173 | 174 | function Player:GetPlayerId() 175 | return self.player_id 176 | end 177 | 178 | function Player:GetSteamId() 179 | return self.steam_id 180 | end 181 | 182 | function Player:GetUniqueId() 183 | return self.unique_id 184 | end 185 | 186 | function Player:Disconnected() 187 | 188 | end 189 | 190 | 191 | 192 | function Player:tostring() 193 | return "Player (" .. self.name .. ")" 194 | end -------------------------------------------------------------------------------- /client/player/cPlayerManager.lua: -------------------------------------------------------------------------------- 1 | PlayerManager = class() 2 | 3 | function PlayerManager:__init() 4 | self:SubscribeToEvents() 5 | self:ListenForNetworkEvents() 6 | end 7 | 8 | function PlayerManager:SubscribeToEvents() 9 | Events:Subscribe("LocalPlayerSpawn", function() 10 | Network:Send("__PlayerSpawned") 11 | end) 12 | end 13 | 14 | function PlayerManager:ListenForNetworkEvents() 15 | Network:Subscribe("PlayerRemoved", self, self.PlayerRemoved) 16 | end 17 | 18 | function PlayerManager:PlayerRemoved(args) 19 | local player = cPlayers:GetByUniqueId(args.player_unique_id) 20 | assert(cPlayers:GetByUniqueId(args.player_unique_id) ~= nil, "PlayerManager:PlayerRemoved tried to remove player that wasn't being stored") 21 | 22 | if player then 23 | Events:Fire("PlayerQuit", {player = player}) 24 | 25 | player:Disconnected() 26 | 27 | cPlayers:RemovePlayer(args.player_unique_id) 28 | end 29 | end 30 | 31 | PlayerManager = PlayerManager() -------------------------------------------------------------------------------- /client/player/cPlayers.lua: -------------------------------------------------------------------------------- 1 | cPlayers = class() 2 | 3 | function cPlayers:__init() 4 | self.players_by_unique_id = {} 5 | self.received_data = false 6 | 7 | Network:Subscribe("__SyncConnectedPlayers", function(data) 8 | self:SyncConnectedPlayers(data, not self.received_data) 9 | self.received_data = true 10 | end) 11 | 12 | Network:Subscribe("__SyncConnectedPlayer", function(sync_data) 13 | if not self.players_by_unique_id[player_unique_id] then 14 | self:AddPlayer(sync_data) 15 | 16 | local player = self:GetByUniqueId(sync_data.unique_id) 17 | Events:Fire("PlayerJoined", {player = player}) 18 | else 19 | self:AddPlayer(sync_data) 20 | end 21 | end) 22 | 23 | Network:Send("__RequestApiPlayerData") 24 | 25 | while not self.received_data do 26 | Wait(10) 27 | end 28 | end 29 | 30 | function cPlayers:__postLoad() 31 | 32 | end 33 | 34 | -- getting all the players from the server 35 | function cPlayers:SyncConnectedPlayers(data, on_init) 36 | -- print("--- syncing Players ---") 37 | 38 | for player_unique_id, sync_data in pairs(data) do 39 | if not self.players_by_unique_id[player_unique_id] then 40 | self:AddPlayer(sync_data) 41 | 42 | local player = self:GetByUniqueId(sync_data.unique_id) 43 | if not on_init then 44 | Events:Fire("PlayerJoined", {player = player}) 45 | end 46 | 47 | -- print("Player Added: ", player) 48 | else 49 | self:AddPlayer(sync_data) 50 | end 51 | end 52 | 53 | -- print("------------------------------------") 54 | end 55 | 56 | function cPlayers:GetLocalPlayer() 57 | for player_unique_id, player in pairs(self.players_by_unique_id) do 58 | if LocalPlayer:IsPlayer(player) then 59 | return player 60 | end 61 | end 62 | end 63 | 64 | function cPlayers:GetNearestPlayer(position) 65 | local closest_player 66 | local closest_distance = 99999 67 | 68 | for player_unique_id, player in pairs(self.players_by_unique_id) do 69 | local distance = #(position - player:GetPosition()) 70 | 71 | if distance < closest_distance then 72 | closest_distance = distance 73 | closest_player = player 74 | end 75 | end 76 | 77 | return closest_player, closest_distance 78 | end 79 | 80 | function cPlayers:AddPlayer(sync_data) 81 | local player = Player(sync_data.source_id, sync_data.steam_id, sync_data.source_id, sync_data.unique_id) 82 | player:SetName(sync_data.name) 83 | 84 | for name, value in pairs(sync_data.network_values) do 85 | player:SetValue(name, value) 86 | end 87 | 88 | self.players_by_unique_id[player:GetUniqueId()] = player 89 | end 90 | 91 | function cPlayers:RemovePlayer(player_unique_id) 92 | self.players_by_unique_id[player_unique_id] = nil 93 | end 94 | 95 | function cPlayers:GetByUniqueId(player_unique_id) 96 | return self.players_by_unique_id[player_unique_id] 97 | end 98 | 99 | function cPlayers:GetByPlayerId(id) 100 | for player_unique_id, player in pairs(self.players_by_unique_id) do 101 | if player:GetPlayerId() == id then 102 | return player 103 | end 104 | end 105 | end 106 | 107 | function cPlayers:GetByServerId(server_id) 108 | for player_unique_id, player in pairs(self.players_by_unique_id) do 109 | if player:GetId() == server_id then 110 | return player 111 | end 112 | end 113 | end 114 | 115 | function cPlayers:GetNumPlayers() 116 | return count_table(self.players_by_unique_id) 117 | end 118 | 119 | function cPlayers:GetPlayers() 120 | -- todo: write new iterator 121 | return self.players_by_unique_id 122 | end 123 | 124 | 125 | cPlayers = cPlayers() -------------------------------------------------------------------------------- /client/prompt/cPrompt.lua: -------------------------------------------------------------------------------- 1 | if IsRedM then 2 | 3 | Prompt = class() 4 | 5 | --[[ 6 | Creates a new UI prompt. 7 | 8 | args (in table): 9 | text (string): text to display 10 | position (vector3) (optional): position to trigger the prompt at 11 | size (number): size of prompt trigger zone 12 | control (Control enum): control to trigger this prompt 13 | 14 | ]] 15 | function Prompt:__init(args) 16 | self.str = CreateVarString(10, "LITERAL_STRING", args.text) 17 | self.position = args.position 18 | self.size = tofloat(args.size) 19 | self.control = args.control 20 | 21 | self:Create() 22 | self:SetEnabled(true) 23 | self:SetVisible(true) 24 | self:SetContextPoint(self.position) 25 | self:SetContextSize(self.size) 26 | 27 | Events:Subscribe("onResourceStop", self, self.OnResourceStop) 28 | end 29 | 30 | function Prompt:Create() 31 | self.prompt = Citizen.InvokeNative(0x29FA7910726C3889, self.control, self.str, 6, 1, 1, -1, Citizen.ResultAsInteger()) 32 | end 33 | 34 | function Prompt:SetEnabled(enabled) 35 | Citizen.InvokeNative(0x8A0FB4D03A630D21, self.prompt, enabled) 36 | end 37 | 38 | function Prompt:SetVisible(visible) 39 | Citizen.InvokeNative(0x71215ACCFDE075EE, self.prompt, visible) 40 | end 41 | 42 | function Prompt:SetHoldMode(seconds_to_hold) 43 | Citizen.InvokeNative(0x74C7D7B72ED0D3CF, self.prompt, seconds_to_hold) 44 | end 45 | 46 | function Prompt:HasHoldModeCompleted() 47 | return Citizen.InvokeNative(0xE0F65F0640EF0617, self.prompt) 48 | end 49 | 50 | function Prompt:SetHoldIndefinitelyMode() 51 | Citizen.InvokeNative(0xEA5CCF4EEB2F82D1, self.prompt) 52 | end 53 | 54 | function Prompt:SetContextPoint(pos) 55 | self.position = pos 56 | Citizen.InvokeNative(0xAE84C5EE2C384FB3, self.prompt, self.position.x, self.position.y, self.position.z) 57 | end 58 | 59 | function Prompt:IsHoldModeRunning() 60 | return Citizen.InvokeNative(0xC7D70EAEF92EFF48, self.prompt) 61 | end 62 | 63 | function Prompt:IsPressed() 64 | return Citizen.InvokeNative(0x21E60E230086697F, self.prompt) 65 | end 66 | 67 | function Prompt:SetContextSize(size) 68 | self.size = size 69 | Citizen.InvokeNative(0x0C718001B77CA468, self.prompt, size) 70 | end 71 | 72 | function Prompt:OnResourceStop(name) 73 | if name == GetCurrentResourceName() then 74 | self:Remove() 75 | end 76 | end 77 | 78 | function Prompt:Remove() 79 | Citizen.InvokeNative(0x00EDE88D4D13CF59, self.prompt) 80 | end 81 | 82 | end -------------------------------------------------------------------------------- /client/render/cTexture.lua: -------------------------------------------------------------------------------- 1 | if not IsFiveM then return end 2 | 3 | Texture = class() 4 | 5 | TextureTypes = {Dui = 1, Image = 2} 6 | local texture_id = 0 7 | local TEXTURE_DICT = CreateRuntimeTxd("Texture_OOF_Dict") 8 | 9 | local function GetTextureName() 10 | texture_id = texture_id + 1 11 | return "Texture_" .. tostring(texture_id) 12 | end 13 | 14 | --[[ 15 | Creates a new texture that can be used with Render:DrawTexture 16 | 17 | args (in table): 18 | width (int): width of the texture 19 | height (int): height of the texture 20 | type (TextureTypes): type of the source of the texture 21 | source (string): path to the texture. Can be png, jpg, html, etc 22 | position (vector2): position of texture on screen 23 | rotation (number) (optional): rotation of the texture 24 | ]] 25 | function Texture:__init(args) 26 | 27 | self.height = args.height 28 | self.width = args.width 29 | self.size = vector2(self.width, self.height) 30 | self.type = args.type 31 | self.source = args.source 32 | self.position = args.position or vector2(0, 0) 33 | 34 | Citizen.CreateThread(function() 35 | self:Create() 36 | end) 37 | end 38 | 39 | function Texture:Create() 40 | print("Creating texture...") 41 | Citizen.Wait(1000) 42 | self.txn = GetTextureName() 43 | Citizen.Wait(1000) 44 | print("Texture name: " .. self.txn) 45 | Citizen.Wait(1000) 46 | 47 | if self.type == TextureTypes.Dui then 48 | print("Creating dui object with source " .. tostring(self.source) .. " width " .. tostring(self.width) .. " height " .. tostring(self.height)) 49 | Citizen.Wait(1000) 50 | self.dui_obj = Citizen.InvokeNative(0x23EAF899, self.source, self.width, self.height, Citizen.ResultAsLong()) 51 | Citizen.Wait(1000) 52 | print("Getting dui handle") 53 | Citizen.Wait(1000) 54 | self.dui = GetDuiHandle(self.dui_obj) 55 | Citizen.Wait(1000) 56 | print("CreateRuntimeTextureFromDuiHandle") 57 | Citizen.Wait(1000) 58 | self.tx = CreateRuntimeTextureFromDuiHandle(TEXTURE_DICT, self.txn, self.dui) 59 | Citizen.Wait(1000) 60 | 61 | elseif self.type == TextureTypes.Image then 62 | self.tx = CreateRuntimeTextureFromImage(TEXTURE_DICT, self.txn, self.source) 63 | end 64 | 65 | print("Done") 66 | end 67 | 68 | function Texture:SetPosition(pos) 69 | self.position = pos 70 | end 71 | 72 | function Texture:SetRotation(rot) 73 | self.rotation = rot 74 | end 75 | 76 | function Texture:Draw() 77 | Render:DrawSprite(self.position, self.size, self.rotation, Colors.White) 78 | end 79 | 80 | function Texture:Destroy() 81 | if self.type == TextureTypes.Dui then DestroyDui(self.dui_obj) end 82 | end -------------------------------------------------------------------------------- /client/screen-effects/cScreenEffects.lua: -------------------------------------------------------------------------------- 1 | if IsFiveM then 2 | ScreenEffects = class() 3 | 4 | --[[ 5 | toggle blurred screen 6 | ]] 7 | function ScreenEffects:Blur(enabled) 8 | assert(type(enabled) == "bool", "enabled expected to be a bool") 9 | if enabled then 10 | TransitionToBlurred(1) 11 | else 12 | TransitionFromBlurred(1) 13 | end 14 | end 15 | 16 | --[[ 17 | Starts a screen effect. 18 | Name is from ScreenEffect list, time is in ms, and loop is bool 19 | ]] 20 | function ScreenEffects:Start(name, time, loop) 21 | assert(type(name) == "string", "name expected to be a string") 22 | assert(type(time) == "number", "time expected to be a number") 23 | assert(type(loop) == "bool", "loop expected to be a bool") 24 | StartScreenEffect(name, time, loop) 25 | end 26 | 27 | --[[ 28 | Stops a screen effect with specified name 29 | ]] 30 | function ScreenEffects:Stop(name) 31 | assert(type(name) == "string", "name expected to be a string") 32 | StopScreenEffect(name) 33 | end 34 | 35 | --[[ 36 | Stops all screen effects. 37 | ]] 38 | function ScreenEffects:StopAll() 39 | StopAllScreenEffects() 40 | end 41 | 42 | ScreenEffects = ScreenEffects() 43 | 44 | ScreenEffect = { 45 | "SwitchHUDIn", 46 | "SwitchHUDOut", 47 | "FocusIn", 48 | "FocusOut", 49 | "MinigameEndNeutral", 50 | "MinigameEndTrevor", 51 | "MinigameEndFranklin", 52 | "MinigameEndMichael", 53 | "MinigameTransitionOut", 54 | "MinigameTransitionIn", 55 | "SwitchShortNeutralIn", 56 | "SwitchShortFranklinIn", 57 | "SwitchShortTrevorIn", 58 | "SwitchShortMichaelIn", 59 | "SwitchOpenMichaelIn", 60 | "SwitchOpenFranklinIn", 61 | "SwitchOpenTrevorIn", 62 | "SwitchHUDMichaelOut", 63 | "SwitchHUDFranklinOut", 64 | "SwitchHUDTrevorOut", 65 | "SwitchShortFranklinMid", 66 | "SwitchShortMichaelMid", 67 | "SwitchShortTrevorMid", 68 | "DeathFailOut", 69 | "CamPushInNeutral", 70 | "CamPushInFranklin", 71 | "CamPushInMichael", 72 | "CamPushInTrevor", 73 | "SwitchOpenMichaelIn", 74 | "SwitchSceneFranklin", 75 | "SwitchSceneTrevor", 76 | "SwitchSceneMichael", 77 | "SwitchSceneNeutral", 78 | "MP_Celeb_Win", 79 | "MP_Celeb_Win_Out", 80 | "MP_Celeb_Lose", 81 | "MP_Celeb_Lose_Out", 82 | "DeathFailNeutralIn", 83 | "DeathFailMPDark", 84 | "DeathFailMPIn", 85 | "MP_Celeb_Preload_Fade", 86 | "PeyoteEndOut", 87 | "PeyoteEndIn", 88 | "PeyoteIn", 89 | "PeyoteOut", 90 | "MP_race_crash", 91 | "SuccessFranklin", 92 | "SuccessTrevor", 93 | "SuccessMichael", 94 | "DrugsMichaelAliensFightIn", 95 | "DrugsMichaelAliensFight", 96 | "DrugsMichaelAliensFightOut", 97 | "DrugsTrevorClownsFightIn", 98 | "DrugsTrevorClownsFight", 99 | "DrugsTrevorClownsFightOut", 100 | "HeistCelebPass", 101 | "HeistCelebPassBW", 102 | "HeistCelebEnd", 103 | "HeistCelebToast", 104 | "MenuMGHeistIn", 105 | "MenuMGTournamentIn", 106 | "MenuMGSelectionIn", 107 | "ChopVision", 108 | "DMT_flight_intro", 109 | "DMT_flight", 110 | "DrugsDrivingIn", 111 | "DrugsDrivingOut", 112 | "SwitchOpenNeutralFIB5", 113 | "HeistLocate", 114 | "MP_job_load", 115 | "RaceTurbo", 116 | "MP_intro_logo", 117 | "HeistTripSkipFade", 118 | "MenuMGHeistOut", 119 | "MP_corona_switch", 120 | "MenuMGSelectionTint", 121 | "SuccessNeutral", 122 | "ExplosionJosh3", 123 | "SniperOverlay", 124 | "RampageOut", 125 | "Rampage", 126 | "Dont_tazeme_bro", 127 | "DeathFailOut" 128 | } 129 | end -------------------------------------------------------------------------------- /client/sound/cSound.lua: -------------------------------------------------------------------------------- 1 | Sound = class() 2 | 3 | --[[ 4 | Creates a new sound instance so you can play sounds. 5 | 6 | args: 7 | 8 | position - (optional) vector3 where you want it to be played from 9 | entity - (optional) entity you want it to be played from 10 | audioName - the name of the audio file from the audioRef 11 | audioRef - the audio bank with the file you want to play 12 | autoplay - (optional) bool of whether you want the sound to play automatically (good for one-shots) 13 | ]] 14 | function Sound:__init(args) 15 | assert(type(args.audioName) == "string", "args.audioName expected to be a string") 16 | assert(type(args.audioRef) == "string", "args.audioRef expected to be a string") 17 | assert(args.position == nil and args.entity == nil, "args.position or args.entity expected, none given") 18 | 19 | self.id = GetSoundId() 20 | self.position = args.position or vector3(0,0,0) 21 | 22 | self.entity = args.entity 23 | 24 | self.audioName = args.audioName 25 | self.audioRef = args.audioRef 26 | 27 | if args.autoplay then 28 | self:Play() 29 | end 30 | end 31 | 32 | function Sound:SetPosition(pos) 33 | assert(type(pos) == "vector3", "position expected to be a vector3") 34 | self.position = pos 35 | end 36 | 37 | function Sound:GetPosition() 38 | return self.position 39 | end 40 | 41 | function Sound:PlayFromCoord(pos) 42 | PlaySoundFromCoord(self.id, self.audioName, pos.x, pos.y, pos.z + 0.5 , self.audioRef, 1, 100, 0) 43 | end 44 | 45 | function Sound:PlayFromEntity(entity) 46 | PlaySoundFromEntity(self.id, self.audioName, entity, self.audioRef, 1, 1) 47 | end 48 | 49 | function Sound:PlayFrontend() 50 | PlaySoundFrontend(self.id, self.audioName, self.audioRef, 1) 51 | end 52 | 53 | --[[ 54 | Plays the sound. 55 | ]] 56 | function Sound:Play() 57 | if self.entity then 58 | self:PlayFromEntity(self.entity) 59 | elseif self.position then 60 | self:PlayFromCoord(self.position) 61 | else 62 | self:PlayFrontend() 63 | end 64 | end 65 | 66 | function Sound:SetVariable(var, val) 67 | SetVariableOnSound(self.id, var, val) 68 | end 69 | 70 | function Sound:IsFinishedPlaying() 71 | return HasSoundFinished(self.id) 72 | end 73 | 74 | function Sound:Stop() 75 | StopSound(self.id) 76 | end 77 | 78 | function Sound:Remove() 79 | self:Stop() 80 | ReleaseSoundId(self.id) 81 | end -------------------------------------------------------------------------------- /client/typecheck/cTypeCheck.lua: -------------------------------------------------------------------------------- 1 | TypeCheck = class() 2 | 3 | 4 | function TypeCheck:Number(n) 5 | assert(type(n) == "number", "arg expected to be a number") 6 | end 7 | 8 | function TypeCheck:Position(pos) 9 | assert(type(pos) == "vector2", "position expected to be a vector2") 10 | end 11 | 12 | function TypeCheck:Position3D(pos) 13 | assert(type(pos) == "vector3", "position3d expected to be a vector3") 14 | end 15 | 16 | function TypeCheck:Text(text) 17 | assert(type(text) == "string", "text expected to be a string") 18 | return text 19 | end 20 | 21 | function TypeCheck:Color(color) 22 | assert(is_class_instance(color, Color), "color expected to be a Color") 23 | assert(color.r ~= nil and color.g ~= nil and color.b ~= nil and color.a ~= nil, "color missing a component") 24 | end 25 | 26 | function TypeCheck:Font(font) 27 | font = math.round(font) 28 | assert(type(font) == "number" and font >= 0 and font < 5, "font expected to be a number 0-4") 29 | end 30 | 31 | function TypeCheck:Scale(scale) 32 | assert(type(scale) == "table", "scale expected to be a table") 33 | assert(scale.x ~= nil and scale.y ~= nil, "scale missing a component") 34 | end 35 | 36 | function TypeCheck:Size(size) 37 | assert(type(size) == "table", "size expected to be a table") 38 | assert(size.x ~= nil and size.y ~= nil, "size missing a component") 39 | end 40 | 41 | TypeCheck = TypeCheck() -------------------------------------------------------------------------------- /client/ui/events.js: -------------------------------------------------------------------------------- 1 | const res_name = GetParentResourceName() 2 | 3 | $(document).ready(function() 4 | { 5 | // Register frames here to call NUI events on them 6 | const frames = {} 7 | let z = 10 8 | 9 | // Listen for NUI events 10 | window.addEventListener('message', function(event) 11 | { 12 | const iframe = $(`#FRAME_${event.data.name}`); 13 | const event_name = event.data.nui_event; 14 | delete event.data.nui_event; 15 | 16 | if (event_name == "ui/hide" && iframe != null) 17 | { 18 | iframe.hide() 19 | EventCalled('Hide', null, event.data.name); 20 | } 21 | else if (event_name == "ui/show") 22 | { 23 | iframe.show() 24 | EventCalled('Show', null, event.data.name); 25 | } 26 | else if (event_name == "ui/event") 27 | { 28 | const nui_event = event.data.data.nui_event; 29 | delete event.data.data.nui_event; 30 | EventCalled(nui_event, event.data.data, event.data.name); 31 | } 32 | else if (event_name == "ui/create") 33 | { 34 | const iframe_new = $(``) 35 | 36 | Object.keys(event.data.css).forEach((key, index) => 37 | { 38 | iframe_new.css(key, event.data.css[key]); 39 | }) 40 | 41 | $('body').append(iframe_new) 42 | frames[event.data.name] = iframe_new 43 | z++; 44 | 45 | if (!event.data.visible) 46 | { 47 | iframe_new.hide() 48 | } 49 | 50 | iframe_new[0].contentWindow.onkeyup = (e) => {OnKey(e, 'keyup')} 51 | iframe_new[0].contentWindow.onkeydown = (e) => {OnKey(e, 'keydown')} 52 | iframe_new[0].contentWindow.onkeypress = (e) => {OnKey(e, 'keypress')} 53 | iframe_new[0].contentWindow.OOF = new OOF(event.data.name); 54 | } 55 | else if (event_name == "ui/remove" && frames[event.data.name] != null) 56 | { 57 | frames[event.data.name].remove() 58 | delete frames[event.data.name] 59 | } 60 | else if (event_name == "ui/bring_to_front" && frames[event.data.name] != null) 61 | { 62 | frames[event.data.name].css('z-index', z++) 63 | } 64 | else if (event_name == "ui/send_to_back" && frames[event.data.name] != null) 65 | { 66 | frames[event.data.name].css('z-index', 0) 67 | } 68 | else if (event_name == "ui/lua_ready") 69 | { 70 | $.post(`http://${res_name}/ui/ready`, 71 | JSON.stringify({size: {x: $(window).width(), y: $(window).height()}})); 72 | } 73 | }) 74 | 75 | function EventCalled(event_name, data, ui_name) 76 | { 77 | if (events[event_name] != null) 78 | { 79 | events[event_name].forEach((entry) => 80 | { 81 | if (entry.callback != null && (ui_name == null || ui_name == entry.name)) 82 | { 83 | entry.callback(data); 84 | } 85 | }) 86 | } 87 | } 88 | 89 | const events = {}; 90 | 91 | // Additional OOF functions 92 | class OOF 93 | { 94 | constructor (name) 95 | { 96 | this.name = name 97 | } 98 | 99 | GetFocus(elem) 100 | { 101 | elem.focus(); 102 | } 103 | 104 | CallEvent(event_name, args) 105 | { 106 | if (event_name == null || event_name.length == 0) 107 | { 108 | this.console.error(`Cannot CallEvent without a valid event_name`) 109 | return; 110 | } 111 | args = args || {} 112 | args = JSON.stringify(args) 113 | 114 | $.post(`http://${res_name}/${this.name}_${event_name}`, args); 115 | } 116 | 117 | Subscribe(event_name, callback) 118 | { 119 | if (events[event_name] == null) 120 | { 121 | events[event_name] = [] 122 | } 123 | events[event_name].push({callback: callback, name: this.name}); 124 | } 125 | } 126 | 127 | const key_types = 128 | { 129 | 'keyup': 'KeyUp', 130 | 'keydown': 'KeyDown', 131 | 'keypress': 'KeyPress', 132 | } 133 | function OnKey(e, type) 134 | { 135 | const keycode = (typeof e.which === 'number') ? e.which : e.keyCode; 136 | 137 | $.post(`http://${res_name}/ui/keypress`, JSON.stringify({key: keycode, type: type})); 138 | 139 | const returns = [] 140 | 141 | const event_name = key_types[type]; 142 | if (events[event_name] != null) 143 | { 144 | events[event_name].forEach((entry) => 145 | { 146 | returns.push(entry.callback({key: keycode, event: e})); 147 | }) 148 | } 149 | 150 | returns.forEach((return_val) => 151 | { 152 | if (return_val != undefined) 153 | { 154 | return return_val; 155 | } 156 | }) 157 | 158 | } 159 | 160 | document.onkeyup = (e) => {OnKey(e, 'keyup');} 161 | document.onkeydown = (e) => {OnKey(e, 'keydown');} 162 | document.onkeypress = (e) => {OnKey(e, 'keypress');} 163 | 164 | }) 165 | -------------------------------------------------------------------------------- /client/ui/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | -------------------------------------------------------------------------------- /client/ui/reset.css: -------------------------------------------------------------------------------- 1 | /* http://meyerweb.com/eric/tools/css/reset/ 2 | v2.0 | 20110126 3 | License: none (public domain) 4 | */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td, 15 | article, aside, canvas, details, embed, 16 | figure, figcaption, footer, header, hgroup, 17 | menu, nav, output, ruby, section, summary, 18 | time, mark, audio, video { 19 | margin: 0; 20 | padding: 0; 21 | border: 0; 22 | font-size: 100%; 23 | font: inherit; 24 | vertical-align: baseline; 25 | } 26 | /* HTML5 display-role reset for older browsers */ 27 | article, aside, details, figcaption, figure, 28 | footer, header, hgroup, menu, nav, section { 29 | display: block; 30 | } 31 | body { 32 | line-height: 1; 33 | } 34 | ol, ul { 35 | list-style: none; 36 | } 37 | blockquote, q { 38 | quotes: none; 39 | } 40 | blockquote:before, blockquote:after, 41 | q:before, q:after { 42 | content: ''; 43 | content: none; 44 | } 45 | table { 46 | border-collapse: collapse; 47 | border-spacing: 0; 48 | } -------------------------------------------------------------------------------- /client/ui/ui.lua: -------------------------------------------------------------------------------- 1 | UIInstance = class() 2 | 3 | function UIInstance:__init(args) 4 | self.name = args.name 5 | self.path = args.path 6 | self.css = args.css 7 | self.visible = args.visible or true 8 | end 9 | 10 | --[[ 11 | Subscribe to UI events! 12 | ]] 13 | function UIInstance:Subscribe(event_name, instance, callback) 14 | RegisterNUICallback(string.format("%s_%s", self.name, event_name), function(args) 15 | -- Use a thread to get proper callstack on future errors 16 | Citizen.CreateThread(function() 17 | if not callback then 18 | instance(args) 19 | else 20 | callback(instance, args) 21 | end 22 | end) 23 | end) 24 | end 25 | 26 | function UIInstance:BringToFront() 27 | SendNUIMessage({nui_event = "ui/bring_to_front", name = self.name}) 28 | end 29 | 30 | function UIInstance:SendToBack() 31 | SendNUIMessage({nui_event = "ui/send_to_back", name = self.name}) 32 | end 33 | 34 | --[[ 35 | Calls an event on a specific UI instance. 36 | 37 | data - (table) table of data you want to send with the event 38 | ]] 39 | function UIInstance:CallEvent(event_name, data) 40 | assert(type(event_name) == "string", "event_name expected to be a string") 41 | if not data then data = {} end 42 | data.nui_event = event_name 43 | -- data = EnsureStringKeys(data) 44 | SendNUIMessage({nui_event = "ui/event", name = self.name, data = data}) 45 | end 46 | 47 | -- Loops through all tables in a table and converts keys to strings if they are not already 48 | function EnsureStringKeys(input) 49 | if type(input) == "table" then 50 | local input_string_keys = {} 51 | for key, value in pairs(input) do 52 | input_string_keys[tostring(key)] = EnsureStringKeys(value) 53 | end 54 | return input_string_keys 55 | end 56 | return input 57 | end 58 | 59 | --[[ 60 | If the UI is visible or not 61 | ]] 62 | function UIInstance:GetVisible() 63 | return self.visible 64 | end 65 | 66 | --[[ 67 | Hides the UI instance. 68 | ]] 69 | function UIInstance:Hide() 70 | SendNUIMessage({nui_event = "ui/hide", name = self.name}) 71 | self.visible = false 72 | end 73 | 74 | --[[ 75 | Shows the UI instance. 76 | ]] 77 | function UIInstance:Show() 78 | SendNUIMessage({nui_event = "ui/show", name = self.name}) 79 | self.visible = true 80 | end 81 | 82 | --[[ 83 | Removes a UI instance 84 | ]] 85 | function UIInstance:Remove() 86 | UI:Remove(self.name) 87 | end 88 | 89 | -- -------------------------------------------------- 90 | 91 | UI = class() 92 | 93 | function UI:__init() 94 | self.uis = {} 95 | 96 | -- ref count nui focus/cursor 97 | self.ref = {focus = 0, cursor = 0} 98 | self.size = {x = 0, y = 0} 99 | 100 | self.ui_ready = false 101 | RegisterNUICallback("ui/ready", function(args) 102 | self.ui_ready = true 103 | self.size.x = args.size.x 104 | self.size.y = args.size.y 105 | end) 106 | 107 | --[[ In order to use this event, please make sure to have these two lines in your code: 108 | UI:KeepInput(true) 109 | UI:SetFocus(true) 110 | 111 | This will ensure that the UI picks up keypresses even when not visible 112 | --]] 113 | RegisterNUICallback("ui/keypress", function(args) 114 | Events:Fire("UIKeyPress", args) 115 | end) 116 | 117 | self:SetNuiFocus() 118 | 119 | SendNUIMessage({nui_event = "ui/lua_ready"}) 120 | end 121 | 122 | function UI:KeepInput(keep_input) 123 | SetNuiFocusKeepInput(keep_input) 124 | end 125 | 126 | function UI:GetSize() 127 | return self.size 128 | end 129 | 130 | function UI:SetCursor(enabled) 131 | self.ref.cursor = math.max(0, enabled and self.ref.cursor + 1 or self.ref.cursor - 1) 132 | self:SetNuiFocus() 133 | end 134 | 135 | function UI:SetFocus(enabled) 136 | self.ref.focus = math.max(0, enabled and self.ref.focus + 1 or self.ref.focus - 1) 137 | self:SetNuiFocus() 138 | end 139 | 140 | function UI:GetFocus() 141 | return self.ref.focus 142 | end 143 | 144 | function UI:GetCursor() 145 | return self.ref.cursor 146 | end 147 | 148 | function UI:SetNuiFocus() 149 | SetNuiFocus(self.ref.focus > 0, self.ref.cursor > 0) 150 | end 151 | 152 | --[[ 153 | Creates a new UIInstance. 154 | 155 | args: (in table) 156 | 157 | name - (string) name of ui instance. Must be unique. 158 | path - (string) path to html page. Must be relative to resource, eg: lobby/client/html/index.html 159 | ]] 160 | function UI:Create(args) 161 | assert(type(args.name) == "string", "name must be a string") 162 | assert(type(args.path) == "string", "path must be a string") 163 | 164 | local ui = UIInstance(args) 165 | 166 | Citizen.CreateThread(function() 167 | while not self.ui_ready do 168 | Wait(100) 169 | end 170 | SendNUIMessage({ 171 | nui_event = "ui/create", 172 | name = args.name, 173 | path = args.path, 174 | css = args.css or {}, 175 | visible = not (args.visible == false) 176 | }) 177 | end) 178 | 179 | self.uis[args.name] = ui 180 | return ui 181 | end 182 | 183 | --[[ 184 | Calls an event on a single or all UI instances. 185 | 186 | args: (in table) 187 | data - (table) the table of data you want to send 188 | event_name - (string) event name 189 | name (optional) - name of the UI instance you want to send to, nil for all 190 | ]] 191 | function UI:CallEvent(args) 192 | if args.name and self.uis[args.name] then 193 | self.uis[args.name]:CallEvent(args.event_name, args.data) 194 | else 195 | for name, ui in pairs(self.uis) do 196 | ui:CallEvent(args.event_name, args.data) 197 | end 198 | end 199 | end 200 | 201 | --[[ 202 | Removes a UI instance by name 203 | ]] 204 | function UI:Remove(name) 205 | if self.uis[name] then 206 | SendNUIMessage({nui_event = "ui/remove", name = name}) 207 | self.uis[name] = nil 208 | end 209 | end 210 | 211 | UI = UI() 212 | -------------------------------------------------------------------------------- /client/volume/cVolume.lua: -------------------------------------------------------------------------------- 1 | if IsRedM then 2 | 3 | Volume = class() 4 | 5 | VolumeType = 6 | { 7 | Sphere = 1, 8 | Box = 2, 9 | Cylinder = 3 10 | } 11 | 12 | --[[ 13 | Creates a new Volume, aka an area within the game. Use Entity:IsInVolume(Volume) to detect 14 | if an entity is within the volume. 15 | 16 | args (in table): 17 | position (vector3): position of the volume 18 | rotation (vector3): rotation of the volume 19 | size (vector3): size of the volume 20 | type (VolumeType): type of the volume 21 | ]] 22 | function Volume:__init(args) 23 | 24 | local volume_func_hash 25 | 26 | if args.type == VolumeType.Sphere then 27 | volume_func_hash = 0xB3FB80A32BAE3065 28 | elseif args.type == VolumeType.Box then 29 | volume_func_hash = 0xDF85637F22706891 30 | elseif args.type == VolumeType.Cylinder then 31 | volume_func_hash = 0x0522D4774B82E3E6 32 | end 33 | 34 | self.volume = Citizen.InvokeNative(volume_func_hash, 35 | args.position.x, args.position.y, args.position.z, 36 | args.rotation.x, args.rotation.y, args.rotation.z, 37 | args.size.x, args.size.y, args.size.z 38 | ) 39 | 40 | end 41 | 42 | function Volume:GetHandle() 43 | return self.volume 44 | end 45 | 46 | function Volume:Remove() 47 | Citizen.InvokeNative(0x43F867EF5C463A53, self.volume) 48 | end 49 | 50 | end -------------------------------------------------------------------------------- /client/water/cWater.lua: -------------------------------------------------------------------------------- 1 | if IsFiveM then 2 | 3 | Water = class() 4 | 5 | function Water:__init() 6 | 7 | end 8 | 9 | --[[ 10 | This seems to edit the water wave, intensity around your current location. 11 | 0.0f = Normal 12 | 1.0f = So Calm and Smooth, a boat will stay still. 13 | 3.0f = Really Intense. 14 | 15 | You can also set the value as high as you want for some crazy looking water. 16 | ]] 17 | function Water:SetWaveIntensity(intensity) 18 | WaterOverrideSetStrength(tofloat(intensity)) 19 | end 20 | 21 | function Water:SetOceanWaveAmplitude(amplitude) 22 | WaterOverrideSetOceanwaveamplitude(amplitude) 23 | end 24 | 25 | --[[ 26 | This function set height to the value of z-axis of the water surface. 27 | This function works with sea and lake. However it does not work with shallow rivers (e.g. raton canyon will return -100000.0f) 28 | note: seems to return true when you are in water 29 | 30 | set ignore_waves to true if you want to ignore waves in this calculation 31 | ]] 32 | function Water:GetHeightAtPos(pos, ignore_waves) 33 | if not ignore_waves then 34 | local water_exists, height = GetWaterHeight(pos.x, pos.y, pos.z) 35 | return height, water_exists 36 | else 37 | local water_exists, height = GetWaterHeightNoWaves(pos.x, pos.y, pos.z) 38 | return height, water_exists 39 | end 40 | end 41 | 42 | --[[ 43 | Sets the water height at specified position. 44 | 45 | Seems to only make a small impact if done every frame. 46 | ]] 47 | function Water:SetHeightAtPos(pos, radius, height) 48 | ModifyWater(pos.x, pos.y, tofloat(radius), tofloat(height)) 49 | end 50 | 51 | -- Does not seem do to anything 52 | function Water:AddCurrentRise(x_low, y_low, x_high, y_high, height) 53 | AddCurrentRise(x_low, y_low, x_high, y_high, height) 54 | end 55 | 56 | Water = Water() 57 | 58 | end -------------------------------------------------------------------------------- /client/weapons/cWeapons.lua: -------------------------------------------------------------------------------- 1 | Weapons = class() 2 | 3 | function Weapons:__init() 4 | 5 | self.current_modifier = 1.0 6 | end 7 | 8 | 9 | function Weapons:SetDamageModifier(modifier) 10 | self.current_modifier = modifier 11 | 12 | local success, localplayer_weapon_hash = GetCurrentPedWeapon(LocalPlayer:GetPedId()) 13 | if success then 14 | SetWeaponDamageModifier(localplayer_weapon_hash, tofloat(self.current_modifier)) 15 | end 16 | end 17 | 18 | function Weapons:GetAllPossibleBaseDamages(weapon_enum) 19 | return self.base_damages[weapon_enum] 20 | end 21 | 22 | 23 | Weapons = Weapons() 24 | -------------------------------------------------------------------------------- /example_fxmanifest.lua: -------------------------------------------------------------------------------- 1 | ui_page 'oof/client/ui/index.html' 2 | 3 | client_scripts { 4 | -- OOF module, nothing should precede this module 5 | 6 | -- Uncomment ONE of these depending on the game this is running on 7 | --'oof/shared/game/IsRedM.lua', 8 | --'oof/shared/game/IsFiveM.lua', 9 | 10 | 'oof/shared/lua-overloads/*.lua', 11 | 'oof/shared/lua-additions/*.lua', 12 | 'oof/shared/object-oriented/class.lua', -- no class instances on initial frame before this file 13 | 'oof/shared/object-oriented/shGetterSetter.lua', -- getter_setter, getter_setter_encrypted 14 | 'oof/shared/object-oriented/shObjectOrientedUtilities.lua', -- is_class_instance 15 | 'oof/shared/standalone-data-structures/*', -- Enum, IdPool 16 | 'oof/shared/math/*.lua', 17 | '**/shared/enums/*Enum.lua', -- load all Enums 18 | '**/client/enums/*Enum.lua', 19 | 'oof/shared/events/*.lua', -- Events class 20 | 'oof/client/network/*.lua', -- Network class 21 | 'oof/shared/value-storage/*.lua', -- ValueStorage class 22 | 'oof/client/typecheck/*.lua', -- TypeCheck class 23 | 'oof/client/asset-requester/*.lua', 24 | 'oof/shared/timer/*.lua', -- Timer class 25 | 'oof/shared/xml/*.lua', -- XML class 26 | 'oof/shared/csv/*.lua', -- CSV class 27 | 'oof/client/entity/*.lua', -- Entity class 28 | 'oof/client/player/cPlayer.lua', 29 | 'oof/client/player/cPlayers.lua', 30 | 'oof/client/player/cPlayerManager.lua', 31 | 'oof/client/ped/*.lua', -- Ped class 32 | 'oof/client/physics/*.lua', 33 | 'oof/client/localplayer/*.lua', -- LocalPlayer class 34 | 'oof/shared/color/*.lua', 35 | 'oof/client/render/*.lua', 36 | 'oof/client/camera/*.lua', -- Camera class 37 | 'oof/client/blip/*.lua', -- Blip class 38 | 'oof/client/object/*.lua', -- Object, ObjectManager classes 39 | 'oof/client/ScreenEffects.lua', 40 | 'oof/client/world/*.lua', -- World class 41 | 'oof/client/sound/*.lua', -- Sound class 42 | 'oof/client/light/*.lua', -- Light class 43 | 'oof/client/particle-effect/*.lua', -- ParticleEffect class 44 | 'oof/client/anim-post-fx/*.lua', -- AnimPostFX class 45 | 'oof/client/volume/*.lua', -- Volume class 46 | 'oof/client/explosion/*.lua', -- Explosion class 47 | 'oof/client/pause-menu/*.lua', -- PauseMenu class 48 | 'oof/client/hud/*.lua', -- HUD class 49 | 'oof/client/keypress/*.lua', 50 | 'oof/client/prompt/*.lua', -- Prompt class 51 | 'oof/client/map/*.lua', -- Imap/Ipl class 52 | 'oof/client/marker/*.lua', -- Marker class 53 | 'oof/client/water/*.lua', -- Water class 54 | 'oof/client/apitest.lua', 55 | 'oof/client/localplayer_behaviors/*.lua', 56 | 'oof/client/weapons/*.lua', 57 | 'oof/client/ui/ui.lua', 58 | 59 | -- Add other modules here (client and shared) 60 | 61 | -- LOAD LAST 62 | 'oof/shared/object-oriented/LOAD_ABSOLUTELY_LAST.lua' 63 | } 64 | 65 | server_scripts { 66 | -- OOF module, nothing should precede this module 67 | 68 | -- Uncomment ONE of these depending on the game this is running on 69 | --'oof/shared/game/IsRedM.lua', 70 | --'oof/shared/game/IsFiveM.lua', 71 | 72 | 'oof/server/sConfig.lua', 73 | 'oof/shared/lua-overloads/*.lua', 74 | 'oof/shared/lua-additions/*.lua', 75 | 'oof/shared/object-oriented/class.lua', -- no class instances on initial frame before this file 76 | 'oof/shared/object-oriented/shGetterSetter.lua', 77 | 'oof/shared/object-oriented/shObjectOrientedUtilities.lua', -- is_class_instance function 78 | 'oof/shared/math/*.lua', 79 | 'oof/shared/standalone-data-structures/*', -- Enum, IdPool 80 | '**/shared/enums/*Enum.lua', -- load all the enums from all the modules 81 | '**/server/enums/*Enum.lua', 82 | 'oof/shared/color/*.lua', 83 | 'oof/shared/events/*.lua', -- Events class 84 | 'oof/server/network/*.lua', -- Network class 85 | 'oof/server/json/*.lua', -- JsonOOF, JsonUtils class 86 | -- mysql enabler 87 | 'oof/server/mysql-async/MySQLAsync.net.dll', 88 | 'oof/server/mysql-async/lib/init.lua', 89 | 'oof/server/mysql-async/lib/MySQL.lua', 90 | -- mysql wrapper 91 | 'oof/server/mysql/MySQL.lua', 92 | 'oof/server/key-value-store/*.lua', 93 | 'oof/shared/value-storage/*.lua', -- ValueStorage class 94 | 'oof/shared/timer/*.lua', -- Timer class 95 | 'oof/shared/xml/*.lua', -- XML class 96 | 'oof/shared/csv/*.lua', -- CSV class 97 | 'oof/server/fs-additions/*.lua', -- Directory/file exists helper functions 98 | 'oof/server/player/sPlayer.lua', -- Player class 99 | 'oof/server/player/sPlayers.lua', -- Players class 100 | 'oof/server/player/sPlayerManager.lua', -- PlayerManager class 101 | 'oof/server/entity/sEntity.lua', -- Entity class 102 | 'oof/server/ped/sPed.lua', -- Ped class 103 | 'oof/server/world/*.lua', -- World class 104 | 105 | -- Add other modules here (server and shared) 106 | 107 | -- Load last 108 | 'oof/shared/object-oriented/LOAD_ABSOLUTELY_LAST.lua' 109 | } 110 | 111 | files { 112 | -- general ui 113 | 'oof/client/ui/reset.css', 114 | 'oof/client/ui/jquery.js', 115 | 'oof/client/ui/events.js', 116 | 'oof/client/ui/index.html' 117 | -- Add files that you need here, like html/css/js for UI 118 | } 119 | 120 | fx_version 'adamant' 121 | games { 'rdr3', 'gta5' } 122 | rdr3_warning 'I acknowledge that this is a prerelease build of RedM, and I am aware my resources *will* become incompatible once RedM ships.' -------------------------------------------------------------------------------- /server/entity/sEntity.lua: -------------------------------------------------------------------------------- 1 | Entity = class(ValueStorage) 2 | 3 | function Entity:__init(entity_id) 4 | self.entity = entity_id 5 | self:InitializeEntity(self.entity) 6 | end 7 | 8 | function Entity:InitializeEntity(ent) 9 | self.entity = ent 10 | self:InitializeValueStorage(self) 11 | end 12 | 13 | function Entity:GetEntity() 14 | return self.entity 15 | end 16 | 17 | function Entity:GetEntityId() 18 | return self.entity 19 | end 20 | 21 | function Entity:Remove() 22 | DeleteEntity(self.entity) 23 | end 24 | 25 | function Entity:Exists() 26 | return DoesEntityExist(self.entity) 27 | end 28 | 29 | function Entity:GetNetworkId() 30 | return NetworkGetNetworkIdFromEntity(self.entity) 31 | end 32 | 33 | function Entity:GetNetworkOwner() 34 | return NetworkGetEntityOwner(self.entity) 35 | end 36 | 37 | function Entity:NetworkGetFirstEntityOwner() 38 | -- Get first entity owner - with fallback in case of older server version 39 | return NetworkGetFirstEntityOwner and NetworkGetFirstEntityOwner(self.entity) or NetworkGetEntityOwner(self.entity) 40 | end 41 | 42 | function Entity:GetPosition() 43 | return GetEntityCoords(self.entity) 44 | end 45 | 46 | function Entity:GetHeading() 47 | return GetEntityHeading(self.entity) 48 | end 49 | 50 | function Entity:GetHealth() 51 | return GetEntityHealth(self.entity) 52 | end 53 | 54 | function Entity:GetMaxHealth() 55 | return GetEntityMaxHealth(self.entity) 56 | end 57 | 58 | function Entity:GetModel() 59 | return GetEntityModel(self.entity) 60 | end 61 | 62 | function Entity:GetRotation() 63 | return GetEntityRotation(self.entity) 64 | end 65 | 66 | function Entity:GetRotationVelocity() 67 | return GetEntityRotationVelocity(self.entity) 68 | end 69 | 70 | function Entity:GetRoutingBucket() 71 | return GetEntityRoutingBucket(self.entity) 72 | end 73 | 74 | function Entity:SetRoutingBucket(bucket) 75 | SetEntityRoutingBucket(self.entity, bucket) 76 | end 77 | 78 | function Entity:GetType() 79 | return GetEntityType(self.entity) 80 | end 81 | 82 | function Entity:GetVelocity() 83 | return GetEntityVelocity(self.entity) 84 | end 85 | -------------------------------------------------------------------------------- /server/fs-additions/exists.lua: -------------------------------------------------------------------------------- 1 | -- https://stackoverflow.com/a/40195356 2 | --- Check if a file or directory exists in this path 3 | function fs_exists(file) 4 | local ok, err, code = os.rename(file, file) 5 | if not ok then 6 | if code == 13 then 7 | -- Permission denied, but it exists 8 | return true 9 | end 10 | end 11 | return ok, err 12 | end 13 | 14 | --- Check if a directory exists in this path 15 | function fs_isdir(path) 16 | -- "/" works on both Unix and Windows 17 | return fs_exists(path.."/") 18 | end 19 | -------------------------------------------------------------------------------- /server/json/sJSONUtils.lua: -------------------------------------------------------------------------------- 1 | JsonUtils = class() 2 | 3 | function JsonUtils:LoadJSON(path) 4 | local contents = LoadResourceFile(GetCurrentResourceName(), path) 5 | if not contents then return end 6 | return json.decode(contents) 7 | end 8 | 9 | function JsonUtils:SaveJSON(data, path) 10 | SaveResourceFile(GetCurrentResourceName(), path, json.encode(data, {indent = true}), -1) 11 | return true 12 | end 13 | 14 | JsonUtils = JsonUtils() -------------------------------------------------------------------------------- /server/mysql-async/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright © 2017, Joel Wurtz 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 4 | documentation files (the “Software”), to deal in the Software without restriction, including without limitation the 5 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit 6 | persons to whom the Software is furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the 9 | Software. 10 | 11 | The Software is provided “as is”, without warranty of any kind, express or implied, including but not limited to the 12 | warranties of merchantability, fitness for a particular purpose and noninfringement. In no event shall the authors or 13 | copyright holders X be liable for any claim, damages or other liability, whether in an action of contract, tort or 14 | otherwise, arising from, out of or in connection with the software or the use or other dealings in the Software. 15 | -------------------------------------------------------------------------------- /server/mysql-async/MySQLAsync.net.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Paradigm-MP/oof/e22d484c63ad7d780f5e1f0aa8c50ae9383596a7/server/mysql-async/MySQLAsync.net.dll -------------------------------------------------------------------------------- /server/mysql-async/MySqlConnector.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Paradigm-MP/oof/e22d484c63ad7d780f5e1f0aa8c50ae9383596a7/server/mysql-async/MySqlConnector.dll -------------------------------------------------------------------------------- /server/mysql-async/System.Buffers.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Paradigm-MP/oof/e22d484c63ad7d780f5e1f0aa8c50ae9383596a7/server/mysql-async/System.Buffers.dll -------------------------------------------------------------------------------- /server/mysql-async/System.Threading.Tasks.Extensions.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Paradigm-MP/oof/e22d484c63ad7d780f5e1f0aa8c50ae9383596a7/server/mysql-async/System.Threading.Tasks.Extensions.dll -------------------------------------------------------------------------------- /server/mysql-async/lib/MySQL.lua: -------------------------------------------------------------------------------- 1 | MySQL = { 2 | Async = {}, 3 | Sync = {}, 4 | Threaded = {} 5 | } 6 | 7 | local function safeParameters(params) 8 | if nil == params then 9 | return {[''] = ''} 10 | end 11 | 12 | assert(type(params) == "table", "A table is expected") 13 | assert(params[1] == nil, "Parameters should not be an array, but a map (key / value pair) instead") 14 | 15 | if next(params) == nil then 16 | return {[''] = ''} 17 | end 18 | 19 | return params 20 | end 21 | 22 | local function safeCallback(callback) 23 | if nil == callback then 24 | return function() end 25 | end 26 | 27 | assert(type(callback) == "function", "A callback is expected") 28 | 29 | return callback 30 | end 31 | 32 | --- 33 | -- Execute a query with no result required, sync version 34 | -- 35 | -- @param query 36 | -- @param params 37 | -- 38 | -- @return int Number of rows updated 39 | -- 40 | function MySQL.Sync.execute(query, params) 41 | assert(type(query) == "string", "The SQL Query must be a string") 42 | 43 | return exports[GetCurrentResourceName()]:mysql_sync_execute(query, safeParameters(params)) 44 | end 45 | 46 | --- 47 | -- Execute a query and fetch all results in an sync way 48 | -- 49 | -- @param query 50 | -- @param params 51 | -- 52 | -- @return table Query results 53 | -- 54 | function MySQL.Sync.fetchAll(query, params) 55 | assert(type(query) == "string", "The SQL Query must be a string") 56 | 57 | return exports[GetCurrentResourceName()]:mysql_sync_fetch_all(query, safeParameters(params)) 58 | end 59 | 60 | --- 61 | -- Execute a query and fetch the first column of the first row, sync version 62 | -- Useful for count function by example 63 | -- 64 | -- @param query 65 | -- @param params 66 | -- 67 | -- @return mixed Value of the first column in the first row 68 | -- 69 | function MySQL.Sync.fetchScalar(query, params) 70 | assert(type(query) == "string", "The SQL Query must be a string") 71 | 72 | return exports[GetCurrentResourceName()]:mysql_sync_fetch_scalar(query, safeParameters(params)) 73 | end 74 | 75 | --- 76 | -- Execute a query and retrieve the last id insert, sync version 77 | -- 78 | -- @param query 79 | -- @param params 80 | -- 81 | -- @return mixed Value of the last insert id 82 | -- 83 | function MySQL.Sync.insert(query, params) 84 | assert(type(query) == "string", "The SQL Query must be a string") 85 | 86 | return exports[GetCurrentResourceName()]:mysql_sync_insert(query, safeParameters(params)) 87 | end 88 | 89 | --- 90 | -- Execute a List of querys and returns bool true when all are executed successfully 91 | -- 92 | -- @param querys 93 | -- @param params 94 | -- 95 | -- @return bool if the transaction was successful 96 | -- 97 | function MySQL.Sync.transaction(querys, params) 98 | assert(type(querys) == "table", "The SQL Query must be a table of strings") 99 | 100 | return exports[GetCurrentResourceName()]:mysql_sync_transaction(querys, safeParameters(params)) 101 | end 102 | 103 | --- 104 | -- Execute a query with no result required, async version 105 | -- 106 | -- @param query 107 | -- @param params 108 | -- @param func(int) 109 | -- 110 | function MySQL.Async.execute(query, params, func) 111 | assert(type(query) == "string", "The SQL Query must be a string") 112 | 113 | exports[GetCurrentResourceName()]:mysql_execute(query, safeParameters(params), safeCallback(func)) 114 | end 115 | 116 | --- 117 | -- Execute a query and fetch all results in an async way 118 | -- 119 | -- @param query 120 | -- @param params 121 | -- @param func(table) 122 | -- 123 | function MySQL.Async.fetchAll(query, params, func) 124 | assert(type(query) == "string", "The SQL Query must be a string") 125 | 126 | exports[GetCurrentResourceName()]:mysql_fetch_all(query, safeParameters(params), safeCallback(func)) 127 | end 128 | 129 | --- 130 | -- Execute a query and fetch the first column of the first row, async version 131 | -- Useful for count function by example 132 | -- 133 | -- @param query 134 | -- @param params 135 | -- @param func(mixed) 136 | -- 137 | function MySQL.Async.fetchScalar(query, params, func) 138 | assert(type(query) == "string", "The SQL Query must be a string") 139 | 140 | exports[GetCurrentResourceName()]:mysql_fetch_scalar(query, safeParameters(params), safeCallback(func)) 141 | end 142 | 143 | --- 144 | -- Execute a query and retrieve the last id insert, async version 145 | -- 146 | -- @param query 147 | -- @param params 148 | -- @param func(string) 149 | -- 150 | function MySQL.Async.insert(query, params, func) 151 | assert(type(query) == "string", "The SQL Query must be a string") 152 | 153 | exports[GetCurrentResourceName()]:mysql_insert(query, safeParameters(params), safeCallback(func)) 154 | end 155 | 156 | --- 157 | -- Execute a List of querys and returns bool true when all are executed successfully 158 | -- 159 | -- @param querys 160 | -- @param params 161 | -- @param func(bool) 162 | -- 163 | function MySQL.Async.transaction(querys, params, func) 164 | assert(type(querys) == "table", "The SQL Query must be a table of strings") 165 | 166 | return exports[GetCurrentResourceName()]:mysql_transaction(querys, safeParameters(params), safeCallback(func)) 167 | end 168 | 169 | --- 170 | -- Execute a query with no result required, Threaded version 171 | -- 172 | -- @param query 173 | -- @param params 174 | -- 175 | -- @return int Number of rows updated 176 | -- 177 | function MySQL.Threaded.execute(query, params) 178 | assert(type(query) == "string", "The SQL Query must be a string") 179 | 180 | return exports[GetCurrentResourceName()]:mysql_threaded_execute(query, safeParameters(params)) 181 | end 182 | 183 | --- 184 | -- Execute a query and fetch all results in an Threaded way 185 | -- 186 | -- @param query 187 | -- @param params 188 | -- 189 | -- @return table Query results 190 | -- 191 | function MySQL.Threaded.fetchAll(query, params) 192 | assert(type(query) == "string", "The SQL Query must be a string") 193 | 194 | return exports[GetCurrentResourceName()]:mysql_threaded_fetch_all(query, safeParameters(params)) 195 | end 196 | 197 | --- 198 | -- Execute a query and fetch the first column of the first row, Threaded version 199 | -- Useful for count function by example 200 | -- 201 | -- @param query 202 | -- @param params 203 | -- 204 | -- @return mixed Value of the first column in the first row 205 | -- 206 | function MySQL.Threaded.fetchScalar(query, params) 207 | assert(type(query) == "string", "The SQL Query must be a string") 208 | 209 | return exports[GetCurrentResourceName()]:mysql_threaded_fetch_scalar(query, safeParameters(params)) 210 | end 211 | 212 | --- 213 | -- Execute a query and retrieve the last id insert, Threaded version 214 | -- 215 | -- @param query 216 | -- @param params 217 | -- 218 | -- @return mixed Value of the last insert id 219 | -- 220 | function MySQL.Threaded.insert(query, params) 221 | assert(type(query) == "string", "The SQL Query must be a string") 222 | 223 | return exports[GetCurrentResourceName()]:mysql_threaded_insert(query, safeParameters(params)) 224 | end 225 | 226 | 227 | local isReady = false 228 | 229 | AddEventHandler('onMySQLReady', function () 230 | isReady = true 231 | end) 232 | 233 | function MySQL.ready(callback) 234 | if isReady then 235 | callback() 236 | 237 | return 238 | end 239 | 240 | AddEventHandler('onMySQLReady', callback) 241 | end 242 | -------------------------------------------------------------------------------- /server/mysql-async/lib/init.lua: -------------------------------------------------------------------------------- 1 | AddEventHandler('onServerResourceStart', function (resource) 2 | if resource == GetCurrentResourceName() then 3 | exports[GetCurrentResourceName()]:mysql_configure() 4 | 5 | Citizen.CreateThread(function () 6 | Citizen.Wait(0) 7 | TriggerEvent('onMySQLReady') 8 | end) 9 | end 10 | end) 11 | 12 | 13 | -------------------------------------------------------------------------------- /server/mysql-async/src/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // Les informations générales relatives à un assembly dépendent de 6 | // l'ensemble d'attributs suivant. Changez les valeurs de ces attributs pour modifier les informations 7 | // associées à un assembly. 8 | [assembly: AssemblyTitle("MySQLAsync")] 9 | [assembly: AssemblyDescription("FiveM MySQL Async Integration for LUA")] 10 | [assembly: AssemblyConfiguration("x64")] 11 | [assembly: AssemblyCompany("Brouznouf")] 12 | [assembly: AssemblyProduct("MySQLAsync")] 13 | [assembly: AssemblyCopyright("Copyright © 2018")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // L'affectation de la valeur false à ComVisible rend les types invisibles dans cet assembly 18 | // aux composants COM. Si vous devez accéder à un type dans cet assembly à partir de 19 | // COM, affectez la valeur true à l'attribut ComVisible sur ce type. 20 | [assembly: ComVisible(false)] 21 | 22 | // Le GUID suivant est pour l'ID de la typelib si ce projet est exposé à COM 23 | [assembly: Guid("fbe7b949-3e81-41ca-a836-e576fab1baf4")] 24 | 25 | // Les informations de version pour un assembly se composent des quatre valeurs suivantes : 26 | // 27 | // Version principale 28 | // Version secondaire 29 | // Numéro de build 30 | // Révision 31 | // 32 | [assembly: AssemblyVersion("2.1.2.0")] 33 | [assembly: AssemblyFileVersion("2.1.2.0")] 34 | -------------------------------------------------------------------------------- /server/mysql-async/src/Execute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using MySql.Data.MySqlClient; 7 | 8 | namespace MySQLAsync 9 | { 10 | class Execute : Operation