Skip to content

API ShopBuy

Codex edited this page Sep 19, 2026 · 4 revisions

ShopBuy

Constants & Special Values · UO.ObjAtLayer · UO.ObjAtLayerEx

ShopBuy — строковое обозначение слоя 27 (0x1B): контейнер непополняемого ассортимента NPC-продавца. Это специальное значение BASIC, а не команда UO.

Точный синтаксис

ShopBuy -> String (чтение без скобок)

Значение читается без круглых скобок. Способы передачи в команды и отличие от числового ID описаны ниже.

Параметры

Параметров нет.

Возвращает

String "ShopBuy" при чтении без скобок. Соответствующий номер слоя — 27; это отдельное числовое значение. Чтение имени не возвращает ID предмета/контейнера.

Поведение

  • Обозначает слой 27 (0x1B): контейнер непополняемого ассортимента NPC-продавца. Другие зарегистрированные имена этого слоя: NRstk.
  • Bare ShopBuy возвращает строку "ShopBuy", а НЕ Integer 27. Числовой номер и строковое имя нельзя подменять в произвольных командах.
  • UO.ObjAtLayer(ShopBuy) принимает строковое имя и читает слой собственного персонажа. UO.ObjAtLayer(27) принимает номер. В текущей UO.ObjAtLayerEx первый параметр — номер: UO.ObjAtLayerEx(27, lasttarget). Передавать ShopBuy в ObjAtLayerEx нельзя: там нет преобразования имени слоя в номер.
  • UO.ObjAtLayer со строковым именем возвращает hex-строку serial при наличии предмета и Integer 0 при отсутствии. Числовая форма и UO.ObjAtLayerEx возвращают Integer serial/0. Это ID содержимого слота, не его graphic/type и не номер слоя.
  • Чтение использует уже полученные клиентом данные. Оно не экипирует предмет, не открывает банк/магазин и не запрашивает содержимое контейнера. Ноль может означать пустой слот, неизвестного персонажа или ещё не загруженный слой.
  • Это vendor layer. Само имя ничего не покупает/продаёт и не устанавливает цену. Сервер может не прислать магазинные контейнеры и их содержимое до открытия торговли; даже после открытия доступность зависит от протокола сервера. Пустой список не доказывает отсутствие товаров у продавца.

Как это выполняется внутри

Ниже описана реализация этого движка. Имена внутренних методов не являются дополнительными командами BASIC.

1. AddLayerValue / layerNameToNumber

При регистрации создаётся чтение имени слоя как строки. Таблица отдельно связывает имена с числами.

Для ShopBuy таблица содержит 27, но само bare-значение остаётся String.

Исходник проекта: external/InjectionScript/src/InjectionScript/Runtime/InjectionApiUO.cs; функция AddLayerValue / layerNameToNumber.

2. ObjAtLayer / ConvertLayer

Строковая форма ObjAtLayer ищет имя в таблице без учёта регистра. Числовая форма использует номер непосредственно.

Bridge читает слот self. Строковая форма оформляет найденный serial как hex; пустой слот возвращает Integer 0.

Исходник проекта: external/InjectionScript/src/InjectionScript/Runtime/InjectionApiUO.cs; функция ObjAtLayer / ConvertLayer.

3. ObjAtLayerEx

Получает числовой layer и mobileId. Проверяет диапазон 1..29 и ищет слой у загруженного Mobile.

Возвращает serial предмета либо 0. Никаких пакетов покупки, экипировки или открытия не отправляет.

Исходник проекта: src/ClassicUO.Client/Game/Managers/ClassicUOInjectionApiBridge.cs; функция ObjAtLayerEx.

Полный helper ReadLayer приведён в примерах обычных слоёв; для vendor-слоёв ниже приведены все строки обхода контейнера.

Примеры

Пример 1. Получить магазинный контейнер и перечислить товары

SUB Main()
    # ShopBuy: vendor layer 27 (0x1B). Сам ничего не покупает.
    # lasttarget должен содержать ID выбранного NPC-продавца.
    VAR vendor = lasttarget
    IF vendor = 0 THEN
        UO.Print("Vendor not selected")
        RETURN
    END IF

    # В ObjAtLayerEx передаётся числовой номер, не строка ShopBuy.
    VAR shop = UO.ObjAtLayerEx(27, vendor)
    IF shop = 0 THEN
        UO.Print("ShopBuy container is not loaded")
        RETURN
    END IF
    UO.Print("Shop container ID: " + CStr(shop))

    # Только уже загруженное непосредственное содержимое. Вложенности не обходятся.
    VAR items = UO.GetContent(shop)
    VAR i = 0
    WHILE i < GetArrayLength(items)
        VAR item = items[i]
        UO.Print(UO.GetName(item) + " | ID: " + CStr(item))
        UO.Print("Graphic: " + CStr(UO.GetType(item)))
        i = i + 1
    WEND
    UO.Print("Loaded entries: " + CStr(GetArrayLength(items)))
END SUB

Разбор параметров и выполнения:

  • LayerType=27; PlayerID=vendor; shop — serial контейнера. Для GetContent передаётся shop, а не номер слоя.
  • Массив индексируется с 0 до длины минус 1. Каждый элемент — serial. Пустой массив означает отсутствие загруженных непосредственных элементов; покупка не выполняется.

Пример 2. Отобрать товары нужного graphic и посчитать единицы

SUB Main()
    VAR vendor = lasttarget
    IF vendor = 0 THEN
        UO.Print("Vendor not selected")
        RETURN
    END IF
    VAR shop = UO.ObjAtLayerEx(27, vendor)
    IF shop = 0 THEN
        UO.Print("Shop layer is not loaded")
        RETURN
    END IF
    # Демонстрационный graphic ткани. Подставьте нужный graphic/type.
    VAR wantedType = 0x1766
    VAR stacks = 0
    VAR units = 0
    VAR items = UO.GetContent(shop)
    VAR i = 0
    WHILE i < GetArrayLength(items)
        VAR item = items[i]
        IF UO.GetType(item) = wantedType THEN
            stacks = stacks + 1
            # GetQuantity читает количество единиц в конкретном стеке.
            units = units + UO.GetQuantity(item)
        END IF
        i = i + 1
    WEND
    UO.Print("Stacks: " + CStr(stacks) + " | Units: " + CStr(units))
END SUB

Разбор параметров и выполнения:

  • wantedType — graphic, а item — ID отдельного предмета. stacks считает записи/стопки; units складывает количество в них.
  • Это чтение уже полученного количества, а не остаток, гарантированный будущей покупкой. Цена здесь не назначается.

Пример 3. Сравнить три магазинных слоя через полную процедуру

SUB Main()
    VAR vendor = lasttarget
    IF vendor = 0 THEN
        UO.Print("Vendor not selected")
        RETURN
    END IF
    # Каждый слой читается отдельно. Имена нижеподписи, номерапараметры API.
    ShowVendorLayer(vendor, 26, "ShopBuyRestock / Rstk")
    ShowVendorLayer(vendor, 27, "ShopBuy / NRstk")
    ShowVendorLayer(vendor, 28, "ShopSell / Sell")
END SUB

SUB ShowVendorLayer(vendor, layerNumber, caption)
    VAR container = UO.ObjAtLayerEx(layerNumber, vendor)
    IF container = 0 THEN
        UO.Print(caption + ": not loaded")
        RETURN
    END IF
    VAR items = UO.GetContent(container)
    UO.Print(caption + " | Container ID: " + CStr(container))
    UO.Print("Loaded entries: " + CStr(GetArrayLength(items)))
END SUB

Разбор параметров и выполнения:

  • ShowVendorLayer объявлена целиком. vendor — ID NPC, layerNumber — 26/27/28, caption — только подпись.
  • Контейнеры могут быть неизвестны клиенту. 0 проверяется до GetContent: явный GetContent(0) означал бы запрос предметов на земле, что здесь не требуется.

Пример 4. Прочитать слой выбранного персонажа по номеру

SUB Main()
    # Сначала выберите персонажа игровым target.
    VAR character = lasttarget
    IF character = 0 THEN
        UO.Print("Character not selected")
        RETURN
    END IF
    # 27номер слоя; characterID персонажа, не ID предмета.
    VAR item = UO.ObjAtLayerEx(27, character)
    IF item = 0 THEN
        UO.Print("Layer is empty or not loaded")
        RETURN
    END IF
    UO.Print("Selected character layer ID: " + CStr(item))
END SUB

Разбор параметров и выполнения:

  • LayerType=27; PlayerID=lasttarget. ObjAtLayerEx всегда возвращает Integer serial/0.
  • Команда не создаёт новый target. Если выбран предмет вместо mobile, слой не находится.

Проверка примеров: скрипты загружены и выполнены в тестовом окружении движка с известными объектами и без выбранной цели/слоя. Отдельно проверены границы расстояния и высоты. Это не тест торговли на игровом сервере.

Clone this wiki locally