srg91 Опубликовано 27 февраля, 2017 Поделиться Опубликовано 27 февраля, 2017 Всем привет. Спасибо Vasabist, который поднял тему RPG Maker. Он попросил сделать возможность ускорения персонажа при нажатии клавиши Shift. Мы как-то давно с другом создавали игры на RPG Maker XP и я даже писал какие-то скрипты, расширяющие возможности персонажа. Тогда уже был VX, но он нам не нравился из-за мелких моделей персонажей. В общем увидел тему, нахлынуло Сами игры на RPG Maker XP - по сути большой набор Ruby-скриптов, которые выполняются движком RPG Maker с помощью Ruby-интерпретатора. Сначала я предложил Vasabist использовать фильтры, так как повторяется ситуация с lua-играми - один код работает со множеством адресов. Но потом вспомнил, что RPG Maker есть такой класс объектов как Events, который так же позволяет выполнить произвольный код при срабатывании определенных условий. Решил по-исследовать - думал выйти на функцию eval, которой скорее всего и выполняется пользовательский код. Но всё оказалось даже проще, движок RPG Maker XP предоставляет функцию RGSSEval, которая выполняет произвольный код переданный в аргументах с доступом к глобальным переменным игры. Собственно на RGSSEval выйти оказалось очень просто. Я скачал RPG Maker XP, создал на карте Event и попросил при взаимодействии с ним вывести строку "Hello, 1234444567": Показать контент После запустил игру и попробовал найти данную строку. И она нашлась! Ставим бряк на access и активируем Event. И в памяти мы видим следующее: Показать контент Да, ребят, это оказался memcpy. Но, это не главное. Главное что мы видим смещения адресов как RGSS104E.regex_error_code_to_str+52018, а это значит в движке RGSS104E присутствуют публичные функции, которые он любезно предоставляет нам. И открыв Memory View - View - Enumerate DLL's and Symbols мы видим RGSSEval: Показать контент Осталось понять как её использовать. В целом на это довольно просто выйти, жмем два раза на функцию данном окне или просто прыгаем на неё и выполняем Memory View - Tools - Dissect Code. В появившемся окне жмем старт и видим всех, кто использует RGSSEval: Показать контент Двойным нажатием на один из (Call) под RGSSEval прыгаем на место использования и видим, что у данной функции один аргумент (один push с выполняемой строкой перед вызовом) и она не двигает за собой стек (add esp,8 - восемь из-за того, чтобы не двигать стек два раза): Показать контент Собственно, можем копировать эти call-ы в наш AutoAssembler и пробовать вызывать. Для тестирования я набросал следующий скрипт: Показать контент [ENABLE] // выделяем память на вызов функции globalalloc(run_script, 64) // выделяем память для текстовой команды на руби globalalloc(text, 1024) // Собственно просто просим вывести Hello, World text: db 'print "Hello, world!"' // наш вызов функции run_script: // передаем нашу команду push text // вызываем Eval call RGSSEval // двигаем стек за собой pop eax ret createthread(run_script) [DISABLE] dealloc(run_script) unregistersymbol(run_script) dealloc(text) unregistersymbol(text) Запускаем и видим следующее: Показать контент Ура, функция работает как мы и ожидали. Проверить доступные глобальные переменные мы можем через print global_variables, либо обратившись к какой-нибудь из известных нам (или пока только мне..) переменных напрямую, например print $game_player. Можем творить! Собственно весь код игры на Ruby можно увидеть в меню Tools - Script Editor в RPG Maker. И скорее всего код в вашем текущем проекте будет совпадать в большом количестве игр на данном движке. Поэтому можем попытаться написать обертки для стандартных функций для выполнения наших хитрых задач: Показать контент Для начала рекомендую пробовать написать обертку в своем проекте, а после внедрять её в Cheat Engine (еще лучше для начала прочитать краткий мануал по Ruby, но я так рвался закончить код что пропустил этот пункт). И сразу оговорюсь, обертки над классами будут работать только до того, как эти классы станут объектами. Поэтому наш будущий чит можно будет активировать лишь раз - до начала игры. Это поправимо, Ruby позволяет определять функции у инстансов на лету, но код становится менее лаконичным, поэтому я пошел по простому пути. Собственно для того, чтобы ускорить нашего персонажа мы должны воздействовать на атрибут move_speed класса Game_Player. Он является приватным, поэтому для взаимодействия с ним лучше сделать его видимым для всех. Опять же нам повезло и у Game_Player есть метод update, в котором мы сможем проверять нажата ли клавиша Shift, но если бы его не было, пришлось бы менять move_speed из вне. Приступим. Проще всего будет исправить Game_Player создав класс Game_Player унаследовав его от оригинала: class Game_Player < Game_Player Для изменения move_speed нам понадобится сохранять его оригинальное значение и назначать скорость бега, выделим для этого две переменные - walk_speed и run_speed. Ну и заодно сделаем move_speed публичным: attr_accessor :move_speed attr_accessor :walk_speed attr_accessor :run_speed Добавляем функции переключения скоростей и простую проверку на то, бежит ли персонаж: def start_run @move_speed = @run_speed end def start_walk @move_speed = @walk_speed end def running? return (@move_speed == @run_speed) end Осталось установить начальные значения переменным и добавить проверку на нажатый SHIFT. Для этого обернем функции initialize и update класса Game_Player с помощью функции alias: alias orig_initialize initialize def initialize orig_initialize @walk_speed = @move_speed @run_speed = 5 end Видите? Мы попросили обозначить родительский initialize как orig_initialize и вызывали его в нашей функции initialize. Скорость бега я установил на 5 (оригинальная скорость 4). Если увеличивать больше - персонаж просто летает и им неудобно управлять. Тоже самое сделаем с update. На нажатый SHIFT проверить очень просто - движок предоставляет объект Input, который знает нажата требуемая клавиша или нет (её я нашел в оригинальных скриптах игры): def update if Input.press?(Input::SHIFT) if !running? start_run end else if running? start_walk end end orig_update end Собственно и всё - скрипт готов. Если вы добавите его в оригинальные скрипты проекта, при нажатии клавиши SHIFT персонаж будет идти быстрее. Осталось просто заменить наш print "Hello, World" на получившийся скрипт. Выглядеть это будет следующим образом - каждую строку обрамляем в db 'xxxxx', где xxxx - строчка с кодом. В конце добавляем #13 #10 (каждое число через пробел) - это обычный Enter в конце строки (\n\r - если так привычнее). Я использовал для этого Sublime Text и его мультикурсоры, но можно воспользоваться простой заменой. Ну и не забываем 0 в конце, как конец текста. В итоге мы получим: Показать контент db 'class Game_Player < Game_Player' #13 #10 db ' alias orig_initialize initialize' #13 #10 db ' alias orig_update update' #13 #10 db #13 #10 db ' attr_accessor :move_speed' #13 #10 db ' attr_accessor :walk_speed' #13 #10 db ' attr_accessor :run_speed' #13 #10 db #13 #10 db ' def initialize' #13 #10 db ' orig_initialize' #13 #10 db #13 #10 db ' @walk_speed = @move_speed' #13 #10 db ' @run_speed = 5' #13 #10 db ' end' #13 #10 db #13 #10 db ' def running?' #13 #10 db ' return (@move_speed == @run_speed)' #13 #10 db ' end' #13 #10 db #13 #10 db ' def start_run' #13 #10 db ' @move_speed = @run_speed' #13 #10 db ' end' #13 #10 db #13 #10 db ' def start_walk' #13 #10 db ' @move_speed = @walk_speed' #13 #10 db ' end' #13 #10 db #13 #10 db ' def update' #13 #10 db ' if Input.press?(Input::SHIFT)' #13 #10 db ' if !running?' #13 #10 db ' start_run' #13 #10 db ' end' #13 #10 db ' else' #13 #10 db ' if running?' #13 #10 db ' start_walk' #13 #10 db ' end' #13 #10 db ' end' #13 #10 db #13 #10 db ' orig_update' #13 #10 db ' end' #13 #10 db 'end' #13 #10 db 0 Теперь просто заменяем db 'print "Hello, world!"' на получившийся код и можем запускать в главном меню игры Итоговый скрипт: Показать контент [ENABLE] // выделяем память на вызов функции globalalloc(run_script, 64) // выделяем память для текстовой команды на руби globalalloc(text, 1024) // Дописываем исходный класс text: db 'class Game_Player < Game_Player' #13 #10 db ' alias orig_initialize initialize' #13 #10 db ' alias orig_update update' #13 #10 db #13 #10 db ' attr_accessor :move_speed' #13 #10 db ' attr_accessor :walk_speed' #13 #10 db ' attr_accessor :run_speed' #13 #10 db #13 #10 db ' def initialize' #13 #10 db ' orig_initialize' #13 #10 db #13 #10 db ' @walk_speed = @move_speed' #13 #10 db ' @run_speed = 5' #13 #10 db ' end' #13 #10 db #13 #10 db ' def running?' #13 #10 db ' return (@move_speed == @run_speed)' #13 #10 db ' end' #13 #10 db #13 #10 db ' def start_run' #13 #10 db ' @move_speed = @run_speed' #13 #10 db ' end' #13 #10 db #13 #10 db ' def start_walk' #13 #10 db ' @move_speed = @walk_speed' #13 #10 db ' end' #13 #10 db #13 #10 db ' def update' #13 #10 db ' if Input.press?(Input::SHIFT)' #13 #10 db ' if !running?' #13 #10 db ' start_run' #13 #10 db ' end' #13 #10 db ' else' #13 #10 db ' if running?' #13 #10 db ' start_walk' #13 #10 db ' end' #13 #10 db ' end' #13 #10 db #13 #10 db ' orig_update' #13 #10 db ' end' #13 #10 db 'end' #13 #10 db 0 // наш вызов функции run_script: // передаем нашу команду push text // вызываем Eval call RGSSEval // двигаем стек за собой pop eax ret createthread(run_script) [DISABLE] dealloc(run_script) unregistersymbol(run_script) dealloc(text) unregistersymbol(text) Итоговый скрипт на Ruby: Показать контент class Game_Player < Game_Player alias orig_initialize initialize alias orig_update update attr_accessor :move_speed attr_accessor :walk_speed attr_accessor :run_speed def initialize orig_initialize @walk_speed = @move_speed @run_speed = 5 end def running? return (@move_speed == @run_speed) end def start_run @move_speed = @run_speed end def start_walk @move_speed = @walk_speed end def update if Input.press?(Input::SHIFT) if !running? start_run end else if running? start_walk end end orig_update end end Видео работы скрипта: Показать контент Для видео дополнительно добавил звук ускорения - $game_system.se_play($data_system.escape_se) Итого (или рубрика "О проблемах"): Скрипт прекрасно работает если включать его в меню, до запуска игры. Но не получится загрузить уже существующие сохранения, так как оно выполняет через дампы и нашему коду там просто не откуда взяться С этим можно бороться подключая код на-лету, но это уже в следующей серии. Так же скрипт ломает будущие сохранения - игра не сможет загрузиться, если чит не был включен. Увы, это частая проблема игр использующих моды, а мы фактически этим и занимались. Вот и всё, народ ) 5 Ссылка на комментарий Поделиться на другие сайты Поделиться
Vasabist Опубликовано 28 февраля, 2017 Поделиться Опубликовано 28 февраля, 2017 да какая рубрика о проблемах, тут целая статья. я вообще ничего сделать не мог, да и сейчас после прочитанного.. тоже))) Ссылка на комментарий Поделиться на другие сайты Поделиться
Garik66 Опубликовано 28 февраля, 2017 Поделиться Опубликовано 28 февраля, 2017 В 27.02.2017 в 22:30, srg91 сказал: Всем привет. Показать srg91, круто!!! Очень круто!!! Только: 1. я бы перенёс твои статьи в раздел "Статьи для продвинутых". - тут уже нужен более продвинутый уровень в GH и программировании ИМХО. 2. может всё-таки уроки попробуешь в видео формате (с голосом конечно) - лучше 1 раз увидеть, чем много раз прочитать. Ссылка на комментарий Поделиться на другие сайты Поделиться
srg91 Опубликовано 28 февраля, 2017 Автор Поделиться Опубликовано 28 февраля, 2017 В 28.02.2017 в 05:29, Garik66 сказал: srg91, круто!!! Очень круто!!! Только: 1. я бы перенёс твои статьи в раздел "Статьи для продвинутых". - тут уже нужен более продвинутый уровень в GH и программировании ИМХО. 2. может всё-таки уроки попробуешь в видео формате (с голосом конечно) - лучше 1 раз увидеть, чем много раз прочитать. Показать Спасибо 1. Да, конечно, можно и перенести. Просто мне кажется, такое ощущение из-за того, что это всё текстом. В целом ничего продвинутого вроде бы не происходит, но возможно я не правильно оцениваю. Поэтому мне кажется нет проблем, если перенесем - главное, что информация где-то есть ) 2. В целом я структурировал текст, возможно попробую на досуге еще раз записать видео. Если получится - дозалью в "Видео" и добавлю ссылочку в начало статьи. Ссылка на комментарий Поделиться на другие сайты Поделиться
Vasabist Опубликовано 28 февраля, 2017 Поделиться Опубликовано 28 февраля, 2017 (изменено) Серег, круто это) пасиб. так что у тебя со своей игрой то? дальше писать будешь?) Изменено 28 февраля, 2017 пользователем Vasabist Ссылка на комментарий Поделиться на другие сайты Поделиться
srg91 Опубликовано 1 марта, 2017 Автор Поделиться Опубликовано 1 марта, 2017 [offtop]Это было далекие года назад, вряд-ли Плюс в основном писал не я, я больше по скриптам - зеркало там забабахать, сохранение инвентаря, etc. У меня упорства никогда не хватает - закончить хоть одну игру )[/offtop] Ссылка на комментарий Поделиться на другие сайты Поделиться
Рекомендуемые сообщения