Files
community_bridge/lib/client/placeable_object.lua
T
gononono64 99389dcd2d itroducing placeable object + gizmo mix + raycasting
NOTE: Did not change Placeable. Should not impact current resources

rebound_entities are still WIP
2025-02-12 08:58:36 -06:00

196 lines
8.4 KiB
Lua

Scaleform = Scaleform or Require("lib/client/scaleform.lua")
Utility = Utility or Require("lib/client/utility.lua")
Gizmo = Gizmo or Require("lib/client/gizmo.lua")
Raycast = Raycast or Require("lib/client/raycast.lua")
PlaceableObject = PlaceableObject or {}
local data = {
entity = nil,
placing = false,
}
function PlaceableObject.Eyetrace(depth, disableSphere)
local screenX = GetDisabledControlNormal(0, 239)
local screenY = GetDisabledControlNormal(0, 240)
local world, normal = GetWorldCoordFromScreenCoord(screenX, screenY)
local playerPos = GetEntityCoords(PlayerPedId())
local target = playerPos + normal * depth
if not disableSphere then
DrawSphere(target.x, target.y, target.z, 0.5, 255, 0, 0, 0.5)
end
return target
end
function PlaceableObject.InBoundary(pos, boundary)
if not boundary then return false end
local x, y, z = table.unpack(pos)
local minX, minY, minZ = table.unpack(boundary.min)
local maxX, maxY, maxZ = table.unpack(boundary.max)
return x >= minX and x <= maxX and y >= minY and y <= maxY and z >= minZ and z <= maxZ
end
-- Creates a placeable object in the game world.
-- @param model (string) The model of the object to create.
-- @param onConfirm (function) [optional] The callback function to execute when the object is confirmed.
-- @param onUpdate (function) [optional] The callback function to execute when the object is moved.
-- @param onCancel (function) [optional] The callback function to execute when the object is canceled.
-- @param settings (table) [optional] Additional settings for the placeable object.
-- - depthMin (number) The minimum depth for the object placement. Default is 2.0.
-- - depthMax (number) The maximum depth for the object placement. Default is 10.0.
-- - rotationStep (number) The rotation step for the object. Default is 15.0.
-- - depthStep (number) The depth step for the object. Default is 1.0.
function PlaceableObject.Create(data, model, onConfirm, onUpdate, onCancel, settings)
local settings = settings or {}
local depthMin = settings.depthMin or 2.0
depthMax = settings.depthMax or 10.0
rotationStep = settings.rotationStep or 15.0
depthStep = settings.depthStep or 1.0
disableSphere = settings.useSphere and false or true
local depth = settings.depth or 10.0
data.placing = true
data.paused = false
local initialPos = nil
local placeOnGround = true
local allowedMats = settings.allowedMats
local boundary = settings.boundary
local config = settings.config or {}
--scaleform
local scaleform = Scaleform.SetupInstructionalButtons({
{type = "CLEAR_ALL"},
{type = "SET_CLEAR_SPACE", int = 200},
{type = "SET_DATA_SLOT", name = config?.place_object?.name or 'Place Object:', keyIndex = config?.place_object?.key or {223}, int = 5},
{type = "SET_DATA_SLOT", name = config?.cancel_placement?.name or 'Cancel Placement:', keyIndex = config?.cancel_placement?.key or {222}, int = 4},
{type = "SET_DATA_SLOT", name = config?.snap_to_ground?.name or 'Snap to Ground:', keyIndex = config?.snap_to_ground?.key or {19}, int = 1},
{type = "SET_DATA_SLOT", name = config?.rotate?.name or 'Rotate:', keyIndex = config?.rotate?.key or {14, 15}, int = 2},
{type = "SET_DATA_SLOT", name = config?.distance?.name or 'Distance:', keyIndex = config?.distance?.key or {14,15,36}, int = 3},
{type = "SET_DATA_SLOT", name = config?.toggle_placement?.name or 'Toggle Placement:', keyIndex = config?.toggle_placement?.key or {199}, int = 0},
{type = "SET_DATA_SLOT", name = config?.gizmo?.name or 'Toggle Gizmo:', keyIndex = config?.gizmo?.key or {38}, int = 6},
{type = "DRAW_INSTRUCTIONAL_BUTTONS"},
{type = "SET_BACKGROUND_COLOUR"},
})
local heading = -GetEntityHeading(PlayerPedId())
local pos = PlaceableObject.Eyetrace(depth, disableSphere)
if not data.spawned and model then
--get player heading
local obj = Utility.CreateProp(model, pos, vector3(0, 0, heading), nil)
data.spawned = obj
end
assert(data.spawned, "Failed to create object")
SetEntityCollision(data.spawned, false, false)
SetEntityAlpha(data.spawned, 150, false)
SetPedConfigFlag(PlayerPedId(), 146, true)
SetCanClimbOnEntity(data.spawned, false)
SetEntityCompletelyDisableCollision(data.spawned, true, false)
SetEntityNoCollisionEntity(PlayerPedId(), data.spawned, false)
local inBounds = true
if allowedMats or boundary then
CreateThread(function()
while data.placing do
local hit, _, coords, _, materialHash = Raycast.FromCamera(1, 4)
if hit and hit ~=1 and (allowedMats?[materialHash] or PlaceableObject.InBoundary(GetEntityCoords(data.spawned), boundary)) then
if not inBounds then
inBounds = true
SetEntityDrawOutlineColor(0, 255, 0, 255)
SetEntityDrawOutline(data.spawned, true)
end
elseif inBounds then
inBounds = false
SetEntityDrawOutline(data.spawned, false)
end
Wait(500)
end
end)
end
CreateThread(function()
while data.placing do
if data.paused then goto continue end
DrawScaleformMovieFullscreen(scaleform, 255, 255, 255, 255, 0)
DisableControlAction(0, 24, true) -- Attack
DisableControlAction(0, 25, true) -- Aim
DisableControlAction(0, 36, true) -- INPUT_DUCK.
if IsDisabledControlJustPressed(0, 223) then -- left click
if not inBounds then
Notify.SendNotify("You can't place this object here", "error", 5000)
elseif onConfirm then
onConfirm(data.spawned, pos, heading)
data.placing = false
end
end
if IsDisabledControlJustPressed(0, 25) then -- right click
data.placing = false
if onCancel then
onCancel(data.spawned, pos, heading)
else
DeleteEntity(data.spawned)
end
return
end
if IsControlJustPressed(0,19) then
placeOnGround = not placeOnGround
end
if IsControlJustPressed(0, 38) then -- TAB key to switch to gizmo
data.placing = false
return Gizmo.UseGizmo(data.spawned, onConfirm, onUpdate, onCancel, settings)
end
if IsControlPressed(0,224) then -- left ctrl
if IsControlJustPressed(0, 261) then -- scroll up
depth = depth + depthStep
if depth > depthMax then
depth = depthMax
end
end
if IsControlJustPressed(0, 262) then -- scroll down
depth = depth - depthStep
if depth < depthMin then
depth = depthMin
end
end
else
if IsControlJustPressed(0, 261) then -- scroll up
heading = heading + rotationStep
if heading > 360 then
heading = 0
end
end
if IsControlJustPressed(0, 262) then -- scroll down
heading = heading - rotationStep
if heading < 0 then
heading = 360
end
end
end
pos = PlaceableObject.Eyetrace(depth, disableSphere)
if data.spawned then
SetEntityCoords(data.spawned, pos.x, pos.y, pos.z)
SetEntityHeading(data.spawned, heading)
if placeOnGround then
PlaceObjectOnGroundProperly(data.spawned)
pos = GetEntityCoords(data.spawned)
end
end
if onUpdate and type(onUpdate) == 'function' then onUpdate(data.spawned, pos, heading) end
::continue::
if IsControlJustPressed(0, 199) then -- home
data.paused = not data.paused
end
Wait(1)
end
end)
end
return PlaceableObject