-
AlexSpl
имя: Александр
- Эксперт
-
- Сообщения: 5587
- Зарегистрирован: 17 сен 2010, 12:58
- Пол:
- Награды: 14
-
-
- Поблагодарили: 2185 раз.
|
AlexSpl » 24 ноя 2021, 04:20
Вот что получилось: - Код: Выделить всё
// Метод возвращает эффективную боевую ценность существа из отряда // double __thiscall army::get_unit_combat_value(const army *this, int attack, int defense, bool shoot, int unused) double __thiscall army::get_unit_combat_value( const army *this, int argVar_0, int argVar_1, bool32 argVar_2, int unused) { bool Shoot; // bl int AdjustedAttack; // eax int AdjustedDefense; // eax double DefenseMod_0; // st7 int Side; // eax hero *Hero; // eax ECreatureType CreatureType; // eax hero *Hero_0; // eax int Side_0; // eax int SpellDurationHypnotize; // edx int Side_1; // ecx int Side_2; // ecx int Side_3; // ecx int BlessSpellDuration; // edx int MinDamage; // eax int MaxDamage; // ecx int *damage; // eax signed int CurseDamagePenalty; // edx bool isDamageLessThan1; // cc FCreatureFlags Flags; // eax double k; // st7 int StackCurrentHealth; // edi bool32 TotalHealth; // edx army *FirstStack; // ecx int Side_4; // esi int ArmyNum; // esi int Side_5; // ecx FCreatureFlags *__shifted(army,0x84) pFlags; // ecx int StackCurrentHealth_0; // eax double TotalHealth_0; // st7 double DefenseModifier; // [esp+14h] [ebp-20h] double AvgDamage; // [esp+1Ch] [ebp-18h] double DefenseMod; // [esp+24h] [ebp-10h] double Damage; // [esp+24h] [ebp-10h] double EffectiveFightValue; // [esp+24h] [ebp-10h] double AttackModifier; // [esp+2Ch] [ebp-8h] int AttackEffective; // [esp+30h] [ebp-4h]
AdjustedAttack = army::get_adjusted_attack(this, NULL, argVar_2); // Получаем эффективную атаку отряда (AttackEffective) AttackEffective = AdjustedAttack - akCreatureTypeTraits[this->Type].Attack - argVar_0; AdjustedDefense = army::get_adjusted_defense(this, NULL, 1); Shoot = argVar_2; // Модификатор защиты отряда по умолчанию равен 1.0 DefenseMod = 1.0; // Находим эффективную защиту отряда (DefenseEffective => argVar_0) argVar_0 = AdjustedDefense - akCreatureTypeTraits[this->Type].Defense - argVar_1; if ( argVar_2 ) { // Стрелковая атака (shoot == true) if ( !this->SpellDuration[AIR_SHIELD] ) goto NoShieldsModifiers; // Если на отряде висит заклинание Air Shield, то модификатор защиты отряда становится равным модификатору заклинания Air Shield DefenseMod_0 = this->AirShieldModifier; } else { // Рукопашная атака (shoot == false) if ( !this->SpellDuration[SHIELD] ) goto NoShieldsModifiers; // Если на отряде висит заклинание Shield, то модификатор защиты отряда становится равным модификатору заклинания Shield DefenseMod_0 = this->ShieldModifier; } DefenseMod = DefenseMod_0; NoShieldsModifiers: // Если на отряде висит эффект Stone Gaze (отряд превращён в камень), модификатор защиты отряда уполовинивается if ( this->SpellDuration[STONE] ) DefenseMod = DefenseMod * 0.5; // Получаем сторону, которая контролирует отряд, учитывая возможный гипноз (заклинание Hypnotize) if ( this->SpellDuration[HYPNOTIZE] ) Side = 1 - this->Side; else Side = this->Side; // Hero - герой, контролирующий отряд Hero = gpCombatManager->Hero[Side]; // Если есть герой (отряд не принадлежит нейтралам), умножаем модификатор защиты отряда на модификатор защиты героя if ( Hero ) DefenseMod = hero::GetDefenseFactor(Hero) * DefenseMod; // Итоговый модификатор защиты отряда равен (1.0 + 0.05 * DefenseEffective) * DefenseMod DefenseModifier = (argVar_0 * 0.05 + 1.0) * DefenseMod; // Теперь переходим к нахождению итогового модификатора атаки отряда AttackModifier // Определяем, возможна ли дистанционная атака if ( Shoot ) { CreatureType = this->Type; if ( CreatureType != _BALLISTA_ && CreatureType != ARROW_TOWER && ((this->Flags & SHOOTER) == 0 || this->Shots <= 0 || ((Hero_0 = gpCombatManager->Hero[combatMonster_GetSide(this)]) == 0 || !hero::IsWieldingArtifact(Hero_0, BOW_OF_THE_SHARPSHOOTER)) && army::enemy_is_adjacent(this, 0) || this->SpellDuration[FORGETFULNESS] && this->ForgetfulnessLevel >= ADVANCED) ) { // Дистанционная атака невозможна в следующих случаях: // 1. Отряд не умеет стрелять: // (this->Flags & SHOOTER) == 0 // 2. Закончились выстрелы: // this->Shots <= 0 // 3. Отряд с дистанционной атакой блокирован, а у героя нет артефакта Bow of the Sharpshooter: // ((Hero_0 = gpCombatManager->Hero[combatMonster_GetSide(this)]) == 0 || !hero::IsWieldingArtifact(Hero_0, BOW_OF_THE_SHARPSHOOTER)) && army::enemy_is_adjacent(this, 0) // 4. Отряд находится под действием заклинания Forgetfulness уровня Advanced или Expert: // this->SpellDuration[FORGETFULNESS] && this->ForgetfulnessLevel >= ADVANCED // 5. Кроме того, исключаем отряды Ballista и Arrow Tower (Баллиста будет учтена ниже): // CreatureType != _BALLISTA_ && CreatureType != ARROW_TOWER Shoot = FALSE; LOBYTE(argVar_2) = FALSE; } } // Модификатор атаки отряда равен AttackModifier = 1.0 + 0.05 * AttackEffective AttackModifier = AttackEffective * 0.05 + 1.0; // Если отряд с дистанционной атакой не может стрелять, модификатор атаки отряда уполовинивается if ( !Shoot && (this->Flags & SHOOTER) != 0 ) AttackModifier = AttackModifier * 0.5; // Отдельно рассмотрим Баллисту if ( this->Type == _BALLISTA_ ) { // Проверка, висит ли заклинание Hypnotize, нужна, видимо, для универсальности SpellDurationHypnotize = this->SpellDuration[HYPNOTIZE]; Side_0 = this->Side; Side_1 = SpellDurationHypnotize ? 1 - Side_0 : this->Side; // Как и проверка на наличие героя... if ( gpCombatManager->Hero[Side_1] ) { if ( SpellDurationHypnotize ) Side_2 = 1 - Side_0; else Side_2 = this->Side; // Если у героя имеется вторичный навык Artillery ступени выше Basic, т.е. Advanced или Expert, модификатор атаки Баллисты удваивается if ( gpCombatManager->Hero[Side_2]->SecondarySkills[ARTILLERY] > BASIC ) AttackModifier = AttackModifier + AttackModifier; // Игра не перестаёт беспокоиться о том, правильно ли она учла заклинание Гипноз :) if ( SpellDurationHypnotize ) Side_3 = 1 - Side_0; else Side_3 = this->Side; // Кроме того, модификатор атаки Баллисты увеличивается в 1.5/1.5/2.0 раза при наличии Basic/Advanced/Expert Artillery у героя, контролирующего Баллисту, соответственно AttackModifier = AttackModifier * ArtilleryEfficiency[gpCombatManager->Hero[Side_3]->SecondarySkills[ARTILLERY]]; } } // Заклинания Bless и Curse BlessSpellDuration = this->SpellDuration[BLESS]; if ( BlessSpellDuration || this->SpellDuration[CURSE] ) { MaxDamage = this->MaxDamage; MinDamage = this->MinDamage; argVar_0 = MinDamage + MaxDamage; // Считаем средний арифметический урон существа в отряде AvgDamage = (MinDamage + MaxDamage) / 2.0; if ( BlessSpellDuration ) { argVar_0 = MaxDamage + this->BlessDamageBonus; // Если на отряде висит заклинание Bless, урон существа в отряде (Damage) равен // сумме максимального урона существа и бонуса заклинания Bless (в игре это +1 для Advanced и Expert Bless) Damage = argVar_0; } else if ( this->SpellDuration[CURSE] ) { CurseDamagePenalty = this->CurseDamagePenalty; argVar_0 = 1; argVar_1 = MinDamage - CurseDamagePenalty; isDamageLessThan1 = MinDamage - CurseDamagePenalty < 1; damage = &argVar_0; if ( !isDamageLessThan1 ) damage = &argVar_1; // Если на отряде висит заклинание Curse, урон существа в отряде (Damage) равен // разности минимального урона существа и штрафа заклинания Curse (в игре это -1 для Advanced и Expert Curse), но не может стать меньше 1 ед. Damage = *damage; } else { // Этот код никогда не выполнится, но перестраховаться не помешает :) Damage = (MinDamage + MaxDamage) / 2.0; } Shoot = argVar_2; // Модификатор атаки отряда под Bless или Curse умножается на отношение урона существа из отряда к среднему урону этого существа // Для Bless модификатор увеличивается, для Curse - уменьшается AttackModifier = Damage / AvgDamage * AttackModifier; } // Если возможна дистанционная атака и отряд умеет атаковать дважды, модификатор атаки удваивается (даже если у отряда остался один выстрел) // Здесь почему-то забыли про отряды ближнего боя, атакующие дважды // Видимо, не захотелось связываться с ответкой if ( Shoot && (this->Flags & DOUBLEATTACK) != 0 ) AttackModifier = AttackModifier + AttackModifier; // Квадратный корень из произведения модификаторов атаки и защиты // Если есть прямоугольник, преврати его в эквивалентный квадрат :) k = sqrt(AttackModifier * DefenseModifier); Flags = this->Flags; // Итого, эффективная боевая ценность одного существа из отряда равна sqrt(AttackModifier * DefenseModifier) * FightValue EffectiveFightValue = k * akCreatureTypeTraits[this->Type].FightValue; // Теперь подкорректируем её для призванных существ, а заодно для боевых машин if ( (Flags & (SUMMON|SIEGEWEAPON)) != 0 ) { // Считаем текущее здоровье всего отряда if ( (Flags & CLONE) != 0 ) // Здоровье отряда клонов равно 1 ед., а не кол-ву существ в клонированном отряде, как может показаться StackCurrentHealth = 1; else StackCurrentHealth = this->AmountAlive * this->Health - this->HealthLost; Side_4 = this->Side; // Общее здоровье армии TotalHealth = 0; argVar_2 = 0; Side_5 = Side_4; ArmyNum = gpCombatManager->ArmyNum[Side_4]; FirstStack = gpCombatManager->Army[Side_5]; if ( ArmyNum > 0 ) { pFlags = &FirstStack->Flags; // Проходим по всем отрядам армии и находим суммарный остаток здоровья (общее здоровье армии), // пропуская призванные существа и боевые машины do { if ( (ADJ(pFlags)->Flags & (SUMMON|CANNOTMOVE|SIEGEWEAPON)) == 0 ) { if ( (ADJ(pFlags)->Flags & CLONE) != 0 ) StackCurrentHealth_0 = 1; else StackCurrentHealth_0 = ADJ(pFlags)->AmountAlive * ADJ(pFlags)->Health - ADJ(pFlags)->HealthLost; TotalHealth += StackCurrentHealth_0; } pFlags += 0x152; --ArmyNum; } while ( ArmyNum ); argVar_2 = TotalHealth; } // Эффективная боевая ценность существа из призванного отряда, который был убит, // или разрушенной боевой машины равна 0.1 (понятия не имею, зачем это нужно, и вызывается ли метод вообще для таких отрядов) if ( !StackCurrentHealth ) return 0.1; TotalHealth_0 = argVar_2; argVar_2 = TotalHealth + StackCurrentHealth; // Эффективная боевая ценность существа из призванного отряда делится на 1 + StackCurrentHealth / TotalHealth // (поделили числитель и знаменатель на TotalHealth, так можно, потому что TotalHealth_0 == TotalHealth) return TotalHealth_0 * EffectiveFightValue / (TotalHealth + StackCurrentHealth); } return EffectiveFightValue; }
Вот эта формула TotalHealth_0 * EffectiveFightValue / (TotalHealth + StackCurrentHealth) говорит о интересной особенности оценки существа из призванного отряда: чем больше здоровье призванного отряда, тем меньше ценность одного существа из этого отряда. Для обычных существ такой зависимости между здоровьем отряда и ценностью одного существа из него нет. Этот метод используется для нахождения полной ценности отряда army::get_total_combat_value() и там не простое умножение на кол-во существ в отряде. Может, та функция прольёт свет на этот нюанс? Кстати, не путать army и армию! В Героях 3 army - это _BattleStack_, т.е. отряд существ, а армия называется armyGroup Я ещё в комментах к коду забыл упомянуть о том, что AI игнорирует особенность некоторых существ с дистанционной атакой бить в полную силу в рукопашном бою. Т.е. уже маленький плагинчик, немножко усиливающий AI, вырисовывается. Также, но об этом я упомянул в комментах, AI не учитывает двойную атаку в рукопашном бою при нахождении ценности существа из отряда, что тоже можно исправить. Хотя... Есть подозрения, что эти "пробелы" уже зашиты в Fight Value существ, так что с этим нужно быть осторожнее. Fight Values тоже вычислялись по формулам, но уже, наверное, потерянным навсегда. * * * Поправил комменты про Катапульту. Флаг SIEGEWEAPON вводит в заблуждение. Он есть у всех боевых машин, а Siege - это осада. Нужно бы исправить. * * * А вот метод, который считает эффективную боевую ценность целого отряда: - Код: Выделить всё
// Метод возвращает эффективную боевую ценность отряда int __thiscall army::get_total_combat_value(army *Army, int attack, int defense) { int EffectiveFightValue; // eax ECreatureType Type; // eax int Side; // eax hero *Hero; // eax double unit_combat_value; // [esp+Ch] [ebp-8h] bool32 CanShoot; // [esp+10h] [ebp-4h]
// Отряд уничтожен, боевая ценность 0 if ( Army->AmountAlive <= 0 ) return 0; Type = Army->Type; // Выясняем, может ли отряд стрелять LOBYTE(CanShoot) = Type == _BALLISTA_ || Type == ARROW_TOWER || (Army->Flags & SHOOTER) != 0 && Army->Shots > 0 && (!Army->SpellDuration[HYPNOTIZE] ? (Side = Army->Side) : (Side = 1 - Army->Side), (Hero = gpCombatManager->Hero[Side]) != 0 && hero::IsWieldingArtifact(Hero, BOW_OF_THE_SHARPSHOOTER) || !combatManager::enemy_is_adjacent(gpCombatManager, Army, Army->Position, 0) && ((Army->Flags & DOUBLEWIDE) == 0 || !combatManager::enemy_is_adjacent( gpCombatManager, Army, Army->Position + (Army->LeftOrRightOrientation != 0 ? 1 : -1), 0))) && (!Army->SpellDuration[FORGETFULNESS] || Army->ForgetfulnessLevel < ADVANCED); // Получаем эффективную боевую ценность одного существа из отряда unit_combat_value = army::get_unit_combat_value(Army, attack, defense, CanShoot, 0); if ( (Army->Flags & CLONE) != 0 ) // Если отряд клон, эффективная боевая ценность отряда равна N * EffectiveFightValueOf1 / 5.0 *&EffectiveFightValue = (Army->AmountAlive * unit_combat_value / 5.0); else // Если не клон, эффективная боевая ценность отряда равна ArmyCurrentHealth * EffectiveFightValueOf1 / HealthOf1 *&EffectiveFightValue = ((Army->AmountAlive * Army->Health - Army->HealthLost) * unit_combat_value / Army->Health); return EffectiveFightValue; }
Тут, я думаю, без комментариев.
|