Поставил игруху посмотреть, а вышла статья про то - как поменять правила игры на свои ;-) В общем, мы исправим некоторые механики игры, показавшиеся мне занудными. К примеру - чтобы построить стену, нужно хренову кучу раз стукнуть молотком, молоток в это время - неумолимо быстро портится, а разборка элементов вообще не работает (возможно у меня), исправим. Ну и до кучи: патроны, здоровье, усталость, голод, жажда, выносливость, износ вещей, топливо, максимальный переносимый вес, вычитание ресурсов при строительстве.
И понадобится нам только dnSnpy. https://github.com/dnSpy/dnSpy
Ну и блокнотик, чтоб куски кода и названия классов запоминать.
Игра на unity, а значит работать будем с ..\Zompiercer v20.0t\Zompiercer_Data\Assembly-CSharp.dll
Заархивируем оригинальную Assembly-CSharp.dll
Запускаем dnSpy, открываем ..\Zompiercer v20.0t\Zompiercer_Data\Assembly-CSharp.dll
Начнём наверное с урона персу. Если немного подумать, то здоровье должно где то вычитаться и переменная или структура её содержащая, может называться как: health, currentDamage, playerHealth и т.п. Поищем Health. Тыкаем в верху в значок поиска,а ниже в строку поиска вводим health
и видим среди найденных строк, пару подходящих:
currentHealth ZombieFighterIndicatorsBar
currentHealth ZombieHP
ZombieHP - здоровье зомби, следует из названия, не интересует, а вот currentHealth ZombieFighterIndicatorsBar - проверим.
Щёлкаем правой мышкой по строке currentHealth ZombieFighterIndicatorsBar и в контекстном меню "Анализировать"
Развернём список "Назначается в" и видим среди функций (методов классов) одно со словом Damage (урон)
Двойной клик по ZombieFighterIndicatorsBar.CauseDamage(float, bool, bool, DamageType) и в окне выше откроется его код,
с выделенной строкой this.currentHealth -= damage;
и видим, из нашего currentHealth вычитается -= содержимое переменной damage, передаваемой в функцию как параметр CauseDamage(float damage,
Выделим мышкой весь код функции CauseDamage(){...} и сохраним в текстовый файл. Особо не мудря, просто закомментируем ( // ) и посмотрим что получилось. Тыкаем правой мышкой по названию функции и в контекстном меню, "Изменить метод C#"
В открывшемся окне "Изменение кода" - закомментируем строку вычитания здоровья //this.currentHealth -= damage; и нажмём кнопку "Компилировать"
Строка исчезла, dnSpy удаляет комментарии из кода. Нажимаем вверху кнопку сохранить (две дискетки), а в окне сохранения оставляем имя Assembly-CSharp.dll , нажимаем ОК изменения применены.
Теперь выносливость. По аналогии с currentHealth сразу ищем currentStamina. Поиск, currentStamina в строку поиска и один результат
currentStamina класса ZombieFighterIndicatorsBar.
Двойной клик по нему, прокрутим в начало код класса
public class ZombieFighterIndicatorsBar и посмотрим что содержит.
По аналогии с this.currentHealth -= damage; из функции ZombieFighterIndicatorsBar.CauseDamage поищем this.currentStamina -= и найдём его в функции
public void CauseFatigue(float fatigue)
{
this.currentStamina -= fatigue;
}
Выделим мышкой весь код функции CauseFatigue(){...} и сохраним в текстовый файл.
А что делаем - уже понятно. Правой мышкой по названию функции CauseFatigue и "Изменить метод C#"
Закомментируем строку this.currentStamina -= fatigue;
Кнопку "Компилировать" и вверху кнопку "Сохранить" (две дискетки), имя файла не меняем.
Запускаем игру и усиленно бегаем, выносливость (stamina) не тратится. Заметьте, прыжок по прежнему тратит выносливость, его патчить не будем, от прыжков толку нет. Но вы можете потренироваться сами, поискав Jump класса ZombieFighterController
Далее износ вещей.
Еслы взять нож на втором этаже, в комнате рядом с лестницей и потыкать им в стены, то на индикаторе состояния предмета видно его заметное уменьшение, исправим это. Будем искать durability (долговечность, прочность,живучесть). В dnSpy вносим в строку поиска durability и видим в списке присутствует такая функция, SubtractWeaponDurability что в переводе - вычитание прочности из оружия. Двойной клик по ней, смотрим её код.
Выделим мышкой весь код функции SubtractWeaponDurability(){...} и сохраним в текстовый файл.
Код совсем мал, а внутри вызов функции модицикации прочности .ModifyDurability(value);
Особо мудрить и тут не будем и впишем в начале возврат из функции. Правой мышкой по названию функции SubtractWeaponDurability и "Изменить метод C#" И сразу за открывающей фигурной скобкой пишем return;
Жмём кнопку "Компилировать"
B функция станет пустой
public void SubtractWeaponDurability(float value = -1f) { }
dnSpy определил - что после return код не выполняется и убрал его. Если закралась ошибка, вверху есть значёк - отменить.
Вверху кнопку "Сохранить" (две дискетки), имя файла не меняем. Запускаем игру, тыкаем ножом в стены и прочность не расходуется. Должно работать для всех предметов и оружия.
Если нажать в игре клавишу G - то в окне "снаряжение" в низу, видны индикаторы: жажды, голда и сна. И как довольно быстро они уменьшаются, поправим это.
Жажда. аглицкая thirst. Значит ищем чтото типа currentThirst, пишем в строку поиска. Получаем всего несколько строк.
Среди них видим:
_currentThirst класса Zompiercer.GUI
currentThirst класса ZombieFighterIndicatorsBar
...
Все предыдущие переменные изменялись в класе ZombieFighterIndicatorsBar, выбираем currentThirst класса ZombieFighterIndicatorsBar
Правый клик, анализировать.
Развернём список "Назаначается в" для ZombieFighterIndicatorsBar.currentThirst
Видим несколько функций, среди которых слово Cause - как в предыдущих (CauseDamage, CauseFatigue)
CauseThirst - двойной клик открывает код функции.
И видим там ровно тоже - что и ранее, знакомое вычитание
this.currentThirst -= thirst;
Выделим мышкой весь код функции CauseThirst(){...} и сохраним в текстовый файл.
Правой мышкой по названию функции CauseThirst и "Изменить метод C#"
Закомментируем //this.currentThirst -= thirst;
Кнопку "Компилировать" и вверху кнопку "Сохранить" (две дискетки), имя файла не меняем.
Сразу проделаем тоже для голода и сна.
Голод - hunger, значит ищем currentHunger. Пишем в строку поиска currentHunger.
Выбираем currentHunger класса ZombieFighterIndicatorsBar
Анализировать, развернуть - "назначается в". Видим среди строк слово Cause.
ZombieFighterIndicatorsBar.CauseStarve(float) двойной клик.
А далее всё тоже. Правой мышкой по названию функции CauseStarve и "Изменить метод C#"
Закомментируем //this.currentHunger -= starve;
Кнопку "Компилировать" и вверху кнопку "Сохранить" (две дискетки), имя файла не меняем.
Сон. Ну то есть, ищем currentSleep , и результат один (не считая пары функций от класса Zompiercer.GUI - их игнорируем).
Анализировать, развернуть - "назначается в".
ZombieFighterIndicatorsBar.CauseSleep(float) двойной клик.
Правой мышкой по названию функции CauseSleep и "Изменить метод C#"
Закомментируем //this.currentSleep -= sleep;
Кнопку "Компилировать" и вверху кнопку "Сохранить" (две дискетки), имя файла не меняем.
Запускаем игру, проверяем. Смотрим по G проценты жажды, голода, сна.
Бегаем (прыжки не патчены), тыкаем зомбей ножиком, тратим энергию. Проценты не меняются, порядок.
Переносимый вес. Если вес - это аглицкий weight, то переменная видимо currentWeight, ищем.
Один результат currentWeight класса Zompiercer.Inventory.Inventory
Анализировать, развернуть - "назначается в". Один результат.
Zompiercer.Inventory.Inventory.RecaculateWaght() двойной клик, смотрим код функции RecaculateWaght
А там , сначала текущему весу устанавливается 0 this.currentWeight = 0f; , а далее вычисляется и присваивается реальное значение.
Правой мышкой по названию функции RecaculateWaght и
"Изменить метод C#"
Закомментируем весь код кроме строки this.currentWeight = 0f;
Не ошибитесь с парными скобками {}
Функция станет из одной строки
public void RecaculateWaght()
{
this.currentWeight = 0f;
}
Кнопку "Компилировать" и вверху кнопку "Сохранить" (две дискетки), имя файла не меняем.
Запускаем игру, набиваем инвентарь всем - что найдём, смотрим на нулевой вес, по клавише Tab, и если предупреждений нет, работает.
Идём к поезду, по пути раздавая люлей зелёным мордам. Смотрим количество топлива.
Тут нам понадобится значение fuel - топливо. Пишем в строке поиска fuel и видим кучу найденных строк. Но мы быстро разберёмся что нужно, по слову Current. А с current у нас всего три строки, одна из них currentFuelAmount - текущее количество топлива.
А ещё мы замечаем функцию со знакомым словом Cause ,
CauseFuelConsumption класса TrainFuelTank
Но сначала проанализируем переменную currentFuelAmount.
Анализировать, развернуть - "назначается в".
Видим функцию CauseFuelConsumption, двойной клик, смотрим код.
Там одна строка this.currentFuelAmount -= consumption * Time.deltaTime; которую мы закомменируем.
Правой мышкой по функции RecaculateWaght и "Изменить метод C#"
//this.currentFuelAmount -= consumption * Time.deltaTime;
Кнопку "Компилировать", "Сохранить" (две дискетки), имя файла не меняем.
Запускаем игру, разгоняем поезд и видим отсутствие изменения топлива.
Доехали до станции, припарковали локомотив - чтоб горловина бака была напротив заправки, включили заправку, заправили всё что было.
Смотрим, добавление топлива работает.
Ресурсы. Ресурсы должны быть налутаны. Смотрим сколько ресурсов есть, запоминаем. Закрываем игру.
Логично предположить, что ресурсы обитают в инвентаре. В dnSpy, в левом окне "Обозреватель сборок" смотрим, что у нас есть со словом Inventory. Есть класс Zompiercer.Inventory с разными методами, один из них
InventoryItem - элемент инвентаря, щёлкнем в него, справа откроется его код. Полистав функции класса, найдём
public void ExtractAmount(int value) где присутствует строка
this.amount -= value; , похоже на предыдущие верно?
Щёлк правой мышкой по amount, анализировать, развернуть - "назначается в". Другие функции похоже не подходят, пробуем с этой, ExtractAmount. Правой мышкой по названию функции ExtractAmount и "Изменить метод C#"
Закомментируем строку //this.amount -= value;
Кнопку "Компилировать", "Сохранить" имя файла не меняем.
Запускаем игру. Идём в поезд и создаём на верстаке молоток и разводной ключ, наблюдая не уменьшение ресурсов. Выбираем по клавиши B меню строительство, полы и строим пол, молоток не изнашивается но требуется много ударов для строительства, а крыша строится ещё дольше.
А разборка объекта у меня не работает. Скорректируем скорость строительства и починим разборку.
Строительство производится на поезде, посмотрим в левом окне класс поезда - Train. Есть Zompiercer.Train
В его иерархии классов и методов, присутствует TrainPart - часть поезда. Щёлкнем по нему, развернём его иерархию, видим там функцию
ConstructWithTool, щёлкаем её и видим код справа. Внутри присутствуют строки сообщений - "Construction complete" (строительство завершено) и "Part deconstructed" (часть разобрана).
А в начале строка this.BuildPointsCurrent += value;
прибавляющее к BuildPointsCurrent (текущая точка строительства) значение value. Возможно это сложение участвует в постройке.
Щёлкнем правой мышкой по ConstructWithTool, анализировать, развернуть - "используется в".
Видим две функции: MeleeAssemblyTarget() и MeleeDisassemblyTarget() (сборка, разборка)
Если this.BuildPointsCurrent += value; - это циклы постройки объекта, попробуем его ускорить. Правой мышкой по названию функции ConstructWithTool и "Изменить метод C#" , увеличим скорость постройки в 4 раза. Допишем * 4f, this.BuildPointsCurrent += value * 4f;
А чтоб разборка происходила сразу, ниже
else if (this.BuildPointsCurrent <= 0f) меняем на просто else
"Компилировать", "Сохранить" , имя файла прежнее.
Запускаем игру и снова пробуем что либо построить. Постройка происходит заметно быстрее, а разборка махом.
Ну что, осталось вычитание патронов, идём в будку охраны, рядом с депо, берём там в шкафу ключ от кладовой, возвращаемся в жилое здание, кладовая на первом этаже, берём лук и стрелы. Сохраняем игру, закрываем.
В строке поиска набираем ammo. Среди найденных строк, виднеется GetAmmoItem() - получение боеприпасов.
Анализировать, развернуть - "используется в".
Видим, что она используется в некой Gun.SubtractThrownObjects() - то есть вычитание объектов в классе Gun - оружие.
Двойной клик по Gun.SubtractThrownObjects , видим код функции.
Там есть строка ammoItem.inInventory.Extract(ammoItem, 1); извлечь одну единицу боеприпаса из инвентаря. Закомментируем её. Правой мышкой по названию функции SubtractThrownObjects и "Изменить метод C#"
"Компилировать", "Сохранить", имя файла прежнее.
Проверим позже, а сейчас до кучи, улучшим стрельбу из лука, пишем в поиск Shot - выстрел и смотрим чого нашлось. В результатах есть функции
TryShot класса Bow и Shot класса Gun. Поправим обе. Двойной клик по TryShot, смотрим код.
В коде присутствует строка вычитания из магазина this.gun.magazine--; , закомментируем.
Правой мышкой по названию функции TryShot и "Изменить метод C#"
"Компилировать"
И давайте сделаем то же для оружия, пишем в поиск Shot и ищем в результатах функцию Shot класса Gun Двойной клик, смотрим код.
Примерно в середине функции, присутствует строка this.magazine--; закомментируем.
Правой мышкой по названию функции Shot и "Изменить метод C#"
"Компилировать", "Сохранить", имя файла прежнее.
Запускаем игру и стреляем из лука, стрелы (и патроны) не тратятся, и из лука можно стрелять быстро но не далеко, или далеко но не быстро ;-)
----- патченная Assembly-CSharp.dll ------
Assembly-CSharp_patch.zip
сохранить оригинальную Assembly-CSharp.dll
распаковать из Assembly-CSharp_patch.zip в
Zompiercer v20.0t\Zompiercer_Data\Managed\
------------------------------------------------
Вроде всё, пока.