QBESXStandalone
Agency-Housing الإعدادات
نظام إسكان لـ FiveM يعتمد على قاعدة بيانات مع اكتشاف تلقائي لـ ESX وQBCore وQBox.
v1.79.0مدفوع5 صفحات

01الإعدادات
تثبيت سريع · config.lua · Agency-Housing
Config = {}
-- =====================================================
-- GENERAL
-- =====================================================
-- 'auto' detects ESX / QBCore / QBox automatically. Force with 'esx' or 'qbcore'.
Config.Framework = 'auto'
-- Language file to use. See locales/ — copy _template.lua to add your own.
Config.Locale = 'en'
-- Database table name (created by sql/install.sql)
Config.TableName = 'agency_houses'
Config.Debug = false
-- Seconds a player has to wait between two actions that change something —
-- locking a door, handing over a key, saving a layout. Zero switches it off,
-- which is the default: it was a single throttle shared by every action, so two
-- unrelated clicks in a row swallowed the second one.
--
-- This is a throttle against event flooding, not a safety mechanism. Buying and
-- selling are protected separately by a per-house lock held across the database
-- write, and that does not depend on this value. Raise it only if you have a
-- reason to slow players down.
Config.ActionCooldown = 0.0
-- Heading shown on notifications. Without this Agency-Notify falls back to its
-- own default ("SYSTEM ALERT").
Config.NotifyTitle = 'Housing'
-- =====================================================
-- ACCENT COLOUR
-- =====================================================
--
-- One colour runs through the whole script: the panels, the ground markers, the
-- property border and the outline around a piece of furniture. Players pick
-- theirs under Housing > Settings, and the choice is stored on their own machine
-- — it is a preference, not something the server decides for everyone.
--
-- `rgb` the colour itself.
-- `deep` the far end of the gradients on the buttons and the badge. Roughly the
-- same hue two steps darker; it is listed rather than calculated so a
-- server can tune it by eye.
-- `ink` text drawn ON the colour. A light accent needs dark ink and the other
-- way round, which is the one thing that cannot be derived safely.
--
-- Add your own by appending an entry. The swatch row in Settings is built from
-- this list, so nothing else has to be touched.
Config.DefaultAccent = 'gold'
Config.Accents = {
{ key = 'gold', label = 'Gold', rgb = { 226, 182, 73 }, deep = { 168, 128, 31 }, ink = { 23, 18, 10 } },
{ key = 'cyan', label = 'Cyan', rgb = { 0, 212, 255 }, deep = { 10, 127, 212 }, ink = { 0, 19, 29 } },
{ key = 'mint', label = 'Mint', rgb = { 53, 214, 166 }, deep = { 15, 118, 110 }, ink = { 4, 26, 21 } },
{ key = 'violet', label = 'Violet', rgb = { 168, 133, 247 }, deep = { 109, 74, 184 }, ink = { 20, 13, 33 } },
{ key = 'crimson', label = 'Crimson', rgb = { 255, 92, 92 }, deep = { 178, 42, 42 }, ink = { 32, 10, 10 } },
{ key = 'amber', label = 'Amber', rgb = { 255, 170, 0 }, deep = { 194, 112, 0 }, ink = { 32, 20, 0 } },
{ key = 'steel', label = 'Steel', rgb = { 214, 219, 226 }, deep = { 138, 146, 158 }, ink = { 22, 24, 29 } },
}
-- Whether the chosen colour also drives what is drawn in the world: the door
-- marker, the property border, the build fence, the storage marker and the
-- outline around a selected piece of furniture. Set to false to keep the
-- colours configured further down no matter what a player picks.
--
-- The garage keeps its own amber either way — that is what tells a parking spot
-- from a doorstep at a glance — and so does the wardrobe.
Config.AccentDrivesWorld = true
-- =====================================================
-- PERMISSIONS
-- =====================================================
-- Who may open the house creator UI (/createhouse).
-- Checked in this order: FiveM ACE ("agency.housing" / "group.admin") -> framework group.
Config.AdminGroups = { 'admin', 'superadmin', 'god' }
-- =====================================================
-- HOTKEY / MENU
-- =====================================================
-- Default key for the housing menu. Players can rebind it themselves under
-- Settings > Key Bindings > FiveM, so this is only the starting value.
Config.OpenKey = 'F7'
-- Command behind the hotkey. Must stay registered — RegisterKeyMapping needs it.
Config.MenuCommand = 'housing'
-- The old chat commands. The hotkey menu covers all of them; set this to false
-- if you do not want them cluttering the command list.
Config.RegisterCommands = true
Config.Commands = {
create = 'createhouse',
delete = 'deletehouse',
list = 'houses',
}
-- Owner menu while standing inside your own house.
Config.ManageKey = 'F3'
Config.ManageCommand = 'housemenu'
-- =====================================================
-- FURNISHING
-- =====================================================
Config.MaxFurniture = 60 -- objects per house
Config.FurnitureReach = 6.0 -- how far in front of the camera you can place
-- The build camera is fenced in, otherwise you fly straight out of the
-- apartment and across the map. Which fence applies depends on where you are
-- furnishing from:
--
-- inside a preset interior or a shell -- there is no plot around you, the
-- room sits under the map, so the camera is held near the spawn point.
-- on your own property (an MLO) -- the property border itself is the fence.
-- You may furnish the whole plot: the garden, the driveway, every floor.
Config.BuildBounds = {
radius = 16.0, -- metres from the interior spawn point
below = 4.0, -- how far you may drop below it
above = 7.0, -- and rise above it
-- Vertical range when the border is the fence. The plot itself has no
-- ceiling, but a build camera needs one or it ends up in the stratosphere.
zoneBelow = 8.0,
zoneAbove = 30.0,
-- The border drawn while furnishing a plot. Nothing draws it during normal
-- play; in build mode it is the edge of what you may place on, and a fence
-- you cannot see is one you keep bumping into. Taller than the creator's,
-- because here you are looking down at it from a camera.
fenceHeight = 3.0,
fenceColor = { r = 226, g = 182, b = 73, a = 55 },
}
-- =====================================================
-- GIZMO — the axes on a selected piece
-- =====================================================
--
-- Click a piece in build mode and three axes appear on it. Drag one and only
-- that direction changes; nothing slides sideways, nothing tilts by accident.
-- G switches to moving, R to turning.
Config.Gizmo = {
enabled = true,
-- How large the axes are on screen. They are drawn in metres but scaled
-- with the camera distance, so this is roughly "fraction of the view".
screenSize = 0.14,
-- How close the cursor has to be to an axis to grab it, in screen units
-- (0..1 across the window). 0.02 is about 20 px on a 1080p screen.
pickRadius = 0.022,
-- The axes start this far above the piece's origin, or they would come out
-- of the floor for anything standing on the ground.
lift = 0.05,
-- The grid every drag snaps to. Either at 0 turns snapping off.
moveSnap = 0.05, -- metres
rotateSnap = 5.0, -- degrees
-- How many degrees a full sweep of the handle across the screen turns the
-- piece. Lower is finer.
rotateSpan = 360.0,
}
-- =====================================================
-- MLO DOORS
-- =====================================================
--
-- An MLO is part of the map: you cannot be teleported into it, you walk through
-- its front door. So an MLO house is locked at that door rather than at a
-- marker. The admin records the doors in the creator by looking at them; every
-- client then registers them with the game's own door system, and the owner and
-- their keyholders can lock and unlock them on the spot.
Config.Doors = {
max = 8, -- doors per house; a double door is two of them
range = 1.6, -- how close you have to stand to work the lock
-- How far the creator's aim reaches when recording a door.
reach = 8.0,
-- The dome sitting on a door, red while locked and green while open, the
-- same reading Agency Doorlock uses. Off: up close it is a large coloured
-- blob across the door itself, and the [E] card already says everything it
-- said. Set enabled = true to bring it back — a smaller `size` is what
-- makes it bearable at arm's length.
--
-- Shown to everyone when on, not only to the owner: that a door can be
-- locked at all is public information, whether you may work it or not.
marker = {
enabled = false,
type = 28, -- dome
distance = 12.0, -- metres
size = 0.18,
alpha = 110,
bob = false,
onlyWhenLocked = false,
},
colors = {
locked = { r = 255, g = 72, b = 72 },
unlocked = { r = 53, g = 214, b = 166 },
},
-- The [E] card at the bottom of the screen. Set to false for the plain 3D
-- text this script drew before.
prompt = true,
-- The gesture played on whoever works the lock: a short forward reach, as
-- if for a handle or a key fob. The same one Agency Doorlock uses, so a
-- player sees one movement for locking a door anywhere on the server.
--
-- Flag 48 in the code below is upper body plus secondary, which is what
-- lets the animation play without stopping the player walking.
animation = {
enabled = true,
dict = 'anim@heists@keycard@',
name = 'exit',
duration = 1100, -- ms
},
}
-- Catalogue for the furnishing mode. All GTA V base game props, so nothing has
-- to be streamed. `cat` only groups them in the UI.
--
-- Entries whose model does not exist in the running game are dropped on the
-- client before the catalogue is shown (IsModelValid). That filter is what
-- makes the generated families below safe: GTA prop names follow patterns, so
-- the ranges are expanded blindly and whatever the game does not have simply
-- never appears. Adding a range costs nothing and misses nothing.
--
-- `variants` = number of texture variations (SET_OBJECT_TEXTURE_VARIATION).
-- Most props have none, so the colour picker stays hidden unless set here.
-- Use /housevariants on a placed object to find out.
--
-- `image` = optional path to a real thumbnail, e.g. 'nui/img/couch.png'.
-- Otherwise the card shows the picture taken by /housethumbs, or a drawn icon.
Config.Furniture = {}
--- Add one prop by name.
local function add(cat, label, model, extra)
local entry = { cat = cat, label = label, model = model }
if extra then
for k, v in pairs(extra) do entry[k] = v end
end
Config.Furniture[#Config.Furniture + 1] = entry
end
--- Expand a numbered family, optionally with letter variants.
--- family('seating', 'Chair', 'prop_chair_%02d%s', 1, 12, { '', 'a', 'b', 'c' })
--- produces prop_chair_01, prop_chair_01a, prop_chair_01b … and the client
--- throws away the ones this copy of GTA does not have.
local function family(cat, label, pattern, from, to, letters)
letters = letters or { '' }
for n = from, to do
for _, letter in ipairs(letters) do
local model = pattern:format(n, letter)
local name = ('%s %d%s'):format(label, n, letter ~= '' and letter:upper() or '')
add(cat, name, model)
end
end
end
local AB = { '', 'a', 'b' }
local ABC = { '', 'a', 'b', 'c' }
-- ---------------- SEATING ----------------
family('seating', 'Chair', 'prop_chair_%02d%s', 1, 12, ABC)
family('seating', 'Office Chair', 'prop_off_chair_%02d%s', 1, 8, AB)
family('seating', 'Bench', 'prop_bench_%02d%s', 1, 12, AB)
family('seating', 'Bar Stool', 'prop_bar_stool_%02d%s', 1, 3, AB)
family('seating', 'Beach Chair', 'prop_beach_lounger_%02d%s',1, 3, AB)
family('seating', 'Sofa', 'prop_rub_couch%02d%s', 1, 4, AB)
add('seating', 'Living Room Sofa', 'v_club_officesofa')
add('seating', 'Lounge Sofa', 'v_res_mbsofa')
add('seating', 'Corner Sofa', 'v_res_d_sofa')
add('seating', 'Trailer Sofa', 'v_res_tre_sofa')
add('seating', 'Armchair', 'v_res_tre_chair')
add('seating', 'Leather Armchair', 'v_res_m_armchair')
add('seating', 'Beanbag', 'prop_beanbag_01')
add('seating', 'Wheelchair', 'prop_wheelchair_01')
-- ---------------- TABLES ----------------
family('tables', 'Table', 'prop_table_%02d%s', 1, 12, ABC)
family('tables', 'Desk', 'prop_off_desk_%02d%s', 1, 6, AB)
family('tables', 'Worktop', 'prop_tool_bench%02d%s', 1, 5, AB)
add('tables', 'Glass Table', 'v_res_tre_table')
add('tables', 'Coffee Table', 'v_res_mcoftable')
add('tables', 'Poker Table', 'prop_poker_tabl_01')
add('tables', 'Pool Table', 'prop_pooltable_02')
add('tables', 'Patio Table', 'prop_patio_table_01')
-- ---------------- BEDROOM ----------------
add('bedroom', 'Double Bed', 'v_res_fh_bed')
add('bedroom', 'Bed', 'v_res_mbed')
add('bedroom', 'Single Bed', 'v_res_tre_bed')
add('bedroom', 'Bunk Bed', 'v_res_bunkbed')
add('bedroom', 'Mattress', 'prop_rub_matress_01')
family('bedroom', 'Mattress', 'prop_rub_matress_%02d%s', 1, 4, AB)
add('bedroom', 'Wardrobe', 'v_res_fh_wardrobe')
add('bedroom', 'Closet', 'v_res_mwardrobe')
add('bedroom', 'Dresser', 'v_res_tre_drawers')
add('bedroom', 'Chest of Drawers', 'v_res_fh_drawer')
add('bedroom', 'Nightstand', 'v_res_m_bedsidetbl')
family('bedroom', 'Mirror', 'prop_wall_mirror_%02d%s', 1, 3, AB)
-- ---------------- KITCHEN ----------------
family('kitchen', 'Fridge', 'prop_fridge_%02d%s', 1, 5, AB)
family('kitchen', 'Microwave', 'prop_micro_%02d%s', 1, 3, AB)
family('kitchen', 'Cooker', 'prop_cooker_%02d%s', 1, 6, AB)
family('kitchen', 'Sink', 'prop_sink_%02d%s', 1, 8, AB)
family('kitchen', 'Bin', 'prop_bin_%02d%s', 1, 12, AB)
add('kitchen', 'Bar Fridge', 'prop_bar_fridge_01')
add('kitchen', 'Kitchen Counter', 'v_res_kitchen_unit')
add('kitchen', 'Coffee Machine', 'prop_coffee_mac_01')
add('kitchen', 'Toaster', 'prop_toaster_01')
add('kitchen', 'Kettle', 'prop_kettle_01')
add('kitchen', 'Water Cooler', 'prop_watercooler')
add('kitchen', 'Bar Counter', 'prop_beer_bar_01')
add('kitchen', 'Wine Rack', 'prop_wine_rack_01')
-- ---------------- ELECTRONICS ----------------
family('tech', 'TV', 'prop_tv_flat_%02d%s', 1, 5, AB)
family('tech', 'Monitor', 'prop_monitor_%02d%s', 1, 4, AB)
family('tech', 'PC', 'prop_pc_%02d%s', 1, 4, AB)
family('tech', 'Laptop', 'prop_laptop_%02d%s', 1, 4, AB)
family('tech', 'Keyboard', 'prop_keyboard_%02d%s', 1, 3, AB)
family('tech', 'Printer', 'prop_printer_%02d%s', 1, 3, AB)
family('tech', 'Speaker', 'prop_speaker_%02d%s', 1, 9, AB)
family('tech', 'Arcade Machine', 'prop_arcade_%02d%s', 1, 4, AB)
add('tech', 'TV Stand', 'v_res_tre_tvstand')
add('tech', 'Stereo', 'prop_boombox_01')
add('tech', 'Telephone', 'prop_phone_ing')
add('tech', 'Dartboard', 'prop_dartboard_01')
-- ---------------- LIGHTING ----------------
family('light', 'Floor Lamp', 'prop_stand_lamp_%02d%s', 1, 4, AB)
family('light', 'Table Lamp', 'prop_lamp_%02d%s', 1, 8, AB)
family('light', 'Desk Lamp', 'prop_desk_lamp_%02d%s', 1, 3, AB)
family('light', 'Ceiling Light', 'prop_light_ceiling_%02d%s',1, 5, AB)
family('light', 'Wall Light', 'prop_wall_light_%02d%s', 1, 15, AB)
family('light', 'Candle', 'prop_candle_%02d%s', 1, 4, AB)
add('light', 'Neon Sign', 'prop_neon_sign_01')
-- ---------------- DECOR ----------------
family('decor', 'Rug', 'prop_rug_%02d%s', 1, 6, AB)
family('decor', 'Shelf', 'prop_shelves_%02d%s', 1, 6, AB)
family('decor', 'Books', 'prop_books_%02d%s', 1, 6, AB)
family('decor', 'Plant', 'prop_plant_%02d%s', 1, 4, ABC)
family('decor', 'Indoor Plant', 'prop_plant_int_%02d%s', 1, 6, ABC)
family('decor', 'Fern', 'prop_plant_fern_%02d%s', 1, 3, ABC)
family('decor', 'Potted Plant', 'prop_pot_plant_%02d%s', 1, 8, ABC)
family('decor', 'Cactus', 'prop_cactus_%02d%s', 1, 4, AB)
family('decor', 'Vase', 'prop_vase_%02d%s', 1, 6, AB)
family('decor', 'Clock', 'prop_clock_%02d%s', 1, 4, AB)
family('decor', 'Boxes', 'prop_boxpile_%02d%s', 1, 8, ABC)
family('decor', 'Crate', 'prop_box_wood%02d%s', 1, 8, AB)
family('decor', 'Painting', 'prop_painting_%02d%s', 1, 6, AB)
add('decor', 'Bookcase', 'v_res_tre_bookcase')
add('decor', 'Curtains', 'v_res_curtains')
add('decor', 'Safe', 'p_v_43_safe_s')
add('decor', 'Gun Cabinet', 'prop_gun_case_01')
add('decor', 'Dumbbell', 'prop_dumbbell_01')
add('decor', 'Punching Bag', 'prop_boxing_bag_01')
add('decor', 'Weight Bench', 'prop_gym_bench_01')
add('decor', 'Fireplace', 'prop_fireplace_01')
add('decor', 'Aquarium', 'prop_fish_tank_01')
add('decor', 'Statue', 'prop_statue_01')
add('decor', 'Globe', 'prop_globe_01')
Config.FurnitureCategories = { 'seating', 'tables', 'bedroom', 'kitchen', 'tech', 'light', 'decor' }
-- =====================================================
-- UPGRADES
-- =====================================================
-- Bought once per house, paid from Config.Account. The camera module comes
-- with a front door camera; further angles are placed by the owner.
Config.Upgrades = {
cameras = {
label = 'Security Cameras',
price = 35000,
note = 'Front door camera included. Up to %s more at $%s each.',
},
}
Config.MaxCameras = 4 -- custom cameras on top of the door camera
Config.CameraPrice = 5000 -- charged for every camera beyond the included one
Config.CameraPanSpeed = 0.45 -- degrees per frame when panning a live feed with A/D
Config.CameraReach = 5.0 -- how far from the cursor a camera hangs in open air
-- Where the included door camera sits, relative to the entrance.
--
-- Straight above the door puts it inside the porch roof on a lot of houses, so
-- it is pulled back away from the building as well. The client additionally
-- checks the spot for geometry and pulls it back further if it is buried.
Config.DoorCamera = {
height = 2.05, -- metres above the entrance
back = 1.45, -- metres away from the door, towards the street
pitch = -26.0, -- tilted down onto the doorstep
label = 'Front Door',
}
-- A visible camera on the wall. Set enabled = false for invisible cameras.
Config.CameraProp = {
enabled = true,
model = 'prop_cctv_cam_06a',
headingOffset = 180.0,
drop = 0.0,
}
-- =====================================================
-- PROPERTY ZONE
-- =====================================================
--
-- The ground the house owns: the garden, the driveway, the yard, the rooms of
-- an MLO. Every house has one — the border is walked on foot in the creator,
-- corner by corner, and a house cannot be saved without it.
--
-- Walked rather than picked, because a plot is never round. An L shaped yard, a
-- driveway down one side, an MLO that has to be fenced along its own walls: a
-- circle either misses half of it or swallows the street.
--
-- The borders are handed to PolyZone (https://github.com/mkafrin/PolyZone),
-- which is a required resource — see the README. Every border on the server is
-- bundled into a single ComboZone, so the cost stays the same whether there are
-- five properties or five hundred.
Config.Zone = {
-- How often the player's position is tested against the borders, in ms.
-- 500 is PolyZone's own default and is plenty for walking pace.
checkInterval = 500,
-- Draws every border as a green PolyZone debug volume. Development only.
debugPoly = false,
poly = {
minPoints = 3, -- fewer than three corners enclose nothing
maxPoints = 24, -- the creator stops accepting corners here
-- Corners dropped closer together than this are ignored, so holding E
-- while walking does not fill the polygon with duplicates.
snap = 1.2,
-- No height setting. A property is the ground it stands on, all the
-- way up and all the way down — a balcony, a roof terrace or the top
-- floor of an MLO belongs to the plot as much as the lawn does.
},
-- The camera the border is marked with. It lifts off above the admin and
-- looks down, so the whole plot is in frame from the first second — walking
-- a boundary on foot means never seeing the shape you are making.
--
-- A line is cast from the camera onto the ground and a pole is drawn where
-- it lands. That spot, not the camera, is where the corner goes.
cam = {
height = 14.0, -- metres above the player when it starts
pitch = -55.0, -- how far it looks down to begin with
speed = 0.35, -- metres per frame
boost = 3.2, -- multiplier while holding Shift
slow = 0.30, -- multiplier while holding Alt
lookSens = 5.0, -- mouse sensitivity
reach = 600.0, -- how far the marker line is cast, in metres
pole = 2.6, -- height of the pole drawn at the marked spot
},
-- The fence drawn while a border is being marked in the creator. It is not
-- shown during play at all: a wall in the middle of a garden is in the way
-- of the house it belongs to, and from inside it stands in your window.
borderColor = { r = 226, g = 182, b = 73, a = 90 },
borderHeight = 1.6, -- how tall that fence is drawn, in metres
}
-- =====================================================
-- GARAGE
-- =====================================================
--
-- A house can have a garage point recorded in the creator. Standing on it, the
-- owner and their keyholders get a prompt that hands over to whatever garage
-- script the server runs — this resource does not manage vehicles itself.
Config.Garage = {
enabled = true,
label = 'Garage',
-- Which resource handles the vehicles. Agency-Garage is wired up below;
-- for anything else, replace `open` and leave `register` empty.
resource = 'Agency-Garage',
-- What the garage script should know about a house garage.
-- `access` is deliberately absent: this resource already draws the marker
-- and checks who owns the house, so the garage script must not add a second
-- prompt on the same spot.
template = {
type = 'public',
category = 'car',
},
-- The car appears on the garage point itself, deliberately without any
-- offset. An offset keeps the point's height while moving sideways, so on
-- a garage under a building it puts the car through the ceiling and onto
-- the roof. The garage script only treats a point as blocked when another
-- vehicle stands within 2.6 m -- a player standing there is fine -- so the
-- point is the one spot guaranteed to be on the right floor.
--- Announce a house garage to the garage script. Runs whenever houses sync.
register = function(house, id, point)
if GetResourceState(Config.Garage.resource) ~= 'started' then return end
exports[Config.Garage.resource]:RegisterGarage(id, {
label = house.name,
type = Config.Garage.template.type,
category = Config.Garage.template.category,
store = vector3(point.x, point.y, point.z),
-- Marks this as a house garage: the garage script must not settle
-- cars on the world ground here. A house sits inside a shell or an
-- interior, where that ground is the roof above it.
houseGarage = true,
spawns = { vector4(point.x, point.y, point.z, point.h or 0.0) },
})
end,
--- Announce it to the garage script's server side too.
---
--- The client draws the menu, but the server decides whether the garage id
--- is real before it hands out vehicles. Registering on one side only means
--- the menu opens and is immediately refused as unknown.
registerServer = function(house, id, point)
if GetResourceState(Config.Garage.resource) ~= 'started' then return end
exports[Config.Garage.resource]:RegisterGarage(id, {
label = house.name,
type = Config.Garage.template.type,
category = Config.Garage.template.category,
store = vector3(point.x, point.y, point.z),
-- Marks this as a house garage: the garage script must not settle
-- cars on the world ground here. A house sits inside a shell or an
-- interior, where that ground is the roof above it.
houseGarage = true,
spawns = { vector4(point.x, point.y, point.z, point.h or 0.0) },
})
end,
-- The short camera move played while a car is handed out. The player is
-- hidden and stepped aside for it, because the car appears exactly where
-- they are standing and would otherwise trap them inside it.
handover = {
enabled = true,
distance = 6.0, -- how far the camera sits from the car
height = 1.9, -- how far above it
swing = 38.0, -- degrees the camera travels around the car
entry = 320, -- glide from the player's view into the shot
time = 1400, -- milliseconds the swing may take AT MOST
wait = 5000, -- ceiling when the shot starts before the car is
-- ordered, so it covers the trip to the server
linger = 140, -- how long the camera holds once the car is there
fade = 220, -- blend back to the player view, in milliseconds
standoff = 3.0, -- where the player is put down next to the car
},
--- Open it for the owner standing on the point.
open = function(house, id)
if GetResourceState(Config.Garage.resource) ~= 'started' then
-- Fall back to an event so a different garage script can pick it up.
return TriggerEvent('agency-housing:garage:open', house.id, house.name)
end
exports[Config.Garage.resource]:OpenGarage(id)
end,
}
-- =====================================================
-- STORAGE AND WARDROBE
-- =====================================================
--
-- Both are optional points inside the house, placed by the owner from their own
-- panel (F3) rather than by an admin in the creator: they are fittings, not
-- architecture, and the person living there knows where the box belongs. Only
-- the owner may move them; owner and key holders may use them, the same rule
-- the garage follows.
--
-- Setting `enabled = false` here removes the row from the owner panel as well,
-- so a server without stashes never offers one.
--
-- The hooks below are wired to what this server actually runs (jpr-inventory
-- and illenium-appearance). Swap the bodies for a different resource; nothing
-- else in the script cares how the menu is opened.
Config.Stash = {
enabled = true,
label = 'Storage',
slots = 50,
maxWeight = 400000, -- grams, matching QB weight units
--- Stash id. Per house, so a key holder reaches the same box as the owner.
id = function(house) return ('agency_house_%s'):format(house.id) end,
--- Empty the box when the house is sold. The stash id is per house, so
--- without this the next buyer inherits whatever the last owner left in it.
---
--- Left switched off because there is no shared way to clear a stash: every
--- inventory does it differently, and calling the wrong export silently
--- does nothing. Fill in the one line your inventory needs, e.g.
---
--- ox_inventory : exports.ox_inventory:ClearInventory(stashId)
--- qs-inventory : exports['qs-inventory']:ClearStash(stashId)
--- qb-inventory : MySQL.query('DELETE FROM stashitems WHERE stash = ?', { stashId })
---
--- The house table is passed too, for inventories that key off the owner.
clear = nil,
--- Open the box. QB-style inventories expect both of these: the server
--- event opens it, the client event tells the interface which stash is
--- currently on screen so items get put back in the right place.
open = function(house, stashId)
TriggerServerEvent('inventory:server:OpenInventory', 'stash', stashId, {
maxweight = Config.Stash.maxWeight,
slots = Config.Stash.slots,
})
TriggerEvent('inventory:client:SetCurrentStash', stashId)
end,
}
Config.Wardrobe = {
enabled = true,
label = 'Wardrobe',
--- Open the outfit menu.
--- Note the spelling: illenium-appearance registers this event with a
--- single 'p' in "apearance". That is their typo, not one here -- the
--- event does not fire if it is corrected.
open = function(house)
TriggerEvent('illenium-apearance:client:outfitsCommand')
end,
}
-- =====================================================
-- SOUNDS
-- =====================================================
--
-- The door sounds are synthesised in the interface, not loaded from audio
-- files: files added to a resource do not reliably reach a client that already
-- has it cached, and a sample pack would have to be shipped and licensed.
-- Shape and pitch live in nui/script.js under DOOR.
Config.Sounds = {
enabled = true,
volume = 0.45, -- 0 to 1
-- How long the door takes to shut behind you, in milliseconds.
--
-- The slam used to fire the instant you pressed the exit marker, while you
-- were still standing in the room and the screen had not even begun to
-- fade. 400 ms puts it where the fade finishes and you are outside, which
-- is when a door actually falls shut. Raise it for a heavier door.
closeDelay = 400,
}
-- =====================================================
-- DOORBELL
-- =====================================================
Config.Doorbell = {
enabled = true,
cooldown = 12, -- seconds between rings, per house, to stop bell spam
range = 12.0, -- how far from the entrance the ring is still heard
waitTime = 90, -- seconds a caller stays listed in the owner's panel
-- Played for whoever is inside. Any frontend sound pair works.
sound = { name = 'TIMER_STOP', set = 'HUD_MINI_GAME_SOUNDSET' },
}
-- =====================================================
-- ECONOMY
-- =====================================================
Config.Account = 'bank' -- 'bank' or 'cash' — account used to buy houses
Config.SellPercentage = 0.60 -- refund when a player sells their house
Config.MaxHousesPerPlayer = 2 -- 0 = unlimited
Config.MaxKeyholders = 8 -- how many people an owner may hand keys to
-- =====================================================
-- INTERACTION
-- =====================================================
Config.InteractKey = 38 -- E
Config.InteractDistance = 1.6
Config.Marker = {
enabled = true,
type = 21,
size = { x = 0.28, y = 0.28, z = 0.22 },
color = { r = 226, g = 182, b = 73, a = 140 },
drawDistance = 12.0,
bobUpAndDown = true,
-- Entrances are recorded from the player's position, and a ped's origin
-- sits roughly 0.9 m above the ground it stands on. Without this correction
-- the marker floats at head height. Raise it towards 0 if your entrances
-- were recorded on stairs or a raised doorstep.
zOffset = -0.75,
-- Height of the "[E] House" text above the entrance.
textOffset = 0.35,
}
-- Each state gets its own colour, scale and name suffix so a house changing
-- hands is actually visible on the map, not just technically different.
--
-- `shortRange = true` means the blip only appears on the minimap when you are
-- near it — that is the normal GTA behaviour and keeps the map clean. Set it to
-- false for a state you want pinned across the whole map at all times.
-- Colours a house can be given on the map, offered as swatches in the creator.
-- `id` is the GTA blip colour; `hex` is only what the swatch looks like in the
-- interface, so it can be tuned to match without touching the game value.
Config.BlipColors = {
{ id = 1, label = 'Red', hex = '#d6342b' },
{ id = 5, label = 'Yellow', hex = '#f2d43b' },
{ id = 17, label = 'Orange', hex = '#f08a20' },
{ id = 2, label = 'Green', hex = '#4caf50' },
{ id = 3, label = 'Blue', hex = '#29a3e0' },
{ id = 26, label = 'Purple', hex = '#9b59d0' },
{ id = 27, label = 'Pink', hex = '#e06fa5' },
{ id = 4, label = 'White', hex = '#f4f5f7' },
}
-- What a house may override on its own, and within which limits. A house that
-- overrides nothing follows the state colours below, which is still the normal
-- case — the overrides are for the handful of places that should stand out.
Config.BlipOverride = {
minScale = 0.40,
maxScale = 1.40,
step = 0.05,
}
Config.Blips = {
enabled = true,
-- Sprite 374 is the house-with-a-dollar-sign icon: right for something on
-- the market, wrong once it belongs to someone. Sprite 40 is the plain
-- house without the price tag.
forSale = {
sprite = 374, color = 2, scale = 0.75, -- green, house + $
shortRange = true, suffix = ' — For sale',
},
owned = {
sprite = 40, color = 3, scale = 0.85, -- blue, plain house
shortRange = true, suffix = ' — Yours',
},
keyholder = {
sprite = 40, color = 26, scale = 0.70, -- purple, plain house
shortRange = true, suffix = ' — Key',
},
other = {
sprite = 40, color = 4, scale = 0.60, -- white, plain house
shortRange = true, suffix = ' — Sold',
},
showOwnedByOthers = false, -- set true to draw the 'other' state above
}
-- =====================================================
-- 3D PREVIEW (creator "View in 3D")
-- =====================================================
Config.Preview = {
distance = 3.2, -- starting camera distance in metres
minDist = 0.8, -- fully zoomed in
maxDist = 7.0, -- fully zoomed out (kept inside the room, see fovMax)
zoomStep = 0.45, -- metres per mouse wheel notch
zoomSmooth = 0.25, -- 0..1, how fast the camera eases to the new distance
orbitSpeed = 0.25, -- degrees per frame
height = 0.9, -- camera height above the interior spawn point
-- GTA interiors are only drawn while the camera is inside them, so pulling
-- the camera far enough back to "see it all" would just show the void.
-- Zooming out widens the lens instead: same viewpoint, much more in frame.
fovMin = 50.0, -- fully zoomed in
fovMax = 100.0, -- fully zoomed out
-- Free-fly mode (F). A camera has no body: it cannot fall through the map,
-- get stuck in geometry or take fall damage, which is why the preview flies
-- instead of teleporting the player.
freeSpeed = 0.10, -- metres per frame
freeBoost = 3.5, -- multiplier while holding Shift
freeSlow = 0.30, -- multiplier while holding Alt
lookSens = 6.0, -- mouse sensitivity
fadeMs = 150, -- blend between entries
}
-- =====================================================
-- SHELLS / INTERIORS
-- =====================================================
-- `category` groups the entry in the creator UI: 'house', 'mlo' or 'shell'.
-- It is purely cosmetic — `type` below decides how the entry actually works.
--
-- Two supported modes per shell entry:
--
-- type = 'interior' -> teleports into an existing GTA V interior. Works on ANY
-- server, no extra resource, no props to stream.
-- type = 'shell' -> spawns a shell prop under the map (needs a shells
-- resource such as qb-interiors / ox_shells that provides
-- the model). Left in as ready-to-use examples.
--
-- `interior` is the vector4 the player is teleported to (x, y, z, heading).
-- `exitOffset` is only used by type = 'shell'.
--
-- TIP: every coordinate below can be re-recorded live. Open /createhouse, stand
-- where you want the interior spawn to be and press "Use my position" — the
-- house is then stored with its own interior coords and this list is ignored
-- for that house. No config editing needed.
Config.ShellSpawn = vector3(1200.0, 4800.0, -100.0) -- base point for type='shell'
Config.Shells = {
-- ---------- VANILLA GTA V INTERIORS (no dependencies) ----------
['motel'] = {
label = 'Motel Room',
category = 'house',
type = 'interior',
price = 25000,
interior = vector4(152.11, -1004.36, -99.0, 340.0),
},
['apartment_low'] = {
label = 'Low End Apartment',
category = 'house',
type = 'interior',
price = 55000,
interior = vector4(265.97, -1007.44, -101.01, 0.0),
},
['apartment_mid'] = {
label = 'Mid End Apartment',
category = 'house',
type = 'interior',
price = 95000,
interior = vector4(346.77, -1012.85, -99.19, 180.0),
},
['apartment_high'] = {
label = 'High End Apartment',
category = 'house',
type = 'interior',
price = 180000,
interior = vector4(-786.87, 315.76, 217.64, 180.0),
},
['eclipse_towers'] = {
label = 'Eclipse Towers Penthouse',
category = 'house',
type = 'interior',
price = 420000,
interior = vector4(-774.01, 342.02, 196.69, 180.0),
},
['trailer'] = {
label = 'Trailer',
category = 'house',
type = 'interior',
price = 18000,
interior = vector4(1972.03, 3815.72, 33.43, 120.0),
},
['stilt_house'] = {
label = 'Stilt House (Vinewood Hills)',
category = 'house',
type = 'interior',
price = 650000,
interior = vector4(-174.29, 497.06, 137.67, 180.0),
},
['garage_small'] = {
label = 'Small Garage',
category = 'mlo',
type = 'interior',
price = 40000,
interior = vector4(227.19, -991.42, -99.5, 90.0),
},
['garage_large'] = {
label = 'Large Garage',
category = 'mlo',
type = 'interior',
price = 120000,
interior = vector4(174.06, -1002.66, -99.5, 90.0),
},
['office'] = {
label = 'Executive Office',
category = 'mlo',
type = 'interior',
price = 300000,
interior = vector4(-141.24, -620.83, 168.82, 270.0),
},
['clubhouse'] = {
label = 'Biker Clubhouse',
category = 'mlo',
type = 'interior',
price = 260000,
interior = vector4(1108.03, -3157.11, -37.51, 180.0),
},
['bunker'] = {
label = 'Bunker',
category = 'mlo',
type = 'interior',
price = 500000,
interior = vector4(894.53, -3245.86, -98.27, 0.0),
},
['hangar'] = {
label = 'Hangar',
category = 'mlo',
type = 'interior',
price = 380000,
interior = vector4(-1267.02, -3013.35, -49.5, 330.0),
},
['nightclub'] = {
label = 'Nightclub',
category = 'mlo',
type = 'interior',
price = 750000,
interior = vector4(-1605.53, -3012.7, -78.02, 180.0),
},
['arcade'] = {
label = 'Arcade',
category = 'mlo',
type = 'interior',
price = 340000,
interior = vector4(2721.5, -376.02, -48.5, 180.0),
},
-- ---------- SHELL PROPS (require a shells resource) ----------
['shell_small'] = {
label = 'Small Shell House',
category = 'shell',
type = 'shell',
model = 'shell_v16low',
price = 90000,
exitOffset = vector3(0.0, -3.5, 1.0),
},
['shell_medium'] = {
label = 'Medium Shell House',
category = 'shell',
type = 'shell',
model = 'shell_v16mid',
price = 170000,
exitOffset = vector3(1.4, -4.2, 1.1),
},
['shell_large'] = {
label = 'Large Shell Mansion',
category = 'shell',
type = 'shell',
model = 'shell_v16high',
price = 400000,
exitOffset = vector3(3.2, -6.1, 1.2),
},
}
-- Order the shells appear in the creator UI. Keys not listed are appended.
Config.ShellOrder = {
'trailer', 'motel', 'apartment_low', 'garage_small', 'apartment_mid',
'garage_large', 'apartment_high', 'clubhouse', 'office', 'arcade',
'hangar', 'eclipse_towers', 'bunker', 'stilt_house', 'nightclub',
'shell_small', 'shell_medium', 'shell_large',
}
02ملف SQL
استورد ملف SQL هذا إلى قاعدة بياناتك قبل تشغيل المورد:
-- =====================================================================
-- Agency Housing - Database
--
-- Two tables. `agency_houses` holds the houses themselves, one row each.
-- `agency_house_inside` remembers who was inside which house, so a player
-- who disconnects indoors is put back where they were on the next join
-- instead of on the pavement outside.
--
-- The resource adds later columns by itself on every start
-- (ALTER TABLE ... ADD COLUMN IF NOT EXISTS), so an existing install only
-- ever needs the two CREATE statements below. Running this file twice is
-- harmless: every statement is IF NOT EXISTS.
--
-- The table name follows Config.TableName. Rename it here too if you
-- change it there.
-- =====================================================================
CREATE TABLE IF NOT EXISTS `agency_houses` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
-- What the house is and what it costs.
`name` VARCHAR(64) NOT NULL,
`shell` VARCHAR(64) DEFAULT NULL,
-- interior | shell | mlo. Decides whether the house is entered through a
-- marker or walked into through its own front door.
`kind` VARCHAR(16) NOT NULL DEFAULT interior,
`price` INT NOT NULL DEFAULT 0,
-- Positions, each stored as JSON. `interior` is only set when the house
-- carries its own recorded coordinates; otherwise the shell entry in
-- config.lua applies.
`entrance` LONGTEXT DEFAULT NULL,
`interior` LONGTEXT DEFAULT NULL,
`exit_point` LONGTEXT DEFAULT NULL,
`stash_point` LONGTEXT DEFAULT NULL,
`wardrobe_point` LONGTEXT DEFAULT NULL,
`garage` LONGTEXT DEFAULT NULL,
-- The property border: the corners walked in the creator, handed to
-- PolyZone on start. Required for houses created since borders exist.
`zone` LONGTEXT DEFAULT NULL,
-- The MLO doors this house owns, for kind = mlo.
`doors` LONGTEXT DEFAULT NULL,
-- Per-house map blip override: {"name":"Villa","color":5,"scale":0.9}
-- Any field may be absent; what is missing follows the state styles in
-- Config.Blips (for sale / owned / keyholder).
`blip` LONGTEXT DEFAULT NULL,
-- Ownership. `owner` is the framework identifier, NULL while the house
-- is unsold.
`owner` VARCHAR(64) DEFAULT NULL,
`owner_name` VARCHAR(64) DEFAULT NULL,
`keys` LONGTEXT DEFAULT NULL,
-- State flags.
`locked` TINYINT(1) NOT NULL DEFAULT 1,
`for_sale` TINYINT(1) NOT NULL DEFAULT 1,
`show_blip` TINYINT(1) NOT NULL DEFAULT 1,
-- What the owner did with the place.
`furniture` LONGTEXT DEFAULT NULL,
`upgrades` LONGTEXT DEFAULT NULL,
`cameras` LONGTEXT DEFAULT NULL,
`created_by` VARCHAR(64) DEFAULT NULL,
INDEX `owner` (`owner`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Who is inside which house. One row per player at most, hence the
-- identifier as primary key: the resource writes with ON DUPLICATE KEY.
CREATE TABLE IF NOT EXISTS `agency_house_inside` (
`identifier` VARCHAR(64) NOT NULL PRIMARY KEY,
`house_id` INT NOT NULL,
`since` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
INDEX `house_id` (`house_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;