Клиентский публичный API
LXWM_GetClientOpenAPI() доступен в клиентских скриптах 5_Mission. Через него можно создать личную или временную метку, узнать положение курсора, открыть редактор или навести карту на уже существующую точку. Если общими метками должен управлять сервер, используйте серверный API. Для событий, данные которых Ваш мод уже передал клиенту, подходит локальная временная метка.
Геттер можно получить заранее, но он не загружает данные игрока. Перед работой проверяйте LXWM_GetClientOpenAPI().IsReady(): пока клиент не получил настройки сервера и не подготовил данные персонажа, результат будет false. После получения LXWM_ProtectedClientMarker или LXWM_ProtectedTemporaryMarker вызывайте методы уже у самой метки. Для необязательной зависимости поместите код вместе с объявлениями типов в #ifdef LUXELWORLDMAP. Отдельно открывать карту для чтения и записи личных и временных меток не требуется.
Пример: отметить место, куда должен вернуться игрок
Поместите метод в клиентский класс Вашего мода. Передайте позицию, например GetGame().GetPlayer().GetPosition(), после проверки, что персонаж существует. Пример оставляет ориентир только на текущем сервере и только в меню карты. Сохраните возвращённый ID, чтобы в следующий раз обновить эту же точку, а не создать вторую. У личных меток ID выдаёт карта: свой префикс добавлять не нужно, иначе поиск по изменённой строке не найдёт метку.
string CreateReturnPoint(vector ReturnPosition)
{
LXWM_ClientOpenAPI MapAPI = LXWM_GetClientOpenAPI();
if (!MapAPI.IsReady() || !MapAPI.CanWritePersonalMarkers())
{
return string.Empty;
}
LXWM_ProtectedClientMarker ReturnMarker = MapAPI.CreatePersonalMarker("Точка возврата", ReturnPosition, "map-pin");
if (!ReturnMarker)
{
Print(MapAPI.GetLastError());
return string.Empty;
}
ReturnMarker.SetOnlyCurrentServer(true);
ReturnMarker.SetShowOnMap(true);
ReturnMarker.SetShowInWorld(false);
if (!ReturnMarker.Apply())
{
Print(ReturnMarker.GetLastError());
return string.Empty;
}
return ReturnMarker.GetMarkerID();
}
Получение меток и готовность
| Сигнатура | Использование |
|---|---|
static bool IsReady()static bool CanWritePersonalMarkers() |
Первый вызов проверяет готовность карты в текущей миссии, второй ещё и возможность записи в личную базу. Ни один из них не означает, что открыто меню карты. |
static bool CanUsePersonalZones() |
Можно ли добавить зону к личной метке с учётом ограничений сервера и текущего режима. Если нельзя, можно создать обычную точку без зоны. |
static LXWM_ProtectedClientMarker CreatePersonalMarker(string MarkerName, vector WorldPosition, string MarkerIconID = "map-pin") |
Возвращает черновик или NULL. MarkerName: название; WorldPosition: вектор X Y Z в метрах; MarkerIconID: ID иконки. Сервер, мир и ID заполняются самой картой. По умолчанию включены привязка к текущему серверу и показ на карте и в мире. |
static LXWM_ProtectedClientMarker GetPersonalMarker(string MarkerID)static array<string> GetPersonalMarkerIDs() |
Найти личную метку по её ID или получить список ID доступных личных меток. Поиск не создаёт запись. Если объект не найден, результат NULL. |
static bool HasServerMarkerSnapshot()static array<string> GetServerMarkerIDs()static LXWM_ProtectedClientMarker GetServerMarker(string MarkerID) |
Сначала проверьте, получены ли серверные данные. До этого список пуст, а получение объекта вернёт NULL. После получения доступны только записи, переданные клиенту и разрешённые в текущем режиме. Это не перечень всех меток сервера. |
static bool RefreshServerMarkers()static bool IsServerRequestPending() |
Первый вызов запрашивает актуальные данные, но не ждёт ответа. Повторные запросы объединяются: не вызывайте его в каждом кадре. Вторая проверка сообщает, занят ли обработчик серверных операций; это не подтверждение успеха конкретной операции. |
static string GetLastError() |
Ошибка последнего создания или получения объекта, например LXWM_NOT_READY, LXWM_STORAGE_UNAVAILABLE или LXWM_MARKER_NOT_FOUND. После ошибки у полученного объекта спрашивайте уже его GetLastError(). |
static string GetServerID()static string GetWorldName() |
ID текущего сервера и имя текущего мира в нижнем регистре. Если карта не готова, возвращается пустая строка. |
Список причин и пример обработки приведены на странице Ошибки API меток. Обратите внимание: GetLastError() у самого API относится к получению объекта, а не к любому действию в меню.
Временные метки интеграций
Для события, которое уже известно клиенту, используйте LXWM_ProtectedTemporaryMarker. Он наследуется от LXWM_ProtectedMarker и поддерживает общие методы названия, цвета, иконки, зоны и анимаций. Это не личная метка и не клиентское представление серверной записи. Перед созданием, поиском и изменением проверяйте MapAPI.IsReady(); открывать меню карты не нужно.
Если мод события уже передаёт путь к картинке, его не нужно превращать в ID Lucide. Вызовите SetMarkerIconPath() у черновика, который вернул CreateTemporaryMarker(), и опубликуйте его через Apply(). Так работает интеграция с KGB LiveMarker. Для личной метки порядок тот же; пример и сброс иконки показаны на странице «Работа с объектом метки».
Временные метки хранятся только в оперативной памяти текущего клиента. Они не попадают в личные базы, не передаются по сети и не имеют методов Save() или SetPersistent(). Метода Copy() у временного объекта также нет; для другой точки создайте новый черновик через CreateTemporaryMarker(). Интеграция сама вызывает Delete(), когда событие заканчивается. При отключении от сервера временное состояние очищается. Автоматического срока жизни (TTL) нет; освобождение переменной или присваивание NULL метку не удаляет.
Создание и поиск
| Сигнатура LXWM_ClientOpenAPI | Использование |
|---|---|
static LXWM_ProtectedTemporaryMarker CreateTemporaryMarker(string MarkerName, vector WorldPosition, string MarkerIconID = "map-pin", bool ShowInServerCategory = true) | Возвращает неопубликованный черновик или NULL при отказе. Название, позиция X Y Z в метрах и ID иконки задаются так же, как у обычной метки. После настройки нужен первый Apply(). Последний аргумент выбирает категорию, а не сторону выполнения. |
static LXWM_ProtectedTemporaryMarker GetTemporaryMarker(string MarkerID)static array<string> GetTemporaryMarkerIDs() | Получить объект опубликованной временной метки или список их ID в текущей миссии. Неопубликованные черновики в список не входят. Если запись не найдена, объект равен NULL. Здесь нет личных или сетевых серверных меток. |
По умолчанию ID генерируется автоматически и читается через GetMarkerID(). Свой ID задавайте через SetMarkerID() с последующим Apply(). Уникальность проверяется без учёта регистра букв; рекомендуем префикс интеграции и ID события, например NORTHEVENT_42. Для повторного обновления храните объект или его ID, а не вызывайте CreateTemporaryMarker() заново.
Категория и действия игрока
GetServerMarker() и GetServerMarkerIDs() не возвращают временные метки, даже если те находятся в той же серверной категории. Для них используйте только GetTemporaryMarker() и GetTemporaryMarkerIDs(). До готовности API поиск вернёт NULL, а список будет пустым; причину можно прочитать через MapAPI.GetLastError().
ShowInServerCategory = true помещает метку в категорию серверных меток SERVER. Это только группировка в интерфейсе: источник остаётся LXWM_ClientOpenAPI.SOURCE_TEMPORARY, сетевой серверной записью метка не становится. При false используется отдельная категория EVENTS («События»), никогда не личная категория.
Игрок и администратор не могут через интерфейс редактировать, перемещать, копировать или удалять временную метку. Наведение, слежение и скрытие доступны, если соответствующие действия разрешены. Эти ограничения интерфейса не запрещают интеграции менять и удалять метку через API. Итоговая видимость также учитывает пользовательское скрытие метки или категории.
Методы временной метки
| Сигнатура LXWM_ProtectedTemporaryMarker | Использование |
|---|---|
string GetSource()bool GetPersistent() | Всегда LXWM_ClientOpenAPI.SOURCE_TEMPORARY и false соответственно. |
bool SetMarkerID(string NewValue)string GetMarkerID() | Подготовить свой ID или прочитать текущий. Новый ID становится доступен для поиска после успешного Apply(). |
bool SetAllowShowMapMarker(bool NewValue)bool GetAllowShowMapMarker()bool SetAllowShowMarker3D(bool NewValue)bool GetAllowShowMarker3D() | Независимо разрешить показ в меню карты и в мире. Изменения применяются через Apply(). Интеграция должна учитывать запреты исходного мода, а не обходить их. |
bool SetShowInServerCategory(bool NewValue)bool GetShowInServerCategory() | Переключить между серверной категорией и категорией событий через Apply(). Сохранение или синхронизация при этом не включаются. |
bool SetPositionDynamic(vector WorldPosition) | Обновить позицию опубликованной метки без Apply(). У объекта не должно быть подготовленных правок. Частые вызовы объединяются до последней позиции; обновление отображения ограничено частотой не выше 10 Гц. Для черновика используйте SetWorldPosition() и первый Apply(). |
bool Apply()bool Refresh()bool Delete()bool IsValid()string GetLastError() | Общие правила описаны на странице Работа с объектом метки. Apply публикует черновик или подготовленные поля только локально. Refresh перечитывает опубликованную запись и отбрасывает свои правки. Delete удаляет запись сразу, без последующего Apply. После отключения старый объект недействителен. |
SetPositionDynamic() не является обычным отложенным сеттером: позиция в памяти меняется сразу. Частые вызовы объединяются до последней позиции, а отображение обновляется не чаще 10 раз в секунду на ближайшем обновлении готовой карты. Это ограничение частоты, а не гарантия задержки до 0.1 с: при задержке кадра отображение обновится позже. Сначала завершите остальные изменения через Apply() либо отмените их через Refresh(). Не вызывайте Apply() после каждого перемещения. После отказа читайте ошибку у объекта; если создание или поиск вернули NULL, читайте MapAPI.GetLastError().
Пример: метка движущегося события
Поместите класс в клиентские скрипты 5_Mission своего мода. Храните его экземпляр в поле с ref. При получении события вызовите Start(EventID, EventTitle, EventPosition), при новых координатах UpdatePosition(EventPosition), при окончании события Stop(). Start() подписывается на обновления и принимает данные, но не обещает, что метка уже опубликована: создание произойдёт только после IsReady().
#ifndef SERVER
#ifdef LUXELWORLDMAP
class NorthEventMapPoint
{
private ref LXWM_ProtectedTemporaryMarker EventMarker;
private string EventMarkerID;
private string EventTitle;
private vector LatestPosition;
private bool HasNewPosition;
private bool IsSubscribed;
private bool HasFailed;
bool Start(int SourceEventID, string Title, vector WorldPosition)
{
if (IsSubscribed || EventMarker)
{
return false;
}
EventMarkerID = "NORTHEVENT_" + SourceEventID.ToString();
EventTitle = Title;
LatestPosition = WorldPosition;
HasNewPosition = true;
HasFailed = false;
LXWM_GetClientOpenAPI().GetUpdateEvent().Insert(OnMapUpdate);
LXWM_GetClientOpenAPI().GetShutdownEvent().Insert(OnMapShutdown);
IsSubscribed = true;
return true;
}
void UpdatePosition(vector WorldPosition)
{
LatestPosition = WorldPosition;
HasNewPosition = true;
}
void OnMapUpdate(float TimeSlice)
{
LXWM_ClientOpenAPI MapAPI = LXWM_GetClientOpenAPI();
if (HasFailed || !MapAPI.IsReady())
{
return;
}
if (!EventMarker)
{
EventMarker = MapAPI.CreateTemporaryMarker(EventTitle, LatestPosition);
if (!EventMarker)
{
ReportFailure(MapAPI.GetLastError());
return;
}
array<int> EventColor = {40, 180, 125};
bool Staged = EventMarker.SetMarkerID(EventMarkerID) && EventMarker.SetColorRGB(EventColor) && EventMarker.SetAllowShowMapMarker(true) && EventMarker.SetAllowShowMarker3D(false) && EventMarker.SetAnimationEnabled(RZCO_MarkerAnimationIDs.BREATH);
if (!Staged || !EventMarker.Apply())
{
ReportFailure(EventMarker.GetLastError());
return;
}
HasNewPosition = false;
}
else if (HasNewPosition)
{
if (!EventMarker.SetPositionDynamic(LatestPosition))
{
ReportFailure(EventMarker.GetLastError());
return;
}
HasNewPosition = false;
}
}
bool Stop()
{
if (EventMarker && !LXWM_GetClientOpenAPI().IsReady())
{
ReportFailure(LXWM_ClientOpenAPI.ERROR_NOT_READY);
return false;
}
if (EventMarker && EventMarker.IsValid() && !EventMarker.Delete())
{
ReportFailure(EventMarker.GetLastError());
return false;
}
Unsubscribe();
EventMarker = NULL;
HasNewPosition = false;
HasFailed = false;
return true;
}
void OnMapShutdown()
{
Unsubscribe();
// The map clears temporary state when the mission ends.
EventMarker = NULL;
HasNewPosition = false;
HasFailed = false;
}
private void Unsubscribe()
{
if (!IsSubscribed)
{
return;
}
LXWM_GetClientOpenAPI().GetUpdateEvent().Remove(OnMapUpdate);
LXWM_GetClientOpenAPI().GetShutdownEvent().Remove(OnMapShutdown);
IsSubscribed = false;
}
private void ReportFailure(string FailureCode)
{
HasFailed = true;
array<string> ErrorLines = {FailureCode};
RZCO_PrintUtils.PrintBoxedHeaderLines("NorthEvent map marker", ErrorLines);
}
}
#endif
#endif
В примере используется серверная категория по умолчанию и отключён показ в мире. На каждый ID события нужен свой экземпляр класса. Обработчик получает float TimeSlice, но не создаёт собственный цикл ожидания. После ошибки он прекращает попытки и пишет причину через RZCO_PrintUtils; вызовите Stop(), проверьте результат и запустите событие снова с исправленными данными. Stop() удаляет опубликованную метку или отменяет неопубликованный черновик через Delete(). Ошибка проверки полей в Apply() не мешает отмене черновика. Перед уничтожением владельца обязательно вызовите Stop() и проверьте результат. В OnMapShutdown() не требуется удалять каждую метку: карта сама очищает временное состояние, а пример снимает свои подписки.
Пример: изменить оформление по ID
Этот метод находит существующую временную метку, меняет название и цвет, выключает анимации и переносит её в категорию событий. Пустая строка означает успех; иначе метод возвращает код ошибки вызывающему коду. Он не удаляет метку: когда она больше не нужна, найдите её через GetTemporaryMarker() и вызовите Delete().
#ifndef SERVER
#ifdef LUXELWORLDMAP
string MarkTemporaryEventFinished(string MarkerID)
{
LXWM_ClientOpenAPI MapAPI = LXWM_GetClientOpenAPI();
if (!MapAPI.IsReady())
{
return LXWM_ClientOpenAPI.ERROR_NOT_READY;
}
LXWM_ProtectedTemporaryMarker EventMarker = MapAPI.GetTemporaryMarker(MarkerID);
if (!EventMarker)
{
return MapAPI.GetLastError();
}
array<int> FinishedColor = {150, 150, 150};
bool Staged = EventMarker.SetMarkerName("Событие завершено") && EventMarker.SetColorRGB(FinishedColor) && EventMarker.DisableAllAnimations() && EventMarker.SetShowInServerCategory(false);
if (!Staged || !EventMarker.Apply())
{
return EventMarker.GetLastError();
}
return string.Empty;
}
#endif
#endif
Если нужно восстановить список своих меток внутри текущей миссии, получите GetTemporaryMarkerIDs() и отберите только ID с префиксом своей интеграции. Не изменяйте и не удаляйте весь список: в нём могут быть метки других модов. Методы AimAtMarker(), FocusMarker() и SelectMarker() принимают временные метки: явно передавайте LXWM_ClientOpenAPI.SOURCE_TEMPORARY, например MapAPI.AimAtMarker(EventMarkerID, LXWM_ClientOpenAPI.SOURCE_TEMPORARY) при открытой карте. OpenMarkerEditor() и BeginMarkerPlacement() для этого источника возвращают false: изменять временную метку можно программно, но не через редактор и размещение в интерфейсе.
Что можно делать с полученным объектом
Методы этой таблицы вызываются у LXWM_ProtectedClientMarker. Два значения GetSource() для описанных здесь меток: LXWM_ClientOpenAPI.SOURCE_PERSONAL и LXWM_ClientOpenAPI.SOURCE_SERVER. Серверный объект на клиенте всегда только для чтения, даже у администратора. Это не мешает открыть штатный редактор, если сервер разрешил редактирование.
| Сигнатура | Использование |
|---|---|
string GetSource()bool IsReadOnly() |
Определить источник и доступность прямой записи. Личная метка поддерживает запись при доступном хранилище. У серверной нельзя напрямую вызывать методы изменения, Apply() или Delete(). |
bool SetOnlyCurrentServer(bool NewValue)bool GetOnlyCurrentServer() |
Для личной метки: true привязывает её к текущему серверу. При false она доступна и на других серверах с той же местностью. Сменить игровой мир через клиентский объект нельзя. |
bool SetShowOnMap(bool NewValue)bool GetShowOnMap()bool SetShowInWorld(bool NewValue)bool GetShowInWorld() |
Для личной метки независимо подготовить показ в меню и в игровом мире, затем применить через Apply(). У серверной записи доступно чтение. Итоговый показ зависит также от категории и правил сервера. |
LXWM_ProtectedClientMarker Copy(vector WorldPosition)bool Save() |
Copy у личной метки создаёт неопубликованный черновик с новым ID и заданной позицией, включая подготовленные правки источника. Чтобы завершить копирование, вызовите Apply() у копии. Save записывает личную базу, но не заменяет Apply и не принимает неопубликованный черновик или незавершённые правки. |
string GetServerID()string GetServerName()string GetServerAddress()string GetWorldName() |
Привязка конкретной записи. Имя и адрес записываются у личной метки при сохранении; у серверного представления эти строки могут быть пустыми. Не определяйте тип метки по адресу. |
bool GetCanEdit()bool GetCanDelete()bool GetCanCopy() |
Разрешения на действия в штатном интерфейсе. Например, GetCanEdit() у серверной метки может вернуть true одновременно с IsReadOnly() == true. |
bool GetShowToPlayers()bool GetAllowShowMapMarker()bool GetAllowShowMarker3D() |
Для серверной записи: опубликована ли метка игрокам, разрешены ли показ на карте и показ в мире. Эти поля не заменяют личные переключатели видимости. |
bool GetPersistent()bool GetDeleteAfterRestart() |
Для серверной записи: хранится ли метка на диске и помечена ли она для удаления при следующем запуске. Оба значения на клиенте только для чтения. |
bool GetShowOnlyWithinRadius()float GetVisibilityRadiusMeters()bool GetWithinDisplayRadius() |
Включён ли фильтр расстояния, сколько метров задано и проходит ли клиент текущую проверку радиуса. Метка может оставаться доступной для чтения за пределами радиуса, даже когда у неё нет панели для наведения. |
bool Aim()bool Focus()bool Select() |
Для опубликованной метки без подготовленных правок при открытой карте. Aim наводит один раз, Focus включает слежение, Select выделяет. Если для метки действие недоступно, метод возвращает false. |
bool OpenEditor()bool BeginPlacement(bool CreateCopy = false) |
Открыть штатный редактор, начать перемещение (false) или разместить копию (true). Требуются готовая карта, опубликованная метка без подготовленных правок и разрешение на действие. true означает, что действие начато, а не что игрок сохранил результат. |
Настройка цветов, иконок, размеров зоны, анимаций и правила Apply() общие с сервером: см. Работа с объектом метки.
Пример: открыть серверную метку в редакторе
Если Вы добавляете свою кнопку в админку, не пытайтесь изменить серверную метку через клиентский объект. Передайте публичный ID (например, "NORTHMARKET_NWAF_TRADER" из серверного примера) и откройте обычный редактор карты. Игрок сам подтвердит изменения, а сервер проверит его права. Этот метод вызывается при уже открытой карте:
bool OpenTraderEditor(string TraderMarkerID)
{
if (!LXWM_GetClientOpenAPI().IsMapOpen() || !LXWM_GetClientOpenAPI().HasServerMarkerSnapshot())
{
return false;
}
LXWM_ProtectedClientMarker TraderMarker = LXWM_GetClientOpenAPI().GetServerMarker(TraderMarkerID);
if (!TraderMarker || !TraderMarker.GetCanEdit() || LXWM_GetClientOpenAPI().IsServerRequestPending())
{
return false;
}
if (!TraderMarker.OpenEditor())
{
Print(TraderMarker.GetLastError());
return false;
}
return true;
}
Пример: открыть карту и навести её на точку
OpenMap() принимает запрос на открытие. Готовность меню проверяется отдельно через IsMapOpen(). Если вызвать наведение сразу после открытия, оно может вернуть false при первом запуске карты, но работать при повторном открытии. Ниже обработчик для поля Вашего клиентского класса. Вызовите OpenReturnPoint(SavedMarkerID) по действию игрока; для завершения собственного обработчика используйте CancelReturnPointRequest().
private string PendingReturnMarkerID;
private float ReturnPointWaitSeconds;
bool OpenReturnPoint(string SavedMarkerID)
{
CancelReturnPointRequest();
if (SavedMarkerID == string.Empty || !LXWM_GetClientOpenAPI().OpenMap())
{
return false;
}
PendingReturnMarkerID = SavedMarkerID;
LXWM_GetClientOpenAPI().GetUpdateEvent().Insert(OnReturnMapUpdate);
LXWM_GetClientOpenAPI().GetShutdownEvent().Insert(CancelReturnPointRequest);
return true;
}
void OnReturnMapUpdate(float TimeSlice)
{
ReturnPointWaitSeconds += TimeSlice;
if (!LXWM_GetClientOpenAPI().IsMapOpen())
{
if (ReturnPointWaitSeconds >= 5.0)
{
CancelReturnPointRequest();
}
return;
}
string ReturnMarkerID = PendingReturnMarkerID;
CancelReturnPointRequest();
LXWM_ProtectedClientMarker ReturnMarker = LXWM_GetClientOpenAPI().GetPersonalMarker(ReturnMarkerID);
if (!ReturnMarker)
{
Print(LXWM_GetClientOpenAPI().GetLastError());
return;
}
if (!ReturnMarker.Aim())
{
Print(ReturnMarker.GetLastError());
}
}
void CancelReturnPointRequest()
{
if (PendingReturnMarkerID != string.Empty)
{
LXWM_GetClientOpenAPI().GetUpdateEvent().Remove(OnReturnMapUpdate);
LXWM_GetClientOpenAPI().GetShutdownEvent().Remove(CancelReturnPointRequest);
}
PendingReturnMarkerID = string.Empty;
ReturnPointWaitSeconds = 0;
}
Пять секунд здесь ограничивают ожидание в Вашем примере, это не таймер самой карты. Обработчик снимает только свои подписки, в том числе при выходе в главное меню. GetUpdateEvent() передаёт float TimeSlice в секундах; GetShutdownEvent() не передаёт аргументы.
Окно карты и редактор
Остальные методы на этой странице относятся к LXWM_ClientOpenAPI. Параметр Source у методов с ID по умолчанию обозначает личную метку. Для серверной явно указывайте LXWM_ClientOpenAPI.SOURCE_SERVER, даже если Вы знаете её ID. Для временной метки при наведении, слежении и выделении используйте LXWM_ClientOpenAPI.SOURCE_TEMPORARY. Открытие редактора и размещение для временных меток недоступны.
| Сигнатура | Использование |
|---|---|
static bool OpenMap()static bool IsMapOpen()static bool CloseMap() |
Запросить открытие по правилам горячей клавиши; проверить открытое и готовое меню; закрыть текущее меню карты. Открытие не обходит требования сервера к предмету карты. |
static ScriptInvoker GetUpdateEvent()static ScriptInvoker GetShutdownEvent() |
Подписывайтесь через Insert, отписывайтесь через Remove. Обновления приходят и во время загрузки. Не очищайте чужие подписки через Clear. |
static bool OpenSettings()static bool HasOpenWindow()static bool CloseTopWindow() |
Открыть настройки, проверить наличие вложенного окна или закрыть верхнее окно. CloseTopWindow не равнозначен закрытию всей карты. |
static bool OpenPersonalMarkerCreation(vector WorldPosition)static bool OpenServerMarkerCreation(vector WorldPosition) |
Открыть редактор новой метки в указанной точке. Для личной нужна возможность записи, для серверной ещё и активный режим администратора. Сама метка при этом не сохраняется. |
static bool OpenMarkerEditor(string MarkerID, string Source = SOURCE_PERSONAL)static bool BeginMarkerPlacement(string MarkerID, bool CreateCopy = false, string Source = SOURCE_PERSONAL) |
Открыть существующую запись по ID или начать размещение. Например: LXWM_GetClientOpenAPI().BeginMarkerPlacement("NORTHMARKET_NWAF_TRADER", true, LXWM_ClientOpenAPI.SOURCE_SERVER) начнёт размещение копии серверной метки при наличии прав. |
static bool IsPlacingMarker()static bool CancelMarkerPlacement() |
Проверить режим размещения и отменить предпросмотр. Отмена не отзывает уже отправленный запрос на сохранение. |
static bool HasAdministratorRights()static bool IsAdministrator()static bool SetAdminMode(bool Enabled) |
Первый вызов проверяет реальные права администратора карты. Второй учитывает, включён ли сейчас режим администратора. SetAdminMode(false) переключает клиент в режим просмотра игроком, не отзывая права на сервере. Назначить себе права через этот метод нельзя. |
Камера, координаты и каталог иконок
| Сигнатура | Использование |
|---|---|
static bool AimAtPosition(vector WorldPosition)static bool AimAtMarker(string MarkerID, string Source = SOURCE_PERSONAL) |
Один раз навести карту на мировую позицию или видимую метку. Вариант с ID требует доступного действия наведения. |
static bool FocusMarker(string MarkerID, string Source = SOURCE_PERSONAL)static bool FocusPlayer()static bool ClearFocus() |
Следить за меткой, следить за своим персонажем или прекратить слежение. Фокус на персонаже зависит от настроек сервера, а фокус на метке требует, чтобы он был разрешён для неё. |
static bool SelectMarker(string MarkerID, string Source = SOURCE_PERSONAL)static bool ClearSelection() |
Подсветить конкретную метку или снять выделение. Выделение не означает слежение камерой. |
static bool SetMapScale(float MapScale, bool Animate = true)static float GetMapScale() |
Положительный масштаб: большее значение отдаляет карту. Движок применяет допустимые границы. Animate = false применяет масштаб сразу; чтение при закрытом меню вернёт 0. |
static bool GetMapPosition(out vector WorldPosition)static bool GetCursorWorldPosition(out vector WorldPosition) |
Записывают центр карты или точку под курсором в переданную переменную vector. Проверяйте возвращаемый bool, а не сравнивайте результат с нулевым вектором. При вызове не пишите out перед переменной. |
static bool SetPersonalCategoryVisibility(bool ShowOnMap, bool ShowInWorld)static bool ShowsPersonalCategoryOnMap()static bool ShowsPersonalCategoryInWorld() |
Общий переключатель личных меток, отдельно карта и мир. Чтобы поменять только одно значение, вторым передайте результат соответствующего метода чтения. |
static bool FlushPersonalMarkers() |
Принудительная запись личной базы, не способ применить отложенные изменения отдельного объекта. Возвращает false, если операция не выполнена. |
static array<string> GetIconCategoryIDs()static array<string> GetIconIDs(string CategoryID = "ALL") |
Вернуть ID категорий или иконок. Без аргумента GetIconIDs() возвращает весь каталог. Передавайте этот ID в SetMarkerIconID(). Для пути к своей картинке используйте SetMarkerIconPath(). |
static string GetIconName(string MarkerIconID)static string GetIconCategoryName(string CategoryID) |
Получить переведённое для текущего клиента название иконки или категории. Неизвестный ID даст пустую строку. |
Например, так можно открыть штатный редактор личной метки под курсором, оставив игроку выбор названия и иконки. Вызывайте метод по нажатию своей кнопки при открытой карте:
bool EditNewMarkerAtCursor()
{
if (!LXWM_GetClientOpenAPI().CanWritePersonalMarkers() || LXWM_GetClientOpenAPI().HasOpenWindow())
{
return false;
}
vector CursorWorldPosition;
if (!LXWM_GetClientOpenAPI().GetCursorWorldPosition(CursorWorldPosition))
{
return false;
}
return LXWM_GetClientOpenAPI().OpenPersonalMarkerCreation(CursorWorldPosition);
}
Пример: второй личный ориентир по образцу первого
Игрок поставил метку у первой стоянки и хочет оформить вторую так же. Copy() здесь создаёт личный черновик по готовым координатам, без открытия меню. Проверьте разрешение на зоны: если правила поменялись с момента создания оригинала, предложите копию без зоны. Исходная метка останется на месте.
string CopyCampsite(string SavedMarkerID, vector SecondCampPosition)
{
LXWM_ClientOpenAPI MapAPI = LXWM_GetClientOpenAPI();
if (!MapAPI.CanWritePersonalMarkers())
{
return string.Empty;
}
LXWM_ProtectedClientMarker FirstCamp = MapAPI.GetPersonalMarker(SavedMarkerID);
if (!FirstCamp || FirstCamp.IsReadOnly())
{
return string.Empty;
}
LXWM_ProtectedClientMarker SecondCamp = FirstCamp.Copy(SecondCampPosition);
if (!SecondCamp)
{
return string.Empty;
}
SecondCamp.SetMarkerName("Запасная стоянка");
if (!MapAPI.CanUsePersonalZones())
{
SecondCamp.SetZoneEnabled(false);
}
if (!SecondCamp.Apply())
{
Print(SecondCamp.GetLastError());
return string.Empty;
}
return SecondCamp.GetMarkerID();
}
У уже опубликованной личной метки без несохранённых изменений можно вызвать SecondCamp.Save(); если объект не нужен и требуется записать всю личную базу, используйте MapAPI.FlushPersonalMarkers(). Не вызывайте оба метода подряд. А если игрок сам должен выбрать точку мышью, вместо копирования из скрипта проверьте FirstCamp.GetCanCopy() и вызовите FirstCamp.BeginPlacement(true) при открытой карте. Для перемещения оригинала передайте false.
Пример: проверить, почему не видно ориентира
Если карта готова, но снимок серверных меток ещё не пришёл, пример запрашивает его и выходит. Выполните проверку снова после получения данных, а не в цикле. По ID можно отличить отсутствие записи в снимке от запрета показа в мире или выхода за разрешённый радиус.
void InspectServerMarker(string MarkerID)
{
LXWM_ClientOpenAPI MapAPI = LXWM_GetClientOpenAPI();
if (!MapAPI.IsReady())
{
return;
}
if (!MapAPI.HasServerMarkerSnapshot())
{
if (!MapAPI.IsServerRequestPending())
{
MapAPI.RefreshServerMarkers();
}
return;
}
array<string> VisibleIDs = MapAPI.GetServerMarkerIDs();
if (VisibleIDs.Find(MarkerID) == -1)
{
Print("No matching ID in the current snapshot: " + MarkerID);
return;
}
LXWM_ProtectedClientMarker Point = MapAPI.GetServerMarker(MarkerID);
if (!Point)
{
return;
}
Print(string.Format("Visible to players: %1 | Allowed on map: %2 | Allowed in world: %3", Point.GetShowToPlayers(), Point.GetAllowShowMapMarker(), Point.GetAllowShowMarker3D()));
if (Point.GetShowOnlyWithinRadius())
{
Print(string.Format("Radius: %1 m | In range: %2", Point.GetVisibilityRadiusMeters(), Point.GetWithinDisplayRadius()));
}
Print(string.Format("Persistent: %1 | Delete on restart: %2", Point.GetPersistent(), Point.GetDeleteAfterRestart()));
}
Для списка личных меток используйте MapAPI.GetPersonalMarkerIDs(). Узнать, откуда взялась запись, можно через Point.GetSource(), а у личного ориентира прочитать Point.GetWorldName(), Point.GetOnlyCurrentServer(), Point.GetServerID(), Point.GetServerName() и Point.GetServerAddress(). Последние три поля относятся к записи; MapAPI.GetServerID() и MapAPI.GetWorldName() относятся к текущему подключению.
Пример: управление из своего меню
В первом методе администратор открывает редактор серверной метки в центре карты. Метод специально не включает режим администратора сам: если администратор смотрит карту от лица игрока, сохраняем его выбор. Для отдельного переключателя режима проверьте MapAPI.HasAdministratorRights() и вызывайте MapAPI.SetAdminMode(!MapAPI.IsAdministrator()) только по действию пользователя.
bool CreateServerPointAtMapCenter()
{
LXWM_ClientOpenAPI MapAPI = LXWM_GetClientOpenAPI();
if (!MapAPI.IsMapOpen() || MapAPI.HasOpenWindow() || !MapAPI.IsAdministrator())
{
return false;
}
vector CenterPosition;
if (!MapAPI.GetMapPosition(CenterPosition))
{
return false;
}
return MapAPI.OpenServerMarkerCreation(CenterPosition);
}
bool BackFromMapTool()
{
LXWM_ClientOpenAPI MapAPI = LXWM_GetClientOpenAPI();
if (MapAPI.HasOpenWindow())
{
return MapAPI.CloseTopWindow();
}
if (MapAPI.IsPlacingMarker())
{
return MapAPI.CancelMarkerPlacement();
}
return MapAPI.CloseMap();
}
Так кнопка «Назад» закрывает сначала верхнее окно, затем активное размещение и только потом саму карту. Для кнопки перехода в настройки используйте MapAPI.OpenSettings(). Для редактирования по ID без получения объекта: MapAPI.OpenMarkerEditor("NORTHMARKET_NWAF_TRADER", LXWM_ClientOpenAPI.SOURCE_SERVER). При построении собственных кнопок редактирования, копирования и удаления читайте Point.GetCanEdit(), Point.GetCanCopy() и Point.GetCanDelete(): наличие кнопки не даёт клиенту права напрямую записывать серверную метку.
Пример: показать маршрут или следить за конвоем
Вызывайте при уже открытой карте. Для неподвижной точки достаточно наведения один раз; для конвоя включите слежение. Масштаб меняется только если карта была отдалена сильнее заданного значения. У личной метки можно вызвать Point.Select(), Point.Aim() или Point.Focus() без явного указания источника.
bool ShowRoutePoint(string MarkerID, bool TrackConvoy)
{
LXWM_ClientOpenAPI MapAPI = LXWM_GetClientOpenAPI();
if (!MapAPI.IsMapOpen() || !MapAPI.SelectMarker(MarkerID, LXWM_ClientOpenAPI.SOURCE_SERVER))
{
return false;
}
if (TrackConvoy)
{
return MapAPI.FocusMarker(MarkerID, LXWM_ClientOpenAPI.SOURCE_SERVER);
}
MapAPI.ClearFocus();
if (!MapAPI.AimAtMarker(MarkerID, LXWM_ClientOpenAPI.SOURCE_SERVER))
{
return false;
}
if (MapAPI.GetMapScale() > 0.2)
{
return MapAPI.SetMapScale(0.2, true);
}
return true;
}
Если известны только координаты, используйте MapAPI.AimAtPosition(DestinationPosition). При завершении маршрута можно снять выделение через MapAPI.ClearSelection(), выключить слежение через MapAPI.ClearFocus() и предложить игроку вернуться к персонажу через MapAPI.FocusPlayer().
Пример: вывести каталог иконок для своего редактора
В свой список выбора сохраняйте ID, а игроку показывайте переведённую подпись. Здесь для краткости результат выводится в лог. Если категории не нужны, получите весь список одним вызовом MapAPI.GetIconIDs().
void PrintIconChoices()
{
LXWM_ClientOpenAPI MapAPI = LXWM_GetClientOpenAPI();
array<string> CategoryIDs = MapAPI.GetIconCategoryIDs();
foreach (string CategoryID: CategoryIDs)
{
Print(MapAPI.GetIconCategoryName(CategoryID));
array<string> IconIDs = MapAPI.GetIconIDs(CategoryID);
foreach (string IconID: IconIDs)
{
Print(string.Format("%1: %2", IconID, MapAPI.GetIconName(IconID)));
}
}
}