local isJustPressed = IsDisabledControlJustPressed local isHeld = IsDisabledControlPressed local drawLine = DrawLine local drawPoly = DrawPoly local actionControls = ActionControls creator = {} creator.raycast = { flags = { ped = 17, vehicle = 17, object = 1 }, defaults = { models = { ped = "mp_m_shopkeep_01", vehicle = "t20", object = "prop_paper_bag_01" } }, entities = { ped = 1, vehicle = 2, object = 3 }, minPointLength = Config.MinPointLength, height = 25.0, points = {} } function creator.updateUI(self) if not self.visible then return end local creatorGarages = {} for _, garageData in pairs(Config.Garages) do if garageData.creator then creatorGarages[#creatorGarages + 1] = garageData end end local gang = GetGang() SendReactMessage("toggle_creator", { visible = true, data = { garages = creatorGarages, jobs = self.jobs, job = GetJobName(), gang = (gang and gang.name) or "" } }) end function creator.open(self) if raycast.active then return Notification(i18n.t("raycast.must_be_completed"), "error") end if not self.jobs then local data = lib.callback.await("garages:getCreatorData", false) if not data then return Error("creator:open", "No data returned from server. Did you follow the docs?", data) end self.jobs = data.jobs end ToggleHud(false) self.visible = true SetNuiFocus(true, true) self:updateUI() Debug("Creator opened") end function creator.close(self) if not self.visible then return end self.visible = false ToggleHud(true) SendReactMessage("toggle_creator", { visible = false }) Debug("Creator closed") end RegisterCommand("garagecreator", function() local hasPermission = lib.callback.await("garages:hasPermission", false) if not hasPermission then return Notification(i18n.t("no_permission"), "error") end creator:open() end) RegisterCommand("creategarage", function() print("[GARAGES] Commande /creategarage invoquee") ExecuteCommand("garagecreator") end) print("[GARAGES] Alias /creategarage enregistre avec succes !") function creator.getPointLength(self, points) local totalLength = 0.0 for i = 1, #points do local nextPoint = points[i + 1] or points[1] totalLength = totalLength + #(points[i] - nextPoint) end return totalLength end function creator.drawRectangle(self, corners, allPoints) local totalLength = self:getPointLength(allPoints) local isComplete = #allPoints >= 4 local r = isComplete and 255 or 255 local g = isComplete and 255 or 40 local b = isComplete and 0 or 24 local function poly(p1, p2, p3) drawPoly(p1.x, p1.y, p1.z, p2.x, p2.y, p2.z, p3.x, p3.y, p3.z, r, g, b, 100) end poly(corners[1], corners[2], corners[3]) poly(corners[2], corners[1], corners[3]) poly(corners[1], corners[4], corners[3]) poly(corners[4], corners[1], corners[3]) end function creator.drawLines(self) local height = self.raycast.height local halfHeight = vec(0, 0, height / 2) for i = 1, #self.raycast.points do local currentPoint = self.raycast.points[i] local z = self.raycast.zCoords or currentPoint.z self.raycast.points[i] = vec(currentPoint.x, currentPoint.y, z) local topPoint = self.raycast.points[i] + halfHeight local bottomPoint = self.raycast.points[i] - halfHeight local nextIdx = i + 1 local nextPoint = self.raycast.points[nextIdx] or self.raycast.points[1] local nextTop = nextPoint + halfHeight local nextBottom = nextPoint - halfHeight local function line(a, b) drawLine(a.x, a.y, a.z, b.x, b.y, b.z, 255, 42, 24, 225) end line(topPoint, bottomPoint) line(topPoint, nextTop) line(bottomPoint, nextBottom) line(currentPoint, nextPoint) self:drawRectangle({ topPoint, bottomPoint, nextBottom, nextTop }, self.raycast.points) end end function creator.isPointInAnyZone(self, coords) for _, zone in pairs(PolyZones) do if zone:contains(coords) then return true end end return false end function creator.raycastRectangle(self) self.raycast.points = {} local pedCoords = GetEntityCoords(cache.ped) self.raycast.zCoords = math.round(pedCoords.z) + 0.0 self.raycast.height = 25.0 actionControls.leftClick.label = i18n.t("creator.raycast.add_point") actionControls.rotate_z_scroll.label = i18n.t("creator.raycast.point_size") Notification(i18n.t("creator.raycast.info"), "info") raycast:freeCamera(function(camData) creator:drawLines() if isJustPressed(0, actionControls.cancel.codes[1]) then camData:destroy() end if isJustPressed(0, actionControls.done.codes[1]) then if not creator.raycast.points then Notification(i18n.t("creator.raycast.no_point_selected"), "error") else camData:destroy() end end if isJustPressed(0, actionControls.leftClick.codes[1]) and camData.hit then if creator:isPointInAnyZone(camData.coords) then Notification(i18n.t("creator.raycast.point_in_another_zone"), "error") else local pts = creator.raycast.points pts[#pts + 1] = vec3(camData.coords.x, camData.coords.y, camData.coords.z) end end if isJustPressed(0, actionControls.undo_point.codes[1]) then local pts = creator.raycast.points if #pts > 0 then pts[#pts] = nil end end if isHeld(0, actionControls.boundary_height.codes[1]) then creator.raycast.height = creator.raycast.height + 15.0 * GetFrameTime() elseif isHeld(0, actionControls.boundary_height.codes[2]) then creator.raycast.height = creator.raycast.height - 15.0 * GetFrameTime() end end, { "done", "undo_point", "leftClick", "cancel", "boundary_height" }) creator.raycast.zCoords = nil if #creator.raycast.points < 4 then return nil end return { points = creator.raycast.points, thickness = creator.raycast.height } end function creator.isInPoints(self, points, hitEntity) local totalLength = self:getPointLength(points) if #points < 3 then return false end local minZ = points[1].z local maxZ = points[1].z local halfHeight = creator.raycast.height / 2.4 for i = 2, #points do if minZ > points[i].z then minZ = points[i].z end if maxZ < points[i].z then maxZ = points[i].z end end if hitEntity.z < (minZ - halfHeight) or hitEntity.z > (maxZ + halfHeight) then return false end local x = hitEntity.x local y = hitEntity.y local inside = false for i = 1, #points do local j = (i % #points) + 1 local xi = points[i].x local yi = points[i].y local xj = points[j].x local yj = points[j].y if (yi < y) ~= (yj < y) then local intersectX = (xj - xi) * (y - yi) / (yj - yi) + xi if x < intersectX then inside = not inside end end end return inside end function creator.selectPoint(self, entityType, count, options) if #self.raycast.points == 0 then if not (options and options.disablePoints) then return Notification(i18n.t("creator.no_points_selected"), "error") end end if not entityType then entityType = "empty" end if not count then count = 1 end if not options then options = {} end local selectedPoints = {} local heading = 0 local raycastFlags = self.raycast.flags[entityType] local entityHandle = nil options.points = options.points or {} if entityType ~= "empty" then options.model = options.model or self.raycast.defaults.models[entityType] lib.requestModel(options.model) end local function spawnPreviewEntity(model, coords) if not model or entityType == "empty" then return nil end local entity = nil if entityType == "ped" then entity = CreatePed(28, model, coords.x, coords.y, coords.z, coords.w or 0, false, false) elseif entityType == "vehicle" then entity = CreateVehicle(model, coords.x, coords.y, coords.z, coords.w or 0, false, true) elseif entityType == "object" then entity = CreateObject(model, coords.x, coords.y, coords.z, false, false) if coords.w then SetEntityHeading(entity, coords.w) end end if entity then SetEntityAsMissionEntity(entity, true, true) FreezeEntityPosition(entity, true) SetEntityInvincible(entity, true) end return entity end entityHandle = spawnPreviewEntity(options.model and joaat(options.model)) for _, savedPoint in pairs(options.points) do if savedPoint.model then lib.requestModel(savedPoint.model) local pointEntity = spawnPreviewEntity(joaat(savedPoint.model), savedPoint.coords) savedPoint.handle = pointEntity if pointEntity then SetEntityDrawOutline(pointEntity, true) if savedPoint.coords.w then SetEntityHeading(pointEntity, savedPoint.coords.w) end end end end actionControls.leftClick.label = i18n.t("creator.raycast.add_point") actionControls.rotate_z.label = i18n.t("creator.raycast.rotate_z_scroll") actionControls.rightClick.label = i18n.t("creator.raycast.undo") local controls = { "leftClick", "rightClick" } if entityType ~= "empty" then controls[#controls + 1] = "rotate_z" end if options.pressEnterToSelect then actionControls.done.label = i18n.t("creator.raycast.press_enter_to_select") controls[#controls + 1] = "done" controls[#controls + 1] = "rightClick" end raycast:gameplayCamera(function(camData) DisableControlAction(0, 25, true) DisableControlAction(0, 24, true) DisableControlAction(0, 20, true) DisableControlAction(0, 73, true) DisableControlAction(0, 191, true) for _, pt in pairs(options.points) do DrawMarker(28, pt.coords.x, pt.coords.y, pt.coords.z, 0, 0, 0, 0, 0, 0, 0.2, 0.2, 0.2, 255, 42, 24, 100, false, false, 0, true, false, false, false) end creator:drawLines() if not camData.hit then return end if entityHandle then if camData.lastCoords ~= camData.coords then SetEntityCoords(entityHandle, camData.coords.x, camData.coords.y, camData.coords.z, false, false, true) end end if isJustPressed(0, 24) then if not (options and options.disablePoints) then if not creator:isInPoints(creator.raycast.points, camData.coords) then return Notification(i18n.t("creator.raycast.not_in_points"), "error") end end local clickCoords = vec3(camData.coords.x, camData.coords.y, camData.coords.z) local newEntity = nil if entityType ~= "empty" then local clickVec4 = vec4(clickCoords.x, clickCoords.y, clickCoords.z, GetEntityHeading(entityHandle)) clickCoords = clickVec4 newEntity = spawnPreviewEntity(nil, clickVec4) if newEntity then SetEntityAlpha(newEntity, 170, false) end end selectedPoints[#selectedPoints + 1] = { coords = clickCoords, handle = newEntity } if not options.pressEnterToSelect then if #selectedPoints == count then Notification(i18n.t("creator.raycast.completed"), "info") camData:destroy() else Notification(i18n.t("creator.raycast.selected_point", { count = #selectedPoints }), "info") end end end if options.pressEnterToSelect and isJustPressed(0, actionControls.done.codes[1]) then Notification(i18n.t("creator.raycast.completed"), "info") camData:destroy() end if isJustPressed(0, actionControls.rightClick.codes[1]) then if #selectedPoints > 0 then local lastEntry = selectedPoints[#selectedPoints] if lastEntry and lastEntry.handle then DeleteEntity(lastEntry.handle) end selectedPoints[#selectedPoints] = nil end end if entityType ~= "empty" then if isHeld(0, 20) then heading = (heading + 1.0) % 360 SetEntityHeading(entityHandle, heading) elseif isHeld(0, 73) then heading = (heading - 1.0) % 360 SetEntityHeading(entityHandle, heading) end end end, controls, raycastFlags) for _, pt in pairs(selectedPoints) do if pt.handle then DeleteEntity(pt.handle) end end for _, savedPt in pairs(options.points) do if savedPt.model then DeleteEntity(savedPt.handle) SetModelAsNoLongerNeeded(savedPt.model) end end Utils.RemoveInstructional() if entityHandle then DeleteEntity(entityHandle) SetModelAsNoLongerNeeded(options.model) end return table.map(selectedPoints, function(pt) return pt.coords end) end RegisterCommand("t1", function() print(json.encode(coords)) end) function creator.selectEntity(self, entityType, count, options) if #self.raycast.points == 0 then return Notification(i18n.t("creator.no_points_selected"), "error") end assert(entityType, "creator:selectEntity ::: entityType is required") if not count then count = 1 end if not options then options = {} end local selectedEntities = {} local heading = 0 local currentZ = 0 local trackedEntity = nil local currentHoveredEnt = nil local pendingPedEntity = nil options.disabledEntities = options.disabledEntities or {} for _, disabledEnt in pairs(options.disabledEntities) do SetEntityDrawOutline(disabledEnt, true) end if options.ped then lib.requestModel(options.ped.model) pendingPedEntity = CreatePed(28, options.ped.model, 0, 0, 0, 0, false, false) SetEntityVisible(pendingPedEntity, false, false) SetEntityInvincible(pendingPedEntity, true) FreezeEntityPosition(pendingPedEntity, true) SetEntityCompletelyDisableCollision(pendingPedEntity, true, false) local anim = options.ped.anim lib.requestAnimDict(anim.dict, 3000) TaskPlayAnim(pendingPedEntity, anim.dict, anim.name, 8.0, 1.0, -1, 1, 0, false, false, false) RemoveAnimDict(anim.dict) options.disabledEntities[#options.disabledEntities + 1] = pendingPedEntity local controls = { "leftClick" } controls[#controls + 1] = "rotate_z" controls[#controls + 1] = "offset_z" end Notification(i18n.t("creator.raycast.select_entity_info", { entityType = entityType }), "info") raycast:gameplayCamera(function(camData) DisableControlAction(0, actionControls.rotate_z.codes[1], true) DisableControlAction(0, actionControls.rotate_z.codes[2], true) DisableControlAction(0, actionControls.offset_z.codes[1], true) DisableControlAction(0, actionControls.offset_z.codes[2], true) creator:drawLines() if pendingPedEntity then Utils.DrawEntityBoundingBox(pendingPedEntity, 255, 42, 24, 100) end if currentHoveredEnt ~= camData.entity then local isDisabled = table.contains(options.disabledEntities, currentHoveredEnt) if not isDisabled then SetEntityDrawOutline(currentHoveredEnt, false) if pendingPedEntity then SetEntityVisible(pendingPedEntity, false, false) end end end currentHoveredEnt = camData.entity if camData.hit and camData.entity then local isNewEntity = (currentHoveredEnt ~= camData.entity) if isNewEntity then local isDisabled = table.contains(options.disabledEntities, camData.entity) if not isDisabled then SetEntityDrawOutline(camData.entity, true) if pendingPedEntity then SetEntityVisible(pendingPedEntity, true, false) end end end if isJustPressed(0, 24) then local isDisabled = table.contains(options.disabledEntities, camData.entity) if not isDisabled then local coords = vec4( GetEntityCoords(camData.entity).x, GetEntityCoords(camData.entity).y, GetEntityCoords(camData.entity).z + currentZ, GetEntityHeading(camData.entity) ) selectedEntities[#selectedEntities + 1] = { entity = camData.entity, coords = coords } if not options.pressEnterToSelect then if #selectedEntities == count then camData:destroy() end end end end end if options.pressEnterToSelect and isJustPressed(0, actionControls.done.codes[1]) then camData:destroy() end if pendingPedEntity then if isHeld(0, actionControls.rotate_z.codes[1]) then heading = (heading + 1.0) % 360 SetEntityHeading(pendingPedEntity, heading) elseif isHeld(0, actionControls.rotate_z.codes[2]) then heading = (heading - 1.0) % 360 SetEntityHeading(pendingPedEntity, heading) end if isHeld(0, actionControls.offset_z.codes[1]) then currentZ = currentZ + GetFrameTime() elseif isHeld(0, actionControls.offset_z.codes[2]) then currentZ = currentZ - GetFrameTime() end end end, { "leftClick" }, self.raycast.flags[entityType]) Utils.RemoveInstructional() if pendingPedEntity then DeleteEntity(pendingPedEntity) end return table.map(selectedEntities, function(entry) return { entity = entry.entity, coords = { x = entry.coords.x, y = entry.coords.y, z = entry.coords.z, w = entry.coords.w } } end) end