Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Мод на ХотА
DF2 :: ФОРУМЫ > Игровые форумы > Heroes of Might & Magic III > Моды
Страницы: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12
DedMorozzz
хм, я ж не перетираю модификатор, а увеличиваю его
Т.е. не а = 100, а "а = а+100"

А в целом идея думаю ясна:
есть навык дипломатии. Есть модификатор силы армии.
И что бы логику изменения держать в 1м месте, для простоты редактирования и читаемости - геттер модификатора вынес в ф-ю
В ней ожидаю все изменения считать. В том числе и артов (пусть в виде доп ф-ии, но не в том суть)
Главное что всё в 1м месте
AlexSpl
В коде посмотрите, как часто функция подсчёта альфы вызывается. Каждый раз, когда она вызывается, Вы делаете это: "а = а+100".
DedMorozzz
Цитата(AlexSpl @ 22 Dec 2016, 21:59) *
В коде посмотрите, как часто функция подсчёта альфы вызывается. Каждый раз, когда она вызывается, Вы делаете это: "а = а+100".

Так всегда одинаковое значение должно быть. Ибо я указываю что модификатор = константным значениям на основании уровня навыка
И когда кто либо попросит "а дай мне значение модификатора" оно всегда будет одинаковым. И спрашивать будет в 1м месте. Не прийдётся собирать по разным функциям

PS: я кажется понял о чём речь... для разных функций *(int*)(c->edx + 124 + (i << 3) + 4) будет содержать разный контент?
По аналогии с дипломатией. Где-то edi, где-то esi, а в 3м месте вообще ebp
А я для всех вариантов юзаю edx в расчёте что там будет айди арта

В этом проблема?
AlexSpl
Итак, есть код:

getAlpha();
getAlpha();
getAlpha();

Есть хук на getAlpha(), в котором мы пишем: Mod += 10 (например). После выполнения этого кода Mod будет равен 30, но должен быть равен 10.

Цитата
*(int*)(c->edx + 124 + (i << 3) + 4) будет содержать разный контент?

И это тоже. У LoHook'ов разный контекст. В одном c->edx будет возвращать адрес героя, в другом с->edx может хранить горизонтальное разрешение экрана вообще.
DedMorozzz
Цитата(AlexSpl @ 22 Dec 2016, 22:20) *
Итак, есть код:

getAlpha();
getAlpha();
getAlpha();

Есть хук на getAlpha(), в котором мы пишем: Mod += 10 (например). После выполнения этого кода Mod будет равен 30, но должен быть равен 10.

Цитата
*(int*)(c->edx + 124 + (i << 3) + 4) будет содержать разный контент?

И это тоже. У LoHook'ов разный контекст. В одном c->edx будет возвращать адрес героя, в другом с->edx может хранить горизонтальное разрешение экрана вообще.


Вот чесно - не пойму. До добавления артов всё работало корректно.
А я по сути только добавил в подсчёт модификатора ещё и арты. Т.е. глобальных изменений не делал

Цитата
И это тоже. У LoHook'ов разный контекст. В одном c->edx будет возвращать адрес героя, в другом с->edx может хранить горизонтальное разрешение экрана вообще.

Вот, а вот это понимаю, это сходится у меня в голове)

Но тогда другая проблема. Если делать так: _PI->WriteLoHook(0x44A985, setArmyValueArt);
тогда я не смогу корректно увеличить силу армии на 5% от базовой. Потому что буду увеличивать на 5% от уже модифицированной дипломатией
Соотв надо разные блоки "*(int*)(c->edx + 124 + (i << 3) + 4) " под каждый тип (превью, нападение, деление на паки) делать?
AlexSpl
Про контекст. Поставьте брейкпоинт по любому из адресов ваших хуков, запустите игру и сделайте так, чтобы он сработал. Содержимое регистров (general registers, FPU registers и т.п.) справа - и есть контекст.

Цитата
Но тогда другая проблема. Если делать так: _PI->WriteLoHook(0x44A985, setArmyValueArt);
тогда я не смогу корректно увеличить силу армии на 5% от базовой. Потому что буду увеличивать на 5% от уже модифицированной дипломатией
Соотв надо разные блоки "*(int*)(c->edx + 124 + (i << 3) + 4) " под каждый тип (превью, нападение, деление на паки) делать?

Вы хотите игнорить модификатор Дипломатии, когда есть арт у героя? Т.е. если есть арт, то не учитывать навык Дипломатии вообще?
DedMorozzz
Цитата(AlexSpl @ 22 Dec 2016, 22:30) *
Вы хотите игнорить модификатор Дипломатии, когда есть арт у героя? Т.е. если есть арт, то не учитывать навык Дипломатии вообще?


Нет. Вот что получится если ставить два хука. Последний (увеличение силы армии от артов) после всех предыдущих просчётов:
базовая сила армии 100 едениц
Цель. Дипломатия даёт 20%. И арты по 5%. Итого если есть дипломатия и 1н арт, должно получится 25%
А если делать разными хуками, то выйдет следующее:

силаАрмии = 100;
силаАрмии = силаАрмии * 1.2 //бонус от дипломатии (120 получится)
силаАрмии = силаАрмии * 1.05 //бонус от артефакта (126 получится)

Т.е. я получу 126, вместо 125 ожидаемых
AlexSpl
Понятно. Тогда каждый раз, когда Вы модифицируете силу армии, должна вызываться функция подсчёта "чистой" силы армии, и только потом следует умножать полученное значение на модификатор. Причём старые бонусы должны аккумулироваться. Надо подумать.


* * *

Во всех LoHook'ах у нас есть адрес структуры героя. Соответственно, мы можем учесть и уровень Дипломатии и арты. Нужно убирать последний LoHook и дублировать его логику в остальных.

* * *

Скиньте под спойлер рабочий код, я для каждого LoHook'а напишу адрес "куклы".
DedMorozzz
Цитата(AlexSpl @ 22 Dec 2016, 23:06) *
Во всех LoHook'ах у нас есть адрес структуры героя. Соответственно, мы можем учесть и уровень Дипломатии и арты. Нужно убирать последний LoHook и дублировать его логику в остальных.


Да, но я так понял что во всех местах этот адресс разный. В этом главная проблема
AlexSpl
Так и есть, отредактировал пост выше.
DedMorozzz
Вот код с разными хуками




Упс... HookContext* c не поубирал. Но по сути он просто передаётся и не юзается. Ломать ничего не должен
AlexSpl
Вот это
Код
_PI->WriteLoHook(0x44A985, setArmyValueArt);

и это
Код
int __stdcall setArmyValueArt(LoHook* h, HookContext* c)
{
    for (int i = 0; i < 19; ++i) {
        if ( *(int*)(c->edx + 124 + (i << 3) + 4) == 66 ) {
            c->eax *= 1.05;
        }
    }
    
    return EXEC_DEFAULT;
}

нужно убирать.

Далее, в функции setArmyValueSplit(), setArmyValuePreview() и setArmyValue() добавить код (на примере первой):

Код
int __stdcall setArmyValueSplit(LoHook* h, HookContext* c)
{
    float Mod = 0;

    for (int i = 0; i < 19; ++i) {
        if ( *(int*)(c->reg + 297 + (i << 3) + 4) == 66 ) Mod += 0.05;
    }

    char diplomacyLevel = *(char*)(*(int*)(c->ebp + 8) + 201 + 4);
    Mod += getArmyValueModifier(diplomacyLevel);
    c->eax *= 1 + Mod;

    return EXEC_DEFAULT;
}


Соответсвенно, здесь поменять модификаторы:
Код
float getArmyValueModifier(char diplomacyLevel)
{
    float armyModifier[4] = {0.00, 0.10, 0.15, 0.20 /* процент же */};
    return ( diplomacyLevel < 4 ? armyModifier[diplomacyLevel] : 0 );
}


c->reg заменить на *(int*)(c->ebp + 8), c->esi и c->edi соответственно.

* * *
Ну всё, кажется, отредактировал smile.gif
DedMorozzz
не учитывается модификатор. Я правильно написал?
Код
int __stdcall setArmyValueSplit(LoHook* h, HookContext* c)
{
    float Mod = 0;

    for (int i = 0; i < 19; ++i) {
        if ( *(int*)(c->ebp + 8 + 297 + (i << 3) + 4) == 66 ) {
            Mod += 40.05;
        }
    }
    //Mod = 20;

    char diplomacyLevel = *(char*)(*(int*)(c->ebp + 8) + 201 + 4);
    float armyModificator = getArmyValueModificator(diplomacyLevel, c);
    (int)c->eax *= (Mod + armyModificator);

    return EXEC_DEFAULT;
}


Вот так никаких изменений. Если расскоментить Mod = 20, тогда делить начинает на 2 пачки, вместо 3х.. Т.е. в условие не заходит
AlexSpl
Не, нужно так:

Код
int __stdcall setArmyValueSplit(LoHook* h, HookContext* c)
{
    float Mod = 0;

    for (int i = 0; i < 19; ++i) {
        if ( *(int*)(*(int*)(c->ebp + 8) + 297 + (i << 3) + 4) == 66 ) {
            Mod += 40.05;
        }
    }

    char diplomacyLevel = *(char*)(*(int*)(c->ebp + 8) + 201 + 4);
    Mod += getArmyValueModifier(diplomacyLevel);
    (int)c->eax *= 1 + Mod;

    return EXEC_DEFAULT;
}


И я отредактировал пост выше (см. функцию getArmyValueModifier()).
DedMorozzz
Код
int __stdcall setArmyValueSplit(LoHook* h, HookContext* c)
{
    float armyModifier = 0;

    for (int i = 0; i < 19; ++i) {
        if ( *(int*)(*(int*)(c->ebp + 8) + 297 + (i << 3) + 4) == 66 ) {
            armyModifier += 40.05;
        }
    }

    char diplomacyLevel = *(char*)(*(int*)(c->ebp + 8) + 201 + 4);
    armyModifier += getArmyValueModifier(diplomacyLevel, c);
    (int)c->eax *= (1 + armyModifier);

    return EXEC_DEFAULT;
}


Это работает корректно. Делит на 3 пачки без арта, на 2 с ним

А вот превью падает. Код:
Код
int __stdcall setArmyValuePreview(LoHook* h, HookContext* c)
{
    float armyModifier = 0;

    for (int i = 0; i < 19; ++i) {
        if ( *(int*)(*(int*)(c->esi + 8) + 297 + (i << 3) + 4) == 66 ) {
            armyModifier += 40.05;
        }
    }

    int diplomacyLevel = (int)(*(char*)(c->esi + 201 + 4));
    armyModifier += getArmyValueModifier(diplomacyLevel, c);
    (int)c->eax *= (1 + armyModifier);

    return EXEC_DEFAULT;
}
AlexSpl
Превью нужно писать так:

Код
int __stdcall setArmyValuePreview(LoHook* h, HookContext* c)
{
    float armyModifier = 0;

    for (int i = 0; i < 19; ++i) {
        if ( *(int*)(c->esi + 297 + (i << 3) + 4) == 66 ) {
            armyModifier += 40.05;
        }
    }

    int diplomacyLevel = (int)(*(char*)(c->esi + 201 + 4));
    armyModifier += getArmyValueModifier(diplomacyLevel, c);
    (int)c->eax *= (1 + armyModifier);

    return EXEC_DEFAULT;
}


см.
Цитата
c->reg заменить на *(int*)(c->ebp + 8), c->esi и c->edi соответственно.


Аналогично первый хук поправить на c->edi.

Потом здесь - getArmyValueModifier(diplomacyLevel, c); - не нужно передавать контекст, он в этой функции не используется.
DedMorozzz
Работает! smile.gif
Цитата
c->reg заменить на *(int*)(c->ebp + 8), c->esi и c->edi соответственно.

Я сперва понял что это краткая запись. Мол по такому же принцицу (*(int*)(c->ebp + 8)) и для остальных, меняя только ebp на нужное)

Цитата
Потом здесь - getArmyValueModifier(diplomacyLevel, c); - не нужно передавать контекст, он в этой функции не используется.

угу, в посте выше это писал.
Цитата
Упс... HookContext* c не поубирал. Но по сути он просто передаётся и не юзается. Ломать ничего не должен

Потом уберу) главное функционал

По поводу артов, а где айдишники артов посмотреть? т.е. 66 для шеи знаю, а где глянуть остальные. И не только дипломата
AlexSpl
[Art]
0 Spell Book
1 Spell Scroll
2 The Grail
3 Catapult
4 Ballista
5 Ammo Cart
6 First Aid Tent
7 Centaurs Axe
8 Blackshard of the Dead Knight
9 Greater Gnoll's Flail
10 Ogre's Club of Havoc
11 Sword of Hellfire
12 Titan's Gladius
13 Shield of the Dwarven Lords
14 Shield of the Yawning Dead
15 Buckler of the Gnoll King
16 Targ of the Rampaging Ogre
17 Shield of the Damned
18 Sentinel's Shield
19 Helm of the Alabaster Unicorn
20 Skull Helmet
21 Helm of Chaos
22 Crown of the Supreme Magi
23 Hellstorm Helmet
24 Thunder Helmet
25 Breastplate of Petrified Wood
26 Rib Cage
27 Scales of the Greater Basilisk
28 Tunic of the Cyclops King
29 Breastplate of Brimstone
30 Titan's Cuirass
31 Armor of Wonder
32 Sandals of the Saint
33 Celestial Necklace of Bliss
34 Lion's Shield of Courage
35 Sword of Judgement
36 Helm of Heavenly Enlightenment
37 Quiet Eye of the Dragon
38 Red Dragon Flame Tongue
39 Dragon Scale Shield
40 Dragon Scale Armor
41 Dragonbone Greaves
42 Dragon Wing Tabard
43 Necklace of Dragonteeth
44 Crown of Dragontooth
45 Still Eye of the Dragon
46 Clover of Fortune
47 Cards of Prophecy
48 Ladybird of Luck
49 Badge of Courage
50 Crest of Valor
51 Glyph of Gallantry
52 Speculum
53 Spyglass
54 Amulet of the Undertaker
55 Vampire's Cowl
56 Dead Man's Boots
57 Garniture of Interference
58 Surcoat of Counterpoise
59 Boots of Polarity
60 Bow of Elven Cherrywood
61 Bowstring of the Unicorn's Mane
62 Angel Feather Arrows
63 Bird of Perception
64 Stoic Watchman
65 Emblem of Cognizance
66 Statesman's Medal
67 Diplomat's Ring
68 Ambassador's Sash
69 Ring of the Wayfarer
70 Equestrian's Gloves
71 Necklace of Ocean Guidance
72 Angel Wings
73 Charm of Mana
74 Talisman of Mana
75 Mystic Orb of Mana
76 Collar of Conjuring
77 Ring of Conjuring
78 Cape of Conjuring
79 Orb of the Firmament
80 Orb of Silt
81 Orb of Tempestuous Fire
82 Orb of Driving Rain
83 Recanter's Cloak
84 Spirit of Oppression
85 Hourglass of the Evil Hour
86 Tome of Fire Magic
87 Tome of Air Magic
88 Tome of Water Magic
89 Tome of Earth Magic
90 Boots of Levitation
91 Golden Bow
92 Sphere of Permanence
93 Orb of Vulnerability
94 Ring of Vitality
95 Ring of Life
96 Vial of Lifeblood
97 Necklace of Swiftness
98 Boots of Speed
99 Cape of Velocity
100 Pendant of Dispassion
101 Pendant of Second Sight
102 Pendant of Holiness
103 Pendant of Life
104 Pendant of Death
105 Pendant of Free Will
106 Pendant of Negativity
107 Pendant of Total Recall
108 Pendant of Courage
109 Everflowing Crystal Cloak
110 Ring of Infinite Gems
111 Everpouring Vial of Mercury
112 Inexhaustible Cart of Ore
113 Eversmoking Ring of Sulfur
114 Inexhaustible Cart of Lumber
115 Endless Sack of Gold
116 Endless Bag of Gold
117 Endless Purse of Gold
118 Legs of Legion
119 Loins of Legion
120 Torso of Legion
121 Arms of Legion
122 Head of Legion
123 Sea Captain's Hat
124 Spellbinder's Hat
125 Shackles of War
126 Orb of Inhibition
127 Vial of Dragon Blood
128 Armageddon's Blade
129 Angelic Alliance
130 Cloak of the Undead King
131 Elixir of Life
132 Armor of the Damned
133 Statue of Legion
134 Power of the Dragon Father
135 Titan's Thunder
136 Admiral's Hat
137 Bow of the Sharpshooter
138 Wizard's Well
139 Ring of the Magi
140 Cornucopia
DedMorozzz
Не нравится когда много дубликата кода, привожу к более вменяемому виду. не финал, но уже почти:
Код
float __stdcall getArmyValueModifier(int baseHeroOffset)
{
    int diplomacyLevel = (int)(*(char*)(baseHeroOffset + 201 + 4));
    float armyModifier[4] = {0.00, 0.10, 0.15, 19.00};
    float currentModifier = ( (diplomacyLevel >= 0) && (diplomacyLevel <= 3) ? armyModifier[diplomacyLevel] : 1 );

    float armyArtModifier = 0;
    for (int i = 0; i < 19; ++i) {
        if ( *(int*)(baseHeroOffset + 297 + (i << 3) + 4) == 66 ) {
            armyArtModifier += 40.05;
        }
    }

    return currentModifier + armyArtModifier;
}

int __stdcall setArmyValue(LoHook* h, HookContext* c)
{
    int baseHeroOffset = c->edi;
    float armyModifier = getArmyValueModifier(baseHeroOffset);
    (int)c->eax *= (1 + armyModifier);

    return EXEC_DEFAULT;
}


int __stdcall setArmyValuePreview(LoHook* h, HookContext* c)
{
    int baseHeroOffset = c->esi;
    float armyModifier = getArmyValueModifier(baseHeroOffset);
    (int)c->eax *= (1 + armyModifier);

    return EXEC_DEFAULT;
}

int __stdcall setArmyValueSplit(LoHook* h, HookContext* c)
{
    int baseHeroOffset = *(int*)(c->ebp + 8);
    float armyModifier = getArmyValueModifier(baseHeroOffset);
    (int)c->eax *= (1 + armyModifier);

    return EXEC_DEFAULT;
}


Всё работает и почти не дублирую код)
AlexSpl
Только всё-таки diplomacyLevel - это char. Т.е. приведение к int - лишнее.

Код
float currentModifier = ( (diplomacyLevel >= 0) && (diplomacyLevel <= 3) ? armyModifier[diplomacyLevel] : 1 );


Почему 1? У нас не модификатор теперь (в понимании множителя), а процент. И currentModifier лучше переименовать в diplomacyBonus. Вообще слово Modifier заменить на Bonus.

* * *
Если нужна читабельность, то следует прислушаться к совету feanor'а.

Создать заголовочный файл (например, hstructs.h):
Код
struct art {
    int id;
    int prop;
};

struct hero {
    char dummy_0[201]; // байты структуры героя с 0-го по 200-й нас не интересуют
    char secondarySkill[28]; // +201
    char dummy_1[72]; // байты структуры героя с 229-го по 300-й тоже не интересуют
    art  dollArt[19]; // +301
    // остальные тоже не интересуют
};

enum
{
    PATHFINDING, ARCHERY, LOGISTICS, SCOUTING, DIPLOMACY, NAVIGATION, LEADERSHIP,
    WISDOM, MYSTICISM, LUCK, BALLISTICS, EAGLE_EYE, NECROMANCY, ESTATES,
    FIRE_MAGIC, AIR_MAGIC, WATER_MAGIC, EARTH_MAGIC, SCHOLAR, TACTICS, ARTILLERY,
    LEARNING, OFFENSE, ARMORER, INTELLIGENCE, SORCERY, RESISTANCE, FIRST_AID
};


Код примерно такой получится:
Код
#include "hstructs.h";

double getArmyValueModifier(hero* myHero)
{
    double diploBonus[4] = {0.00, 0.10, 0.15, 0.20};
    
    double artBonus = 0.00;
    for (int i = 0; i < 19; ++i) {
        if ( myHero->dollArt[i].id == 66 ) artBonus += 0.05;
    }

    return 1 + diploBonus[myHero->secondarySkill[DIPLOMACY]] + artBonus;
}

int __stdcall setArmyValue(LoHook* h, HookContext* c)
{
    c->eax *= getArmyValueModifier((hero*)(c->edi));
    return EXEC_DEFAULT;
}

// и т.п.


* * *
UPD Поэкспериментировал с артефактами на "кукле" героя. Действительно, ID артефакта идёт первым (почему-то казалось, что вторым). Соответственно, исправил стуктуры art и hero в коде выше. Старый код со смещением артов 297 + (i << 3) + 4 будет работать всё равно (из-за +4), но правильно записывать смещение так: 301 + (i << 3). Прошу прощения. Таким образом, смещение "куклы" в структуре feanor'а указано верно и тупил я.
DedMorozzz
С замедлением засада вышла...
Была переделана логика замедления. Что бы округлялось в большую сторону. Т.е. 11->6 / 10->5 / 9->5 ... вместо 11->5 / 10->5 / 9->4 ...
Проблема следующая, замедление корректно работает только на летающих юнитах. Виверну замедляет с 11 до 6 и пролетает 6 клеток
А вот с пешими юнитами пишется скорость (у единорога примеру) 7. После замедления пишется 4. И область хода показывается в 4 клетки. Но по факту проходит 3!
Т.е. нажимаю на 4ю клетку, юнит начинает ходить и пройдя 3 клетки останавливается. Или даже лучше. на 4х клетках от врага, пишется что я могу атаковать юнита (появляется меч и пишется (снизу) сколько урона нанесу), нажмая атаковать, юнит проходит 3 клетки и останавливается smile.gif
Повторюсь, с летающими всё ок

Код
.text:004489F0; int __thiscall combatMonster_GetSpeed(_CombatMonster_ *this)
.text:004489F0 combatMonster_GetSpeed proc near                          ; CODE XREF: CombatMan_0041E310+F5p
.text:004489F0                                                        ; CombatMan_0041E310+109p ...
.text:004489F0
.text:004489F0 var_4               = dword ptr -4
.text:004489F0
.text:004489F0                     push    ebp
.text:004489F1                     mov     ebp, esp
.text:004489F3                     push    ecx
.text:004489F4                     mov     edx, [ecx+270h]
.text:004489FA                     mov     eax, [ecx+0C4h]
.text:00448A00                     test    edx, edx                      ; Logical Compare
.text:00448A02                     mov     [ebp+var_4], eax
.text:00448A05                     jz      short loc_00448A37            ; Jump if Zero (ZF=1)
.text:00448A07                     mov     eax, [ecx+84h]
.text:00448A0D                     shr     eax, 6                        ; Shift Logical Right
.text:00448A10                     test    al, 1                         ; Logical Compare
.text:00448A12                     jz      short loc_00448A1A            ; Jump if Zero (ZF=1)
.text:00448A14                     xor     eax, eax                      ; Logical Exclusive OR
.text:00448A16                     mov     esp, ebp
.text:00448A18                     pop     ebp
.text:00448A19                     retn                                  ; Return Near from Procedure
.text:00448A1A; ---------------------------------------------------------------------------
.text:00448A1A
.text:00448A1A loc_00448A1A:                                             ; CODE XREF: combatMonster_GetSpeed+22j
.text:00448A1A                     fild    [ebp+var_4]                ; Load Integer
.text:00448A1D                     fstp    [ebp+var_4]                ; Store Real and Pop
.text:00448A20                     fld     [ebp+var_4]                ; Load Real
.text:00448A23                     fmul    dword ptr [ecx+4C8h]          ; Multiply Real
.text:00448A29                     call    __ftol                        ; Call Procedure
.text:00448A2E                     test    eax, eax                      ; Logical Compare
.text:00448A30                     jg      short loc_00448A37            ; Jump if Greater (ZF=0 & SF=OF)
.text:00448A32                     mov     eax, 1
.text:00448A37
.text:00448A37 loc_00448A37:                                             ; CODE XREF: combatMonster_GetSpeed+15j
.text:00448A37                                                        ; combatMonster_GetSpeed+40j
.text:00448A37                     mov     esp, ebp
.text:00448A39                     pop     ebp
.text:00448A3A                     retn                                  ; Return Near from Procedure
.text:00448A3A combatMonster_GetSpeed endp


используемый код:
Код
int __stdcall spellSlow(LoHook* h, HookContext* c)
{
    // тут структура монстра лежит в ecx.
    // откуда мы и можем получить все интересующие нас параметры
    float speed = (float)(*(int*)(c->ecx +196));                   // получаем скорость монстра (структура +196)
    c->eax = floor( (speed * (*(float*)(c->ecx + 1224))) + 0.5 );  // функция правильного округления (скорость * коэфф.замедления)
    
    // а также приходится "обойти" часть оригинального кода
    c->return_address = 0x448A37;                                  // указываем новый адрес возврата
    return NO_EXEC_DEFAULT;                                        // после завершения хука не выполняется затертый хуком код
}


Судя по этому комменту:
Цитата
в структуре стека в битве есть поле множителя (SlowMul в вогобазе), которое устанавливается при касте скорости и потом используется как коэффициент для актуальной скорости в combatMonster_00448560 (что-то с определением достижимости цели, что ли) и combatMonster_GetSpeed (004489F0, собственно расчет скорости)

изменён только метод combatMonster_GetSpeed (хоть и с достижимостью цели проблем не возникло, работает корректно)
Попробовал изменить combatMonster_00448560
Код
int __stdcall spellSlow2(LoHook* h, HookContext* c)
{
    float speed = (float)(*(int*)(c->ecx +196));
    c->eax = floor( (speed * (*(float*)(c->ecx + 1224))) + 0.5 );
    
    c->return_address = 0x4485B5;
    return NO_EXEC_DEFAULT;
}

Не помогло...

Как сделать корректное замедление для пеших юнитов?

PS:
Код
_PI->WriteLoHook(0x448A1A, spellSlow);
_PI->WriteLoHook(0x4485A5, spellSlow2);
Sav
Функция получения скорости много где подставлена inline. То есть при компиляции кода Героев вместо того, чтобы добавить в некоторые места вызов этой функции, туда была добавлена прямо копия кода этой функции (несколько видоизменённая: с использованием других регистров и т. п.). Для геройского кода это обычное дело, поэтому при модификации не очень больших функций всегда следует исследовать те места, откуда они вызываются, и подумать над тем, не может ли она теоретически использоваться где-то ещё (не забывая, например, про код ИИ).

Часто, впрочем, есть возможность зацеписться за техническую особенность функции и найти её inline-вызовы без понимания, где и зачем они происходят. Здесь тоже повезло: используется редкое смещение +4C8h. Можно просто поиском по ассемблерному тексту найти все использования этого смещения и на взгляд определить, какие именно относятся к получению скорости; соответственно, все их модифицировать.

Это не гарантирует того, что будут найдены действительно все inline-вызовы: иногда используется смещённый указатель на структуру, и тогда 4C8h превращается в другое число. Здесь уже помогает только опыт исследования кода и проверка всех мест, откуда потенциально могла бы вызываться эта функций. Но такие случае не так часты.
AlexSpl
А по каким адресам хуки?

Например, в sub_448560:

int speed = *(int*)(c->esi + 196); // скорость
float speedMod = *(float*)(c->esi + 1224); // модификатор скорости
DedMorozzz
Цитата(AlexSpl @ 24 Dec 2016, 14:40) *
А по каким адресам хуки?

Например, в sub_448560:

int speed = *(int*)(c->esi + 196); // скорость
float speedMod = *(float*)(c->esi + 1224); // модификатор скорости

Я пост выше обновил. Добавил 2 адреса

------------------

По смещению(+4C8h) нашел такую функу. Имеется подозрение что её и надо отредачить:
00441CC0 ; char __thiscall combatMonster_WalkToPos(_CombatMonster_ *a1, signed int a2, char a3)
AlexSpl
Здесь нужно отследить сам каст "Замедления" и посмотреть, что он делает с модификатором скорости. А иначе придётся перехватывать в коде все умножения скорости на модификатор, и последующее округление (call __ftol).
DedMorozzz
Вроде сделал!
Код
int __stdcall spellSlow3(LoHook* h, HookContext* c)
{
    float speed = (float)(*(int*)(c->esi +196));
    c->eax = floor( (speed * (*(float*)(c->esi + 1224))) + 0.5 );
    
    c->return_address = 0x441E36;
    return NO_EXEC_DEFAULT;
}


Код
_PI->WriteLoHook(0x441E19, spellSlow3);
AlexSpl
Лучше управление возвратить по адресу 441E2Dh, а не 441E36h, чтобы выполнилась проверка на 0.

И потом нет гарантии, что учтены все случаи округления.

В этом блоке инструкций меняется модификатор при касте Slow:

Код
.text:00444A33 push    35h
.text:00444A35 mov     ecx, esi
.text:00444A37 call    sub_444230
.text:00444A3C fild    [ebp+arg_8]
.text:00444A3F fstp    [ebp+var_18]
.text:00444A42 fld     [ebp+var_18]
.text:00444A45 fdiv    ds:dbl_63A698
.text:00444A4B fstp    dword ptr [esi+4C8h]
.text:00444A51 fild    dword ptr [esi+68h]
.text:00444A54 fstp    [ebp+var_18]
.text:00444A57 fld     [ebp+var_18]
.text:00444A5A fmul    ds:dbl_63B8D8
.text:00444A60 call    __ftol
.text:00444A65 mov     [esi+158h], eax
DedMorozzz
Цитата(AlexSpl @ 24 Dec 2016, 15:36) *
Лучше управление возвратить по адресу 441E2Dh, а не 441E36h, чтобы выполнилась проверка на 0.

И потом нет гарантии, что учтены все случаи округления.


Переделал, код чуть ниже

Насчёт гарантий.. да нету. Но я проверил что смог, как на АИ, так и на ручном управлении юнитами. Как на пеших, так и на летающих. В том числе бонус от родной земли

Ещё важный момент, надо было юзать esi, а не ecx во 2й функции
Мб кому понадобится:
Код
.text:00441E19 loc_00441E19:                                             ; CODE XREF: combatMonster_WalkToPos+153j
.text:00441E19                     fild    dword ptr [ebp+a5]            ; Load Integer
.text:00441E1C                     fstp    dword ptr [ebp+a5]            ; Store Real and Pop
.text:00441E1F                     fld     dword ptr [ebp+a5]            ; Load Real
.text:00441E22                     fmul    dword ptr [esi+4C8h]          ; Multiply Real
.text:00441E28                     call    __ftol                        ; Call Procedure
.text:00441E2D                     test    eax, eax                      ; Logical Compare
.text:00441E2F                     jg      short loc_00441E36            ; Jump if Greater (ZF=0 & SF=OF)
.text:00441E31                     mov     eax, 1


Т.е. тут нужное смещение применятся к esi ([esi+4C8h]), а в 1й функе:
Код
.text:00448A1A loc_00448A1A:                                             ; CODE XREF: combatMonster_GetSpeed+22j
.text:00448A1A                     fild    [ebp+var_4]                ; Load Integer
.text:00448A1D                     fstp    [ebp+var_4]                ; Store Real and Pop
.text:00448A20                     fld     dword ptr [ebp-4]             ; Load Real
.text:00448A23                     fmul    dword ptr [ecx+4C8h]          ; Multiply Real
.text:00448A29                     call    __ftol                        ; Call Procedure
.text:00448A2E                     test    eax, eax                      ; Logical Compare
.text:00448A30                     jg      short loc_00448A37            ; Jump if Greater (ZF=0 & SF=OF)
.text:00448A32                     mov     eax, 1

Т.е. тут ( [ecx+4C8h]). И юзать надо ecx. Может это и не верный алоритм определения esi/ecs/..., но другого не знаю)

Итого, финальный код:
Код
int __stdcall spellSlow(LoHook* h, HookContext* c)
{
    float speed = (float)(*(int*)(c->ecx +196));                   // получаем скорость монстра (структура +196)
    c->eax = floor( (speed * (*(float*)(c->ecx + 1224))) + 0.5 );  // функция правильного округления (скорость * коэфф.замедления)
    
    c->return_address = 0x448A37;                                  // указываем новый адрес возврата
    return NO_EXEC_DEFAULT;                                        // после завершения хука не выполняется затертый хуком код
}

int __stdcall spellSlowMelee(LoHook* h, HookContext* c)
{
    float speed = (float)(*(int*)(c->esi +196));
    c->eax = floor( (speed * (*(float*)(c->esi + 1224))) + 0.5 );
    
    c->return_address = 0x441E2D;
    return NO_EXEC_DEFAULT;
}

Код
_PI->WriteLoHook(0x448A1A, spellSlow);
_PI->WriteLoHook(0x441E19, spellSlowMelee);
AlexSpl
Аналогично 0x448A37 на 0x448A2E, иначе пропускается проверка if (speed <= 0) speed = 1;
DedMorozzz
Цитата(AlexSpl @ 24 Dec 2016, 15:56) *
Аналогично 0x448A37 на 0x448A2E, иначе пропускается проверка if (speed <= 0) speed = 1;

заменил, всё работает

Терь хочу допилить 2 оставшихся момента с дипломатией...
как реализовать возможность сдаваться при бое с монстрами?
Т.е. хочу сделать активной кнопку "сдаться" за 30-20-10% от цены войска. Но только при бое с монстрами, против героя не меняя
AlexSpl
А Haste и Prayer (Молитва) работают нормально?
DedMorozzz
Цитата(AlexSpl @ 24 Dec 2016, 16:04) *
А Haste и Prayer (Молитва) работают нормально?

Проверил. Да ок. Как и ПВП, так и против мобов. И корректно считается бафф после замедления (и скорость и молитва)
DedMorozzz
А куда копать для того что бы реализовать "сдаться" при бое с монстрами?
AlexSpl
Это сложная задача, так как алгоритмы написаны для сдачи конкретному герою (его имя в сообщении, и в казну игрока, которому принадлежит этот герой, переводится золото). Для начала я бы нашёл место в коде, где "гасится" кнопка "Surrender".
AlexSpl
Посмотрел немного. Если нажать кнопку "Surrender" в бою с нейтралами, игра ведёт себя корректно. Сообщение: "{Hero} surrenders to the enemy, and departs in shame.", герой подает в таверну с войском на момент сдачи. Одно но: нейтралы не требуют денег. Похоже, стоимость сдачи вычитается автоматически.

Наверное, я пропускаю диалог. Так что нужно искать вызов диалога.

Диалог - sub_4F6C00.
Код по адресу 474919h обрабатывает нажатие кнопки "Surrender", свитч джамп здесь: 474783h (0 - сдаться, 1 - сбежать, 2 - опции и т.д.).
Код, который реализует сдачу: 478EBCh, остальные действия в бою: свитч джамп - 47895Fh (функция sub_4786B0).

Решение, активна кнопка "Surrender" или нет, принимается здесь: 477EB3h.

Примерный алгоритм:
1. Делаем кнопку "Surrender" активной в битве с нейтралами (477EB3h);

2. Реализуем корректный диалог при сдаче нейтралам (474919h);

3. Проверяем код по адресу 478EBCh.

Осталось удостовериться, что деньги за откуп уходят в пустоту, а не прописываются по левому адресу. Вылета нет, но проверить стоит.
DedMorozzz
Пробую разрулить с диалогом. Что бы показывался только для героя с дипломатией:
Код
int __stdcall setSurrenderButton(LoHook* h, HookContext* c)
{
    char diplomacyLevel = *(char*)(c->esi + 201 + 4);

    if (diplomacyLevel > 0) {
        _P = GetPatcher();
        _PI = _P->GetInstance("HD.Plugin.DedMorozzz");
        _PI->WriteHexPatch(0x477EB3, "9090");
        //*(int*)0x477EB3 = 9090;
    }

    return EXEC_DEFAULT;
}

Код
_PI->WriteLoHook(0x4AC35E, setSurrenderButton);


Не работает. Явно патчер вызывать не надо, но я не знаю как сделать..

В чём была логика - на нужный адрес сделать лоу хук, который проверит наличие вторичного навыка. И если он есть - разблокируем кнопку "сдаться"
AlexSpl
Можно и через LoHook:
Код
int __stdcall setSurrenderButton(LoHook* h, HookContext* c)
{
    char diplomacyLevel = *(char*)(*(int*)(c->esi + c->eax * 4 + 0x53CC) + 201 + 4);

    if ( diplomacyLevel > 0 ) {
        c->return_address = 0x477EB5;
        return NO_EXEC_DEFAULT;
    }

    return EXEC_DEFAULT;
}
DedMorozzz
Цитата(AlexSpl @ 25 Dec 2016, 13:13) *
Можно и через LoHook:
Код
int __stdcall setSurrenderButton(LoHook* h, HookContext* c)
{
    char diplomacyLevel = *(char*)(*(int*)(c->esi + c->eax * 4 + 0x53CC) + 201 + 4);

    if ( diplomacyLevel > 0 ) {
        c->return_address = 0x477EB5;
        return NO_EXEC_DEFAULT;
    }

    return EXEC_DEFAULT;
}

вылетает. Пробовал 0x477EB5 на 0x477EB3 заменить. То же самое

----------------------

я так понимаю это:
char diplomacyLevel = *(char*)(*(int*)(c->esi + c->eax * 4 + 0x53CC) + 201 + 4);

не работает. Ибо нападая героем без дипломатии - всё равно вылетает
AlexSpl
Так LoHook на

Код
_PI->WriteLoHook(0x477EB3, setSurrenderButton);
DedMorozzz
Цитата(AlexSpl @ 25 Dec 2016, 13:26) *
Так LoHook на

Код
_PI->WriteLoHook(0x477EB3, setSurrenderButton);

да, точно! У меня чего-то вообще на 0x4AC35E стоял. Терь всё ок, не вылетает. Но и кнопки нету)) В смысле не активна


PS: её нету даже без условия.. Даже если так написать в ф-ии:
Код
c->return_address = 0x477EB5;
return NO_EXEC_DEFAULT;
AlexSpl
А у героя, нападающего на нейтралов, есть Дипломатия?
DedMorozzz
Цитата(AlexSpl @ 25 Dec 2016, 13:37) *
А у героя, нападающего на нейтралов, есть Дипломатия?

Обновил пост выше. Да есть. Нет кнопки при любом раскладе
AlexSpl
Какой код в IDA c этого адреса 00477EB3 по этот 00477EF2?
DedMorozzz
Код
.text:00477EB3                     jz      short loc_00477EE2             ; Jump if Zero (ZF=1)
.text:00477EB5                     cmp     [esi+eax*4+53CCh], edi         ; Compare Two Operands
.text:00477EBC                     jz      short loc_00477EE2             ; Jump if Zero (ZF=1)
.text:00477EBE                     cmp     [esi+53C8h], edi               ; Compare Two Operands
.text:00477EC4                     jz      short loc_00477ECB             ; Jump if Zero (ZF=1)
.text:00477EC6                     cmp     eax, 1                         ; Compare Two Operands
.text:00477EC9                     jz      short loc_00477EE2             ; Jump if Zero (ZF=1)
.text:00477ECB
.text:00477ECB loc_00477ECB:                                              ; CODE XREF: CombatMan_00477C00+2C4j
.text:00477ECB                     mov     ecx, [esi+132FCh]              ; dlg
.text:00477ED1                     push    1000h                          ; itemind
.text:00477ED6                     push    7D1h                           ; newparval
.text:00477EDB                     call    DlgItem_Send6Cmd2Item          ; Call Procedure
.text:00477EE0                     jmp     short loc_00477EF7             ; Jump
.text:00477EE2; ---------------------------------------------------------------------------
.text:00477EE2
.text:00477EE2 loc_00477EE2:                                              ; CODE XREF: CombatMan_00477C00+2B3j
.text:00477EE2                                                            ; CombatMan_00477C00+2BCj ...
.text:00477EE2                     mov     ecx, [esi+132FCh]
.text:00477EE8                     push    1000h
.text:00477EED                     push    7D1h
.text:00477EF2                     call    DlgItem_Send5Cmd2Item          ; Call Procedure
.text:00477EF7
.text:00477EF7 loc_00477EF7:                                              ; CODE XREF: CombatMan_0047
AlexSpl
Давайте для теста с нейтралами попробуем пропустить условия (адрес возврата 0x477ECB):
Код
int __stdcall setSurrenderButton(LoHook* h, HookContext* c)
{
    char diplomacyLevel = *(char*)(*(int*)(c->esi + c->eax * 4 + 0x53CC) + 201 + 4);

    if ( diplomacyLevel > 0 ) {
        c->return_address = 0x477ECB;
        return NO_EXEC_DEFAULT;
    }

    return EXEC_DEFAULT;
}
DedMorozzz
Цитата(AlexSpl @ 25 Dec 2016, 13:58) *
Давайте для теста с нейтралами попробуем пропустить условия (адрес возврата 0x477ECB):
Код
int __stdcall setSurrenderButton(LoHook* h, HookContext* c)
{
    char diplomacyLevel = *(char*)(*(int*)(c->esi + c->eax * 4 + 0x53CC) + 201 + 4);

    if ( diplomacyLevel > 0 ) {
        c->return_address = 0x477ECB;
        return NO_EXEC_DEFAULT;
    }

    return EXEC_DEFAULT;
}

Так всё работает! Более того корректно сдаюсь. Напал с архом на триглодита, нажимаю убежать - пояляется диалог:
Инферальные триглодиты говорят "мы позволим убежать за 499 золотых"
При этом герой без дипломатии не может нажать на кнопку "сдаться", не ктивна

PS: триглодиты явно в эльдорадо работают

PPS: я такпонимаю это изменения ХОТЫ уже, что диалог корректный. И если этот мод прикрутить к СОДУ - будет вылетать..
AlexSpl
Цитата
PPS: я такпонимаю это изменения ХОТЫ уже, что диалог корректный. И если этот мод прикрутить к СОДУ - будет вылетать..

Сначала подумал, что шутите smile.gif Получается, что так. Изменения Хоты или HD.

Только код setSurrenderButton нужно доработать, чтобы кнопка не появлялась в тактической фазе и при осаде города, например.

Диалог сдачи нейтралам заскринить можете? Что-то маловато текста по сравнению с англ. версией.
DedMorozzz
Цитата(AlexSpl @ 25 Dec 2016, 14:21) *
Цитата
PPS: я такпонимаю это изменения ХОТЫ уже, что диалог корректный. И если этот мод прикрутить к СОДУ - будет вылетать..

Сначала подумал, что шутите smile.gif Получается, что так. Изменения Хоты или HD.

Только код setSurrenderButton нужно доработать, чтобы кнопка не появлялась в тактической фазе и при осаде города, например.

Диалог сдачи нейтралам заскринить можете? Что-то маловато текста по сравнению с англ. версией.



Я сократил, не полный текст написал, суть оставил только. Вот скрин с эльдорадовскими триглодитами - https://sc-cdn.scaleengine.net/i/a847973a80...ef88c5f63b2.png

А по СОДу я не проверял, то предположение (СОДы нету у меня smile.gif )
Ибо вот :

Цитата
Сборные артефакты Мантия Дипломата
Мантия Дипломата
=
Медаль Дипломата
+
Кольцо Дипломата
+
Лента Посла

Позволяет герою сбегать и откупаться при обороне города, а так же откупаться в битвах, в которых у противника нет героя. Увеличивает силу армии героя в глазах нейтральных монстров (при определении их желания присоединиться или убежать) и Гильдии Воров в 3 раза.

Соотв. логика и диалоги должны быть прописаны, если это работает при наличии сборного арта
AlexSpl
Кстати, стоимость сдачи рассчитывается в этом цикле: loc_474942. Можно поставить LoHook на 4749B4h, чтобы её модифицировать уровнем Дипломатии.
DedMorozzz
Цитата(AlexSpl @ 25 Dec 2016, 14:31) *
Кстати, стоимость сдачи рассчитывается в этом цикле: loc_474942.

спасибо, это полезно! Как раз планирую для боя с монстрами делать другой расчёт стоимости выкупа

PS: проверил, да ... с замка можно убежать
Т.о. я вижу 2 варианта как это исправить:
1. проверить что герой в замке и тогда блочить кнопку. Так же учесть что это не варварский замок или в нём не построен ход (позволяющий убегать) и может ещё что то упустил
2й вариант - проверить можно ли сбежать. Если да - делать кнопку активной smile.gif
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Форум IP.Board © 2001-2025 IPS, Inc.