Need help? Join our Discord for support and updates

Executive Offices

v1.0.0UtlityESXQBQBOX
On this page

Executive Offices

Overview

The Executive Offices script for FiveM allows players to own and manage advanced office spaces, complete with features like private garages, safes, and assistants. Users can engage in various synchronized activities and utilize tactical planning tools, enhancing the roleplay experience. The script supports multiple frameworks and offers a range of interactive commands for office management.

Compatibility

Framework: ESX, QBCore, QBox, Standalone

Target: ox_target, qb-target, or qtarget (optional) — falls back to 3D text / TextUI if none are installed

Dependencies

Required Dependencies

    • oxmysql
    • bob74_ipl

Optional Dependencies

    • ox_lib
    • ox_target or qb-target or qtarget
    • qb-inventory or qs-inventory
    • es_extended or qb-core

Installation

1. Upload the resource

Place the resource folder inside your server's resources directory (rename it to drc_office if needed).

2. Install dependencies

Make sure all of the following are installed and started before this script:

  • oxmysql

3. Import database

This script uses a database. Import the included drc_office/install/drc_office.sql — or run the following SQL directly:

-- DRC Executive Offices
-- Fresh installation schema for MySQL / MariaDB.
-- Existing installations are migrated automatically when the resource starts.

CREATE TABLE IF NOT EXISTS `drc_offices` (
    `slot` TINYINT UNSIGNED NOT NULL,
    `office_id` VARCHAR(50) NOT NULL,
    `owner` VARCHAR(80) NOT NULL,
    `owner_name` VARCHAR(100) NOT NULL,
    `owner_phone` VARCHAR(50) NULL DEFAULT NULL,
    `preset` VARCHAR(50) NOT NULL,
    `agency_art` TINYINT UNSIGNED NOT NULL DEFAULT 1,
    `agency_tint` TINYINT UNSIGNED NOT NULL DEFAULT 0,
    `assistant` VARCHAR(20) NULL DEFAULT NULL,
    `assistant_secondary` VARCHAR(20) NULL DEFAULT NULL,
    `assistant_appearance` LONGTEXT NULL,
    `has_tactical_table` TINYINT(1) NOT NULL DEFAULT 0,
    `dog_breed` VARCHAR(20) NULL DEFAULT NULL,
    `has_locker` TINYINT(1) NOT NULL DEFAULT 0,
    `has_safe` TINYINT(1) NOT NULL DEFAULT 0,
    `has_garage` TINYINT(1) NOT NULL DEFAULT 0,
    `garage_preset` VARCHAR(30) NOT NULL DEFAULT 'concrete',
    `company_name` VARCHAR(100) NOT NULL,
    `company_color` CHAR(7) NOT NULL DEFAULT '#000000',
    `safe_left` TINYINT(1) NOT NULL DEFAULT 0,
    `safe_right` TINYINT(1) NOT NULL DEFAULT 0,
    `safe_balance` BIGINT UNSIGNED NOT NULL DEFAULT 0,
    `total_paid` BIGINT UNSIGNED NOT NULL DEFAULT 0,
    `purchased_at` BIGINT UNSIGNED NOT NULL DEFAULT 0,
    `visitors` LONGTEXT NULL,
    `access` LONGTEXT NULL,
    `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`slot`),
    UNIQUE KEY `uq_drc_offices_owner` (`owner`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS `drc_office_garage` (
    `plate` VARCHAR(12) NOT NULL,
    `slot` SMALLINT UNSIGNED NOT NULL,
    `parking_spot` TINYINT UNSIGNED NULL,
    `owner` VARCHAR(80) NOT NULL,
    `stored_at` INT UNSIGNED NOT NULL DEFAULT 0,
    PRIMARY KEY (`plate`),
    KEY `slot` (`slot`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS `drc_office_mapboards` (
    `office_slot` INT NOT NULL,
    `strokes` LONGTEXT NOT NULL,
    `markers` LONGTEXT NULL,
    `permissions` LONGTEXT NOT NULL,
    `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    PRIMARY KEY (`office_slot`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

4. Add to server.cfg

ensure oxmysql
ensure drc_office

5. Configure the script

Open the config file and adjust the settings for your server. See the Config section below.

6. Restart and test

Restart your server (or start the resource) and test it in-game, e.g. with /agencydogspawn.

Config

drc_office/shared/sh_config.lua

Config = {}

Config.Language = 'en' -- 'en' or 'cs'
Config.Debug = false
Config.Framework = 'auto-detect' -- auto-detect, standalone, esx or qbcore
Config.NewESX = true
Config.InteractionType = 'target' -- target, 3dtext or textui
Config.FrameworkTarget = 'auto-detect' -- qtarget, qb-target, ox_target or auto-detect
Config.Inventory = "auto-detect" -- auto-detect, ox, quasar, chezza, qb, qb-new
Config.NotificationType = 'ox_lib'
Config.Progress = 'ox_lib'
Config.TextUI = 'ox_lib' -- 'gta' = native GTA help text, 'ox_lib' = ox_lib TextUI
Config.KeySystem = 'none' -- none, qs, wasabi, fivecode, qbcore or custom

-- Phone integration used by the executive assistant. Set Phone to false to
-- disable it. RingCooldown prevents one visitor from spamming missed-call SMS.
Config.Phone = 'lb-phone' -- lb-phone, qs-smartphone, gksphone, high-phone, yseries, 17mov_Phone, okokPhone, roadphone, jpr-phonesystem, sbf_phone, or false
Config.AssistantSMS = {
    Enabled = true,
    SenderNumber = 'Executive Assistant',
    RingCooldown = 60
}

-- Protects instanced office sessions from death, disconnects and resource/client
-- crashes. A stale local marker restores the player's validated office session.
Config.SessionProtection = {
    Enabled = true,
    ExitOnDeath = true,
    ReconnectRescue = true,
    DeathCheckInterval = 300,
    RecoveryDelay = 3500
}

-- Company boss menu available from the CEO computer. The script automatically
-- uses the player's current job. QBCore checks job.isboss; other frameworks use BossGradeNames.
Config.Bossmenu = 'esx' -- esx, qbcore, vms, zerio, or drc
Config.BossMenu = {
    Enabled = true,
    BossGradeNames = {
        boss = true
    }
}

Config.DefaultBuilding = 'maze_bank_tower'
Config.Buildings = {
    maze_bank_tower = {
        id = 'maze_bank_tower',
        label = L('building_maze_bank_tower'),
        enabled = true,
        ipl = {
            resource = 'bob74_ipl',
            objectExport = 'GetFinanceOffice2Object'
        },
        teleport = {
            entry = vector4(-68.72, -799.83, 44.23, 340.0),
            interior = vector4(-75.8228, -826.9456, 243.3857, 72.9),
            rooftop = vector4(-75.4404, -824.6033, 321.2923, 143.8816)
        },
        chair = {
            searchCenter = vector3(-82.593, -801.0, 243.385),
            boardroomCenter = vector3(-60.115589141846, -806.81652832031, 242.98204040527)
        },
        assistant = {
            coords = vector4(-71.95586953, -814.34771210, 242.37710000, 161.4495),
            -- Positions derived from Maze Bank West; fine-tune them for this interior if needed.
            desk = {
                chair = {
                    model = 'ex_prop_offchair_exec_03',
                    coords = vector4(-71.9230, -813.6122, 242.3771, 342.9568)
                },
                phone = {
                    model = 'vw_prop_casino_phone_01a',
                    originalModel = 'prop_off_phone_01',
                    originalCoords = vector3(-71.2116, -815.2229, 243.1776),
                    searchRadius = 3.0,
                    heading = 170.3797
                },
                coffee = {
                    -- Position where the assistant stands at the coffee machine.
                    machine = vector4(-61.9076, -818.4446, 243.3860, 280.8902),
                    -- Waypoints from the desk to the coffee machine (reversed on the way back).
                    path = {
                        vector3(-72.1756, -814.1731, 243.3857),
                        vector3(-69.2301, -816.2446, 243.3857),
                        vector3(-68.5081, -818.2308, 243.3857),
                        vector3(-65.6453, -818.9931, 243.3980)
                    },
                    -- Position where the mug is placed while brewing.
                    machineCup = {
                        coords = vector3(-61.22698974609375, -818.2568359375, 243.4764862060547),
                        rotation = vector3(0.0, 0.0, 102.99874114990236)
                    },
                    -- Position where the assistant stands while finishing the coffee.
                    drinkSpot = vector4(-72.6292, -814.1288, 243.3860, 165.7646),
                    -- Mug position on the assistant's desk.
                    mug = {
                        model = 'v_ret_ta_mug',
                        coords = vector3(-73.50834655761719, -814.495849609375, 243.24310302734375),
                        rotation = vector3(0.0, 0.0, 102.99874877929688)
                    }
                }
            },
            -- Assistant -> "Outside": exterior vehicle spawn points.
            vehicleSpawnPoints = {
                vector4(-32.8313, -791.2683, 44.2237, 277.5916),
                vector4(-42.9128, -790.9021, 44.2238, 257.6813),
                vector4(-50.4281, -787.8771, 44.2238, 243.8956),
                vector4(-61.6240, -779.9169, 44.2226, 224.1102)
            },
            -- Assistant -> "Garage": destination vehicle spawn points.
            garageSpawnPoints = {
                vector4(-97.4977, -804.9476, 36.4749, 218.7982),
                vector4(-99.5372, -807.2713, 36.3605, 221.0684),
                vector4(-103.8625, -813.7568, 36.0178, 248.2062),
                vector4(-105.5879, -817.0943, 35.8455, 245.9591),
                vector4(-106.4774, -820.9844, 35.6542, 235.6055),
                vector4(-106.7337, -824.5859, 35.4818, 275.7375),
                vector4(-68.4787, -807.8618, 36.5911, 139.4497),
                vector4(-65.8372, -810.1285, 36.5056, 123.5145),
                vector4(-63.8162, -813.1183, 36.3810, 114.6675),
                vector4(-62.2185, -817.1063, 36.2054, 106.1000),
                vector4(-61.6564, -820.8833, 36.0317, 90.8294),
                vector4(-61.6873, -824.7566, 35.8481, 78.7809)
            }
        },
        sign = {
            position = vector3(-75.8228, -826.9456, 243.3857),
            renderTarget = 'prop_ex_office_text',
            model = 'ex_prop_ex_office_text'
        },
        safe = {
            position = vector3(-82.593, -801.0, 243.385),
            leftCoords = vector3(-83.526062011719, -803.26849365234, 243.43630981445),
            rightCoords = vector3(-81.871711730957, -798.72552490234, 243.43630981445),
            leftHeading = 250.0
        },
        -- Office garage (valet mode): purchased as an extra in /offices.
        garage = {
            objectExport = 'GetImportCEOGarage2Object',
            interiorPart = 'Garage1',
            -- Location where the player drives in to store a vehicle (vehicle elevator).
            storeZone = vector4(-84.1521, -821.4365, 35.3067, 348.1742),
            -- Elevator platform inside the garage. The vehicle is first moved below it,
            -- then the lift animation begins.
            vehicleElevator = vector4(-76.8900, -829.5800, 221.1100, 0.0),
            -- Player arrival point from the passenger elevator on the garage floor.
            walkIn = vector4(-91.7751, -821.2618, 222.0006, 306.4546),
            -- Physical parking spaces across the three CEO garage levels.
            parkingSpots = {
                vector4(-85.24082, -818.235535, 220.23494, -144.433289),
                vector4(-76.9321747, -819.9362, 220.234451, 176.218323),
                vector4(-71.09352, -822.318542, 220.234543, 140.8338),
                vector4(-67.9834747, -828.685547, 220.234467, 94.56514),
                vector4(-70.25896, -836.0581, 220.234222, 45.08332),
                vector4(-84.3974152, -819.8488, 225.672928, -148.987808),
                vector4(-76.77495, -819.621033, 225.672867, 171.160126),
                vector4(-71.1063461, -823.0381, 225.672928, 136.971),
                vector4(-67.4827652, -828.406433, 225.672943, 92.51076),
                vector4(-70.124855, -835.7496, 225.673065, 49.92409),
                vector4(-85.98553, -820.654846, 231.018417, -142.9082),
                vector4(-77.21592, -820.092957, 231.018982, 178.564911),
                vector4(-71.42833, -822.0914, 231.018478, 133.955643),
                vector4(-68.0297, -828.6458, 231.018784, 88.1085358),
                vector4(-70.2125, -835.0709, 231.0186, 48.27413)
            },
            -- Exterior vehicle elevator. After the reversed animation, the vehicle appears
            -- at the same entrance used to store it.
            spawnPoints = {
                vector4(-84.1100, -821.2500, 35.6300, 351.1500)
            }
        }
    },
    arcadius = {
        id = 'arcadius', label = L('building_arcadius'), enabled = true,
        ipl = { resource = 'bob74_ipl', objectExport = 'GetFinanceOffice1Object' },
        teleport = {
            entry = vector4(-116.81, -604.58, 36.28, 70.0),
            interior = vector4(-141.708818, -620.948110, 168.870700, 278.9),
            rooftop = vector4(-136.8158, -596.1592, 206.9105, 264.1059)
        },
        -- Actual IPL origin, used only to transform local points between offices.
        layoutAnchor = vector4(-141.4987, -620.9130, 168.8205, 278.9),
        chair = {
            searchCenter = vector3(-124.25, -641.30, 168.870),
            boardroomCenter = vector3(-147.002361, -645.925587, 168.467040)
        },
        assistant = {
            coords = vector4(-139.66184139, -633.96616692, 167.86210000, 7.4495),
            -- Positions derived from Maze Bank West; fine-tune them for this interior if needed.
            desk = {
                chair = {
                    model = 'ex_prop_offchair_exec_03',
                    coords = vector4(-139.3691, -634.6417, 167.8621, 188.9568)
                },
                phone = {
                    model = 'vw_prop_casino_phone_01a',
                    originalModel = 'prop_off_phone_01',
                    originalCoords = vector3(-140.270388, -633.510588, 168.620930),
                    searchRadius = 3.0,
                    heading = 26.4961
                },
                coffee = {
                    machine = vector4(-150.045112, -634.693668, 168.829330, 126.890200),
                    path = {
                        vector3(-138.943792, -634.031672, 168.829030),
                        vector3(-142.499276, -633.461043, 168.829030),
                        vector3(-144.018898, -631.992362, 168.829030),
                        vector3(-146.926136, -632.562180, 168.841330)
                    },
                    machineCup = {
                        coords = vector3(-150.574530, -635.160789, 168.919816),
                        rotation = vector3(0.0, 0.0, 308.998741)
                    },
                    drinkSpot = vector4(-138.516679, -633.872644, 168.829330, 11.764600),
                    mug = {
                        model = 'v_ret_ta_mug',
                        coords = vector3(-137.887412, -633.157349, 168.686433),
                        rotation = vector3(0.0, 0.0, 308.998749)
                    }
                }
            },
            -- Assistant -> "Outside": exterior vehicle spawn points.
            vehicleSpawnPoints = {
                vector4(-110.5974, -616.2058, 36.2656, 159.4127),
                vector4(-107.3253, -606.7595, 36.2656, 159.2125),
                vector4(-99.5503, -588.9263, 36.2557, 123.0244)
            },
            -- Assistant -> "Garage": destination vehicle spawn points.
            garageSpawnPoints = {
                vector4(-140.5338, -594.5033, 32.4245, 93.3681),
                vector4(-139.0528, -590.9170, 32.4245, 67.4573),
                vector4(-137.9874, -587.0488, 32.4245, 67.0416),
                vector4(-141.8087, -598.4326, 32.4245, 71.9669),
                vector4(-156.3671, -597.6523, 32.4245, 71.0685),
                vector4(-155.5476, -593.8319, 32.4245, 69.6511),
                vector4(-154.2967, -590.0490, 32.2112, 68.1430),
                vector4(-165.2259, -576.9338, 32.4244, 248.0168),
                vector4(-166.8240, -580.6039, 32.4245, 240.2231),
                vector4(-169.9979, -588.0058, 32.4245, 240.1703)
            }
        },
        sign = {
            position = vector3(-141.708818, -620.948110, 168.870700),
            renderTarget = 'prop_ex_office_text', model = 'ex_prop_ex_office_text'
        },
        safe = {
            position = vector3(-124.25, -641.30, 168.870),
            leftCoords = vector3(-124.405812, -638.852064, 168.921310),
            rightCoords = vector3(-123.901225, -643.660477, 168.921310),
            leftHeading = 96.0
        },
        garage = {
            objectExport = 'GetImportCEOGarage1Object',
            interiorPart = 'Garage1',
            -- Exterior point used to store a vehicle and later drive it out.
            storeZone = vector4(-143.9370, -576.1690, 32.4245, 155.8781),
            -- Arcadius Garage 1 is rotated +26 degrees from the Maze Bank reference garage.
            -- The previous -154-degree transform pointed in the opposite direction.
            vehicleElevator = vector4(-181.709302, -581.673150, 135.110000, 26.000000),
            walkIn = vector4(-198.7344, -580.7220, 136.0006, 286.6978),
            parkingSpots = {
                vector4(-194.188055, -575.137571, 134.234940, 241.566711),
                vector4(-185.974772, -573.023848, 134.234451, 202.218323),
                vector4(-179.682674, -572.605585, 134.234543, 166.833800),
                vector4(-174.096272, -576.964858, 134.234467, 120.565140),
                vector4(-172.909550, -584.588771, 134.234222, 71.083320),
                vector4(-192.722799, -576.217840, 139.672928, 237.012192),
                vector4(-185.971619, -572.671655, 139.672867, 197.160126),
                vector4(-179.378768, -573.257943, 139.672928, 162.971000),
                vector4(-173.768593, -576.494495, 139.672943, 118.510760),
                vector4(-172.924255, -584.252706, 139.673065, 75.924090),
                vector4(-193.796840, -577.638493, 145.018417, 243.091800),
                vector4(-186.161083, -573.289126, 145.018982, 204.564911),
                vector4(-180.083172, -572.548203, 145.018478, 159.955643),
                vector4(-174.155243, -576.949397, 145.018784, 114.108536),
                vector4(-173.300552, -583.681115, 145.018600, 74.274130)
            },
            spawnPoints = {
                vector4(-143.9370, -576.1690, 32.4245, 155.8781)
            }
        }
    },
    lombank = {
        id = 'lombank', label = L('building_lombank_west'), enabled = true,
        ipl = { resource = 'bob74_ipl', objectExport = 'GetFinanceOffice3Object' },
        teleport = {
            entry = vector4(-1581.46, -557.82, 34.95, 215.0),
            interior = vector4(-1579.375725, -564.816355, 108.527900, 308.9),
            rooftop = vector4(-1560.7532, -569.1786, 114.4484, 44.5763)
        },
        chair = {
            searchCenter = vector3(-1554.08, -573.7122, 108.5272),
            boardroomCenter = vector3(-1571.471329, -589.094257, 108.124240)
        },
        assistant = {
            coords = vector4(-1571.09396271, -575.06683494, 107.51930000, 37.4495),
            -- Positions derived from Maze Bank West; fine-tune them for this interior if needed.
            desk = {
                chair = {
                    model = 'ex_prop_offchair_exec_03',
                    coords = vector4(-1570.5026, -575.5054, 107.5193, 218.9568)
                },
                phone = {
                    model = 'vw_prop_casino_phone_01a',
                    originalModel = 'prop_off_phone_01',
                    originalCoords = vector3(-1572.2358, -575.1944, 108.3198),
                    searchRadius = 3.0,
                    heading = 56.4961
                },
                coffee = {
                    machine = vector4(-1580.109358, -581.106278, 108.528200, 156.890200),
                    path = {
                        vector3(-1570.826331, -574.982312, 108.527900),
                        vector3(-1574.190785, -576.265874, 108.527900),
                        vector3(-1576.241157, -575.753770, 108.527900),
                        vector3(-1578.473989, -577.700866, 108.540200)
                    },
                    machineCup = {
                        coords = vector3(-1580.334287, -581.775525, 108.618686),
                        rotation = vector3(0.0, 0.0, 338.998741)
                    },
                    drinkSpot = vector4(-1570.535955, -574.631033, 108.528200, 41.764600),
                    mug = {
                        model = 'v_ret_ta_mug',
                        coords = vector3(-1570.348640, -573.696936, 108.385303),
                        rotation = vector3(0.0, 0.0, 338.998749)
                    }
                }
            },
            -- Assistant -> "Outside": exterior vehicle spawn points.
            vehicleSpawnPoints = {
                vector4(-1600.8574, -550.6784, 34.7447, 311.4289),
                vector4(-1596.1202, -546.8539, 34.9464, 309.5117),
                vector4(-1588.0861, -540.0878, 35.2035, 308.8603)
            },
            -- Assistant -> "Garage": destination vehicle spawn points.
            garageSpawnPoints = {
                vector4(-1555.0598, -571.4216, 25.7079, 215.0975),
                vector4(-1553.0619, -569.1874, 25.7078, 214.0734),
                vector4(-1558.2183, -572.5042, 25.7079, 201.5009),
                vector4(-1540.2065, -560.0936, 25.7076, 225.0849),
                vector4(-1537.5955, -558.2851, 25.7079, 239.4308),
                vector4(-1534.9987, -556.4719, 25.7079, 239.7592),
                vector4(-1543.8259, -582.8467, 25.7079, 32.6582),
                vector4(-1546.3337, -584.5580, 25.7079, 26.0425),
                vector4(-1548.6771, -586.6136, 25.7079, 29.9554),
                vector4(-1551.5396, -588.4955, 25.7079, 61.0154),
                vector4(-1554.2257, -589.9069, 25.7079, 49.1690),
                vector4(-1557.2980, -591.6714, 25.7079, 37.4068)
            }
        },
        sign = {
            position = vector3(-1579.375725, -564.816355, 108.527900),
            renderTarget = 'prop_ex_office_text', model = 'ex_prop_ex_office_text'
        },
        safe = {
            position = vector3(-1554.08, -573.7122, 108.5272),
            leftCoords = vector3(-1555.438905, -571.670131, 108.578510),
            rightCoords = vector3(-1552.597714, -575.582045, 108.578510),
            leftHeading = 126.0
        },
        garage = {
            objectExport = 'GetImportCEOGarage3Object',
            interiorPart = 'Garage1',
            -- Exterior point used to store a vehicle and later drive it out.
            storeZone = vector4(-1537.4257, -577.8056, 25.7078, 31.9370),
            -- Lombank Garage 1 is rotated -34 degrees from Maze Bank Garage 1.
            vehicleElevator = vector4(-1578.667637, -576.728009, 85.610000, 326.000000),
            walkIn = vector4(-1586.3499, -561.4644, 86.5004, 31.2419),
            parkingSpots = {
                vector4(-1579.247036, -562.653302, 84.734940, 181.566711),
                vector4(-1573.309857, -568.709352, 84.734451, 142.218323),
                vector4(-1569.801581, -573.949338, 84.734543, 106.833800),
                vector4(-1570.783621, -580.966939, 84.734467, 60.565140),
                vector4(-1576.792763, -585.806627, 84.734222, 11.083320),
                vector4(-1579.449948, -564.462385, 90.172928, 177.012192),
                vector4(-1573.003272, -568.535986, 90.172867, 137.160126),
                vector4(-1570.214586, -574.538706, 90.172928, 102.971000),
                vector4(-1570.212435, -581.015536, 90.172943, 58.510760),
                vector4(-1576.509074, -585.625860, 90.173065, 15.924090),
                vector4(-1581.217290, -564.242565, 95.518417, 183.091800),
                vector4(-1573.632750, -568.680641, 95.518982, 144.564911),
                vector4(-1569.952135, -573.573805, 95.518478, 99.955643),
                vector4(-1570.799717, -580.908139, 95.518784, 54.108536),
                vector4(-1576.202211, -585.014182, 95.518600, 14.274130)
            },
            spawnPoints = {
                vector4(-1537.4257, -577.8056, 25.7078, 31.9370)
            }
        }
    },
    maze_bank_west = {
        id = 'maze_bank_west', label = L('building_maze_bank_west'), enabled = true,
        ipl = { resource = 'bob74_ipl', objectExport = 'GetFinanceOffice4Object' },
        teleport = {
            entry = vector4(-1370.85, -503.38, 33.16, 215.0),
            interior = vector4(-1392.635189, -480.238453, 72.050700, 10.9),
            rooftop = vector4(-1369.5989, -472.0570, 84.4469, 293.4199)
        },
        -- Actual IPL origin used as the reference point for this layout.
        layoutAnchor = vector4(-1392.6670, -480.4736, 72.04217, 10.9),
        chair = {
            searchCenter = vector3(-1372.905, -462.08, 72.05),
            boardroomCenter = vector3(-1367.488184, -484.657070, 71.647040)
        },
        assistant = {
            coords = vector4(-1379.6965, -477.7384, 71.0421, 99.4495),
            -- The assistant uses a dedicated chair and occasionally makes phone calls.
            desk = {
                chair = {
                    model = 'ex_prop_offchair_exec_03',
                    coords = vector4(-1379.0316, -477.4223, 71.0421, 280.9568)
                },
                phone = {
                    model = 'vw_prop_casino_phone_01a',
                    originalModel = 'prop_off_phone_01',
                    originalCoords = vector3(-1380.1201, -478.8064, 71.842575073242),
                    heading = 118.4961
                }
            },
            -- Assistant -> "Outside": exterior vehicle spawn points.
            vehicleSpawnPoints = {
                vector4(-1346.2568, -523.8737, 31.6009, 125.6929),
                vector4(-1350.7373, -527.4612, 31.0594, 123.8608),
                vector4(-1356.5107, -531.1049, 30.7647, 126.4723),
                vector4(-1373.0142, -538.2178, 30.0844, 85.9095),
                vector4(-1380.9487, -536.4824, 30.3187, 63.7331)
            },
            -- Assistant -> "Garage": destination vehicle spawn points.
            garageSpawnPoints = {
                vector4(-1370.8925, -466.5498, 31.5954, 192.8649),
                vector4(-1373.9050, -466.9721, 31.5953, 188.0956),
                vector4(-1376.8707, -467.4860, 31.5951, 188.0488),
                vector4(-1379.9263, -467.9613, 31.5947, 176.3674),
                vector4(-1383.0557, -468.7381, 31.5942, 185.7874),
                vector4(-1385.9237, -469.0766, 31.5939, 187.8482),
                vector4(-1388.9471, -469.3786, 31.5938, 178.6557),
                vector4(-1368.9619, -480.6163, 31.5954, 4.3287),
                vector4(-1371.8337, -481.0351, 31.5951, 15.5792),
                vector4(-1374.7587, -481.3833, 31.5947, 9.8200),
                vector4(-1377.9144, -481.7034, 31.5943, 11.4817)
            }
        },
        sign = {
            position = vector3(-1392.635189, -480.238453, 72.050700),
            renderTarget = 'prop_ex_office_text', model = 'ex_prop_ex_office_text'
        },
        safe = {
            position = vector3(-1372.905, -462.08, 72.05),
            leftCoords = vector3(-1375.346007, -462.321148, 72.101310),
            rightCoords = vector3(-1370.558133, -461.649058, 72.101310),
            leftHeading = 188.0
        },
        garage = {
            objectExport = 'GetImportCEOGarage4Object',
            -- Garage 1 and Garage 3 overlap the West office, so Garage 2 is used.
            interiorPart = 'Garage2',
            -- Exterior point used to store a vehicle and later drive it out.
            storeZone = vector4(-1362.3105, -472.1411, 31.5957, 95.6399),
            -- West Garage 2 is rotated +28 degrees from Maze Bank Garage 1.
            vehicleElevator = vector4(-1379.339441, -481.051158, 48.210000, 28.000000),
            walkIn = vector4(-1396.3037, -480.6659, 49.1005, 286.1435),
            parkingSpots = {
                vector4(-1392.038681, -474.955063, 47.334940, 243.566711),
                vector4(-1383.904169, -472.555988, 47.334451, 204.218323),
                vector4(-1377.630501, -471.918389, 47.334543, 168.833800),
                vector4(-1371.895367, -476.080043, 47.334467, 122.565140),
                vector4(-1370.443297, -483.657896, 47.334222, 73.083320),
                vector4(-1390.536617, -475.983537, 52.772928, 239.012192),
                vector4(-1383.913310, -472.203900, 52.772867, 199.160126),
                vector4(-1377.304014, -472.559743, 52.772928, 164.971000),
                vector4(-1371.584302, -475.598531, 52.772943, 120.510760),
                vector4(-1370.469721, -483.322549, 52.773065, 77.924090),
                vector4(-1391.560424, -477.440808, 58.118417, 245.091800),
                vector4(-1384.081109, -472.827607, 58.118982, 206.564911),
                vector4(-1378.032758, -471.875018, 58.118478, 161.955643),
                vector4(-1371.954841, -476.066650, 58.118784, 116.108536),
                vector4(-1370.865737, -482.764439, 58.118600, 76.274130)
            },
            spawnPoints = {
                vector4(-1362.3105, -472.1411, 31.5957, 95.6399)
            }
        }
    },
    agency_rockford = {
        id = 'agency_rockford', label = L('building_agency_rockford'), enabled = true,
        type = 'agency',
        ipl = { resource = 'bob74_ipl', objectExport = 'GetMpSecurityOffice1Object' },
        layoutAnchor = vector4(-1020.2864, -427.30176, 62.861137, 47.0385),
        teleport = {
            entry = vector4(-1035.30, -431.42, 39.62, 119.03),
            interior = vector4(-1033.35, -434.92, 63.86, 296.08)
        },
        agency = { assistantSlots = 2, assistants = {}, sign = { mode = 'dui', surfaces = {} } },
        featureSupport = {
            assistant = false, locker = false, safe = false, garage = false,
            chair = true, sign = true, mapboard = true, couch = false,
            shower = false, wardrobe = false, music = false, alcohol = false,
            smoking = false, telescope = false
        }
    },
    agency_hawick = {
        id = 'agency_hawick', label = L('building_agency_hawick'), enabled = true,
        type = 'agency',
        ipl = { resource = 'bob74_ipl', objectExport = 'GetMpSecurityOffice2Object' },
        layoutAnchor = vector4(384.8140, -60.7270, 102.3630, 0.0),
        teleport = {
            entry = vector4(389.92, -76.12, 68.18, 159.33),
            interior = vector4(370.9706, -56.2936, 103.3580, 252.0072),
            rooftop = vector4(382.8279, -51.0997, 122.5351, 237.6145)
        },
        -- Private Agency elevator. Ground lobby is the building entry above;
        -- the remaining destinations stay inside the current office bucket.
        floors = {
            {
                id = 'rooftop',
                label = L('nui_entry_rooftop'),
                code = 'R',
                coords = vector4(382.8279, -51.0997, 122.5351, 237.6145)
            },
            {
                id = 'second',
                label = L('floor_second'),
                code = '2',
                coords = vector4(370.0964, -56.9436, 111.9630, 247.1770)
            },
            {
                id = 'first',
                label = L('floor_first'),
                code = '1',
                coords = vector4(370.9706, -56.2936, 103.3580, 252.0072)
            }
        },
        chair = {
            searchCenter = vector3(395.5476, -60.3790, 107.1633),
            boardroomCenter = vector3(395.5476, -60.3790, 107.1633),
            placed = {
                {
                    model = 'sf_prop_sf_offchair_exec_01a',
                    coords = vector4(395.5476, -60.7790, 106.1633, 300.4301)
                },
                {
                    model = 'sf_prop_sf_offchair_exec_01a',
                    coords = vector4(373.8750, -61.0540, 106.1633, 35.7534)
                }
            }
        },
        agency = {
            assistantSlots = 2,
            -- Hawick is the reference layout. The client rotates and offsets this
            -- equipment into the other Agency interiors automatically.
            tactical = {
                board = {
                    {
                        model = 'ex_office_citymodel_01',
                        coords = vector3(388.1879, -54.7817, 106.9815),
                        rotation = vector3(0.0000, 0.0000, 339.6119)
                    }
                },
                chairs = {
                    { model = 'ex_prop_offchair_exec_02', coords = vector3(389.2188, -56.5158, 106.7449), rotation = vector3(0.0000, 0.0000, 160.0948) },
                    { model = 'ex_prop_offchair_exec_02', coords = vector3(388.2315, -56.1583, 106.7449), rotation = vector3(0.0000, 0.0000, 160.0948) },
                    { model = 'ex_prop_offchair_exec_02', coords = vector3(387.2443, -55.8008, 106.7449), rotation = vector3(0.0000, 0.0000, 160.0948) },
                    { model = 'ex_prop_offchair_exec_02', coords = vector3(386.2570, -55.4433, 106.7449), rotation = vector3(0.0000, 0.0000, 160.0948) },
                    { model = 'ex_prop_offchair_exec_02', coords = vector3(386.0469, -53.9842, 106.7449), rotation = vector3(0.0000, 0.0000, 68.5948) },
                    { model = 'ex_prop_offchair_exec_02', coords = vector3(387.2574, -53.0584, 106.7449), rotation = vector3(0.0000, 0.0000, -19.4052) },
                    { model = 'ex_prop_offchair_exec_02', coords = vector3(388.2478, -53.4072, 106.7449), rotation = vector3(0.0000, 0.0000, -19.4052) },
                    { model = 'ex_prop_offchair_exec_02', coords = vector3(389.2381, -53.7561, 106.7449), rotation = vector3(0.0000, 0.0000, -19.4052) },
                    { model = 'ex_prop_offchair_exec_02', coords = vector3(390.2285, -54.1050, 106.7449), rotation = vector3(0.0000, 0.0000, -19.4052) },
                    { model = 'ex_prop_offchair_exec_02', coords = vector3(390.5135, -55.6886, 106.7449), rotation = vector3(0.0000, 0.0000, -111.4052) }
                }
            },
            assistants = {
                {
                    chair = vector4(389.7260, -68.7377, 102.3632, 239.5618),
                    ped = vector4(388.8109, -68.4565, 102.3632, 63.4719),
                    phone = vector4(387.68273925781, -68.662979125977, 103.16339874268, 115.19341278076)
                },
                {
                    chair = vector4(384.1215, -71.3716, 102.3632, 181.0060),
                    ped = vector4(384.1079, -70.6446, 102.3632, 1.0354),
                    phone = vector3(383.404296875, -69.601753234863, 103.16339874268)
                }
            },
            dog = {

                breed = 'retriever',
                coords = vector4(392.9811, -61.3612, 106.6290, 155.1168),
                bed = vector4(393.0585, -59.2132, 106.8490, 114.6330),
                path = {
                vector4(390.3667, -60.5376, 107.1619, 75.0995),
                vector4(386.0518, -58.8175, 107.1619, 75.4028),
                vector4(383.0479, -59.7875, 107.1619, 134.8418),
                vector4(380.7734, -63.2305, 107.1619, 156.2601),
                vector4(380.7269, -67.1728, 107.1619, 235.4683),
                vector4(385.3485, -66.4815, 107.1619, 283.0859),
                vector4(394.3390, -68.7602, 107.1619, 258.7293),
                vector4(395.5331, -65.4328, 107.1633, 111.1117),
                vector4(392.9917, -63.6174, 107.1633, 79.4592),
                }
            },
            -- Fill both surfaces with /agencydui 1 and /agencydui 2 while inside Hawick.
            -- Hawick is the reference layout; empty Agency configs inherit and transform these corners.
            sign = {
                mode = 'dui',
                surfaces = {
                    [1] = {
                        topLeft = vector3(388.88342285, -66.35215759, 103.16972351),
                        topRight = vector3(386.73318481, -67.34590149, 103.17704010),
                        bottomRight = vector3(386.72222900, -67.35102081, 102.62050629),
                        bottomLeft = vector3(388.87905884, -66.34548950, 102.59070587)
                    },
                    [2] = {
                        topLeft = vector3(384.72473145, -68.28266907, 103.16902161),
                        topRight = vector3(382.61001587, -69.27924347, 103.16326141),
                        bottomRight = vector3(382.60211182, -69.27221680, 102.60618591),
                        bottomLeft = vector3(384.75064087, -68.27042389, 102.59614563)
                    }
                }
            }
        },
        featureSupport = {
            assistant = false, locker = false, safe = false, garage = false,
            chair = true, sign = true, mapboard = true, couch = true,
            shower = true, wardrobe = true, music = true, alcohol = false,
            smoking = false, telescope = true
        }
    },
    agency_vespucci = {
        id = 'agency_vespucci', label = L('building_agency_vespucci'), enabled = true,
        type = 'agency',
        ipl = { resource = 'bob74_ipl', objectExport = 'GetMpSecurityOffice3Object' },
        layoutAnchor = vector4(-1003.9110, -759.6040, 60.89419, 283.8203),
        teleport = {
            entry = vector4(-1039.25, -759.28, 19.84, 92.67),
            interior = vector4(-1003.11, -744.16, 61.89, 2.58)
        },
        agency = { assistantSlots = 2, assistants = {}, sign = { mode = 'dui', surfaces = {} } },
        featureSupport = {
            assistant = false, locker = false, safe = false, garage = false,
            chair = true, sign = true, mapboard = true, couch = false,
            shower = false, wardrobe = false, music = false, alcohol = false,
            smoking = false, telescope = false
        }
    },
    agency_little_seoul = {
        id = 'agency_little_seoul', label = L('building_agency_little_seoul'), enabled = true,
        type = 'agency',
        ipl = { resource = 'bob74_ipl', objectExport = 'GetMpSecurityOffice4Object' },
        layoutAnchor = vector4(-589.4757, -716.52594, 112.00506, 199.9630),
        teleport = {
            entry = vector4(-589.42, -706.89, 36.28, 0.23),
            interior = vector4(-574.58, -715.70, 113.01, 87.17)
        },
        agency = { assistantSlots = 2, assistants = {}, sign = { mode = 'dui', surfaces = {} } },
        featureSupport = {
            assistant = false, locker = false, safe = false, garage = false,
            chair = true, sign = true, mapboard = true, couch = false,
            shower = false, wardrobe = false, music = false, alcohol = false,
            smoking = false, telescope = false
        }
    }
}

-- Convenience aliases. Edit building-specific positions only in the definitions above.
Config.MazeBank = Config.Buildings.maze_bank_tower
Config.Arcadius = Config.Buildings.arcadius
Config.Lombank = Config.Buildings.lombank
Config.MazeBankWest = Config.Buildings.maze_bank_west
Config.AgencyRockford = Config.Buildings.agency_rockford
Config.AgencyHawick = Config.Buildings.agency_hawick
Config.AgencyVespucci = Config.Buildings.agency_vespucci
Config.AgencyLittleSeoul = Config.Buildings.agency_little_seoul
Config.BuildingOrder = {
    'maze_bank_tower', 'arcadius', 'lombank', 'maze_bank_west',
    'agency_rockford', 'agency_hawick', 'agency_vespucci', 'agency_little_seoul'
}
Config.ActiveBuilding = Config.Buildings[Config.DefaultBuilding]

-- Map: one blip at the main entrance of every enabled office building.
Config.Blips = {
    Enabled = true,
    Sprite = 475,
    Color = 2,
    Scale = 0.78,
    Display = 4,
    Alpha = 255,
    ShortRange = true
}

Config.Command = 'officepreset'

Config.Screenshot = {
    Command = 'officescreenshot',
    Quality = 0.95,
    HideHud = true,
    AdminOnly = false,
    AdminAce = 'drc.office.screenshot'
}

-- Dynasty 8 Executive - office purchases through /offices (NUI).
Config.Shop = {
    Command = 'offices',
    MaxUnits = 10,
    BucketBase = 5100,
    DoorRequestTimeout = 30,

    -- true = the assistant, gun locker, and safe work only after purchase in /offices.
    -- false = all features work without purchase; /offices is cosmetic only.
    RequirePurchase = true,

    -- true = every player can purchase an office.
    -- false = the player's current job and grade must match PurchaseAccess below.
    PurchaseAccess = {
        Everyone = true,
        -- Leave empty to allow every job that has an accepted grade.
        Jobs = {},
        GradeNames = {
            boss = true
        },
        -- Optional numeric minimum grade. Leave nil to use GradeNames only.
        MinimumGrade = nil
    },

    -- Percentage of the total investment refunded to the owner on sale.
    SellRefundPercent = 50,

    SaveKey = 'maze_bank_tower_ownership',

    -- Equipment surcharges (inspired by GTA Online).
    Extras = {
        assistant = 250000,
        dog = 250000,
        tacticalTable = 350000,
        locker = 175000,
        safe = 115000,
        garage = 995000
    },

    -- Agency artwork and highlight are independent from the wallpaper.
    AgencyCustomisation = {
        ArtPrices = { 0, 265000, 340000 },
        HighlightPrices = { 0, 100000, 100000, 100000, 100000, 100000, 100000, 100000 }
    },

    -- Dog breeds. Every breed has its own animation set with identical clip
    -- names, so only the model and the animation folder differ. The dictionary
    -- is built as anim@scripted@creatures@<clips>@amb@mansion@<male|heeled>@.
    Dogs = {
        DefaultBreed = 'retriever',
        Order = { 'retriever', 'shepherd', 'husky', 'rottweiler', 'poodle', 'pug', 'westy' },

        -- Jak dlouho pes vydrzi lezet (sam od sebe i na prikaz) a jak dlouho
        -- plati prikaz "sedni" od hrace.
        SleepDuration = 300000,   -- 5 minut
        CommandDuration = 300000, -- 5 minut
        -- Pauza mezi vlastnimi rutinami. Vyssi hodnoty = pes se prochazi min.
        RoutineGapMinimum = 30000,
        RoutineGapMaximum = 70000,
        -- Vahy vyberu rutiny: cim vyssi cislo, tim castejsi. Chuze (patrol,
        -- route) ma zamerne nizkou vahu, aby pes porad nechodil dokola.
        RoutineWeights = {
            idle = 4, idle_a = 4, idle_b = 4, idle_c = 4,
            sleep = 3,
            patrol_left = 1, patrol_right = 1, route = 1
        },
        Breeds = {
            retriever  = { label = L('dog_breed_retriever'),  model = 'a_c_retriever',  clips = 'retriever',  image = 'img/presets/retriever.jpg' },
            shepherd   = { label = L('dog_breed_shepherd'),   model = 'a_c_shepherd',   clips = 'shepherd',   image = 'img/presets/shepherd.jpg' },
            husky      = { label = L('dog_breed_husky'),      model = 'a_c_husky',      clips = 'husky',      image = 'img/presets/husky.jpg' },
            rottweiler = { label = L('dog_breed_rottweiler'), model = 'a_c_rottweiler', clips = 'rottweiler', image = 'img/presets/rotwiler.jpg' },
            poodle     = { label = L('dog_breed_poodle'),     model = 'a_c_poodle',     clips = 'poodle',     image = 'img/presets/puddle.jpg' },
            pug        = { label = L('dog_breed_pug'),        model = 'a_c_pug',        clips = 'pug',        image = 'img/presets/pug.jpg' },
            westy      = { label = L('dog_breed_westy'),      model = 'a_c_westy',      clips = 'westy',      image = 'img/presets/westy.jpg' }
        }
    },

    -- Interior style surcharges (IDs must match Config.Presets).
    PresetPrices = {
        warm = 0,
        classical = 340000,
        vintage = 365000,
        contrast = 415000,
        rich = 500000,
        cool = 470000,
        ice = 835000,
        conservative = 700000,
        polished = 935000,
        agency_classic = 0,
        agency_marble = 208500,
        agency_geometric = 232500,
        agency_designer = 259500,
        agency_presidential = 286500,
        agency_exotic = 332500,
        agency_tiled = 360000,
        agency_organic = 400500,
        agency_regal = 442500
    },

    -- Office catalogue. Images belong in ui/img/offices/<id>.jpg.
    -- When an image is missing, the UI renders a styled placeholder.
    Offices = {
        {
            id = Config.MazeBank.id,
            label = Config.MazeBank.label,
            location = L('location_pillbox_hill'),
            price = 4000000,
            available = true,
            image = 'img/presets/maze-bank.jpg',
            description = L('office_maze_bank_tower_desc'),
            features = LT('office_maze_bank_tower_features')
        },
        {
            id = 'arcadius',
            label = L('building_arcadius'),
            location = L('location_pillbox_hill'),
            price = 2250000,
            available = true,
            image = 'img/presets/bank-arcadius.jpg',
            description = L('office_arcadius_desc'),
            features = LT('office_arcadius_features')
        },
        {
            id = 'lombank',
            label = L('building_lombank_west'),
            location = L('location_del_perro'),
            price = 3100000,
            available = true,
            image = 'img/presets/lombank.jpg',
            description = L('office_lombank_desc'),
            features = LT('office_lombank_features')
        },
        {
            id = 'maze_bank_west',
            label = L('building_maze_bank_west'),
            location = L('location_del_perro'),
            price = 1000000,
            available = true,
            image = 'img/presets/mazebank-west.jpg',
            description = L('office_maze_bank_west_desc'),
            features = LT('office_maze_bank_west_features')
        },
        {
            id = 'agency_little_seoul',
            label = L('building_agency_little_seoul'),
            location = L('location_little_seoul'),
            price = 2010000,
            available = true,
            image = 'img/presets/little-soul.jpg',
            description = L('office_agency_little_seoul_desc'),
            features = LT('office_agency_features')
        },
        {
            id = 'agency_vespucci',
            label = L('building_agency_vespucci'),
            location = L('location_vespucci_canals'),
            price = 2145000,
            available = true,
            image = 'img/presets/vessupuci-canals.jpg',
            description = L('office_agency_vespucci_desc'),
            features = LT('office_agency_features')
        },
        {
            id = 'agency_rockford',
            label = L('building_agency_rockford'),
            location = L('location_rockford_hills'),
            price = 2415000,
            available = true,
            image = 'img/presets/rockford-hills.jpg',
            description = L('office_agency_rockford_desc'),
            features = LT('office_agency_features')
        },
        {
            id = 'agency_hawick',
            label = L('building_agency_hawick'),
            location = L('location_hawick'),
            price = 2830000,
            available = true,
            image = 'img/presets/hawick-agency.jpg',
            description = L('office_agency_hawick_desc'),
            features = LT('office_agency_features')
        }
    }
}

-- Runtime systems use the shorter name; the purchase configuration remains
-- the single source of truth for breeds, pricing and routine timings.
Config.Dogs = Config.Shop.Dogs

-- Dynasty 8 office seller. The NPC opens the same catalogue as /offices.
Config.OfficeSeller = {
    Enabled = true,
    Model = 'u_m_m_bankman',
    Coords = vector4(-602.0941, -347.2580, 34.2411, 110.6627),
    Scenario = 'WORLD_HUMAN_CLIPBOARD',
    GreetingDistance = 5.0,
    GreetingResetDistance = 15.0,
    GreetingSpeech = 'GENERIC_HI',
    Blip = {
        Enabled = true,
        Label = L('office_seller_blip'),
        Sprite = 375,
        Color = 2,
        Scale = 0.8,
        Display = 4,
        Alpha = 255,
        ShortRange = true
    }
}

-- When false, every player may change the preset.
-- When true, grant administrators this ACE: add_ace group.admin drc.office.preset allow
Config.AdminOnly = false
Config.AdminAce = 'drc.office.preset'

Config.DefaultPreset = 'polished'
Config.SaveKey = 'maze_bank_tower_preset'

Config.Teleport = {
    Enabled = true,
    DrawDistance = 15.0,
    InteractDistance = 1.6,
    Entry = Config.ActiveBuilding.teleport.entry,
    Interior = Config.ActiveBuilding.teleport.interior
}

Config.Marker = {
    Type = 2,
    Scale = vector3(0.25, 0.25, 0.25),
    Color = { r = 212, g = 175, b = 55, a = 190 }
}

Config.Chair = {
    Enabled = true,
    ReplaceOfficeChairs = {
        enabled = true,
        radius = 50.0,
        models = {
            {
                fromModel = 'v_corp_offchair',
                toModel = 'ex_prop_offchair_exec_03'
            },
            {
                -- Boss zidle: nechteny ex_prop_offchair_exec_01 (v nekterych
                -- presetech) nahradime rovnou animacnim modelem, takze pri stani
                -- i pri sezeni je videt stejna zidle a uz se pri sednuti nemeni.
                fromModel = 'ex_prop_offchair_exec_01',
                toModel = 'sf_prop_sf_offchair_exec_01a'
            }
        }
    },

    -- The position near the safe reliably selects the CEO chair instead of boardroom chairs.
    SearchCenter = Config.ActiveBuilding.chair.searchCenter,
    OriginalModels = {
        'sf_prop_sf_offchair_exec_01a',
        'ex_prop_offchair_exec_02',
        'ex_prop_offchair_exec_01',
        'ex_prop_offchair_exec_03',
    }
}

Config.Assistant = {
    Enabled = true,
    Command = 'officeassistant',
    SaveKey = 'maze_bank_tower_assistant',
    DefaultType = 'female',
    Models = {
        female = {
            label = L('assistant_model_female'),
            model = 'MP_F_ExecPA_01',
            components = {},
            presetOnly = true,
            outfits = {
                {
                    label = L('assistant_outfit_executive_01'),
                    components = {
                        [3] = { 1, 0, 0 }, [4] = { 0, 2, 0 }, [6] = { 1, 0, 0 },
                        [7] = { 2, 0, 0 }, [8] = { 3, 0, 0 }, [11] = { 0, 0, 0 }
                    }
                },
                {
                    label = L('assistant_outfit_executive_02'),
                    components = {
                        [3] = { 1, 0, 0 }, [4] = { 1, 2, 0 }, [6] = { 1, 0, 0 },
                        [7] = { 2, 0, 0 }, [8] = { 0, 0, 0 }, [11] = { 1, 0, 0 }
                    }
                },
                {
                    label = L('assistant_outfit_executive_03'),
                    components = {
                        [3] = { 0, 0, 0 }, [4] = { 2, 5, 0 }, [6] = { 1, 0, 0 },
                        [7] = { 2, 0, 0 }, [8] = { 3, 0, 0 }, [11] = { 2, 0, 0 }
                    }
                },
                {
                    label = L('assistant_outfit_executive_04'),
                    components = {
                        [3] = { 0, 0, 0 }, [4] = { 2, 5, 0 }, [6] = { 1, 0, 0 },
                        [7] = { 2, 0, 0 }, [8] = { 3, 0, 0 }, [11] = { 2, 1, 0 }
                    }
                },
                {
                    label = L('assistant_outfit_executive_05'),
                    components = {
                        [3] = { 1, 0, 0 }, [4] = { 3, 2, 0 }, [6] = { 1, 0, 0 },
                        [7] = { 1, 1, 0 }, [8] = { 3, 0, 0 }, [11] = { 3, 0, 0 }
                    }
                },
                {
                    label = L('assistant_outfit_executive_06'),
                    components = {
                        [3] = { 1, 0, 0 }, [4] = { 4, 0, 0 }, [6] = { 1, 0, 0 },
                        [7] = { 2, 0, 0 }, [8] = { 1, 1, 0 }, [11] = { 4, 0, 0 }
                    }
                }
            }
        },
        male = {
            label = L('assistant_model_male'),
            model = 'MP_M_ExecPA_01',
            components = {},
            presetOnly = true,
            -- GTA/FiveM values are zero-based; the screenshot editor displayed them as one-based.
            outfits = {
                {
                    label = L('assistant_outfit_executive_01'),
                    components = {
                        [3] = { 1, 0, 0 }, [4] = { 0, 0, 0 }, [6] = { 0, 0, 0 },
                        [7] = { 0, 0, 0 }, [8] = { 4, 0, 0 }, [11] = { 5, 0, 0 }
                    }
                },
                {
                    label = L('assistant_outfit_executive_02'),
                    components = {
                        [3] = { 2, 0, 0 }, [4] = { 0, 2, 0 }, [6] = { 0, 0, 0 },
                        [7] = { 3, 0, 0 }, [8] = { 5, 0, 0 }, [11] = { 3, 0, 0 }
                    }
                },
                {
                    label = L('assistant_outfit_executive_03'),
                    components = {
                        [3] = { 1, 0, 0 }, [4] = { 2, 4, 0 }, [6] = { 0, 0, 0 },
                        [7] = { 3, 0, 0 }, [8] = { 2, 0, 0 }, [11] = { 2, 0, 0 }
                    }
                },
                {
                    label = L('assistant_outfit_executive_04'),
                    components = {
                        [3] = { 0, 0, 0 }, [4] = { 3, 0, 0 }, [6] = { 0, 0, 0 },
                        [7] = { 3, 0, 0 }, [8] = { 3, 0, 0 }, [11] = { 4, 0, 0 }
                    }
                },
                {
                    label = L('assistant_outfit_executive_05'),
                    components = {
                        [3] = { 0, 0, 0 }, [4] = { 3, 0, 0 }, [6] = { 0, 0, 0 },
                        [7] = { 3, 0, 0 }, [8] = { 1, 4, 0 }, [11] = { 1, 0, 0 }
                    }
                },
                {
                    label = L('assistant_outfit_executive_06'),
                    components = {
                        [3] = { 0, 0, 0 }, [4] = { 0, 3, 0 }, [6] = { 0, 0, 0 },
                        [7] = { 0, 0, 0 }, [8] = { 4, 2, 0 }, [11] = { 5, 2, 0 }
                    }
                }
            }
        }
    },
    Coords = Config.ActiveBuilding.assistant.coords,

    -- Live mode: when a building defines assistant.desk, the assistant sits in the chair,
    -- alternates idle animations, and occasionally makes a call from the casino phone.
    -- Animations are defined in shared/sh_animations.lua (Anims.Desk / Anims.Phone /
    -- Anims.Coffee); only timing and sounds remain here.
    Desk = {
        Enabled = true,
        IdleSwitchMin = 9000,
        IdleSwitchMax = 17000,
        Phone = {
            CallIntervalMin = 45000,
            CallIntervalMax = 100000,
            -- The phone rings from the call announcement until the handset is picked up.
            -- It plays spatially through xsound directly from the phone.
            Ring = {
                -- How long it rings before the assistant gets up from the chair.
                AnswerDelay = 1500,
                File = 'ui/sounds/phone-ringing.ogg',
                Volume = 0.18,
                Distance = 3.5
            },
            -- Call duration follows the speech duration; the handset is replaced after speaking.
            SpeechLinesMin = 1,
            SpeechLinesMax = 2,
            -- Listening pause between voice lines.
            SpeechGapMin = 3500,
            SpeechGapMax = 6500,
            PostPickupDelay = 900,
            HangupDelay = 1200
        },

        -- Coffee routine: takes the mug from the desk, walks to the coffee machine,
        -- brews and slowly drinks coffee, then returns the mug. Enabled when the building
        -- defines coffee = { ... } positions inside assistant.desk.
        Coffee = {
            Enabled = true,
            IntervalMin = 150000,
            IntervalMax = 300000,
            BrewTime = 20000,
            BrewSound = {
                File = 'ui/sounds/coffee-machine.ogg',
                Volume = 0.4,
                Distance = 14.0
            }
        }
    },

    GreetingDistance = 7.0,
    ResetGreetingDistance = 20.0,
    ReturnToWorkDelay = 3500,
    -- Speech contexts are defined in shared/sh_animations.lua (Anims.SpeechContexts).
    Speech = {
        Cooldown = 2500,
        DecorDelay = 1800,
        PhoneChatMin = 45000,
        PhoneChatMax = 90000,
        DangerReactionDistance = 15.0,
        DangerReactionCooldown = 10000
    },
    Dialogue = {
        MaxVisitors = 8,
        VehicleClearRadius = 3.0,
        -- A successful valet delivery blocks another request from the same
        -- player for this many seconds.
        VehicleRequestCooldown = 300,
        -- Keeps the selected point occupied while the network entity is being
        -- created, preventing simultaneous requests from sharing one spawn.
        VehicleSpawnReservation = 15,
        VehicleSpawnPoints = Config.ActiveBuilding.assistant.vehicleSpawnPoints,
        -- Public Vespucci Boulevard garage from the current esx_garage configuration.
        GarageSpawnPoints = Config.ActiveBuilding.assistant.garageSpawnPoints
    },
    Greetings = LT('assistant_greetings'),
    Dialogues = LT('assistant_dialogues')
}

Config.OfficeSign = {
    Enabled = true,
    Command = 'officename',
    SaveKey = 'maze_bank_tower_company_name',
    DefaultName = L('nui_default_company_name'),
    DefaultColour = '#000000',
    MaxLength = 30,
    RenderTarget = Config.ActiveBuilding.sign.renderTarget,
    Model = Config.ActiveBuilding.sign.model,
    -- Chalet Comprime is a narrower GTA font that does not look stretched on the wide sign.
    Font = 4,
    Scale = 1.5,
    MinimumScale = 0.20,
    MaximumTextWidth = 0.72,
    TextY = 0.35,
    Colour = { r = 0, g = 0, b = 0, a = 255 },
    DrawDistance = 60.0,
    Position = Config.ActiveBuilding.sign.position
}

Config.Safe = {
    CashItem = 'money',
    Enabled = true,
    -- Maximum amount of cash that one office safe can hold.
    MaximumBalance = 300000000,
    DebugZones = false,
    CashVisualEnabled = true,
    -- Up to this amount, manually placed cash props inside the safe are filled progressively.
    CashVisualMaximum = 5000000,
    CashPropReference = {
        coords = vector3(-1370.558133, -461.649058, 72.101310),
        heading = 8.0
    },
    CashProps = {
        { model = 'ex_prop_exec_cashpile', coords = vector3(-1371.3834228515625, -461.21875, 71.98069763183594), rotation = vector3(0.0, 0.0, 7.8926887512207) },
        { model = 'ex_prop_exec_cashpile', coords = vector3(-1371.3592529296875, -461.4029235839844, 71.97547912597656), rotation = vector3(0.0, 0.0, 5.89311695098876) },
        { model = 'ex_prop_exec_cashpile', coords = vector3(-1370.8353271484375, -461.16351318359375, 71.97232055664062), rotation = vector3(0.0, 0.0, 4.88262557983398) },
        { model = 'ex_prop_exec_cashpile', coords = vector3(-1370.81689453125, -461.3491516113281, 71.97834777832031), rotation = vector3(0.0, 0.0, 5.56916379928588) },
        { model = 'ex_cash_roll_01', coords = vector3(-1371.3834228515625, -461.21875, 72.45242309570312), rotation = vector3(0.0, 0.0, 0.0) },
        { model = 'ex_prop_exec_cashpile', coords = vector3(-1370.8353271484375, -461.16351318359375, 72.50584411621094), rotation = vector3(0.0, 0.0, 0.0) },
        { model = 'ex_cash_pile_01', coords = vector3(-1371.3834228515625, -461.21875, 71.35482788085938), rotation = vector3(0.0, 0.0, 0.0) },
        { model = 'ex_cash_roll_01', coords = vector3(-1370.8353271484375, -461.2566528320313, 71.35482788085938), rotation = vector3(0.0, 0.0, 0.0) },
        { model = 'ex_cash_roll_01', coords = vector3(-1370.8353271484375, -461.46484375, 71.35482788085938), rotation = vector3(0.0, 0.0, -36.06253051757812) }
    },
    Position = Config.ActiveBuilding.safe.position,
    Sides = {}
}

-- Office garage (valet): capacity and entrance interaction.
Config.Garage = {
    Capacity = 15,
    DrawDistance = 18.0,
    InteractDistance = 5.0,
    ExitGhost = {
        Enabled = true,
        VehicleClearRadius = 5.0,
        PlayerClearRadius = 3.0,
        MinimumDuration = 1500,
        StableClearDuration = 1250,
        MaximumDuration = 45000,
        CheckInterval = 250,
        Alpha = 185
    },
    -- Fixed world-space DUI dimensions above the vehicle (in metres).
    VehicleDuiWidth = 1.15,
    VehicleDuiHeight = 0.65,
    VehicleDuiRoofGap = 0.30,
    -- The server validates the player's distance from storeZone (anti-cheat protection).
    ServerCheckRadius = 20.0,
    DefaultPreset = 'concrete',
    PresetOrder = { 'concrete', 'plain', 'wooden', 'marble' },
    Presets = {
        concrete = {
        label = L('garage_style_industrial'), image = 'img/presets/industrial.jpg', price = 0,
            style = 'concrete', lighting = 'style1', numbering = 'style1'
        },
        plain = {
        label = L('garage_style_executive'), image = 'img/presets/executive.jpg', price = 150000,
            style = 'plain', lighting = 'style3', numbering = 'style3'
        },
        wooden = {
        label = L('garage_style_classic'), image = 'img/presets/classic.jpg', price = 275000,
            style = 'wooden', lighting = 'style9', numbering = 'style9'
        },
        marble = {
        label = L('garage_style_luxury'), image = 'img/presets/luxury.jpg', price = 450000,
            style = 'marble', lighting = 'style6', numbering = 'style6'
        }
    }
}

Config.GunLockerStash = {
    Label = L('gunlocker_stash_label'),
    Slots = 60,
    Weight = 250000
}

-- Drinking animations are defined in shared/sh_animations.lua (Anims.Wine).
Config.Wine = {
    Enabled = true,
    OriginalBottleModel = 'prop_wine_red',
    OriginalGlassModel = -1296547421,
    OriginalSearchRadius = 2.0
}

Config.Smoking = {
    Enabled = true
}

Config.OfficeMusic = {
    Enabled = true,
    Resource = 'xsound',
    DefaultVolume = 0.25,
    MinimumVolume = 0.02,
    MaximumVolume = 1.0,
    -- Assistant playback uses one synchronized source covering the complete
    -- office. Distance below remains exclusive to the physical desk radio.
    OfficeDistance = 100.0,
    Distance = 12.0,
    FadeDistance = 4.0,
    SoundPointCommand = 'setsoundpoint',
    MaximumSoundPoints = 12,
    SpeakerDistance = 28.0,
    OccludedVolumeMultiplier = 0.0,
    EditorAce = 'drc.office.soundpoints',
    Locations = {
        maze_bank_tower = vector3(-80.078182, -805.911306, 243.466081),
        arcadius = vector3(-128.663278, -637.988171, 168.951081),
        lombank = vector3(-1559.557926, -573.050711, 108.608281),
        maze_bank_west = vector3(-1376.0607910156, -466.6061706543, 72.131080627441),
        -- Centralni bod agentury Hawick (dosah SpeakerDistance pokryva patro).
        -- Presnou pozici reproduktoru lze doladit editorem rozmisteni.
        agency_hawick = vector3(386.0000, -65.0000, 103.3000)
    },
    SoundPoints = {
        maze_bank_tower = {
            vector3(-80.078182, -805.911306, 243.466081)
        },
        arcadius = {
            vector3(-128.663278, -637.988171, 168.951081)
        },
        lombank = {
            vector3(-1559.557926, -573.050711, 108.608281)
        },
        maze_bank_west = {
            vector3(-1376.0607910156, -466.6061706543, 72.131080627441)
        },
        agency_hawick = {
            vector3(386.0000, -65.0000, 103.3000)
        }
    },
    Stations = {
        { id = 'RADIO_01_CLASS_ROCK', label = L('radio_los_santos_rock') },
        { id = 'RADIO_02_POP', label = L('radio_non_stop_pop') },
        { id = 'RADIO_03_HIPHOP_NEW', label = L('radio_los_santos') },
        { id = 'RADIO_04_PUNK', label = L('radio_channel_x') },
        { id = 'RADIO_06_COUNTRY', label = L('radio_rebel') },
        { id = 'RADIO_07_DANCE_01', label = L('radio_soulwax') },
        { id = 'RADIO_09_HIPHOP_OLD', label = L('radio_west_coast_classics') },
        { id = 'RADIO_12_REGGAE', label = L('radio_blue_ark') },
        { id = 'RADIO_16_SILVERLAKE', label = L('radio_mirror_park') },
        { id = 'RADIO_18_90S_ROCK', label = L('radio_vinewood_boulevard') },
        { id = 'RADIO_20_THELAB', label = L('radio_the_lab') },
        { id = 'RADIO_27_DLC_PRHEI4', label = L('radio_still_slipping') },
        { id = 'RADIO_34_DLC_HEI4_KULT', label = L('radio_kult_fm') },
        { id = 'RADIO_37_MOTOMAMI', label = L('radio_motomami') }
    }
}

Config.Telescope = {
    Enabled = true,
    ReferenceBuilding = 'maze_bank_west',
    AutoTransformBuildings = true,
    Locations = {
        maze_bank_west = vector4(-1369.9180, -468.0386, 72.0571, 273.3414),
        agency_hawick = vector4(380.0635, -68.1648, 107.1620, 194.5436)
    }
}

-- Couch seats are static resource data shared by every office instance.
-- EditorEnabled can be used temporarily for setup; runtime edits are not persistent.
Config.Couch = {
    Enabled = true,
    ReferenceBuilding = 'maze_bank_west',
    AutoTransformBuildings = true,
    EditorEnabled = true,
    Commands = { 'chouch', 'couch', 'couchdebug' },
    MaximumSeats = 3,

    -- An empty EditorAce allows everyone inside the office to use the editor.
    -- For production, set e.g. 'drc.office.couch' and add the ACE to server.cfg.
    EditorAce = '',
    Buildings = {
        maze_bank_west = {
            { id = 1, seats = {{ x = -1369.4504, y = -476.1187, z = 72.0571, heading = 98.21705627441406, animation = 1 }} },
            { id = 2, seats = {{ x = -1369.7553, y = -475.4349, z = 72.0571, heading = 140.0126495361328, animation = 3 }} },
            { id = 3, seats = {{ x = -1369.6655, y = -476.8169, z = 72.1051, heading = 104.01264953613281, animation = 6 }} },
            { id = 4, seats = {{ x = -1372.4955, y = -474.8161, z = 72.0091, heading = 244.0126495361328, animation = 7 }} },
            { id = 5, seats = {{ x = -1372.0703, y = -478.3880, z = 72.0571, heading = 326.48052978515627, animation = 1 }} },
            { id = 6, seats = {{ x = -1383.1455, y = -481.1977, z = 72.0262, heading = 276.12969970703127, animation = 1 }} },
            { id = 7, seats = {{ x = -1382.9301, y = -482.3951, z = 72.0422, heading = 280.12969970703127, animation = 1 }} },
            { id = 8, seats = {{ x = -1383.7897, y = -485.9140, z = 72.0182, heading = 281.71710205078127, animation = 1 }} },
            { id = 9, seats = {{ x = -1383.5191, y = -487.4652, z = 72.0422, heading = 289.71315002441409, animation = 4 }} },
            { id = 10, seats = {{ x = -1383.3314, y = -486.7420, z = 72.0422, heading = 286.1974792480469, animation = 5 }} },
            { id = 11, seats = {{ x = -1382.7307, y = -488.4270, z = 72.0422, heading = 6.19747924804687, animation = 1 }} },
            { id = 12, seats = {{ x = -1381.4042, y = -488.2607, z = 72.0422, heading = 12.19747924804687, animation = 1 }} }
        },
        agency_hawick = {
            { id = 1, seats = {
                { x = 373.5739, y = -65.8550, z = 111.9779, heading = 345.9669, animation = 1 },
                { x = 373.6532, y = -64.2585, z = 111.9779, heading = 255.9669, animation = 1 },
                { x = 373.3767, y = -65.2656, z = 111.9779, heading = 255.9669, animation = 1 }
            } },
            { id = 2, seats = {
                { x = 377.6147, y = -64.1889, z = 111.9779, heading = 159.5142, animation = 1 },
                { x = 377.5400, y = -65.6591, z = 111.9779, heading = 69.5142, animation = 1 },
                { x = 377.8732, y = -64.7331, z = 111.9779, heading = 69.5142, animation = 1 }
            } },
            { id = 3, seats = {
                { x = 379.1695, y = -62.4023, z = 107.1632, heading = 70.9406, animation = 1 },
                { x = 378.7933, y = -63.4911, z = 107.1632, heading = 70.9406, animation = 1 }
            } },
            { id = 4, seats = {
                { x = 377.2290, y = -64.3053, z = 107.1632, heading = 14.2642, animation = 1 }
            } },
            { id = 5, seats = {
                { x = 384.4589, y = -63.7134, z = 107.1620, heading = 210.1513, animation = 1 },
                { x = 385.6051, y = -63.1864, z = 107.1620, heading = 208.1513, animation = 1 }
            } },
            { id = 6, seats = {
                { x = 384.4524, y = -65.0525, z = 107.1620, heading = 286.1513, animation = 2 }
            } },
            { id = 7, seats = {
                { x = 391.9665, y = -64.1963, z = 107.1633, heading = 278.0300, animation = 1 }
            } },
            { id = 8, seats = {
                { x = 392.6163, y = -65.5620, z = 107.1633, heading = 338.0115, animation = 1 },
                { x = 393.8848, y = -66.0742, z = 107.1633, heading = 338.0115, animation = 1 }
            } }
        }
    }
}

Config.Clothing = {
    Provider = 'auto-detect', -- illenium-appearance, fivem-appearance, ox_appearance, qb-clothing, esx_skin, native
    Wardrobe = {
        Enabled = true,
        Editor = {
            ped = false,
            headBlend = false,
            faceFeatures = false,
            headOverlays = false,
            components = true,
            componentConfig = {
                masks = true,
                upperBody = true,
                lowerBody = true,
                bags = true,
                shoes = true,
                scarfAndChains = true,
                bodyArmor = true,
                shirts = true,
                decals = true,
                jackets = true
            },
            props = true,
            propConfig = {
                hats = true,
                glasses = true,
                ear = true,
                watches = true,
                bracelets = true
            },
            tattoos = false,
            enableExit = true,
            hasTracker = false,
            automaticFade = true
        },
        Buildings = {
            maze_bank_tower = vector4(-78.138176, -812.128308, 243.377200, 243.6083),
            arcadius = vector4(-133.132298, -633.250810, 168.862200, 89.6083),
            lombank = vector4(-1565.796891, -571.182546, 108.519400, 119.6083),
            maze_bank_west = vector4(-1380.6393, -471.2378, 72.0422, 181.6083),
            agency_hawick = vector4(392.1706, -70.8035, 111.9629, 87.8231)
        }
    },
    Presets = {
        shower = {
            male = {
                components = {
                    [1] = { drawable = 0, texture = 0 },
                    [3] = { drawable = 15, texture = 0 },
                    [4] = { drawable = 61, texture = 0 },
                    [5] = { drawable = 0, texture = 0 },
                    [6] = { drawable = 34, texture = 0 },
                    [7] = { drawable = 0, texture = 0 },
                    [8] = { drawable = 15, texture = 0 },
                    [9] = { drawable = 0, texture = 0 },
                    [10] = { drawable = 0, texture = 0 },
                    [11] = { drawable = 15, texture = 0 }
                },
                clearProps = true
            },
            female = {
                components = {
                    [1] = { drawable = 0, texture = 0 },
                    [3] = { drawable = 15, texture = 0 },
                    [4] = { drawable = 15, texture = 0 },
                    [5] = { drawable = 0, texture = 0 },
                    [6] = { drawable = 35, texture = 0 },
                    [7] = { drawable = 0, texture = 0 },
                    [8] = { drawable = 14, texture = 0 },
                    [9] = { drawable = 0, texture = 0 },
                    [10] = { drawable = 0, texture = 0 },
                    [11] = { drawable = 15, texture = 0 }
                },
                clearProps = true
            }
        }
    }
}

Config.Shower = {
    Enabled = true,
    ReferenceBuilding = 'maze_bank_west',
    AutoTransformBuildings = true,
    -- Shower effects are static below. The editor is only a temporary setup tool.
    EditorEnabled = true,
    EditorCommand = 'setshower',
    EditorAce = '',
    Buildings = {
        maze_bank_tower = {
            undress = vector4(-81.529846, -816.366620, 243.314700, 162.5056),
            showerTarget = vector4(-80.410956, -816.753605, 243.364600, 163.8594),
            showerAnimation = vector4(-79.928152, -816.100976, 243.364600, 152.0560),
            clothes = {
                {
                    model = 'prop_ld_tshirt_01',
                    coords = vector3(-81.387100, -816.404864, 242.376168),
                    rotation = vector3(0.0, 0.0, 96.219)
                },
                {
                    model = 'prop_ld_jeans_02',
                    coords = vector3(-81.708302, -816.150775, 242.389000),
                    rotation = vector3(-3.481, 1.040, -11.386)
                }
            }
        },
        arcadius = {
            undress = vector4(-131.941839, -627.954630, 168.799700, 8.5056),
            showerTarget = vector4(-133.117134, -628.097300, 168.849600, 9.8594),
            showerAnimation = vector4(-133.264982, -628.895526, 168.849600, 358.0560),
            clothes = {
                {
                    model = 'prop_ld_tshirt_01',
                    coords = vector3(-132.086904, -627.982833, 167.861168),
                    rotation = vector3(0.0, 0.0, -57.781)
                },
                {
                    model = 'prop_ld_jeans_02',
                    coords = vector3(-131.686824, -628.070400, 167.874000),
                    rotation = vector3(-3.481, 1.040, -165.386)
                }
            }
        },
        lombank = {
            undress = vector4(-1567.414013, -566.000690, 108.456900, 38.5056),
            showerTarget = vector4(-1568.360514, -566.711893, 108.506800, 39.8594),
            showerAnimation = vector4(-1568.089441, -567.477101, 108.506800, 28.0560),
            clothes = {
                {
                    model = 'prop_ld_tshirt_01',
                    coords = vector3(-1567.525542, -566.097646, 107.518368),
                    rotation = vector3(0.0, 0.0, -27.781)
                },
                {
                    model = 'prop_ld_jeans_02',
                    coords = vector3(-1567.135279, -565.973442, 107.531200),
                    rotation = vector3(-3.481, 1.040, -135.386)
                }
            }
        },
        maze_bank_west = {
            undress = vector4(-1385.9738, -470.2329, 71.9797, 100.5056),
            showerTarget = vector4(-1385.7902, -471.4025, 72.0296, 101.8594),
            showerAnimation = vector4(-1384.9873, -471.5224, 72.0296, 90.0560),
            clothes = {
                {
                    model = 'prop_ld_tshirt_01',
                    coords = vector3(-1385.9405517578125, -470.37689208984375, 71.04116821289062),
                    rotation = vector3(0.0, 0.0, 34.219)
                },
                {
                    model = 'prop_ld_jeans_02',
                    coords = vector3(-1385.867, -469.974, 71.054),
                    rotation = vector3(-3.481, 1.040, -73.386)
                }
            },
            effects = {
                water = {
                    x = -1385.7513983821876,
                    y = -471.50996427468257,
                    z = 73.4290318708414,
                    rx = 294.0,
                    ry = 20.0,
                    rz = 127.03140258789063
                },
                smoke = {
                    x = -1385.2068166347409,
                    y = -471.31554059846158,
                    z = 72.10780194282516,
                    rx = 0.0,
                    ry = 0.0,
                    rz = 120.8541030883789
                }
            }
        },
        agency_hawick = {
            undress = vector4(389.1897, -70.1049, 111.9630, 132.3428),
            dressAtUndress = true,
            -- Bod, kam hrac dojde a spusti se sprchova animace (pod hlavici -
            -- odvozeno z vodniho efektu). Otoceni a presna vyska se daji doladit.
            showerTarget = vector4(390.5500, -69.0500, 111.9630, 132.3428),
            showerAnimation = vector4(391.0500, -68.8500, 111.9630, 132.3428),
            -- Odlozene obleceni lezi u undress bodu na podlaze (z je o vysku
            -- modelu nize nez stojici hrac, stejne jako u executive kancelari).
            clothes = {
                {
                    model = 'prop_ld_tshirt_01',
                    coords = vector3(389.3500, -69.9500, 111.0230),
                    rotation = vector3(0.0, 0.0, 110.0)
                },
                {
                    model = 'prop_ld_jeans_02',
                    coords = vector3(389.0400, -70.2600, 111.0360),
                    rotation = vector3(-3.481, 1.040, -20.0)
                }
            },
            effects = {
                water = {
                    x = 391.18526543,
                    y = -68.79104840,
                    z = 113.24201953,
                    rx = 292.0000,
                    ry = 0.0000,
                    rz = 266.6141
                },
                smoke = {
                    x = 390.87489531,
                    y = -68.62581316,
                    z = 111.73789754,
                    rx = 4.0000,
                    ry = 0.0000,
                    rz = -172.6700
                }
            }
        }
    }
}

Config.AgencyStorage = {
    Enabled = true,
    Label = L('agency_storage_label'),
    Slots = 80,
    Weight = 300000,
    Buildings = {
        agency_hawick = {
            vector4(390.3702, -62.1821, 111.9630, 160.4208),
            vector4(381.6796, -59.8017, 111.9629, 173.5757)
        }
    }
}

Config.MapBoard = {
    Enabled = true,
    ReferenceBuilding = 'maze_bank_west',
    AutoTransformBuildings = true,
    SetupCommand = 'officemapsetup',
    -- Everyone inside the office may open the map. The "map" permission controls drawing only.
    EveryoneCanView = true,
    MaximumSegments = 12000,
    MaximumMarkers = 250,
    MarkerSize = 32,
    MaximumThickness = 40,
    SaveDelay = 3000,
    StrokeRateLimit = 90,
    Colours = {
        '#E8D9A7',
        '#D7AE37',
        '#FFFFFF',
        '#E64B4B',
        '#4EA1FF',
        '#51C878',
        '#B56CFF',
        '#111111'
    },
    MarkerTypes = {
        { id = 'pin', label = L('mapboard_marker_pin') },
        { id = 'target', label = L('mapboard_marker_target') },
        { id = 'entry', label = L('mapboard_marker_entry') },
        { id = 'vehicle', label = L('mapboard_marker_vehicle') },
        { id = 'police', label = L('mapboard_marker_police') },
        { id = 'enemy', label = L('mapboard_marker_enemy') },
        { id = 'money', label = L('mapboard_marker_money') },
        { id = 'camera', label = L('mapboard_marker_camera') },
        { id = 'meeting', label = L('mapboard_marker_meeting') },
        { id = 'flag', label = L('mapboard_marker_flag') },
        { id = 'warning', label = L('mapboard_marker_warning') }
    },

    -- /officemapsetup records points in this order: top left, top right,
    -- bottom right, bottom left. Paste the completed entry here.
    Buildings = {
        maze_bank_west = {
            target = vector3(-1369.74548340, -484.98715210, 71.86494446),
            cameraHeading = 278.381,
            corners = {
                topLeft = vector3(-1368.48071289, -484.30615234, 72.16763763),
                topRight = vector3(-1368.30078125, -485.27606201, 72.15593414),
                bottomRight = vector3(-1371.03259277, -485.66439819, 72.16315918),
                bottomLeft = vector3(-1371.16784668, -484.70202637, 72.17306213)
            }
        }
    }
}

-- theme matches the theme name in bob74_ipl FinanceOffice2.
-- Texts are stored in locales/<language>.json (preset_<id>_label / preset_<id>_desc).
Config.Presets = {}

for _, presetId in ipairs({
    'warm', 'classical', 'vintage', 'contrast', 'rich',
    'cool', 'ice', 'conservative', 'polished'
}) do
    Config.Presets[presetId] = {
        label = L(('preset_%s_label'):format(presetId)),
        description = L(('preset_%s_desc'):format(presetId)),
        theme = presetId,
        category = 'executive'
    }
end

for index, presetId in ipairs({
    'agency_classic', 'agency_marble', 'agency_geometric',
    'agency_designer', 'agency_presidential', 'agency_exotic',
    'agency_tiled', 'agency_organic', 'agency_regal'
}) do
    Config.Presets[presetId] = {
        label = L(('preset_%s_label'):format(presetId)),
        description = L(('preset_%s_desc'):format(presetId)),
        category = 'agency',
        agencyStyle = index
    }
end

local agencyPresetImages = {
    agency_classic = 'img/presets/agency_wallpaper_floral.jpg',
    agency_marble = 'img/presets/agency_wallpaper_marble.jpg',
    agency_geometric = 'img/presets/agency_wallpaper_geometric.jpg',
    agency_designer = 'img/presets/agency_wallpaper_designer.jpg',
    agency_presidential = 'img/presets/agency_wallpaper_presidental.jpg',
    agency_exotic = 'img/presets/agency_wallpaper_exotic.jpg',
    agency_tiled = 'img/presets/agency_wallpaper_tiled.jpg',
    agency_organic = 'img/presets/agency_wallpaper_organic.jpg',
    agency_regal = 'img/presets/agency_wallpaper_regal.jpg'
}

for presetId, image in pairs(agencyPresetImages) do
    Config.Presets[presetId].image = image
end

Config.PresetOrder = {
    'warm',
    'classical',
    'vintage',
    'contrast',
    'rich',
    'cool',
    'ice',
    'conservative',
    'polished',
    'agency_classic',
    'agency_marble',
    'agency_geometric',
    'agency_designer',
    'agency_presidential',
    'agency_exotic',
    'agency_tiled',
    'agency_organic',
    'agency_regal'
}

Config.PresetCollections = {
    executive = {
        'warm', 'classical', 'vintage', 'contrast', 'rich',
        'cool', 'ice', 'conservative', 'polished'
    },
    agency = {
        'agency_classic', 'agency_marble', 'agency_geometric',
        'agency_designer', 'agency_presidential', 'agency_exotic',
        'agency_tiled', 'agency_organic', 'agency_regal'
    }
}
Last updated 8/27/2026Browse all docs →