понедельник, 20 августа 2012 г.

Добавление и прокачка монстров в MuOnline клиенте

Уже давно заметил что многие задаются вопросом: каким образом можно добавить новых монстров в игру? но ответа на него в большинстве случаев не даётся, а если и даётся то в ход идут только альтернативы, типо замени не используемого моба и т.п. А ведь по серверной части всё просто, добавил строчку в Monster.txt и дело в шляпе, но вот с клиентом такое не прокатит. Что не делай, что не подставляй, один хрен вместо желаемого моба появится бычок) Как то давненько и меня заинтересовал этот вопрос, я тогда еще толком неумел обращаться с отладчиком, но всё-таки полез с надеждой найти и изменить лимит загрузки мобов как это делается с лимитом вещей. Соответственно ничего у меня не вышло и я забил) Потом спустя какое-то время я набрался немного знаний и полез опять, как выяснилось механизм загрузки мобов оказался куда сложнее чем я думал. Но добавить новых мобов у меня уже как-никак получилось и мало того протюнинговать их, однако интерес пропал и я опять забил оставив пылится сырой код. Но совсем недавно ко мне вернулся стимул и я допилил код, который сейчас и опубликую. (на этом моменте представьте барабанную дробь)

В исходнике представлены следующие реализации :
1. Добавление собственных монстров
2. Изменение параметров монстров (размер, тип, цвет и т.п.)

Есть важный момент, код который я выкидываю в оригинальном виде скорее всего не будет работать на всех версиях main'ов, потому что структура мобов(Unit_Struct) в памяти  на разных main'ах может быть разная. Это касается только реализации изменений параметров монстров, на добавление эта структура не влияет. Но ничего страшного, как подогнать структуру под main я поверхностно поясню далее.

Вот сам код :
MonsLib.h - link
MonsLib.cpp - link
Hook.h - link
Hook.cpp - link

В исходнике оффсеты и структуры для main'ов 1.03.25(1.03y) JPN и 1.05.50(1.05x+) KOR. Причем тестировался код только на 1.03, оффсеты и структуры для 1.05 я взял со старого исходника, где они работали, но я не тестил их на новом коде, думаю нет причин чтобы они не работали.

Как это работает
* Названия функций взяты из исходника

Добавление монстров работает достаточно просто, в main'e существует функция, назовём её LoadMonster(id), она вызывается при появлении на экране моба, в качестве аргумента получает его id, затем выполняет какие-то действия и решает каким образом грузить монстра. Именно эта функция решает какая модель будет загружена и кидает вместо новой модели бычка. В ней перехватывается 2 процедуры загрузки бычка(zero-bull), подставляются нужные значения и новый моб успешно загружен. Первая процедура HOpenMonsterModel(id) открывает модель, загружает bmd, текстуры, звуки, выбирает какие специфические параметры нужно сделать для модели и т.п. Затем вызывается вторая процедура HConstructMonsterModel() как я понимаю она уже конфигурирует модель для вывода на экран. Оффсеты этих 2х процедур есть в исходнике.

Изменение монстров немного сложнее, тут уже есть кое какие подводные камни, параметры мобов представлены в памяти в виде некой структуры (в коде Unit_Struct), эта структура содержит всё что касается визуализации модели: тип, цвет, имя, размер, анимации, эффекты, доп. оружие и много чего другого, данная структура используется как для мобов так и для моделей игроков. Как я уже сказал эти индивидуальные параметры мобам вещаются в процедуре HOpenMonsterModel, но изменять в ней параметры конкретного моба было бы очень не удобно, потому что там описываются параметры для разных мобов типо if else. Поэтому я решил вносить изменения в мобов после вызова этой функции и нашел подходящее местов в самом низу LoadMonster(). Там вызывалась некая процедура HSetMonsterNameStyle которая устанавливала для модели стиль имени, а что самое главное, в качестве аргумента получала ID моба и указатель на его структуру Unit_Struct.

Подводные камни

Проблема переносимости
Дело в том что сама структура Unit_Struct в разных main'aх может быть разная, например в 1.03.25(1.03y) JPN и 1.05.50(1.05x+) KOR она отличается. Поэтому подогнать структуру под свой main необходимо самому иначе внешний вид мобов изменятся не будет или частично не будет. Я это делаю через ольгу, указатель получаю из функции HSetMonsterNameStyle, перехожу в память и методом подстановки(тыка) нахожу нужные значения. Это совсем не сложно.

Распределение памяти
Каждая модель в main'е имеет свой Index(ResInx) который передаётся в функцию HLoadBMDModel, этот Index для каждой модели является постоянным и указывает на элемент некого массива структур или классов модели. Первоначально новым монстрам я добавлял Index идущий за последним стандартным мобом, но как оказалось эти индексы могли использоваться другими моделями поэтому и чтобы не трогать чужое пространство я задействовал индексы с конца массива, которые скорее всего не используются. Но массив этот из-за странной реализации постоянно имеет переменный размер(~10к-11к), поэтому пришлось писать кое какие расчёты чтобы не вылазить за границы блока памяти. Вроде такая реализация сбоев пока не давала и по идее должна работать без проблем, однако 100% уверенности нету поэтому и пищу.

Конфигурация

Добавление монстра
Ну тут я думаю всё понятно, структура MonsterAdd_Struct:
dwMobID - id монстра из Monster.txt
lpDir - директория где лежит моб
lpFile - имя файла модели монстра, без .bmd

Изменение параметров монстра
Изменять любых монстров, как новых, так и стандартных. Структура MonsterParam_Struct:
dwMobID - id юнита из Monster.txt
lpName - имя юнита, если NULL то не меняется
bMobType -тип юнита (0,1-unit, 2-monster, 3-can't select, 4-NPC), если NONE то не меняется
bPKStatus - пк статус юнита (0-7), если NONE то не меняется
bMoveOut - эффект, юнит убегает вперёд (0-1), если NONE то не меняется
sItem1ResID - id ресурса модели, если NONE то не меняется
bItem1IsExcl -переливается ли модель как excellent, если NONE то не меняется
bItem1JoinID - join модели юнита к которому прекреплена модель sItem1ResID, если NONE то не меняется
sItem2ResID - id ресурса модели, если NONE то не меняется
bItem2IsExcl - переливается ли модель как excellent, если NONE то не меняется
bItem2JoinID - join модели юнита к которому прекреплена модель sItem1ResID, если NONE то не меняется
float fSize - размер модели(стандартно 1.0), если NULL то не меняется

Я вывел в конфиг sItem1ResID и sItem2ResID как параметры используемые для вставки в руки мобов оружия, однако ресурсы моделей вещей нужно искать в main'e самому, тоже самое касается подбора необходимого join'а в модели, потому как в разных моделях разные join'ы. Кстати ID ресурсов вещей можно найти там же где и фиксится кол-во их загрузки.

Небольшая демонстрация

Yeti разных размеров
Yeti с разными PC статусами и типом unit
Модифицированные собачки
Новый NPC
Новый моб Тиранозавр
Еще Тиранозавр
Изменённое оружие и щит у Hommerd'a
Изменённое оружие у Tantalos'a

Заключение

Я лишь привёл пример как использовать самое основное, если поковырять найденные процедуры, то можно еще много чего навешать на мобов, звуки, отблески, эффекты, прозрачные текстуры и т.п. Если поизучать структуру Unit_Struct, так же можно мобов обвешивать шмотом, снаряжать их эффектами, аурами, анимациями, менять цвет, делать из них необычных юнитов, превращать в персонажей. Тут всё упрётся лишь в ваши фантизии и желание изучать.

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

1 комментарий:

  1. Hello My Friend, I use your source for two years or more, Always use only for modify monsters, but right now i'm trying to add New monsters, I have a 1.05X+ Main, Season 4 Kor, all the source dor modify mobs works perfectly, but when I try to add new monster, the client crash.
    I really don't know nothing about programming, and checked offsets (comparing whit 1.03Y JPN) and all looks fine, but can't figure out why the main crash when the new mob appears on screen.
    I know this is very old proyect, but.. well, i don't know what can i do now...
    Thanks on advance.

    ОтветитьУдалить