Scripts/resources/[ps]/ps-objectspawner/client/main.lua

376 lines
12 KiB
Lua
Raw Permalink Normal View History

2024-12-29 20:28:24 +00:00
local QBCore = exports['qb-core']:GetCoreObject()
local ObjectList = {} -- Object, Model, Coords, IsRendered, SpawnRange
local PlacingObject, LoadedObjects = false, false
local CurrentModel, CurrentObject, CurrentObjectType, CurrentObjectName, CurrentSpawnRange, CurrentCoords = nil, nil, nil, nil, nil, nil
local group = {user = true}
local ObjectTypes = {
"none",
"container",
}
local ObjectParams = {
["container"] = {event = "ps-objectspawner:client:containers", icon = "fas fa-question", label = "Container", SpawnRange = 200},
["none"] = {SpawnRange = 200},
}
--Functions
local function openMenu()
SetNuiFocus(true, true)
if LoadedObjects then
SendNUIMessage({
action = "open",
})
else
LoadedObjects = true
local tempList = {}
-- In js, objects cant have number keys so we need to change them to strings to be treated as object
-- If we dont do this it will be sent as an array which is bad because it fills in missing array indexes
-- from 0 to min(table ids)
for k, v in pairs(ObjectList) do
tempList[""..k] = v
end
SendNUIMessage({
action = "load",
objects = Objects,
objectTypes = ObjectTypes,
spawnedObjects = tempList,
})
end
end
local function CancelPlacement()
DeleteObject(CurrentObject)
PlacingObject = false
CurrentObject = nil
CurrentObjectType = nil
CurrentObjectName = nil
CurrentSpawnRange = nil
CurrentCoords = nil
end
AddEventHandler('onResourceStart', function(resourceName)
if GetCurrentResourceName() == resourceName then
QBCore.Functions.TriggerCallback('ps-objectspawner:server:RequestObjects', function(incObjectList)
ObjectList = incObjectList
end)
end
end)
AddEventHandler('onResourceStop', function(resourceName)
if GetCurrentResourceName() == resourceName then
for k, v in pairs(ObjectList) do
if v["IsRendered"] then
RemoveRoadNodeSpeedZone(v["speedzone"])
DeleteObject(v["object"])
end
end
end
end)
RegisterNetEvent('ps-objectspawner:client:registerobjectcommand', function(perms)
permission = perms
if permission == 'god' then
openMenu()
end
end)
RegisterNetEvent('QBCore:Client:OnPlayerLoaded', function()
QBCore.Functions.TriggerCallback('ps-objectspawner:server:RequestObjects', function(incObjectList)
ObjectList = incObjectList
end)
end)
local function ButtonMessage(text)
BeginTextCommandScaleformString("STRING")
AddTextComponentScaleform(text)
EndTextCommandScaleformString()
end
local function Button(ControlButton)
N_0xe83a3e3557a56640(ControlButton)
end
local function setupScaleform(scaleform)
local scaleform = RequestScaleformMovie(scaleform)
while not HasScaleformMovieLoaded(scaleform) do
Citizen.Wait(0)
end
-- draw it once to set up layout
DrawScaleformMovieFullscreen(scaleform, 255, 255, 255, 0, 0)
PushScaleformMovieFunction(scaleform, "CLEAR_ALL")
PopScaleformMovieFunctionVoid()
PushScaleformMovieFunction(scaleform, "SET_CLEAR_SPACE")
PushScaleformMovieFunctionParameterInt(200)
PopScaleformMovieFunctionVoid()
PushScaleformMovieFunction(scaleform, "SET_DATA_SLOT")
PushScaleformMovieFunctionParameterInt(0)
Button(GetControlInstructionalButton(2, 152, true))
ButtonMessage("Cancel")
PopScaleformMovieFunctionVoid()
PushScaleformMovieFunction(scaleform, "SET_DATA_SLOT")
PushScaleformMovieFunctionParameterInt(1)
Button(GetControlInstructionalButton(2, 153, true))
ButtonMessage("Place object")
PopScaleformMovieFunctionVoid()
PushScaleformMovieFunction(scaleform, "SET_DATA_SLOT")
PushScaleformMovieFunctionParameterInt(2)
Button(GetControlInstructionalButton(2, 190, true))
Button(GetControlInstructionalButton(2, 189, true))
ButtonMessage("Rotate object")
PopScaleformMovieFunctionVoid()
PushScaleformMovieFunction(scaleform, "DRAW_INSTRUCTIONAL_BUTTONS")
PopScaleformMovieFunctionVoid()
PushScaleformMovieFunction(scaleform, "SET_BACKGROUND_COLOUR")
PushScaleformMovieFunctionParameterInt(0)
PushScaleformMovieFunctionParameterInt(0)
PushScaleformMovieFunctionParameterInt(0)
PushScaleformMovieFunctionParameterInt(80)
PopScaleformMovieFunctionVoid()
return scaleform
end
local function RequestSpawnObject(object)
local hash = GetHashKey(object)
RequestModel(hash)
while not HasModelLoaded(hash) do
Wait(1000)
end
end
local function RotationToDirection(rotation)
local adjustedRotation =
{
x = (math.pi / 180) * rotation.x,
y = (math.pi / 180) * rotation.y,
z = (math.pi / 180) * rotation.z
}
local direction =
{
x = -math.sin(adjustedRotation.z) * math.abs(math.cos(adjustedRotation.x)),
y = math.cos(adjustedRotation.z) * math.abs(math.cos(adjustedRotation.x)),
z = math.sin(adjustedRotation.x)
}
return direction
end
local function RayCastGamePlayCamera(distance)
local cameraRotation = GetGameplayCamRot()
local cameraCoord = GetGameplayCamCoord()
local direction = RotationToDirection(cameraRotation)
local destination =
{
x = cameraCoord.x + direction.x * distance,
y = cameraCoord.y + direction.y * distance,
z = cameraCoord.z + direction.z * distance
}
local a, b, c, d, e = GetShapeTestResult(StartShapeTestSweptSphere(cameraCoord.x, cameraCoord.y, cameraCoord.z, destination.x, destination.y, destination.z, 0.2, 339, PlayerPedId(), 4))
return b, c, e
end
local function PlaceSpawnedObject(heading)
local ObjectType = 'prop' --will be replaced with inputted prop type later, which will determine options/events
local Options = { SpawnRange = tonumber(CurrentSpawnRange) }
if ObjectParams[CurrentObjectType] ~= nil then
Options = { event = ObjectParams[CurrentObjectType].event, icon = ObjectParams[CurrentObjectType].icon, label = ObjectParams[CurrentObjectType].label, SpawnRange = ObjectParams[CurrentObjectType].SpawnRange} --will be replaced with config of options later
end
local finalCoords = vector4(CurrentCoords.x, CurrentCoords.y, CurrentCoords.z, heading)
TriggerServerEvent("ps-objectspawner:server:CreateNewObject", CurrentModel, finalCoords, CurrentObjectType, Options, CurrentObjectName)
DeleteObject(CurrentObject)
PlacingObject = false
CurrentObject = nil
CurrentObjectType = nil
CurrentObjectName = nil
CurrentSpawnRange = nil
CurrentCoords = nil
CurrentModel = nil
end
local function CreateSpawnedObject(data)
if data.object == nil then return print("Invalid Object") end
local object = data.object
CurrentObjectType = data.type
CurrentObjectName = data.name or "Random Object"
CurrentSpawnRange = ObjectParams[objectType] and ObjectParams[objectType] ~= nil or data.distance or 15
RequestSpawnObject(object)
CurrentModel = object
CurrentObject = CreateObject(object, 1.0, 1.0, 1.0, true, true, false)
local heading = 0.0
SetEntityHeading(CurrentObject, 0)
SetEntityAlpha(CurrentObject, 150)
SetEntityCollision(CurrentObject, false, false)
-- SetEntityInvincible(CurrentObject, true)
FreezeEntityPosition(CurrentObject, true)
CreateThread(function()
form = setupScaleform("instructional_buttons")
while PlacingObject do
local hit, coords, entity = RayCastGamePlayCamera(20.0)
CurrentCoords = coords
DrawScaleformMovieFullscreen(form, 255, 255, 255, 255, 0)
if hit then
SetEntityCoords(CurrentObject, coords.x, coords.y, coords.z)
end
if IsControlJustPressed(0, 174) then
heading = heading + 5
if heading > 360 then heading = 0.0 end
end
if IsControlJustPressed(0, 175) then
heading = heading - 5
if heading < 0 then heading = 360.0 end
end
if IsControlJustPressed(0, 44) then
CancelPlacement()
end
SetEntityHeading(CurrentObject, heading)
if IsControlJustPressed(0, 38) then
PlaceSpawnedObject(heading)
end
Wait(1)
end
end)
end
exports("CreateSpawnedObject", CreateSpawnedObject)
RegisterNUICallback('close', function(data, cb)
SetNuiFocus(false, false)
cb('ok')
end)
RegisterNUICallback('spawn', function(data, cb)
SetNuiFocus(false, false)
PlacingObject = true
CreateSpawnedObject(data)
cb('ok')
end)
RegisterNetEvent("ps-objectspawner:client:UpdateObjectList", function(NewObjectList)
ObjectList = NewObjectList
end)
CreateThread(function()
while true do
for k, v in pairs(ObjectList) do
local data = v["options"]
local objectCoords = v["coords"]
local playerCoords = GetEntityCoords(PlayerPedId())
local dist = #(playerCoords - vector3(objectCoords["x"], objectCoords["y"], objectCoords["z"]))
if dist < data["SpawnRange"] and v["IsRendered"] == nil then
local object = CreateObject(v["model"], objectCoords["x"], objectCoords["y"], objectCoords["z"], false, false, false)
SetEntityHeading(object, objectCoords["w"])
SetEntityAlpha(object, 0)
PlaceObjectOnGroundProperly(object)
FreezeEntityPosition(object, true)
v["IsRendered"] = true
v["object"] = object
--local model = GetEntityModel(object)
--local min, max = GetModelDimensions(model) --TODO: get max model dimensions to generate the SpeedZone radius
v["speedzone"] = AddRoadNodeSpeedZone(objectCoords["x"], objectCoords["y"], objectCoords["z"], 10.0, 0, false)
for i = 0, 255, 51 do
Wait(50)
SetEntityAlpha(v["object"], i, false)
end
if ObjectParams[v.type] ~= nil and ObjectParams[v.type].event ~= nil then
exports['qb-target']:AddTargetEntity(object, {
--debugPoly=true,
options = {
{
name = "object_spawner_"..object,
event = ObjectParams[v.type].event,
icon = ObjectParams[v.type].icon,
label = ObjectParams[v.type].label,
id = v.id
},
},
distance = ObjectParams[data.SpawnRange]
})
end
end
if dist >= data["SpawnRange"] and v["IsRendered"] then
if DoesEntityExist(v["object"]) then
for i = 255, 0, -51 do
Wait(50)
SetEntityAlpha(v["object"], i, false)
end
DeleteObject(v["object"])
RemoveRoadNodeSpeedZone(v["speedzone"])
v["object"] = nil
v["IsRendered"] = nil
end
end
end
Wait(1500)
end
end)
RegisterNetEvent("ps-objectspawner:client:AddObject", function(object)
ObjectList[object.id] = object
if permission == 'god' then
SendNUIMessage({
action = "created",
newSpawnedObject = object,
})
end
end)
RegisterNUICallback('tpTo', function(data, cb)
if permission == 'god' then
SetEntityCoords(PlayerPedId(), data.coords.x+1, data.coords.y+1, data.coords.z)
end
cb('ok')
end)
RegisterNUICallback('delete', function(data, cb)
if permission == 'god' then
TriggerServerEvent("ps-objectspawner:server:DeleteObject", data.id)
end
cb('ok')
end)
RegisterNetEvent('ps-objectspawner:client:receiveObjectDelete', function(id)
if permission == 'god' then
if ObjectList[id]["IsRendered"] then
if DoesEntityExist(ObjectList[id]["object"]) then
for i = 255, 0, -51 do
Wait(50)
SetEntityAlpha(ObjectList[id]["object"], i, false)
end
DeleteObject(ObjectList[id]["object"])
RemoveRoadNodeSpeedZone(ObjectList[id]["speedzone"])
end
end
ObjectList[id] = nil
SendNUIMessage({
action = "delete",
id = id,
})
end
end)