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

Ломаем RPG Maker XP через RGSSEval


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

Всем привет.

 

Спасибо 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 и пробовать вызывать. Для тестирования я набросал следующий скрипт:

  Показать контент

 

Запускаем и видим следующее:

  Показать контент

 

Ура, функция работает как мы и ожидали. Проверить доступные глобальные переменные мы можем через 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 'print "Hello, world!"' на получившийся код и можем запускать в главном меню игры :)

 

Итоговый скрипт:

  Показать контент

 

Итоговый скрипт на Ruby:

  Показать контент

 

Видео работы скрипта:

  Показать контент

 

Итого (или  рубрика "О проблемах"):

  • Скрипт прекрасно работает если включать его в меню, до запуска игры. Но не получится загрузить уже существующие сохранения, так как оно выполняет через дампы и нашему коду там просто не откуда взяться :) С этим можно бороться подключая код на-лету, но это уже в следующей серии.
  • Так же скрипт ломает будущие сохранения - игра не сможет загрузиться, если чит не был включен. Увы, это частая проблема игр использующих моды, а мы фактически этим и занимались.

 

Вот и всё, народ )

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

      В 27.02.2017 в 22:30, srg91 сказал:

    Всем привет.

    Показать  

    srg91, круто!!! Очень круто!!!

    Только:

    1. я бы перенёс твои статьи в раздел "Статьи для продвинутых". - тут уже нужен более продвинутый уровень в GH и программировании ИМХО.

    2. может всё-таки уроки попробуешь в видео формате (с голосом конечно) - лучше 1 раз увидеть, чем много раз прочитать.

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

      В 28.02.2017 в 05:29, Garik66 сказал:

    srg91, круто!!! Очень круто!!!

    Только:

    1. я бы перенёс твои статьи в раздел "Статьи для продвинутых". - тут уже нужен более продвинутый уровень в GH и программировании ИМХО.

    2. может всё-таки уроки попробуешь в видео формате (с голосом конечно) - лучше 1 раз увидеть, чем много раз прочитать.

    Показать  

     

    Спасибо :) 

     

    1. Да, конечно, можно и перенести. Просто мне кажется, такое ощущение из-за того, что это всё текстом. В целом ничего продвинутого вроде бы не происходит, но возможно я не правильно оцениваю. Поэтому мне кажется нет проблем, если перенесем - главное, что информация где-то есть )

    2. В целом я структурировал текст, возможно попробую на досуге еще раз записать видео. Если получится - дозалью в "Видео" и добавлю ссылочку в начало статьи.

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

    Серег, круто это) пасиб. так что у тебя со своей игрой то? дальше писать будешь?)

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

    [offtop]Это было далекие года назад, вряд-ли :) Плюс в основном писал не я, я больше по скриптам - зеркало там забабахать, сохранение инвентаря, etc. У меня упорства никогда не хватает - закончить хоть одну игру )[/offtop]

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

    ×
    ×
    • Создать...

    Важная информация

    Находясь на нашем сайте, Вы автоматически соглашаетесь соблюдать наши Условия использования.