FlexHour — система люксовых часов для FiveM. Игроки покупают часы у продавщицы в бутике, собирают свою конфигурацию, носят их, хвастаются ими, отдают в сервис и обменивают. Параллельно работает целая торговля подделками.
Продукт состоит из двух ресурсов: concept_flexhour (сам скрипт) и flexhour_boutique (интерьер бутика, в который можно зайти). У бутика есть отдельное руководство: Бутик FlexHour.
FlexHour использует Cfx Asset Escrow. После оплаты в Tebex ресурсы появляются в Keymaster в разделе Granted Assets — на том аккаунте Cfx.re, под которым вы входили при оплате.
concept_flexhour и flexhour_boutique приходят в одной покупке двумя отдельными загрузками. Скачайте и установите оба.concept_flexhour. Если переименовать папку, появится No such export useBox in resource concept_flexhour.config.lua и файлы locales/ остаются редактируемыми. Остальное защищено escrow.| Ресурс | Примечания |
|---|---|
ox_lib |
Используется для callback'ов и локализации. Меню, формы, окна да/нет и прогресс-бар FlexHour рисует сам. |
oxmysql |
Таблицы базы данных создаются при первом запуске. |
concept_bridge 1.1.0 или новее |
Проверено на 1.1.1. Бесплатный отдельный ресурс под GPL-3.0: concept_bridge. В этот продукт он не входит. Запускайте только один bridge. |
| Фреймворк | QBCore, qbx_core, ESX или ox_core |
| Инвентарь с метаданными у каждого предмета | Рекомендуется ox_inventory (см. ниже) |
| Ресурс таргета | ox_target, qb-target или sleepless_interact |
concept_flexhour и flexhour_boutique в папку resources.server.cfg после зависимостей. Bridge подгружает свои модули из уже запущенных ресурсов, поэтому запускайте его после фреймворка, инвентаря и таргета:ensure ox_lib
ensure oxmysql
ensure your_framework # es_extended / qb-core / qbx_core / ox_core
ensure ox_inventory # or your inventory
ensure ox_target # or your target
ensure concept_bridge
ensure concept_flexhour
ensure flexhour_boutique # the boutique interior, after concept_flexhour
add_ace group.admin flexhour.admin allow
/fhmodels. Команда покажет модели часов, которые не стримятся. Если список пуст, папка stream/ полная.Каждая модель часов — отдельный предмет с именем fh_. Конфигурация хранится в метаданных предмета: металл корпуса, циферблат, безель, камни, серийный номер, гравировка и состояние. Магазины, обмены и другие скрипты могут выдавать конкретную модель.
ox_inventory
docs/ox_items.lua (все 23 модели) в ox_inventory/data/items.lua.['flexhour_box'] = {
label = 'FlexHour Box',
weight = 450,
stack = false,
close = true,
description = 'Something shiny inside.',
client = { export = 'concept_flexhour.useBox' },
},
['flexhour_replica_kit'] = {
label = 'Replica Kit',
weight = 900,
stack = true,
close = true,
description = 'Blank case, cheap movement, dial stencils, gold plating. Not from any boutique.',
},
web/img/*.png и web/img/items/*.png в ox_inventory/web/images/. Иконок часов 78: по одной на каждую модель и материал корпуса, плюс по одной на каждую лимитированную серию. Каждые часы указывают на свою иконку через metadata.image.Инвентари в стиле qb
Добавьте предметы из docs/qb_items.lua (формат QBCore). FlexHour регистрирует их как используемые при запуске. В таких инвентарях предмет показывает название и картинку модели. Подробности конфигурации — в поле info предмета и в режиме осмотра (I, пока часы надеты).
FlexHour обращается к фреймворку (деньги, личность, работы), инвентарю, таргету и уведомлениям через concept_bridge.
| Часть | Поддерживается | Статус |
|---|---|---|
| Фреймворк | QBCore, qbx_core, ESX, ox_core | Денежные счета — cash и bank. Bridge сопоставляет cash со счётом money в ESX. |
| Инвентарь | ox_inventory | Рекомендуется. Проверено в игре. |
| Инвентарь | qb-inventory, ps-inventory, qs-inventory, tgiann-inventory, origen_inventory, codem-inventory, core_inventory, one-inventory | Должен сохранять метаданные каждого предмета. Покрыто автотестами, в игре ещё не проверено. |
| Инвентарь | Встроенный инвентарь ESX | Не поддерживается. В нём нет метаданных предметов и слотов, а часы — это и есть их метаданные. Консоль предупредит об этом при запуске. |
| Таргет | ox_target, qb-target, sleepless_interact | Вместо таргета можно использовать подсказку с клавишей: см. Config.Interaction. |
В config.lua собраны все параметры, которые сервер может менять. Измените значение и перезапустите ресурс. Если параметр не указан, FlexHour использует значение по умолчанию. То, что не является параметром, из config.lua переопределить нельзя.
| Параметр | По умолчанию | Что делает |
|---|---|---|
Config.PayAccounts |
{ 'cash', 'bank' } |
Счета для списания, по порядку. Платит первый, на котором хватает денег. |
Config.Delivery |
'box' |
'box': покупка приходит в футляре, игрок его распаковывает. 'direct': часы сразу попадают в инвентарь. |
Config.Interaction |
{ mode = 'target', key = 38, keyName = 'E' } |
Как игроки взаимодействуют с продавцами, витринами и точками: через таргет или подсказку с клавишей. |
Config.Notify |
{ system = 'flexhour', position = 'top-right' } |
Собственные уведомления FlexHour или уведомления вашего сервера (через bridge). |
Config.InspectKey |
'I' |
Клавиша осмотра надетых часов. Игроки могут переназначить её в настройках клавиш GTA. |
Config.FlexCommand |
'flex' |
Команда флекса запястьем. |
Config.FlexLoopCommand |
'flex2' |
Команда зацикленного флекса. |
Config.AdminAce |
'flexhour.admin' |
ACE-право для админ-инструментов. |
Config.AdminOpenToAll |
false |
true делает админом каждого игрока. Только для тестов. |
Config.TryOnSeconds |
12 |
Сколько длится примерка у продавца. |
Config.Clerks — список продавцов бутика. Продавщица из комплекта стоит в Вайнвуде с моделью a_f_y_business_02, сценарием WORLD_HUMAN_STAND_IMPATIENT, блипом (спрайт 617, цвет 46, масштаб 0.8) и продаёт уровни budget, mid, luxury и mythic.
Если запущен flexhour_boutique, его продавщица стоит за прилавком бутика. Эта позиция сохраняется в data/clerks.json, который имеет приоритет над config.lua. Если вы используете FlexHour без бутика, откройте /fhclerk и выберите Put the config.lua clerks back.
Config.Negotiate)| Параметр | По умолчанию | Что делает |
|---|---|---|
enabled |
true |
Игроки могут торговаться с продавцами. |
rounds |
3 |
Раунды мини-игр до финального предложения: чтение продавца, загадка и игра на память. |
cooldownSeconds |
90 |
Ожидание для игрока после любого торга. |
showFloor |
true |
Показывать минимальную цену, на которую согласится продавец. |
onWalkAway |
'list' |
'list': игрок всё ещё может купить по прайсовой цене. 'abort': продажа завершается. |
Config.Services)| Параметр | По умолчанию | Что делает |
|---|---|---|
strapSwap |
1500 |
Плата за замену ремешка плюс разница в цене ремешков. |
replatePercent |
0.35 |
Стоимость перепокрытия как доля прайсовой цены. |
replateMin |
2500 |
Минимальная цена перепокрытия. |
icePerLevel |
0.40 |
Стоимость каждого уровня камней как доля прайсовой цены корпуса. |
engrave |
750 |
Гравировка (до 24 символов). |
polish |
400 |
Полировка, восстанавливающая состояние. |
authenticate |
150 |
Сертификат подлинности. |
authDays |
14 |
Сколько дней действует сертификат. 0 — бессрочно. |
Config.Limited)Config.Limited задаёт, сколько экземпляров каждой лимитированной модели может существовать вообще. См. Лимитированные серии.
Config.Wear)| Параметр | По умолчанию | Что делает |
|---|---|---|
keepWorn |
false |
false: надетые часы остаются предметом инвентаря. true: пока часы надеты, FlexHour хранит их в своей таблице, и потерять их нельзя. См. Надетые часы. |
restoreOnSpawn |
true |
Часы остаются на запястье после перезахода. Действует при keepWorn = false. Сохранённые часы остаются всегда. |
gameWatch |
'hide' |
Что делать со штатными аксессуарами GTA на запястье. 'hide': снимаются, пока надеты часы FlexHour. 'block': слот левого запястья всегда пуст. 'off': не трогать. |
conditionLossPerHour |
0.5 |
Потеря состояния за час ношения. Реплики изнашиваются быстрее. |
hideInVehicle |
false |
Скрывать часы в транспорте. |
playerAdjust |
true |
Игроки могут сами подгонять посадку командой /watchfit. Остальные видят результат. |
allowScale |
true |
Гизмо подгонки может также менять размер часов (от 0.75 до 1.25). |
fitPresets |
3 |
Слоты сохранённых посадок на игрока, для /watchpreset. |
sleeve.enabled |
true |
Игроки могут поддёрнуть рукав командой /watchsleeve. |
Config.Wear.rob)| Параметр | По умолчанию | Что делает |
|---|---|---|
enabled |
true |
Надетые часы можно отнять. |
seconds |
6 |
Сколько времени занимает снятие часов. |
when |
{ dead = true, handsUp = true, cuffed = true } |
Состояния, в которых должен быть владелец. |
cooldownMinutes |
10 |
Сколько грабитель ждёт до следующего ограбления. |
protectMinutes |
30 |
Сколько жертва защищена после ограбления. |
command |
'stealwatch' |
Команда ограбления. false отключает команду. |
handsUpAnims |
{} |
Анимации «руки вверх» вашего сервера: { { 'dict', 'clip' }, ... }. |
minigame |
false |
Ваша мини-игра: function(victim) return true end. Верните true, чтобы продолжить, false при провале. |
Config.Fakes)| Параметр | По умолчанию | Что делает |
|---|---|---|
enabled |
true |
Торговля подделками включена. |
dealer.coords / dealer.model |
в config.lua / g_m_y_ballaeast_01 |
Где стоит дилер наборов и его модель педа. |
dealer.kitPrice |
2500 |
Базовая цена набора. Уровень набора её умножает. |
dealer.cooldown |
900 |
Секунд на персонажа между наборами. |
workshop.coords / workshop.prop |
в config.lua / 'prop_tool_bench02' |
Где стоит верстак. prop = false использует стол с карты вместо создания верстака. |
fence.coords / fence.model |
в config.lua / s_m_y_dealer_01 |
Где стоит скупщик и его модель педа. |
fence.account |
'cash' |
Счёт, на который платит скупщик. Используйте 'black_money', если он есть в вашем фреймворке. |
fence.maxPayout |
30000 |
Максимум, который скупщик платит за одни часы. |
fence.cooldown |
600 |
Секунд на персонажа между продажами. |
fence.alertChance |
0.10 |
Вероятность, что о продаже сообщат полиции. |
fence.alertChanceFake |
0.25 |
Та же вероятность, если часы — реплика. |
onAlert |
function(src, coords, build) end |
Серверный хук для вашего скрипта диспетчерской. |
Чтобы расставить три точки в игре, используйте /fhfake dealer, /fhfake workshop или /fhfake fence. Расставленные точки сохраняются в data/fakes.json, который имеет приоритет над config.lua.
Всё, что читают игроки, берётся из locales/язык.json: уведомления, меню, страница магазина (ui_*), карты торга (card_*), подсказки продавца (tell_*) и реплики продавцов, скупщика и дилера (neg_*).
Включённые языки: английский (en), венгерский (hu), немецкий (de), испанский (es) и французский (fr). FlexHour использует языковую настройку ox_lib. Укажите её в server.cfg:
setr ox:locale "en" # or "hu", "de", "es", "fr"
Чтобы добавить язык, скопируйте en.json в код.json и переведите значения. Загадки для торга хранятся на сервере на всех пяти языках. Названия часов, материалов и ремешков — это названия продуктов, их не переводят.
| Команда | Что делает |
|---|---|
/watch |
Надеть или снять часы. |
/watchfit |
Подогнать посадку гизмо (см. ниже). |
/watchhand |
Перенести часы на другое запястье. |
/watchsleeve |
Поддёрнуть или опустить рукав. |
/watchpreset |
Сохранить или загрузить пресет посадки. |
/flex |
Флекс запястьем. |
/flex2 |
Зацикленный флекс. |
I |
Осмотреть надетые часы. |
X |
Убрать футляр, который вы держите. Переназначается. |
/stealwatch |
Отнять надетые часы (см. Ограбление). |
/fhhelp |
Список команд в чате. |
Гизмо подгонки работает так:
allowScale.flexhour.admin)| Команда | Что делает |
|---|---|
/fhclerk |
Переместить, переименовать или убрать размещённых продавцов либо вернуть продавцов из config.lua. |
/fhclerk |
Разместить нового продавца. Наведите призрак туда, где на прилавке должен стоять футляр. |
/fhstand |
Поставить витрину там, где вы стоите. |
/fhfake [dealer|workshop|fence] |
Управлять точками подделок или расставить их. |
/fhgive [id] |
Выдать часы в футляре с выбором конфигурации. |
/fhwrist |
Гизмо подгонки на запястье. G сохраняет посадку как серверную по умолчанию для вашей модели педа. |
/fhmodels |
Список моделей часов, которые не стримятся. |
Админ-инструменты проверяют ACE из Config.AdminAce (по умолчанию flexhour.admin):
add_ace group.admin flexhour.admin allow
Перед тем как пускать игроков, убедитесь, что Config.AdminOpenToAll равен false. При true каждый игрок — админ: он может выдавать себе часы, ставить продавцов и витрины, а строка ACE ничего не значит. Пока это включено, консоль при каждом запуске выводит красное предупреждение.
Продавщица открывает магазин. Игрок выбирает модель, затем металл корпуса, циферблат, ремешок и его цвет, безель, стрелки и уровень камней. У каждого уровня свои варианты. Игрок может примерить часы на TryOnSeconds, затем купить их или сначала поторговаться. При Delivery = 'box' часы приходят в flexhour_box. Использование футляра запускает ритуал распаковки на прилавке, и игрок держит футляр в руке, пока не уберёт его клавишей X.
Каждые подлинные часы получают уникальный серийный номер (FH-…), который записывается в реестр (flexhour_serials).
Каждая продавщица на каждую сессию получает скрытый характер из пяти черт: тщеславие, жадность, гордость, лояльность и нервозность. Язык тела даёт подсказки. Игрок разыгрывает карты — Комплимент, Наличные, Сбить цену, Блеф, Постоянный клиент, Конкурент, Комплект и История — и проходит смешанные раунды: чтение продавца, загадки и игра на память. Итог — финальное предложение. Дилер наборов и скупщик так же используют уличную колоду.
Админы ставят витрины командой /fhstand и наполняют каждую подушку из меню витрины. Витрины продают по цене на ценнике, которая должна быть от 70% до 160% прайсовой цены, и запас у них неограничен. С витриной в пределах 30 м от продавца можно торговаться с этим продавцом. В этой версии игроки не могут владеть витринами, и лимитированные серии нельзя выставлять в витрине.
Игрок носит одни часы, на любом запястье. Встроенные посадки покрывают персонажей freemode. Для кастомных педов админы сохраняют посадку для каждой модели педа, а игроки могут подогнать свою. Другие игроки видят часы вблизи: живые стрелки, блики и камни, светящиеся ночью. Действия игрока — флекс, осмотр и рукав.
Продавщица предлагает замену ремешка, перепокрытие, камни, гравировку, полировку и экспертизу. Экспертиза выдаёт подлинным часам документы, действующие authDays дней. Для реплики экспертиза показывает, что это подделка.
Существует 14 нумерованных экземпляров. Каждый — фиксированная конфигурация с циферблатом, отделкой или ремешком, которых не может быть у обычных часов. Сервер ограничивает их количество, и каждый покупатель получает собственный номер (например, «No. 3 / 10»). Когда все экземпляры проданы, серия исчезает из магазина. Продавцы продают их в группе Limited editions списка магазина.
На лимитированной серии разрешены камни, гравировка и полировка. Замену ремешка и перепокрытие отклоняют, чтобы экземпляр оставался оригинальным.
Торговля подделками идёт в три шага:
Реплика выглядит настоящей в инвентаре, но её серийного номера нет в реестре, и чем ниже её качество, тем больше она себя выдаёт: кривая печать на циферблате, уплывающее время, дёргающаяся или стоящая секундная стрелка, быстрый износ и стирающееся золотое покрытие.
При значении по умолчанию Config.Wear.keepWorn = false часы всегда остаются предметом инвентаря, даже когда надеты. Они остаются в своём слоте, и всё, что происходит с инвентарём, происходит и с часами: очистка после смерти, обыск или другой скрипт ограбления.
При keepWorn = true надетые часы убираются из инвентаря, и FlexHour хранит их в flexhour_wrist. Сняв их, вы снова получаете предмет. Пока часы надеты, их не может забрать ничего, что происходит с инвентарём: очистка после смерти, конфискация, обыск, выброс, тайник или багажник. Чтобы продать, подарить, убрать на хранение или отдать в сервис, игрок сначала снимает их командой /watch.
Если вернуть keepWorn в false, каждый персонаж при следующем входе получит надетые часы обратно в инвентарь.
Надетые часы можно забрать только одним способом — ограблением FlexHour. Игрок рядом с владельцем, который мёртв, держит руки вверх или в наручниках, может забрать часы через опцию таргета, подсказку [E] или /stealwatch. Те же часы с тем же серийным номером оказываются в карманах грабителя. Другие ресурсы могут реагировать на событие flexhour:watchRobbed.
Меню одежды в игре могут надеть часы на левое запястье (слот пропа 6) или браслет на правое (слот 7). При gameWatch = 'hide' аксессуар GTA снимается, пока на этом запястье часы FlexHour. Если меню одежды в это время сохранит образ, он сохранится без этого аксессуара. Либо снимайте часы FlexHour перед сохранением образа, либо установите gameWatch в 'off'.
-- Give a player a watch. build = { base = '', material = ..., dial = ..., strap = ..., iced = 0 }
-- Only base is required; anything you leave out or set to something invalid falls back to that model's defaults.
-- boxed = true (default) delivers it in a flexhour_box. Returns true, serial.
local ok, serial = exports.concept_flexhour:GiveWatch(source, { base = 'timor_basic' }, true)
-- Release the watch a deleted or banned character was wearing (keepWorn = true).
-- identifier = the character id from your framework (the owner column of flexhour_wrist), or an online player's server id.
-- Returns true, serial, or false when nothing was on that wrist.
local released, serial = exports.concept_flexhour:ReleaseWrist(identifier)
Идентификаторы моделей есть в shared/catalog.lua (FH.BaseOrder). Имя предмета без префикса fh_ тоже подходит.
concept_flexhour.useBox и concept_flexhour.useWatch — это export'ы предметов, которые вызывает инвентарь. Вызывать их самостоятельно не нужно.
| Событие | Аргументы | Когда |
|---|---|---|
flexhour:purchased |
src, build, price, kind |
Игрок купил часы. |
flexhour:watchRobbed |
robber, victim, serial, build |
Надетые часы отняли. |
flexhour:fenceAlert |
src, coords, build |
О продаже скупщику сообщили. Также вызывает Config.Fakes.onAlert. |
flexhour:wristReleased |
owner, serial, build |
ReleaseWrist снял надетые часы. |
flexhour:suspect |
src, reason, serial |
Возможный дубликат ('duplicate', 'watch_twice'). |
AddEventHandler('flexhour:watchRobbed', function(robber, victim, serial, build)
-- log it, alert the police, ...
end)
Таблицы создаются при первом запуске:
| Таблица | Содержит |
|---|---|
flexhour_serials |
Реестр подлинных часов (серийный номер, конфигурация, владелец). |
flexhour_wrist |
Надетые часы, которые хранит FlexHour (keepWorn = true). |
flexhour_worn |
Что надето на каждом персонаже. |
flexhour_fit |
Сохранённые посадки на запястье. |
flexhour_fit_presets |
Пресеты посадки игроков. |
flexhour_stands |
Витрины и их запас. |
flexhour_meta |
Внутреннее состояние. |
FlexHour строит только то, что вы действительно видите. Ограничения фиксированы:
| Проблема | Решение |
|---|---|
missing ox_inventory items в консоли |
Добавьте предметы из раздела Предметы. |
model "fh_…" is not streamed в F8 |
Папка stream/ неполная. Выполните /fhmodels, затем снова скопируйте stream/ из загрузки. |
| Часы висят рядом с рукой | Выполните /watchfit. Для целой модели педа админ сохраняет посадку по умолчанию клавишей G. |
No such export useBox in resource concept_flexhour |
Папку переименовали. Оставьте concept_flexhour. |
ESX's built-in inventory cannot hold a watch |
Установите инвентарь с метаданными предметов. |
| Часы пропадают из инвентаря, когда их надевают | Это keepWorn = true: часы на запястье. /watch снимает их. |
| Продавец или скупщик не видит мои часы | Они у вас на запястье. Сначала снимите их. |
| Деньги не списываются или не выплачиваются | В Config.PayAccounts должны быть названия счетов, которые ваш фреймворк знает через bridge (cash, bank, money). |
| Все могут пользоваться админ-командами | Установите Config.AdminOpenToAll = false. |
Инвентарь не записывает каждое изменение в базу сразу, поэтому жёсткое падение может откатить его на несколько минут.
Останавливайте сервер через txAdmin, а не закрытием окна.
Config.AdminOpenToAll равен false./fhmodels ничего не выводит.setr ox:locale).flexhour_boutique запускается после concept_flexhour.We aim to reply on Discord within 24 hours on business days.
Нужна помощь с установкой? Вступить в Discord или support@conceptstudio.store.