AI Core
Справочник APILib: клиент

Точки взаимодействия

Points - каждое место, которым пользуется игрок (прилавки, магазины, двери, NPC, банкоматы, багажники транспорта), в виде подсказки или опции target, с педами, блипами и точками, построенными из записей панели.

Точка это любое место, которым пользуется игрок: прилавок магазина, дверь, NPC, место возврата, каждый банкомат, багажник каждой машины. Вы описываете её один раз (где, какой текст, что происходит), а lib рисует её так, как выбрал владелец сервера.

Prompt или target

Владелец выбирает стиль взаимодействия в админ-панели ai-core (Interaction). В обоих случаях выполняется один и тот же onPressed, поэтому вашему скрипту неважно, какой стиль включён.

СтильЧто видит игрок
PromptОкно «нажмите E» справа на экране, пока игрок стоит внутри радиуса точки. Клавиша это одна переназначаемая клавиша в ai-lib (по умолчанию E), поэтому ничто не опрашивает клавиши каждый кадр.
TargetОпция в глазе target на месте, NPC, объекте или транспорте. Для этого должен быть запущен один из target-скриптов, для которых в lib есть адаптер. Если запущено несколько, побеждает первый в таком порядке: ai-target, tgiann-radialmenu-target, tgiann-target, sleepless_interact, ox_target, qb-target, interact.

Если владелец выбрал target, но ни один target-скрипт не запущен, каждая точка становится подсказкой (prompt). Точка может переопределить выбор владельца: promptOnly оставляет подсказку (действия из транспорта: топливные колонки, автомойки), preferTarget использует опцию target всегда, когда запущен target-скрипт (сиденья). Когда target-скрипт запускается или останавливается или владелец переключает стиль, каждая точка переходит сама.

Одновременно выполняется только одно действие точки вашего ресурса. Когда onPressed возвращает promise, остальные точки вашего ресурса игнорируют нажатия, пока он не завершится, поэтому собственный флаг busy вам никогда не нужен. Кроме того, в момент нажатия игрок должен проходить canShow.

new Points

Создаёт группу точек. Создайте одну группу на функцию в начале файла и добавляйте в неё точки.

Сторона: Клиент · Ждёт: нет

Сигнатура
new Points()
Lua
local points = aiLib.Points.new()

Возвращает

Группа Points с методами ниже.

Пример

import { Points } from "@aicore-fivem/lib/client";

const points = new Points();

points.add

Добавляет точку в месте или заменяет точку с тем же id. Точка может иметь собственного NPC (ped) и блип на карте (blip).

Сторона: Клиент · Ждёт: нет

Сигнатура
points.add(id: string, options: PointOptions): void
Lua
points:add(id, options)

Параметры

Prop

Type

PointOptions

Prop

Type

PointPed

Поле ped принимает опции LocalPed, кроме onSpawn и onDespawn (их использует точка):

Prop

Type

PointBlip

Поле blip принимает опции блипа; coords по умолчанию равен точке:

Prop

Type

PromptAnchor

Что возвращает функция world (PromptAnchor), одно из:

Prop

Type

Возвращает

void.

Пример

resource/client/index.ts
import { callAction, createLocale, hasJob, onMembershipChange, Points } from "@aicore-fivem/lib/client";

const Locale = createLocale<typeof import("../../locales/en.json")>();
const points = new Points();

// Прилавок магазина с продавцом и блипом на карте.
points.add("shop", {
  coords: { x: 25.7, y: -1347.3, z: 29.5 },
  label: Locale("OPEN_SHOP"),
  icon: "basket-shopping",
  ped: { model: "mp_m_shopkeep_01", coords: { x: 24.5, y: -1346.6, z: 29.5, heading: 270 } },
  blip: { sprite: 52, color: 2, label: Locale("SHOP") },
  onPressed: () => callAction("myshop:open", [], { fallback: "SHOP_FAILED" }),
});

// Хранилище, которое может открыть только полиция; текст следует состоянию.
let locked = true;
points.add("evidence", {
  coords: { x: 474.9, y: -996.4, z: 26.3 },
  label: () => (locked ? Locale("UNLOCK") : Locale("LOCK")),
  icon: "box-archive",
  canShow: () => hasJob(["police"], 2),
  onPressed: async () => {
    const reply = await callAction("myscript:toggle", [], { fallback: "TOGGLE_FAILED" });
    if (reply) locked = !locked;
    points.refresh("evidence");
  },
});

// canShow читает работу: проверяем снова, когда меняются работы, дежурство или банды.
onMembershipChange(() => points.refresh());

canShow решает только то, что видит игрок. Сервер должен снова проверить (работу, расстояние, запасы) в своём обработчике. Читер может вызвать ваше серверное событие без всякой точки.

points.addModel

Добавляет точку на каждый объект мира с определёнными моделями (банкоматы, торговые автоматы, мусорные контейнеры, стулья) или заменяет точку с тем же id. В режиме target это опция target на этих моделях; в режиме prompt подсказка показывается рядом с ближайшим объектом. Поиск ближайшего объекта выполняется один раз в ai-lib для всех ресурсов, два раза в секунду, и приостанавливается, пока игрок стоит на месте.

Сторона: Клиент · Ждёт: нет

Сигнатура
points.addModel(id: string, options: ModelPointOptions): void
Lua
points:addModel(id, options)

Параметры

Prop

Type

ModelPointOptions

Prop

Type

Возвращает

void.

Пример

import { callAction, createLocale, Points } from "@aicore-fivem/lib/client";

const Locale = createLocale<typeof import("../../locales/en.json")>();
const points = new Points();

points.addModel("atm", {
  models: ["prop_atm_01", "prop_atm_02", "prop_atm_03", "prop_fleeca_atm"],
  label: Locale("USE_ATM"),
  icon: "credit-card",
  onPressed: (atm) => callAction("mybank:atm", [NetworkGetNetworkIdFromEntity(atm)], { fallback: "ATM_FAILED" }),
});

Два состояния одного объекта (привязать и отвязать лодку) это две точки моделей с противоположным canShow, а не одна функция подписи. Точка с более чем 6 моделями находит свои объекты через индекс пула объектов, поэтому сотни моделей стоят столько же, сколько несколько.

points.addVehicle

Добавляет точку на каждый сетевой транспорт (багажник, капот, толкать, ремонт) или заменяет точку с тем же id. В режиме target это глобальная опция транспорта target-скрипта; в режиме prompt подсказка показывается у ближайшего сетевого транспорта в пределах radius.

Сторона: Клиент · Ждёт: нет

Сигнатура
points.addVehicle(id: string, options: VehiclePointOptions): void
Lua
points:addVehicle(id, options)

Параметры

Prop

Type

Возвращает

void.

Пример

import { cache, createLocale, Points } from "@aicore-fivem/lib/client";

const Locale = createLocale<typeof import("../../locales/en.json")>();
const points = new Points();

points.addVehicle("trunk", {
  label: Locale("OPEN_TRUNK"),
  icon: "box-open",
  radius: 2.5,
  canShow: (vehicle) => vehicle !== 0 && cache.vehicle === false && GetVehicleDoorLockStatus(vehicle) < 2,
  onPressed: (vehicle) => SetVehicleDoorOpen(vehicle, 5, false, false),
});

points.remove

Удаляет одну точку вместе с её подсказкой, опцией target, NPC и блипом. Работает для точек, созданных через add, addModel и addVehicle. Неизвестный id ничего не делает.

Сторона: Клиент · Ждёт: нет

Сигнатура
points.remove(id: string): void
Lua
points:remove(id)

Параметры

Prop

Type

Возвращает

void.

Пример

points.remove("shop");

points.clear

Удаляет все точки группы.

Сторона: Клиент · Ждёт: нет

Сигнатура
points.clear(): void
Lua
points:clear()

Возвращает

void.

Пример

onServerEvent("myscript:jobEnded", () => points.clear());

Никогда не перестраивайте точки панели через clear() + добавление всего при каждом изменении панели: при тысячах записей одна правка перестраивала бы их все на каждом клиенте. Используйте RecordObjects: правка перестраивает только свою запись.

points.refresh

Заново читает canShow и функции подписей. Вызывайте, когда изменилось то, что они читают: работа, дежурство, транспорт, состояние, которое прислал сервер. Подсказки обновляются сразу; опции target читают canShow при открытии глаза, а изменённая подпись ставится заново.

Сторона: Клиент · Ждёт: нет

Сигнатура
points.refresh(id?: string): void
Lua
points:refresh() -- каждая точка группы

Параметры

Prop

Type

Возвращает

void.

Пример

import { EntityState, onCache, onMembershipChange, Points, stateBoolean } from "@aicore-fivem/lib/client";

const points = new Points();
const robbed = new EntityState("robbed", stateBoolean);

onCache("vehicle", () => points.refresh());
onMembershipChange(() => points.refresh());
// Изменилась одна касса: обновляем только её точку.
robbed.onChange((netId) => points.refresh(`register:${netId}`));

Внутри голого AddStateBagChangeHandler новое значение ещё не читается: вызывайте там refresh в setTimeout(..., 0). Классы состояния lib (EntityState.onChange) уже дают вам новое значение.

points.isInside

Стоит ли игрок сейчас внутри радиуса точки (точки, созданные через add).

Сторона: Клиент · Ждёт: нет

Сигнатура
points.isInside(id: string): boolean

Lua-аналога нет.

Параметры

Prop

Type

Возвращает

boolean: true, пока игрок внутри; false снаружи, а также для неизвестного id или точки модели.

Пример

if (points.isInside("garage")) notify(Locale("PARK_HERE"), "info");

points.restyle

Переводит все точки группы на текущий стиль взаимодействия. Lib вызывает это сама, когда владелец переключает стиль или target-скрипт запускается или останавливается; вам это никогда не нужно.

Сторона: Клиент · Ждёт: нет

Сигнатура
points.restyle(): void

Lua-аналога нет (Lua тоже меняет стиль сам).

Возвращает

void.

Точки из записей панели

Большинство точек берутся из файла записей админ-панели вашего скрипта (магазины, гаражи, стойки аренды). Зеркалируйте файл на клиенте через AdminWorld, затем стройте точки каждой записи через RecordObjects: изменённая запись перестраивает только свои точки, удалённая их убирает.

new RecordObjects

Поддерживает объекты мира из файла записей (точки, блипы, педы, зоны) в соответствии с файлом, по одной записи за раз. build(key, record) создаёт объекты одной записи и возвращает способ их удалить. Он строит все записи в своём конструкторе.

Сторона: Клиент и сервер (из @aicore-fivem/lib/shared) · Ждёт: нет

Сигнатура
new RecordObjects<T>(
  source: RecordSource,
  file: string,
  records: RecordReader<T> | ReadonlyMap<string, T>,
  build: (key: string, record: T) => (() => void) | void,
)

Lua-аналога нет.

Параметры

Prop

Type

Члены

Prop

Type

Пример

resource/client/index.ts
import { AdminWorld, callAction, createLocale, Points } from "@aicore-fivem/lib/client";
import { readVec3, RecordObjects, str } from "@aicore-fivem/lib/shared";

const Locale = createLocale<typeof import("../../locales/en.json")>();
const world = new AdminWorld(["settings", "shops"]);
const points = new Points();

interface Shop { label: string; coords: { x: number; y: number; z: number } }
const readShop = (_key: string, raw: Record<string, unknown>): Shop | null => {
  const coords = readVec3(raw.coords);
  return coords ? { label: str(raw.label, "Shop"), coords } : null;
};

const shops = new RecordObjects(world, "shops", readShop, (id, shop) => {
  points.add(id, {
    coords: shop.coords,
    label: Locale("OPEN_SHOP", shop.label),
    onPressed: () => callAction("myshop:open", [id], { fallback: "SHOP_FAILED" }),
  });
  return () => points.remove(id);
});

// Изменилась настройка, которую читает каждый магазин: перестраиваем все.
world.onChange("settings", () => shops.rebuildAll());

RecordObjects строит в своём конструкторе. Создавайте его после всего, что читает build, а внутри build используйте аргумент record: поле класса, которое хранит RecordObjects, во время этой первой постройки ещё не присвоено.

new AdminWorld

Клиентская копия мировых файлов панели вашего скрипта (файлов, в схеме которых указано sync: "all", см. Админ-панель). Она читает копию, которую FiveM закэшировал при старте, один раз спрашивает сервер о записях, изменённых с тех пор, а затем следует живым изменениям. Никогда не опрашивает.

Сторона: Клиент · Ждёт: нет (promise ready сообщает, когда пришли изменения с момента старта)

Сигнатура
new AdminWorld(files: string[])

Lua-аналога нет.

Параметры

Prop

Type

Члены

Prop

Type

Пример

import { AdminWorld } from "@aicore-fivem/lib/client";
import { num, record } from "@aicore-fivem/lib/shared";

const world = new AdminWorld(["settings"]);
const settings = world.view("settings", (raw) => ({
  radius: num(record(raw).radius, 2),
}));

world.onChange("settings", () => console.log("new radius", settings.value.radius));

На этой странице