IPB

Здравствуйте, гость ( Вход | Регистрация )

29 страниц V  « < 26 27 28 29 >  
Reply to this topicStart new topic
> Мод на ХотА
AlexSpl
сообщение 27 Jan 2017, 02:30 (Сообщение отредактировал AlexSpl - 27 Jan 2017, 02:45)
Сообщение #541

Immortal
Сообщений: 798
Спасибо сказали: 555 раз




Посмотрел. Дело в указателе на стек esp. Так пробуйте:

Код
{
    int unitAddr = *(int*)(c->ebp - 0x20);    // получаем адрес отряда, чьё информационное окно смотрим
    int hpShieldValue = *(int*)(unitAddr + 0x94);    // получаем значение щита

    if ( hpShieldValue > 0 ) {
        sprintf( (char*)0x697428, "%d [%d]", *(int*)(c->ebp + 8), hpShieldValue );    // отображаем остаток_здоровья [щит]
    };
    else return EXEC_DEFAULT;    // отображаем по умолчанию: остаток_здоровья

    с->esp -= 12;    // adjust esp*
    c->return_address = 0x5F64C2;
    return NO_EXEC_DEFAULT;
}


* Посмотрите этот код, а именно последнюю инструкцию:
Код
mov     eax, [ebp+arg_0]
push    eax
push    offset aD    ; "%d"
push    offset Text; char *
call    _sprintf
push    50h    ; unsigned int
call    operator new(uint)
add     esp, 16


Заметьте, что непосредственно _sprintf стек не освобождает, как и operator new(uint). Это делает инструкция add esp, 16 за обе(!) функции (3 аргумента dword функции _sprintf и один аргумент dword оператора new: 3 * 4 + 4 = 16. Поэтому если мы скипуем оригинальный код, мы должны поправить и указатель стека esp, чтобы инстукция add esp, 16 отрабатывала корректно.


Спасибо сказали:
Go to the top of the pageAdd Nick
 
+Quote Post
DedMorozzz
сообщение 27 Jan 2017, 22:03 (Сообщение отредактировал DedMorozzz - 27 Jan 2017, 22:13)
Сообщение #542

God
Сообщений: 267
Спасибо сказали: 25 раз




Цитата
Заметьте, что непосредственно _sprintf стек не освобождает, как и operator new(uint). Это делает инструкция add esp, 16 за обе(!) функции (3 аргумента dword функции _sprintf и один аргумент dword оператора new: 3 * 4 + 4 = 16. Поэтому если мы скипуем оригинальный код, мы должны поправить и указатель стека esp, чтобы инстукция add esp, 16 отрабатывала корректно.

ух.. вроде понятно. Но только после объяснения. Да и то, как-то не очевидно)

Подправлю текст "Текущее здоровье" на что нить поменьше, аля "текущее хп", ибо не влазит) Текст друг на друга налазит. Как раз в ММ архиве пытаюсь найти где он лежит (а я надеюсь он именно там) и постараюсь до конца осознать почему и как выше происходит smile.gif
Go to the top of the pageAdd Nick
 
+Quote Post
AlexSpl
сообщение 27 Jan 2017, 22:18
Сообщение #543

Immortal
Сообщений: 798
Спасибо сказали: 555 раз




Цитата
ух.. вроде понятно. Но только после объяснения. Да и то, как-то не очевидно)

Есть функции, которые сами "чистят" стек от аргументов, а есть такие, которые этого не делают. Вышеприведённый код равносилен следующему:

Код
mov     eax, [ebp+arg_0]
push    eax
push    offset aD   ; "%d"
push    offset Text; char *
call    _sprintf
add     esp, 12; почистили за _sprintf
push    50h   ; unsigned int
call    operator new(uint)
add     esp, 4; почистили за operator new(uint)


Но компилятор оптимизирует. Так, инструкция add esp, x необязательно следует непосредственно за функцией и может корректировать указатель на стек сразу для нескольких функций.


Спасибо сказали:
Go to the top of the pageAdd Nick
 
+Quote Post
DedMorozzz
сообщение 27 Jan 2017, 22:31
Сообщение #544

God
Сообщений: 267
Спасибо сказали: 25 раз




другими словами инструкция add гооврит что надо свободить в переменной(?) esp стек аргументов и сместить указатель назад на кол-во освобождённых аргументов (умноженных на 4). Верно?
И да, переименовал "Текущее здоровье", в "Текущее хп". Теперь и кол-во здоровья и щит в скобках корректно влазит в поле
Текстовка находится тут - HotA_lng.lod
файл - GENRLTXT.txt
Go to the top of the pageAdd Nick
 
+Quote Post
AlexSpl
сообщение 27 Jan 2017, 22:36
Сообщение #545

Immortal
Сообщений: 798
Спасибо сказали: 555 раз




Если интересно, почитайте про __cdecl, __stdcall, __fastcall.
Go to the top of the pageAdd Nick
 
+Quote Post
AlexSpl
сообщение 27 Jan 2017, 22:56 (Сообщение отредактировал AlexSpl - 27 Jan 2017, 22:57)
Сообщение #546

Immortal
Сообщений: 798
Спасибо сказали: 555 раз




Я следил за темой не с самого начала и не припомню, чтобы кто-то раньше модифицировал HotA. Может, список изменений опубликовать? Вдруг кто заинтересуется? Ну и плагин в общий доступ, само собой.
Go to the top of the pageAdd Nick
 
+Quote Post
DedMorozzz
сообщение 27 Jan 2017, 23:11 (Сообщение отредактировал DedMorozzz - 27 Jan 2017, 23:14)
Сообщение #547

God
Сообщений: 267
Спасибо сказали: 25 раз




Вроде нашел чёт по теме - http://natalia.appmat.ru/c&c++/dll.html
Но картина не складывается...
Во всех свои ф-ях на С++ я использую только _stdcall
Цитата
При использовании соглашения о вызовах _fastcall первые два параметра, размер которых не больше двойного слова, передаются через регистры ECX и EDX. Остальные параметры передаются через стек.

Я так понимаю что использую только регистры. Но их больше чем 2. И eax есть и прочее, это получеается тоже регистры же? Тогда что такое стек...

Да и в целом пока что не понял что именно надо о этих соглашениях почитать. Слишком много разной инфы)

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

Цитата(AlexSpl @ 27 Jan 2017, 21:56) *
Я следил за темой не с самого начала и не припомню, чтобы кто-то раньше модифицировал HotA. Может, список изменений опубликовать? Вдруг кто заинтересуется? Ну и плагин в общий доступ, само собой.

Да, думаю как будет готовая версия, когда дойдёт до определённого этапа - выложить в шапку дллку и код
С варнингом что может вылетать или не работать в случае апдейта версии хоты)
И обозвать как нить "кастомный баланс хоты для шаблона Джебус".
Но думается надо бы и уточнить у разработчиков Хоты, вдруг они будут против. В этом случае уберу всё что завязано на хоте и выложу (аля сбегание от монстров, явно логика хоты юзается)

Но текущая цель такая, что бы под выпавшие навыки подстраивалась игра. Другими словами что бы всё было полезным.

Палатку как причешу, хочу вернутся к дипломатии... сколько раз играли и она выпадала - ниразу не брали. Слишком сомнительные бонусы получились.
Обсудили такую идею, на все повышалки за 1к годы, при наличии навыка дипломатии сделать цену 1.5/1/0.5к голды и получить +2 к статам, а не +1
После этого думается навык станет ощутимо привлекательнее

И забегая на перёд имеется идея сделать грамотность
для героя сумма статов которого выше некого числа, к примеру 15-20 что бы давалось +3/6/9 знания
Почему сумму статов учитывать, что бы левак не бегал и не спамил молниями. Да и леваку это и так норм навык, если надо что нить мейну передать
А вот если это получит меин... уже будет досадно, особено когда преследую цель убрать бесполезные навыки
Но тут загвоздка, я понятия не имею как построить логику которая при повышении статов пересчитывать бонус от уже полученого вторичного навыка


Спасибо сказали:
Go to the top of the pageAdd Nick
 
+Quote Post
AlexSpl
сообщение 27 Jan 2017, 23:26 (Сообщение отредактировал AlexSpl - 27 Jan 2017, 23:34)
Сообщение #548

Immortal
Сообщений: 798
Спасибо сказали: 555 раз




MSDN почитайте. __stdcall. Там же и остальные соглашения описаны.

Например, __stdcall функция сама освобождает стек от переданных ей аргументов (например, retn szArgs, где szArgs - суммарный размер аргументов). Аргументы, разумеется, передаются через стек: push arg_{N}, push arg_{N-1}, ..., push arg_0.

За __cdecl функцию стек освобождает вызывающая функция (например, add esp, szArgs).

__fastcall функция тоже освобождает стек самостоятельно. Отличается от __stdcall тем, что первые 2 аргумента DWORD (или меньшего размера) передаются через регистры ecx и edx, а остальные через стек.
Go to the top of the pageAdd Nick
 
+Quote Post
DedMorozzz
сообщение 27 Jan 2017, 23:53
Сообщение #549

God
Сообщений: 267
Спасибо сказали: 25 раз




Вот такой код получился для первой помощи.
Код
int __stdcall setFirstAidShield(LoHook* h, HookContext* c)
{
    int cmAddr =  *(int*)0x699420;
    char firstAidLevel = *(char*)(*(int*)(cmAddr + 0x53CC) + 201 + 27);
    int unitLevel = *(int*)(c->esi + 0x78) + 1;
    *(int*)(c->esi + 0x94) = unitLevel * firstAidLevel * 10;
    
    return EXEC_DEFAULT;
}

int __stdcall absorbDamage(LoHook* h, HookContext* c)
{
    if ( c->ecx <= *(int*)(c->esi + 0x94) ) {
        *(int*)(c->esi + 0x94) -= c->ecx;
        c->ecx = 1;
    } else {
        c->ecx -= *(int*)(c->esi + 0x94);
        *(int*)(c->esi + 0x94) = 0;
    }
    
    return EXEC_DEFAULT;
}

int __stdcall getStackHp(LoHook* h, HookContext* c)
{
    int unitAddr = *(int*)(c->ebp - 0x20);    // получаем адрес отряда, чьё информационное окно смотрим
    int hpShieldValue = *(int*)(unitAddr + 0x94);    // получаем значение щита

    if ( hpShieldValue > 0 ) {
        sprintf( (char*)0x697428, "%d [%d]", *(int*)(c->ebp + 8), hpShieldValue );    // отображаем остаток_здоровья [щит]
    }
    else return EXEC_DEFAULT;    // отображаем по умолчанию: остаток_здоровья

    c->esp -= 12;    // adjust esp*
    c->return_address = 0x5F64C2;

    return NO_EXEC_DEFAULT;
}

Код
_PI->WriteLoHook(0x478552, setFirstAidShield);
            _PI->WriteLoHook(0x443DBB, absorbDamage);
            _PI->WriteLoHook(0x5F64AF, getStackHp);

Цифры мб надо будет подправить, но это уже донастройка. Мб так же учесть кол-во отрядов в стеке. Аля кол-во/5*уровень = бонусный щит
Т.е. для 1000 скелетов бонусный щит будет в 200 едениц. Или для 45 виверн будет 54 бонусного поглощения
Ибо если скелеты основное войско, то эти 25 едениц щита что андеду припарка..
Но это надо будет прикинуть, посчитать, что бы не сделать слишком сильным навык

Но прямо сейчас другая проблема. Если у юнита фул хп, я не могу на него щит кинуть. Даже если его атаковали уже. К примеру ударили пачку кентавров но так что убили ровно по кол-ву хп. Я не могу повесть щит
Как убрать ограничение для хила, что только юниты с не фул здоровьем могут быть вылечены палаткой?


Спасибо сказали:
Go to the top of the pageAdd Nick
 
+Quote Post
AlexSpl
сообщение 28 Jan 2017, 00:02
Сообщение #550

Immortal
Сообщений: 798
Спасибо сказали: 555 раз




Цитата
Как убрать ограничение для хила, что только юниты с не фул здоровьем могут быть вылечены палаткой?

Это можно сделать, но я бы не стал. Думайте не о стеке в целом, а о индивидуальном воине в нём. Ударили так, что погиб - лечить некого. Иначе палатка будет накладывать щит уже на другого воина в стеке. Понимаете идею? Палатка не воскрешает, а лечит и накладывает щит на ещё живого воина в стеке.
Go to the top of the pageAdd Nick
 
+Quote Post
DedMorozzz
сообщение 28 Jan 2017, 00:44 (Сообщение отредактировал DedMorozzz - 28 Jan 2017, 00:48)
Сообщение #551

God
Сообщений: 267
Спасибо сказали: 25 раз




Верно. И идея в том, что бы палатка была элементом тактики. Что бы можно было напасть на противника. Прокинуть щит на отряд и им ударить. С расчётом, что ответный урон будет поглощён щитом, ну или частично им поглощён
В противном случае если нажать всеми припас. А так всегда и происходит, то палатку можно будет использовать только на 2м кругу, что ощутимо снижает её ценность
С другой стороны это надо взять и попробовать, если будет слишком сильно - всегда можно будет отключить возможность прокидывания щита на целых юнитов. Ну иль уменьшить размер поглощения урона
К примеру если был хил то щит в полном объёме. Если целые хп, то щит в пол силы. Это будет совсем не трудно сделать
И это в целом даст тоже ещё элемент в бою, выбирай что лучше, пол щита но заранее. Или отхилить юнита после получения урона и более сильный щит. Может так и стоит сделать
Go to the top of the pageAdd Nick
 
+Quote Post
DedMorozzz
сообщение 28 Jan 2017, 13:05
Сообщение #552

God
Сообщений: 267
Спасибо сказали: 25 раз




хз какой-то бред...
Уменьшил бонус щита с 10 * левелНавыка, до 5 + 5*левелНавыка
Т.е. вместо 10/20/30 базовый коэфициент сделал 10/15/20
И добавил расчёт юнитов в стеке. Там где юнитов меньше щит слабее(доп бонус меньше), там где их больше - сильнее..

Формулу такую хочу использовать:
кол-во юнитов / 5 * левел
При этом кол-во / 5 округлить в большую сторону

Код:

Код
int countMonsters = *(int*)(c->esi + 0x4C);
    float countUnitsForShield = (countMonsters / 5) + 0.99;
    int countUnitBonus = floor(countUnitsForShield) * unitLevel;
        //float countUnitsForShield = (countMonsters / 5);
        //int countUnitBonus = ceil(countUnitsForShield) * unitLevel;

    *(int*)(c->esi + 0x94) = countUnitBonus;//unitLevel * firstAidBonus + countUnitBonus;

Не работает как надо
есть 2 пака. 1 арх и 24 арха
каунт верный. Я в виде щита кидал кол-во юнитов. Всё ок. Цифры были 1 и 24 соответсвенно
но вот получить щит 7 и 35 не выходит, по факту выходит 0 и 28. Т.е. округляет всегда в меньшую сторону
Где тут ошибка?
Go to the top of the pageAdd Nick
 
+Quote Post
DedMorozzz
сообщение 28 Jan 2017, 14:08 (Сообщение отредактировал DedMorozzz - 28 Jan 2017, 14:16)
Сообщение #553

God
Сообщений: 267
Спасибо сказали: 25 раз




нагавнокодил, но сделал
Код
int __stdcall setFirstAidShield(LoHook* h, HookContext* c)
{
    int cmAddr =  *(int*)0x699420;
    int stackBonusModificator = 5;
    char firstAidLevel = *(char*)(*(int*)(cmAddr + 0x53CC) + 201 + 27);
    int firstAidBonus = 5 + firstAidLevel * 5;

    int unitLevel = *(int*)(c->esi + 0x78) + 1;
    int countMonsters = *(int*)(c->esi + 0x4C);

    float countUnitsForShield = int(countMonsters / stackBonusModificator);
    
    if (countMonsters > (countUnitsForShield * stackBonusModificator)) {
        countUnitsForShield += 1;
    }
    
    int countUnitBonus = countUnitsForShield * unitLevel;

    *(int*)(c->esi + 0x94) = unitLevel * firstAidBonus + countUnitBonus;
    
    return EXEC_DEFAULT;
}


Так и не удалось реализовать округление через floor или ceil
И даубл и флоат и добавление чисел делал, всегда, при любом раскладе получается округление в меньшую сторону
Потому переделал на просчёт от обратного и теперь работает как надо

Теперь возник вопрос - как получить специализацию героя? Нашел что айди специализация "первая помощь" это 27. А как получить теперь её что бы сравнить с этим значением?
Go to the top of the pageAdd Nick
 
+Quote Post
AlexSpl
сообщение 29 Jan 2017, 02:59 (Сообщение отредактировал AlexSpl - 29 Jan 2017, 05:34)
Сообщение #554

Immortal
Сообщений: 798
Спасибо сказали: 555 раз




Цитата
Так и не удалось реализовать округление через floor или ceil

Всё потому, что "/" - это целочисленное деление, если делимое и делитель есть целые чиcла. Делить нужно так для float: ... / 5.0f; Разницы нет для целых чисел, т.е. можно просто написать 5.0, но если делите на дробное число, лучше указать тип прямо. 5.0 по умолчанию есть 5.0d (double).

Цитата
Теперь возник вопрос - как получить специализацию героя? Нашел что айди специализация "первая помощь" это 27. А как получить теперь её что бы сравнить с этим значением?


Для спеца по First Aid (ID = 27):
Код
int heroID = *(int*)(heroAddr + 26);
int specInfoAddr = *(int*)0x679C80;
bool firstAidSpec = *(int*)(specInfoAddr + heroID * 40) == 0 && *(int*)(specInfoAddr + heroID * 40 + 4) == 27;


*(int*)(specInfoAddr + heroID * 40) - тип специализации (0 - спец по вторичному навыку, 1 - спец по юниту, 2 - спец по ресурсу, 3 - спец по заклинанию; другие значения - особые специализации героев кампаний),
*(int*)(specInfoAddr + heroID * 40 + 4) - ID вторичного навыка, юнита, ресурса, закла.

* * *
Цитата
Код
char firstAidLevel = *(char*)(*(int*)(cmAddr + 0x53CC) + 201 + 27);


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

Код
_Hero_* hero[2]; // + 21452 // 0 - attacker, 1 - defender
_int32_ current_side; // +78528 0x132C0


Код
int currentSide = *(int*)(cmAddr + 0x132C0);
char firstAidLevel = *(char*)(*(int*)(cmAddr + currentSide * 4 + 0x53CC) + 201 + 27);


Спасибо сказали:
Go to the top of the pageAdd Nick
 
+Quote Post
igrik
сообщение 29 Jan 2017, 13:12 (Сообщение отредактировал igrik - 29 Jan 2017, 13:13)
Сообщение #555

Immortal
Сообщений: 589
Спасибо сказали: 891 раз




Цитата(AlexSpl @ 27 Jan 2017, 22:56) *
Я следил за темой не с самого начала и не припомню, чтобы кто-то раньше модифицировал HotA. Может, список изменений опубликовать? Вдруг кто заинтересуется? Ну и плагин в общий доступ, само собой.

Вы модифицируете основной код игры: участки кода, которые одинаковы и для HotA и для SoD и для WoG/ERA. Правки именно в хотовских фишек (которые в HotA.dll) я не увидел.


--------------------
Go to the top of the pageAdd Nick
 
+Quote Post
DedMorozzz
сообщение 29 Jan 2017, 16:51
Сообщение #556

God
Сообщений: 267
Спасибо сказали: 25 раз




Действительно, забыл про палатку противника...
Итого: добавил проверку на сторону, сделал нормальное округление, добавил бонус для специализации (левел юнита увеличивается на 1, при расчёте силы щита)


Но всё же мне кажется верным решением дать возможность вешать щит на целых юнитов. Собсно щит для этого и нужен - даёт возможность избежать урона
Тут я вижу 2 проблемы:
1. необходимо разрешить хилять целых юнитов
2. при расчёте передаче хода палатке убрать условие проверки, что все юниты целые

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

Куда копать? Как это разрешить

PS: igrik, вроде ток логика сбегания связана с кодом хоты.. надо будет проверить. Но СОД не установлен)
Go to the top of the pageAdd Nick
 
+Quote Post
AlexSpl
сообщение 29 Jan 2017, 17:20
Сообщение #557

Immortal
Сообщений: 798
Спасибо сказали: 555 раз




Поправил код получения heroID. Вы не учитывали текущую сторону currentSide.



Спасибо сказали:
Go to the top of the pageAdd Nick
 
+Quote Post
DedMorozzz
сообщение 29 Jan 2017, 18:00
Сообщение #558

God
Сообщений: 267
Спасибо сказали: 25 раз




Спс, добавил. А насчёт палатки есть идеи? Как разрешить хил целых юнитов?
Go to the top of the pageAdd Nick
 
+Quote Post
AlexSpl
сообщение 29 Jan 2017, 20:08
Сообщение #559

Immortal
Сообщений: 798
Спасибо сказали: 555 раз




Задача комплексная smile.gif Поэтому разобьём её на несколько подзадач.

1) Вот возможность лечить любой дружественный юнит, даже если он не терял HP, при ручном управлении палаткой:

Код
_PI->WriteHexPatch(0x47609A, "9090");


Требования: хотя бы один отряд должен потерять HP, чтобы работало, иначе палатка просто пропустит свой ход. Это уже следующая подзадача.


Спасибо сказали:
Go to the top of the pageAdd Nick
 
+Quote Post
AlexSpl
сообщение 29 Jan 2017, 21:52
Сообщение #560

Immortal
Сообщений: 798
Спасибо сказали: 555 раз




2) Делаем возможным лечение, даже если никто не получил урон:

Код
_PI->WriteHexPatch(0x4738B1, "EB");


Здесь есть подводный камень. Если лечение автоматическое, то на ходе палатки игра зависнет, так как не выбрана цель. Поэтому предлагаю сначала посмотреть, как работает теперь ручное лечение, а потом определить критерий, по которому будет выбираться отряд для автоматического лечения в случае, если никто не ранен smile.gif


Спасибо сказали:
Go to the top of the pageAdd Nick
 
+Quote Post

29 страниц V  « < 26 27 28 29 >
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 



Текстовая версия Сейчас: 27 September 2025 - 20:45
Copyright by Алексей Крючков
Strategy Gamez by GrayMage
Programming by Degtyarev Dmitry
  Яндекс.Метрика