-- client.lua
local Props, Targets, Peds, Blip, soundId = {}, {}, {}, {}, GetSoundId()
local MiningLoaded = false
Mining = {
Functions = {},
MineOre = {},
Other = {},
Menus = {},
}
local propTable = {
{ full = "cs_x_rubweec", empty = "prop_rock_5_a" },
}
if Config.General.K4MB1Prop then
propTable = {
{ full = "cs_x_rubweec", empty = "prop_rock_5_a" },
{ full = "k4mb1_crystalblue", empty = "k4mb1_crystalempty" },
{ full = "k4mb1_crystalgreen", empty = "k4mb1_crystalempty" },
{ full = "k4mb1_crystalred", empty = "k4mb1_crystalempty" },
{ full = "k4mb1_bauxiteore2", empty = "k4mb1_emptyore2" },
{ full = "k4mb1_coal2", },
{ full = "k4mb1_copperore2", empty = "k4mb1_emptyore2" },
{ full = "k4mb1_ironore2", empty = "k4mb1_emptyore2" },
{ full = "k4mb1_goldore2", empty = "k4mb1_emptyore2" },
{ full = "k4mb1_leadore2", empty = "k4mb1_emptyore2" },
{ full = "k4mb1_tinore2", empty = "k4mb1_emptyore2" },
{ full = "k4mb1_diamond", },
}
end
MiningLoaded = MiningLoaded or false
Mining.Functions.checkForJob = function()
if Config.General.requiredJob then
if hasJob(Config.General.requiredJob) then
-- 🔥 Prevent double spawn
if not MiningLoaded then
MiningLoaded = true
Mining.Functions.makeJob()
end
else
-- 🔥 Remove everything properly
if MiningLoaded then
MiningLoaded = false
Mining.Functions.removeJob()
end
end
else
-- 🔥 No job required, still prevent duplicates
if not MiningLoaded then
MiningLoaded = true
Mining.Functions.makeJob()
end
end
end
Mining.Functions.weightedRandomReward = function()
local totalWeight, weightedTable = 0, {}
for _, item in ipairs(Config.setMiningTable) do
local weight = item.rarity == "common" and 5 or item.rarity == "rare" and 3 or 1
totalWeight += weight
local prop = item.prop
if not Config.General.K4MB1Prop then
prop = "cs_x_rubweec"
end
table.insert(weightedTable, {name = item.name, weight = totalWeight, prop = prop})
end
local randValue = math.random(1, totalWeight)
for _, item in ipairs(weightedTable) do
if randValue <= item.weight then
return item
end
end
end
Mining.Functions.spawnProp = function(coords, propName, adjustHeight)
local propCoords = vec4(coords.x, coords.y, coords.z + adjustHeight, coords.a)
local prop = makeProp({coords = propCoords, prop = propName}, 1, false)
local rot = GetEntityRotation(prop)
SetEntityRotation(prop, rot.x - math.random(60,100), rot.y, rot.z, 0, 0)
return prop
end
Mining.Functions.setupMiningTarget = function(name, coords, prop, emptyProp, setReward, job)
Targets[name] = createCircleTarget({name, vec3(coords.x, coords.y, coords.z), 1.2, {name = name, debugPoly = debugMode, useZ = true}}, {
{ action = function()
Mining.MineOre.pickaxe({stone = prop, name = name, coords = coords, emptyProp = emptyProp, setReward = setReward})
end,
icon = "fas fa-hammer",
item = "pickaxe",
label = locale("info", "mine_ore")..
" ("..(Items["pickaxe"] and Items["pickaxe"].label or "pickaxe❌")..") "..
(Config.General.AltMining and Items[setReward].label or "")..
(debugMode and " ["..name.."]" or ""),
job = job,
canInteract = function()
return not isMining
end,
},
{ action = function()
Mining.MineOre.miningDrill({stone = prop, name = name, coords = coords, emptyProp = emptyProp, setReward = setReward})
end,
icon = "fas fa-screwdriver",
item = "miningdrill",
label = locale("info", "mine_ore")..
" ("..(Items["miningdrill"] and Items["miningdrill"].label or "miningdrill❌")..")"..
(Config.General.AltMining and Items[setReward].label or "")..
(debugMode and " ["..name.."]" or ""),
job = job,
canInteract = function()
return not isMining
end,
},
{ action = function()
Mining.MineOre.miningLaser({stone = prop, name = name, coords = coords, emptyProp = emptyProp, setReward = setReward})
end,
icon = "fas fa-screwdriver-wrench",
item = "mininglaser",
label = locale("info", "mine_ore")..
" ("..(Items["mininglaser"] and Items["mininglaser"].label or "mininglaser❌")..")"..
(Config.General.AltMining and Items[setReward].label or "")..
(debugMode and " ["..name.."]" or ""),
job = job,
canInteract = function()
return not isMining
end,
},
}, 1.7)
end
Mining.Functions.makeJob = function()
Mining.Functions.removeJob()
if Locations["Mines"]["MineShaft"].Enable then
CreateModelHide(vec3(-596.04, 2089.01, 131.41), 10.5, prop_mineshaft_door, true)
end
--Ore Spawning
for mine, loc in pairs(Locations["Mines"]) do
if loc.Enable then
if loc["OrePositions"] then
for i, coords in ipairs(loc["OrePositions"]) do
local name = "Ore".."_"..mine.."_"..i
local chosenProp, reward = propTable[math.random(#propTable)], "stone"
if Config.General.AltMining then
local weightedReward = Mining.Functions.weightedRandomReward()
chosenProp = {
full = weightedReward.prop,
empty = nil
}
reward = weightedReward.name
end
local mainProp = Mining.Functions.spawnProp(coords, chosenProp.full, Config.General.K4MB1Prop and 0.8 or 1.1)
local emptyProp
if chosenProp.empty then
emptyProp = Mining.Functions.spawnProp(coords, chosenProp.empty, Config.General.K4MB1Prop and 0.8 or 1.1)
end
Mining.Functions.setupMiningTarget(name, coords, mainProp, emptyProp, reward, loc.Job)
end
end
--[[Blips]]--
if loc.Blip.Enable then
Blip[#Blip+1] = makeBlip(loc["Blip"])
end
--[[LIGHTS]]--
if loc["Lights"] then
if loc["Lights"].Enable then
for i = 1, #loc["Lights"].positions do
makeDistProp({coords = loc["Lights"].positions[i], prop = loc["Lights"].prop}, 1, false)
end
end
end
--[[Stores]]--
if loc["Store"] then
for i = 1, #loc["Store"] do
local name = getScript()..":Store:"..mine..":"..i
Peds[#Peds+1] = makePed(loc["Store"][i].model, loc["Store"][i].coords, 1, 1, loc["Store"][i].scenario)
Targets[name] =
createCircleTarget({name, loc["Store"][i].coords.xyz, 1.0, { name=name, debugPoly = debugMode, useZ=true, }, }, {
{ action = function()
openShop({ items = Config.Items, shop = "miningShop", coords = loc["Store"][i].coords })
end,
icon = "fas fa-store",
label = locale("info", "browse_store")..(debugMode and " ["..name.."]" or ""),
job = loc.Job,
},
(( mine == "K4MB1Shaft" and Config.General.K4MB1Cart ) and
{ action = function()
mineCartMenu(true)
end,
icon = "fas fa-wheelchair",
label = "Minecart",
job = loc.Job,
} or nil
),
}, 2.0)
end
end
--[[Smelting]]--
if loc["Smelting"] then
for i = 1, #loc["Smelting"] do
local name = getScript()..":Smelting:"..i
if loc["Smelting"][i].Enable then
Blip[#Blip+1] = makeBlip(loc["Smelting"][i])
end
Targets[name] =
createCircleTarget({name, loc["Smelting"][i].coords.xyz, 3.0, { name=name, debugPoly = debugMode, useZ=true, }, }, {
{ action = function()
craftingMenu({ craftable = Crafting.SmeltMenu, coords = loc["Smelting"][i].coords }) end,
icon = "fas fa-fire-burner",
label = locale("info", "use_smelter")..(debugMode and " ["..name.."]" or ""),
job = loc.Job,
canInteract = function()
return not CraftLock
end,
},
}, 10.0)
end
end
--[[Cracking]]--
if loc["Cracking"] then
for i = 1, #loc["Cracking"] do
local name = getScript()..":Cracking:"..mine..":"..i
if loc["Cracking"][i].Enable then
Blip[#Blip+1] = makeBlip(loc["Cracking"][i])
end
Props[#Props+1] = makeProp(loc["Cracking"][i], 1, false)
local bench = Props[#Props]
Targets[name] =
createCircleTarget({name, loc["Cracking"][i].coords.xyz, 1.2, { name=name, debugPoly = debugMode, useZ=true, }, }, {
{ action = function()
Mining.Other.crackStart({ bench = bench })
end,
icon = "fas fa-compact-disc",
label = locale("info", "crackingbench")..(debugMode and " ["..name.."]" or ""),
item = "stone",
job = loc.Job,
canInteract = function()
return not Cracking
end,
},
}, 2.0)
end
end
--[[Ore Buyer]]--
if loc["OreBuyer"] then
for i = 1, #loc["OreBuyer"] do
local name = getScript()..":OreBuyer:"..mine..":"..i
Peds[#Peds+1] = makePed(loc["OreBuyer"][i].model, loc["OreBuyer"][i].coords, 1, 1, loc["OreBuyer"][i].scenario)
local ped = Peds[#Peds]
if loc["OreBuyer"][i].Enable then
Blip[#Blip+1] = makeBlip(loc["OreBuyer"][i])
end
Targets[name] =
createCircleTarget({name, loc["OreBuyer"][i].coords.xyz, 0.9, { name = name, debugPoly = debugMode, uzeZ = true }, }, {
{ action = function()
sellMenu({
name = name,
ped = ped,
sellTable = Selling["OreSell"]
})
end,
icon = "fas fa-sack-dollar",
label = locale("info", "sell_ores")..(debugMode and " ["..name.."]" or ""),
job = Config.General.requiredJob
},
}, 2.0)
end
end
--[[Jewel Cutting]]--
if loc["JewelCut"] then
for i = 1, #loc["JewelCut"] do
local name = getScript()..":JewelCut:"..mine..":"..i
if loc["JewelCut"][i].Enable then
Blip[#Blip+1] = makeBlip(loc["JewelCut"][i])
end
Props[#Props+1] = makeProp(loc["JewelCut"][i], 1, false)
local bench = Props[#Props]
Targets[name] =
createCircleTarget({name, loc["JewelCut"][i].coords.xyz, 1.2, { name = name, debugPoly = debugMode, useZ = true }, }, {
{ action = function()
Mining.Menus.jewelCut({bench = bench})
end,
icon = "fas fa-gem",
label = locale("info", "jewelcut")..(debugMode and " ["..name.."]" or ""),
job = Config.General.requiredJob,
canInteract = function()
return not CraftLock
end,
},
}, 2.0)
end
end
if (mine == "K4MB1Shaft" and Config.General.K4MB1Cart) then
local cartCoords = {
{ coords = vec4(-471.6, 2090.23, 120.08, 103.49),
right = false,
},
{ coords = vec4(-564.39, 1885.17, 123.04, 22.38),
right = true,
},
}
for i = 1, #cartCoords do
local name = getScript()..":Cart:"..mine..":"..i
Peds[#Peds+1] = makePed("G_M_M_ChemWork_01", cartCoords[i].coords, 1, 1, "WORLD_HUMAN_CLIPBOARD")
Targets[name] =
createCircleTarget({name, cartCoords[i].coords.xyz, 1.0, { name=name, debugPoly = debugMode, useZ=true, }, }, {
{ action = function()
mineCartMenu(false, cartCoords[i].right)
end,
icon = "fas fa-wheelchair", label = "Minecart", job = loc.Job,
},
}, 2.0)
end
end
end
end
--[[Stone Washing]]--
if Locations["Washing"].Enable then
for k, v in pairs(Locations["Washing"].positions) do
local name = getScript()..":Washing:"..k
Targets[name] =
createCircleTarget({ name, v.coords.xyz, 9.0, { name = name, debugPoly = debugMode, usez = true, }, }, {
{ action = function()
Mining.Other.washStart({ coords = v.coords })
end,
icon = "fas fa-hands-bubbles",
item = "stone",
label = locale("info", "washstone")..(debugMode and " ["..name.."]" or ""),
canInteract = function()
return not Washing
end
},
}, 2.0)
if v.blipEnable then
Blip[#Blip+1] = makeBlip(v)
end
end
end
--[[Panning]]--
if Locations["Panning"].Enable then
for location in pairs(Locations["Panning"].positions) do
local loc = Locations["Panning"].positions[location]
if loc.Blip.Enable then Blip[#Blip+1] = makeBlip(loc["Blip"]) end
for i = 1, #loc.Positions do
local name = getScript()..":Panning:"..location..":"..i
Targets[name] =
createBoxTarget( { name, loc.Positions[i].coords.xyz, loc.Positions[i].w, loc.Positions[i].d, { name=name, heading = loc.Positions[i].coords.w, debugPoly = debugMode, minZ=loc.Positions[i].coords.z-10.0, maxZ=loc.Positions[i].coords.z+10.0 }, }, {
{ action = function()
Mining.Other.panStart({ coords = loc.Positions[i].coords })
end,
icon = "fas fa-ring",
item = "goldpan",
label = locale("info", "goldpan"),
canInteract = function()
return not Panning
end,
},
}, 2.0)
end
end
end
--[[Jewel Buyer]]--
if Locations["JewelBuyer"].Enable then
for k, v in pairs(Locations["JewelBuyer"].positions) do
local name = getScript()..":JewelBuyer:"..k
if v.blipTrue then Blip[#Blip+1] = makeBlip(v) end
Peds[#Peds+1] = makePed(v.model, v.coords, 1, 1, v.scenario)
local ped = Peds[#Peds]
Targets[name] =
createCircleTarget({ name, v.coords.xyz, 1.2, { name = name, debugPoly = debugMode, useZ = true }, }, {
{ action = function()
sellMenu({
name = name,
ped = ped,
sellTable = Selling["JewelSell"]
})
end,
icon = "fas fa-gem", label = locale("info", "jewelbuyer")..(debugMode and " ["..name.."]" or ""), job = Config.General.requiredJob
},
}, 2.0)
end
end
end
Mining.Other.stoneBreak = function(name, stone, coords, job, rot, empty)
CreateThread(function()
local prop, emptyProp, setReward = stone, empty, "stone"
removeZoneTarget(Targets[name])
Targets[name] = nil
if Config.General.AltMining then
destroyProp(prop)
Wait(debugMode and 2000 or GetTiming(Config.Timings["OreRespawn"]))
local weightedReward = Mining.Functions.weightedRandomReward()
local propPick = { full = weightedReward.prop, empty = nil }
setReward = weightedReward.name
prop = Mining.Functions.spawnProp(coords, propPick.full, Config.General.K4MB1Prop and 0.8 or 1.1)
destroyProp(emptyProp)
if propPick.empty then
emptyProp = Mining.Functions.spawnProp(coords, propPick.empty, Config.General.K4MB1Prop and 0.8 or 1.1)
end
else
debugPrint("^5Debug^7: ^2Hiding prop and target^7: '^6"..name.."^7' ^2at coords^7: ^6"..coords)
SetEntityAlpha(prop, 0)
Wait(debugMode and 2000 or GetTiming(Config.Timings["OreRespawn"]))
SetEntityAlpha(prop, 255)
end
Mining.Functions.setupMiningTarget(name, coords, prop, emptyProp, setReward, job)
debugPrint("^5Debug^7: ^2Remaking Prop and Target^7: '^6"..name.."^7' ^2at coords^7: ^6"..coords)
end)
end
isMining = false
Mining.MineOre.pickaxe = function(data)
local Ped = PlayerPedId()
if isMining then return else isMining = true end -- Stop players from doubling up the event
-- Anim Loading
local dict, anim = "amb@world_human_hammering@male@base", "base"
loadDrillSound()
--Create Pickaxe and Attach
local PickAxe = makeProp({ prop = "prop_tool_pickaxe", coords = vec4(0,0,0,0)}, 0, 1)
DisableCamCollisionForObject(PickAxe)
DisableCamCollisionForEntity(PickAxe)
AttachEntityToEntity(PickAxe, Ped, GetPedBoneIndex(Ped, 57005), 0.09, -0.53, -0.22, 252.0, 180.0, 0.0, false, true, true, true, 0, true)
local IsDrilling = true
local rockcoords = GetEntityCoords(data.stone)
--Calculate if you're facing the stone--
lookEnt(data.stone)
if #(rockcoords - GetEntityCoords(Ped)) > 1.5 then TaskGoStraightToCoord(Ped, rockcoords, 0.5, 400, 0.0, 0) Wait(400) end
loadPtfxDict("core")
CreateThread(function()
while IsDrilling do
UseParticleFxAssetNextCall("core")
playAnim(dict, anim, -1, 2)
Wait(200)
local pickcoords = GetOffsetFromEntityInWorldCoords(PickAxe, -0.4, 0.0, 0.7)
local dust = StartNetworkedParticleFxNonLoopedAtCoord("ent_dst_rocks", pickcoords.x, pickcoords.y, pickcoords.z, 0.0, 0.0, 0.0, 0.4, 0.0, 0.0, 0.0)
Wait(350)
end
end)
if progressBar({label = locale("info", "drilling_ore"), time = GetTiming(Config.Timings["Pickaxe"]), cancel = true, icon = "pickaxe"}) then
TriggerServerEvent(getScript()..":Reward", { mine = true, cost = nil, setReward = data.setReward })
Mining.Other.stoneBreak(data.name, data.stone, data.coords, data.job, data.rot, data.emptyProp)
if Config.BreakTool and Config.BreakTool.Pickaxe then
breakTool({ item = "pickaxe", damage = math.random(2, 3) })
end
end
stopAnim(dict, anim)
destroyProp(PickAxe)
unloadPtfxDict("core")
unloadDrillSound()
StopSound(soundId)
IsDrilling, isMining = false, false
end
Mining.MineOre.miningDrill = function(data)
local Ped = PlayerPedId()
if isMining then return else isMining = true end -- Stop players from doubling up the event
if hasItem("drillbit", 1) then
-- Sounds & Anim loading
loadDrillSound()
local dict, anim = "anim@heists@fleeca_bank@drilling", "drill_straight_fail"
--Create Drill and Attach
local DrillObject = makeProp({ prop = "hei_prop_heist_drill", coords = vec4(0,0,0,0)}, 0, 1)
AttachEntityToEntity(DrillObject, Ped, GetPedBoneIndex(Ped, 57005), 0.14, 0, -0.01, 90.0, -90.0, 180.0, true, true, false, true, 1, true)
local IsDrilling = true
local rockcoords = GetEntityCoords(data.stone)
--Calculate if you're heading is within 20.0 degrees -
lookEnt(data.stone)
if #(rockcoords - GetEntityCoords(Ped)) > 1.5 then TaskGoStraightToCoord(Ped, rockcoords, 0.5, 400, 0.0, 0) Wait(400) end
playAnim(dict, anim, -1, 1)
Wait(200)
if Config.General.DrillSound then PlaySoundFromEntity(soundId, "Drill", DrillObject, "DLC_HEIST_FLEECA_SOUNDSET", 1, 0) end
CreateThread(function() -- Dust/Debris Animation
loadPtfxDict("core")
while IsDrilling do
UseParticleFxAssetNextCall("core")
local dust = StartNetworkedParticleFxNonLoopedAtCoord("ent_dst_rocks", rockcoords.x, rockcoords.y, rockcoords.z, 0.0, 0.0, GetEntityHeading(Ped)-180.0, 1.0, 0.0, 0.0, 0.0)
Wait(600)
end
end)
if progressBar({label = locale("info", "drilling_ore"), time = GetTiming(Config.Timings["Mining"]), cancel = true, icon = "miningdrill"}) then
TriggerServerEvent(getScript()..":Reward", { mine = true, cost = nil, setReward = data.setReward })
Mining.Other.stoneBreak(data.name, data.stone, data.coords, data.job, data.rot, data.emptyProp)
if Config.BreakTool and Config.BreakTool.MiningDrill then
breakTool({ item = "miningdrill", damage = math.random(2, 3) })
end
if Config.BreakTool and Config.BreakTool.DrillBit then
breakTool({ item = "drillbit", damage = math.random(2, 3) })
else
--Destroy drill bit chances
local chance = math.random(1, 100)
debugPrint("Debug: crackStart chance: "..chance)
if chance >= 90 then
local breakId = GetSoundId()
PlaySoundFromEntity(breakId, "Drill_Pin_Break", Ped, "DLC_HEIST_FLEECA_SOUNDSET", 1, 0)
removeItem("drillbit", 1)
end
end
end
stopAnim(dict, anim)
unloadDrillSound()
StopSound(soundId)
destroyProp(DrillObject)
unloadPtfxDict("core")
IsDrilling, isMining = false, false
else
triggerNotify(nil, locale("error", "no_drillbit"), nil) isMining = false return
end
end
Mining.MineOre.miningLaser = function(data)
local Ped = PlayerPedId()
if isMining then return else isMining = true end -- Stop players from doubling up the event
-- Sounds & Anim Loading
RequestAmbientAudioBank("DLC_HEIST_BIOLAB_DELIVER_EMP_SOUNDS", 0)
RequestAmbientAudioBank("dlc_xm_silo_laser_hack_sounds", 0)
local dict, anim = "anim@heists@fleeca_bank@drilling", "drill_straight_fail"
--Create Drill and Attach
local DrillObject = makeProp({ prop = "ch_prop_laserdrill_01a", coords = vec4(0,0,0,0)}, 0, 1)
AttachEntityToEntity(DrillObject, Ped, GetPedBoneIndex(Ped, 57005), 0.14, 0, -0.01, 90.0, -90.0, 180.0, true, true, false, true, 1, true)
local IsDrilling = true
local rockcoords = GetEntityCoords(data.stone)
--Calculate if you're facing the stone--
lookEnt(data.stone)
--Activation noise & Anims
playAnim(dict, 'drill_straight_idle', -1, 1)
PlaySoundFromEntity(soundId, "Pass", DrillObject, "dlc_xm_silo_laser_hack_sounds", 1, 0)
Wait(1000)
playAnim(dict, anim, -1, 1)
PlaySoundFromEntity(soundId, "EMP_Vehicle_Hum", DrillObject, "DLC_HEIST_BIOLAB_DELIVER_EMP_SOUNDS", 1, 0) --Not sure about this sound, best one I could find as everything else wouldn't load
--Laser & Debris Effect
local lasercoords = GetOffsetFromEntityInWorldCoords(DrillObject, 0.0,-0.5, 0.02)
CreateThread(function()
loadPtfxDict("core")
while IsDrilling do
UseParticleFxAssetNextCall("core")
local laser = StartNetworkedParticleFxNonLoopedAtCoord("muz_railgun", lasercoords.x, lasercoords.y, lasercoords.z, 0, -10.0, GetEntityHeading(DrillObject)+270, 1.0, 0.0, 0.0, 0.0)
UseParticleFxAssetNextCall("core")
local dust = StartNetworkedParticleFxNonLoopedAtCoord("ent_dst_rocks", rockcoords.x, rockcoords.y, rockcoords.z, 0.0, 0.0, GetEntityHeading(Ped)-180.0, 1.0, 0.0, 0.0, 0.0)
Wait(60)
end
end)
if progressBar({label = locale("info", "drilling_ore"), time = GetTiming(Config.Timings["Laser"]), cancel = true, icon = "mininglaser"}) then
TriggerServerEvent(getScript()..":Reward", { mine = true, cost = nil, setReward = data.setReward })
Mining.Other.stoneBreak(data.name, data.stone, data.coords, data.job, data.rot, data.emptyProp)
if Config.BreakTool and Config.BreakTool.MiningLaser then
breakTool({ item = "mininglaser", damage = math.random(2, 3) })
end
end
IsDrilling, isMining = false, false
stopAnim(dict, anim)
ReleaseAmbientAudioBank("DLC_HEIST_BIOLAB_DELIVER_EMP_SOUNDS")
ReleaseAmbientAudioBank("dlc_xm_silo_laser_hack_sounds")
StopSound(soundId)
destroyProp(DrillObject)
unloadPtfxDict("core")
end
-- Cracking Command / Animations
Cracking = false
Mining.Other.crackStart = function(data)
local Ped = PlayerPedId()
if Cracking then return end
local cost = 1
if hasItem("stone", cost) then
if (Config.General.crackingRequiresDrillbit and not hasItem("drillbit", 1)) then
triggerNotify(nil, locale("error", "no_drillbit"), "error")
return
end
Cracking = true
-- Sounds & Anim Loading
local dict, anim ="amb@prop_human_parking_meter@male@idle_a", "idle_a"
loadDrillSound()
local benchcoords = GetOffsetFromEntityInWorldCoords(data.bench, 0.0, -0.2, 2.08)
--Calculate if you're facing the bench--
lookEnt(data.bench)
lockInv(true)
if #(benchcoords - GetEntityCoords(Ped)) > 1.5 then TaskGoStraightToCoord(Ped, benchcoords, 0.5, 400, 0.0, 0) Wait(400) end
local Rock = makeProp({ prop = "prop_rock_5_smash1", coords = vec4(benchcoords.x, benchcoords.y, benchcoords.z, 0)}, 0, 1)
if Config.General.DrillSound then
PlaySoundFromCoord(soundId, "Drill", benchcoords, "DLC_HEIST_FLEECA_SOUNDSET", 0, 4.5, 0)
end
loadPtfxDict("core")
CreateThread(function()
while Cracking do
UseParticleFxAssetNextCall("core")
local dust = StartNetworkedParticleFxNonLoopedAtCoord("ent_dst_rocks", benchcoords.x, benchcoords.y, benchcoords.z-0.9, 0.0, 0.0, 0.0, 0.2, 0.0, 0.0, 0.0)
Wait(100)
end
end)
playAnim(dict, anim, -1, 1)
--TaskPlayAnim(Ped, dict, anim, 3.0, 3.0, -1, 1, 0, false, false, false)
if progressBar({label = locale("info", "cracking_stone"), time = GetTiming(Config.Timings["Cracking"]), cancel = true, icon = "stone"}) then
TriggerServerEvent(getScript()..":Reward", { crack = true, cost = cost })
if Config.General.crackingRequiresDrillbit then
if Config.BreakTool and Config.BreakTool.DrillBit then
breakTool({ item = "drillbit", damage = math.random(2, 3) })
else
--Destroy drill bit chances
local chance = math.random(1, 100)
debugPrint("Debug: crackStart chance: "..chance)
if chance >= 90 then
local breakId = GetSoundId()
PlaySoundFromEntity(breakId, "Drill_Pin_Break", Ped, "DLC_HEIST_FLEECA_SOUNDSET", 1, 0)
removeItem("drillbit", 1)
end
end
end
end
StopAnimTask(Ped, dict, anim, 1.0)
unloadDrillSound()
StopSound(soundId)
unloadPtfxDict("core")
unloadAnimDict(dict)
destroyProp(Rock)
lockInv(false)
Cracking = false
else
triggerNotify(nil, locale("error", "no_stone"), 'error')
end
end
-- Washing Command / Animations
Washing = false
Mining.Other.washStart = function(data)
local Ped = PlayerPedId()
if Washing then return end
local cost = 1
if hasItem("stone", cost) then
Washing = true
lockInv(true)
--Create Rock and Attach
local Rock = makeProp({ prop = "prop_rock_5_smash1", coords = vec4(0, 0, 0, 0)}, 0, 1)
AttachEntityToEntity(Rock, Ped, GetPedBoneIndex(Ped, 60309), 0.1, 0.0, 0.05, 90.0, -90.0, 90.0, true, true, false, true, 1, true)
TaskStartScenarioInPlace(Ped, "PROP_HUMAN_BUM_BIN", 0, true)
local water
CreateThread(function()
Wait(3000)
loadPtfxDict("core")
while Washing do
UseParticleFxAssetNextCall("core")
water = StartNetworkedParticleFxLoopedOnEntity("water_splash_veh_out", Ped, 0.0, 1.0, -0.2, 0.0, 0.0, 0.0, 2.0, 0, 0, 0)
Wait(500)
end
end)
if progressBar({ label = locale("info", "washing_stone"), time = GetTiming(Config.Timings["Washing"]), cancel = true, icon = "stone" }) then
TriggerServerEvent(getScript()..":Reward", { wash = true, cost = cost })
end
lockInv(false)
StopParticleFxLooped(water, 0)
destroyProp(Rock)
unloadPtfxDict("core")
Washing = false
ClearPedTasks(Ped)
else
triggerNotify(nil, locale("error", "no_stone"), 'error')
end
end
-- Gold Panning Command / Animations
Panning = false
Mining.Other.panStart = function(data)
local Ped = PlayerPedId()
if Panning then return else Panning = true end
lockInv(true)
--Create Rock and Attach
local trayCoords = GetOffsetFromEntityInWorldCoords(Ped, 0.0, 0.5, -0.9)
Props[#Props+1] = makeProp({ coords = vec4(trayCoords.x, trayCoords.y, trayCoords.z+1.03, GetEntityHeading(Ped)), prop = "bkr_prop_meth_tray_01b"} , 1, 1)
CreateThread(function()
loadPtfxDict("core")
while Panning do
UseParticleFxAssetNextCall("core")
local water = StartNetworkedParticleFxLoopedOnEntity("water_splash_veh_out", Props[#Props], 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.0, 0, 0, 0)
Wait(100)
end
end)
--Start Anim
TaskStartScenarioInPlace(Ped, "CODE_HUMAN_MEDIC_KNEEL", 0, true)
if progressBar({ label = locale("info", "goldpanning"), time = GetTiming(Config.Timings["Panning"]), cancel = true, icon = "goldpan" }) then
TriggerServerEvent(getScript()..":Reward", { pan = true, cost = nil })
if Config.BreakTool and Config.BreakTool.GoldPan then
breakTool({ item = "goldpan", damage = math.random(2, 3) })
end
end
ClearPedTasksImmediately(Ped)
destroyProp(Props[#Props])
unloadPtfxDict("core")
lockInv(false)
Panning = false
end
--Cutting Jewels
Mining.Menus.jewelCut = function(data)
loadDrillSound()
local Menu = {}
local table = {
{ header = locale("info", "gem_cut"), txt = locale("info", "gem_cut_section"), craftable = Crafting.GemCut, },
{ header = locale("info", "make_ring"), txt = locale("info", "ring_craft_section"), craftable = Crafting.RingCut, },
{ header = locale("info", "make_neck"), txt = locale("info", "neck_craft_section"), craftable = Crafting.NeckCut, },
{ header = locale("info", "make_ear"), txt = locale("info", "ear_craft_section"), craftable = Crafting.EarCut, },
}
for i = 1, #table do
Menu[#Menu+1] = {
header = table[i].header,
txt = table[i].txt,
onSelect = function()
craftingMenu({
craftable = table[i].craftable,
coords = GetEntityCoords(data.bench),
sound = {
soundId = soundId, audioName = "Drill", audioRef = "DLC_HEIST_FLEECA_SOUNDSET"
},
-- requiredItem = function()
-- if Config.BreakTool and Config.BreakTool.DrillBit then
-- breakTool({ item = "drillbit", damage = math.random(2, 3) })
-- else
-- --Destroy drill bit chances
-- if math.random(1, 100) >= 90 then
-- local breakId = GetSoundId()
-- PlaySoundFromEntity(breakId, "Drill_Pin_Break", Ped, "DLC_HEIST_FLEECA_SOUNDSET", 1, 0)
-- removeItem("drillbit", 1)
-- end
-- end
-- end,
onBack = function()
Mining.Menus.jewelCut(data)
end
})
end,
}
end
openMenu(Menu, {
header = locale("info", "craft_bench"),
headertxt = locale("info", "req_drill_bit"),
canClose = true,
})
end
Mining.Functions.removeJob = function()
for k in pairs(Targets) do removeZoneTarget(k) end
for _, v in pairs(Peds) do DeletePed(v) end
for i = 1, #Props do DeleteObject(Props[i]) end
for i = 1, #Blip do RemoveBlip(Blip[i]) end
end
onResourceStop(Mining.Functions.removeJob, true)
onPlayerLoaded(function() Wait(1000) Mining.Functions.checkForJob() end, true)
if Config.General.requiredJob then
RegisterNetEvent('QBCore:Client:OnJobUpdate', Mining.Functions.checkForJob)
end
-- 🔥 Anti Double Spawn Fix
CreateThread(function()
Wait(500)
if Mining and Mining.Functions and Mining.Functions.removeJob then
Mining.Functions.removeJob()
Wait(200)
Mining.Functions.checkForJob()
end
end)
AddEventHandler('onResourceStart', function(res)
if res == GetCurrentResourceName() then
Wait(500)
if Mining and Mining.Functions then
Mining.Functions.removeJob()
Wait(200)
Mining.Functions.checkForJob()
end
end
end)
-- server.lua
local function GetRandItemFromTable(table)
debugPrint("^5Debug^7: ^2Picking random item from table^7")
::start::
local randNum = math.random(1, 100)
local items = {}
for _, item in ipairs(table) do
if randNum <= tonumber(item.rarity) then
if doesItemExist(item.item) then
items[#items+1] = item.item
end
end
end
if #items == 0 then
goto start
end
local rand = math.random(1, #items)
local selectedItem = items[rand]
debugPrint("^5Debug^7: ^2Selected item ^7'^3"..selectedItem.."^7' - ^2rand^7: "..rand.." ^2length^7: "..#items)
return selectedItem
end
RegisterServerEvent(getScript()..":Reward", function(data)
local src = source
local amount = 1
if data.mine then
local amount = GetTiming(Config.PoolAmounts.Mining.AmountPerSuccess)
local carryCheck = canCarry({ [data.setReward] = amount }, src)
if carryCheck[data.setReward] then
addItem(data.setReward, amount, nil, src)
else
triggerNotify(nil, locale("error", "full"), "error", src)
end
elseif data.crack then
local selectedItem = GetRandItemFromTable(Config.CrackPool)
amount = GetTiming(Config.PoolAmounts.Cracking.AmountPerSuccess)
local canCarryCheck = canCarry({ [selectedItem] = amount }, src)
if selectedItem and canCarryCheck[selectedItem] then
removeItem("stone", data.cost, src)
addItem(selectedItem, amount, nil, src)
else
triggerNotify(nil, locale("error", "full"), "error", src)
end
elseif data.wash then
local rewards = {}
-- Step 1: Determine all reward items
for i = 1, GetTiming(Config.PoolAmounts.Washing.Successes) do
local selectedItem = GetRandItemFromTable(Config.WashPool)
local amount = GetTiming(Config.PoolAmounts.Washing.AmountPerSuccess)
if selectedItem then
rewards[selectedItem] = (rewards[selectedItem] or 0) + amount
end
end
-- Step 2: Check if the player can carry all rewards
local canCarryCheck = canCarry(rewards, src)
local canCarryAll = true
for item, _ in pairs(rewards) do
if not canCarryCheck[item] then
canCarryAll = false
break
end
end
-- Step 3: If they can carry, remove and reward. If not, notify.
if canCarryAll then
removeItem("stone", data.cost, src)
for item, amount in pairs(rewards) do
addItem(item, amount, nil, src)
end
else
triggerNotify(nil, locale("error", "full"), "error", src)
end
elseif data.pan then
for i = 1, GetTiming(Config.PoolAmounts.Panning.Successes) do
local selectedItem = GetRandItemFromTable(Config.PanPool)
amount = GetTiming(Config.PoolAmounts.Panning.AmountPerSuccess)
local canCarryCheck = canCarry({ [selectedItem] = amount }, src)
if selectedItem and canCarryCheck[selectedItem] then
addItem(selectedItem, amount, nil, src)
else
triggerNotify(nil, locale("error", "full"), "error", src)
end
end
end
end)
onResourceStart(function()
Wait(1000)
for id, info in pairs(Locations["Mines"]) do
if info.Enable then
if info.Store then
for i = 1, #info.Store do
registerShop("miningShop", Config.Items.label, Config.Items.items, nil, info.Store[i].coords.xyz)
end
end
if info.OreBuyer then
for i = 1, #info.OreBuyer do
local name = getScript()..":OreBuyer:"..id..":"..i
registerSellShop(name, info.OreBuyer[i].coords.xyz)
end
end
end
end
if Locations["JewelBuyer"] and Locations["JewelBuyer"].Enable then
for i = 1, #Locations["JewelBuyer"].positions do
local name = getScript()..":JewelBuyer:"..i
registerSellShop(name, Locations["JewelBuyer"].positions[i].coords.xyz)
end
end
--registerShop("miningShop", Config.Items.label, Config.Items.items)
for k in pairs(Selling) do
if Selling[k].Items then
for b in pairs(Selling[k].Items) do
if not Items[b] then print("Selling: Missing Item from Items: '"..b.."'") end
end
else
for l in pairs(Selling[k]) do
if l ~= "Header" then
for b in pairs(Selling[k][l].Items) do
if not Items[b] then print("Selling: Missing Item from Items: '"..b.."'") end
end
end
end
end
end
for i = 1, #Config.CrackPool do if not Items[Config.CrackPool[i].item] then print("CrackPool: Missing Item from Items: '"..Config.CrackPool[i].item.."'") end end
for i = 1, #Config.WashPool do if not Items[Config.WashPool[i].item] then print("WashPool: Missing Item from Items: '"..Config.WashPool[i].item.."'") end end
for i = 1, #Config.PanPool do if not Items[Config.PanPool[i].item] then print("PanPool: Missing Item from Items: '"..Config.PanPool[i].item.."'") end end
for i = 1, #Config.Items.items do if not Items[Config.Items.items[i].name] then print("Shop: Missing Item from Items: '"..Config.Items.items[i].name.."'") end end
local itemcheck = {}
for _, v in pairs(Crafting) do
if type(v) == "table" then
for _, b in pairs(v.Recipes) do
for k, l in pairs(b) do
if k ~= "amount" then
itemcheck[k] = {}
for j in pairs(l) do
itemcheck[j] = {}
end
end
end
end
end
end
for k in pairs(itemcheck) do
if not Items[k] then print("Crafting recipe couldn't find item '"..k.."' in the shared") end
end
end, true)
-- 🔥 Anti Duplicate Shop Register Fix
local alreadyRegistered = false
AddEventHandler('onResourceStart', function(res)
if res == GetCurrentResourceName() then
if alreadyRegistered then return end
alreadyRegistered = true
end
end)
-- client.lua
local Props, Targets, Peds, Blip, soundId = {}, {}, {}, {}, GetSoundId()
local MiningLoaded = false
Mining = {
Functions = {},
MineOre = {},
Other = {},
Menus = {},
}
local propTable = {
{ full = "cs_x_rubweec", empty = "prop_rock_5_a" },
}
if Config.General.K4MB1Prop then
propTable = {
{ full = "cs_x_rubweec", empty = "prop_rock_5_a" },
{ full = "k4mb1_crystalblue", empty = "k4mb1_crystalempty" },
{ full = "k4mb1_crystalgreen", empty = "k4mb1_crystalempty" },
{ full = "k4mb1_crystalred", empty = "k4mb1_crystalempty" },
{ full = "k4mb1_bauxiteore2", empty = "k4mb1_emptyore2" },
{ full = "k4mb1_coal2", },
{ full = "k4mb1_copperore2", empty = "k4mb1_emptyore2" },
{ full = "k4mb1_ironore2", empty = "k4mb1_emptyore2" },
{ full = "k4mb1_goldore2", empty = "k4mb1_emptyore2" },
{ full = "k4mb1_leadore2", empty = "k4mb1_emptyore2" },
{ full = "k4mb1_tinore2", empty = "k4mb1_emptyore2" },
{ full = "k4mb1_diamond", },
}
end
MiningLoaded = MiningLoaded or false
Mining.Functions.checkForJob = function()
end
Mining.Functions.weightedRandomReward = function()
local totalWeight, weightedTable = 0, {}
for _, item in ipairs(Config.setMiningTable) do
local weight = item.rarity == "common" and 5 or item.rarity == "rare" and 3 or 1
totalWeight += weight
local prop = item.prop
if not Config.General.K4MB1Prop then
prop = "cs_x_rubweec"
end
table.insert(weightedTable, {name = item.name, weight = totalWeight, prop = prop})
end
end
Mining.Functions.spawnProp = function(coords, propName, adjustHeight)
local propCoords = vec4(coords.x, coords.y, coords.z + adjustHeight, coords.a)
local prop = makeProp({coords = propCoords, prop = propName}, 1, false)
local rot = GetEntityRotation(prop)
SetEntityRotation(prop, rot.x - math.random(60,100), rot.y, rot.z, 0, 0)
return prop
end
Mining.Functions.setupMiningTarget = function(name, coords, prop, emptyProp, setReward, job)
Targets[name] = createCircleTarget({name, vec3(coords.x, coords.y, coords.z), 1.2, {name = name, debugPoly = debugMode, useZ = true}}, {
{ action = function()
Mining.MineOre.pickaxe({stone = prop, name = name, coords = coords, emptyProp = emptyProp, setReward = setReward})
end,
icon = "fas fa-hammer",
item = "pickaxe",
label = locale("info", "mine_ore")..
" ("..(Items["pickaxe"] and Items["pickaxe"].label or "pickaxe❌")..") "..
(Config.General.AltMining and Items[setReward].label or "")..
(debugMode and " ["..name.."]" or ""),
job = job,
canInteract = function()
return not isMining
end,
},
{ action = function()
Mining.MineOre.miningDrill({stone = prop, name = name, coords = coords, emptyProp = emptyProp, setReward = setReward})
end,
icon = "fas fa-screwdriver",
item = "miningdrill",
label = locale("info", "mine_ore")..
" ("..(Items["miningdrill"] and Items["miningdrill"].label or "miningdrill❌")..")"..
(Config.General.AltMining and Items[setReward].label or "")..
(debugMode and " ["..name.."]" or ""),
job = job,
canInteract = function()
return not isMining
end,
},
{ action = function()
Mining.MineOre.miningLaser({stone = prop, name = name, coords = coords, emptyProp = emptyProp, setReward = setReward})
end,
icon = "fas fa-screwdriver-wrench",
item = "mininglaser",
label = locale("info", "mine_ore")..
" ("..(Items["mininglaser"] and Items["mininglaser"].label or "mininglaser❌")..")"..
(Config.General.AltMining and Items[setReward].label or "")..
(debugMode and " ["..name.."]" or ""),
job = job,
canInteract = function()
return not isMining
end,
},
}, 1.7)
end
Mining.Functions.makeJob = function()
Mining.Functions.removeJob()
if Locations["Mines"]["MineShaft"].Enable then
CreateModelHide(vec3(-596.04, 2089.01, 131.41), 10.5,
prop_mineshaft_door, true)end
end
Mining.Other.stoneBreak = function(name, stone, coords, job, rot, empty)
CreateThread(function()
local prop, emptyProp, setReward = stone, empty, "stone"
removeZoneTarget(Targets[name])
Targets[name] = nil
end
isMining = false
Mining.MineOre.pickaxe = function(data)
local Ped = PlayerPedId()
if isMining then return else isMining = true end -- Stop players from doubling up the event
-- Anim Loading
local dict, anim = "amb@world_human_hammering@male@base", "base"
loadDrillSound()
--Create Pickaxe and Attach
local PickAxe = makeProp({ prop = "prop_tool_pickaxe", coords = vec4(0,0,0,0)}, 0, 1)
DisableCamCollisionForObject(PickAxe)
DisableCamCollisionForEntity(PickAxe)
AttachEntityToEntity(PickAxe, Ped, GetPedBoneIndex(Ped, 57005), 0.09, -0.53, -0.22, 252.0, 180.0, 0.0, false, true, true, true, 0, true)
local IsDrilling = true
local rockcoords = GetEntityCoords(data.stone)
--Calculate if you're facing the stone--
lookEnt(data.stone)
if #(rockcoords - GetEntityCoords(Ped)) > 1.5 then TaskGoStraightToCoord(Ped, rockcoords, 0.5, 400, 0.0, 0) Wait(400) end
loadPtfxDict("core")
CreateThread(function()
while IsDrilling do
UseParticleFxAssetNextCall("core")
playAnim(dict, anim, -1, 2)
Wait(200)
local pickcoords = GetOffsetFromEntityInWorldCoords(PickAxe, -0.4, 0.0, 0.7)
local dust = StartNetworkedParticleFxNonLoopedAtCoord("ent_dst_rocks", pickcoords.x, pickcoords.y, pickcoords.z, 0.0, 0.0, 0.0, 0.4, 0.0, 0.0, 0.0)
Wait(350)
end
end)
if progressBar({label = locale("info", "drilling_ore"), time = GetTiming(Config.Timings["Pickaxe"]), cancel = true, icon = "pickaxe"}) then
TriggerServerEvent(getScript()..":Reward", { mine = true, cost = nil, setReward = data.setReward })
Mining.Other.stoneBreak(data.name, data.stone, data.coords, data.job, data.rot, data.emptyProp)
if Config.BreakTool and Config.BreakTool.Pickaxe then
breakTool({ item = "pickaxe", damage = math.random(2, 3) })
end
end
stopAnim(dict, anim)
destroyProp(PickAxe)
unloadPtfxDict("core")
unloadDrillSound()
StopSound(soundId)
IsDrilling, isMining = false, false
end
Mining.MineOre.miningDrill = function(data)
local Ped = PlayerPedId()
if isMining then return else isMining = true end -- Stop players from doubling up the event
if hasItem("drillbit", 1) then
-- Sounds & Anim loading
loadDrillSound()
local dict, anim = "anim@heists@fleeca_bank@drilling", "drill_straight_fail"
--Create Drill and Attach
local DrillObject = makeProp({ prop = "hei_prop_heist_drill", coords = vec4(0,0,0,0)}, 0, 1)
AttachEntityToEntity(DrillObject, Ped, GetPedBoneIndex(Ped, 57005), 0.14, 0, -0.01, 90.0, -90.0, 180.0, true, true, false, true, 1, true)
local IsDrilling = true
local rockcoords = GetEntityCoords(data.stone)
--Calculate if you're heading is within 20.0 degrees -
lookEnt(data.stone)
if #(rockcoords - GetEntityCoords(Ped)) > 1.5 then TaskGoStraightToCoord(Ped, rockcoords, 0.5, 400, 0.0, 0) Wait(400) end
playAnim(dict, anim, -1, 1)
Wait(200)
if Config.General.DrillSound then PlaySoundFromEntity(soundId, "Drill", DrillObject, "DLC_HEIST_FLEECA_SOUNDSET", 1, 0) end
CreateThread(function() -- Dust/Debris Animation
loadPtfxDict("core")
while IsDrilling do
UseParticleFxAssetNextCall("core")
local dust = StartNetworkedParticleFxNonLoopedAtCoord("ent_dst_rocks", rockcoords.x, rockcoords.y, rockcoords.z, 0.0, 0.0, GetEntityHeading(Ped)-180.0, 1.0, 0.0, 0.0, 0.0)
Wait(600)
end
end)
if progressBar({label = locale("info", "drilling_ore"), time = GetTiming(Config.Timings["Mining"]), cancel = true, icon = "miningdrill"}) then
TriggerServerEvent(getScript()..":Reward", { mine = true, cost = nil, setReward = data.setReward })
Mining.Other.stoneBreak(data.name, data.stone, data.coords, data.job, data.rot, data.emptyProp)
if Config.BreakTool and Config.BreakTool.MiningDrill then
breakTool({ item = "miningdrill", damage = math.random(2, 3) })
end
if Config.BreakTool and Config.BreakTool.DrillBit then
breakTool({ item = "drillbit", damage = math.random(2, 3) })
else
--Destroy drill bit chances
local chance = math.random(1, 100)
debugPrint("Debug: crackStart chance: "..chance)
if chance >= 90 then
local breakId = GetSoundId()
PlaySoundFromEntity(breakId, "Drill_Pin_Break", Ped, "DLC_HEIST_FLEECA_SOUNDSET", 1, 0)
removeItem("drillbit", 1)
end
end
end
stopAnim(dict, anim)
unloadDrillSound()
StopSound(soundId)
destroyProp(DrillObject)
unloadPtfxDict("core")
IsDrilling, isMining = false, false
else
triggerNotify(nil, locale("error", "no_drillbit"), nil) isMining = false return
end
end
Mining.MineOre.miningLaser = function(data)
local Ped = PlayerPedId()
if isMining then return else isMining = true end -- Stop players from doubling up the event
-- Sounds & Anim Loading
RequestAmbientAudioBank("DLC_HEIST_BIOLAB_DELIVER_EMP_SOUNDS", 0)
RequestAmbientAudioBank("dlc_xm_silo_laser_hack_sounds", 0)
local dict, anim = "anim@heists@fleeca_bank@drilling", "drill_straight_fail"
--Create Drill and Attach
local DrillObject = makeProp({ prop = "ch_prop_laserdrill_01a", coords = vec4(0,0,0,0)}, 0, 1)
AttachEntityToEntity(DrillObject, Ped, GetPedBoneIndex(Ped, 57005), 0.14, 0, -0.01, 90.0, -90.0, 180.0, true, true, false, true, 1, true)
local IsDrilling = true
local rockcoords = GetEntityCoords(data.stone)
--Calculate if you're facing the stone--
lookEnt(data.stone)
--Activation noise & Anims
playAnim(dict, 'drill_straight_idle', -1, 1)
PlaySoundFromEntity(soundId, "Pass", DrillObject, "dlc_xm_silo_laser_hack_sounds", 1, 0)
Wait(1000)
playAnim(dict, anim, -1, 1)
PlaySoundFromEntity(soundId, "EMP_Vehicle_Hum", DrillObject, "DLC_HEIST_BIOLAB_DELIVER_EMP_SOUNDS", 1, 0) --Not sure about this sound, best one I could find as everything else wouldn't load
--Laser & Debris Effect
local lasercoords = GetOffsetFromEntityInWorldCoords(DrillObject, 0.0,-0.5, 0.02)
CreateThread(function()
loadPtfxDict("core")
while IsDrilling do
UseParticleFxAssetNextCall("core")
local laser = StartNetworkedParticleFxNonLoopedAtCoord("muz_railgun", lasercoords.x, lasercoords.y, lasercoords.z, 0, -10.0, GetEntityHeading(DrillObject)+270, 1.0, 0.0, 0.0, 0.0)
UseParticleFxAssetNextCall("core")
local dust = StartNetworkedParticleFxNonLoopedAtCoord("ent_dst_rocks", rockcoords.x, rockcoords.y, rockcoords.z, 0.0, 0.0, GetEntityHeading(Ped)-180.0, 1.0, 0.0, 0.0, 0.0)
Wait(60)
end
end)
if progressBar({label = locale("info", "drilling_ore"), time = GetTiming(Config.Timings["Laser"]), cancel = true, icon = "mininglaser"}) then
TriggerServerEvent(getScript()..":Reward", { mine = true, cost = nil, setReward = data.setReward })
Mining.Other.stoneBreak(data.name, data.stone, data.coords, data.job, data.rot, data.emptyProp)
if Config.BreakTool and Config.BreakTool.MiningLaser then
breakTool({ item = "mininglaser", damage = math.random(2, 3) })
end
end
IsDrilling, isMining = false, false
stopAnim(dict, anim)
ReleaseAmbientAudioBank("DLC_HEIST_BIOLAB_DELIVER_EMP_SOUNDS")
ReleaseAmbientAudioBank("dlc_xm_silo_laser_hack_sounds")
StopSound(soundId)
destroyProp(DrillObject)
unloadPtfxDict("core")
end
-- Cracking Command / Animations
Cracking = false
Mining.Other.crackStart = function(data)
local Ped = PlayerPedId()
if Cracking then return end
local cost = 1
if hasItem("stone", cost) then
if (Config.General.crackingRequiresDrillbit and not hasItem("drillbit", 1)) then
triggerNotify(nil, locale("error", "no_drillbit"), "error")
return
end
Cracking = true
-- Sounds & Anim Loading
local dict, anim ="amb@prop_human_parking_meter@male@idle_a", "idle_a"
loadDrillSound()
local benchcoords = GetOffsetFromEntityInWorldCoords(data.bench, 0.0, -0.2, 2.08)
--Calculate if you're facing the bench--
lookEnt(data.bench)
lockInv(true)
if #(benchcoords - GetEntityCoords(Ped)) > 1.5 then TaskGoStraightToCoord(Ped, benchcoords, 0.5, 400, 0.0, 0) Wait(400) end
local Rock = makeProp({ prop = "prop_rock_5_smash1", coords = vec4(benchcoords.x, benchcoords.y, benchcoords.z, 0)}, 0, 1)
if Config.General.DrillSound then
PlaySoundFromCoord(soundId, "Drill", benchcoords, "DLC_HEIST_FLEECA_SOUNDSET", 0, 4.5, 0)
end
loadPtfxDict("core")
CreateThread(function()
while Cracking do
UseParticleFxAssetNextCall("core")
local dust = StartNetworkedParticleFxNonLoopedAtCoord("ent_dst_rocks", benchcoords.x, benchcoords.y, benchcoords.z-0.9, 0.0, 0.0, 0.0, 0.2, 0.0, 0.0, 0.0)
Wait(100)
end
end)
playAnim(dict, anim, -1, 1)
--TaskPlayAnim(Ped, dict, anim, 3.0, 3.0, -1, 1, 0, false, false, false)
if progressBar({label = locale("info", "cracking_stone"), time = GetTiming(Config.Timings["Cracking"]), cancel = true, icon = "stone"}) then
TriggerServerEvent(getScript()..":Reward", { crack = true, cost = cost })
if Config.General.crackingRequiresDrillbit then
if Config.BreakTool and Config.BreakTool.DrillBit then
breakTool({ item = "drillbit", damage = math.random(2, 3) })
else
--Destroy drill bit chances
local chance = math.random(1, 100)
debugPrint("Debug: crackStart chance: "..chance)
if chance >= 90 then
local breakId = GetSoundId()
PlaySoundFromEntity(breakId, "Drill_Pin_Break", Ped, "DLC_HEIST_FLEECA_SOUNDSET", 1, 0)
removeItem("drillbit", 1)
end
end
end
end
StopAnimTask(Ped, dict, anim, 1.0)
unloadDrillSound()
StopSound(soundId)
unloadPtfxDict("core")
unloadAnimDict(dict)
destroyProp(Rock)
lockInv(false)
Cracking = false
else
triggerNotify(nil, locale("error", "no_stone"), 'error')
end
end
-- Washing Command / Animations
Washing = false
Mining.Other.washStart = function(data)
local Ped = PlayerPedId()
if Washing then return end
local cost = 1
if hasItem("stone", cost) then
Washing = true
lockInv(true)
--Create Rock and Attach
local Rock = makeProp({ prop = "prop_rock_5_smash1", coords = vec4(0, 0, 0, 0)}, 0, 1)
AttachEntityToEntity(Rock, Ped, GetPedBoneIndex(Ped, 60309), 0.1, 0.0, 0.05, 90.0, -90.0, 90.0, true, true, false, true, 1, true)
TaskStartScenarioInPlace(Ped, "PROP_HUMAN_BUM_BIN", 0, true)
local water
CreateThread(function()
Wait(3000)
loadPtfxDict("core")
while Washing do
UseParticleFxAssetNextCall("core")
water = StartNetworkedParticleFxLoopedOnEntity("water_splash_veh_out", Ped, 0.0, 1.0, -0.2, 0.0, 0.0, 0.0, 2.0, 0, 0, 0)
Wait(500)
end
end)
if progressBar({ label = locale("info", "washing_stone"), time = GetTiming(Config.Timings["Washing"]), cancel = true, icon = "stone" }) then
TriggerServerEvent(getScript()..":Reward", { wash = true, cost = cost })
end
lockInv(false)
StopParticleFxLooped(water, 0)
destroyProp(Rock)
unloadPtfxDict("core")
Washing = false
ClearPedTasks(Ped)
else
triggerNotify(nil, locale("error", "no_stone"), 'error')
end
end
-- Gold Panning Command / Animations
Panning = false
Mining.Other.panStart = function(data)
local Ped = PlayerPedId()
if Panning then return else Panning = true end
lockInv(true)
--Create Rock and Attach
local trayCoords = GetOffsetFromEntityInWorldCoords(Ped, 0.0, 0.5, -0.9)
Props[#Props+1] = makeProp({ coords = vec4(trayCoords.x, trayCoords.y, trayCoords.z+1.03, GetEntityHeading(Ped)), prop = "bkr_prop_meth_tray_01b"} , 1, 1)
CreateThread(function()
loadPtfxDict("core")
while Panning do
UseParticleFxAssetNextCall("core")
local water = StartNetworkedParticleFxLoopedOnEntity("water_splash_veh_out", Props[#Props], 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.0, 0, 0, 0)
Wait(100)
end
end)
--Start Anim
TaskStartScenarioInPlace(Ped, "CODE_HUMAN_MEDIC_KNEEL", 0, true)
if progressBar({ label = locale("info", "goldpanning"), time = GetTiming(Config.Timings["Panning"]), cancel = true, icon = "goldpan" }) then
TriggerServerEvent(getScript()..":Reward", { pan = true, cost = nil })
if Config.BreakTool and Config.BreakTool.GoldPan then
breakTool({ item = "goldpan", damage = math.random(2, 3) })
end
end
ClearPedTasksImmediately(Ped)
destroyProp(Props[#Props])
unloadPtfxDict("core")
lockInv(false)
Panning = false
end
--Cutting Jewels
Mining.Menus.jewelCut = function(data)
loadDrillSound()
local Menu = {}
local table = {
{ header = locale("info", "gem_cut"), txt = locale("info", "gem_cut_section"), craftable = Crafting.GemCut, },
{ header = locale("info", "make_ring"), txt = locale("info", "ring_craft_section"), craftable = Crafting.RingCut, },
{ header = locale("info", "make_neck"), txt = locale("info", "neck_craft_section"), craftable = Crafting.NeckCut, },
{ header = locale("info", "make_ear"), txt = locale("info", "ear_craft_section"), craftable = Crafting.EarCut, },
}
for i = 1, #table do
Menu[#Menu+1] = {
header = table[i].header,
txt = table[i].txt,
onSelect = function()
craftingMenu({
craftable = table[i].craftable,
coords = GetEntityCoords(data.bench),
sound = {
soundId = soundId, audioName = "Drill", audioRef = "DLC_HEIST_FLEECA_SOUNDSET"
},
-- requiredItem = function()
-- if Config.BreakTool and Config.BreakTool.DrillBit then
-- breakTool({ item = "drillbit", damage = math.random(2, 3) })
-- else
-- --Destroy drill bit chances
-- if math.random(1, 100) >= 90 then
-- local breakId = GetSoundId()
-- PlaySoundFromEntity(breakId, "Drill_Pin_Break", Ped, "DLC_HEIST_FLEECA_SOUNDSET", 1, 0)
-- removeItem("drillbit", 1)
-- end
-- end
-- end,
onBack = function()
Mining.Menus.jewelCut(data)
end
})
end,
}
end
openMenu(Menu, {
header = locale("info", "craft_bench"),
headertxt = locale("info", "req_drill_bit"),
canClose = true,
})
end
Mining.Functions.removeJob = function()
for k in pairs(Targets) do removeZoneTarget(k) end
for _, v in pairs(Peds) do DeletePed(v) end
for i = 1, #Props do DeleteObject(Props[i]) end
for i = 1, #Blip do RemoveBlip(Blip[i]) end
end
onResourceStop(Mining.Functions.removeJob, true)
onPlayerLoaded(function() Wait(1000) Mining.Functions.checkForJob() end, true)
if Config.General.requiredJob then
RegisterNetEvent('QBCore:Client:OnJobUpdate', Mining.Functions.checkForJob)
end
-- 🔥 Anti Double Spawn Fix
CreateThread(function()
Wait(500)
if Mining and Mining.Functions and Mining.Functions.removeJob then
Mining.Functions.removeJob()
Wait(200)
Mining.Functions.checkForJob()
end
end)
AddEventHandler('onResourceStart', function(res)
if res == GetCurrentResourceName() then
Wait(500)
if Mining and Mining.Functions then
Mining.Functions.removeJob()
Wait(200)
Mining.Functions.checkForJob()
end
end
end)
-- server.lua
local function GetRandItemFromTable(table)
debugPrint("^5Debug^7: ^2Picking random item from table^7")
::start::
local randNum = math.random(1, 100)
local items = {}
for _, item in ipairs(table) do
if randNum <= tonumber(item.rarity) then
if doesItemExist(item.item) then
items[#items+1] = item.item
end
end
end
if #items == 0 then
goto start
end
local rand = math.random(1, #items)
local selectedItem = items[rand]
debugPrint("^5Debug^7: ^2Selected item ^7'^3"..selectedItem.."^7' - ^2rand^7: "..rand.." ^2length^7: "..#items)
return selectedItem
end
RegisterServerEvent(getScript()..":Reward", function(data)
local src = source
local amount = 1
end)
onResourceStart(function()
Wait(1000)
for id, info in pairs(Locations["Mines"]) do
if info.Enable then
if info.Store then
for i = 1, #info.Store do
registerShop("miningShop", Config.Items.label, Config.Items.items, nil, info.Store[i].coords.xyz)
end
end
if info.OreBuyer then
for i = 1, #info.OreBuyer do
local name = getScript()..":OreBuyer:"..id..":"..i
registerSellShop(name, info.OreBuyer[i].coords.xyz)
end
end
end
end, true)
-- 🔥 Anti Duplicate Shop Register Fix
local alreadyRegistered = false
AddEventHandler('onResourceStart', function(res)
if res == GetCurrentResourceName() then
if alreadyRegistered then return end
alreadyRegistered = true
end
end)