2838 lines
78 KiB
Lua
2838 lines
78 KiB
Lua
local crowbarCoords = vec4(1699.67, 3775.30, 34.74, 207.22)
|
|||
|
|
|
||
|
|
local crowbarPed
|
||
|
|
local crowbarBlip
|
||
|
|
local crowbarBike
|
||
|
|
|
||
|
|
local mackCoords = vec4(2366.60, 3154.91, 48.21, 17.84)
|
||
|
|
local mackPed
|
||
|
|
local mackBlip
|
||
|
|
local gabeBlip
|
||
|
|
|
||
|
|
local dekeCoords = vec4(2359.89, 3136.74, 48.21, 110.90)
|
||
|
|
local dekePed
|
||
|
|
|
||
|
|
local crowbarMission1Vehicle
|
||
|
|
local crowbarMission1Blip
|
||
|
|
local crowbarMission1ReturnActive = false
|
||
|
|
|
||
|
|
local crowbarMission2Vehicle
|
||
|
|
local crowbarMission2Blip
|
||
|
|
|
||
|
|
-- ============================================================
|
||
|
|
-- Mack world setup
|
||
|
|
-- ============================================================
|
||
|
|
|
||
|
|
local function createDeke()
|
||
|
|
if DoesEntityExist(dekePed) then
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
local model = `s_m_m_autoshop_02`
|
||
|
|
|
||
|
|
lib.requestModel(model)
|
||
|
|
|
||
|
|
dekePed = CreatePed(
|
||
|
|
0,
|
||
|
|
model,
|
||
|
|
dekeCoords.x,
|
||
|
|
dekeCoords.y,
|
||
|
|
dekeCoords.z - 1.0,
|
||
|
|
dekeCoords.w,
|
||
|
|
false,
|
||
|
|
false
|
||
|
|
)
|
||
|
|
|
||
|
|
SetPedDefaultComponentVariation(dekePed)
|
||
|
|
SetEntityInvincible(dekePed, true)
|
||
|
|
FreezeEntityPosition(dekePed, true)
|
||
|
|
SetBlockingOfNonTemporaryEvents(dekePed, true)
|
||
|
|
|
||
|
|
TaskStartScenarioInPlace(
|
||
|
|
dekePed,
|
||
|
|
'WORLD_HUMAN_STAND_IMPATIENT',
|
||
|
|
0,
|
||
|
|
true
|
||
|
|
)
|
||
|
|
|
||
|
|
exports.ox_target:addLocalEntity(dekePed, {
|
||
|
|
{
|
||
|
|
name = 'how_progression_deke_shop',
|
||
|
|
icon = 'fa-solid fa-toolbox',
|
||
|
|
label = 'Talk to Deke',
|
||
|
|
distance = 2.0,
|
||
|
|
|
||
|
|
onSelect = function()
|
||
|
|
exports.ox_inventory:openInventory(
|
||
|
|
'shop',
|
||
|
|
{
|
||
|
|
type = 'DekeTools',
|
||
|
|
id = 1
|
||
|
|
}
|
||
|
|
)
|
||
|
|
end
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
SetModelAsNoLongerNeeded(model)
|
||
|
|
end
|
||
|
|
|
||
|
|
local function createMack()
|
||
|
|
createDeke()
|
||
|
|
|
||
|
|
if DoesEntityExist(mackPed) then
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
local model = `s_m_y_xmech_01`
|
||
|
|
|
||
|
|
lib.requestModel(model)
|
||
|
|
|
||
|
|
mackPed = CreatePed(
|
||
|
|
0,
|
||
|
|
model,
|
||
|
|
mackCoords.x,
|
||
|
|
mackCoords.y,
|
||
|
|
mackCoords.z - 1.0,
|
||
|
|
mackCoords.w,
|
||
|
|
false,
|
||
|
|
false
|
||
|
|
)
|
||
|
|
|
||
|
|
SetPedDefaultComponentVariation(mackPed)
|
||
|
|
|
||
|
|
SetEntityInvincible(mackPed, true)
|
||
|
|
FreezeEntityPosition(mackPed, true)
|
||
|
|
SetBlockingOfNonTemporaryEvents(mackPed, true)
|
||
|
|
|
||
|
|
TaskStartScenarioInPlace(
|
||
|
|
mackPed,
|
||
|
|
'WORLD_HUMAN_CLIPBOARD',
|
||
|
|
0,
|
||
|
|
true
|
||
|
|
)
|
||
|
|
|
||
|
|
exports.ox_target:addLocalEntity(mackPed, {
|
||
|
|
{
|
||
|
|
name = 'how_progression_mack_talk',
|
||
|
|
icon = 'fa-solid fa-comment',
|
||
|
|
label = 'Talk to Mack',
|
||
|
|
distance = 2.0,
|
||
|
|
|
||
|
|
canInteract = function()
|
||
|
|
return lib.callback.await(
|
||
|
|
'how_progression:server:canTalkToMack',
|
||
|
|
false
|
||
|
|
)
|
||
|
|
end,
|
||
|
|
|
||
|
|
onSelect = function()
|
||
|
|
local state = lib.callback.await(
|
||
|
|
'how_progression:server:getCrowbarMission4State',
|
||
|
|
false
|
||
|
|
)
|
||
|
|
|
||
|
|
if state == 'meet_mack' then
|
||
|
|
lib.alertDialog({
|
||
|
|
header = 'Mack',
|
||
|
|
|
||
|
|
content =
|
||
|
|
"So you're Crowbar's new errand boy.\n\n" ..
|
||
|
|
"He put in a good word for you. Well... as good a word " ..
|
||
|
|
"as Crowbar gives anybody. Says you're about halfway trustworthy.\n\n" ..
|
||
|
|
"You need that cash cleaned, go see Gabe. He's in the warehouse.\n\n" ..
|
||
|
|
"And don't ask how it works. Gabe gets cranky when people ask questions.",
|
||
|
|
|
||
|
|
centered = true
|
||
|
|
})
|
||
|
|
|
||
|
|
TriggerServerEvent(
|
||
|
|
'how_progression:server:crowbarMission4TalkedToMack'
|
||
|
|
)
|
||
|
|
else
|
||
|
|
local mackState = lib.callback.await(
|
||
|
|
'how_progression:server:getMackMission1State',
|
||
|
|
false
|
||
|
|
)
|
||
|
|
|
||
|
|
if mackState
|
||
|
|
and mackState.objective == 'available'
|
||
|
|
then
|
||
|
|
local answer = lib.alertDialog({
|
||
|
|
header = 'Mack',
|
||
|
|
content =
|
||
|
|
"Got a delivery for you. Guy named Vinny just opened up a little bike shop.\n\n" ..
|
||
|
|
"Fixed a few Sanchezes for him. They're out back.\n\n" ..
|
||
|
|
"Take 'em over. Apparently he's got customers waiting, which means he's called me about fourteen fucking times today.\n\n" ..
|
||
|
|
"Try not to scratch 'em.\n\n" ..
|
||
|
|
"Take these. It gets dangerous in my line of work.",
|
||
|
|
centered = true,
|
||
|
|
cancel = true,
|
||
|
|
labels = {
|
||
|
|
confirm = 'Take the job',
|
||
|
|
cancel = 'Not now'
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
if answer == 'confirm' then
|
||
|
|
TriggerServerEvent(
|
||
|
|
'how_progression:server:startMackMission1'
|
||
|
|
)
|
||
|
|
end
|
||
|
|
elseif mackState
|
||
|
|
and mackState.objective ~= 'complete'
|
||
|
|
then
|
||
|
|
lib.notify({
|
||
|
|
title = 'Mack',
|
||
|
|
description =
|
||
|
|
"Finish what you're doing first.",
|
||
|
|
type = 'inform'
|
||
|
|
})
|
||
|
|
else
|
||
|
|
lib.alertDialog({
|
||
|
|
header = 'Mack',
|
||
|
|
content =
|
||
|
|
"Crowbar says you're all right. That's good enough for me.",
|
||
|
|
centered = true
|
||
|
|
})
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
SetModelAsNoLongerNeeded(model)
|
||
|
|
end
|
||
|
|
|
||
|
|
-- ============================================================
|
||
|
|
-- Crowbar world setup
|
||
|
|
-- ============================================================
|
||
|
|
|
||
|
|
local function createCrowbar()
|
||
|
|
if DoesEntityExist(crowbarPed) then
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
local model = `ig_clay`
|
||
|
|
|
||
|
|
lib.requestModel(model)
|
||
|
|
|
||
|
|
crowbarPed = CreatePed(
|
||
|
|
0,
|
||
|
|
model,
|
||
|
|
crowbarCoords.x,
|
||
|
|
crowbarCoords.y,
|
||
|
|
crowbarCoords.z - 1.0,
|
||
|
|
crowbarCoords.w,
|
||
|
|
false,
|
||
|
|
false
|
||
|
|
)
|
||
|
|
|
||
|
|
SetEntityInvincible(crowbarPed, true)
|
||
|
|
FreezeEntityPosition(crowbarPed, true)
|
||
|
|
SetBlockingOfNonTemporaryEvents(crowbarPed, true)
|
||
|
|
|
||
|
|
TaskStartScenarioInPlace(
|
||
|
|
crowbarPed,
|
||
|
|
'WORLD_HUMAN_SMOKING',
|
||
|
|
0,
|
||
|
|
true
|
||
|
|
)
|
||
|
|
|
||
|
|
exports.ox_target:addLocalEntity(crowbarPed, {
|
||
|
|
{
|
||
|
|
name = 'how_progression_crowbar_talk',
|
||
|
|
icon = 'fa-solid fa-comment',
|
||
|
|
label = 'Talk to Crowbar',
|
||
|
|
distance = 2.0,
|
||
|
|
|
||
|
|
canInteract = function()
|
||
|
|
local active = lib.callback.await(
|
||
|
|
'how_progression:server:isCrowbarMissionActive',
|
||
|
|
false
|
||
|
|
)
|
||
|
|
|
||
|
|
return not active
|
||
|
|
end,
|
||
|
|
|
||
|
|
onSelect = function()
|
||
|
|
local stage = lib.callback.await(
|
||
|
|
'how_progression:server:getCrowbarMission',
|
||
|
|
false
|
||
|
|
)
|
||
|
|
|
||
|
|
if stage == 1 then
|
||
|
|
local result = lib.alertDialog({
|
||
|
|
header = 'Crowbar',
|
||
|
|
|
||
|
|
content =
|
||
|
|
"So you're the one looking for work.\n\n" ..
|
||
|
|
"I don't know you, and I sure as hell don't trust you. " ..
|
||
|
|
"So we're gonna start simple. You prove you can follow directions, " ..
|
||
|
|
"maybe we'll talk about real work.\n\n" ..
|
||
|
|
"Some creep owes me money. He's holed up at an abandoned service " ..
|
||
|
|
"station outside town. God knows what that creep is doing in there.\n\n" ..
|
||
|
|
"I don't want the money. I want his car. It's parked outside.\n\n" ..
|
||
|
|
"I had a copy of the key made. Don't ask how. Take the car and " ..
|
||
|
|
"bring it back here in one piece.\n\n" ..
|
||
|
|
"Do that, and maybe you're worth knowing.",
|
||
|
|
|
||
|
|
centered = true,
|
||
|
|
cancel = true,
|
||
|
|
|
||
|
|
labels = {
|
||
|
|
confirm = "I'm in",
|
||
|
|
cancel = "Not right now"
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
if result == 'confirm' then
|
||
|
|
TriggerServerEvent(
|
||
|
|
'how_progression:server:startCrowbarMission1'
|
||
|
|
)
|
||
|
|
end
|
||
|
|
|
||
|
|
elseif stage == 2 then
|
||
|
|
local result = lib.alertDialog({
|
||
|
|
header = 'Crowbar',
|
||
|
|
|
||
|
|
content =
|
||
|
|
"I always loved this car. Hope that creep's crying " ..
|
||
|
|
"in a basement somewhere. Anyway...\n\n" ..
|
||
|
|
"You did good. But don't get excited—I still don't trust you.\n\n" ..
|
||
|
|
"So we're gonna try something a little harder. Another car. " ..
|
||
|
|
"This time I didn't get you a key. You'll have to steal it yourself.\n\n" ..
|
||
|
|
"Good news is, you get to keep this one.\n\n" ..
|
||
|
|
"I'll give you the picks to get inside. You're gonna need a pair " ..
|
||
|
|
"of snips to get it running. You Tool sells 'em.\n\n" ..
|
||
|
|
"You in?",
|
||
|
|
|
||
|
|
centered = true,
|
||
|
|
cancel = true,
|
||
|
|
|
||
|
|
labels = {
|
||
|
|
confirm = "I'm in",
|
||
|
|
cancel = "Not right now"
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
if result == 'confirm' then
|
||
|
|
TriggerServerEvent(
|
||
|
|
'how_progression:server:startCrowbarMission2'
|
||
|
|
)
|
||
|
|
end
|
||
|
|
|
||
|
|
elseif stage == 3 then
|
||
|
|
local result = lib.alertDialog({
|
||
|
|
header = 'Crowbar',
|
||
|
|
|
||
|
|
content =
|
||
|
|
"All right. Let's see how you handle yourself when things get loud.\n\n" ..
|
||
|
|
"There's a 24/7 down in Harmony. Easy money.\n\n" ..
|
||
|
|
"Use this. There's no magazine, so make it convincing. " ..
|
||
|
|
"Hit the register and get out of there.\n\n" ..
|
||
|
|
"You in?",
|
||
|
|
|
||
|
|
centered = true,
|
||
|
|
cancel = true,
|
||
|
|
|
||
|
|
labels = {
|
||
|
|
confirm = "I'm in",
|
||
|
|
cancel = "Not right now"
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
if result == 'confirm' then
|
||
|
|
TriggerServerEvent(
|
||
|
|
'how_progression:server:startCrowbarMission3'
|
||
|
|
)
|
||
|
|
end
|
||
|
|
|
||
|
|
elseif stage == 4 then
|
||
|
|
local result = lib.alertDialog({
|
||
|
|
header = 'Crowbar',
|
||
|
|
|
||
|
|
content =
|
||
|
|
"That cash from the store? Don't spend it. It's marked.\n\n" ..
|
||
|
|
"I know a guy who can help, but he doesn't deal with strangers. " ..
|
||
|
|
"Go see Mack over at the scrapyard. I told him you were coming.\n\n" ..
|
||
|
|
"Don't make me regret that.\n\n" ..
|
||
|
|
"You in?",
|
||
|
|
|
||
|
|
centered = true,
|
||
|
|
cancel = true,
|
||
|
|
|
||
|
|
labels = {
|
||
|
|
confirm = "I'm in",
|
||
|
|
cancel = "Not right now"
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
if result == 'confirm' then
|
||
|
|
TriggerServerEvent(
|
||
|
|
'how_progression:server:startCrowbarMission4'
|
||
|
|
)
|
||
|
|
end
|
||
|
|
|
||
|
|
elseif stage == 5 then
|
||
|
|
local result = lib.alertDialog({
|
||
|
|
header = 'Crowbar',
|
||
|
|
|
||
|
|
content =
|
||
|
|
"Now you got money you can actually spend. Good. " ..
|
||
|
|
"Means I don't gotta keep supplying you.\n\n" ..
|
||
|
|
"Next thing I got ain't a stick-up. You're picking up " ..
|
||
|
|
"a package for me, and there's a chance somebody else wants it.\n\n" ..
|
||
|
|
"Go buy yourself some ammo at the Ammu-Nation next door. " ..
|
||
|
|
"Maybe a vest too, if you're the nervous type... or don't. " ..
|
||
|
|
"I don't care. I just want my package.\n\n" ..
|
||
|
|
"I'll make it worth your while.",
|
||
|
|
|
||
|
|
centered = true,
|
||
|
|
cancel = true,
|
||
|
|
|
||
|
|
labels = {
|
||
|
|
confirm = "I'm in",
|
||
|
|
cancel = "Not right now"
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
if result == 'confirm' then
|
||
|
|
TriggerServerEvent(
|
||
|
|
'how_progression:server:startCrowbarMission5'
|
||
|
|
)
|
||
|
|
end
|
||
|
|
|
||
|
|
elseif stage >= 6 then
|
||
|
|
lib.alertDialog({
|
||
|
|
header = 'Crowbar',
|
||
|
|
|
||
|
|
content =
|
||
|
|
"You've done what I asked. For now, that's enough.\n\n" ..
|
||
|
|
"I'll let you know when I've got something else.",
|
||
|
|
|
||
|
|
centered = true
|
||
|
|
})
|
||
|
|
|
||
|
|
else
|
||
|
|
lib.alertDialog({
|
||
|
|
header = 'Crowbar',
|
||
|
|
content = "You shouldn't be talking to me yet.",
|
||
|
|
centered = true
|
||
|
|
})
|
||
|
|
end
|
||
|
|
end
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
SetModelAsNoLongerNeeded(model)
|
||
|
|
end
|
||
|
|
|
||
|
|
local function createCrowbarBlip()
|
||
|
|
if DoesBlipExist(crowbarBlip) then
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
crowbarBlip = AddBlipForCoord(
|
||
|
|
crowbarCoords.x,
|
||
|
|
crowbarCoords.y,
|
||
|
|
crowbarCoords.z
|
||
|
|
)
|
||
|
|
|
||
|
|
SetBlipSprite(crowbarBlip, 280)
|
||
|
|
SetBlipScale(crowbarBlip, 0.75)
|
||
|
|
SetBlipAsShortRange(crowbarBlip, false)
|
||
|
|
|
||
|
|
BeginTextCommandSetBlipName('STRING')
|
||
|
|
AddTextComponentString('Crowbar')
|
||
|
|
EndTextCommandSetBlipName(crowbarBlip)
|
||
|
|
end
|
||
|
|
|
||
|
|
local function createCrowbarBike()
|
||
|
|
if DoesEntityExist(crowbarBike) then
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
local model = `daemon`
|
||
|
|
|
||
|
|
lib.requestModel(model)
|
||
|
|
|
||
|
|
crowbarBike = CreateVehicle(
|
||
|
|
model,
|
||
|
|
1699.70,
|
||
|
|
3771.63,
|
||
|
|
34.53,
|
||
|
|
258.21,
|
||
|
|
false,
|
||
|
|
false
|
||
|
|
)
|
||
|
|
|
||
|
|
SetEntityAsMissionEntity(crowbarBike, true, true)
|
||
|
|
SetVehicleOnGroundProperly(crowbarBike)
|
||
|
|
SetVehicleDoorsLocked(crowbarBike, 2)
|
||
|
|
SetEntityInvincible(crowbarBike, true)
|
||
|
|
FreezeEntityPosition(crowbarBike, true)
|
||
|
|
|
||
|
|
SetModelAsNoLongerNeeded(model)
|
||
|
|
end
|
||
|
|
|
||
|
|
local function unlockCrowbar()
|
||
|
|
createCrowbar()
|
||
|
|
createCrowbarBike()
|
||
|
|
createCrowbarBlip()
|
||
|
|
|
||
|
|
print('[how_progression] Crowbar unlocked')
|
||
|
|
end
|
||
|
|
|
||
|
|
RegisterNetEvent(
|
||
|
|
'how_progression:client:unlockCrowbar',
|
||
|
|
unlockCrowbar
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
-- Forward declarations for progression-driven world state.
|
||
|
|
local createMackBlip
|
||
|
|
local unlockVinnyWorld
|
||
|
|
|
||
|
|
-- Restore persistent progression-driven world state after character login.
|
||
|
|
RegisterNetEvent(
|
||
|
|
'how_progression:client:syncProgression',
|
||
|
|
function(progression)
|
||
|
|
if not progression then return end
|
||
|
|
|
||
|
|
-- Stage 0 means the contact has not been discovered.
|
||
|
|
if (progression.crowbar_stage or 0) >= 1 then
|
||
|
|
unlockCrowbar()
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Mack becomes a persistent known contact once his progression begins.
|
||
|
|
if (progression.mack_stage or 0) >= 1 then
|
||
|
|
createMack()
|
||
|
|
createMackBlip()
|
||
|
|
end
|
||
|
|
|
||
|
|
if progression.vinny_fence_unlocked == true
|
||
|
|
or tonumber(progression.vinny_fence_unlocked) == 1
|
||
|
|
then
|
||
|
|
unlockVinnyWorld()
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Future persistent contacts are restored here.
|
||
|
|
-- Tina, Cosmic Dave, etc. will use their appropriate progression
|
||
|
|
-- state as they are added.
|
||
|
|
end
|
||
|
|
)
|
||
|
|
|
||
|
|
-- ============================================================
|
||
|
|
-- Mack Mission 1: Easy Rider
|
||
|
|
-- ============================================================
|
||
|
|
|
||
|
|
local mackMission1BikeSpawns = {
|
||
|
|
vec4(2360.90, 3114.91, 48.21, 239.14),
|
||
|
|
vec4(2360.28, 3113.93, 48.21, 245.54),
|
||
|
|
vec4(2359.64, 3112.59, 48.21, 265.43),
|
||
|
|
vec4(2359.45, 3111.15, 48.21, 246.34),
|
||
|
|
vec4(2361.34, 3117.44, 48.21, 234.06)
|
||
|
|
}
|
||
|
|
|
||
|
|
local vinnyMissionDrop =
|
||
|
|
vec3(1373.86, 3615.83, 34.89)
|
||
|
|
|
||
|
|
local vinnyFenceDrop =
|
||
|
|
vec4(1356.79, 3617.37, 34.88, 105.38)
|
||
|
|
|
||
|
|
local vinnyCoords =
|
||
|
|
vec4(1362.57, 3602.49, 34.96, 236.75)
|
||
|
|
|
||
|
|
local mackMission1DetourCoords =
|
||
|
|
vec3(252.87, 2814.15, 43.19)
|
||
|
|
|
||
|
|
local mackMission1TrashCarCoords =
|
||
|
|
vec4(285.10, 2827.15, 42.89, 116.30)
|
||
|
|
|
||
|
|
local mackMission1Bikes = {}
|
||
|
|
local mackMission1TrashCar
|
||
|
|
local mackMission1Blip
|
||
|
|
local mackMission1Active = false
|
||
|
|
local mackMission1Objective
|
||
|
|
local mackMission1Delivered = {}
|
||
|
|
local mackMission1DetourSent = false
|
||
|
|
local mackMission1TrashReported = false
|
||
|
|
local mackMission1Owner = false
|
||
|
|
local mackMission1BikeCount = 1
|
||
|
|
local mackMission1HeadingToVinny = false
|
||
|
|
local mackMission1DetourExplained = false
|
||
|
|
local mackMission1DeliveryMarkerActive = false
|
||
|
|
|
||
|
|
local vinnyPed
|
||
|
|
local vinnyBlip
|
||
|
|
local vinnyFenceUnlocked = false
|
||
|
|
local vinnyDisplayBikes = {}
|
||
|
|
local vinnyDisplayPositions = {
|
||
|
|
vec4(1368.39, 3594.54, 34.34, 237.54),
|
||
|
|
vec4(1366.95, 3598.24, 34.37, 244.92),
|
||
|
|
vec4(1368.76, 3592.05, 34.37, 241.99),
|
||
|
|
vec4(1357.67, 3601.45, 34.44, 194.85),
|
||
|
|
vec4(1364.94, 3603.77, 34.33, 195.30)
|
||
|
|
}
|
||
|
|
|
||
|
|
local function clearMackMission1Blip()
|
||
|
|
if DoesBlipExist(mackMission1Blip) then
|
||
|
|
RemoveBlip(mackMission1Blip)
|
||
|
|
end
|
||
|
|
|
||
|
|
mackMission1Blip = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
local function setMackMission1Route(coords, name)
|
||
|
|
clearMackMission1Blip()
|
||
|
|
|
||
|
|
mackMission1Blip =
|
||
|
|
AddBlipForCoord(coords.x, coords.y, coords.z)
|
||
|
|
|
||
|
|
SetBlipSprite(mackMission1Blip, 1)
|
||
|
|
SetBlipColour(mackMission1Blip, 5)
|
||
|
|
SetBlipRoute(mackMission1Blip, true)
|
||
|
|
SetBlipRouteColour(mackMission1Blip, 5)
|
||
|
|
|
||
|
|
BeginTextCommandSetBlipName('STRING')
|
||
|
|
AddTextComponentString(name)
|
||
|
|
EndTextCommandSetBlipName(mackMission1Blip)
|
||
|
|
|
||
|
|
SetNewWaypoint(coords.x, coords.y)
|
||
|
|
end
|
||
|
|
|
||
|
|
local function spawnMackMission1Bikes()
|
||
|
|
if #mackMission1Bikes > 0 then return end
|
||
|
|
|
||
|
|
local model = `sanchez`
|
||
|
|
lib.requestModel(model)
|
||
|
|
|
||
|
|
for i = 1, mackMission1BikeCount do
|
||
|
|
local coords = mackMission1BikeSpawns[i]
|
||
|
|
|
||
|
|
local bike = CreateVehicle(
|
||
|
|
model,
|
||
|
|
coords.x,
|
||
|
|
coords.y,
|
||
|
|
coords.z,
|
||
|
|
coords.w,
|
||
|
|
true,
|
||
|
|
true
|
||
|
|
)
|
||
|
|
|
||
|
|
SetEntityAsMissionEntity(bike, true, true)
|
||
|
|
SetVehicleOnGroundProperly(bike)
|
||
|
|
SetVehicleDoorsLocked(bike, 1)
|
||
|
|
SetVehicleEngineOn(bike, false, true, true)
|
||
|
|
|
||
|
|
Entity(bike).state:set(
|
||
|
|
'howMackMission1Bike',
|
||
|
|
i,
|
||
|
|
true
|
||
|
|
)
|
||
|
|
|
||
|
|
mackMission1Bikes[i] = bike
|
||
|
|
|
||
|
|
local netId = NetworkGetNetworkIdFromEntity(bike)
|
||
|
|
|
||
|
|
TriggerServerEvent(
|
||
|
|
'how_progression:server:mackMission1BikeSpawned',
|
||
|
|
i,
|
||
|
|
netId
|
||
|
|
)
|
||
|
|
end
|
||
|
|
|
||
|
|
SetModelAsNoLongerNeeded(model)
|
||
|
|
end
|
||
|
|
|
||
|
|
local function spawnMackMission1TrashCar()
|
||
|
|
if DoesEntityExist(mackMission1TrashCar) then
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
local model = `superd`
|
||
|
|
lib.requestModel(model)
|
||
|
|
|
||
|
|
mackMission1TrashCar = CreateVehicle(
|
||
|
|
model,
|
||
|
|
mackMission1TrashCarCoords.x,
|
||
|
|
mackMission1TrashCarCoords.y,
|
||
|
|
mackMission1TrashCarCoords.z,
|
||
|
|
mackMission1TrashCarCoords.w,
|
||
|
|
true,
|
||
|
|
true
|
||
|
|
)
|
||
|
|
|
||
|
|
SetEntityAsMissionEntity(
|
||
|
|
mackMission1TrashCar,
|
||
|
|
true,
|
||
|
|
true
|
||
|
|
)
|
||
|
|
|
||
|
|
SetVehicleOnGroundProperly(mackMission1TrashCar)
|
||
|
|
SetVehicleDoorsLocked(mackMission1TrashCar, 2)
|
||
|
|
SetVehicleEngineOn(
|
||
|
|
mackMission1TrashCar,
|
||
|
|
false,
|
||
|
|
true,
|
||
|
|
true
|
||
|
|
)
|
||
|
|
|
||
|
|
SetModelAsNoLongerNeeded(model)
|
||
|
|
end
|
||
|
|
|
||
|
|
local function createVinnyDisplayBikes()
|
||
|
|
lib.requestModel(`sanchez`)
|
||
|
|
local price = exports.qbx_core:GetVehiclesByName().sanchez.price
|
||
|
|
|
||
|
|
for i, coords in ipairs(vinnyDisplayPositions) do
|
||
|
|
if not DoesEntityExist(vinnyDisplayBikes[i]) then
|
||
|
|
local bike = CreateVehicle(
|
||
|
|
`sanchez`, coords.x, coords.y, coords.z, coords.w,
|
||
|
|
false, false
|
||
|
|
)
|
||
|
|
|
||
|
|
if DoesEntityExist(bike) then
|
||
|
|
SetEntityAsMissionEntity(bike, true, true)
|
||
|
|
SetVehicleOnGroundProperly(bike)
|
||
|
|
SetVehicleDoorsLocked(bike, 2)
|
||
|
|
SetEntityInvincible(bike, true)
|
||
|
|
FreezeEntityPosition(bike, true)
|
||
|
|
|
||
|
|
exports.ox_target:addLocalEntity(bike, {
|
||
|
|
{
|
||
|
|
name = 'how_progression_vinny_buy_' .. i,
|
||
|
|
icon = 'fa-solid fa-motorcycle',
|
||
|
|
label = ('Buy Sanchez ($%s)'):format(
|
||
|
|
lib.math.groupdigits(price)
|
||
|
|
),
|
||
|
|
distance = 2.0,
|
||
|
|
onSelect = function()
|
||
|
|
TriggerServerEvent(
|
||
|
|
'qbx_vehicleshop:server:buyShowroomVehicle',
|
||
|
|
'sanchez'
|
||
|
|
)
|
||
|
|
end
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
vinnyDisplayBikes[i] = bike
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
SetModelAsNoLongerNeeded(`sanchez`)
|
||
|
|
end
|
||
|
|
|
||
|
|
local function createVinny()
|
||
|
|
if DoesEntityExist(vinnyPed) then
|
||
|
|
createVinnyDisplayBikes()
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Generic salesman/businessman. Easy to change after we see him.
|
||
|
|
local model = `a_m_m_business_01`
|
||
|
|
lib.requestModel(model)
|
||
|
|
|
||
|
|
vinnyPed = CreatePed(
|
||
|
|
0,
|
||
|
|
model,
|
||
|
|
vinnyCoords.x,
|
||
|
|
vinnyCoords.y,
|
||
|
|
vinnyCoords.z - 1.0,
|
||
|
|
vinnyCoords.w,
|
||
|
|
false,
|
||
|
|
false
|
||
|
|
)
|
||
|
|
|
||
|
|
SetPedDefaultComponentVariation(vinnyPed)
|
||
|
|
SetEntityAsMissionEntity(vinnyPed, true, true)
|
||
|
|
SetEntityInvincible(vinnyPed, true)
|
||
|
|
FreezeEntityPosition(vinnyPed, true)
|
||
|
|
SetBlockingOfNonTemporaryEvents(vinnyPed, true)
|
||
|
|
|
||
|
|
TaskStartScenarioInPlace(
|
||
|
|
vinnyPed,
|
||
|
|
'WORLD_HUMAN_STAND_IMPATIENT',
|
||
|
|
0,
|
||
|
|
true
|
||
|
|
)
|
||
|
|
|
||
|
|
exports.ox_target:addLocalEntity(vinnyPed, {
|
||
|
|
{
|
||
|
|
name = 'how_progression_vinny_talk',
|
||
|
|
icon = 'fa-solid fa-comment',
|
||
|
|
label = 'Talk to Vinny',
|
||
|
|
distance = 2.0,
|
||
|
|
|
||
|
|
canInteract = function()
|
||
|
|
return mackMission1Objective == 'talk_vinny'
|
||
|
|
end,
|
||
|
|
|
||
|
|
onSelect = function()
|
||
|
|
lib.alertDialog({
|
||
|
|
header = 'Vinny',
|
||
|
|
content =
|
||
|
|
"Yeah, thanks, thanks. Now get outta here. " ..
|
||
|
|
"I see a sucker ready to buy.\n\n" ..
|
||
|
|
"Give me your number and I'll text ya.\n\n" ..
|
||
|
|
"I promise you're gonna like this.",
|
||
|
|
centered = true
|
||
|
|
})
|
||
|
|
|
||
|
|
TriggerServerEvent(
|
||
|
|
'how_progression:server:completeMackMission1'
|
||
|
|
)
|
||
|
|
end
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
SetModelAsNoLongerNeeded(model)
|
||
|
|
createVinnyDisplayBikes()
|
||
|
|
end
|
||
|
|
|
||
|
|
local function createVinnyBlip()
|
||
|
|
if DoesBlipExist(vinnyBlip) then return end
|
||
|
|
|
||
|
|
vinnyBlip = AddBlipForCoord(
|
||
|
|
vinnyCoords.x,
|
||
|
|
vinnyCoords.y,
|
||
|
|
vinnyCoords.z
|
||
|
|
)
|
||
|
|
|
||
|
|
SetBlipSprite(vinnyBlip, 226)
|
||
|
|
SetBlipScale(vinnyBlip, 0.7)
|
||
|
|
SetBlipAsShortRange(vinnyBlip, true)
|
||
|
|
|
||
|
|
BeginTextCommandSetBlipName('STRING')
|
||
|
|
AddTextComponentString('Vinny')
|
||
|
|
EndTextCommandSetBlipName(vinnyBlip)
|
||
|
|
end
|
||
|
|
|
||
|
|
unlockVinnyWorld = function()
|
||
|
|
vinnyFenceUnlocked = true
|
||
|
|
mackMission1DeliveryMarkerActive = false
|
||
|
|
|
||
|
|
createVinny()
|
||
|
|
createVinnyBlip()
|
||
|
|
end
|
||
|
|
|
||
|
|
AddEventHandler('QBCore:Client:OnPlayerUnload', function()
|
||
|
|
vinnyFenceUnlocked = false
|
||
|
|
mackMission1DeliveryMarkerActive = false
|
||
|
|
|
||
|
|
if DoesEntityExist(vinnyPed) then
|
||
|
|
exports.ox_target:removeLocalEntity(vinnyPed, 'how_progression_vinny_talk')
|
||
|
|
DeleteEntity(vinnyPed)
|
||
|
|
end
|
||
|
|
vinnyPed = nil
|
||
|
|
|
||
|
|
if DoesBlipExist(vinnyBlip) then RemoveBlip(vinnyBlip) end
|
||
|
|
vinnyBlip = nil
|
||
|
|
|
||
|
|
for i, bike in ipairs(vinnyDisplayBikes) do
|
||
|
|
if DoesEntityExist(bike) then
|
||
|
|
exports.ox_target:removeLocalEntity(
|
||
|
|
bike, 'how_progression_vinny_buy_' .. i
|
||
|
|
)
|
||
|
|
DeleteVehicle(bike)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
vinnyDisplayBikes = {}
|
||
|
|
end)
|
||
|
|
|
||
|
|
RegisterNetEvent(
|
||
|
|
'how_progression:client:startMackMission1',
|
||
|
|
function(isOwner, bikeCount)
|
||
|
|
mackMission1Active = true
|
||
|
|
mackMission1Objective = 'leave_scrapyard'
|
||
|
|
mackMission1Delivered = {}
|
||
|
|
mackMission1DetourSent = false
|
||
|
|
mackMission1TrashReported = false
|
||
|
|
mackMission1HeadingToVinny = false
|
||
|
|
mackMission1DetourExplained = false
|
||
|
|
mackMission1DeliveryMarkerActive = false
|
||
|
|
mackMission1Owner = isOwner == true
|
||
|
|
mackMission1BikeCount = math.min(
|
||
|
|
math.max(tonumber(bikeCount) or 1, 1),
|
||
|
|
5
|
||
|
|
)
|
||
|
|
|
||
|
|
if mackMission1Owner then
|
||
|
|
spawnMackMission1Bikes()
|
||
|
|
end
|
||
|
|
|
||
|
|
createVinny()
|
||
|
|
|
||
|
|
setMackMission1Route(
|
||
|
|
vec3(
|
||
|
|
mackMission1BikeSpawns[1].x,
|
||
|
|
mackMission1BikeSpawns[1].y,
|
||
|
|
mackMission1BikeSpawns[1].z
|
||
|
|
),
|
||
|
|
'Pick Up the Bikes'
|
||
|
|
)
|
||
|
|
|
||
|
|
lib.notify({
|
||
|
|
title = 'Easy Rider',
|
||
|
|
description = (
|
||
|
|
'Pick up the Sanchez%s behind Mack. (%d total)'
|
||
|
|
):format(
|
||
|
|
mackMission1BikeCount == 1 and '' or 'es',
|
||
|
|
mackMission1BikeCount
|
||
|
|
),
|
||
|
|
type = 'inform'
|
||
|
|
})
|
||
|
|
end
|
||
|
|
)
|
||
|
|
|
||
|
|
RegisterNetEvent(
|
||
|
|
'how_progression:client:mackMission1TrashCar',
|
||
|
|
function(isOwner)
|
||
|
|
if not mackMission1Active then return end
|
||
|
|
|
||
|
|
mackMission1Objective = 'trash_car'
|
||
|
|
|
||
|
|
if isOwner == true then
|
||
|
|
spawnMackMission1TrashCar()
|
||
|
|
end
|
||
|
|
|
||
|
|
setMackMission1Route(
|
||
|
|
mackMission1DetourCoords,
|
||
|
|
"Mack's Detour"
|
||
|
|
)
|
||
|
|
|
||
|
|
lib.notify({
|
||
|
|
title = 'Mack',
|
||
|
|
description = "Read Mack's message.",
|
||
|
|
type = 'inform'
|
||
|
|
})
|
||
|
|
end
|
||
|
|
)
|
||
|
|
|
||
|
|
RegisterNetEvent(
|
||
|
|
'how_progression:client:mackMission1DetourExplained',
|
||
|
|
function()
|
||
|
|
if not mackMission1Active then return end
|
||
|
|
|
||
|
|
mackMission1DetourExplained = true
|
||
|
|
|
||
|
|
local targetCoords = mackMission1TrashCarCoords
|
||
|
|
if DoesEntityExist(mackMission1TrashCar) then
|
||
|
|
targetCoords = GetEntityCoords(mackMission1TrashCar)
|
||
|
|
end
|
||
|
|
|
||
|
|
setMackMission1Route(targetCoords, "Mack's Target")
|
||
|
|
|
||
|
|
lib.notify({
|
||
|
|
title = 'Mack',
|
||
|
|
description = "Read Mack's message.",
|
||
|
|
type = 'inform'
|
||
|
|
})
|
||
|
|
end
|
||
|
|
)
|
||
|
|
|
||
|
|
RegisterNetEvent(
|
||
|
|
'how_progression:client:mackMission1DeliverBikes',
|
||
|
|
function(deliveredCount, bikeCount)
|
||
|
|
if not mackMission1Active then return end
|
||
|
|
|
||
|
|
mackMission1Objective = 'deliver_bikes'
|
||
|
|
mackMission1DeliveryMarkerActive = true
|
||
|
|
mackMission1BikeCount =
|
||
|
|
tonumber(bikeCount) or mackMission1BikeCount
|
||
|
|
|
||
|
|
setMackMission1Route(
|
||
|
|
vinnyMissionDrop,
|
||
|
|
'Deliver the Bikes'
|
||
|
|
)
|
||
|
|
|
||
|
|
lib.notify({
|
||
|
|
title = 'Easy Rider',
|
||
|
|
description = (
|
||
|
|
'Deliver the Sanchez%s to Vinny. (%d/%d)'
|
||
|
|
):format(
|
||
|
|
mackMission1BikeCount == 1 and '' or 'es',
|
||
|
|
deliveredCount or 0,
|
||
|
|
mackMission1BikeCount
|
||
|
|
),
|
||
|
|
type = 'inform'
|
||
|
|
})
|
||
|
|
end
|
||
|
|
)
|
||
|
|
|
||
|
|
RegisterNetEvent(
|
||
|
|
'how_progression:client:mackMission1DeliveryCount',
|
||
|
|
function(count, bikeCount)
|
||
|
|
mackMission1BikeCount =
|
||
|
|
tonumber(bikeCount) or mackMission1BikeCount
|
||
|
|
|
||
|
|
lib.notify({
|
||
|
|
title = 'Easy Rider',
|
||
|
|
description = (
|
||
|
|
'Sanchez%s delivered: %d/%d'
|
||
|
|
):format(
|
||
|
|
mackMission1BikeCount == 1 and '' or 'es',
|
||
|
|
count,
|
||
|
|
mackMission1BikeCount
|
||
|
|
),
|
||
|
|
type = 'success'
|
||
|
|
})
|
||
|
|
end
|
||
|
|
)
|
||
|
|
|
||
|
|
RegisterNetEvent(
|
||
|
|
'how_progression:client:mackMission1TalkVinny',
|
||
|
|
function()
|
||
|
|
mackMission1Objective = 'talk_vinny'
|
||
|
|
mackMission1DeliveryMarkerActive = false
|
||
|
|
|
||
|
|
clearMackMission1Blip()
|
||
|
|
|
||
|
|
setMackMission1Route(
|
||
|
|
vec3(
|
||
|
|
vinnyCoords.x,
|
||
|
|
vinnyCoords.y,
|
||
|
|
vinnyCoords.z
|
||
|
|
),
|
||
|
|
'Vinny'
|
||
|
|
)
|
||
|
|
|
||
|
|
lib.notify({
|
||
|
|
title = 'Easy Rider',
|
||
|
|
description = 'Talk to Vinny.',
|
||
|
|
type = 'inform'
|
||
|
|
})
|
||
|
|
end
|
||
|
|
)
|
||
|
|
|
||
|
|
RegisterNetEvent(
|
||
|
|
'how_progression:client:mackMission1Complete',
|
||
|
|
function()
|
||
|
|
mackMission1Active = false
|
||
|
|
mackMission1Objective = nil
|
||
|
|
mackMission1Owner = false
|
||
|
|
mackMission1DeliveryMarkerActive = false
|
||
|
|
|
||
|
|
ClearGpsPlayerWaypoint()
|
||
|
|
clearMackMission1Blip()
|
||
|
|
|
||
|
|
lib.notify({
|
||
|
|
title = 'Easy Rider',
|
||
|
|
description = 'Job complete.',
|
||
|
|
type = 'success'
|
||
|
|
})
|
||
|
|
end
|
||
|
|
)
|
||
|
|
|
||
|
|
RegisterNetEvent(
|
||
|
|
'how_progression:client:mackReward',
|
||
|
|
function(repReward, newRep, _, cashReward)
|
||
|
|
lib.notify({
|
||
|
|
title = 'Mack',
|
||
|
|
description = (
|
||
|
|
'$%d | +%d REP | Mack REP: %d'
|
||
|
|
):format(
|
||
|
|
cashReward or 0,
|
||
|
|
repReward or 0,
|
||
|
|
newRep or 0
|
||
|
|
),
|
||
|
|
type = 'success'
|
||
|
|
})
|
||
|
|
end
|
||
|
|
)
|
||
|
|
|
||
|
|
RegisterNetEvent(
|
||
|
|
'how_progression:client:unlockVinnyFence',
|
||
|
|
function()
|
||
|
|
unlockVinnyWorld()
|
||
|
|
|
||
|
|
lib.notify({
|
||
|
|
title = 'Sanchez Fence Unlocked',
|
||
|
|
description =
|
||
|
|
'Vinny buys stolen Sanchezes around back.',
|
||
|
|
type = 'success'
|
||
|
|
})
|
||
|
|
end
|
||
|
|
)
|
||
|
|
|
||
|
|
RegisterNetEvent('how_progression:client:leaveFenceBike', function(netId)
|
||
|
|
local ped = PlayerPedId()
|
||
|
|
local vehicle = GetVehiclePedIsIn(ped, false)
|
||
|
|
if vehicle ~= 0 and NetworkGetNetworkIdFromEntity(vehicle) == netId then
|
||
|
|
TaskLeaveVehicle(ped, vehicle, 0)
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
|
||
|
|
CreateThread(function()
|
||
|
|
local lastAttempt = 0
|
||
|
|
|
||
|
|
while true do
|
||
|
|
Wait(vinnyFenceUnlocked and 150 or 1000)
|
||
|
|
|
||
|
|
if vinnyFenceUnlocked and GetGameTimer() - lastAttempt >= 5000 then
|
||
|
|
local ped = PlayerPedId()
|
||
|
|
local vehicle = GetVehiclePedIsIn(ped, false)
|
||
|
|
|
||
|
|
if vehicle ~= 0
|
||
|
|
and GetPedInVehicleSeat(vehicle, -1) == ped
|
||
|
|
and GetEntityModel(vehicle) == `sanchez`
|
||
|
|
and #(GetEntityCoords(vehicle) - vinnyFenceDrop.xyz) <= 2.5
|
||
|
|
then
|
||
|
|
lastAttempt = GetGameTimer()
|
||
|
|
|
||
|
|
local gameDay = GetClockYear() * 10000
|
||
|
|
+ GetClockMonth() * 100
|
||
|
|
+ GetClockDayOfMonth()
|
||
|
|
|
||
|
|
local success, message = lib.callback.await(
|
||
|
|
'how_progression:server:fenceSanchez',
|
||
|
|
false,
|
||
|
|
NetworkGetNetworkIdFromEntity(vehicle),
|
||
|
|
gameDay
|
||
|
|
)
|
||
|
|
|
||
|
|
lib.notify({
|
||
|
|
title = 'Vinny',
|
||
|
|
description = message or 'Vinny could not take that bike.',
|
||
|
|
type = success and 'success' or 'error'
|
||
|
|
})
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
|
||
|
|
-- Draw Vinny's fence marker and the mission-bike delivery circle every frame.
|
||
|
|
CreateThread(function()
|
||
|
|
while true do
|
||
|
|
Wait(0)
|
||
|
|
|
||
|
|
if vinnyFenceUnlocked then
|
||
|
|
DrawMarker(
|
||
|
|
1,
|
||
|
|
vinnyFenceDrop.x,
|
||
|
|
vinnyFenceDrop.y,
|
||
|
|
vinnyFenceDrop.z - 1.0,
|
||
|
|
0.0, 0.0, 0.0,
|
||
|
|
0.0, 0.0, vinnyFenceDrop.w,
|
||
|
|
3.0, 3.0, 0.5,
|
||
|
|
255, 255, 255, 150,
|
||
|
|
false,
|
||
|
|
false,
|
||
|
|
2,
|
||
|
|
false,
|
||
|
|
nil,
|
||
|
|
nil,
|
||
|
|
false
|
||
|
|
)
|
||
|
|
end
|
||
|
|
|
||
|
|
if mackMission1DeliveryMarkerActive
|
||
|
|
and mackMission1Objective == 'deliver_bikes'
|
||
|
|
then
|
||
|
|
DrawMarker(
|
||
|
|
1,
|
||
|
|
vinnyMissionDrop.x,
|
||
|
|
vinnyMissionDrop.y,
|
||
|
|
vinnyMissionDrop.z - 1.0,
|
||
|
|
0.0, 0.0, 0.0,
|
||
|
|
0.0, 0.0, 0.0,
|
||
|
|
4.5, 4.5, 1.0,
|
||
|
|
255, 255, 255, 160,
|
||
|
|
false,
|
||
|
|
false,
|
||
|
|
2,
|
||
|
|
false,
|
||
|
|
nil,
|
||
|
|
nil,
|
||
|
|
false
|
||
|
|
)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
|
||
|
|
-- Mission monitoring.
|
||
|
|
CreateThread(function()
|
||
|
|
while true do
|
||
|
|
if not mackMission1Active then
|
||
|
|
Wait(1000)
|
||
|
|
else
|
||
|
|
Wait(100)
|
||
|
|
|
||
|
|
local ped = PlayerPedId()
|
||
|
|
local playerCoords = GetEntityCoords(ped)
|
||
|
|
|
||
|
|
-- Riding a mission Sanchez makes Vinny the main destination.
|
||
|
|
if mackMission1Objective == 'leave_scrapyard'
|
||
|
|
and not mackMission1HeadingToVinny
|
||
|
|
and IsPedInAnyVehicle(ped, false)
|
||
|
|
then
|
||
|
|
local vehicle = GetVehiclePedIsIn(ped, false)
|
||
|
|
|
||
|
|
if GetPedInVehicleSeat(vehicle, -1) == ped then
|
||
|
|
local missionBike =
|
||
|
|
Entity(vehicle).state.howMackMission1Bike
|
||
|
|
|
||
|
|
if missionBike then
|
||
|
|
mackMission1HeadingToVinny = true
|
||
|
|
|
||
|
|
setMackMission1Route(
|
||
|
|
vinnyMissionDrop,
|
||
|
|
'Deliver the Bikes'
|
||
|
|
)
|
||
|
|
|
||
|
|
lib.notify({
|
||
|
|
title = 'Easy Rider',
|
||
|
|
description = 'Deliver the bike to Vinny.',
|
||
|
|
type = 'inform'
|
||
|
|
})
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Mack interrupts once the crew is underway.
|
||
|
|
if mackMission1Objective == 'leave_scrapyard'
|
||
|
|
and not mackMission1DetourSent
|
||
|
|
then
|
||
|
|
local distance =
|
||
|
|
#(playerCoords - vec3(
|
||
|
|
mackMission1BikeSpawns[1].x,
|
||
|
|
mackMission1BikeSpawns[1].y,
|
||
|
|
mackMission1BikeSpawns[1].z
|
||
|
|
))
|
||
|
|
|
||
|
|
if distance >= 175.0 then
|
||
|
|
mackMission1DetourSent = true
|
||
|
|
|
||
|
|
TriggerServerEvent(
|
||
|
|
'how_progression:server:mackMission1Detour'
|
||
|
|
)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Reaching Mack's address triggers the actual explanation.
|
||
|
|
if mackMission1Objective == 'trash_car'
|
||
|
|
and not mackMission1DetourExplained
|
||
|
|
then
|
||
|
|
local distance =
|
||
|
|
#(playerCoords - mackMission1DetourCoords)
|
||
|
|
|
||
|
|
if distance <= 35.0 then
|
||
|
|
mackMission1DetourExplained = true
|
||
|
|
|
||
|
|
TriggerServerEvent(
|
||
|
|
'how_progression:server:mackMission1AtDetour'
|
||
|
|
)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
-- The car must now be genuinely trashed.
|
||
|
|
--
|
||
|
|
-- Body health must be heavily reduced AND the engine must
|
||
|
|
-- have taken meaningful damage. A few bumps won't do it.
|
||
|
|
if mackMission1Owner
|
||
|
|
and mackMission1Objective == 'trash_car'
|
||
|
|
and mackMission1DetourExplained
|
||
|
|
and DoesEntityExist(mackMission1TrashCar)
|
||
|
|
and not mackMission1TrashReported
|
||
|
|
then
|
||
|
|
local bodyHealth =
|
||
|
|
GetVehicleBodyHealth(mackMission1TrashCar)
|
||
|
|
|
||
|
|
local engineHealth =
|
||
|
|
GetVehicleEngineHealth(mackMission1TrashCar)
|
||
|
|
|
||
|
|
if bodyHealth <= 250.0
|
||
|
|
and engineHealth <= 650.0
|
||
|
|
then
|
||
|
|
mackMission1TrashReported = true
|
||
|
|
|
||
|
|
TriggerServerEvent(
|
||
|
|
'how_progression:server:mackMission1CarTrashed'
|
||
|
|
)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
if mackMission1DeliveryMarkerActive
|
||
|
|
and mackMission1Objective == 'deliver_bikes'
|
||
|
|
then
|
||
|
|
-- Owner is authoritative for mission-bike delivery.
|
||
|
|
if mackMission1Owner
|
||
|
|
and IsPedInAnyVehicle(ped, false)
|
||
|
|
then
|
||
|
|
local vehicle =
|
||
|
|
GetVehiclePedIsIn(ped, false)
|
||
|
|
|
||
|
|
if GetPedInVehicleSeat(vehicle, -1) == ped then
|
||
|
|
local bikeIndex =
|
||
|
|
Entity(vehicle).state.howMackMission1Bike
|
||
|
|
|
||
|
|
if bikeIndex
|
||
|
|
and not mackMission1Delivered[bikeIndex]
|
||
|
|
then
|
||
|
|
local vehicleCoords =
|
||
|
|
GetEntityCoords(vehicle)
|
||
|
|
|
||
|
|
local distance =
|
||
|
|
#(vehicleCoords - vinnyMissionDrop)
|
||
|
|
|
||
|
|
if distance <= 3.0 then
|
||
|
|
mackMission1Delivered[bikeIndex] = true
|
||
|
|
|
||
|
|
TaskLeaveVehicle(
|
||
|
|
ped,
|
||
|
|
vehicle,
|
||
|
|
0
|
||
|
|
)
|
||
|
|
|
||
|
|
local timeout =
|
||
|
|
GetGameTimer() + 3000
|
||
|
|
|
||
|
|
while IsPedInVehicle(
|
||
|
|
ped,
|
||
|
|
vehicle,
|
||
|
|
false
|
||
|
|
) and GetGameTimer() < timeout do
|
||
|
|
Wait(50)
|
||
|
|
end
|
||
|
|
|
||
|
|
Wait(500)
|
||
|
|
|
||
|
|
if DoesEntityExist(vehicle) then
|
||
|
|
SetEntityAsMissionEntity(
|
||
|
|
vehicle,
|
||
|
|
true,
|
||
|
|
true
|
||
|
|
)
|
||
|
|
DeleteVehicle(vehicle)
|
||
|
|
end
|
||
|
|
|
||
|
|
TriggerServerEvent(
|
||
|
|
'how_progression:server:mackMission1BikeDelivered',
|
||
|
|
bikeIndex
|
||
|
|
)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
|
||
|
|
-- ============================================================
|
||
|
|
-- Crowbar Mission 4: Wash the Cash
|
||
|
|
-- ============================================================
|
||
|
|
|
||
|
|
local gabeCoords = vec3(2340.96, 3126.11, 48.21)
|
||
|
|
|
||
|
|
createMackBlip = function()
|
||
|
|
if DoesBlipExist(mackBlip) then return end
|
||
|
|
|
||
|
|
mackBlip = AddBlipForCoord(
|
||
|
|
mackCoords.x,
|
||
|
|
mackCoords.y,
|
||
|
|
mackCoords.z
|
||
|
|
)
|
||
|
|
|
||
|
|
SetBlipSprite(mackBlip, 280)
|
||
|
|
SetBlipScale(mackBlip, 0.75)
|
||
|
|
SetBlipAsShortRange(mackBlip, false)
|
||
|
|
|
||
|
|
BeginTextCommandSetBlipName('STRING')
|
||
|
|
AddTextComponentString('Mack')
|
||
|
|
EndTextCommandSetBlipName(mackBlip)
|
||
|
|
end
|
||
|
|
|
||
|
|
local function createGabeBlip()
|
||
|
|
if DoesBlipExist(gabeBlip) then return end
|
||
|
|
|
||
|
|
gabeBlip = AddBlipForCoord(
|
||
|
|
gabeCoords.x,
|
||
|
|
gabeCoords.y,
|
||
|
|
gabeCoords.z
|
||
|
|
)
|
||
|
|
|
||
|
|
SetBlipSprite(gabeBlip, 500)
|
||
|
|
SetBlipDisplay(gabeBlip, 4)
|
||
|
|
SetBlipScale(gabeBlip, 0.7)
|
||
|
|
SetBlipAsShortRange(gabeBlip, true)
|
||
|
|
|
||
|
|
BeginTextCommandSetBlipName('STRING')
|
||
|
|
AddTextComponentString('Money Laundering')
|
||
|
|
EndTextCommandSetBlipName(gabeBlip)
|
||
|
|
end
|
||
|
|
|
||
|
|
RegisterNetEvent(
|
||
|
|
'how_progression:client:startCrowbarMission4',
|
||
|
|
function()
|
||
|
|
createMackBlip()
|
||
|
|
SetNewWaypoint(mackCoords.x, mackCoords.y)
|
||
|
|
|
||
|
|
lib.notify({
|
||
|
|
title = 'Wash the Cash',
|
||
|
|
description = 'Go meet Mack at the scrapyard.',
|
||
|
|
type = 'inform'
|
||
|
|
})
|
||
|
|
end
|
||
|
|
)
|
||
|
|
|
||
|
|
RegisterNetEvent(
|
||
|
|
'how_progression:client:crowbarMission4Mack',
|
||
|
|
function()
|
||
|
|
ClearGpsPlayerWaypoint()
|
||
|
|
createMackBlip()
|
||
|
|
createGabeBlip()
|
||
|
|
SetNewWaypoint(gabeCoords.x, gabeCoords.y)
|
||
|
|
end
|
||
|
|
)
|
||
|
|
|
||
|
|
RegisterNetEvent(
|
||
|
|
'how_progression:client:crowbarMission4Gabe',
|
||
|
|
function()
|
||
|
|
ClearGpsPlayerWaypoint()
|
||
|
|
createGabeBlip()
|
||
|
|
end
|
||
|
|
)
|
||
|
|
|
||
|
|
RegisterNetEvent(
|
||
|
|
'how_progression:client:unlockMack',
|
||
|
|
function(setRoute)
|
||
|
|
createMack()
|
||
|
|
createMackBlip()
|
||
|
|
|
||
|
|
if setRoute then
|
||
|
|
SetNewWaypoint(mackCoords.x, mackCoords.y)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
)
|
||
|
|
|
||
|
|
RegisterNetEvent(
|
||
|
|
'how_progression:client:unlockGabe',
|
||
|
|
function(setRoute)
|
||
|
|
createGabeBlip()
|
||
|
|
|
||
|
|
if setRoute then
|
||
|
|
SetNewWaypoint(gabeCoords.x, gabeCoords.y)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
)
|
||
|
|
|
||
|
|
RegisterNetEvent(
|
||
|
|
'how_progression:client:cleanupCrowbarMission4',
|
||
|
|
function()
|
||
|
|
ClearGpsPlayerWaypoint()
|
||
|
|
createMackBlip()
|
||
|
|
createGabeBlip()
|
||
|
|
end
|
||
|
|
)
|
||
|
|
|
||
|
|
-- ============================================================
|
||
|
|
-- Crowbar Mission 5: Buy In
|
||
|
|
-- ============================================================
|
||
|
|
|
||
|
|
local crowbarMission5Drop =
|
||
|
|
vec3(1537.56, 1700.49, 109.68)
|
||
|
|
|
||
|
|
local crowbarMission5DropBlip
|
||
|
|
local crowbarMission5ReturnBlip
|
||
|
|
local crowbarMission5Active = false
|
||
|
|
local crowbarMission5CanReturn = false
|
||
|
|
local crowbarMission5StashId
|
||
|
|
local crowbarMission5Crate
|
||
|
|
|
||
|
|
local function clearCrowbarMission5Blips()
|
||
|
|
if DoesBlipExist(crowbarMission5DropBlip) then
|
||
|
|
RemoveBlip(crowbarMission5DropBlip)
|
||
|
|
crowbarMission5DropBlip = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
if DoesBlipExist(crowbarMission5ReturnBlip) then
|
||
|
|
RemoveBlip(crowbarMission5ReturnBlip)
|
||
|
|
crowbarMission5ReturnBlip = nil
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local function createCrowbarMission5DropBlip()
|
||
|
|
if DoesBlipExist(crowbarMission5DropBlip) then return end
|
||
|
|
|
||
|
|
crowbarMission5DropBlip = AddBlipForCoord(
|
||
|
|
crowbarMission5Drop.x,
|
||
|
|
crowbarMission5Drop.y,
|
||
|
|
crowbarMission5Drop.z
|
||
|
|
)
|
||
|
|
|
||
|
|
SetBlipSprite(crowbarMission5DropBlip, 478)
|
||
|
|
SetBlipScale(crowbarMission5DropBlip, 0.75)
|
||
|
|
SetBlipRoute(crowbarMission5DropBlip, true)
|
||
|
|
|
||
|
|
BeginTextCommandSetBlipName('STRING')
|
||
|
|
AddTextComponentString('Package Pickup')
|
||
|
|
EndTextCommandSetBlipName(crowbarMission5DropBlip)
|
||
|
|
end
|
||
|
|
|
||
|
|
local function createCrowbarMission5ReturnBlip()
|
||
|
|
if DoesBlipExist(crowbarMission5ReturnBlip) then return end
|
||
|
|
|
||
|
|
crowbarMission5ReturnBlip = AddBlipForCoord(
|
||
|
|
crowbarCoords.x,
|
||
|
|
crowbarCoords.y,
|
||
|
|
crowbarCoords.z
|
||
|
|
)
|
||
|
|
|
||
|
|
SetBlipSprite(crowbarMission5ReturnBlip, 280)
|
||
|
|
SetBlipScale(crowbarMission5ReturnBlip, 0.75)
|
||
|
|
SetBlipRoute(crowbarMission5ReturnBlip, true)
|
||
|
|
|
||
|
|
BeginTextCommandSetBlipName('STRING')
|
||
|
|
AddTextComponentString('Crowbar')
|
||
|
|
EndTextCommandSetBlipName(crowbarMission5ReturnBlip)
|
||
|
|
end
|
||
|
|
|
||
|
|
local function armCrowbarMission5Ambusher(ped)
|
||
|
|
SetEntityAsMissionEntity(ped, true, true)
|
||
|
|
|
||
|
|
SetPedRelationshipGroupHash(
|
||
|
|
ped,
|
||
|
|
`HOW_CROWBAR_AMBUSH`
|
||
|
|
)
|
||
|
|
|
||
|
|
GiveWeaponToPed(
|
||
|
|
ped,
|
||
|
|
`WEAPON_PISTOL`,
|
||
|
|
90,
|
||
|
|
false,
|
||
|
|
true
|
||
|
|
)
|
||
|
|
|
||
|
|
SetPedAccuracy(ped, 18)
|
||
|
|
SetPedCombatAbility(ped, 1)
|
||
|
|
SetPedCombatRange(ped, 1)
|
||
|
|
SetPedCombatMovement(ped, 2)
|
||
|
|
SetPedKeepTask(ped, true)
|
||
|
|
end
|
||
|
|
|
||
|
|
local crowbarMission5Ambushers = {}
|
||
|
|
local crowbarMission5EscapeActive = false
|
||
|
|
local crowbarMission5EscapeReported = false
|
||
|
|
|
||
|
|
local function attackCrowbarMission5Crew(ped)
|
||
|
|
TaskCombatHatedTargetsAroundPed(
|
||
|
|
ped,
|
||
|
|
150.0,
|
||
|
|
0
|
||
|
|
)
|
||
|
|
end
|
||
|
|
|
||
|
|
local function createCrowbarMission5AmbushVehicle(data)
|
||
|
|
local vehicleModel = joaat(data.vehicle)
|
||
|
|
|
||
|
|
lib.requestModel(vehicleModel)
|
||
|
|
|
||
|
|
local vehicle = CreateVehicle(
|
||
|
|
vehicleModel,
|
||
|
|
data.spawn.x,
|
||
|
|
data.spawn.y,
|
||
|
|
data.spawn.z,
|
||
|
|
data.spawn.w,
|
||
|
|
true,
|
||
|
|
true
|
||
|
|
)
|
||
|
|
|
||
|
|
if not DoesEntityExist(vehicle) then
|
||
|
|
print(
|
||
|
|
'[how_progression] Failed to create M5 ambush vehicle: '
|
||
|
|
.. data.vehicle
|
||
|
|
)
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
SetEntityAsMissionEntity(vehicle, true, true)
|
||
|
|
SetVehicleOnGroundProperly(vehicle)
|
||
|
|
SetVehicleEngineOn(vehicle, true, true, false)
|
||
|
|
|
||
|
|
local occupants = {}
|
||
|
|
|
||
|
|
for seat = -1, 1 do
|
||
|
|
local model = data.peds[seat + 2]
|
||
|
|
|
||
|
|
lib.requestModel(model)
|
||
|
|
|
||
|
|
local ped = CreatePedInsideVehicle(
|
||
|
|
vehicle,
|
||
|
|
4,
|
||
|
|
model,
|
||
|
|
seat,
|
||
|
|
true,
|
||
|
|
true
|
||
|
|
)
|
||
|
|
|
||
|
|
if DoesEntityExist(ped) then
|
||
|
|
armCrowbarMission5Ambusher(ped)
|
||
|
|
occupants[#occupants + 1] = ped
|
||
|
|
crowbarMission5Ambushers[#crowbarMission5Ambushers + 1] = ped
|
||
|
|
end
|
||
|
|
|
||
|
|
SetModelAsNoLongerNeeded(model)
|
||
|
|
end
|
||
|
|
|
||
|
|
local driver = occupants[1]
|
||
|
|
|
||
|
|
if not DoesEntityExist(driver) then
|
||
|
|
DeleteEntity(vehicle)
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
SetDriverAbility(driver, 1.0)
|
||
|
|
SetDriverAggressiveness(driver, 1.0)
|
||
|
|
|
||
|
|
CreateThread(function()
|
||
|
|
local target = PlayerPedId()
|
||
|
|
local lastTask = 0
|
||
|
|
local onFootAttack = false
|
||
|
|
|
||
|
|
while crowbarMission5Active
|
||
|
|
and crowbarMission5EscapeActive
|
||
|
|
and DoesEntityExist(vehicle)
|
||
|
|
and DoesEntityExist(driver)
|
||
|
|
and not IsEntityDead(driver)
|
||
|
|
and DoesEntityExist(target)
|
||
|
|
and not IsEntityDead(target)
|
||
|
|
do
|
||
|
|
local vehicleCoords = GetEntityCoords(vehicle)
|
||
|
|
local targetCoords = GetEntityCoords(target)
|
||
|
|
local distance = #(vehicleCoords - targetCoords)
|
||
|
|
|
||
|
|
--
|
||
|
|
-- Stay in the truck and pursue while the carrier is
|
||
|
|
-- moving away or still some distance from the Lost.
|
||
|
|
--
|
||
|
|
if distance > 30.0 and not onFootAttack then
|
||
|
|
if GetGameTimer() - lastTask > 2500 then
|
||
|
|
TaskVehicleChase(driver, target)
|
||
|
|
|
||
|
|
SetDriveTaskDrivingStyle(
|
||
|
|
driver,
|
||
|
|
786603
|
||
|
|
)
|
||
|
|
|
||
|
|
SetDriveTaskCruiseSpeed(
|
||
|
|
driver,
|
||
|
|
35.0
|
||
|
|
)
|
||
|
|
|
||
|
|
lastTask = GetGameTimer()
|
||
|
|
end
|
||
|
|
|
||
|
|
--
|
||
|
|
-- Once they actually catch the carrier, pile out and fight.
|
||
|
|
--
|
||
|
|
elseif distance <= 30.0 and not onFootAttack then
|
||
|
|
onFootAttack = true
|
||
|
|
|
||
|
|
BringVehicleToHalt(
|
||
|
|
vehicle,
|
||
|
|
4.0,
|
||
|
|
1000,
|
||
|
|
false
|
||
|
|
)
|
||
|
|
|
||
|
|
Wait(750)
|
||
|
|
|
||
|
|
for _, ped in ipairs(occupants) do
|
||
|
|
if DoesEntityExist(ped)
|
||
|
|
and not IsEntityDead(ped)
|
||
|
|
then
|
||
|
|
TaskLeaveVehicle(
|
||
|
|
ped,
|
||
|
|
vehicle,
|
||
|
|
256
|
||
|
|
)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
Wait(1200)
|
||
|
|
|
||
|
|
for _, ped in ipairs(occupants) do
|
||
|
|
if DoesEntityExist(ped)
|
||
|
|
and not IsEntityDead(ped)
|
||
|
|
then
|
||
|
|
TaskCombatPed(
|
||
|
|
ped,
|
||
|
|
target,
|
||
|
|
0,
|
||
|
|
16
|
||
|
|
)
|
||
|
|
|
||
|
|
SetPedKeepTask(ped, true)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
--
|
||
|
|
-- If the carrier runs after the Lost have dismounted,
|
||
|
|
-- keep the surviving attackers actively hunting them.
|
||
|
|
--
|
||
|
|
elseif onFootAttack then
|
||
|
|
for _, ped in ipairs(occupants) do
|
||
|
|
if DoesEntityExist(ped)
|
||
|
|
and not IsEntityDead(ped)
|
||
|
|
and not IsPedInCombat(ped, target)
|
||
|
|
then
|
||
|
|
TaskCombatPed(
|
||
|
|
ped,
|
||
|
|
target,
|
||
|
|
0,
|
||
|
|
16
|
||
|
|
)
|
||
|
|
|
||
|
|
SetPedKeepTask(ped, true)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
Wait(500)
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
|
||
|
|
SetModelAsNoLongerNeeded(vehicleModel)
|
||
|
|
end
|
||
|
|
|
||
|
|
local function spawnCrowbarMission5Ambush()
|
||
|
|
AddRelationshipGroup('HOW_CROWBAR_AMBUSH')
|
||
|
|
|
||
|
|
SetRelationshipBetweenGroups(
|
||
|
|
5,
|
||
|
|
`HOW_CROWBAR_AMBUSH`,
|
||
|
|
`PLAYER`
|
||
|
|
)
|
||
|
|
|
||
|
|
SetRelationshipBetweenGroups(
|
||
|
|
5,
|
||
|
|
`PLAYER`,
|
||
|
|
`HOW_CROWBAR_AMBUSH`
|
||
|
|
)
|
||
|
|
|
||
|
|
lib.notify({
|
||
|
|
title = 'Buy In',
|
||
|
|
description =
|
||
|
|
"Looks like somebody else wanted Crowbar's package.",
|
||
|
|
type = 'error'
|
||
|
|
})
|
||
|
|
|
||
|
|
CreateThread(function()
|
||
|
|
-- Lead truck
|
||
|
|
createCrowbarMission5AmbushVehicle({
|
||
|
|
vehicle = 'rebel',
|
||
|
|
|
||
|
|
spawn = vec4(
|
||
|
|
1464.0,
|
||
|
|
1760.0,
|
||
|
|
108.0,
|
||
|
|
130.0
|
||
|
|
),
|
||
|
|
|
||
|
|
destination = vec3(
|
||
|
|
1521.0,
|
||
|
|
1715.0,
|
||
|
|
109.0
|
||
|
|
),
|
||
|
|
|
||
|
|
peds = {
|
||
|
|
`g_m_y_lost_01`,
|
||
|
|
`g_m_y_lost_02`,
|
||
|
|
`g_m_y_lost_03`
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
-- Second truck follows the same reliable approach.
|
||
|
|
createCrowbarMission5AmbushVehicle({
|
||
|
|
vehicle = 'bodhi2',
|
||
|
|
|
||
|
|
spawn = vec4(
|
||
|
|
1448.0,
|
||
|
|
1774.0,
|
||
|
|
108.0,
|
||
|
|
130.0
|
||
|
|
),
|
||
|
|
|
||
|
|
destination = vec3(
|
||
|
|
1513.0,
|
||
|
|
1723.0,
|
||
|
|
109.0
|
||
|
|
),
|
||
|
|
|
||
|
|
peds = {
|
||
|
|
`g_m_y_lost_02`,
|
||
|
|
`g_m_y_lost_03`,
|
||
|
|
`g_m_y_lost_01`
|
||
|
|
}
|
||
|
|
})
|
||
|
|
end)
|
||
|
|
end
|
||
|
|
|
||
|
|
RegisterNetEvent(
|
||
|
|
'how_progression:client:startCrowbarMission5',
|
||
|
|
function(stashId)
|
||
|
|
crowbarMission5Active = true
|
||
|
|
crowbarMission5CanReturn = false
|
||
|
|
crowbarMission5StashId = stashId
|
||
|
|
|
||
|
|
clearCrowbarMission5Blips()
|
||
|
|
createCrowbarMission5DropBlip()
|
||
|
|
|
||
|
|
SetNewWaypoint(
|
||
|
|
crowbarMission5Drop.x,
|
||
|
|
crowbarMission5Drop.y
|
||
|
|
)
|
||
|
|
|
||
|
|
lib.notify({
|
||
|
|
title = 'Buy In',
|
||
|
|
description = 'Find the package at the desert shed.',
|
||
|
|
type = 'inform'
|
||
|
|
})
|
||
|
|
end
|
||
|
|
)
|
||
|
|
|
||
|
|
RegisterNetEvent(
|
||
|
|
'how_progression:client:crowbarMission5PackageTaken',
|
||
|
|
function(spawnAmbush)
|
||
|
|
crowbarMission5CanReturn = false
|
||
|
|
|
||
|
|
if DoesBlipExist(crowbarMission5DropBlip) then
|
||
|
|
RemoveBlip(crowbarMission5DropBlip)
|
||
|
|
crowbarMission5DropBlip = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
ClearGpsPlayerWaypoint()
|
||
|
|
|
||
|
|
if spawnAmbush then
|
||
|
|
crowbarMission5Ambushers = {}
|
||
|
|
crowbarMission5EscapeActive = true
|
||
|
|
crowbarMission5EscapeReported = false
|
||
|
|
|
||
|
|
spawnCrowbarMission5Ambush()
|
||
|
|
|
||
|
|
CreateThread(function()
|
||
|
|
--
|
||
|
|
-- Give both ambush vehicles time to spawn before
|
||
|
|
-- evaluating whether everybody is dead.
|
||
|
|
--
|
||
|
|
Wait(3000)
|
||
|
|
|
||
|
|
local clearSince = nil
|
||
|
|
|
||
|
|
while crowbarMission5Active
|
||
|
|
and crowbarMission5EscapeActive
|
||
|
|
and not crowbarMission5EscapeReported
|
||
|
|
do
|
||
|
|
local playerPed = PlayerPedId()
|
||
|
|
local playerCoords =
|
||
|
|
GetEntityCoords(playerPed)
|
||
|
|
|
||
|
|
local living = 0
|
||
|
|
local allFarEnough = true
|
||
|
|
|
||
|
|
for _, ped in ipairs(crowbarMission5Ambushers) do
|
||
|
|
if DoesEntityExist(ped)
|
||
|
|
and not IsEntityDead(ped)
|
||
|
|
then
|
||
|
|
living = living + 1
|
||
|
|
|
||
|
|
local distance =
|
||
|
|
#(GetEntityCoords(ped) - playerCoords)
|
||
|
|
|
||
|
|
if distance < 250.0 then
|
||
|
|
allFarEnough = false
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
--
|
||
|
|
-- Killing every ambusher resolves the escape
|
||
|
|
-- immediately.
|
||
|
|
--
|
||
|
|
if living == 0
|
||
|
|
and #crowbarMission5Ambushers > 0
|
||
|
|
then
|
||
|
|
crowbarMission5EscapeReported = true
|
||
|
|
|
||
|
|
TriggerServerEvent(
|
||
|
|
'how_progression:server:crowbarMission5Escaped'
|
||
|
|
)
|
||
|
|
|
||
|
|
break
|
||
|
|
end
|
||
|
|
|
||
|
|
--
|
||
|
|
-- Otherwise every surviving pursuer must remain
|
||
|
|
-- at least 250m away for 20 continuous seconds.
|
||
|
|
--
|
||
|
|
if living > 0 and allFarEnough then
|
||
|
|
if not clearSince then
|
||
|
|
clearSince = GetGameTimer()
|
||
|
|
elseif GetGameTimer() - clearSince >= 20000 then
|
||
|
|
crowbarMission5EscapeReported = true
|
||
|
|
|
||
|
|
TriggerServerEvent(
|
||
|
|
'how_progression:server:crowbarMission5Escaped'
|
||
|
|
)
|
||
|
|
|
||
|
|
break
|
||
|
|
end
|
||
|
|
else
|
||
|
|
clearSince = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
Wait(500)
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
)
|
||
|
|
|
||
|
|
RegisterNetEvent(
|
||
|
|
'how_progression:client:crowbarMission5Escaped',
|
||
|
|
function()
|
||
|
|
crowbarMission5EscapeActive = false
|
||
|
|
crowbarMission5CanReturn = true
|
||
|
|
|
||
|
|
lib.notify({
|
||
|
|
title = 'Buy In',
|
||
|
|
description = 'You lost the pursuers.',
|
||
|
|
type = 'success'
|
||
|
|
})
|
||
|
|
|
||
|
|
createCrowbarMission5ReturnBlip()
|
||
|
|
|
||
|
|
SetNewWaypoint(
|
||
|
|
crowbarCoords.x,
|
||
|
|
crowbarCoords.y
|
||
|
|
)
|
||
|
|
end
|
||
|
|
)
|
||
|
|
|
||
|
|
RegisterNetEvent(
|
||
|
|
'how_progression:client:cleanupCrowbarMission5',
|
||
|
|
function()
|
||
|
|
crowbarMission5Active = false
|
||
|
|
crowbarMission5CanReturn = false
|
||
|
|
crowbarMission5EscapeActive = false
|
||
|
|
crowbarMission5EscapeReported = false
|
||
|
|
crowbarMission5StashId = nil
|
||
|
|
crowbarMission5Ambushers = {}
|
||
|
|
|
||
|
|
ClearGpsPlayerWaypoint()
|
||
|
|
clearCrowbarMission5Blips()
|
||
|
|
end
|
||
|
|
)
|
||
|
|
|
||
|
|
RegisterNetEvent(
|
||
|
|
'how_progression:client:crowbarMission5Complete',
|
||
|
|
function()
|
||
|
|
lib.alertDialog({
|
||
|
|
header = 'Crowbar',
|
||
|
|
content = "You got it here. That's all that matters.",
|
||
|
|
centered = true
|
||
|
|
})
|
||
|
|
end
|
||
|
|
)
|
||
|
|
|
||
|
|
local function createCrowbarMission5Crate()
|
||
|
|
if DoesEntityExist(crowbarMission5Crate) then
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
local model = `prop_box_wood02a_pu`
|
||
|
|
lib.requestModel(model)
|
||
|
|
|
||
|
|
-- Visually verified placement for the package crate.
|
||
|
|
local objectZ = 108.6397
|
||
|
|
|
||
|
|
crowbarMission5Crate = CreateObject(
|
||
|
|
model,
|
||
|
|
crowbarMission5Drop.x,
|
||
|
|
crowbarMission5Drop.y,
|
||
|
|
objectZ,
|
||
|
|
false,
|
||
|
|
false,
|
||
|
|
false
|
||
|
|
)
|
||
|
|
|
||
|
|
SetEntityHeading(crowbarMission5Crate, 56.26)
|
||
|
|
FreezeEntityPosition(crowbarMission5Crate, true)
|
||
|
|
SetEntityAsMissionEntity(
|
||
|
|
crowbarMission5Crate,
|
||
|
|
true,
|
||
|
|
true
|
||
|
|
)
|
||
|
|
|
||
|
|
exports.ox_target:addLocalEntity(
|
||
|
|
crowbarMission5Crate,
|
||
|
|
{
|
||
|
|
{
|
||
|
|
name = 'how_progression_crowbar_package',
|
||
|
|
icon = 'fa-solid fa-box',
|
||
|
|
label = 'Search Container',
|
||
|
|
distance = 2.5,
|
||
|
|
|
||
|
|
canInteract = function()
|
||
|
|
return crowbarMission5Active
|
||
|
|
and not crowbarMission5CanReturn
|
||
|
|
and crowbarMission5StashId ~= nil
|
||
|
|
end,
|
||
|
|
|
||
|
|
onSelect = function()
|
||
|
|
exports.ox_inventory:openInventory(
|
||
|
|
'stash',
|
||
|
|
crowbarMission5StashId
|
||
|
|
)
|
||
|
|
end
|
||
|
|
}
|
||
|
|
}
|
||
|
|
)
|
||
|
|
|
||
|
|
SetModelAsNoLongerNeeded(model)
|
||
|
|
end
|
||
|
|
|
||
|
|
CreateThread(function()
|
||
|
|
Wait(2000)
|
||
|
|
createCrowbarMission5Crate()
|
||
|
|
end)
|
||
|
|
|
||
|
|
exports.ox_target:addSphereZone({
|
||
|
|
coords = vec3(
|
||
|
|
crowbarCoords.x,
|
||
|
|
crowbarCoords.y,
|
||
|
|
crowbarCoords.z
|
||
|
|
),
|
||
|
|
radius = 2.0,
|
||
|
|
debug = false,
|
||
|
|
|
||
|
|
options = {
|
||
|
|
{
|
||
|
|
name = 'how_progression_crowbar_package_return',
|
||
|
|
icon = 'fa-solid fa-box',
|
||
|
|
label = 'Give Crowbar the Package',
|
||
|
|
|
||
|
|
canInteract = function()
|
||
|
|
return crowbarMission5Active
|
||
|
|
and crowbarMission5CanReturn
|
||
|
|
end,
|
||
|
|
|
||
|
|
onSelect = function()
|
||
|
|
TriggerServerEvent(
|
||
|
|
'how_progression:server:completeCrowbarMission5'
|
||
|
|
)
|
||
|
|
end
|
||
|
|
}
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
-- ============================================================
|
||
|
|
-- Crowbar Mission 1
|
||
|
|
-- ============================================================
|
||
|
|
|
||
|
|
RegisterNetEvent(
|
||
|
|
'how_progression:client:startCrowbarMission1',
|
||
|
|
function(netId)
|
||
|
|
crowbarMission1Vehicle = netId
|
||
|
|
crowbarMission1ReturnActive = false
|
||
|
|
|
||
|
|
if DoesBlipExist(crowbarMission1Blip) then
|
||
|
|
RemoveBlip(crowbarMission1Blip)
|
||
|
|
crowbarMission1Blip = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
crowbarMission1Blip = AddBlipForCoord(
|
||
|
|
911.40,
|
||
|
|
3589.50,
|
||
|
|
33.25
|
||
|
|
)
|
||
|
|
|
||
|
|
SetBlipSprite(crowbarMission1Blip, 225)
|
||
|
|
SetBlipScale(crowbarMission1Blip, 0.85)
|
||
|
|
SetBlipRoute(crowbarMission1Blip, true)
|
||
|
|
|
||
|
|
BeginTextCommandSetBlipName('STRING')
|
||
|
|
AddTextComponentString("Crowbar's Target")
|
||
|
|
EndTextCommandSetBlipName(crowbarMission1Blip)
|
||
|
|
|
||
|
|
lib.notify({
|
||
|
|
title = 'Crowbar',
|
||
|
|
description = "Take the car and bring it back to Crowbar.",
|
||
|
|
type = 'inform',
|
||
|
|
duration = 5000
|
||
|
|
})
|
||
|
|
end
|
||
|
|
)
|
||
|
|
|
||
|
|
RegisterNetEvent(
|
||
|
|
'how_progression:client:crowbarMission1Return',
|
||
|
|
function()
|
||
|
|
if not crowbarMission1Vehicle then
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
crowbarMission1ReturnActive = true
|
||
|
|
|
||
|
|
if DoesBlipExist(crowbarMission1Blip) then
|
||
|
|
RemoveBlip(crowbarMission1Blip)
|
||
|
|
crowbarMission1Blip = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
crowbarMission1Blip = AddBlipForCoord(
|
||
|
|
1705.50,
|
||
|
|
3771.76,
|
||
|
|
34.49
|
||
|
|
)
|
||
|
|
|
||
|
|
SetBlipSprite(crowbarMission1Blip, 1)
|
||
|
|
SetBlipScale(crowbarMission1Blip, 0.85)
|
||
|
|
SetBlipRoute(crowbarMission1Blip, true)
|
||
|
|
|
||
|
|
BeginTextCommandSetBlipName('STRING')
|
||
|
|
AddTextComponentString("Crowbar's Drop-Off")
|
||
|
|
EndTextCommandSetBlipName(crowbarMission1Blip)
|
||
|
|
|
||
|
|
lib.notify({
|
||
|
|
title = 'Crowbar',
|
||
|
|
description = 'Good. Bring it back to me.',
|
||
|
|
type = 'inform',
|
||
|
|
duration = 5000
|
||
|
|
})
|
||
|
|
end
|
||
|
|
)
|
||
|
|
|
||
|
|
-- Mission 1 state transition:
|
||
|
|
-- Once the player starts the target vehicle, remove the target
|
||
|
|
-- blip and route them back to Crowbar's drop-off.
|
||
|
|
CreateThread(function()
|
||
|
|
while true do
|
||
|
|
Wait(500)
|
||
|
|
|
||
|
|
if crowbarMission1Vehicle
|
||
|
|
and not crowbarMission1ReturnActive
|
||
|
|
and NetworkDoesEntityExistWithNetworkId(
|
||
|
|
crowbarMission1Vehicle
|
||
|
|
) then
|
||
|
|
|
||
|
|
local vehicle =
|
||
|
|
NetToVeh(crowbarMission1Vehicle)
|
||
|
|
|
||
|
|
if DoesEntityExist(vehicle) then
|
||
|
|
local playerPed = PlayerPedId()
|
||
|
|
|
||
|
|
if GetPedInVehicleSeat(
|
||
|
|
vehicle,
|
||
|
|
-1
|
||
|
|
) == playerPed
|
||
|
|
and GetIsVehicleEngineRunning(vehicle) then
|
||
|
|
|
||
|
|
TriggerServerEvent(
|
||
|
|
'how_progression:server:crowbarMission1VehicleAcquired'
|
||
|
|
)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
|
||
|
|
-- Mission 1 completion / return detection.
|
||
|
|
CreateThread(function()
|
||
|
|
while true do
|
||
|
|
Wait(500)
|
||
|
|
|
||
|
|
if crowbarMission1Vehicle
|
||
|
|
and crowbarMission1ReturnActive then
|
||
|
|
|
||
|
|
local vehicle =
|
||
|
|
NetToVeh(crowbarMission1Vehicle)
|
||
|
|
|
||
|
|
if DoesEntityExist(vehicle) then
|
||
|
|
local playerPed = PlayerPedId()
|
||
|
|
|
||
|
|
local vehicleCoords =
|
||
|
|
GetEntityCoords(vehicle)
|
||
|
|
|
||
|
|
local returnCoords =
|
||
|
|
vector3(
|
||
|
|
1705.50,
|
||
|
|
3771.76,
|
||
|
|
34.49
|
||
|
|
)
|
||
|
|
|
||
|
|
if #(vehicleCoords - returnCoords) < 8.0
|
||
|
|
and GetPedInVehicleSeat(
|
||
|
|
vehicle,
|
||
|
|
-1
|
||
|
|
) == playerPed then
|
||
|
|
|
||
|
|
-- Prevent this from firing repeatedly while
|
||
|
|
-- the player is getting out.
|
||
|
|
crowbarMission1ReturnActive = false
|
||
|
|
|
||
|
|
if DoesBlipExist(crowbarMission1Blip) then
|
||
|
|
RemoveBlip(crowbarMission1Blip)
|
||
|
|
crowbarMission1Blip = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Shut the vehicle down and have the player exit.
|
||
|
|
SetVehicleEngineOn(vehicle, false, true, true)
|
||
|
|
SetVehicleHandbrake(vehicle, true)
|
||
|
|
|
||
|
|
TaskLeaveVehicle(
|
||
|
|
playerPed,
|
||
|
|
vehicle,
|
||
|
|
0
|
||
|
|
)
|
||
|
|
|
||
|
|
lib.notify({
|
||
|
|
title = 'Crowbar',
|
||
|
|
description = 'Good. Leave the car here.',
|
||
|
|
type = 'inform',
|
||
|
|
duration = 3000
|
||
|
|
})
|
||
|
|
|
||
|
|
-- Wait until the player has actually exited
|
||
|
|
-- before asking the server to delete the vehicle.
|
||
|
|
CreateThread(function()
|
||
|
|
local timeout = GetGameTimer() + 10000
|
||
|
|
|
||
|
|
while DoesEntityExist(vehicle)
|
||
|
|
and GetPedInVehicleSeat(
|
||
|
|
vehicle,
|
||
|
|
-1
|
||
|
|
) == playerPed
|
||
|
|
and GetGameTimer() < timeout do
|
||
|
|
|
||
|
|
Wait(250)
|
||
|
|
end
|
||
|
|
|
||
|
|
if DoesEntityExist(vehicle) then
|
||
|
|
TriggerServerEvent(
|
||
|
|
'how_progression:server:completeCrowbarMission1',
|
||
|
|
crowbarMission1Vehicle
|
||
|
|
)
|
||
|
|
end
|
||
|
|
|
||
|
|
crowbarMission1Vehicle = nil
|
||
|
|
end)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
|
||
|
|
-- Visible return marker.
|
||
|
|
CreateThread(function()
|
||
|
|
while true do
|
||
|
|
Wait(0)
|
||
|
|
|
||
|
|
if crowbarMission1ReturnActive then
|
||
|
|
DrawMarker(
|
||
|
|
1,
|
||
|
|
1705.50,
|
||
|
|
3771.76,
|
||
|
|
33.49,
|
||
|
|
0.0,
|
||
|
|
0.0,
|
||
|
|
0.0,
|
||
|
|
0.0,
|
||
|
|
0.0,
|
||
|
|
0.0,
|
||
|
|
3.0,
|
||
|
|
3.0,
|
||
|
|
0.5,
|
||
|
|
255,
|
||
|
|
255,
|
||
|
|
255,
|
||
|
|
150,
|
||
|
|
false,
|
||
|
|
false,
|
||
|
|
2,
|
||
|
|
false,
|
||
|
|
nil,
|
||
|
|
nil,
|
||
|
|
false
|
||
|
|
)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
|
||
|
|
-- ============================================================
|
||
|
|
-- Crowbar Mission 2
|
||
|
|
-- ============================================================
|
||
|
|
|
||
|
|
RegisterNetEvent(
|
||
|
|
'how_progression:client:startCrowbarMission2',
|
||
|
|
function(netId)
|
||
|
|
crowbarMission2Vehicle = netId
|
||
|
|
crowbarMission2Stolen = false
|
||
|
|
|
||
|
|
if DoesBlipExist(crowbarMission2Blip) then
|
||
|
|
RemoveBlip(crowbarMission2Blip)
|
||
|
|
crowbarMission2Blip = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
crowbarMission2Blip = AddBlipForCoord(
|
||
|
|
2684.30,
|
||
|
|
3292.18,
|
||
|
|
54.75
|
||
|
|
)
|
||
|
|
|
||
|
|
SetBlipSprite(crowbarMission2Blip, 225)
|
||
|
|
SetBlipScale(crowbarMission2Blip, 0.85)
|
||
|
|
SetBlipRoute(crowbarMission2Blip, true)
|
||
|
|
|
||
|
|
BeginTextCommandSetBlipName('STRING')
|
||
|
|
AddTextComponentString("Crowbar's Target")
|
||
|
|
EndTextCommandSetBlipName(crowbarMission2Blip)
|
||
|
|
|
||
|
|
lib.notify({
|
||
|
|
title = 'Crowbar',
|
||
|
|
description = 'Find the car Crowbar told you about.',
|
||
|
|
type = 'inform',
|
||
|
|
duration = 5000
|
||
|
|
})
|
||
|
|
end
|
||
|
|
)
|
||
|
|
|
||
|
|
local crowbarMission2Stolen = false
|
||
|
|
|
||
|
|
CreateThread(function()
|
||
|
|
while true do
|
||
|
|
Wait(500)
|
||
|
|
|
||
|
|
if crowbarMission2Vehicle
|
||
|
|
and not crowbarMission2Stolen then
|
||
|
|
|
||
|
|
local vehicle =
|
||
|
|
NetToVeh(crowbarMission2Vehicle)
|
||
|
|
|
||
|
|
if DoesEntityExist(vehicle) then
|
||
|
|
local playerPed =
|
||
|
|
PlayerPedId()
|
||
|
|
|
||
|
|
local vehicleCoords =
|
||
|
|
GetEntityCoords(vehicle)
|
||
|
|
|
||
|
|
local targetCoords =
|
||
|
|
vector3(
|
||
|
|
2684.30,
|
||
|
|
3292.18,
|
||
|
|
54.75
|
||
|
|
)
|
||
|
|
|
||
|
|
-- The player must actually be driving the stolen
|
||
|
|
-- vehicle away from the target before the mission
|
||
|
|
-- can complete.
|
||
|
|
if GetPedInVehicleSeat(
|
||
|
|
vehicle,
|
||
|
|
-1
|
||
|
|
) == playerPed
|
||
|
|
and #(vehicleCoords - targetCoords) >= 100.0 then
|
||
|
|
|
||
|
|
crowbarMission2Stolen = true
|
||
|
|
|
||
|
|
if DoesBlipExist(crowbarMission2Blip) then
|
||
|
|
RemoveBlip(crowbarMission2Blip)
|
||
|
|
crowbarMission2Blip = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
local props =
|
||
|
|
lib.getVehicleProperties(vehicle)
|
||
|
|
|
||
|
|
TriggerServerEvent(
|
||
|
|
'how_progression:server:completeCrowbarMission2',
|
||
|
|
crowbarMission2Vehicle,
|
||
|
|
props
|
||
|
|
)
|
||
|
|
|
||
|
|
crowbarMission2Vehicle = nil
|
||
|
|
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
|
||
|
|
-- ============================================================
|
||
|
|
-- Crowbar Mission 3: Quick Score
|
||
|
|
-- ============================================================
|
||
|
|
|
||
|
|
local crowbarMission3Blip = nil
|
||
|
|
local crowbarMission3EscapeActive = false
|
||
|
|
local crowbarMission3WantedSeen = false
|
||
|
|
|
||
|
|
RegisterNetEvent(
|
||
|
|
'how_progression:client:startCrowbarMission3',
|
||
|
|
function()
|
||
|
|
print('[how_progression] Mission 3 client start event received')
|
||
|
|
|
||
|
|
crowbarMission3EscapeActive = false
|
||
|
|
crowbarMission3WantedSeen = false
|
||
|
|
|
||
|
|
if DoesBlipExist(crowbarMission3Blip) then
|
||
|
|
RemoveBlip(crowbarMission3Blip)
|
||
|
|
crowbarMission3Blip = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
crowbarMission3Blip = AddBlipForCoord(
|
||
|
|
549.33,
|
||
|
|
2669.57,
|
||
|
|
42.16
|
||
|
|
)
|
||
|
|
|
||
|
|
SetBlipSprite(crowbarMission3Blip, 52)
|
||
|
|
SetBlipScale(crowbarMission3Blip, 0.85)
|
||
|
|
SetBlipRoute(crowbarMission3Blip, true)
|
||
|
|
|
||
|
|
print(('[how_progression] Mission 3 Harmony blip created: %s'):format(
|
||
|
|
tostring(crowbarMission3Blip)
|
||
|
|
))
|
||
|
|
|
||
|
|
BeginTextCommandSetBlipName('STRING')
|
||
|
|
AddTextComponentString('Harmony 24/7')
|
||
|
|
EndTextCommandSetBlipName(crowbarMission3Blip)
|
||
|
|
|
||
|
|
end
|
||
|
|
)
|
||
|
|
|
||
|
|
RegisterNetEvent(
|
||
|
|
'how_progression:client:crowbarMission3Escape',
|
||
|
|
function()
|
||
|
|
if DoesBlipExist(crowbarMission3Blip) then
|
||
|
|
RemoveBlip(crowbarMission3Blip)
|
||
|
|
crowbarMission3Blip = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
crowbarMission3EscapeActive = true
|
||
|
|
|
||
|
|
-- The robbery may already have raised the wanted level
|
||
|
|
-- before this event reaches the client.
|
||
|
|
if GetPlayerWantedLevel(PlayerId()) > 0 then
|
||
|
|
crowbarMission3WantedSeen = true
|
||
|
|
end
|
||
|
|
|
||
|
|
lib.notify({
|
||
|
|
title = 'Crowbar',
|
||
|
|
description = 'Get out of there. Lose the cops.',
|
||
|
|
type = 'inform',
|
||
|
|
duration = 5000
|
||
|
|
})
|
||
|
|
end
|
||
|
|
)
|
||
|
|
|
||
|
|
CreateThread(function()
|
||
|
|
while true do
|
||
|
|
Wait(500)
|
||
|
|
|
||
|
|
if crowbarMission3EscapeActive then
|
||
|
|
local wantedLevel =
|
||
|
|
GetPlayerWantedLevel(PlayerId())
|
||
|
|
|
||
|
|
if wantedLevel > 0 then
|
||
|
|
crowbarMission3WantedSeen = true
|
||
|
|
|
||
|
|
elseif crowbarMission3WantedSeen then
|
||
|
|
crowbarMission3EscapeActive = false
|
||
|
|
crowbarMission3WantedSeen = false
|
||
|
|
|
||
|
|
TriggerServerEvent(
|
||
|
|
'how_progression:server:completeCrowbarMission3'
|
||
|
|
)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
|
||
|
|
-- ============================================================
|
||
|
|
-- Rewards
|
||
|
|
-- ============================================================
|
||
|
|
|
||
|
|
RegisterNetEvent(
|
||
|
|
'how_progression:client:crowbarReward',
|
||
|
|
function(repReward, totalRep, newStage)
|
||
|
|
lib.notify({
|
||
|
|
title = 'Crowbar',
|
||
|
|
description = ('+%s Crowbar REP | Total: %s/100'):format(
|
||
|
|
repReward,
|
||
|
|
totalRep
|
||
|
|
),
|
||
|
|
type = 'success',
|
||
|
|
duration = 5000
|
||
|
|
})
|
||
|
|
end
|
||
|
|
)
|
||
|
|
|
||
|
|
RegisterNetEvent(
|
||
|
|
'how_progression:client:crowbarMission1Reward',
|
||
|
|
function(cashReward, repReward)
|
||
|
|
lib.alertDialog({
|
||
|
|
header = 'Crowbar',
|
||
|
|
|
||
|
|
content = (
|
||
|
|
"Not bad.\n\n" ..
|
||
|
|
"You followed directions, brought the car back intact, " ..
|
||
|
|
"and didn't make a complete mess of things.\n\n" ..
|
||
|
|
"Here's your cut: $%s.\n\n" ..
|
||
|
|
"Maybe there's something else you can do for me."
|
||
|
|
):format(cashReward),
|
||
|
|
|
||
|
|
centered = true
|
||
|
|
})
|
||
|
|
end
|
||
|
|
)
|
||
|
|
|
||
|
|
-- ============================================================
|
||
|
|
-- Mack scrapyard ambience
|
||
|
|
-- ============================================================
|
||
|
|
|
||
|
|
local scrapyardFestivalBus
|
||
|
|
local scrapyardJunkCar
|
||
|
|
local scrapyardForklift
|
||
|
|
local scrapyardWorkers = {}
|
||
|
|
|
||
|
|
local function protectScrapyardVehicle(vehicle)
|
||
|
|
SetEntityAsMissionEntity(vehicle, true, true)
|
||
|
|
SetVehicleOnGroundProperly(vehicle)
|
||
|
|
SetVehicleDoorsLocked(vehicle, 2)
|
||
|
|
SetEntityInvincible(vehicle, true)
|
||
|
|
FreezeEntityPosition(vehicle, true)
|
||
|
|
end
|
||
|
|
|
||
|
|
local function createScrapyardVehicle(modelName, coords)
|
||
|
|
local model = joaat(modelName)
|
||
|
|
|
||
|
|
lib.requestModel(model)
|
||
|
|
|
||
|
|
local vehicle = CreateVehicle(
|
||
|
|
model,
|
||
|
|
coords.x,
|
||
|
|
coords.y,
|
||
|
|
coords.z,
|
||
|
|
coords.w,
|
||
|
|
false,
|
||
|
|
false
|
||
|
|
)
|
||
|
|
|
||
|
|
protectScrapyardVehicle(vehicle)
|
||
|
|
SetModelAsNoLongerNeeded(model)
|
||
|
|
|
||
|
|
return vehicle
|
||
|
|
end
|
||
|
|
|
||
|
|
local function createScrapyardWorker(modelName, coords, radius)
|
||
|
|
local model = joaat(modelName)
|
||
|
|
|
||
|
|
lib.requestModel(model)
|
||
|
|
|
||
|
|
local ped = CreatePed(
|
||
|
|
0,
|
||
|
|
model,
|
||
|
|
coords.x,
|
||
|
|
coords.y,
|
||
|
|
coords.z - 1.0,
|
||
|
|
coords.w,
|
||
|
|
false,
|
||
|
|
false
|
||
|
|
)
|
||
|
|
|
||
|
|
SetPedDefaultComponentVariation(ped)
|
||
|
|
SetEntityAsMissionEntity(ped, true, true)
|
||
|
|
SetEntityInvincible(ped, true)
|
||
|
|
SetBlockingOfNonTemporaryEvents(ped, true)
|
||
|
|
SetPedCanRagdoll(ped, false)
|
||
|
|
SetPedFleeAttributes(ped, 0, false)
|
||
|
|
|
||
|
|
-- Wander around this part of the scrapyard.
|
||
|
|
TaskWanderInArea(
|
||
|
|
ped,
|
||
|
|
coords.x,
|
||
|
|
coords.y,
|
||
|
|
coords.z,
|
||
|
|
radius,
|
||
|
|
2.0,
|
||
|
|
10.0
|
||
|
|
)
|
||
|
|
|
||
|
|
SetModelAsNoLongerNeeded(model)
|
||
|
|
|
||
|
|
scrapyardWorkers[#scrapyardWorkers + 1] = ped
|
||
|
|
|
||
|
|
return ped
|
||
|
|
end
|
||
|
|
|
||
|
|
local function createScrapyardAmbience()
|
||
|
|
if not DoesEntityExist(scrapyardFestivalBus) then
|
||
|
|
scrapyardFestivalBus = createScrapyardVehicle(
|
||
|
|
'pbus2',
|
||
|
|
vec4(2349.54, 3133.84, 48.21, 77.73)
|
||
|
|
)
|
||
|
|
end
|
||
|
|
|
||
|
|
if not DoesEntityExist(scrapyardJunkCar) then
|
||
|
|
scrapyardJunkCar = createScrapyardVehicle(
|
||
|
|
'emperor',
|
||
|
|
vec4(2343.67, 3141.72, 48.21, 211.39)
|
||
|
|
)
|
||
|
|
|
||
|
|
-- Make it look like something Mack is stripping for parts.
|
||
|
|
SetVehicleEngineHealth(scrapyardJunkCar, 100.0)
|
||
|
|
SetVehicleBodyHealth(scrapyardJunkCar, 300.0)
|
||
|
|
|
||
|
|
SmashVehicleWindow(scrapyardJunkCar, 0)
|
||
|
|
SmashVehicleWindow(scrapyardJunkCar, 1)
|
||
|
|
SmashVehicleWindow(scrapyardJunkCar, 2)
|
||
|
|
SmashVehicleWindow(scrapyardJunkCar, 3)
|
||
|
|
|
||
|
|
SetVehicleDoorBroken(scrapyardJunkCar, 0, true)
|
||
|
|
SetVehicleTyreBurst(scrapyardJunkCar, 0, true, 1000.0)
|
||
|
|
SetVehicleTyreBurst(scrapyardJunkCar, 4, true, 1000.0)
|
||
|
|
end
|
||
|
|
|
||
|
|
if not DoesEntityExist(scrapyardForklift) then
|
||
|
|
scrapyardForklift = createScrapyardVehicle(
|
||
|
|
'forklift',
|
||
|
|
vec4(2350.42, 3142.49, 48.21, 231.27)
|
||
|
|
)
|
||
|
|
end
|
||
|
|
|
||
|
|
if #scrapyardWorkers == 0 then
|
||
|
|
-- Main yard
|
||
|
|
createScrapyardWorker(
|
||
|
|
's_m_y_xmech_02',
|
||
|
|
vec4(2355.0, 3148.0, 48.21, 180.0),
|
||
|
|
12.0
|
||
|
|
)
|
||
|
|
|
||
|
|
createScrapyardWorker(
|
||
|
|
's_m_m_autoshop_01',
|
||
|
|
vec4(2345.0, 3150.0, 48.21, 90.0),
|
||
|
|
12.0
|
||
|
|
)
|
||
|
|
|
||
|
|
createScrapyardWorker(
|
||
|
|
's_m_y_construct_01',
|
||
|
|
vec4(2360.0, 3143.0, 48.21, 270.0),
|
||
|
|
15.0
|
||
|
|
)
|
||
|
|
|
||
|
|
-- Around warehouse/work areas
|
||
|
|
createScrapyardWorker(
|
||
|
|
's_m_y_construct_02',
|
||
|
|
vec4(2341.0, 3130.0, 48.21, 0.0),
|
||
|
|
10.0
|
||
|
|
)
|
||
|
|
|
||
|
|
createScrapyardWorker(
|
||
|
|
's_m_m_dockwork_01',
|
||
|
|
vec4(2334.0, 3125.0, 48.21, 180.0),
|
||
|
|
15.0
|
||
|
|
)
|
||
|
|
|
||
|
|
-- Wider-ranging yard hand
|
||
|
|
createScrapyardWorker(
|
||
|
|
's_m_m_gardener_01',
|
||
|
|
vec4(2368.0, 3130.0, 48.21, 90.0),
|
||
|
|
22.0
|
||
|
|
)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
-- ============================================================
|
||
|
|
-- Cosmic Dave
|
||
|
|
-- ============================================================
|
||
|
|
|
||
|
|
local cosmicDavePed
|
||
|
|
|
||
|
|
local cosmicDaveCoords = vec4(
|
||
|
|
2447.63,
|
||
|
|
3775.08,
|
||
|
|
41.43,
|
||
|
|
23.30
|
||
|
|
)
|
||
|
|
|
||
|
|
local function createCosmicDave()
|
||
|
|
if DoesEntityExist(cosmicDavePed) then
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
local model = `ig_omega`
|
||
|
|
|
||
|
|
lib.requestModel(model)
|
||
|
|
|
||
|
|
cosmicDavePed = CreatePed(
|
||
|
|
0,
|
||
|
|
model,
|
||
|
|
cosmicDaveCoords.x,
|
||
|
|
cosmicDaveCoords.y,
|
||
|
|
cosmicDaveCoords.z - 1.0,
|
||
|
|
cosmicDaveCoords.w,
|
||
|
|
false,
|
||
|
|
false
|
||
|
|
)
|
||
|
|
|
||
|
|
SetPedDefaultComponentVariation(cosmicDavePed)
|
||
|
|
SetEntityAsMissionEntity(cosmicDavePed, true, true)
|
||
|
|
SetEntityInvincible(cosmicDavePed, true)
|
||
|
|
FreezeEntityPosition(cosmicDavePed, true)
|
||
|
|
SetBlockingOfNonTemporaryEvents(cosmicDavePed, true)
|
||
|
|
|
||
|
|
TaskStartScenarioInPlace(
|
||
|
|
cosmicDavePed,
|
||
|
|
'WORLD_HUMAN_PARTYING',
|
||
|
|
0,
|
||
|
|
true
|
||
|
|
)
|
||
|
|
|
||
|
|
SetModelAsNoLongerNeeded(model)
|
||
|
|
end
|
||
|
|
|
||
|
|
-- ============================================================
|
||
|
|
-- Initial state
|
||
|
|
-- ============================================================
|
||
|
|
|
||
|
|
CreateThread(function()
|
||
|
|
Wait(3000)
|
||
|
|
|
||
|
|
-- Mack's scrapyard physically exists even before the player
|
||
|
|
-- discovers him as a contact.
|
||
|
|
createMack()
|
||
|
|
createScrapyardAmbience()
|
||
|
|
createCosmicDave()
|
||
|
|
|
||
|
|
local stage = lib.callback.await(
|
||
|
|
'how_progression:server:getCrowbarMission',
|
||
|
|
false
|
||
|
|
)
|
||
|
|
|
||
|
|
if stage and stage >= 1 then
|
||
|
|
unlockCrowbar()
|
||
|
|
end
|
||
|
|
|
||
|
|
print((
|
||
|
|
'[how_progression] Initial Crowbar stage: %s'
|
||
|
|
):format(tostring(stage)))
|
||
|
|
end)
|
||
|
|
|
||
|
|
RegisterNetEvent(
|
||
|
|
'how_progression:client:resetProgression',
|
||
|
|
function()
|
||
|
|
ClearGpsPlayerWaypoint()
|
||
|
|
vinnyFenceUnlocked = false
|
||
|
|
mackMission1DeliveryMarkerActive = false
|
||
|
|
|
||
|
|
if DoesBlipExist(crowbarBlip) then
|
||
|
|
RemoveBlip(crowbarBlip)
|
||
|
|
crowbarBlip = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
if DoesBlipExist(mackBlip) then
|
||
|
|
RemoveBlip(mackBlip)
|
||
|
|
mackBlip = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
if DoesBlipExist(gabeBlip) then
|
||
|
|
RemoveBlip(gabeBlip)
|
||
|
|
gabeBlip = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
if DoesBlipExist(crowbarMission1Blip) then
|
||
|
|
RemoveBlip(crowbarMission1Blip)
|
||
|
|
crowbarMission1Blip = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
if DoesBlipExist(crowbarMission2Blip) then
|
||
|
|
RemoveBlip(crowbarMission2Blip)
|
||
|
|
crowbarMission2Blip = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
crowbarMission1Vehicle = nil
|
||
|
|
crowbarMission1ReturnActive = false
|
||
|
|
crowbarMission2Vehicle = nil
|
||
|
|
end
|
||
|
|
)
|
||
|
|
|
||
|
|
print('[how_progression] client.lua loaded')
|
||
|
|
|
||
|
|
RegisterNetEvent(
|
||
|
|
'how_progression:client:cleanupCrowbarMission1',
|
||
|
|
function()
|
||
|
|
crowbarMission1Vehicle = nil
|
||
|
|
crowbarMission1ReturnActive = false
|
||
|
|
|
||
|
|
if DoesBlipExist(crowbarMission1Blip) then
|
||
|
|
RemoveBlip(crowbarMission1Blip)
|
||
|
|
crowbarMission1Blip = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
ClearGpsPlayerWaypoint()
|
||
|
|
end
|
||
|
|
)
|
||
|
|
|
||
|
|
RegisterNetEvent(
|
||
|
|
'how_progression:client:cleanupCrowbarMission2',
|
||
|
|
function()
|
||
|
|
crowbarMission2Vehicle = nil
|
||
|
|
|
||
|
|
if DoesBlipExist(crowbarMission2Blip) then
|
||
|
|
RemoveBlip(crowbarMission2Blip)
|
||
|
|
crowbarMission2Blip = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
ClearGpsPlayerWaypoint()
|
||
|
|
end
|
||
|
|
)
|
||
|
|
|
||
|
|
-- Remove mission package when its carrier dies.
|
||
|
|
local function crowbarPackageCarrierDied()
|
||
|
|
TriggerServerEvent(
|
||
|
|
'how_progression:server:crowbarPackageCarrierDied'
|
||
|
|
)
|
||
|
|
end
|
||
|
|
|
||
|
|
AddEventHandler(
|
||
|
|
'baseevents:onPlayerDied',
|
||
|
|
crowbarPackageCarrierDied
|
||
|
|
)
|
||
|
|
|
||
|
|
AddEventHandler(
|
||
|
|
'baseevents:onPlayerKilled',
|
||
|
|
crowbarPackageCarrierDied
|
||
|
|
)
|
||
|
|
|
||
|
|
-- Ammu-Nation Body Armor
|
||
|
|
exports('useBodyArmor', function(data, slot)
|
||
|
|
local ped = PlayerPedId()
|
||
|
|
|
||
|
|
if GetPedArmour(ped) >= 100 then
|
||
|
|
lib.notify({
|
||
|
|
title = 'Body Armor',
|
||
|
|
description = 'Your armor is already full.',
|
||
|
|
type = 'error'
|
||
|
|
})
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
exports.ox_inventory:useItem(data, function(used)
|
||
|
|
if not used then
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
SetPedArmour(ped, 100)
|
||
|
|
|
||
|
|
lib.notify({
|
||
|
|
title = 'Body Armor',
|
||
|
|
description = 'Body armor equipped.',
|
||
|
|
type = 'success'
|
||
|
|
})
|
||
|
|
end)
|
||
|
|
end)
|
||
|
|
|
||
|
|
-- Temporary NPWD auto-contact bridge
|
||
|
|
RegisterNetEvent('how_progression:client:addPhoneContact', function(contact)
|
||
|
|
exports.npwd:sendNPWDMessage('CONTACTS', 'npwd:autoAddContact', contact)
|
||
|
|
end)
|