Перейти к содержанию

Рекомендуемые сообщения

MWSE - "расширитель скриптов Мирровинда"

Описание функций MWSE:
https://mwse.github.io/MWSE/apis/tes3/

Настройки игры, Game Settings (глобальные переменные):
GMST List https://mwse.github.io/MWSE/references/gmst/

MWSE, начиная с версии 2.1, поддерживает простой и быстрый скриптовый язык Lua.

Уроки задумывались как справочник, идут в целом по усложнению, но читать можно с любого места.

Самая лучшая книга по Lua от его создателя: Р.Иерусалимски, Программирование на языке Lua

Руководство на русском языке:  https://lua.org.ru/contents_ru.html

Сокращенное руководство: https://lua.org.ru/m...little-lua-book

 

*** Урок 1: Сообщения ***  

 

 

Для запуска lua-скриптов, вам не нужно создавать мод в Construction Set. Достаточно открыть блокнот и записать, к примеру, вот такую функцию:

local function myFun(e)
    mwse.log("Запущено сохранение: %s", e.filename)
end
event.register("loaded", myFun)

Мы создали функцию local myFun(e), которая пишет имя загруженного в игре сохранения в лог-файл (по адресу <Путь к папке Morrowind>\MWSE.log).

local  - означает, что функция локальная (не может быть вызвана из других файлов),

e - информация события (типа event), которая будет передаваться в вашу функцию,

%s - команда форматирования, которая вставляет вместо "%s" строку из переменной e.filename.

event.register - регистрирует вашу функцию для события (привязывает функцию к событию)

"loaded" - имя события, данное событие вызывается при окончании загрузки сохранения в игре. Каждый раз, при возникновении события "loaded"(загрузке сохранения) будет запускаться ваша функция.

Итак, первый lua-скрипт к Морровинду написан. Сохраняем файл как:

<Путь к папке Morrowind>\Data Files\MWSE\mods\<ваш мод>\main.lua

Имя основного файла должно быть обязательно "main.lua", таковы требования МыВСЕ.
Имя подкаталога <ваш мод> может быть любым. При старте игры MWSE сканирует все подкаталоги ..\Data Files\MWSE\ и компилирует найденные скрипты. Ваш скрипт также скомпилируется, и если синтаксических ошибок нет, скрипт запустится один раз. Что означает "скрипт запустится один раз"? Это значит, что ваш файл исполнится при старте игры, лишь раз. Но так как фунция "myFun" будет зарегистрирована для события"loaded", то и Запускаться она будет каждый раз по событию. При старте игры функция "myFun" вызвана не будет.

Заходим в Морровинд, загружаем любое сохранение и выходим. Где-то в середине файле логов MWSE.log должна появится запись:

"Запущено сохранение: <имя файл сохранения>"

Если допущена ошибка, вы получите сообщение в файле логов:

<Путь к папке Morrowind>\MWSE.log

Используйте поиск по тексту с именем <вашего мода>, чтобы быстро отыскать сообщения об ошибках в вашем lua-коде.
В логах после слова Error указано описание ошибки и строка, в которой она возникла. Исправляйте ошибку и запускайте
Морровинд снова. Ну что же. Поздравляю, первый lua-скрипт для Морровинда создан!

Давайте запустим ещё какой-нибудь пример. Замените текст в вашем файле, или просто добавьте ниже:

local function showMessage(e)
    tes3.messageBox("Вы взяли " .. tes3.getMobilePlayer().readiedWeapon.object.id)
end
event.register("weaponReadied", showMessage)

Здесь при экипировке оружия будет выведено сообщение на экран. Событие "weaponReadied" вызывается, когда орудие взято в руки готово к атаке.

.. - две точки есть операция слияния строк. id является строкой

getMobilePlayer() - возвращает ссылку на mobile игрока (на вашу мобилку :), ваш игровой болванчик, а точнее тело)

Подробнее о типах объектов, актеров, предметов в следующих уроках.

 

 

 

*** Урок 2: События ***  

 

 

 

Вот все типы событий: https://mwse.github.io/MWSE/references/events/

Рассмотрим с дюжину основных и как они работают:

"loaded" - игра загружена (из сохранения)
"initialized" - игра инициализирована, .esm и .esp файлы загружены
"simulate" - начало каждого игрового кадра (не пауза и не меню)
"enterFrame" - начало любого кадра
"skillRaised" - произошло повышение скила
"attack" - ближняя физ атака  участием актера или существа, не обязательно игрока)
"activate" - активация (кто-либо нажал на активатор)
"activationTargetChanged" - в прицеле игрока объект для активации изменился
"spellCast" - кто-либо начал произносить заклинание
"magicCasted" - заклинание успешно произнесено или алхимия дала эффект
"equip" - момент экипировки предмета (начало момента)
"keyDown" - нажата клавиша на клавиатуре
"menuEnter" - вызвано меню (на пр.кн. мыши)
"weaponReadied" - экипировано оружие

На событиях держится все обращения от MWSE к вашему коду.

event.register(<событие>, <функция>)  -- регистрирует имя вашей функции в специальной таблице

Событие одно, а привязанных к нему функций может быть много. Функции даже могут иметь одни и те же имена в разных файлах.
Все они будет вызываться при каждом наступлении условий и возниконвении связанного с ними события.
Таким образом, для каждого события MWSE вызывает свои, зарегистрированные именно для него функции.
Мы можем очистить таблицу для конкретного события, написав:

event.clear("имя события") Все запомненные для события функции будут отменены.

 

 

 

*** Урок 3: Клавиши ***

 

 

Коды всех клавиш есть тут:
https://mwse.github.io/MWSE/references/scan-codes/

Для клавиатуры нам достаточно двух событий:

event.register("keyDown", checkKeys) - вызывается при нажатии клавиши
event.register("keyUp", checkKeys)   - вызывается при отпускании клавиши

Как сделать так, чтобы нажатие на кнопку клавиатуры что-нибудь совершало?

local function checkKeys(e)
-- pressed 'V'
if (e.keyCode == 47) then
tes3.messageBox("You pressed 'V'")
end
end
event.register("keyDown", checkKeys)

Функция checkKeys(e) проверяет код нажатой клавиши. Если это код клавиши 'v', то будет выведено сообщение.
 
Когда несколько клавиш задействовано, то удобнее оформить так:

local function dance()
 ...
end

local function savePoint()
 ...
end

local function createItem()
 ...
end

local function checkKeys(e)
 -- pressed 'U'
 if (e.keyCode == 22) then dance(); return end
 -- pressed 'I'
 if (e.keyCode == 23) then createItem(); return end
 -- pressed 'O'
 if (e.keyCode == 24) then savePoint(); return end
end

local function onLoaded()
   event.register("keyDown", checkKeys)
end
event.register("loaded", onLoaded)

 

 

Ну что, готовы к чему-то побольше, навроде циклов?

 

*** Урок 4: Казначей в локации ***  

 

 

 

Давайте напишем функцию, которая просканирует локацию и выведет в лог имена и координаты всех и всего найденного в ней. Зарегистрируйте функцию самостоятельно, на событие "loaded".

local function makeLog()
    
    local cell = tes3.getPlayerCell() -- получаем ячейку нахождения игрока
    local step = 1                    -- это счетчик шагов цикла
    local str = tostring(step)        -- запишем значение счётчика в строку str
    
    for ref in cell:iterateReferences() do
      if (ref.object) then
       str = str .. " type: " .. tostring(ref.object.objectType)
       str = str .. " name: " .. ref.object.name
       str = str .. " id: " .. ref.object.id
          str = str .. " pos: " .. tostring(ref.position)
       if (ref.object.objectType == tes3.objectType.npc) then
           str = str .. " " .. tostring(ref.object.race.name)
       end
       print(str)              -- печатам нашу строку в лог
          step = step + 1
          str = tostring(step) -- перезапишем всю строку, записав туда снова значение нашего счётчика
      end
    end
end

Данный цикл на каждом шаге печатает в лог-файл новую заполненную строку.

Функция print() после вывода сама переходит в начало след.строки без доп.команд.
ref - пустая новая переменная-ссылка.

iterateReferences() - метод для перебора ссылок на все объекты в ячейке. Методы вызываются через двоеточие.

Цикл for ref in cell:iterateReferences() do при каждом шаге кладёт в пустую переменную ref новую ссылку на очередной объект до тех пор, пока объекты не закончатся.

do ... end - тело цикла. В теле цикла мы проверяем if (ref.object) - содержит ли наша ссылка объект. Если объект пустой (nil то есть нуль), то условие будет ложным (false), и мы переходим далее, на новый шаг. Если ref.object отлично от нуля, условие истинно (true), и выполняется всё, что между then ... end.

tostring(ref.object.objectType) - дополняет нашу строку типом объекта. objectType является числом, поэтому мы переводим его в строку c помощью tostring()

if (ref.object.objectType == tes3.objectType.npc) then - проверяет, равен ли тип объекта npc. Если истина, то дополним нашу строку расой объекта.

objectType - по сути одно из чисел из таблицы ниже. Таблица находится по адресу tes3.objectType. Это таблица типов (неполная, на самом деле типов больше, см. 7 урок).

      Value    Code     Meaning
1230259009     ACTI     Activator
1212369985     ACTI     Alchemy
1330466113     AMMO     Ammunition
1095782465     ACTI     Apparatus
1330467393     ARMO     Armor
1263488834     BOOK     Book
1414483011     CLOT     Clothing
1414418243     CONT     Container
1095062083     CREA     Creature
1380929348     DOOR     Door
1380404809     INGR     Ingredient
1129727308     LEVC     Levelled Creature
1230390604     LEVI     Levelled Item
1212631372     LIGH     Light
1262702412     LOCK     Lockpick
1129531725     MISC     Misc Item
1598246990     NPC_     NPC               -- наш случай
1112494672     PROB     Probe
1095779666     REPA     Repair Item
1414546259     SCPT     Script
1195658835     SNDG     Sound Generator
1279610963     SPEL     Spell
1413567571     STAT     Static
1346454871     WEAP     Weapon

 

 

 

***Урок 5 Кровати-Двери-Сундуки ***

 



В предыдущем уроке мы нашли не одну сотню объектов. Давайте присмотримся к некоторой мебели. Вдруг не для каждого нпс найдется кровать или спальник? Где им спать, в сундуках? Ах да, в Морровинде время же заморожено, никто не спит, кроме нашего героя. Вот беда. Может быть, мы поправим это в последнем уроке?

local beds = 0
local doors = 0
local chests = 0

Три локальные переменные для мебели. Если убрать слово local, то переменные станут глобальными (доступными из любого модуля). Локальные переменные могут быть свободно доступны только функциям, определенным в их зоне видимости (то есть в нашем файле).

Как выделить из всех предметов, например, кровати? У них имя object.name = 'Кровать'. Но это ненадежно, вдруг имена на английском. Удобнее ориентироваться по id. id для каждого предмета уникален.

Как найти слово в строке id? Для поиска в строке существует:

 string.find(id,'[Bb]ed')
 -- [Bb] - означает один любой из символов 'B' 'b'  

Если в строке id есть слово "bed" или "Bed", функция вернёт нам 2 числа (или nil, если ничего не найдено):

    -- pos1 - на каком символе начинается наше слово
    -- pos2 - на каком символе заканчивается наше слово

    local pos1, pos2 = string.find(id,'[Bb]ed') 

Теперь давайте дополним функцию makeLog:

local function makeLog()
    
    local cell = tes3.getPlayerCell()
    local step = 1
    local str = tostring(step)
    
    for ref in cell:iterateReferences() do
      if (ref.object) then
    if (ref.object.name) then              -- не у всех предметов имеется поле name
        str = str .. " " .. ref.object.name
    end
    str = str .. " id: " .. ref.object.id   
    if (ref.object.class) then             -- не у всех предметов имеется поле class
        str = str .. " class: " .. ref.object.class.name
    end
       if (ref.object.objectType == tes3.objectType.npc) then
           str = str .. " " .. tostring(ref.object.race.name)
       end
                if (string.find(ref.object.id,'[Bb]ed')) then       -- если находим кровать
                 beds = beds + 1
                 str = str .. " BED#" .. tostring(beds) .. ""
                elseif (string.find(ref.object.id,'[Dd]oor')) then  -- иначе если находим дверь
                 doors = doors + 1
                 str = str .. " DOOR#" .. tostring(doors)
                elseif (string.find(ref.object.id,'[Cc]hest')) then -- иначе если находим сундук
                 chests = chests + 1
                 str = str .. " Chest#" .. tostring(chests)
                end
       print(str)
          step = step + 1
          str = tostring(step)
      end
    end
    tes3.messageBox("Найдено кроватей %d, дверей %d, сундуков %d", beds, doors, chests)
end

%d - на это место будет вставлено число из переменной, переданной в функцию messageBox следом за строкой (процесс называется "форматирование строки").

Запускаем, проверяем! Если появились хорошие идеи, пишите в комментариях.

 

 

Кого создать?

 

*** Урок 6: Bторой муж для Тойвалэ Отрален***

 

Из урока 4 у нас есть лог объектов в локации, из него можно узнать id - уникальные идентификаторы мировых персонажей, тварей, мебели, и прочих вещей. Давайте заглянем к Арриллу на огонёк и клонируем его 🙂 Надеюсь, его жена не упадёт в обморок. Или это не его жена стоит рядом?

--[[ Комментарий.
     tes3.createReference({...}) - это функция для создания кого-либо, чего-либо,
     на вход получает таблицу {...}
     Можно записать без круглых скобок tes3.createReference{...}
     Делает то же, что и mwscript.PlaceAtPC или xPlace.
  ]]
        local ref = tes3.createReference                   
        {                                                  -- начало таблицы
            object = "arrille00000000",                    -- первый элемент таблицыб можно элементы менять местами, порядок не важен
            position = tes3.getPlayerRef().position,    
            orientation = tes3.getPlayerRef().orientation, 
            cell = tes3.getPlayerCell(),
            scale = 1                                      
        }                                                  -- конец таблицы
        tes3.messageBox("Привет, Тойвалэ!")
end

Не вздумайте запускать эту функцию не в начальной локации. Аррилл против такого! Нужны данные из незагруженной ячейки игрового мира. Morrowind чего-то не находит в доступе и крашится. Жалко.

Ладно. Тут у нас между фигурными скобками целая таблица с полями

object,        - сюда подойдет id объекта или объект типа tes3physicalObject
position,     - позиция создаваемого объекта,         tes3vector3(x,y,z)
orientation, - отриентация создаваемого объекта, tes3vector3(x,y,z)
cell,            - id ячейки или ячейка tes3cell
scale          - масштаб, обычно у всех = 1

Как нам усовершенствовать наш код, чтобы клонировать кого угодно от бутылки до Вивека? Сделаем в следующих уроках ^^)

 

 

 

Типы tes3объектов (tes3.objectType)

 

 

-- '>>' обозначим наследование  исходниках MWSE)
GMST                                        (objectType: 'TSMG')
PathGrid                                    (objectType: 'DRGP')
Region                                      (objectType: 'NGER')
Birthsign                                   (objectType: 'NGSB')

BaseObject >> Object >> Reference           (objectType: 'RFER')
BaseObject >> Object >> Spell               (objectType: 'LEPS')
BaseObject >> Object >> Enchantment         (objectType: 'HCNE')
BaseObject >> Object >> PhisicalObject

PhisicalObject >> Activator                 (objectType: 'ITCA')     
PhisicalObject >> Actor >> Container        (objectType: 'TNOC')
PhisicalObject >> Actor >> Creature         (objectType: 'AERC')
PhisicalObject >> Actor >> NPC              (objectType: '_CPN')
PhisicalObject >> BodyPart                  (objectType: 'YDOB')
PhisicalObject >> Door                      (objectType: 'ROOD')
PhisicalObject >> Land                      (objectType: 'DNAL')
PhisicalObject >> LandTexture               (objectType: 'XETL')
PhisicalObject >> LeveledCreature           (objectType: 'CVEL')
PhisicalObject >> LeveledItem               (objectType: 'IVEL')
PhisicalObject >> Static                    (objectType: 'TATS')
PhisicalObject >> Item >> Armor             (objectType: 'OMRA')
PhisicalObject >> Item >> Alchemy           (objectType: 'HCLA')
PhisicalObject >> Item >> Apparatus         (objectType: 'APPA')
PhisicalObject >> Item >> Book              (objectType: 'KOOB')
PhisicalObject >> Item >> Clothing          (objectType: 'TOLC')
PhisicalObject >> Item >> Ingredient        (objectType: 'RGNI')
PhisicalObject >> Item >> Light             (objectType: 'HGIL')
PhisicalObject >> Item >> Lockpick          (objectType: 'KCOL')
PhisicalObject >> Item >> Lockpick >> Probe (objectType: 'BORP')
PhisicalObject >> Item >> Misc              (objectType: 'CSIM')
PhisicalObject >> Item >> Weapon            (objectType: 'PEAW')
                          Ammo              (objectType: 'OMMA')
                          Repair            (objectType: 'APER')

BaseObject >> AnimationGroup
BaseObject >> Class                         (objectType: 'SALC')
BaseObject >> Cell                          (objectType: 'LLEC')
BaseObject >> Dialogue                      (objectType: 'LAID')
BaseObject >> DialogueInfo                  (objectType: 'OFNI')
BaseObject >> Faction                       (objectType: 'TCAF')
BaseObject >> MagicEffect                   (objectType: 'FEGM')
BaseObject >> Quest                         (objectType: 'SEUQ')
BaseObject >> Global                        (objectType: 'BOLG')
BaseObject >> MagicSourceInstance             (objectType: 'LLPS')
BaseObject >> Race                          (objectType: 'ECAR')
BaseObject >> Script                        (objectType: 'TPCS')
BaseObject >> Skill                         (objectType: 'LIKS')
BaseObject >> Sound                         (objectType: 'NUOS')
BaseObject >> SoundGenerator                (objectType: 'GDNS')

MobileObject >> MobileActor >> MobileNPC                  (objectType: 'HCAM')
MobileObject >> MobileActor >> MobileNPC >> MobilePlayer  (objectType: 'PCAM')
MobileObject >> MobileActor >> MobileCreature             (objectType: 'RCAM')
MobileObject >> MobileProjectile >> MobileSpellProjectile (objectType: 'JRPM')

 

 

 

***Урок 7: Что от чего происходит в мире tes3 ***

 

 

Все что мы видим и слышим в мире tes3, MWSE разделяет на tes3объекты разных типов (см. таблицу tes3типов выше).

GMST          -- глобальные переменные игры        (objectType: 'TSMG')
PathGrid      -- сетка для поиска пути актёрами    (objectType: 'DRGP')
Region        -- задаёт погоду и звуки региона     (objectType: 'NGER')
Birthsign     -- знак рождения героя               (objectType: 'NGSB')
BaseObject    -- базовый объект для остальных объектов
MobileObject  -- мобильный объект - это подвижная модель, 'болванка', ваше tes3 (или чьё-то) бренное тельце, а также снаряд

(objectType: 'XXXX') - это 4 символа, которые также можно представить уникальным числом. Таблица с типами находится по адресу tes3.objectType. Тип необходим для выведения разновидностей объектов и проверки в программе, с каким объектом мы имеем дело. Давайте рассмотрим основные важные типы поближе.

tes3.objectType.object

BaseObject >> Object >> Reference           (objectType: 'RFER')
BaseObject >> Object >> Spell               (objectType: 'LEPS')
BaseObject >> Object >> Enchantment         (objectType: 'HCNE')
BaseObject >> Object >> PhisicalObject 

Как мы видим, объект (tes3object) происходит от базового объекта и может быть ссылкой (reference), заклинанием, зачарованием или физическим объектом. Физические объекты подразделяются на (см.таблицу): активаторы, актёров, части тела, двери, мировые поверхности, их текстуры, статики (неподвижные объекты), вещи, а также уровневые существа и уровневые вещи. Почти всё, что передставлено в Construction Set, является физическим объектом (tes3.objectType.phisicalObject).

Все объекты типа tes3object имеют три поля:

object.id          (string)    -- "айди", идентификатор
object.objectType  (number)    -- tes3-тип это просто уникальное число из таблицы
object.sourceMod   (string)    -- мод-источник объекта

Кстати объекты actor, npc не являются видимыми моделями, движущимися игре, а являются описаниями, содержащими имя и прочие свойства. "Болванчиков" представляют объекты tes3mobileObject.

tes3.objectType.mobileObject

Представляет свойства подвижной модели персонажа, существа, выпущенной стрелы. Имеет свойства для управления движением и положением в пространстве.
https://mwse.github.io/MWSE/types/tes3mobileObject/?h=mobileo
                                                                                                        

MobileObject >> MobileActor >> MobileNPC                  (objectType: 'HCAM') -- тело персонажа
MobileObject >> MobileActor >> MobileNPC >> MobilePlayer  (objectType: 'PCAM') -- тело персонажа игрока
MobileObject >> MobileActor >> MobileCreature             (objectType: 'RCAM') -- тело сщество
MobileObject >> MobileProjectile >> MobileSpellProjectile (objectType: 'JRPM') -- тело снаряда

Тело персонажа имеет:

инвентарь
атрибуты (сила, ловкость, и т.д.)
скилы
наложенные эффекты
список друзей
список врагов
управляющие флаги
AI данные


tes3.objectType.reference

Очень нужный и важный тип tes3reference, или tes3-ссылка (далее просто "ссылка").
https://mwse.github.io/MWSE/types/tes3reference/

С помощью этого объекта из функции в функцию передаётся информация об остальных tes3объектах: вещах, актёрах (tes3actor), заклинаниях, генераторах звука и т.п. Давайте взглянем на её происхождение:

BaseObject >> Object >> Reference           (objectType: 'RFER')

Как мы видим, ссылка имеет tes3тип (tes3reference) и происходит от объекта, а он в свою очередь от базового объекта. Это означает, что можно написать:

ref.id             -- то же, что и ref.object.id
ref.objectType     -- а вот тип у ссылки свой (objectType: 'RFER')
ref.sourceMod      -- то же, что и ref.object.sourceMod

Поскольку ссылка - объект, настроенный на другой объект, то в её свойствах мы айди самого объекта, мод и т.п. также. Все кроме типа, тип у ссылки собственный.

Ссылок на один и тот же объект может быть много. Обращаться через ссылку очень удобно. Например, мы можем проверить ref, на кого или что она настроена:

if (ref.objectType == tes3.objectTypeontainer) then ... end          -- выполнится, если ref это контейнер
if (ref.objectType == tes3.objectType.phisicalObject) then ... end     -- выполнится, если ref физ.объект
if (ref.objectType == tes3.objectType.light) then ... end              -- выполнится, если ref источник света

 

 

 

Тут последняя версия уроков, а также рассортированное описание TES3 классов:

https://yadi.sk/d/pNaDhws2m1-6AA

 

См.ниже в теме:

 

Урок 8: "Как создать / прочитать текстовый файл"

Урок 9: "Как создавать окна, меню, кнопки в Морровинд"

Урок 10: "Разговоры"

Изменено пользователем mintmike
Ссылка на комментарий
Поделиться на другие сайты

У кого вопросы по lua-базовым вещам читайте в руководстве вот эти разделы:

 

3.4 – Выражения, все виды операторов
3.4.9 – Конструкторы таблиц (как создавать {...} таблицы)
3.4.8 – Приоритет операторов (что быстрее выполнится + или >= и т.п)
3.5 – Правила видимости (создание/удаление локальных переменных)

Ссылка на комментарий
Поделиться на другие сайты

Спасибо огромное за дельный мануал!! Жаль нет лайков у постов. Хотелось бы еще узнать больше о создании собственных новых навыках.

Ссылка на комментарий
Поделиться на другие сайты

Спасибо! Добавил ссылку на последнюю версию уроков, а также более удобно рассортированную документацию  по TES3 классам в txt виде.

 

 

https://www.fullrest.ru/forum/topic/3105-prosby-o-perevode-plaginov-ili-perevedite-po/?p=997387

https://www.nexusmods.com/morrowind/mods/47111

Я смотрю, набирает популярность и уже включен в требования некоторых многообещающих плагинов, например, https://www.nexusmods.com/morrowind/mods/47480

 

 

Очень интересный плагин про маг эффекты. Добавлят заклинания управления погодой, призыва существ из Tamriel Rebuilt, телепортации в города за пределами Вварденфелла, просмотр состояния противника, а также функции для добавления собственных эффектов и заклинаний.

 

Использует недокументированную (ещё) функцию addMagicEffect, которая позволяет добавлять новые эффекты. Примеры использования можно посмотреть в скриптах мода.

tes3.addMagicEffect
{
name,             -- string,
id,               -- number, EffectID
description,      -- string, описние
lighting          -- vector3, цвет свечения, RGB
icon,             -- string, путь к иконке
particleTexture , -- string, путь к текстуре частиц
baseCost,         -- 1.0f по умолчанию
school,           -- 0 по умолчанию
size,             -- 1.0f по умолчанию
sizeCap,          -- 1.0f по умолчанию
speed,            -- 1.0f по умолчанию
castSound,        -- string, звуки
boltSound
hitSound,
areaSound,
castVFX,          -- сами эффекты, PhysicalObject
boltVFX,
hitVFX,
areaVFX,
allowEnchanting,  -- дальше идут флаги (true/false)
allowSpellmaking,
appliesOnce,
canCastSelf,
canCastTarget,
canCastTouch,
casterLinked,
hasContinuousVFX,
hasNoDuration,
hasNoMagnitude,
illegalDaedra,
isHarmful,
nonRecastable,
targetsAttributes,
targetsSkills,
unreflectable,
usesNegativeLighting
onTick,
onCollision
}

Можно надеяться, что появится и функция типа addNewSkill ...

Изменено пользователем mintmike
Ссылка на комментарий
Поделиться на другие сайты

  • 2 месяца спустя...

Урок 8: "Как создать / прочитать текстовый файл"

--=== Функции Lua для работы с файлами ===--
io.input()    -- задаём файл для чтения
io.output()   -- задаём файл для записи
io.read()     -- читаем строку из файла
io.write()    -- пишем строку в файл

Для примера посчитаем количество строк и слов в интересующем нас файле, пусть он называется inp.txt

 

Запускать программу будем на компиляторе lua53.exe (последняя версия).

 

1. Скачаем, если у вас его нет. Скачать можно тут: http://luabinaries.s...t/download.html

 

2. Создаём в одном каталоге с lua53.exe новый текстовый файл с текстом программы, например "prog.lua":

io.output('out.txt') -- задаём файл с результатом
io.input('inp.txt')  -- откуда будем читать

local str = io.read() -- читаем первую строку из файла
local snum = 0 -- количество строк
local wnum = 0 -- количество слов

while str do        -- цикл исполняется пока str не равно nil
  snum = snum + 1   -- увеличиваем счётчик строк на 1
  for s in str:gmatch("[%w'-]+") do -- для каждой найденной в строке цепочки цифро-букв, "'" и "-"
    if s:find("%a") then            -- если цепочка имеет хотя бы одну букву
      wnum = wnum + 1               -- увеличиваем счётчик слов на 1
    end
  end
  str = io.read()   -- читаем следующую строку
end
io.write('Всего строк в файле ',snum,'\n') -- выводим результат в файл
io.write('Bсего слов в файле ',wnum,'\n')  -- \n -- служебный символ перехода на новую строку
print('Всего строк в файле',snum,' всего слов в файле',wnum)

Замечание: все файлы должны лежать в одной папке.

 

3. Перетаскиваем мышкой нашу программу "prog.lua" на lua53.exe и отпускаем, рядом появляется наш файл с результатом "res.txt".

 

4. Для повторных запусков можно создать prog.bat файл, и запускать нашу програму нажатием на него

lua53 prog.lua
cmd
Изменено пользователем mintmike
Ссылка на комментарий
Поделиться на другие сайты

Спасибо огромное за ваш труд! Ждем еще новых уроков!

 

Интересует, как привязать константу к новому навыку. Когда я увеличиваю простым скриптом константу - растет и новый навык в меню

Ссылка на комментарий
Поделиться на другие сайты

Спасибо огромное за ваш труд! Ждем еще новых уроков!

 

Интересует, как привязать константу к новому навыку. Когда я увеличиваю простым скриптом константу - растет и новый навык в меню <- это интересно.

Насколько я понял, нужно привязать значение скила к одной из игровых констант

В модуле \MWSE\lib\OtherSkills\common.lua, функция updateSkillList  выводит список скилов.

Нужно, чтобы значение скила обновлялось в соответствии с константой:

this.updateSkillList = function ()
...
for i,skill in pairs(this.otherSkills) do
 if skill.active == "active" then
  if skill.name == "<имя скила>" then skill.value = tes3.gmst.<имя константы>  -- << вставить вот эту строку
...

Если нужна помощь, пиши лс

Ссылка на комментарий
Поделиться на другие сайты

Про что сделать следующий сложный урок? (не более 2 вариантов в ответе, или свои варианты)

 

1. Как правильно подключить и использовать mcm (Настройка Модов в Главном меню)

2. Как создавать окошки и кнопочки. Внедрим кнопку в окно с диалогом с нпс. По нажатию он будет нам отвечать про погоду и силу ветра.

3. Как заставить нпс`а: упасть на кровать и лежать на ней ничком.

4. Появляется и исчезает бог (Вивек, например), все сбегаются к месту силы.

5. Как работает самый частый пакет ии: AIWander (шататься окрест)

6. Как работает  AIActivate. Как заставить нпс`а выйти на улицу, а Фаргота бегать по кругу.

7. Выделить стат-ку из редактора и посмотреть: сколько и каджитов и орков на Ввардэнфелле и чем они занимаются, какой ср.уровень, скорость, магия.

 

... итак, ваш выбор?

 

Но сначала простой урок номер 9: "Как создавать окна, меню, кнопки в Морровинд"

 

 

 

Самый короткий вариант создать окошко. Назовём его Винни. Короткий плагин из десятка строчек:

local winny

local function showWinny()
  winny = tes3ui.createMenu -- создается новое меню (просто окно)
  {
  id = tes3ui.registerID('winny'), -- нашему окну выдаётся id, по которому можно затем к нему обращаться
  fixedFrame = true, -- с жёстко фиксированной рамкой
  }
  winny:updateLayout() -- обновить элементы на экране
end
event.register("keyDown", showWinny, {filter = 44}) -- при нажатии на "z" будет появляться Винни

Запускаем Главное меню Морровинд. Жмакаем на "z".

 

Появляясь, наше окно не хочет исчезать. Давайте добавим текст в наше окно, а также кнопку "назад":

local winny

local function hideWinny()
  winny:destroy() -- функция для удаления Винни
end

local function showWinny()
  winny = tes3ui.createMenu
  {
   id = tes3ui.registerID('winny'),
   fixedFrame = true,
  }
  winny:createLabel{ text = 'winny'} -- добавляем текст в окно
  local button = winny:createButton -- добавляем кнопку
  {
   id = tes3ui.registerID('Cancel'),
   text = 'Назад',
  }
  button:register("mouseClick", hideWinny) -- при щелчке по кнопке будет вызываться hideWinny()
  winny:updateLayout()
end
event.register("keyDown", showWinny, {filter = 44}) -- z

Теперь, если нажать "z" в игре, окно появляется и маячит посреди экрана. Как перейти в режим меню?

 

tes3ui.enterMenuMode(winny) -- попадаем в режим, когда игра на паузе и появляется указатель мыши для управления в меню

 

Добавим ещё несколько полезных строк:

local winny
local winny_id -- сохраним id окна отдельно

local function hideWinny()
  winny:destroy()
  if tes3ui.menuMode then tes3ui.leaveMenuMode() end -- выходим из режима меню
end

local function showWinny()
  if tes3ui.findMenu(winny_id) then return end -- если окно уже есть, не создаём его повторно
  winny_id = tes3ui.registerID('winny')
  winny = tes3ui.createMenu
  {
   id = winny_id,
   fixedFrame = true,
  }
  winny:createLabel { text = 'winny'}
  local button = winny:createButton
  {
   id = tes3ui.registerID('Cancel'),
   text = 'Назад',
  }
  button:register("mouseClick", hideWinny)
  winny:updateLayout()
  tes3ui.enterMenuMode(winny) -- режим меню
end
event.register("keyDown", showWinny, {filter = 44}) -- z

Готово. Также, в окно можо добавлять блоки (свободное пространство для элементов), а в блоки уже добавлять тексты, изображения и кнопки.

  local block = winny:createBlock{}
  block.width = 1000       -- ширина
  block.autoHeight = true  -- высота выбирается автоматически
  block.childAlignX = 0.5  -- горизонт. выравнивание элементов по центру
  block.childAlignY = 0.5  -- верт. выравнивание элементов по центру
  block.flowDirection = 'top_to_bottom' -- разместить элементы сверху вниз

  local l = block:createLabel{ ... }   -- добавить запись
  local b = block:createButton{ ... }  -- добавить кнопку
  local img = block:createImage{ ... } -- добавить изображение
  local nif = block:createNif{ ... }   -- добавить модель

Теперь вы можете создавать меню в Морровинд под свои нужды.

Полные возможности см. в описании TES3 классов (последняя ссылка в шапке темы)

 

 

Изменено пользователем mintmike
Ссылка на комментарий
Поделиться на другие сайты

Урок 10: "Разговоры"

 

Сегодня мы:

1. Создадим модуль, который будет штамповать нам окна

2. Используя этот модуль, сотворим окно для разговора с НИП'ом (Не Игровым Персонажем)

3. Внедрим кнопку в MenuDalogue (в основное окно для диалогов в Морровинде)

4. Добавим функции для ответа: "кто вы?", "который час?", "как погода?"

 

 

 

 

Для понимания этого материала нужно основательно усвоить предыдущий урок.

 

Пусть у нас есть каталог с модом MyMod. А в нём основной модуль main.lua

Создадим вспомогательный модуль (файл) win.lua. Он нужен нам для того, чтобы потом затем пользоваться им, не повторяя один и тот же код  over9000+ раз.

 

win.lua будет иметь всего одну функцию new(name) - создавать новое окно:

local this = {}

this.new = function(name)      -- функция, создающая новое окно и возвращающая нам его данные

local m = {                    -- данные нашего нового окна
 id = tes3ui.registerID(name),
 ltext = ' Enter: ',
 block = {[1] = ''},
}
m.menu = tes3ui.createMenu{id = m.id, fixedFrame = true}
m.menu.alpha = 1.0             -- нулевая прозрачность

m.show = function()            -- отобразить окно
  m.menu:updateLayout()
  tes3ui.enterMenuMode(m.menu)
end

m.hide = function()            -- спрятать окно
  if not tes3ui.findMenu(m.id) then return end
  m.menu:destroy()
  if tes3ui.menuMode then tes3ui.leaveMenuMode() end
end

m.addBlock = function(n, width, ltext)  -- добавить блок с номером n, шириной [и необязательным текстом надписи]
  m.block[n] = m.menu:createBlock{}
  m.block[n].width = width or 360
  m.block[n].autoHeight = true
  m.block[n].childAlignX = 0.5
  m.block[n].childAlignY = 0.5
  m.block[n].flowDirection = 'top_to_bottom'
  
  if ltext then                         -- если указан 3 параметр - текст надписи, то добавляем её
    m.block[n]:createLabel {text = ltext}
  end
end

m.addBlockButton = function(n, buttext, func)  -- добавить в блок номер n кнопку с текстом buttext
  local button = m.block[n]:createButton
  {
    id = tes3ui.registerID(buttext),
    text = buttext,
  }
  button:register("mouseClick", func)  -- при нажатии на кнопку будет вызываться функция func
end

  return m  -- возвращаем созданную таблицу с данными и функциями для нашего нового окна
end

return this

Добавим этот модуль в наш основной модуль main.lua

local win = require("MyMod.win")

Теперь мы можем:

 

w = win.new(...),              -- создавать новые окна

w.addBlock (...),               -- добавлять в них болки

w.addBlockButton (...)      -- конопки

w.show (...)                       -- показывать

w.hide (...)                        -- и прятать их

 

Что мы и сделаем:

local win = require("Clever Actors Dialogues.win")
local winny

local function winnyShow()
  local width = 360

  winny = win.new('winny')                -- создадим новое окно
  winny.addBlock(1, width, 'Сказать...')  -- добавим блок номер 1
  winny.addBlock(2, width, '----------')  -- добавим блок номер 2
  winny.addBlockButton(2, 'назад', winny.hide) -- добавим в блок номер 2 кнопку назад
  winny.show()                            -- а теперь выведем окно
end
event.register("keyDown", winnyShow, {filter = 44}) -- z

При нажатии на "z" окно покажется, при нажатии "назад"  - спрячется.

 

Теперь создадим функцию, которая внедрит в меню диалога (MenuDialog) новую кнопку "Диалог с  <имя персонажа>"

 

Есть такое событие "menuEnter". Оно возникает, когда на экран выводится любое окно (меню). В данных этого события отправляется id этого нового окна (просто число).

 

Подробнее о всех события тут:

https://mwse.readthedocs.io/en/latest/lua/event.html

 

Это нам и нужно. Зарегистрируем на событие "menuEnter" новую функцию в main.lua:

local md_id                     -- id MenuDialog
local md_db                     -- это будет наша кнопка "Диалог с npc"
local md_db_id                  -- id нашей кнопки

local function dlgButton(e)  -- е - это данные события "menuEnter"

  md_id = tes3ui.registerID("MenuDialog")
  if e.menu.id ~= md_id then return end -- выходим, если id окна не совпадает с MenuDialog (открыто что-то другое)
  md = tes3ui.findMenu(md_id)           -- иначе, продолжаем. Сохраняем меню MenuDialog как md

  md_db_id = tes3ui.registerID("md_db")
  md_db = md:createButton {             -- добавляем нашу кнопку в MenuDialog (появится внизу окна)
    id = md_db_id,
    text = 'Диалог c '..tes3ui.getServiceActor().object.name,
  }
  md_db.widthProportional = 1.0
  md_db:register("mouseClick", winnyShow) -- при нажатии на кнопку будет вызываться наше окно Winny
end

event.register("menuEnter", dlgButton) -- регистрируем функцию на событие "menuEnter"

Можно пробовать запускать. Теперь не хватает вопросов и ответов от нпс.

 

При использовании tes3.messageBox(...) и открытом MenuDialog текст выводится прямо в него, а не появляется отдельно.

Этим и воспользуемся.

НИП будет нам отвечать:

local function selfDialog()  -- говорит своё имя, расу и класс
  local mbl = tes3ui.getServiceActor()
  local str = 'Это я, '..mbl.object.name..', '.. mbl.object.race.name ..', '..mbl.object.class.id
  tes3.messageBox(str)
end

local function daytimeDialog()  -- говорит сколько часов до конца дня
  local wc = tes3.getCurrentWeather().controller
  local str = tostring(wc.daysRemaining)
  str = str ..' день , час ' .. tostring(wc.hoursRemaining)
  tes3.messageBox(str)
end

local function weatherDialog()  -- проверяет состояние погоды, озвучивает
  local w = tes3.getCurrentWeather()
  local str = 'Нынче'
  if w.index == 1 then str = str..' ясно'
  else str = str..'... что там, не знаю'
  end
  tes3.messageBox(str)
end

Теперь нужно вызывать эти функции из нашего winny.

Дополним получившийся main.lua

local function winnyShow()
  local width = 360
  local chlist = -- лист ответов ("Choiсe list")
  {
   ['Кто вы?'] = selfDialog,
   ['Который час?'] = daytimeDialog,
   ['О погоде'] = weatherDialog,
  }

  winny = win.new('winny')

  winny.addBlock(1, width, 'Сказать...')
  for chtext,fun in pairs(chlist) do   -- для каждой записи из chlist
      winny.addBlockButton(1, chtext, function()  -- добавить функцию
        winny.hide()                   -- прячущую окно winny
        fun()                          -- и вызывающую соответствующую функцию
      end)
    end
  winny.addBlock(2, width, '----------')
  winny.addBlockButton(2, 'назад', winny.hide)
  winny.show()
end

local md_id                     -- Menu Dialogue id
local md_db                     -- Menu Dialogue _ Dialogue Button
local md_db_id

local win = require("Clever Actors Dialogues.win")
local winny

local function dlgButton(e)
  --collectgarbage('collect')
  md_id = tes3ui.registerID("MenuDialog")
  if e.menu.id ~= md_id then return end
  md = tes3ui.findMenu(md_id)

  md_db_id = tes3ui.registerID("cleveractors:md_db")
  md_db = md:createButton {
    id = md_db_id,
    text = 'Диалог c '..tes3ui.getServiceActor().object.name,
  }
  md_db.widthProportional = 1.0
  md_db:register("mouseClick", winnyShow)
end

local function onLoad()
  event.register("menuEnter", dlgButton)
end
event.register("loaded", onLoad)

После добавления нашей кнопки в меню диалога, его нужно обновить (updateLayout). Иначе наша кнопка будет появляться не сразу, а только после первого ответа нип`а на любую из тем, или любого др. обновляющего окно события.

 

Если есть идеи, как улучшить ответы нпс, предлагайте. Или, ещё лучше, дополняйте плагин самостоятельно. Публикую opensource :)

 

 

 

 

Последняя версия планина "Morrowind-Dialogues"  https://gitlab.com/Scoommich/morrowind-dialogues

Изменено пользователем mintmike
Ссылка на комментарий
Поделиться на другие сайты

  • 2 месяца спустя...

MWSE Lua

продолжает обзаводиться новыми объектами, событиями и возможностями ;-D

 

Что нового?

Новые функции появились (~ за первое полугодие 2020) :

 

tes3.createObject - создает объект и возврящает его. Созданный объект будет частью сохраненной игры. Поддерживаемые типы объектов - это те, которые имеют свою собственную функцию создания, такую как, например, tes3activator.

 

tes3.findClosestExteriorReferenceOfObject - Используя ту же функцию двигателя, что и заклинания «Ванильное вмешательство», размещает первую ссылку на данный объект во внешнем мире.

tes3.getLastExteriorPosition - Возврящает последнюю запомненную позицию игрока в экстерьере (снаружи)

tes3.togglePOV - Вызывает переключение POV игрока в следующем кадре симуляции и возвращает, если игрок ранее был от третьего лица. Несколько вызовов в одном кадре не будут складываться.

tes3.force1stPerson - Принудительно вводит POV (Область зрения) игрока от первого лица в следующем кадре симуляции и возвращает true, если POV было изменено.

 

 

= Новые события =

 

barterOffer Это событие вызывается, когда игрок делает потенциально успешное бартерное предложение. Потенциально успешный означает, что обе стороны имеют необходимые средства для совершения сделки.

calcSunDamageScala  используется при расчете количества урона, применяемого к магическому эффекту солнечного урона.

containerClosed  Это событие возникает, когда контейнер закрыт.

crimeWitnessed  Это событие происходит, когда актером засвидетельствовано преступление.

detectSneak  Это событие вызывается всякий раз, когда актер проверяет, могут ли они обнаружить, что другой актер подкрался.

filterBarterMenu  Это событие возникает, когда элемент в бартерном меню фильтруется.

determineAction  Это событие возникает, когда актер определяет действие в боевом сеансе.

determinedAction  Это событие возникает, когда актер определил действие в боевом сеансе.

filterInventory  Это событие возникает, когда предмет в инвентаре фильтруется.

filterInventorySelect  Это событие возникает, когда для предмета выбран фильтр инвентаря.

filterBarterMenu  Это событие возникает, когда элемент в бартерном меню фильтруется.

filterSoulGemTarget  Это событие происходит, когда цель самоцвета души фильтруется.

infoGetText  Это событие возникает, когда извлекается текст объекта диалога.

infoResponse  Это событие срабатывает при срабатывании ответа диалога.

isGuard  Событие isGuard срабатывает всякий раз, когда игровой движок проверяет, является ли объект NPC охранником. Статус охраны может быть изменен. Это само по себе не заставит NPC вести себя как охранник.

itemDropped   Что-то уронили

itemTileUpdated  Это событие срабатывает при срабатывании ответа диалога.


lockPick  Это событие срабатывает при взломе замка.

objectInvalidated  Это событие возникает, когда ссылка недействительна. Это включает в себя удаление из памяти. Это событие может быть использовано для безопасного удаления ссылок из таблиц.

postInfoResponse  Это событие возникает сразу после обработки ответа диалога.

preLevelUp  Это событие запускается как раз перед тем, как уровень игрока поднимется.

projectileHitObject  Событие projectileHitObject возникает, когда снаряд сталкивается с объектом.

trapDisarm  Это событие срабатывает, когда ловушка обезвреживается.

 

 

= Объекты =

 

Нижереченные сущности пребывали в коде и ранее, но теперь описаны в документации и к ним имеется доступ:

 

tes3birthsign
Объект, представляющий знак рождения.

tes3region
Структура, которая содержит информацию о регионе.

tes3regionSound
Структура, которая содержит информацию о звуке региона.

tes3static
Статический игровой объект.

tes3statistic
Структура, которая содержит статистическую информацию, относящуюся к текущим и базовым значениям статистики, таким как здоровье, усталость, магия, атрибуты или навыки.

tes3weather
Структура, которая содержит базовую информацию о погоде, которая используется для наследования структур погоды.

 

 

= Доступ к NIF =   

 

Расширился доступ к NIF мешам и узам сцены. Типы можно найти в таблице tes3.niType.

 

niObject - Самый базовый NIF объект.
Самый базовый объект, из которого получены почти все структуры NetImmerse.

niAmbientLight
Объект, который представляет окружающий свет. Этот объект довольно прост и не имеет местоположения, направления или затухания.

niCamera
Объект, представляющий камеру. Положение и ориентация определяются родительскими свойствами.

niDirectionalLight
Объект, который представляет свет со светом, движущимся в заданном направлении. Свет может быть направлен в любом направлении, вращая свет.

niCollisionSwitch
Объект, который указывает, включена ли система столкновений.

niDynamicEffect
Базовый класс для динамических эффектов, таких как NiLights или эффекты проецируемых текстур.

niFormatPrefs
Класс, представляющий набор предпочтений для форматов текстур. Эти предпочтения определяют уровни точности пикселей.


niGeometryData
Объекты NiGeometryData содержат данные геометрии, необходимые для визуализации объекта NiGeometry. Когда создается объект на основе NiGeometry, фактические данные геометрии сохраняются в прикрепленном объекте NiGeometryData.

niLight
Базовый класс, который представляет источники света в графе сцены. Это включает в себя окружающий, рассеянный и зеркальный цвета света, а также его интенсивность.

NiNode
Базовый класс, представляющий узлы графа сцены. Узел может иметь любое количество дочерних узлов.

niObjectNET
Объект, который имеет имя, дополнительные данные и контроллеры.

niPick
Класс, используемый в системе пересечения комплектации в игровом движке. Используя луч, движок выполняет операции выбора на графе сцены или любом поддереве. При заданном луче и поддереве поддерево пересекается, и совпадающие узлы добавляются в массив.

niPickRecord
Вложенный класс NiPick, в котором хранятся результаты предыдущих операций выбора для использования игровым движком.

niPixelData

Представляет 2D-массивы значений пикселей, а также пирамиды с несколькими уровнями mipmap, каждый из 2D-массивов значений пикселей. Также содержит информацию, представляющую формат пикселей и размеры массивов.

Изменено пользователем mintmike
Ссылка на комментарий
Поделиться на другие сайты

  • 2 недели спустя...

Просьба подробнее рассказать в рамках уроков, как работать с таблицами :)

 

Например, есть вот такой код:

local function transferItems(e)
    local cell = tes3.getPlayerCell()
    local sack = tes3.createReference{object="sack_id", position=tes3.player.position, orientation=tes3.player.orientation, cell=cell}
    for _, stack in pairs(tes3.player.object.inventory) do
        tes3.transferItem{from=tes3.player, to=sack, item=stack.object, count=stack.count, playSound=true}
    end
end

local function initialized(e)
    event.register("keyUp", transferItems, { filter = tes3.scanCode.backspace } )
end

event.register("initialized", initialized)

Идея в том, что такой код переносит все объекты из инвентаря персонажа в контейнер. А хотелось бы, что переносил все, кроме экипированных.

 

Один умный человек посоветовал создать таблицу, в которую прописать экипированные предметы.

Вопрос - а какие элементы таблицы нужно использовать в этом случае? И какие можно использовать в принципе?

 

В уроке номер 6 мы создали таблицу для Аррилла. Но почему именно эти значения мы туда прописали?

 

 

В общем, прошу подробнее остановиться на таблицах :)

 

p.s. Пункт 3.4.9. руководства я читал, но понятнее не стало :scratch:

Изменено пользователем -=ChieF=-
Ссылка на комментарий
Поделиться на другие сайты

Вопрос - а какие элементы таблицы нужно использовать в этом случае? И какие можно использовать в принципе?

Айди и количество объектов. Будет что-то вроде

for _, stack2 in pairs (tes3.player.object.equipment) do 
EquipmentList [_] =
{
ObjId = stack2.object.id
Amount = stack2.count 
}
end

Таблица -- это, грубо говоря, таблица в экселе. Есть строки (0, 1, 2 и т. д.), есть столбцы. Вместо столбцов -- все свойства объекта. То есть айди, имя, вес, etc.

 

Изменено пользователем Муурн Шепард
Ссылка на комментарий
Поделиться на другие сайты

В уроке номер 6 мы создали таблицу для Аррилла. Но почему именно эти значения мы туда прописали?

 

Потому как функция tes3.createReference {...}   требует передать ей именно эти значения

 

createReference

{

object    -- указать кого создавать (tes3physicalObject|string)

position -- указать в каком месте создавать (tes3vector3|table)

orientation -- оринетация по сторонам света (tes3vector3|table)

cell -- где создавать, то есть в какой ячейке мира (tes3cell|string|table)

scale: -- масштаб созданного объекта, не обязательно указывать, по умолчанию 1 (number)

}

 

Также как tes3.transferItem -- просит передать ей откуда, куда, какой item и в каком количестве перемещать.

 

В шапке темы есть ссылка скачать рассортированное описание tes3 функций (файл tes3.txt)

 

Вот в доках пример

 

Идея в том, что такой код переносит все объекты из инвентаря персонажа в контейнер. А хотелось бы, что переносил все, кроме экипированных.

Идея отличная, осталось сделать фильтр по надетым на ГГ вещам. Ну ещё можно прикрутить меню и будет готовый плагин.

 

 

Фильтр можно сделать так:

   local equip = {} -- создаём новую пустую таблицу
   for i, node in pairs(tes3.player.object.equipment) do
     -- запоминаем id и ссылки на надетые на ГГ вещи
     equip[node.object.id] = node
   end
   
   for i, stack in pairs(tes3.player.object.inventory) do
     -- проверяем по id, если вещь не запомнена в equip, то сбрасываем её в sack
     if not equip[stack.object.id] then
       tes3.transferItem
       {
        from = tes3.player,
        to = sack,
        item = stack.object,
        count = stack.count,
        playSound = true
       }
     end
   end
   tes3.messageBox("Вещи сброшены")
Изменено пользователем mintmike
Ссылка на комментарий
Поделиться на другие сайты

  • 1 месяц спустя...

Здравствуйте, если еще есть кто живой в этой теме, то может быть он мне поможет разобраться.

 

Я перебираю объекты в инвентаре, выбираю там броню и пытаюсь узнать класс брони(легкая, средняя, тяжелая), но у меня в результате выдает nil. Может кто может мне объяснить что я делаю не так.

for i, stack in pairs(tes3.player.object.inventory) do
  if (stack.object) then
    if (stack.object.objectType == tes3.objectType.armor) then
      tes3.messageBox{ message = "armorClass=" .. tostring(stack.object.armorWeightClass)}
    end
  end
end

 

Изменено пользователем borivit
Ссылка на комментарий
Поделиться на другие сайты

В 9/18/2023 в 8:35 PM, mintmike сказал:

Не находит armorWeightClass, должно быть просто weightClass 

Завтра буду дома, попробую. Но у меня такая же история и с weaponType хотя здесь проще т.к. почти все id имеют указание на тип оружия и можно просто искать нужное через id.

P.S.: А можно ли как-то вытянуть все значения и ключи относящиеся к объекту? Чтобы можно было увидеть какие есть у объекта свойства и соответственно тогда будет понятно как их узнать.

P.S.S.: Только что до читался что через for  можно посмотреть все ключи и значения. Завтра попробую. О результатах напишу.

 

Всех приветствую.

С weightClass заработало:) а для оружия нужно писать просто type.

Это не совсем по теме, но может вы знаете как отключить показ названия и характеристик оружия. Я доступ отключил, а название и характеристики не получается отключить.

Цитата

P.S.S.: Только что до читался что через for можно посмотреть все ключи и значения. Завтра попробую. О результатах напишу.

Ничего с этой затеей не вышло т.к. pairs нельзя запускать внутри pairs.

У меня есть еще одна маленькая проблемка. Хочу сделать создание объекта при активации другого объекта, а потом при активации того же объекта удалить созданный объект, но lua почему-то ругается на createReference хотя 1н раз объект создается и 1н раз он удаляется, а потом уже ничего не создается.

local function basurero(e)
  if (e.target.object.id == "A_RZZ_active_bell_01") then
    if (saco == nil) then
      local cell = tes3.getPlayerCell()
      local vector_pos = tes3vector3.new(-346, 250, 25)
      local vector_orient = tes3vector3.new(0, 0, 25)
      basura = tes3.createReference{object="A_RZZ_basura", position=vector_pos, orientation=vector_orient, cell=cell}
      saco = true
    else
      saco = nil
      tes3.deleteObject(basura)
    end
  end
end
event.register("activate", basurero)

Ошибку выдает такую: invalid 'object' parameter privided 

 

В 9/19/2023 в 7:47 PM, borivit сказал:

Ошибку выдает такую: invalid 'object' parameter privided 

Я так понимаю что удаляется полностью объект и потому выдает эту ошибку. В tes3 конструкторе для точно такой же операции(placeAtPC) я использовал disable и setdelete, а здесь я нашел только disable, а setdelete нет или может я не увидел.

P.S.: я нашел в tes3reference  - myObject:delete() там пишут что ссылка помечается как удаленная и потом полностью удаляется - это похоже на setdelete в tes3. Завтра буду пробовать.

 

В 9/19/2023 в 9:51 PM, borivit сказал:

P.S.: я нашел в tes3reference  - myObject:delete() там пишут что ссылка помечается как удаленная и потом полностью удаляется - это похоже на setdelete в tes3. Завтра буду пробовать.

Это сработало.  Вместо tes3.deleteObject(basura) я вставил basura:delete() и теперь все работает

Как через lua запретить показ содержимого контейнера, а потом показать это содержимое после нажатия кнопки в меню?

Изменено пользователем borivit
Ссылка на комментарий
Поделиться на другие сайты

4 часа назад, borivit сказал:

Как через lua запретить показ содержимого контейнера, а потом показать это содержимое после нажатия кнопки в меню?

Например, перемещать или сделать disable предметы, а по нажатии кнопки снова переместить или enable. Кстати, при удалении вещей из контейнера/инвентаря, лучше делать это в следующем кадре (меньше шансов вылета на рабочий стол):

            timer.delayOneFrame(function()
                local pgn = tes3.getItemCount{reference = tes3.player, item = 'pouch_gold'}
                if pgn > 0 then
                    tes3.removeItem{reference = tes3.player, item = 'pouch_gold', count = 1}
                end
            end)

 

В 19.09.2023 в 9:47 PM, borivit сказал:

Ничего с этой затеей не вышло т.к. pairs нельзя запускать внутри pairs.

Сделайте серез функцию, которая вызывается внутри pairs  и в ней будет свой pairs. Вообще говоря pairs внутри pairs можно

Цитата

Это не совсем по теме, но может вы знаете как отключить показ названия и характеристик оружия

Возможно есть в моде Descriptive Descriptions, там дополняются описания предметов

Изменено пользователем mintmike
Ссылка на комментарий
Поделиться на другие сайты

Как удобно распечатать таблицу, чтобы посмотреть все содержимое, все ключи и значения? Есть удобная функция deepToString(value, maxDepth) для распечатки в лог содержимого таблицы (взято из OpenMW util.lua)

value - сама таблица

maxDepth - максимальная глубина вложенных таблиц, до которой будет распаковываться и печататься в строку содержимое value

-- Implementation can be found in `resources/vfs/openmw_aux/util.lua`.

local aux_util = {}

local function deepToString(val, level, prefix)
    local level = (level or 1) - 1
    local ok, iter, t = pcall(function() return pairs(val) end)
    if level < 0 or not ok then
        return tostring(val)
    end
    local newPrefix = prefix .. '  '
    local strs = {tostring(val) .. ' {\n'}
    for k, v in iter, t do
        strs[#strs + 1] = newPrefix .. tostring(k) .. ' = ' .. deepToString(v, level, newPrefix) .. ',\n'
    end
    strs[#strs + 1] = prefix .. '}'
    return table.concat(strs)
end

-- Works like `tostring` but shows also content of tables.
-- @function [parent=#util] deepToString
-- @param #any value The value to convert to string
-- @param #number maxDepth Max depth of tables unpacking (optional, 1 by default)
                
function aux_util.deepToString(value, maxDepth)
    return deepToString(value, maxDepth, '')
end
                
--...
                
return aux_util

 

Изменено пользователем mintmike
Ссылка на комментарий
Поделиться на другие сайты

Цитата

Например, перемещать или сделать disable предметы, а по нажатии кнопки снова переместить или enable. Кстати, при удалении вещей из контейнера/инвентаря, лучше делать это в следующем кадре (меньше шансов вылета на рабочий стол):

Это не совсем то что я имел ввиду. Видимо я не правильно изложил вопрос. Мне нужно при активации контейнера получить вместо окна с содержимым контейнера, мое окно с кнопками.

Я здесь нашел такую функцию:

local tomb = tes3.getReference("A_RZZ_chest_tomb") --контейнер
tes3.player:activate(tomb) --открывает содержиме контейнера

это работает как activate в tes3 конструкторе, но у меня не получается запретить показ или закрыть меню с содержимым при стандартной активации т.е. как отследить меню контейнера при его активации и сразу же его закрыть? или может можно его запретить по событию? Кстати -> myObject:clearActionFlag(flagIndex)  - это не помогло.

И я никак не могу понять как правильно заполнять поле buttons и callbackParams

tes3ui.showMessageMenu({ id = ..., buttons = ..., callbackParams = ..., cancels = ..., cancelText = ..., cancelCallback = ..., header = ..., message = ..., customBlock = ..., page = ..., pageSize = ... })
 

Цитата

Как удобно распечатать таблицу, чтобы посмотреть все содержимое, все ключи и значения? Есть удобная функция deepToString(value, maxDepth) для распечатки в лог содержимого таблицы (взято из OpenMW util.lua)

Благодарю, еще не попробовал, но обязательно испытаю:)

Как работает menu:destroy? Я почему-то не могу найти описания. Я заметил что когда запущено мое меню и меню контейнера, то если я закрываю свое меню, закрывается и меню контейнера.

P.S.: я нашел описание myObject:destroy(), но мне это не очень помогло, я так понимаю что вместо myObject нужна переменная с объектом окна, но как найти id окна контейнера, а потом получить его объект я пока незнаю или может я не в ту сторону копаю.

Изменено пользователем borivit
Ссылка на комментарий
Поделиться на другие сайты

Рассказываю что у меня получилось:)

1. Заменить меню контейнера на свое при его активации 

local function eActivate(e)
    if (e.target.object.id == "A_RZZ_chest_tomb") then
		if (not RZZ_activate) then
			timer.delayOneFrame(function()
				if tes3ui.menuMode then tes3ui.leaveMenuMode() end --закрываем контейнер
				myMenuShow() --Открываем меню
			end)
			return false
		end
		RZZ_activate = nil --метка активации контейнера
	end
end
event.register("activate", eActivate)

2. Открыть контейнер при нажатии кнопки своего меню

local function abrirRZZ()
	local tomb = tes3.getReference("A_RZZ_chest_tomb")
	cerrarMenu() --закрываем свое меню
	timer.delayOneFrame(function()
		RZZ_activate = true --метка активации контейнера
		tes3.player:activate(tomb) --открываем контейнер
	end)
end

3. Убрать helpMenu т.е. окошко всплывающее при наведении прицела на предмет

local function onUIObjectTooltip(e)
	if string.find(e.reference.id,'muestra') then --выборка нужных предметов
		e.tooltip.absolutePosAlignX = 4
		e.tooltip.absolutePosAlignY = 4
	else
		e.tooltip.absolutePosAlignX = nil
		e.tooltip.absolutePosAlignY = nil
	end
end
event.register("uiObjectTooltip", onUIObjectTooltip)

Теперь оталось выяснить как отследить запуск заклинания, как запустить проклятие, как остановить выполнение заклинания, как телепортироваться в нужное место и тогда я свой плагин полностью переведу на lua:)

Изменено пользователем borivit
Ссылка на комментарий
Поделиться на другие сайты

2 часа назад, borivit сказал:

Теперь оталось выяснить как отследить запуск заклинания, как запустить проклятие, как остановить выполнение заклинания, как телепортироваться в нужное место и тогда я свой плагин полностью переведу на lua:)

Здорово )

spellCast

spellCasted

Список маг эффектов

Пример телепорта:

    local result = tes3.positionCell {
      reference = a.id,
      cell = c,
      position = a.lpoint,
    }
Ссылка на комментарий
Поделиться на другие сайты

Цитата

Благодарю:)

Я пытаюсь поставить пометку и после телепортировать главного героя, но пометка телепортируется вместе с ним. Как это можно исправить?

 

12 часов назад, borivit сказал:

Я пытаюсь поставить пометку и после телепортировать главного героя, но пометка телепортируется вместе с ним. Как это можно исправить?

Вопрос снят:) Сделал свою пометку и теперь не портит стандартную пометку.

Теперь мой плагин полностью работает на lua за исключением запрета активации некоторых предметов т.к. это удобнее через конструктор.

Благодарю вас за помощь:) Если возникнут еще вопросы, то буду писать сюда:)

P.S.: Интересный глюк нарисовался, я когда открываю контейнер через свое меню и беру оттуда предмет, то после закрытия контейнера мое меню уже не открывается, а просто открывается контейнер, а когда возвращаешь предмет на место, то после закрытия контейнера опять начинает открывается мое меню.

Ссылка на комментарий
Поделиться на другие сайты

Для публикации сообщений создайте учётную запись или авторизуйтесь

Вы должны быть пользователем, чтобы оставить комментарий

Создать учетную запись

Зарегистрируйте новую учётную запись в нашем сообществе. Это очень просто!

Регистрация нового пользователя

Войти

Уже есть аккаунт? Войти в систему.

Войти
  • Последние посетители   0 пользователей онлайн

    • Ни одного зарегистрированного пользователя не просматривает данную страницу
×
×
  • Создать...