![]() |
Здравствуйте, гость ( Вход | Регистрация )
![]() ![]() |
![]() |
![]()
Сообщение
#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 отрабатывала корректно. |
|
|
![]()
Сообщение
#542
|
|
![]() God Сообщений: 267 Спасибо сказали: 25 раз ![]() |
Цитата Заметьте, что непосредственно _sprintf стек не освобождает, как и operator new(uint). Это делает инструкция add esp, 16 за обе(!) функции (3 аргумента dword функции _sprintf и один аргумент dword оператора new: 3 * 4 + 4 = 16. Поэтому если мы скипуем оригинальный код, мы должны поправить и указатель стека esp, чтобы инстукция add esp, 16 отрабатывала корректно. ух.. вроде понятно. Но только после объяснения. Да и то, как-то не очевидно) Подправлю текст "Текущее здоровье" на что нить поменьше, аля "текущее хп", ибо не влазит) Текст друг на друга налазит. Как раз в ММ архиве пытаюсь найти где он лежит (а я надеюсь он именно там) и постараюсь до конца осознать почему и как выше происходит ![]() |
|
|
![]()
Сообщение
#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 необязательно следует непосредственно за функцией и может корректировать указатель на стек сразу для нескольких функций. |
|
|
![]()
Сообщение
#544
|
|
![]() God Сообщений: 267 Спасибо сказали: 25 раз ![]() |
другими словами инструкция add гооврит что надо свободить в переменной(?) esp стек аргументов и сместить указатель назад на кол-во освобождённых аргументов (умноженных на 4). Верно?
И да, переименовал "Текущее здоровье", в "Текущее хп". Теперь и кол-во здоровья и щит в скобках корректно влазит в поле Текстовка находится тут - HotA_lng.lod файл - GENRLTXT.txt |
|
|
![]()
Сообщение
#545
|
|
Immortal Сообщений: 798 Спасибо сказали: 555 раз ![]() |
Если интересно, почитайте про __cdecl, __stdcall, __fastcall.
|
|
|
![]()
Сообщение
#546
|
|
Immortal Сообщений: 798 Спасибо сказали: 555 раз ![]() |
Я следил за темой не с самого начала и не припомню, чтобы кто-то раньше модифицировал HotA. Может, список изменений опубликовать? Вдруг кто заинтересуется? Ну и плагин в общий доступ, само собой.
|
|
|
![]()
Сообщение
#547
|
|
![]() God Сообщений: 267 Спасибо сказали: 25 раз ![]() |
Вроде нашел чёт по теме - http://natalia.appmat.ru/c&c++/dll.html
Но картина не складывается... Во всех свои ф-ях на С++ я использую только _stdcall Цитата При использовании соглашения о вызовах _fastcall первые два параметра, размер которых не больше двойного слова, передаются через регистры ECX и EDX. Остальные параметры передаются через стек. Я так понимаю что использую только регистры. Но их больше чем 2. И eax есть и прочее, это получеается тоже регистры же? Тогда что такое стек... Да и в целом пока что не понял что именно надо о этих соглашениях почитать. Слишком много разной инфы) -------------------------------------------------------------------------------------------------------------------------------------- Я следил за темой не с самого начала и не припомню, чтобы кто-то раньше модифицировал HotA. Может, список изменений опубликовать? Вдруг кто заинтересуется? Ну и плагин в общий доступ, само собой. Да, думаю как будет готовая версия, когда дойдёт до определённого этапа - выложить в шапку дллку и код С варнингом что может вылетать или не работать в случае апдейта версии хоты) И обозвать как нить "кастомный баланс хоты для шаблона Джебус". Но думается надо бы и уточнить у разработчиков Хоты, вдруг они будут против. В этом случае уберу всё что завязано на хоте и выложу (аля сбегание от монстров, явно логика хоты юзается) Но текущая цель такая, что бы под выпавшие навыки подстраивалась игра. Другими словами что бы всё было полезным. Палатку как причешу, хочу вернутся к дипломатии... сколько раз играли и она выпадала - ниразу не брали. Слишком сомнительные бонусы получились. Обсудили такую идею, на все повышалки за 1к годы, при наличии навыка дипломатии сделать цену 1.5/1/0.5к голды и получить +2 к статам, а не +1 После этого думается навык станет ощутимо привлекательнее И забегая на перёд имеется идея сделать грамотность для героя сумма статов которого выше некого числа, к примеру 15-20 что бы давалось +3/6/9 знания Почему сумму статов учитывать, что бы левак не бегал и не спамил молниями. Да и леваку это и так норм навык, если надо что нить мейну передать А вот если это получит меин... уже будет досадно, особено когда преследую цель убрать бесполезные навыки Но тут загвоздка, я понятия не имею как построить логику которая при повышении статов пересчитывать бонус от уже полученого вторичного навыка |
|
|
![]()
Сообщение
#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, а остальные через стек. |
|
|
![]()
Сообщение
#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 едениц щита что андеду припарка.. Но это надо будет прикинуть, посчитать, что бы не сделать слишком сильным навык Но прямо сейчас другая проблема. Если у юнита фул хп, я не могу на него щит кинуть. Даже если его атаковали уже. К примеру ударили пачку кентавров но так что убили ровно по кол-ву хп. Я не могу повесть щит Как убрать ограничение для хила, что только юниты с не фул здоровьем могут быть вылечены палаткой? |
|
|
![]()
Сообщение
#550
|
|
Immortal Сообщений: 798 Спасибо сказали: 555 раз ![]() |
Цитата Как убрать ограничение для хила, что только юниты с не фул здоровьем могут быть вылечены палаткой? Это можно сделать, но я бы не стал. Думайте не о стеке в целом, а о индивидуальном воине в нём. Ударили так, что погиб - лечить некого. Иначе палатка будет накладывать щит уже на другого воина в стеке. Понимаете идею? Палатка не воскрешает, а лечит и накладывает щит на ещё живого воина в стеке. |
|
|
![]()
Сообщение
#551
|
|
![]() God Сообщений: 267 Спасибо сказали: 25 раз ![]() |
Верно. И идея в том, что бы палатка была элементом тактики. Что бы можно было напасть на противника. Прокинуть щит на отряд и им ударить. С расчётом, что ответный урон будет поглощён щитом, ну или частично им поглощён
В противном случае если нажать всеми припас. А так всегда и происходит, то палатку можно будет использовать только на 2м кругу, что ощутимо снижает её ценность С другой стороны это надо взять и попробовать, если будет слишком сильно - всегда можно будет отключить возможность прокидывания щита на целых юнитов. Ну иль уменьшить размер поглощения урона К примеру если был хил то щит в полном объёме. Если целые хп, то щит в пол силы. Это будет совсем не трудно сделать И это в целом даст тоже ещё элемент в бою, выбирай что лучше, пол щита но заранее. Или отхилить юнита после получения урона и более сильный щит. Может так и стоит сделать |
|
|
![]()
Сообщение
#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. Т.е. округляет всегда в меньшую сторону Где тут ошибка? |
|
|
![]()
Сообщение
#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. А как получить теперь её что бы сравнить с этим значением? |
|
|
![]()
Сообщение
#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); |
|
|
![]()
Сообщение
#555
|
|
![]() Immortal Сообщений: 589 Спасибо сказали: 891 раз ![]() |
Я следил за темой не с самого начала и не припомню, чтобы кто-то раньше модифицировал HotA. Может, список изменений опубликовать? Вдруг кто заинтересуется? Ну и плагин в общий доступ, само собой. Вы модифицируете основной код игры: участки кода, которые одинаковы и для HotA и для SoD и для WoG/ERA. Правки именно в хотовских фишек (которые в HotA.dll) я не увидел. -------------------- |
|
|
![]()
Сообщение
#556
|
|
![]() God Сообщений: 267 Спасибо сказали: 25 раз ![]() |
Действительно, забыл про палатку противника...
Итого: добавил проверку на сторону, сделал нормальное округление, добавил бонус для специализации (левел юнита увеличивается на 1, при расчёте силы щита) Но всё же мне кажется верным решением дать возможность вешать щит на целых юнитов. Собсно щит для этого и нужен - даёт возможность избежать урона Тут я вижу 2 проблемы: 1. необходимо разрешить хилять целых юнитов 2. при расчёте передаче хода палатке убрать условие проверки, что все юниты целые Так же, возможно, необходимо сделать так что бы иконка появлялась всегда, при наведении на юнита, если это не входит в пункт 1... Куда копать? Как это разрешить PS: igrik, вроде ток логика сбегания связана с кодом хоты.. надо будет проверить. Но СОД не установлен) |
|
|
![]()
Сообщение
#557
|
|
Immortal Сообщений: 798 Спасибо сказали: 555 раз ![]() |
|
|
|
![]()
Сообщение
#558
|
|
![]() God Сообщений: 267 Спасибо сказали: 25 раз ![]() |
Спс, добавил. А насчёт палатки есть идеи? Как разрешить хил целых юнитов?
|
|
|
![]()
Сообщение
#559
|
|
Immortal Сообщений: 798 Спасибо сказали: 555 раз ![]() |
Задача комплексная
![]() 1) Вот возможность лечить любой дружественный юнит, даже если он не терял HP, при ручном управлении палаткой: Код _PI->WriteHexPatch(0x47609A, "9090"); Требования: хотя бы один отряд должен потерять HP, чтобы работало, иначе палатка просто пропустит свой ход. Это уже следующая подзадача. |
|
|
![]()
Сообщение
#560
|
|
Immortal Сообщений: 798 Спасибо сказали: 555 раз ![]() |
2) Делаем возможным лечение, даже если никто не получил урон:
Код _PI->WriteHexPatch(0x4738B1, "EB"); Здесь есть подводный камень. Если лечение автоматическое, то на ходе палатки игра зависнет, так как не выбрана цель. Поэтому предлагаю сначала посмотреть, как работает теперь ручное лечение, а потом определить критерий, по которому будет выбираться отряд для автоматического лечения в случае, если никто не ранен ![]() |
|
|
![]() ![]() |
Текстовая версия | Сейчас: 27 September 2025 - 20:45 |
Copyright by Алексей Крючков
![]() Programming by Degtyarev Dmitry |
|