Сделал класс по фэншую почти:
- Код: Выделить всё
class type_AI_spellcaster
{
public:
type_AI_spellcaster* (__thiscall** VMT)(void*, char); // 0x000
Hero* current_hero; // 0x004
Hero* enemy_hero; // 0x008
long our_group; // 0x00C
long enemy_group; // 0x010
long enemy_can_attack; // 0x014
long can_be_attacked; // 0x018
bool win_likely; // 0x01C
bool is_creature_spell; // 0x01D
type_AI_combat_parameters estimate; // 0x020
type_AI_spellcaster *enemy_caster; // 0x048
bool owns_enemy_caster; // 0x04C
type_AI_enemy_data melee_enemies[20]; // 0x050
type_AI_enemy_data ranged_enemies[20]; // 0x190
type_AI_enemy_data worst_enemies[20]; // 0x2D0
// 0x410
inline int get_attack_skill_value(Army* const army, Army* const enemyArmy, int spellDuration, int effect) const {
return CALL_5(int, __thiscall, 0x437450, this, army, enemyArmy, spellDuration, effect);
}
inline int get_defense_skill_value(Army* const army, int spellDuration, int effect) const {
return CALL_4(int, __thiscall, 0x438560, this, army, spellDuration, effect);
}
int (type_AI_spellcaster::*type_AI_spellcaster::get_enchantment_function(SpellID spell) const)(Army* const, type_enchant_data) const;
int get_poison_value(Army* const army, type_enchant_data data) const;
int get_disease_value(Army* const army, type_enchant_data data) const;
int get_age_value(Army* const army, type_enchant_data data) const;
int get_fear_value(Army* const army, type_enchant_data data) const;
int get_death_blow_value(Army* const army, type_enchant_data data) const;
int get_drain_life_value(Army* const army, type_enchant_data data) const;
int get_toughness_value(Army* const army, type_enchant_data data) const;
int get_behemoths_claws_value(Army* const army, type_enchant_data data) const;
int unimplemented(Army* const army, type_enchant_data data) const;
};
А вот, как нужно получать адрес метода (еле нашёл в сети, как это сделать, столько мусора по теме):
- Код: Выделить всё
int __stdcall get_enchantment_function(HiHook* h, type_AI_spellcaster* caster, SpellID spell)
{
int (__thiscall type_AI_spellcaster::*value_func)(Army* const, type_enchant_data) const = caster->get_enchantment_function(spell);
switch (spell)
{
case SPELL_POISON:
case SPELL_DISEASE:
case SPELL_AGE:
case SPELL_FEAR:
case SPELL_DEATH_BLOW:
case SPELL_DRAIN_LIFE:
case SPELL_TOUGHNESS:
case SPELL_BEHEMOTHS_CLAWS:
return (int&)value_func;
case SPELL_EXPLOSION:
// get_damage_spell_value
return 0x436BB0;
default:
return CALL_2(int, __thiscall, h->GetDefaultFunc(), caster, spell);
}
}
Код выходит на новый уровень
Вот с таким уже можно менять любую функцию взвешивания. По сути, у нас оригинальный класс (за исключением неиспользуемых в плагине методов) и работаем мы с ним в точности так, как это делает оригинальный код. Нет больше никаких __fastcall и unused_edx, только родной __thiscall
* * *
Глядя на функции взвешивания хочется плакать. Это такая аппроксимация. Единственное оправдание этому то, что в 1999-м году очень важно было экономить время CPU. Если кто помнит, Герои 3 были довольно требовательной игрой и много кого разочаровали потому, что не хотели нормально работать на машинах, которые тянули Героев 2. Я думаю, именно поэтому такие упрощения. Много упрощений и есть косяки (отсутствуют банальные проверки, симуляция идёт против какого-то идеального отряда, который юнит даже атаковать не собирается и т.п.). Поэтому чуток поправить не повредит. Понимаю, смысла нет особого, все давно привыкли нагибать AI и считать его баги фичами, но посмотреть всё равно хочется, на что он способен с минимальными правками.
И это только начало. Наверняка есть новое поколение геройщиков-моддеров (пусть и не такое многочисленное), которое не знает, как сделать что-то, что бы превзошло все достижения прошлого
Казалось бы, всё давно разобрано на винтики и гаечки (хотя бы той же HotA Crew), а я скажу, что к AI даже и не прикасались, в том числе и HotA Crew. Так что, дерзайте. Впереди ещё один дивный мир под названием AI.
* * *
Можно ли считать программистов Героев 3 гениями из-за того, что мы копаемся в их коде спустя 20 лет? Можно, но не поэтому и не их
Квадратный корень всегда более сложная процедура, чем возведение в квадрат. Программисты NWC просто реализовывали идеи на С++: нужен метод - пишем метод
По сути реверсеры начинают с минимальной информацией о коде и далеко бы не продвинулись, если бы не дизассемблеры, а декомпиляторы - это и вовсе подарок. А теперь у нас есть исходный код RoE (ок, почти, но разница очень маленькая). Гении - реверсеры. Которые смогли создать WoG (пусть я и недолюбливаю WoG, но я играл в WoG, там просто людей накрыло от возможностей и они начали добавлять всё, что можно добавить), Era и HotA. ОК, добавлю MoP (тоже примечальный мод). Спасибо Вам за это
Реально, пройденный путь - это
и
Но теперь стоит всё-таки изучить дампы
void_17 и придать хаосу некое подобие порядка.
Я допускаю, что у HotA Crew такой дамп уже был (всё-таки неглупые люди в команде), но для всех остальных моддеров, эти дампы - бесценный дар. Да, есть отличия кое-где, другой UI и нет RMG, но ядро оригинальное, а это уже больше, чем можно хотеть в отсутствие исходного кода. Я думаю, что моддеры после НГ чуток поправят здоровье и перепишут свой код с "догадками", чтобы он обрёл наконец настоящий смысл. Я никогда не увлекался Героями так сильно, чтобы пробовать писать что-то вроде Era, но я уже вижу, как Эру бустят эти дампы.
* * *
Добавлю про
void_17. Мне всегда его вопросы казались странными и оторванными от темы. Если честно, я думал, что человек вообще далёк от темы и только учится
А оказывается, он делал то, что я считаю революцией 2021-го в моддинге Героев 3. Что ж, хочется и себя похвалить ленивого: я смог убедить, что код RoE стоит внимания и усилий. Но если копнуть глубже, то оригинальные посты о базе Dreamcast принадлежат
Херомант'у. Просто никто из нас не понимал/ленился разбираться в том, что может дать дебажная инфа в Dreamcast для моддинга в целом (которая не лежала на поверхности, между прочим). А
void_17 взял и разобрал, так что и мне стало стыдно
Теперь, конечно, моддинг сильно упрощается. Я могу посмотреть любое поле/метод класса, параметры методов и т.п. и восстановить сигнатуры со 100% точностью такими, какими они были в RoE. Ну, а допилить до SoD, я думаю, не проблема. Всё-таки реверсеры мы, чи не?