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

ArtemSH

Модмейкер
  • Постов

    275
  • Зарегистрирован

  • Посещение

  • Победитель дней

    12

Весь контент ArtemSH

  1. ArtemSH

    Weye Expansion Mod

    Капитан Немо, я правильно понимаю, что при создании анвила и лейявина, вы руководствовались образами карибских островов и городов вот той эпохи пиратских времен? В лейявинской бухте на севере очень чувствуется, что это не средневековый порт, а именно некая пиратская бухта на карибах))
  2. Немного конфликтует с TIE, COBL и еще парочкой модов. Нужен фильтр-патч (разобраться бы как такие делать).
  3. katkat74, а что такое виртуальные текстуры? 0_о
  4. В этом гайде Arthmoor расскажет, как правильно сделать патч совместимости для разрешения конфликта между двумя модами. Вы наслаждаетесь времяпрепровождением в игре, красотами нового свежеустановленного мода. Всё идет хорошо, пока вы не натыкаетесь на вещь, совершенно лишнюю на том месте, где она стоит. И после этого, ошарашенный, вы поворачиваете за угол и видите эту сцену: Вы думаете: «что-то здесь явно не так», и вы правы. У вас коттедж прямо посреди нового Unique Landscapes. Вы пытаетесь понять, в чем и дело, и тут все сходится: «Это же Bravil Bridge Cottage от Emma! Но он ведь никогда не был усыпан растениями…» И вы снова правы. Коттедж Эммы был построен на лужайке, где ничего не росло. Пока Bravil Barrowfields не пришел на эту поляну. Не говоря уже о том, что рекогносцировка местности обнаружит огромные камни, закрывающие добавленные Эммой доки. Стало быть, нужен патч. Такие случаи – обычное дело, когда в сборке много модов. Внимательный ценитель Unique Landscapes наизусть задекламирует целый ворох патчей, уже созданных для UL. В этот туотриале, я расскажу основы патчинга и о примерах проблем, с которыми сталкивались я и Vorians. Этот патч для коттеджа Эммы – прекрасный пример наименее сложных патчей. Планирование Сперва надо оценить фронт работ (the scope of the project). Лучший способ – прийти в игре в то место, где нужен патч, и сделать скриншоты. Вот один сверху, показывающий все конфликты двух модификаций. Чтобы было понятнее, я обвел нужное красным: Используя команду tcl, я поднялся над коттеджем, и осмотрел всё, что нуждается в патче. Пара обьектов ввалились в дом, и если войти внутрь внешнего меша дома, можно увидеть, что другие обьекты оказались вообще «внутри» дома. Узнав необходимое, я еще раз заскриншотил дом сверху, но уже в CS. Как вы догадались, Bravil Barrowfields добавил в эту область много всякого добра. Заметьте, что в моде Эммы ни стен, ни виноградника, ни руин, ни деревьев, ни камней, ни даже особенностей увиденного ранее ландшафта нет. Теперь-то, возьмем следующие инструменты: The TES Construction Set TES4Edit (xEdit – примечание переводчика) Wrye Bash Если вы не знакомы с ними, то патчинг – последнее для вас дело, поскольку он требует продвинутых знаний в ландшафте и манипуляции с обьектами, а также де-изоляции модов и использовании флагов ESM. Начинаем Откройте Wrye Bash и найдите те моды. Лучше всего сортировать по Порядку Загрузки, чтобы наши моды шли в правильном порядке. Удерживая ctrl, кликните левой по обоим. Затем правый клик, и выбираем ESMify Self. После этого, если убрать выделение, буквы обоих модов будут синими. Это означает, что в CS они будут значиться мастер-файлами. Construction Set Откройте CS и загрузите оба мода. Не активируйте их. Хотя обоих и нельзя активировать, когда они в состоянии ESM. Если возникнут ошибки при загрузке, жмите «Да для всех». Ошибки в случаях конфликтов – норма. (это актуально для оригинального CS, но не для CSE. Серьезно, лучше используйте CSE – прим. Переводчика). Перейдите в нужную ячейку. В нашем случае это ячейка 13,-9 в Мире Тамриэль. У вас будет нечто такое: Вспомните всё, о чем мы говорили. Некоторые добавленные обьекты можно переосмыслить и оставить. Другие – удалить. Держите в уме центральную задумку: менять что-либо надо по минимуму. Главная цель не разрушать задумку и не менять слишком много кроме самого необходимого. Итак, приступим. Сперва, что очевидно, уберем обьекты, упирающиеся в коттедж, такие как некоторые виноградные лозы, растения, бочки и другие мелкие предметы. Периодически сохраняйте работу. CS нестабилен даже с нормальными модами, а с патчами он особенно чудной, так что крашей бывает больше, чем обычно. Не стоит терять часы работы из-за того, что вы забыли сохраниться. Пока патчите, обращайте внимание на двери, Xmarkers, Маркеры Карты, Врата Обливиона и всё в тако мдухе. Это постоянные обьекты и их нельзя двигать, если игрок уже посетил локации с ними. Они навсегда остаются в файле сохранения с того момента. Так что любые патчи никогда не должны трогать постоянные обьекты. Если это невозможно, то нужно найти того, кто найдет другой способ решить вопрос. Любой ценой избегаем скриптов, меняющих расположение таких обьектов в нашем патче, поскольку ЛЮБЫЕ скриптовые изменения ТАКЖЕ становятся частью файла сохранения. Но можно безопасно двигать сами маркеры дверей (полупрозрачный желтый прямоугольник с розоватой стрелкой сверху), если нужно. НИКОГДА НЕ УДАЛЯЙТЕ ПОСТОЯННЫЙ ОБЬЕКТ! Это приведет к несказанным последствиям для игры! Обливион не терпит удаления постоянных обьектов ни под каким соусом, даже с нормальными модами. Игра станет нестабильной и будет крашится, если таких обьектов не будет на месте. Есть методы обхода проблемы, но лучше приберечь их для более продвинутого туториала, включающего знание TES4Edit для таких манипуляций. Возвращаясь к рутине, думаю, вам будет полезно отключить отображение листьев и включить границы ячеек. Когда работа завершена, сразу виден результат: Я удалил множество растений вокруг, кусок руин, впадавший в коттедж, а также обьекты в стенах и некоторые бочки и кирпичи. Два больших валуна, закрывавших собою доки, я тоже удалил. Дерево, прораставшее из колодца, я переместил. Сам коттедж я предусмотрительно не трогал, а также садовую кормушку напротив. И маркер двери не тронул. К сожалению, постоянные обьекты в этой области надо сдвинуть, так что головной боли прибавилось. Однако кое-что я не проверил. Под коттеджем ничего нет? В этом случае кое-что было: коттедж закрывал пару растений и кирпичные изгороди. Их тоже удаляем. В целом, всё, что рендерится, но не видно глазу, удаляем оптимизации ради. Оно в любом случае рендерится игровым движком. Следующий шаг: проверка обьектов, критичных для вида локации, но оказавшихся под ландшафтом. У Эммы камни в районе доков частично закрыты поднявшимся уровнем ландшафта. Используя как можно меньший радиус ландшафта, я осторожно опускаю землю, пока доки снова не показываются на глаза. В процессе всплыла и лодка. Если поверхность земли недостаточно опущена, опускаем пока всё не проявится, или двигаем сам обьект. Теперь чиним весь ландшафт в целом. На границах ячеек могут быть разрывы, но у нас их нет. Можно сразу поднять или опустить большой массив земли, чтобы отразить изначальный замысел Эммы. Поскольку в случае ландшафта, его высоту, текстуры и теней побеждает последний мод, то ландшафт отражает задумку Bravil Barrowfields. Так что придется возвращать вдиение Эммы. Убедившись, что высота земли достаточная, кругом нет резких или кривых перепадов высот, обьекты не под землей, идем дальше. Теперь исправим бросающиеся в глаза ошибки текстурирования ландшафта. В нашем случае: - у Эммы вокруг коттеджа зеленая трава, которую нужно вернуть. - от дороги до двери дома шла каменная тропинка, её тоже возвращаем. При текстурировании не следует истерить, если вы выбрали траву, рисуете её, а вместо этого видите уродливую грязь. CS имеетбаг, когда 9 текстур используются в одной ячейке разом. Отобразит он неправильную текстуру при работе в CS, а вот в самой ире всё будет как надо (актуально и для CSE, к сожалению – прим. Переводчика). Поскольку текстуру надо менять и под домом, я выбрал текстуру без генерации травы. После этого нужно исправить затенение текстуры ландшафта. В нашем случае это почти ненужно, только домик поправить. Вот результаты: Теперь финальная фаза. Сетки путей. Они наследуются последним модом. Поскольку последним является Bravil Barrowfields, то и сетки путей – от него. Исправим это. Не так уж и плохо! Единственное, что исправить точно: точку внутри дома. Удаляем её и добавляем новые точки, чтобы соединть сетку, но уже вокруг коттеджа. Сделали и теперь всё как надо. Уходим из CS, не забываем сохраниться. Нам же не охота терять такую красотень? Очистка мода в TES4Edit CS – взбалмошный зверь. Он осталяет нежеланые правки (unwanted edits) в простейших модах, где их даже не ожидаешь увидеть. Эти изменения, не удали их, приведут к проблемам с другими модами. Это особенно плохо, если вы допустили в моде тотальный атас, вроде нажатия кнопки compile all в скриптовом окне, думая, что это компиляция скриптов только вашего мода. Результаты плачевные, поскольку ваш мод в таком случае сохраняет в себя копию всех скриптов оригинальной игры. Еще хуже, что удаленные обьекты в одном моде, могут подвергнуться изменениям во втором, что ведет к печально известному «крашу при выходе из игры». Или Обливион просто зависнет навеки при выходе из него. Надо понимать, что почти все моды нуждаются в том или ином очищении в рамках защиты от подобного исхода. Это проблема характерна не только для Обливиона. В Морровинде она тоже часто встречается, так что для него создали серию инстурментов. TES4Edit и TES Gecko – два основных. Но первый надежнее в этом деле. К сожалению, проблема касается и Fallout 3, но там нужен FO3edit. Патчи добавляют фрустрации тем, что CS никогда не задумывался для работы с такими файлами, которые редактируют файлы других esp. Так что проблема нежелательных правок (грязных) (unwanted (dirty) edits) серьезна. Ч теперь нам стоит исправить ошибку CS, чтобы наш патч не привел к еще большим проблемам для конечного пользователя. Есть две школы очистки патча. Те, кто пользуются TES4Edit и его автоматической встроенной функцией отчистки, и те, кто еще и сами заглядывают под капот грязных правок. Таких правок, которые автоматике не сможет удалить. Их еще называют «дикими» (wild) правками. Для обучения я пойду вторым путем. Это послужит уроком, показав для чего очистка вообще нужна, а вы увидете, как выглядят грязные правки. Открываем TES4Edit. Выбираем наш патч и дважды кликаем на него. TES4Edit автоматически подхватит и наши мастер-файлы. Раскройте древо в левой панели. Примерно так это будет выглядеть: Помним, в какой ячейке происходило дело. Ячейка 13, -9 и 13, -8. Что-то сразу должно бросаться в глаза. Это изменения в ячейках 13,-12; 20,-17 и 20,-14. В этих ячейках нет никаких ветвей, они помечены зеленым, показывающим, что они идентичны информации их мастер-файлов мода. Если кликнуть по каждому, обратите внимания на правую часть экрана. Эти записи следует удалить. Правый клик по ним в левом окне и жмем удалить. После этого вы заметите, что "Sub-Block 2, -3" и "Sub-Block 2, -2" теперь не имеют ничего в своем составе. Их тоже удаляем. Результат: Оставшиеся ветви раскрываем. Будет что-то подобное: Те, что с надписью "Placed Object" и больше ни с чем – это обьекты, удаленные еще в CS. У них зеленые буквы на желтом фоне. В нашем случае это камни и растения. Наконец, оранжевый на красном (очень плохо) – у ландшафта и сетки путей. Но в данном случае так и должно быть, так что ничего не меняем. Еще раз про удаленные обьекты – их следует обязательно удалять, иначе краши и нестабильности неизбежны. Единственный путь – через функцию "Undelete and Disable". Последуйте на CS Wiki в статью по очистке мода и прямо в Секцию 2. Четко следуйте инструкции по фильтрации процесса. Как только отфильтровали, удаляем и отключаем референсы и БОЛЬШЕ НИЧЕГО. Это пункт 13 в той статье. В нашем случае "Remove 'Identical to Master' records" уже была проведена. Вообще следует почаще перечитывать все гайды по очистке, где всё разжевано, зачем и почему это надо. Не стоит верить тем, кто отрицает необходимость очистки, поскольку эти люди не знают, что говорят. Как только вы всё сделали, закройте TES4Edit и сохраните результаты. Завершая Патч Опять включаем Wrye Bash. Выбираем два мода. Но вместо ESMификации выбираем ESPификацию. Если этого не сделать, то ландшафт пропадет и на месте модов будут визуальные баги. Активируйте патч и пересоздайте Bashed Patch, если он есть, перезапустите TES4LODGen для дальней отрисовки местности и вперед. Наслаждаемся Результатами Труда Теперь посмотрим на плоды нашей работы. Загрузим игру и вперед к Эмме. Походите по месту. Убедитесь, что ничего не пропустили. Видите, теперь всё выглдяит намного лучше без этого хаоса. Еще пара Мыслей Этот гайд оказался полезным для многих моддеров. Именно этот патч с Эммой – один из простейших. Он почти не включает удаление обьектов, в нем мало работы с ландшафтом и сеткой путей. Многие патчи на голову выше в плане сложности, чем то, что мы делали. Наиболее неприятные – те, кто требуют целые комюинации модов, в духе Cloudtop Mountains, Ravenview Village, и мода Mimics. Или вот какой зверь: патч для Let the People Drink, UL: Imperial Isle, New Roads & Bridges и Open Cities. Ага, патч для четырех модов одновременно, который, как нетрудно догадаться, был той задачкой в реализации. Практика – критерий совершенства. Чем больше патчей создаете, тем проще процесс и быстрее результат. Патчи совместимости улучшают игру в своем особом духе. Внимательные читатели заметят лишнее бревно на скриншотах. Оно из TIE и совершенно случайно попало в кадр на самом краю. Но нам повезло: это бревно вполне вписалось в кадр и тем показало, насколько пространство Сиродиила заполнено самыми разными модами.
  5. Гайд от Arthmoor, посвященный работе с постоянными референсами при создании патча. Этот гайд – продолжение гайда Создание Патча Совместимости. Если не прочли, то прочтите, чтобы знать базу. В какой-то момент при создании патча совместимости вырастает проблема того, что делать с постоянными референсами (persistent objects).  Обычно все проблемы – из-за перемещения дверей из изначальной позиции в другую, чтобы встроить их в сделанные изменения. В рамках гайда мы будем работать именно с дверями. Но держите в уме, что всё, что мы сделаем, актуально для всех постоянных референсов (persistent references), видимых игроку. Первое правило: НИКОГДА не сдвигать двери в новое место! Постоянные рефернсы навсегда вписываются в файл сохранения, едва игрок посетил область, где они расположены. Второй правило: НИКОГДА не использовать скрипт, делающий ЛЮБЫЕ изменения с постоянными обьектами. Даже с целью их отключить (to disable). Каждый раз, когда скрипт вносит изменения, эти изменения навсегда записываются в файл сохранения. Включая и включение обьектов через включение родителя (enable parenting). Важно это запомнить, поскольку эти вещи не самоочевидны. Любые изменения с состоянием обьекта (to an enable state), сделанные скриптом, становятся постоянными. Поскольку два наиболее удобных метода – вне списка, то остается изобретать велосипед. Сперва отключите телепорт у двери. Главное, не забудьте куда именно она вела, какой был formID той двери прежде, чем вы её отсоедините от нашей. Первый и наименее опасный метод: просто установить малюсенькие размеры двери. Наименьший размер в CS – 0,01. В основном этого хватит, чтобы рендерить её незаметно для наблюдателя, если двери не большие, как городские врата, к примеру.  Если после уменьшения двери не видно, то всё готово. Велик шанс, что её скроют другие обьекты вокруг, например, обьекты из мода, конфликтовавшего с модом двери. Это идеальная ситуация,поскольку вместо старой двери мы просто поставим новую, и дело с концом. Второй метод: поставить флажок "initially disabled" оригинальной двери. В целом это рабочий вариант, но с нюансом. Если игрок перезагрузится, а не выйдет из игры, дверь может отрендериться, несмотря на опцию, из-за бага движка, возвращающего референсы к базовому, до-модовскому состоянию. Это особенно опасно, когда дело идет о дверях из оригинальной игры. Хотя в остальном этот метод гарантирует, что двери мы не увидим. Так что, если уменьшение размеров в пролете, можно попробовать этот метод. Последний фокус изворотлив, но полезен в некоторых ситуациях, когда размеры не помогут, а отключение по каким-то причинам не работает. Поэтому если первые два – в пролете, попробуйте этот метод, как последнее прибежище.  Отключите флажок persistence у двери. Поставьте флажок "initially disabled". Установите координату Z на -30000. Как только игра загрузится, и игрок войдет в ячейку обьекта, игра поймет, что дверь больше не постоянный референс и его координата Z уберет обьект из поля зрения. Но у этого подхода есть недостаток. Если игрок перезагрузит игру без выхода из неё, то со сто процентной вероятностью дверь сбросит все изменения и вернется к первоначальному состоянию. Действие всё того же бага движка. Без применения OBSE обойти эту проблему нельзя. Если ничего из вышеизложенного не помогает, то есть железный способ убрать дверь с глаз долой: удалить её. Надо быть ТОЧНО уверенным, что у двери отключен телепорт перед тем как делать это, иначе CS крашнется. Сохранитесь перед этим. Хоть метод поможет, но у него есть другая опасность в виде удаленного референса (deleted reference), запись о котором сохранится в файле нашего патча. Это может крашнуть игру, даже если другие моды не пытаются воздействовать на удаленный обьект. Обливион очень плохо воспринимает удаление постоянных обьектов, любых обьектов такого рода. Особенно дверей с телепортом. Риск краша довольно низок для дверей из модов, но НИКОГДА не нужно удалять двери из оригинальной игры – это приведет к ошибкам, вылетам и нестабильности при любом раскладе. Если дверь помечена как цель в квесте, если к ней привязан пакет ИИ, если скрипт на неё ссылается, то при попытке удалить эту дверь вылезет предупреждение. Если оно выскачило, остановитесь, выключите CS и НЕ сохраняйтесь, поскольку у CS есть баг: если отменить предупрждения любого рода, у двери, вызвавшей предупреждение, отключится телепорт. Мда-а-а, Бесезда. Как только с дверью покончено, теперь самое легкое. Нужно поставить новую в отпатченной локации, и эта дверь будет вести к той же двери, к которой вела старая. Расположите новую дверь на нужном месте, убедитесь, что подключили её к правильной двери с правильным formID. Поправьте маркер телепорта, и затем проследуйте через телепорт к той двери, чтобы сделать то же самое с её маркером. Если нужно, убедитесь, что поменяли референсы в скриптах, которые ссылались на старую дверь – просто замените старую дверь новой в строчках скрипта. То же самое надо сделать и с пакетами ИИ, и с целями квеста, если это актуально. Редко бывает так, что нужно поправить диалог, в результирующем скрипте которого фигурировал референс на отключенную нами дверь. А теперь самое веселое. Сохраните патч, загрузите игру, убедитесь, что всё работает, как надо. Если всё хорошо, то релоцированная дверь покажется на локальной карте игрока и впишется в новое, отпатченное окружение. Надеюсь, этот гайд пролил свет на проблему загрузочных дверей в патчах совместимости.
  6. Прошу одобрения) https://tesall.club/tutorials/the-elder-scrolls-modding/modostroenie-oblivion/1742500317037-razbiraemsya-s-postoyannimi-referensami https://tesall.club/tutorials/the-elder-scrolls-modding/1742580482210-sozdanie-patcha-sovmestimosti
  7. ArtemSH

    Уникальные Ландшафты

    sdnkrn, gweow (автов flavor'ов) мастер маленьких таких дополнений, хотя не везде у него получается остаться в рамках своей задачи на мой взгляд. тот же нибенейский мост сделан отлично, но немного выбивается небольшая часовенка, добавленная рядом, и в которую зайти-то нельзя. это напоминает геймдизайн ХЕСУ, который, по честному, рушит иллюзию "реальности" каждого здания и двери такими ходами.
  8. ArtemSH

    Map Marker Overhaul_Update

    АлексRoyt, легче пиратку Золотого Издания взять. Я слышал, что русификация стимовской версии это головная боль.
  9. ArtemSH

    Map Marker Overhaul_Update

    АлексRoyt, по ходу при русификации поменялись formID маркеров, судя по ошибке. он ищет старые маркеры (ванильные), а они подверглись русификации и поменяли formID. По ходу это.
  10. katkat74, это уже будет не ремейк, а демейк на пс1 :)))
  11. LivelyDismay обьясняет как на примере Условий (Conditions) работать с данными в xEdit и насколько удобнее создавать небольшие моды, ребалансы и патчи через xEdit, чем делать их "вручную" через редактор. Условия великолепны. Они дают массу возможностей разнообразить контент и творить всё, что вздумается. И они, прямо как и ключевые слова, используются для всего. Загрузочные Экраны, перки, крафт…всё ими управляется. Посмотрим на них. Поработаем сегодня с Fallout 4 и его DLC. Запускаем xEdit. Загружаем модули. Ждем фразы Background Loader: finished. Начнем с простого. Раскройте содержимое Fallout4.esm и найдите категорию Load Screen. Раскройте её. Найдите вторую запись: Vault111CharGen. Удивительно, но здесь есть условие! Раскройте его, чтобы лучше его рассмотреть. Всё просто. Type: Equal to – определяет как использовать информацию прямо под этой линией. Comparison value 1.00000 - 1 = true. 0 = false. Как говорит линия сверху, условие, чтобы работать, должно иметь значение 1. А что именно за условие? Смотрим дальше. Function: GetInCell – проверяет, в какой именно ячейке вы находитесь. Так что кто-то или что-то должен быть в нужной локации, чтобы это условие было истинным. Какой именно? Cell: PrewarVault111 – в этой ячейке! None – это игнорируем, это просто мусорная информация. Run On: Subject – «Субьект» это цель конкретного условия. В данном случае – игрок. Другие две линии под этим тоже являются мусорной информацией.  Parameter всегда будет -1. Если поменять значение, то всё сломается. Не меняйте ничего. Суммируем. Что мы имеем? Легко: Субьект должен быть в ячейке PrewarVault111, чтобы условие стало истинным. Как только оно таковым стало, этот загрузочный экран появится в вашей игре. Без шуток. Это всё. Всё сводится к одному условию. И поскольку вы в этой ячейке окажетесь ровно один раз за всю игру, этот загрузочный экран вы увидите только один раз. Поскольку Субьект больше ни разу не посетит эту ячейку, и Условие всегда будет ложным (то есть не будет равно 1), загрузочный экран больше не будет появляться. Вот так! Давайте еще смотреть. Напишите 46946 в строке поиска FormID (FormID search bar) вверху слева и нажмите Enter. Выделится следующая запись. Это запись COBJ или Constructible Object. Опять же прочесть её очень легко: если у нас два Bloatfly Meat, можно скрафтить Baked Bloatfly, пищу под грифом ROAST, на верстаке приготовления. Но у нас здесь никаких условий, так чего мы тут забыли? Ну так мы собираемся их добавить! Для практики. Праывый клик по заголовку записи на правой панели и выбираем Copy as override into... Выбираем опцию <new file> с флагом ESL (только для Skyrim\Fo4. В остальных играх это ESP. – прим. переводчика) в правой колоне. Такой тип плагинов вы будете делать 99% случаев. Назовите мод как хотите. Теперь у нас дубликат этой записи в нашем плагине. В нем-то и будем делать изменения, ну или патчи, если хотите. Правый клик по пустому полю Conditions в нашей новой записи и выбираем Add. Теперь у нас вот такой зеленый блок. Прогресс! Раскройте его. Давайте добавим для закрепления Перк, как условие. Это маленькое условие дает нам тонну информации. Нам нужно от игрока владение конкретным перком. Стало быть, сперва введем условие True, подтверждающее, что, да, у игрока есть перк. Для этого поменяйте значение Comparison Value на 1. Теперь игрок не может иметь больше 1 в этом поле, а значит и двух одинаковых перков (ранги перков в реальности устроены как совершенно новые перки взамен предыдущих), так что оставьте поле Equal to. Еще мы хотели, чтобы субьектом был игрок. Для этого есть два пути:  1. Оставить его в субьектах. Поскольку условие будет проверять того, кто активировал верстак, оно и так будет знать, игрок это или нет, поскольку игрок указан в субьекте. 2. Изменить Run On на Reference и выбрать PlayerRef в выползающем меню выбора. В этом случае, и субьект и PlayerRef делают ровно одно и то же, поскольку из значения совпадают – оба подразумевают игрока. Но знать разницу важно. Дальше. GetWantBlocking – это не то условие, которое мы хотим. Мы хотим перк, да? Ну давайте менять эту линию. Выползет очередное поле заполнения. Там выбирайте перк! После этого выползет еше одно и там выбирайте конкретный перк, который хотите. Посмотрите, сколько там разнообразия. Я выберу что-нибудь безумное прикола ради, как пример, что перков на деле намного больше того, что показано в меню прокачки. Так что теперь нужно быть «Хорошо отдохнувшим» (Well Rested) чтобы скрафтить предмет. Почему нет? Моя игра, ворочу, что хочу. И вы тоже!  Добавим еще кое-что. Для этого условия (Condition) сделаем условием (requirement) наличие хотя бы одной Сковороды (Cooking Pan) в инвентаре Субьекта. Но, увы, такое условие будет сломано! Это неочевидно, но надо быть крайне внимательным в Условиях. Посмотрите на две линии. Equal to 1. Она говорит, что Субьект должен иметь именно ОДНУ сковородку. Не две, не ноль, не 14. Только одну. Так что, имей игрок две сковородки в инвентаре, и условие сразу будет ложным, и предмет крафтить запретят. Поэтому условие надо поменять. Двойной клик по Equal To, и в выпадающем меню выбираем то, что нам нужно. Выбираем чекбокс Greater. Теперь тип будет Greater than or equal to. Отлично! Теперь нам НУЖНО 1 сковородка или больше! Уже заметили чекбокс с надписью Or? Все Условия имеют по умолчанию свойство AND, то есть они сочетаются с другими Условиями, чтобы позволить что-то сделать (Быть Отдохнувшим И иметь сковородку). Но мы пока что оставим условие OR. Еще условий! В него тоже бросаем условие со сковородкой. Чтобы всё сделать побыстрее. Помните: надо переносить (drag) условия со слота родителя (top (parent) slot) в раздел Условия нового. Теперь наши Условия дублируют друг друга! Поменяем Сковороду на котелок (Cooking Pot). Добавьте тип условия OR (condition Type) и сковороде и котелку в свойства (requirements). Теперь сверните все наши условия, чтобы видеть их в линию. Видно, как первая кончается с AND, а другие две с OR? Это значит именно то, о чем вы думаете: игрок должен быть Отдохнувшим (это работает как перк), И ЛИБО хотя бы с одной сковородой, ЛИБО хотя бы с одним котелком. Теперь добавим эти условия всей еде в игре. Это довольно времязатратно, да? Да! И это отстой! Поэтому все сделаем за пять секунд! Выберите все записи, которым хотите добавить наши Условия, затем правый клик и Copy as override into... Выберите патч, сделанный нами пять минут назад. Кликните по любой записи, которую мы только что скопировали, не важно по какй. Затем правый клик по верхушке названия плагина в правой панели и выбираем Jump to. Наша правая панель не поменялась, но левая панель расширилась. Теперь посмотрим на наши записи с перспективы нашего патча. Выберите все эти записи на левой панели, правый клик и выбираем Compare Selected. Теперь у нас целая куча записей в правой панели. Листаем прямо вниз, где покоятся наши Условия. Правый клик по шапке Условия (Conditions) и выбираем Copy to selected records. Хотим ли мы применить условия ко всем этим записям? Да, кликните ОК. И вот таким макаром все эти COBJ записи имеют те же условия, заданные нами до этого. Вот так вот быстро! И эффективно! Таких опций для Условий УЙМА. Парочка моих любимых: GetQuestCompleted, GetLevel, GetIsSex (для создания особых условий в зависимости от пола героя), GetIsRace и GetLocationCleared – просто парочка примеров того, как разнообразны ваши возможности в области обуславливания. И вам стоит их попробовать! За ними лежит абсолютная свобода.
  12. Это перевод статьи LivelyDismay, немного раскрывающей базовые вещи патчинга конфликтов в xEdit. Привет! Слово «конфликт» всегда возбуждает мысли о борьбе и негативе. Когда кто-то говорит тебе «ты должен исправить конфликты в своем модлисте», то сразу думаешь «о нет, нужно срочно заставить моды остановить борьбу и исправить их!» Да? НЕТ! НАМ НУЖНЫ КОНФЛИКТЫ. Конфликты дают мне жизнь. Они дают мне цель. И тебе тоже. Если же нет, то зачем это читать? Нам нужна правильная перспектива, вот о чем я думаю. Конфликты — это не обязательно плохо. Они необходимы. В моддинге «конфликт» синонимичен фразе «что-то поменялось». Моды и задуманы, собственно, менять игру тем или иным путем. Вот с такой перспективы понятно, почему конфликты – не зло, а необходимость. Посмотрим на различные типы конфликтов. Запускаем xEdit. Грузим все модули. Ждем пока не явится надпись Background loader: finished. Кликни на любой плагин в левой панели. Правая панель тоже поменяется, но пока не суть важно на что. Посмотрите на правый угол вверху. Там кнопка Legend. Кликните её. Вы видите разноцветное окошко. Так как это отдельное окно, то двигать его можно по всему экрану как захотите. Немного уберем его туда, где оно не будет мешать, но будет как на ладони, когда нам нужно туда заглянуть. К примеру, я привык помещать его вниз. Заметка: цветовая палитра у вас может отличаться, поскольку для меня Light Mode – игрушка дьявола. Если хотите поменять свою, то откройте Options Menu. И кастомизируйте тему UI: Ладно, к конфликтам. Окошко Legend само-понятно. Но я за наглядность, так что посмотрим примеры. Предполагаю, что мод, на который уже кликнули, подсвечен и его содержимое отображено в первой панели, там и смотрим. Одиночная Запись (Single Record) – однотонный фон и белый текст. Ни перезаписей, ни конфликтов, ни проблем. Информация как она есть. Не о чем волноваться. Ищем другую. Идем в StandaloneWorkbenches.esp и раскроем категорию Furniture. Видим единственный конфликт под номером FE000821 wSW_ManufacturingWorkbench. Жмем на него! Вы видите малюсенький конфликт: я переименовал верстак, потому его имя справа ярко-зеленое на желто-зеленом фоне. Посмотрим на нашу Легенду, говорящую нам, что данная ситуация называется Перезапись без конфликта (Override without conflict). Другими словами, базовая запись (base record) была единожды изменена, и ничего больше это единственное изменение не меняет. По сути, это даже и не конфликт, поскольку никто другой с этим изменением и не борется. Что я имею в виду под «борются с изменением» (fighting for control)? Ну, поищем пример! Найдем Crafting Mastery.esp в левой панели и раскроем его содержимое. Затем раскроем содержимое категории Constructible Object. Так много цветов! Но, надеюсь, вы можете заметить, что все цвета одинаковы: ярко-зеленый текст на желто-зеленом фоне. Даже не открывая каждую запись, мы уже знаем, что это: Перезапись без конфликта. Что-то в этом моде перезаписывает настройку из базового плагина, и никто другой не перезаписывает ту же настройку в свою очередь. Так что пропустим всё это дело и листаем дальше, пока не увидим КРАСНЫЙ. Ну это уже выглядит страшновато. Взглянем на 06025B1A  co_weap_throw_DLC04PackScentGrenade. Листаем вниз на правой панели пока снова не увидим жутковатый красный. Вот ТЕПЕРЬ-ТО мы нашли конфликт, на который стоит посмотреть. DLCNukaWorld.esm говорит одно, Crafting Mastery.esp другое, а Crafting-VanillaItemKeywords.esp – третье, и никто из них не согласен друг с другом. Давайте посмотрим повнимательнее на каждый. DLCNukaWorld.esm хочет, чтобы Гранаты Охотника (Predator Grenades) можно было скрафтить в химлабаратории под фильтром GRENADES (гранаты) после того, как игрок завершил квестовую стадию 50 квеста DLC04MQ04. Условия DlCNukaWorld.esm Crafting Mastery.esp хочет, чтобы те же гранаты можно было скрафтить на верстаке взрывчатки под фильтром GRENADES – SPECIAL после того, как игрок выполнит квест “An Ambitious Plan” И получит стадию квеста 500 у “Home Sweet Home” И стадию квеста 50 у всё того же DLC04MQ04. Условия CraftingMastery.esp Crafting-VanillaItemKeywords.esp хочет, чтобы гранаты можно было скрафтить на верстаке взрывчатки под той же категорией GRENADES – SPECIAL после выполнения игроком квестовой стадии 500 у “Home Sweet Home” И получения 50 стадии у DLC04MQ04. Условия Crafting-VanillaItemKeywords.esp ВОТ ЭТО конфликт. Множественные запси по-разному манипулируют содержанием той же записи и не согласны с изменениями друг друга. А что-нибудь сломается, если этот конфликт не разрешить? НЕТ. Это именно то, что я и хочу увидеть в игре. Возвратившись к Легенде, мы увидим, что красный фон означает: Проигравший в Конфликте (Conflict loser). Это означает, что перезапись БЫЛА, пока другой плагин, загрузившийся ниже в списке загрузки, не пришел и не сказал: «ну, братец, я так не думаю», и не заменил содержимое записи. Если я перенесу условия проигравшего в условия другого мода ниже, сломает ли это что-нибудь? НЕТ. Я знаю, что выглядит это всё мило и зелено, но цвет означает лишь тип конфликта. Еще раз, самая важная часть, которой я только могу обучить вас по поводу xEdit: КРАСНЫЙ НЕ ОЗНАЧАЕТ ПЛОХО, ЗЕЛЕНЫЙ НЕ ОЗНАЧАЕТ ХОРОШО. Выбросьте эти мысли из головы. Цвета означают только ТИП конфликта. Цвета НЕ показывают вам хорошие или плохие конфликты. Чтобы еще раз посмотреть на это по-другому, снова сменим нашу перспективу. Прямо сейчас мы в содержимом Crafting Mastery.esp, а co_weap_throw_DLC04PackScentGrenade имеет тот жуткий красный цвет. Правый клик по Crafting-VanillaItemKeywords.esp наверху правой панели и жмем Jump to. Мы всё еще на той же записи справа, но посмотрите на левую панель. Вот, что я имею в виду под перспективой. Crafting Mastery.esp проиграла борьбу за перезапись, поэтому была красной. Crafting-VanillaItemKeywords.esp выигрывает, поэтому он обозначен иначе. В данном случае Идентичным Мастеру победителем конфликта (Identical to master but conflict winner), потому что вся его информация в этой записи не отличается от предыдущего плагина Crafting Mastery.esp. С одним единственным исключением. И это исключение – в условии, добавленном Crafting Mastery.esp, которое больше не существует в этом плагине и записи. Так что, идентичный мастеру, но победитель конфликта. Знаю, некоторым этот гайд покажется чрезвычайно примитивным…и пару раз попробовав, ты сам понимаешь, что это выглядит просто. Но я четко помню, когда впервые начал использовать xEdit. Я не понимал, что я делаю. Я не знал, что есть Легенда. Я не имел никаких гайдов с помощью. Это был кошмар. И я не хочу, чтобы вы прошли через то же самое. Поэтому очень важно понимать, что значат эти цвета. Всякий раз, когда работаете, оставляйте Легенду открытой. Уже совсем скоро это станет вашей второй натурой, не волнуйтесь. Закройте xEdit, когда закончите. И поскольку я пытался в этих плагинах поменять записи на зеленый, то мне предложат сохранить изменения. Я снимаю галочку с чекбокса, поскольку я-то как раз не хочу сохранять эти изменения. Но сломает ли это игру, если я всё-таки сохраню то, что мы сделали? Вот вы мне и ответьте.
  13. Еще немножечко добра)) https://tesall.club/tutorials/the-elder-scrolls-modding/1742042022817-konflikti-kak-chitat-xedit https://tesall.club/tutorials/the-elder-scrolls-modding/1742055687492-usloviya-kak-podarit-sebe-unikalnii-igrovoi-opit-s-pomoshchyu-xedit
  14. katkat74, кстати говоря nexusmods.com/oblivion/mods/51391?tab=description
  15. allexa, благо дарю)
  16. а что за SMCO?
  17. Автор расскажет о специальных функциях NX из форка NVSE, добавляющих уникальные возможности для моддинга Fallout New Vegas. Необходимые навыки: референсы и базовые формы, переменные скриптов (плавающие, целы, референсы), get\set Навыки для продвинутых пользователей: строки, строковые переменные, переменные массивов, ссылающиеся на квестовые переменные в диалоге, другие условия Что ж… Если немного побродить вокруг скриптинга, вылезет слово ‘nx’. Это экстра-функции из NVSE Extender от prideslayer. Самые популярные из них – переменные NX.   Переменная NX функций… - позволяет хранить игровую информацию в референсе - как переменная EVFI хранит плавающие и целые числа, как EVFo хранит формы, референсы и базовые формы, динамические референсы с помощью NX_Get или NX_Set - без необходимости обьявлять её в скрипте: как только на ней применили set, она существует в референсе: “the name you give it” = ключ И будучи функцией референсов (reference functions), NX_Set/GetEVF* функции становястя применимыми в синтаксисе референсов     Короче, если нужно отслеживать сколько напитков выпил непись, нужно и создать переменную ‘DrinksDrunk’ и изменять её значение, привязав его к референсу: И то же для его любимого яда\алкоголя: - отслеживая всё это, переменная референса останется в сохранении, но только на том же референсе. Больше ей ничего не нужно, так что можно спокойно удалять мод, и она останется. - и если любой мог захочет получить доступ к информации из переменной, он сможет получить её из референса, как только будет знать ключи доступа: Например: Другой мод, который хочет прочитать эту информацию, не нуждается в вашем мастере, чтобы сделать это. И вы можете выполнять все эти отслеживания на стольких NPC, на скольких захотите. - можно иметь столько переменных NX на любом референсе, сколько захотите - а если они не нужны, то избавляемся от них так: а если нужно избавиться от кучи таких переменных, то вот так: все наши ключи в моде на напитки начинаются с DM, так что уничтожение всей информации на референсе BuddyRef будет выглядеть вот так: Очистим состояние и дух! Это означает, что вы можете иметь целую кучу пользовательских переменных, привязанных к любому актору или предмету, какой захотите. Не требуется никаких эффектов, никаких токенов, никаких квестовых скриптов чтобы все это хранить, что уменьшает конфликты модов и позволяет модам общаться друг с другом вне зависимости от мастера и порядка загрузки. Если вы хотите сохранить какую-либо информацию об акторе, например, его любимую пиццерию, вы просто прикрепляете ее к этому актору, у которого потом вы и найдете ее, когда она вам понадобится. Хорошей практикой будет: Никогда не знаешь, будет ли повторено название твоего ключа для переменной nx, но для других целей. Поэтому в большинстве случаев, просто добавим немного акронимов своего мода в начале строки: «MyCleverModAcronym:restofthekey» и т. д.   Еще один полезный совет: Поскольку это реально крутой инструмент взаимодействия между модами, лучше всего четко указать какие переменные NX вы используете, особенно если вы используете их в огромных количествах. Сохраните их в dummy esp или перечислите их в фиктивном скрипте, добавьте текстовый файл к архиву с модом, что угодно. Обновление: С выходом NX12 появляется возможность хранить и извлекать строки как переменную EVST: NX_SetEVST, NX_GetEVST и NX_ClrEVST. Вы можете делать это как с реальными строками, так и с переменными строк. Продолжая пример с напитками: В NVSE4+ есть целый ряд функций строковых переменных, которые позволяют вам сохранять, изменять и извлекать строки на лету. В сочетании с функцией ToString (сокращенно: $), которая позволяет вам передавать строковые переменные в функции, которые в противном случае ожидали бы фактическую строку, это означает, что вы можете динамически создавать ключи nx, а в случае EVST — и значения. Это означает, что вы можете сделать что-то вроде: Также в NX12 появились новые функции, которые возвращают информацию nx об актере в строковой карте, с ключами nx в ключах строковых карт и значениями в значениях строковых карт: NX_GetEVFlAr, NX_GetEVFoAr и NX_GetEVStAr. Допустим, у нас есть потасовки в барах, в которых buddyref то выиграл, то проиграл, а чтобы вренуть эту инфу мы напишем следующее: и ar_dump инфа будет выглядет так: [DM:Fight:1:Score] : Won [DM:Fight:2:Score] : Lost Нужно немного рабираться в массивах и циклах foreach чтобы работать с этой информацией. Позже я напишу гайд об этом.   Обновление 2: Об этом много говорили, и вот оно здесь: EVFL переменные nx теперь можно просматривать в условиях диалога/квеста/анимации. (Только EVFL, потому что такие условия имеют только числа для значений.) Сначала вставляете строковую переменную в квестовый скрипт и устанавливаете ее на ключ nx, который вы хотите, чтобы она представляла. В соответствии с предыдущими примерами я выбрал "DM:DrinksDrunk": Прикрепите скрипт к квесту, очевидно. Обратите внимание, что квест не обязательно должен быть включен, чтобы просто хранить переменную, но в этом случае я устанавливаю переменную в том же скрипте, поэтому я включаю ее, чтобы всё работало. (Я мог бы добавить let DSTestQst.sv_drinksdrunk := "DM:DrinksDrunk" в какой-нибудь другой скрипт и сделать то же самое.) Поскольку у нас уже есть квест, давайте продолжим его тестирование с условиями диалога. Как вы видите, я создал тему и строку, которую я хочу сделать зависящей от EVFL, имеющего значение 1. Я добавляю условие и выбираю NX_GetQVEVFL для функции условия. Появляется окно, очень похожее на то, которое вы увидите, если проверите обычную переменную квеста. Выберите квест, в котором есть скрипт квеста со строковой переменной в нем в выпадающем меню. Чтобы выбрать переменную, вам нужно ввести ее индекс. Если это первая переменная в скрипте, она будет 1. Если вы не уверены, какой индекс имеет ваша строковая переменная, вы можете посмотреть его в FNVEdit: И как вы можете видеть на следующих скринах, вы можете легко изменить переменную nx для актера в результирующем скрипте, и следующая строка обнаружит изменение. Обратите внимание — и это справедливо для всех переменных квеста — что вам не следует переставлять переменные квеста после того, как вы уже сделали сохранения с активным квестом; вы можете добавлять новые в конец списка переменных скрипта, но не перемешивать их. (Ну, вы можете, если вас не волнует получение отчетов об ошибках.) Спасибо jaam за создание этой штуки, за EVST и переменные массивов и обьяснение всего этого.
  18. Это пятый из серии гайдов по нововведениям NVSE4, освежившими моддинг сцену Нью Вегаса: UDFы, строковые переменные и массивы. Моддеры Обливиона, незнакомые с ОБСЕ 16+ тоже получат пользу от этих статей. В данной части речь пойдет об Обработчиках Событий (Event Handlers) и Пользовательских Событиях (User-Defined Events). Почему? Как модер, вы точно знаете, что кое-какие штуки вполне возможно реализовать, но лучше этого не делать из-за конфликтов и других проблем. Вот как бы вы реализовали: Изменение переменной «личная гигиена» каждый раз, когда активируется раковина или туалет? Размытие экрана при испитии алкоголя? Звук растегнувшейся молнии при снятии одежды? Джингл основной темы «Хороший, плохой, злой» при экипировке револьвера? Срабатывание мины-ловушки с определенным шансом при открытии запертой двери? Отслеживание изменений в пакетах ИИ у актора, чтобы заставить следовать определенному пакету? Маркировку к удалению всех выкинутых игроком предметов в определенной ячейке во избежание разбухания сохранения (savegame bloat), ну или сымитировать, что их кто-то забрал, пока игрока не было рядом? Форсировать кастомный диалог между неписями при уроне одним второго определенным оружием или типом оружия? Пока что на ум приходит только привязка обьектного скрипта к обьектам с одно-фреймным блоком OnActivate, On(Actor)Equip, OnOpen, OnPackageChange, OnDrop или OnHit(With), срабатывающим при соответствующем событии. Но проблема-то в том, что нельзя просто взять и присоединить эти скрипты к оригинальным обьектам без конфликтов с другими модами, меняющими те же обьекты. Тогда придется создавать уйму отдельных патчей для их разрешения. При этом New Vegas уже и так имеет настолько ограниченный предел модов, какой не снился ни одной игре Бесезды, и если большая часть порядка загрузки – в одних только патчах, то это просто трата драгоценных слотов esp. Не говоря уже о том, что все эти патчи надо создать и потом сотни раз повторять людям, пишущих о багах, как и в каком порядке эти патчи устанавливать. Другая проблема – все эти скрипты надо прикреплять к огромному количеству обьектов, чтобы все работало, как задумано. То есть, ваш мод на личную гигиену затронет вообще все туалеты и раковины, или мод на звук револьвера – все револьверы, если вы хотите, чтобы при попадании персонаж кричал «Яппи-ка-эй, чертила!». А что насчет добавленных модами вещей того же типа? Предпочтете создавать патчи для каждого добавленного револьвера? Я пас. Во многих случаях скрипт будет срабатывать даже когда не нужно: скрипт удаления выброшенного барахла должен срабаывать не на всех акторах, ловушки должны срабаывать не на всех референсах дверей. А единственный способ проверить условие срабатывания – только через уже работающий скрипт, что уже создает конфликты простым фактом прикрипления скрипта к базовой форме обьекта. Это душнилово уже заставляет отбросить такие задумки. А что если… - мы могли бы отслеживать активацию, открытие, сброс предметов, удары, потребленные предметы и прочие действия без привязки скриптов к базовым формам обьектов? - мы могли бы фильтровать отслеживание через параметры, в духе кто активировал, что было открыто или кто открывает дверь и тому подобное? Вот было бы круто, да? Так вот именно это и делают обработчики. 2. Стандартные События, Их Обработка, Параметры Обработчики Событий – UDF, зарегистрированные с помощью NVSE с целью вызывать их в момент совершения события. Эти обработчики работают всю игровую сессию, даже если загрузить другое сохранение, они все равно работают до перезапуска игры. Если я хочу отслеживать в какой момент что-либо активировано или выброшено, я регистрирую обработчик событий с помощью команды SetEventHandler каждый раз, когда игра загружена: И теперь, каждый раз, когда нечто вот-вот будет (is about to be) активировано или выброшено, NVSE вызовет (call) обработчик событий прежде, чем активировать или выбросить обьект. Помните: некоторые функции, работающие в обычных блоках, не работают в их эквивалентах из обработчиков событий, поскольку то, что является условием срабатывания скрипта еще не сработало. Использование GetActionRef на блоке OnActivate ничего не вернет. Но, к счастью, у нас есть параметры обработчиков для таких целей. В случае с событием OnDrop, NVSE вернет первым параметром бросавшего, а вторым – брошенный предмет. Так что наш myDropEventHandler нуждается в том, чтобы эти параметры зарегистрировать в следующем порядке: Если эти переменные не обьявлены прежде, чем быть параметрами, то NVSE выдаст ошибку.   В случае события OnActivate нужно помнить, что скриптовый блок OnActivate и команда activate сбивают с толку из-за неподходящей терминологии. Переключатели света, раковины и триггеры находятся в категории «Активаторы» в ГЕККе, хотя они должны быть активированы кем-либо. Хотя кажется, будто непись активирует референс контейнера, на деле, если форсировать скриптом активацию контейнера, мы увидим, что именно референс контейнера и будет активатором: Событие OnActivate сперва ожидает активатора, который должен быть активирован, и только потом вещь или актора, который будет активировать. Последнего мы и возвращаем командой GetActionRef в обычном скриптовом блоке OnActivate. Представим это так: Большая часть стандартных обработчиков событий, а это те, чьи скриптовые блоки соответствуют обычным блокам, имеют по обыкновению такой же набор параметров. Они всегда присутствуют, вам нужно лишь их получать.   (параметры необязательно называть так, как я, имена ничего не значат и нужны только вам для различения). 3. Стандартные Параметры как Фильтры Параметры обработчиков могут использоваться как фильтры, когда добавлены как параметры для функции SetEventHandler при регистрации обработчика. С ними обработчик будет вызываться только при необходимости, так что фильтры надо применять как можно чаще. Также убедитесь, что фильтры отфильтровали валидную форму (valid form), иначе система баганет, и фильтрация просто отвалится, а вы останетесь с обработчиком, срабатывающим без фильтра. Если вы выбрали использовать один фильтр или вообще никакого, вам нужно определить какой именно параметр фильтрации, первый или второй, вы фильтруете, и затем поменить его двойными двоеточиями (как делаем это командой ar_Map): В OBSE «first» назывался «ref», а «second» назывался «object». Это возможно и в NVSE: Но, в отличие от OBSE, NVSE позволяет любому параметру отсылать к референсу, базовой форме и даже к формлисту (!), содержащему референсы и\или базовые формы и\или другие формлисты, когда это возможно. Так что использование слов «first» и «second» просто менее запутывающее.   Так что используя обработчики и их фильтры, вам будет намного легче реализовать всё то, что я предложил во введении: Для отслеживания пакетов также вполне возможно применить блоки старта, изменения и завершения пакетов в одном и том же обработчике: В таком случае можно использовать команду GetCurrentEventName чтобы выяснить что за событие вызвано вашим обработчиком: 4. Удаление Обработчиков Событий Вполне возможно, что у вас есть настройка, позволяющая больше не применять обработчик к событию. Кроме того, обработчики событий все еще действуют при загрузке другого сохранения без перезахода в игру, поэтому вам захочется удалить обработчик ради безопасности чтобы загрузить сохранение, где условия срабатывания обработчика еще не наступили. Чтобы удалить обработчик используем RemoveEventHandler, с тем же самым синтаксисом, что и у команды создания обработчика: Это остановит вызов обработчика событий при срабатывании фильтров. Если это единственные фильтры, то можно просто их пропустить: Так что можно останавливать вызов обработчика при одних фильтрах и оставлять при других. 5. События NVSE Помимо событий, связанных с оригинальными типами блоков, вы также можете регистрировать обработчики, которые будут вызываться при возникновении определенных метаигровых событий. Это особенно полезно для модов, которые пишут какие-то файлы совместного сохранения и т. д. (После тестирования выяснилось, что это касается только плагинов NVSE.) 6. Пользовательские События (UDE) Еще круче то, что можно вводить кастомные события, которые другие люди могут регистрировать тем же путем, что и стандартные, и получать информацию из параметров. Это привычнее для моддеров, работающих с глобальным функционалом, желающим отслеживать нечто произошедшее и одномоментное, то, что классифицируется как «событие», чтобы другие моддеры тоже могли это отслеживать (нечто в духе Conscribe – прим. Переводчика). Допустим, вы создали полный ребаланс скрытности и хотите показать, когда игрок обнаружен или скрыт независимо от того, согласуются ли с этим оригинальные функции игры. Возможно, вашему моду необходимо пройти ряд инициализаций, прежде чем он заработает, и вы хотите, чтобы зависимые моды знали, что ваш мод действует, когда инициализация завершена. Возможно, вы хотите, чтобы ваш сортировщик инвентаря не только уведомлял других о том, что сортировка прошла, но и отправлял информацию о том, где все оказалось. Возможно, вы даже работали над кое-каким модом, который отслеживает кое-какие вещи, происходящие до тех пор, пока один из неписей, участвующих в этом, не столкнется с каким-то кульминационным событием, и вы хотите, чтобы другие кое-какие моды знали об этом, чтобы, например, воспроизводить соответствующие звуки и тому подобное. В таких случаях нужно отправить событие (dispatch an event) с помощью функции DispatchEvent, за которой должно следовать как минимум имя события, опционально строковая карта с данными и опционально еще одна строка, переписываающая изначальное имя отправителя (overriding the default sender name): Моды, желающие зарегистрировать обработчик через него, смогут это сделать вот с какими ограничениями: - поскольку у диспатча нет стандартных параметров, фильтрами нельзя воспользоваться при создании обработчики для кастомных событий - единственный параметр обработчика UDF, который должне быть, это переменная массива, которая хранит строковую карту, которую всегда получает, даже если ничего не было отправлено (even if none was dispatched): Если строковая карта не была отправлена (), то в диспатче будут только два элемента: Диспатчер может определить «SomeSenderString», добавив третий необязательный параметр в строку DispatchEvent. Если параметр пуст, то это будет имя мода диспатча. Если была отправлена ​​строковая карта, к ней добавляются стандартные элементы.   Ну и всё. Вперед модифицировать! ИЗ КОММЕНТАРИЕВ Вопрос: - Обработчик событий OnDrop, он вызывается в menumode или при закрытии Пип-бой и переходе в gamemode? - Могу ли я получить полный пример того, как написать скрипт для обнаружения заклинаний? Если говорить точнее, мне интересен эффект пьянства, я думал о нем давно, но так и не реализовал как раз из-за проблем с совместимостью. - Есть ли также AddEventHandler? Ответ: 1) Хороший вопрос; я не уверен. Но учитывая, что события «обрабатываются» до того, как они действительно произойдут, и что вещь, которую вы выбрасываете, уже исчезла из инвентаря, пока вы все еще находитесь в режиме меню, я думаю, что это работает в menumode. Честно говоря, я не тестировал события так же тщательно, как строки и массивы, и даже они никогда не тестировались мной полностью; я всегда фокусировался на изучении того, как управлять этими вещами, а не на том, как работает движок, если вы понимаете, о чем я. 2) Ну вот пример: «Магические эффекты» — это обливионская версия «базовых эффектов» из Fallout. ChemIncCHalcohol — это базовый эффект, прикрепленный к выпивке. Однако, возможно, что у некоторых добавленных/измененных модами выпивок его нет. Вместо этого можно заполнить формлист базовыми эффектами. Возможно, событие «OnActorEquip» тоже сработает на еде, хотя некоторые моды (например, Sofo) накладывают эффект выпивки на людей как заклинания, а не экипируют их, поэтому эффект магии должен лучше их улавливать. Так что обработчик нужен для уведомлений, ну или для применения скрипта или отслеживания тех же изменений визуала (image space modifier) скриптом токена или заклинанием. 3) Не уверен, зачем он нужен. Есть стандартные события, к ним добавляются обработчики командой SetEventHandler. Есть кастомные, созданные пользователями, которые творятся точно так же. Вопрос: 3 - Я имел в виду что-то вроде противоположности RemoveEventHandler. Можно остановить обработчик событий, но нельзя запустить его снова, верно? 2 - Дело в том, что я понимаю сам обработчик событий, но не то, как практически заскриптовать все это. Я понял, что это своего рода UDF с принудительными параметрами, но тогда я не понимаю остального. Мне всегда нужно регистрировать его внутри GameMode и в условиях GameRestarted / GameLoaded? И еще: вы сказали, что UDF всегда будут выполняться (ценой статтера для остального...), потому я начал использовать UDF для своих небольших скриптов, для которых важна надежность. Есть ли такая же надежность для обработчиков? Они железно срабатывают или нет? Мне очень интересен пример, который вы привели с пакетами, поскольку мы все знаем, что они иногда немного того... может быть, это и есть окончательное решение вопроса, чтобы они, наконец, не ломались.   Ответ: Помимо строки кода SetEventHandler, я бы написал обработчик, который произносит заклинание при его срабатывании: А в заклинании я бы начал модификатор изображения с "imod" в блоке scripteffectstart и продолжал бы проверять, находится ли игрок все еще под влиянием в блоке scripteffectupdate, чтобы можно было удалить его с помощью rimod в какой-то момент. Что касается UDF и статтеров, это не обязательно вина UDF, это скорее вопрос комбинированной нагрузки скрипта, вызывающего UDF, и самого UDF, в сочетании с другими скриптами, которые, возможно, делают то же самое примерно в тот же момент. Другие типы скриптов могут просто останавливать скрипт, если они не могут завершиться в течение фрейма, но UDF очень-очень стремится к завершению своих функций как можно сокрее. Я понятия не имею, ведут ли себя обработчики событий как-то по-другому; я предполагаю, что нет, но jaam шарит лучше меня. Я предлагаю использовать обработчики в первую очередь как метод уведомления о происходящих событиях и о том, с кем это происходит и т. д., а затем использовать другие скрипты, чтобы что-то с этим сделать. Вопрос: 2 – Простите за духоту, но я хочу быть уверен, что понял: myHandler - это простой скрипт объекта, как UDF, но затем мы помещаем свою строку SetEventHandler в игровой режим в каком-то состоянии GameRestarted / GameLoaded? Например, когда делаем свою первую инициализацию? (однако, просто интересно... для совместимости с другими модами на выпивку, может быть, такой обработчик событий можно было бы больше применять к эффектам отмены алкоголя, может быть, у некоторых пользовательских алкоголиков есть другие эффекты, но у всех есть абстиненция) Ответ: А, да, нельзя зарегистрировать свой обработчик событий с помощью обработчика событий. Потому что этот скрипт никогда не будет выполнен. Поэтому мы региструем его в другом скрипте  — у большинства из нас уже есть первоначальная инициализация и проверка GGL/GGR где-то в наших модах, так что это закономерно.
  19. Это четвертый из серии гайдов по нововведениям NVSE4, освежившими моддинг сцену Нью Вегаса: UDFы, строковые переменные и массивы. Моддеры Обливиона, незнакомые с ОБСЕ 16+ тоже получат пользу от этих статей. В данной части речь пойдет о массивах, их переменных и применении их в игре. 1. Что? Терминология, Постоянство Переменные массивов отсылают к массивам. Массивы – списки. Представьте себе таблицу с двумя столбцами в Ворде. Массивы содержатся в NVSE для ваших нужд и если они существуют в момент сохранения, то их сохраняют в .nvse файле соответствующего сохранения. В этом плане они идентичны строковым переменным. Но отличие от строковых в том, что NVSE убирает их за вас: когда массив ни на что не ссылается, будь это переменная массива в активном скрипте или другой массив, держащий ваш массив, он удаляется. В общем, либо в массиве храним информацию на длинный период и используем для этого квестовую переменную массива, либо на короткий, для чего придется де-инициализировать соответствующую переменную как можно быстрее после того, как в её услугах нет нужды, чтобы не получить разбухание сохранения. 2. Типы Массивов, Ключей и Значений Существуют разные типы массивов. Первый, будем называть его «регулярным» массивом (массив типа «массив», «массив» массив – такие еще термины в ходу, но звучит немного тупо, имхо), выглядит как формлист, подобный левел листам из ГЕККа: НеАлкогольныеНапитки формлист: 0    MS05NukaColaQstm 1    MS05IceNukaCola 2    NukaCola 3    WaterUnpurified 4    WaterPurified Как и формлисты, регулярные массивы индексируются последовательностью целых натуральных чисел, начиная с нуля. Эти индексные числа называются ключами. Главное отличие и преимущество всех видов массив в наличие значений, ассоциируемых с этими ключами. Значениями может быть всё что угодно: не только формы и референсы, но и числа, строки и даже другие массивы. (Когда мы говорим о комбинации ключ-значение, мы используем термин «элемент»). Вот регулярный массив: 0    NukaCola                                       ; форма 1    3                                                     ; число 2    "You can't beat the real thing"        ; строка 3    RecipesInvolvingNukaColaArray   ; массив   Как и с формлистами индексы всегда будут последовательными. Если удалить любый элемент, то элементы выше него сдвинутся – пропуски в последовательности ключей недопустимы, так же, как и десятичные дроби или отрицательные числа: 0    NukaCola 1    "You can't beat the real thing" 2    RecipesInvolvingNukaColaArray Это ограничение, в случае с другим типом числового индексирования, можно обойти картами (maps), которые тоже массивы, только они могут хранить любое число как ключ и позволяют делать зияния (поскольку допускают наличие десятичных дробей). Так что мы можем, к примеру, сконвертировать приветствие с CS Wiki в эту карту, где время сконвертируется в десятичные и активности в строки: 8.5     "Запись, кофе"  ; массивы не хранят кавычки, я их написал, чтобы вы не забыли, что это строка 9        "Введение" 9.5     "Презентация A" 10.5   "Кофе-брейк" 11      "Презентация B"   Вот еще один массив, хранящий внешние ячейки как значения с их X координатой как ключем, некоторые из ключей отрицательны: -21      MojaveOutpost -9        MojaveDriveIn 7         188TradingPost     12       BoulderCity 17       BitterSprings Наконец, иногда чисел любого рода недостаточно, поэтому у нас строковые карты (stringmaps), хранящие строки как ключи: "Имя"                    "Prudencia" "Возраст"                       27 "Тело"                     "T6M" "Профессия"            "Test character" "Времени в игре (часов)" 106.5 "Ячейек посещено"          CellsVisitedArray ; увы, пока что там хранится только GSDocMitchellHouse за все это время, под ключом 0 3. Обьявление и Инициализация Переменных Массива Обьявляем переменные массива любого типа вот так: array_var somearrayvarname   Прямо как строковые переменные, переменные массива должны быть прежде инициализированы чтобы ссылаться на существующий массив в NVSЕ. Инициализировать массивы можно так: - прямо сконструировав их, используя команды let и ar_construct с типом массива как параметром «Массив», «карта» и «строковая карта» - параметры строки у функции ar_construct, подобные параметру Health в GetAV, GetAv Health. Важно, что при включенной перезаписи компилятора (script compiler override), вам высветится предупреждение по поводу этих строк, в котором GeckPU скажет что-то в духе «я полагаю, что это строки, подумай, стоит ли их оставлять, а я пока не буду это компилировать», но если проигнорировать это предупреждение, то всё скомпилируется как миленькое, и в игре всё будет работать без проблем. - используя let присваиваем им другую переменную массива, то есть теперь обе переменных массива ссылаются на один и тот же массив: Если вторая переменная массива ссылается на тот же массив подобным образом, вы можете спокойно сломать одну из двух переменных, используя команду ar_construct, чтобы удалить всё её содержимое. Если нужно скопировать массив в другой, просто используйте ar_copy или  ar_deepcopy (см. седьмой раздел этого туториала).   - используя let присваиваем результат функции (the result of a function), который возвращает массив (включая UDF, которые возвращают массив с помощью команды SetFunctionValue)         в результате получится обычный массив, содержащий фрагменты строки в виде строк:                          '         0    Chop         1    up         2    this         3    string Множество связанных с массивами команд (ar_copy, ar_range, ar_map и т.д.) возвращают массивы и так инициализируют переменные массивов, которым присвоены эти массивы через let. Можно де-инициализировать переменные массивов можно через присвоение им ar_Null с помощью let: В таком случае это будет нечто в духе переменной ref со значением null (null ref var). Если хотите знать, инициализирован ли массив, прежде, чем что-либо с ним будете делать, можно его проверить как переменную bool или использовать команду LogicalNot (!) в случае, если она де-инициализирована. Если вы и так в курсе её состояния, то тогда нет смысла добавлять этот код. 4. Возврат и Хранение Элементов Если вы знаете ключ значения, то вы можете прямо его вызвать подобной строчкой кода: применимо и к примерам из предыдущей главы: Необязательно строго пробрасывать значение из массива в локальную переменную скрипта всякий раз, когда хотите сделать что-то с ними. Если помните раздел по перезаписи компилятора () в туториале по синтаксису, как только вы его включили, вы можете сотворить вот это: и подобные этой комбинации – всё в ваших руках. Тестите, что работает, а что нет.   И добавлять или заменять элементы массива можно вот так:   Замечу, что бывают ситации, когда присваивание через let элемента массива к результату функции напрямую, как в случае с расой, может выйти боком, так что придется сперва хранить их в переменной локального скрипта. Сразу скажу, не ждите, что на getitemcount можно положиться. И, да, возвращая или помещая в строковую карту, можно использовать строковые переменные вместо строковых ключей.   Неважно, в каком порядке добавляются элементы в массив, он автоматически сортирует их по возрастающей, числовым способом для регулярных массивов и карт, по алфавиту для строковых карт: будет отсортировано:   0     value0                                                            A     valueA 1     value1                                                            B    valueB   5. Наполняем Массивы Чуть Быстрее Не надо быть семи пядей, чтобы увидеть, что такое присвоение: может быть чем-то вроде рутины по набору текста, если вам нужно поместить в него много элементов, что еще и занимает драгоценные строки скрипта. Впрочем, есть несколько сокращений (shortcuts).   Для регулярных массивов можно применить команду ar_List, которая создает массив с 20 элементами в одной строчке, обычно разделяемых запятыми: Порядок добавления элементом зависит от индексасии массива (от 0 до 19).   0   NukaCola 1   2 2   "You can't beat the real thing." 3   RecipesInvolvingNukaColaArrayVar   Ar_list – функция, возвращающая массив, так что она сразу же инциализирует массив, к которому вы применили let.   Если надо добавить элементы в виде диапазона целых чисел в регулярный массив, то примените команду ar_Range: Для карт и строковых карт применяют команду ar_Map, снова до 20 элементов для обьявления, только ключи могут быть  любыми, так что нужно обьявить их для каждого элемента самому, связав каждый ключ со значением с помощью двойных двоеточий:   Карты и строковые карты опять автоматически отсоритуруют свое содержимое в порядке возрастания, когда всё будет готово. А если элементом больше 20? А если мы не знаем, сколько их там будет? 6. Осмотр Массивов: Проход по Массивам, TYPEOF, Дампинг Массивов в Консоль В таком случае нужен способ отправить часть во временный массив с помощью команды ar_list, добавив каждый элемент другому массиву, уже содержащему парочку элементов. Хотя можно просто использовать ar_InsertRange для таких целей (см. главу 8). Но для обучения пока условимся забыть эту информацию (в любом случае эта команда работает только с регулярными массивами). В общем, нужен способ выбирать каждый элемент массива поочередно и менять его. С регулярными массивами, коль скоро ключи – целые последовательные числа, мы можем применить ar_size (см. глава 7), чтобы построить (to build) цикл while, в котором мы добавим каждое значение, содержащееся в массиве2 массиву1 посредством команды ar_append (см. главу 8): И всё прекрасно работает, покуда каждое значение в массиве2 имеет тот же тип (форма, число, строка или массив), чтобы мы знали, как должен быть объявлен 'someVar'. Проблема в том, что массивы могут содержать любую комбинацию таких значений. Строго говоря, мы могли бы использовать удобную команду TypeOf, чтобы узнать тип значения в виде строки: Всё это скорее обходной путь, да и в любом случае цикл while не работает с картами (зияниями и десятичными) или строковыми картами (строковые ключи). Переходим к циклу foreach.   Циклы foreach выбирают каждый элемент типа коллекции (collection type) в прямом цикле, и, как и цикл while, воспринимайте цикл foreach как блок. Для строк они выбирают каждый символ, для контейнеров — каждую ссылку на инвентарь, а для любого типа массива они выбирают каждый элемент массива. Как и в случае с циклами while, вы прерываете цикл foreach командой «break» и пропускаете экземпляр тела цикла командой «continue».   При использовании цикла foreach для массива каждый элемент копируется в stringmap, итератор, который необходимо объявить — ключ проверяемого элемента будет найден в stringmap["key"], а значение — в stringmap["value"]: "key"      thekeyoftheelement "value"    thevalueoftheelement   Итератор, т. е. временная строковая карта, будет иметь только эти 2 элемента: "key"::KeyOfTheInspectedElement и "value"::ValueOfTheInspectedElement. Вам не нужно инициализировать его — это делает команда foreach — и когда цикл завершается, он немедленно обнуляется/неинициализируется. так что длинный цикл while может превратиться в этот короткий цикл foreach: Как пример с нашей картой, хранящей внешние ячейки по их X координате: допустим, мы хотим разделить её на карту с западными ячейками (-Х) и восточными (+Х). А нам нужен fXPos? Нет. И напоследок. Как сказано еще в туториале по синтаксису, нужно тестить все штуки в игре, так что нам нужен вывод в консоль для наших массивов. Используем команду ar_Dump: ** Dumping Array #1 ** Refs: 1 Owner 3D: DSTest36.esp     [ 0.000000 ] : Value0 [ 1.000000 ] : Value1 etc. Строковые карты очевидно хранят строки в квадратных скобках, где пишутся ключи. Refs: 1 – значит, что только один референс хранится в этом массиве, в данном случае переменная массива в скрипте, вызванная функцией ar_dump Owner – мод, создавший массив, то есть DSTest36.esp, а также его номер в актуальном Порядке Загрузки. ID массива – число, присвоенное массиву при создании. Можно вызвать дамп массива, специфицировав ID, а не просто вызвав его на переменную массива: Но знайте, коль скоро массивы удалены, уже не ссылаясь ни начто, их ID будут использованы вновь. ar_DumpID – полезна по большей части только при вызове из консоли. Просто надо четко знать число ID. Обе функции ar_dump работают как printc; они не зависят от debugmode. Однако создать версию, зависящую от debugmode, в UDF очень просто: На самом деле чтение показаний консоли утомляет, поэтому есть удобная функция con_scof, которая выводит все, что появляется в консоли, в текстовый файл в корневой папке: 7. Функции Массивов: Быстрый Обзор Ar_Size вернет размер массива в виде целого числа. -1 означает, что массив еще не инициализирован, 0 — что он инициализирован, но пуст. Поиск информации:  Ar_Find вернет ключ для значения, которое нужно найти. Если значение нигде не найдено, будет возвращено значение -99999.0 для обычных массивов и карт, и пустая строка (sv_length sv_somestringvar == 0) для строковых карт. Ar_HasKey проверит, существует ли элемент с указанным вами ключом, возвращая простое логическое значение. Отмечу, что если есть эквивалентные элементы «ключ-значение» (оба являются числами или строками), то для поиска нужного элемента с помощью ar_HasKey потребуется чуть меньше времени/мощности процессора, чем с помощью ar_Find, поэтому, если считаете, что вам нужно искать что-то с одной стороны чаще, чем с другой, используйте их в качестве ключей. ar_Keys возвращает обычный массив, содержащий все ключи исходного массива/карты/строковой карты: Следующее должно быть очень удобно при работе с картами и строковыми картами (хотя совершенно не нужно при работе с обычными массивами): ar_First возвращает первый ключ. ar_Last возвращает последний ключ. ar_Next возвращает следующий ключ из указанного. ar_Prev возвращает предыдущий ключ из указанного. Ar_Next и ar_Prev возвращают значение 'bad index', когда ничего не находят. Запрашиваем 'bad index' для сравнения, используя ar_BadNumericIndex или ar_BadStringIndex в зависимости от типа массива: Копирование: Ar_Copy копирует элементы из одного массива в другой: Если someArray1 в свою очередь содержит несколько массивов в качестве значений, someArray2 будет содержать рференсы тех же массивы, а не копии. Чтобы скопировать подмассивы «чисто», используйте ar_DeepCopy: Я не могу советовать какой лучше - это зависит от структуры вашего мода, типа скриптов, с которыми работаете (постоянных или нет), и того, что вы пытаетесь сделать, т. е. хотите ли вы, чтобы изменения, которые вы вносите в эти подмассивы через someArray1, влияли на эти подмассивы под someArray2. Выбор за вами, просто четко определитесь, что нужно. Стирание элементов: Ar_Erase – классно, что название отражает суть - делает свое дело: Он также может стереть диапазон элементов, если вы укажете этот диапазон в нотации среза, разделив первый и последний ключи для стирания двоеточием: Если удалить элемент или диапазон элементов из обычного массива, элементы с более высокими ключевыми номерами автоматически сместятся вниз. Если elfkbnm элемент из массива из цикла foreach, когда этот элемент находится в итерации, это, скорее всего, разорвет цикл. С обычными массивами это происходит не всегда, но случается, если, к примеру, передать массив в качестве параметра в UDF из цикла. С картами и строковыми картами это приведет к прерыванию (break). Здесь нужно стирание (erasing) по завершению цикла, допустим, добавив элементы для стирания в новый массив во время цикла и удалив их из исходного впоследствии, или добавив элементы для сохранения в новый массив и позволить старому впоследствии стать его копией. Сортировка: Ar_Sort возвращает новый массив со значениями старого, отсортированными в определенном порядке. Это можно сделать только в том случае, если все они одного типа (числа, объекты или строки), иначе он просто вернет пустой массив. Строки сортируются в алфавитном порядке, числа — в числовом, а формы — в соответствии с их formID. Результирующий массив — это «регулярный» массив, независимо от того, какой массив сортировался: Ar_SortAlpha делает что-то похожее, но значения не обязательно должны быть одного типа — все они преобразуются в строки, как если бы вы использовали ToString, и сортируются в алфавитном порядке: строки остаются строками, числа становятся строковыми представлениями чисел, формы становятся именами базового обьекта, если они у них есть (в противном случае это их строки FormID). Если вам нужна более конкретная сортировка, вы можете использовать Ar_CustomSort, для которой вам нужно написать UDF. Ar_CustomSort будет многократно (repeatedly) брать 2 элемента из массива для сортировки и передавать их в UDF в качестве параметров в виде массивов. Ваша UDF определяет, как значения 2 параметров var массива должны быть отсортированы относительно друг друга. Это особенно удобно, если сортировать формы не по их имени/formID (например, вес, размер, навыки и т. д.) или подмассивам. Вы даете функции знать, что один элемент следует поставить «прежде» или «позже» в порядке результирующего массива, позволяя UDF возвращать «true», то есть, задавая значение функции. Попробуйте так. Ок? Я хотел сортировать жителей Гудспрингс по их навыку медицины, те, что круче, в начало, те, что слабее, вниз.     теперь array2 — это обычный массив, отсортированный по возрастанию, что в нашем случае означает по убыванию навыка медицины:     0    DocMitchellRef    ; У Дока 33 баллов навыка     1    GSChetRef          ; У Чета 15     2    TrudyRef              ; все по 12     3    SunnyRef     4    EasyPeteRef     5    GSJoeCobbRef   ; Джо Кобб имеет 10          Если я использую функцию ar_customsort с descendingbool, она отсортирует массив в порядке убывания, что в нашем случае означает в порядке возрастания навыка медицины:     0    GSJoeCobbRef     1    EasyPeteRef     2    SunnyRef     3    TrudyRef     4    GSChetRef     5    DocMitchellRef    8. Функции «Постоянного Массива» Как уже много раз было сказано, регулярные массивы могут хранить только целые натуральные числа как ключи, начиная с нуля. Это ограничение, но и благославение, поскольку мы можем автоматизировать весь процесс, а не париться как с картами и строковыми картами. Например, Ar_Append добавляет элемент в массив со следующим доступным номером ключа. Что то идентично вот этому: Если бы нам пришлось делать это с картами и строковыми картами, и мы не знали бы, какие ключи у них уже есть, нам пришлось бы выбрать случайный ключ, проверить, есть ли он у (строковой) карты с помощью ar_HasKey, и только потом добавить его или нет, выбрав другой. Или использовать ar_last и добавить что-то к этому, чтобы создать новый ключ. Ar_Insert добавляет значение в массив по указанному числовому ключу, сдвигая вверх те, что уже есть, а также те, у кого ключ выше. Также можно применить команду ar_insert к общему размеру массива, фактически делая то же самое, что и ar_append. Однако нельзя вставлять значение по индексу, превышающему общий размер массива, потому что вы потеряете его часть. будет выглядеть так: 0  "value1"     1  "value2"     2  "value2.5"     3  "value3"     4  "value4"     Ar_InsertRange делает еще один шаг вперед и позволяет вам вставлять диапазон значений в середину или в конец массива. Вы указываете диапазон как еще один массив. Итак, здесь мы продолжим наш предыдущий пример из главы 6, добавив array2 в конец array1: создаст     0    value1fromarray1     ...     39    value20fromarray2     Если было 20. Наконец, Ar_Resize позволяет вам сократить массив до указанного вами размера или расширить его до указанного вами размера. Подождите, что такое paddingMulti? Ну, Multi — это значение элемента массива — может быть чем угодно. Так что под 'paddingMulti' подразумевают любое заданное значение  для новых значений, если вы масштабируете массив. Они наполняются. Ar_insert, ar_insertrange и ar_resize также могут возвращать bool, сообщающее, выполнили ли они свою работу — вы можете проверить это в той же строке, где отдаете им команду выполнить ее:
  20. Это третий из серии гайдов по нововведениям NVSE4, освежившими моддинг сцену Нью Вегаса: UDFы, строковые переменные и массивы. Моддеры Обливиона, незнакомые с ОБСЕ 16+ тоже получат пользу от этих статей. В данной части речь пойдет о Строках (Strings) и Строковых Переменных (String Variables). 1. Введение. Теория строк Строки – определенные комбинации символов, то есть буквы, цифры и знаки пунткуации. Ничего нового. Всё в игровом интефрейсе, что является словом или предложением, а не числом, является строкой: названия кнопок в меню, слово «Открыть» и «Войти» при наведении на дверь или контейнер, заметка «Вы больше не хотите есть», «имя» клавиши, которую нужно нажать, и так далее. Всё это хранится как строки под вывеской «Игровые Настройки» (Game Settings). Различные языковые версии игры хранят различные значения этих строк. Из туториала по синтаксису вы помните, что в строчке playerref.SetAV Health, та самая Health это просто напросто строка, которая может быть заменена целым числом (numeric int), ссылающимся на то же значение актора — использование строки «Health» просто избавляет вас от необходимости искать это целое число. Мы же говорим о форматированных строках, однако, не относящихся к игровым настройкам или значениям актора. Если вы пользовались командами DebugPrint, Printc или MessageEX (а если вы это читаете, то значит вы их знаете, поскольку их использование – стандартная практика даже для новичков), то вы использовали форматированне строки для того, чтобы игра отобразила вам информацию, которую вы хотите: То, что взято в кавычки, это форматированная строка, поскольку использованы двойные кавычки чтобы сказать компилятору скрипта, что это форматированная строка. Без этого вылезет ошибка: I’m – это не аргумент для функции. (тк эти кавычки создают строки, я буду использовать одинарные кавычки для закавычивания текста в туториале, чтобы избежать путаницы. С этого момента «строка» и «форматированная строка»  - одно и то же). С точки зрения скриптинга проблема со строками в том, что оригинал не дает нам достаточно команд для работы с ними. Это особенно актуально для игровых настроек и их строк и строк значений актора. Форматированные строки, по крайней мере, имеют то преимущество, что оригинал позволяет нам использовать для них несколько спецификаторов формата (format specifiers), чтобы передавать значение переменной float в саму строку: Если fSomeFloat равен 3.4, то: И NVSE кое-что добавляет к этому. Если rRef равен DocMitchellRef: Ну хорошо, но мы все еще не умеем хранить эти строки и не может их менять по скрипту, лишь только заново переписывать. Вот здесь-то нам пригодятся переменные типа string. 2. Основы Строковых Переменных: Обьявление и Инициализация Прямо как переменные ref отсылают к форме или референсу, а переменные float отсылают к числу с плавающей точкей, строковые переменные отсылают к строке. «Очевидно!», скажете вы, но нужно помнить, что сами по себе эти переменные строками не являются, поэтому я на это и указываю и под «строкой» имею в виду «строковую переменную» (string var). Строки у нас уже были, а вот строковые переменные и манипуляции с ними – вещь новая. Собственно строки, на которые ссылаются строковые переменные, и содержатся в файле .nvse, соответствующем вашему сохранению. Обьявляются строковые переменные следующим образом: (заметка о конвенциональном наименовании: большинство скриптеров добавляют префикс к переменным, например, float, значит fSomeFloat, ref, значит rSomeForm, чтобы не забыть к какому типу эти переменные отсылают. Некоторые скриптеры делают это и со строковыми переменными и используют s в начале (sSomeStringVar), но я лично против, поскольку в игре сотни таких же строковых игровых настроек с тем же префиксом s, что ведет к конфликтам. Лично я использую префикс sv_ или вообще не пишу ничего. Конечно, и в такой ситуации может быть конфликт с другими строковыми переменными с таким же префиксом, но их немного. В любом случае выбор за вами, просто будьте внимательны). Обьявление строковой переменной не означает, что она содержит уже строку, что логично и для других переменных в скрипте. Если вы сами не присвоили ей значение, то она ни на что и не ссылается, и если команда вызвана без обьекта, то игра баганет или вылетет. Присвоение строки строковой переменной именуется «инициализацией» и сделать её можно: - используя let, присвоить форматированную строку - используя let, присвоить другую строковую переменную - используя let, присвоить функцию, которая может возвращать строку (включая UDF, которые возвращают строку или строковую переменную с помощью SetFunctionValue). Можно использовать и форматирующие спецификаторы при инициализации, хотя это заставляет вернуться к устаревшему пути инициализации строк, используя sv_construct: (Заметьте, что присваивание цифр и имен предметов строкам легче делать с помощью комнады ToString, смотри соответствующую главу туториала) Если строковая переменная не инициализированна, она возвращает числовое значение 0. Это единственный раз, когда строковые и числовые переменные смешиваются: никогда не присваивайте строковым числовое значение, никогда не присваивайте float переменным строку или строковую переменную. Чтобы проверить прошла ли инициализиация строковой переменной, перед использованием этой переменной сделайте так: Конечно, если вы в курсе, что она проинициализированна, смысла в этом коде нет. 3. Синтаксис: Let Vs Set Строковые переменные введены в OBSE прямо перед Let и if eval, так что имейте в виду, что старая документация обсе ссылается на до-letовую эпоху. Прежде чем просто написать ‘let sv_mystringvar := "I'm a string”’ приходилось писать ‘set sv_mystringvar to sv_construct "I'm a string"'. С командой let функция sv_construct полностью лишился смысла, поэтому я больше не буду упоминать эквиваленты команд с set, если в этом нет необходимости. Реально, используйте let. Я ссылался на set, потому что присваивание строковой переменной к другой через set приводит к тому, что они ссылаются на ту же самую строку, и изменение одной переменной ведет к изменению другой: Скрипт выкладка из скрипта: чтобы такого не было, нужно создать sv_string2, которая будет ссылаться на копию строки sv_string1, а не на ту же самую строку: и это был единственную путь «чисто» скопировать содержание строковой переменной в другую. Когда мы присваиваем одну строковую другой через let, оно сразу сошлется на копию первой, и если поменять что-то в одной, другая останется той же. Я вижу так, что путь set заставляет не только больше писать, но и создает больше опасностей, к примеру тот с потереей данных строки. Ну и давайте прямо скажу: зачем две переменных на одну и ту же вещь? Вы хотите скопировать значение первой, чтобы затем поменять её, а не для того чтобы дублировать в двух переменных то же самое значение. То же характерно и для присваивания через set\let строковым переменным значений, возвращаемых через GetName, увеличивая тем самым шансы для косяков с этим set. 4. Уничтожение Строковых Переменных С оригинальными переменными скриптов (), не надо волноваться с тем, что будет после того, как вы их использовали. Если они храились в квестовом скрипте, вы храните их вечно, пока не удалите мод. Если же они были частью заклинания или обьекта, который больше не действует, то они просто исчезают из поля зрения: их не нужно уничтожать. Но для строковых переменных всё иначе. Они хранятся во .nvse файле даже после того, как скрипт перестал работать. Они насильственно удаляются при удалении мода, но до этого они приводят к разбуханию сохранения (cause a bit of bloat), так что нужно четко следить за их удалением\де-инициализацией\уничтоежением как можно скорее, когда они перестают быть нужными. Можно разрушить до 10 различных строковых переменных подобным образом: Как вы помните из туториала по UDF, строковые переменные – единственные локальные переменные UDF, которые нужно удалять самому. 5. Проверка Строковых Переменных Синтаксис с if eval, позволяющий проверять равна ли строковая переменная другой, и то же со строкой, выглядит вот так: и можно использовать != для проверки не-равенства. Но до этого следует проверить возвращаются ли целочисленные значения командой sv_compare чтобы сравнить строковую переменную и строку: Нет смысла использовать sv_compare, когда есть альтернатива в лице if eval, только если только вас действительно интересует сравнение по алфавиту. Эти сравнения не учитываю регистр (case-insensitive), если только не использовать sv_compare. 6. Проброс Строковых Переменных как Параметров: TOSTRING ($) Если  оригинальные или NVSE функции хранят одну и более строк как параметры Можно заставить их хранить строковые переменные как параметр, используя ToString, или $ Есть парочка функций, особенно только для Fallout или тех, что вводят плагины NVSE, подобные MCM, которые не работают с ToString + строковая переменная, поскольку не были созданы для этого. Если это актуально для вашего скрипта, используйте script compiler override, который заставит ваш скрипт работать. ToString может возвращать в виде строк числа и формы, то есть превращать их в строки: Вернет имя формы, если оно есть: Вернет гексовую formID,если имени нет: Числа: 7. Контаминация Строковых Переменных, Или: Их Добавление Легко: Отмечу: имена определяются базовой формой, использование set меняет их для базовой формы тоже Работая со строками, можно наткнуться на некоторые ограничения в превращении переменных в строки с спецификаторами форматирования в контексте добавления к уже существующим строкам: это не сработает и не скомпилируется.   Но у нас есть ToString, в очередной раз помогая: сработает.   И если на одной строке этого сделать нельзя и нельзя применить ToString, то можно разбить на два или использовать sv_construct: И также можно получить некоторую выгоду от функции sv_Insert: 8. Измерение и Поиск Ваших Строковых Переменных   sv_Length возвращает длину строки строковой переменной: Поскольку начальная позиция всегда индексируется как 0, конечная позиция всегда будет (sv_length sv_stringvar) - 1.   sv_Find находит первое вхождение подстроки в строке и возвращает позицию в виде целого числа: sv_Count возвращает количество вхождений подстроки, содержащихся в строке: 9. Разбиение Строковых Переменных, Замена Их Частей и Их Пересборка Самый простой способ извлечения фрагментов из строки строковой переменной — использование sv_Erase: Или вы можете заменить часть строки другой с помощью sv_Replace: усложненная версия: Если элементы строки отделены распространенным символом, как пробел или слеш, sv_Split может разделить их и возвратить их как строковые переменные в массиве: и затем команда ar_dump возвратит: [ 0.000000 ] : This [ 1.000000 ] : is [ 2.000000 ] : a [ 3.000000 ] : sentence. Каждое из значений – строка. Пример 1. Допустим, у нас есть кусок одежды в виде меша для конкретной части тела, ну как в оригинале, и мы хотим менять их по скрипту.   Оригинальные пути файлов будут такими: armor\LegateArmor\LegateArmor.NIF А наши такими: armor\MyModFolderName\LegateArmor\LegateArmor.NIF   Тогда действовать будем так: Заметьте, что я использовать параметр 0 чтобы получить мужской меш, и вызвал базовый обьект, а не референс (ArmorRef.GetBiPedModelPath 0), поэтому этот код переключает пути к файлам для базовой формы. Что-то близкое к get в TempCloneForm.   Вы можете заметить, что мне на самом деле не нужно знать конкретные пути к файлам, мне нужно просто вставить имя папки после первого раздела («armor»), поэтому ArmorLegate может с таким же успехом быть  переменной ref, содержащей любую броню, полученную с помощью GetEquippedObject или чего-то подобного, при условии, что новый путь к файлу в остальном тот же самый. Пример 2. Потратьте немного времени на изучение разделов Fallout на LL, и вы обязательно столкнетесь с упоминанием переменных NX, которые принимают строку для клавиш: Эти клавиши будут строками, можно применить ToString чтобы функции NX хранили строковую переменную как клавишу: Вероятно, вы создадите клавиши в виде «MyModNameString:SomeKeyString:SomeOptionalSubKeyString» (и эти двоеточия будут хорошим разделителем для sv_split), но, конечно, такие наборы текста утомляют, и иногда следует создавать NX клавиши динамически, на лету, не зная, сколько конкретно нужно. Допустим, мы хотим отслеживать конкретную комбинацию предметов одежды, которые мы экипировали, чтобы сохранить эту комбинацию: и чтобы затем экипировать эту комбинацию: И если это поменять, то можно иметь несколько таких комбинаций В зависимости от того, что вы носите, и сколько слотов отмечено каждым предметом, вы можете установить любое количество этих переменных EVFo для своего персонажа. Строковые переменные полезны тем, что они редактируемы и вам не нужно вводить все это в условия elseif, учитывая, что вы не знаете, сколько их нужно. Надеюсь, кто-нибудь перепишет MCM, чтобы всё это применить, да? 10. Переключение Между Строкой и Другими Представлениями, Функциями Символов Не буду долго тут останавливаться, и так много написал. Просто соберем эту инфу в одном месте, а то она размазана по всей документации обсе. Немного основ: вы уже знаете ToString, и, окзаывается, она очень полезна. Альтернатива – ToNumber (или вкратце: #), которая прокинет числовое значение строки в тип float\int: Это также можно сделать с шестнадцатеричными числами (hex numbers), хранящимися в строке, если добавить параметр hex bool или если строка начинается с «0x», что может перехватить динамически созданный ref. NumToHex, в свою очередь, преобразует целое число в шестнадцатеричную строку с шириной по умолчанию 8 символов, хотя вы можете указать этот параметр: Чтобы выполнить некоторые операции на уровне отдельных символов, вам необходимо передать символ из строки в его ascii-код, что можно сделать с помощью sv_GetChar: Если наша строка состоит из одного символа, мы можем просто использовать CharToAscii После передачи его ASCII-коду вы можете проверить тип символа с помощью булевых функций IsDigit, IsLetter, IsPunctuation и IsPrintable. Вы также можете переключаться между верхним и нижним регистром с помощью ToUpper и ToLower. А затем вернуть этот ASCII-код int обратно в строку с помощью ASCIIToChar: Если вы хотите выполнять такие функции и проверки для каждого символа в строке, а не для того, который вы извлекаете из его позиции с помощью sv_GetChar, вы можете использовать цикл foreach, который передает каждый символ в строковую переменную, называемую итератор, который хранит его на протяжении всего тела цикла: Команды "Break" и "continue", которые вы, возможно, помните по циклам while, здесь тоже применимы. Подробнее о циклах foreach смотрите в руководстве по массивам. И теперь, когда мы говорим о кодах типа int, GetKeyName (он же GetKey) вернет строку для клавиши клавиатуры, если вы передадите DirectX scan code как параметр int: что, как я полагаю, может быть удобно для различных локализаций или в ситуациях, когда спецификатора формата %k недостаточно. Вот и всё. Поверьте, вам редко придется использовать все эти функции, просто убедитесь, что вы четко понимаете основы и знаете, какие у вас есть варианты.
  21. Это второй из серии гайдов по нововведениям NVSE4, освежившими моддинг сцену Нью Вегаса: UDFы, строковые переменные и массивы. Моддеры Обливиона, незнакомые с ОБСЕ 16+ тоже получат пользу от этих статей. Данный гайд посвящен теме Пользовательских Функций, User Defined Functions, или же UDF. Разве вам нравится видеть, как ваши скрипты забиваются кусками кода, которые будут запускаться только изредка, при выполнении должных условий? Видеть, как большая часть вашего скрипта размазана по всему окну редактора с 5-ю уровнями отступа? Или как постоянно повторяются куски кода для каждого случая, пока скрипт просто не превратится в нечто невообразимо длинное? Кошмар! Разве не хочется хотя бы иногда отдавать часть этого на аутсорс — но тогда придется наплодить кучу скриптов заклинаний или квестовых скриптов для одноразовых задач, что только усугубляет ситуацию, да и заклинания работают только в Gamemode, а скрипты квестовых стадий не могут обрабатывать сложный код или циклы. Не говоря уже о том, что вам приходится дублировать все переменные через квествые скрипты, копируя всю эту чепуху дважды и снова потратив на это сотни строчек кода! Ну уж нет! Не теперь! NVSE4+ дает игроку возможность создавать свои собственные функции. В форме скрипта, который можно вызвать, когда это необходимо: Кого вызываем (call)? Какую-то функцию! :) Пример: если в моде есть настройки, как в меню МСМ, значения настроек по умолчанию выставлятся для всех переменных при запуске мода. И если в них используется buildref, то вызывать эту команду buildref надо каждый раз, когда начинается новая игровая сессия, на случай, если порядок загрузки изменится. Так что люди обычно привязывают это дело к квестовому скрипту, который работает каждый фрейм, в зависимости от переменных iInit или bDoonce. Не знаю как вы, но я не могу терпеть мысль, что код лежит мертвым грузом, когда инициализируется в игре, но в данный момент не нужен и просто лежит без дела. Конечно, как я уже сказал, часть его можно отрезать олдскульными методами, но как на счет просто отправить эти куски в UDF? 1. Основы UDF структуры и вызова Создайте обьектный скрипт для будущей UDF. Не прикрепляйте его ни к чему. UDF вызываются напрямую, их нельзя прилепить к вещи или НИПу. Это свободные скрипты в обьектном меню ГЕККа, и если по ним кликнуть и выбрать “use info”, вылезет только тот скрипт, который их, собственно, и вызывает. UDF могут иметь только один скриптовый блок: Function Block. И тогда главному скрипту всего лишь надо вызвать UDF с помощью init. Мило и уютно, а? Когда этот квестовый скрипт запускает и встречает UDF, который он должен вызвать, он перестает делать свою работу, затем вызывает UDF и только потом возвращается к той же точке, где его вызывал – и всё это в одном и том же фрейме. И если условие не приложимо, он просто пропускает эту строку. Но это только начало! Сейчас мы используем эти UDF как хранилища кода чтобы у нас были простые и более читабельные скрипты. А теперь давайте-ка использовать их как настоящие функции. Их же не по приколу называют Заданные Пользователем Функции (User-Defined Functions). Оригинальная игра имеет довольно много типов функций. Некоторые могут быть вызваны на референсе (функции референциальные, такие как moveto или enable), другие не могут (IsHardCore, IsPC1stPerson). Некоторые возвращают float (математические функции или GetEquipped) или референс (GetActionRef), другие ничего не возвращают (enable, disable). Некоторые включают в себя или прямо требуют параметров (GetAV), другие нет (GetCurrentAIPackage). UDF всё это могут…или не могут, зависит от того, чего вам нужно. 2. Вызов UDF на референсе и Гнездование UDF’ов В примерах выше я не вызывал UDF на референсе, поскольку в этом не было смысла. То, что происходит в этом момент аналогично вызову результирующего скрипта стадии квеста (quest stage result script), в общем аналогично одно-фреймовому что-то делающему скрипт. Но если вы хотите чтобы ваш UDF что-то сделал с референсом или вернул информацию о нем, то вы вызываете эту функцию прямо как с любыми другими обьектными функциями прямо на референсе. Как вы видите, прямо сейчас UDF работает как обьектный скрипт в рамках одного фрейма (one-frame-only), привязанный к Buddy Ref, или как скрипт заклинания (spell script), кастующийся на Buddy Ref. Подразумеваемый референс (implied reference) это тот референс, которым вызывается UDF. (Если хотите отобразить в дебаге какой референс вызвал функцию, вы найдете этот референс с помощью команды GetSelf). И если UDF включает в себя вызов другого UDF, то их взаимосвязь будет такой: Независимо от того, вызываете ли вы UDF на референсе или нет, вы можете вложить до 30 штук UDF подобным образом, хотя я считаю, что это немного перебор, и что в таких ситуациях надо пересмотреть всю структуру вашего мода. Также UDF может вызывать сама себя, если сперва линию само-вызова закомментировать и скомпиллировать скрипт. 3. Возврат Значений из UDF Если нужно UDF может возвращать значение в виде числа, формы, строки или массива. Для этого надо использовать команду SetFunctionValue:  И, конечно же, надо приготовить вызывающий скрипт чтобы адекватно возвратить значение: В случае нашей UDF я сделаю то же самое вот так: Вот то же самое с тем же эффектом. Эти штуки лучше прописывать в конце UDF, хотя, по большому счету, это зависит от структуры кода. Видите команду return? Как и в других скриптах, она оканчивает работу скрипта, и поскольку UDF срабатывает только один раз и в одном фрейме, мы вернемся напрямую к вызывающему скрипту. Очевидно, что можно проще и быстрее поставить проверки условий в начало UDF, чтобы return стоял прежде обработки более сложной части кода, если его не нужно всегда вызывать. В общем, всё как обычно. 4. Проброс Параметров в UDF Возможно вы недоумеваете, что это за фигурные скобки (accolades) после команды Begin Function? Ну, в оригинале всё жестко фиксировано в плане того, какие функции могут принимать параметры, а какие нет. А вот NVSE не знает, будете ли вы вообще их включать в свою функцию, или сколько их будет, или что это за параметры, в каком порядке и тому подобное, ока вы сами не укажете это. Именно для этого указания и нужны фигурные скобки. Вы указываете параметры, обьявляя переменные и включая их в эти фигурные скобки: Как вы видите, можно использовать конкретные значения или переменные для параметров, когда вызываете UDF, если эти самые UDF могут их хранить (can "catch" them), то есть, к примеру, переменная типа float может хранить число или другую переменную типа float, а переменная типа string, и саму строку и другую такую же переменную типа string. То же самое, но в обратном смысле, работает и с SetFunctionValue. Как только эти переменные обьявлены параметрами UDF, то есть включены в фигурные скобки, необходимо эти параметры специфицировать в том же количестве, что и в вызывающей функции, и в том же порядке: обе эти команды работать не будут: первая, потому что второй параметр должен быть int, а команда вызывает ref, а вторая, потому что параметр массива отсутствует. Пример 1. Простой пример из документации OBSE: И вызывающий скрипт будет такой: Пример 2. Некоторые оригинальные функции хранят формлисты как параметры, но остальные так не могут. IsSpellTarget – одна из таких уникумов. Тогда вызывающий скрипт будет таким: Заметьте: в таком случае в наших интересах опустить самые используемые напитки\наркотики в самый низ нашего формлиста, верно? Пример 3. Предположим, вы устали писать конкретные строки для пользовательского танца с неписем в каждом результирующем скрипте диалога в своем моде: А можно просто вызвать этот UDF: Да ерунда! Мы можем расширить этот вызывающий скрипт с помощью параметров refSufarce, fSurfaceX/Y/Z/Angle и RefMoveA/Bto. К UDF можно притянуть до 10 параметров (15 штук в NVSE 4.5 beta 1). 5. Что Происходит с Нашими Переменными в UDF Когда UDF выполняет свой код и возвращается к вызывающему скрипту, какие переменные не хранились бы в UDF, параметры уничтожаются и больше ни к чему не отсылают (don't refer to anything anymore). Переменные и значения, которые они содержали, никак не затрагиваются. Это касается всех видов параметров. Локальные переменные тоже уничтожаются\обнуляются, за исключением одной локальной переменной: переменной типа string. Строки, на которые ссылаются локальные строковые переменные UDF, не уничтожаются и продолжат находится в .nvse файле, пока вы сами их не уничтожите, что можно сделать вот так: 6. Другие Приколюхи Поскольку UDF – независимые скрипты, которые все равно отображаются как обьектные скрипты, на них можно ссылаться с помощью переменной типа ref. И делать почти то же самое, что и с остальныеми переменнами типа ref, ссылающимися на базовые формы без конкретного референса в игре (world model). То есть, UDF нельзя сдвинуть командой moveto, но её можно содержать в формлисте или массиве, возвращать её formID в дебагге с помощью %i, применять команду NX_SetEVFo, и использовать set в зависимости от условий. И коль скоро её можно хранить в переменной типа ref, на неё работает команда buildref! Это означает, что, если у какого-то другого мода есть классные UDF, которые вы хотели бы использовать, вы можете его использовать, не копируя код, не делая зависимости к моду и не беспокоится на счет порядка загрузки. 
  22. Анонс ремейка The Elder Scrolls IV: Oblivion состоится «в ближайшие недели». Об этом  сообщил инсайдер NateTheHate, а после эту информацию подтвердили источники издания Video Games Chronicle. Согласно данным инсайдера, игра, скорее всего, выйдет до июня. Но некоторые источники VGC утверждают, что релиз состоится уже в следующем месяце, то есть в апреле. Демонстрация pet-проекта, воспроизводящего стартовую локацию Обливиона на Unreal 5. По информации нескольких инсайдеров, разработкой ремейка занимается студия Virtuos, которая специализируется на поддержке крупных ААА-проектов и переносе игр на современные платформы. Она работала с такими франшизами, как Call of Duty, Tomb Raider, Dark Souls, BioShock, Battlefield, Uncharted и Horizon. Сейчас Virtuos помогает Konami в разработке ремейка Metal Gear Solid 3.  Рабочий процесс в студии Virtuos Оригинальная The Elder Scrolls IV: Oblivion вышла 20 марта 2006 года. Возможно, анонс переиздания приурочат ко дню рождения игры. Ремейк создают на движке Unreal Engine 5. Ранее появились подробности об обновлённой версии, в которой должны переработать основные механики.
  23. Прошу одобрить! https://tesall.club/tutorials/mir-fallout/modostroenie-fallout/1741271281257-nvse4-chast-2-polzovatelskie-funktsii-udf https://tesall.club/tutorials/mir-fallout/modostroenie-fallout/1741723816464-nvse-chast-3-stroki-i-strokovie-peremennie-stringvars   https://tesall.club/tutorials/mir-fallout/modostroenie-fallout/1741779604226-nvse-chast-4-massivi-i-ikh-peremennie   https://tesall.club/tutorials/mir-fallout/modostroenie-fallout/1741779729399-nvse-chast-5-obrabotchiki-sobitii-i-polzovatelskie-sobitiya-handlers-ude   https://tesall.club/tutorials/mir-fallout/modostroenie-fallout/1741780802473-nvse-funktsii-nx
  24. 12.03.25 В этой статье был надмозговый перевод. Я исправил и переписал почти всю статью. Теперь читаемость должна быть намного лучше.
  25. 12.03.25 Гайд переработан, исправлены описки, некоторая часть текста переписана. Теперь должно читаться понятнее.
×
×
  • Создать...