Объявления
Поздравляем
Roman2211


Друзья, если не получается зарегистрироваться, напишите на почту vdv_forever@bk.ru.
Я оторву свою задницу от всех дел и обязательно Вас активирую! :smile10:
Добро пожаловать на геройский форум! :smile25:

База данных IDA от void17

Герои Меча и Магии III: Возрождение Эрафии, Герои Меча и Магии III Дыхание Смерти, Герои Меча и Магии III Клинок Армагеддона, Герои Меча и Магии III Хроники Героев
offlineRoseKavalier  
Мастер
Мастер
 
Сообщения: 331
Зарегистрирован: 23 сен 2017, 17:00
Пол: Не указан
Поблагодарили: 234 раз.

Re: База данных IDA от void17

Сообщение RoseKavalier » 24 ноя 2021, 13:55

The SIEGE_WEAPON flag is obtained directly from crtraits.txt, same as every other creature flag.
Вернуться к началу

offlineigrik  
Подмастерье
Подмастерье
 
Сообщения: 108
Зарегистрирован: 14 сен 2017, 12:35
Пол: Не указан
Поблагодарили: 84 раз.

Re: База данных IDA от void17

Сообщение igrik » 24 ноя 2021, 15:42

Классная тема. Как жаль что не появилась она лет так 5 назад.

Кстати, void_17, я видел вы в ВК делаете "Обучение".
Я недавно малость делал такой "начальный гайд", но у вас это выходит куда лучше.
http://wforum.heroes35.net/showthread.php?tid=5706
Вернуться к началу

offlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 5587
Зарегистрирован: 17 сен 2010, 12:58
Пол: Мужчина
Награды: 14
Высшая медаль (1) Победителю турнира по HMM1_TE (2) Победителю этапа по HMM1 (1) Победителю этапа по HMM2 (1) Лучшему из лучших (1) 2 место 1 этапа по HMM1 (1)
3 место 1 этапа по HMM1 (1) 1 место 2 этапа по HMM2 (1) Победителю турнира по KB (2) Победителю турнира по KB (1) Грандмастер оффлайн-турниров (1) Боевой шлем (1)
Поблагодарили: 2185 раз.

Re: База данных IDA от void17

Сообщение AlexSpl » 24 ноя 2021, 17:02

army::get_loss_combat_value():

Код: Выделить всё
// Метод определяет эффективную боевую ценность потерь при нанесении урона
int __thiscall army::get_loss_combat_value(
        const army *this,
        int attack,
        int defense,
        bool shoot,
        int damage,
        bool isFightValueCapped)
{
  int Health; // ecx
  int HealthLost_0; // edi
  int HealthLost; // esi
  double unit_combat_value; // [esp+Ch] [ebp-8h]

  // Получаем эффективную боевую ценность существа из отряда
  unit_combat_value = army::get_unit_combat_value(this, attack, defense, shoot, 0);
  if ( (this->Flags & CLONE) != 0 )
  {
    // Если отряд клон, любой урон убивает, поэтому возвращаем total_combat_value отряда
    return (this->AmountAlive * unit_combat_value / 5.0);
  }
  else
  {
    // AI в каких-то случаях ограничивает эффективную боевую ценность существа значением 1000.0
    if ( isFightValueCapped )
      unit_combat_value = 1000.0;
    Health = this->Health;
    HealthLost = this->HealthLost;
    HealthLost_0 = 0;
    // Если от урона погибает одно или несколько существ из отряда, берём потерянное здоровье
    if ( HealthLost + damage % Health >= Health )
      HealthLost_0 = HealthLost;
    // Возвращаем эффективную боевую ценность потерь
    return ((damage + HealthLost_0) * unit_combat_value / Health);
  }
}

Используется, например, для оценки эффективности ударных заклинаний и заклинаний, связанных с потерей здоровья. Пример - оценка заклинания/эффекта Poison:

Код: Выделить всё
int __thiscall type_AI_spellcaster::get_poison_value(
        type_AI_spellcaster *this,
        army *Army,
        int a3,
        int a4,
        int a5,
        int a6,
        int a7)
{
  bool CanShoot; // al
  int HalfHealth; // [esp-Ch] [ebp-10h]
  bool isFightValueCapped; // [esp-8h] [ebp-Ch]

  if ( this->stackDestroyedOrBattleMachine )
    return 0;
  isFightValueCapped = this->IsFightValueCapped;
  HalfHealth = (Army->Health - Army->HealthLost) / 2;
  CanShoot = army::can_shoot(Army, NULL);
  return army::get_loss_combat_value(Army, this->attack, this->defense, *&CanShoot, HalfHealth, isFightValueCapped);
}

* * *
Метод type_AI_spellcaster::get_damage_value():

Код: Выделить всё
// Метод возвращает оценку урона, нанесённого отряду заклинанием
// int __thiscall type_AI_spellcaster__get_damage_value(type_AI_spellcaster *this, enum SpellID spell, int, const struct hero *hero, const struct army *army)
int __thiscall type_AI_spellcaster::get_damage_value(
        type_AI_spellcaster *this,
        enum SpellID spell,
        int damage,
        const struct hero *hero,
        const struct army *army)
{
  struct army *army_0; // esi
  int spellDamage; // eax
  bool field_29; // dl
  double spellChance; // st7
  int expectedDamage_0; // ebx
  bool isFightValueCapped; // dl
  int expectedDamage_1; // eax
  int CanShoot; // eax
  int loss_combat_value; // ebx
  ECreatureType Type; // eax
  int total_combat_value; // eax
  int Damage; // [esp-8h] [ebp-14h]
  bool IsFightValueCapped_0; // [esp-4h] [ebp-10h]
  int total_hit_points; // [esp+1Ch] [ebp+10h] FORCED BYREF
  int spellDamage_0; // [esp+20h] [ebp+14h] FORCED
  int expectedDamage; // [esp+20h] [ebp+14h] FORCED BYREF

  army_0 = army;
  if ( (army->Flags & CANNOTMOVE) != 0 )
    return 0;
  if ( army->Type == ARROW_TOWER )
    return 0;
  spellDamage = combatManager::ModifySpellDamage(gpCombatManager, damage, spell, this->hero[0], hero, army, 0);
  // Ещё одно булево поле type_AI_spellcaster
  field_29 = this->field_29;
  spellDamage_0 = spellDamage;
  spellChance = combatManager::SpellCastWorkChance(gpCombatManager, spell, this->side[0], army_0, 0, 1, field_29);
  *&expectedDamage = spellDamage_0;
  // Математическое ожидание урона
  expectedDamage_0 = (spellChance * *&expectedDamage);
  if ( expectedDamage_0 <= 0 )
    return 0;
  expectedDamage = (spellChance * *&expectedDamage);
  total_hit_points = army::get_total_hit_points(army_0, 0);
  expectedDamage_1 = &expectedDamage;
  // Если урон больше, чем здоровье отряда, ограничиваем урон здоровьем отряда
  if ( expectedDamage_0 >= total_hit_points )
    expectedDamage_1 = &total_hit_points;
  isFightValueCapped = this->IsFightValueCapped;
  *&IsFightValueCapped_0 = *&isFightValueCapped;
  Damage = *expectedDamage_1;
  LOBYTE(CanShoot) = army::can_shoot(army_0, 0);
  // Получаем эффективную боевую ценность потерь
  loss_combat_value = army::get_loss_combat_value(
                        army_0,
                        this->attack,
                        this->defense,
                        CanShoot,
                        Damage,
                        *&IsFightValueCapped_0);
  // Отдельный случай для отрядов под заклинаниями Blind, Stone Gaze, Paralyze, а также боевых машин
  if ( army_0->SpellDuration[BLIND]
    || army_0->SpellDuration[STONE]
    || army_0->SpellDuration[PARALYZE]
    || (army_0->Flags & CANNOTMOVE) != 0
    || (Type = army_0->Type, Type == _FIRST_AID_TENT_)
    || Type == _AMMO_CART_ )
  {
    // Получаем эффективную боевую ценность отряда
    total_combat_value = army::get_total_combat_value(army_0, this->attack, this->defense);
    // Если эффективная боевая ценность потерь меньше эффективной боевой ценности отряда под Blind, Stone Gaze, Paralyze или боевой машины,
    // то в качестве эффективной боевой ценности потерь возвращаем 2 * loss_combat_value - total_combat_value
    if ( total_combat_value > loss_combat_value )
      return 2 * loss_combat_value - total_combat_value;
  }
  // Возвращаем эффективную боевую ценность потерь
  return loss_combat_value;
}

Вот это: return 2 * loss_combat_value - total_combat_value; по идее, означает, что AI не будет кастовать одиночное ударное заклинание на временно обездвиженные отряды, если заклинание уничтожает меньше половины отряда, чтобы не снимать эффект раньше времени (а Stone Gaze включили, наверное, из-за уполовинивания урона). И даже если заклинание уничтожает больше половины отряда, то ценность всё равно остаётся маленькой по сравнению с полной ценностью потерь. Получается, AI будет кастовать одиночные ударные заклинания на такие отряды, только если остальные окажутся достаточно слабыми. А при касте по площади отряды под Blind, Stone Gaze и Paralyze и вовсе могут отнимать ценность, что тоже логично.
Вернуться к началу

offlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 5587
Зарегистрирован: 17 сен 2010, 12:58
Пол: Мужчина
Награды: 14
Высшая медаль (1) Победителю турнира по HMM1_TE (2) Победителю этапа по HMM1 (1) Победителю этапа по HMM2 (1) Лучшему из лучших (1) 2 место 1 этапа по HMM1 (1)
3 место 1 этапа по HMM1 (1) 1 место 2 этапа по HMM2 (1) Победителю турнира по KB (2) Победителю турнира по KB (1) Грандмастер оффлайн-турниров (1) Боевой шлем (1)
Поблагодарили: 2185 раз.

Re: База данных IDA от void17

Сообщение AlexSpl » 24 ноя 2021, 21:07

А вот как прекрасно выглядит оценка одиночных ударных заклинаний:

Код: Выделить всё
int __thiscall type_AI_spellcaster::get_damage_spell_value(type_AI_spellcaster *this, type_enchant_data data)
{
  return type_AI_spellcaster::get_damage_value(
           this,
           data.Spell,
           data.SpellPower * akSpelltraits[data.Spell].Eff_Power + akSpelltraits[data.Spell].Effect[data.SchoolLevel],
           this->Hero[1],
           data.Army);
}

Правда, как писал RoseKavalier, type_enchant_data - хитрая структура: для разных типов заклинаний поля означают разные вещи. Здесь я задал имена полей для одиночных ударных заклинаний, чтобы было понятно.
Вернуться к началу

offlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 5587
Зарегистрирован: 17 сен 2010, 12:58
Пол: Мужчина
Награды: 14
Высшая медаль (1) Победителю турнира по HMM1_TE (2) Победителю этапа по HMM1 (1) Победителю этапа по HMM2 (1) Лучшему из лучших (1) 2 место 1 этапа по HMM1 (1)
3 место 1 этапа по HMM1 (1) 1 место 2 этапа по HMM2 (1) Победителю турнира по KB (2) Победителю турнира по KB (1) Грандмастер оффлайн-турниров (1) Боевой шлем (1)
Поблагодарили: 2185 раз.

Re: База данных IDA от void17

Сообщение AlexSpl » 24 ноя 2021, 22:30

Выбор заклинания (основной свитч):

Код: Выделить всё
void __thiscall type_AI_spellcaster::consider_spell(type_AI_spellcaster *this, type_spell_choice *spell_choice)
{
  enum SpellID Spell; // edi
  type_AI_spellcaster *spell_caster; // esi
  int SchoolLevel; // eax
  int damage; // eax
  int enemySide; // ecx
  int stacksNum; // edx
  int stackOffset; // ecx
  int side; // edi
  enum SpellID SpellID; // ecx
  int stackNum; // ecx
  int stackIx; // edi
  int damage_value; // eax
  TSpellTraits *DefaultSpell; // edi
  int areaSpellDamage; // ecx
  int hex; // edi
  int area_effect_value; // eax
  int creaturesNum; // edi
  int AIValue; // ecx
  type_spell_choice spell_choice_copy; // [esp+Ch] [ebp-38h] BYREF
  enum SpellID spell; // [esp+30h] [ebp-14h]
  const struct hero *hero; // [esp+34h] [ebp-10h]
  type_AI_spellcaster *caster; // [esp+38h] [ebp-Ch]
  int i; // [esp+38h] [ebp-Ch] FORCED
  signed int damageToEnemies; // [esp+3Ch] [ebp-8h]
  int damage_0; // [esp+40h] [ebp-4h]
  int enemySide_0; // [esp+4Ch] [ebp+8h]
  int stackOffset_0; // [esp+4Ch] [ebp+8h]
  signed int damageToAllies; // [esp+4Ch] [ebp+8h]
  int areaSpellDamage_0; // [esp+4Ch] [ebp+8h]

  Spell = spell_choice->SpellID;
  spell_caster = this;
  caster = this;
  switch ( Spell )
  {
    case EARTHQUAKE:
      type_AI_spellcaster::consider_earthquake(this, spell_choice);
      break;
    case CHAIN_LIGHTNING:
      type_AI_spellcaster::consider_chain_lightning(this, spell_choice);
      break;
    case FROST_RING:
    case FIREBALL:
    case INFERNO:
    case METEOR_SHOWER:
      areaSpellDamage = akSpelltraits[Spell].Effect[spell_choice->SchoolLevel]
                      + spell_choice->SpellPower * akSpelltraits[Spell].Eff_Power;
      hex = 0;
      areaSpellDamage_0 = areaSpellDamage;
      do
      {
        if ( hex < 0 || hex % 17 && hex % 17 != 16 )
        {
          area_effect_value = type_AI_spellcaster::get_area_effect_value(
                                spell_choice->SpellID,
                                areaSpellDamage_0,
                                spell_choice->SchoolLevel,
                                hex);
          if ( area_effect_value > spell_choice->spell_value )
          {
            spell_choice->monster_position = hex;
            spell_choice->spell_value = area_effect_value;
            spell_choice->spellApproved = 1;
          }
        }
        ++hex;
      }
      while ( hex < 187 );
      break;
    case DEATH_RIPPLE:
    case DESTROY_UNDEAD:
    case ARMAGEDDON:                            // inline void  type_AI_spellcaster::consider_mass_damage(struct type_spell_choice &)const
      SchoolLevel = spell_choice->SchoolLevel;
      damageToEnemies = 0;
      damage = spell_choice->SpellPower * akSpelltraits[Spell].Eff_Power + akSpelltraits[Spell].Effect[SchoolLevel];
      hero = this->Hero[1];
      enemySide = this->Side[1];
      damage_0 = damage;
      enemySide_0 = enemySide;
      stacksNum = gpCombatManager->ArmyNum[enemySide];
      i = stacksNum - 1;
      if ( stacksNum )
      {
        stackOffset = 0x548 * (i + 14 * enemySide + 7 * enemySide);
        stackOffset_0 = 0x548 * (i + 14 * enemySide_0 + 7 * enemySide_0);
        ++i;
        while ( 1 )
        {
          damageToEnemies += type_AI_spellcaster::get_damage_value(
                               spell_caster,
                               Spell,
                               damage,
                               hero,
                               (gpCombatManager->Army[0] + stackOffset));
          --i;
          stackOffset_0 -= 1352;
          damage = damage_0;
          if ( !i )
            break;
          stackOffset = stackOffset_0;
        }
      }
      side = spell_caster->Side[0];
      SpellID = spell_choice->SpellID;
      hero = spell_caster->Hero[0];
      spell = SpellID;
      damageToAllies = 0;
      stackNum = gpCombatManager->ArmyNum[side];
      if ( stackNum )
      {
        stackIx = stackNum - 1 + 14 * side + 7 * side;
        i = stackNum;
        while ( 1 )
        {
          damage_value = type_AI_spellcaster::get_damage_value(
                           spell_caster,
                           spell,
                           damage,
                           hero,
                           &gpCombatManager->Army[0][stackIx--]);
          damageToAllies += damage_value;
          if ( !--i )
            break;
          damage = damage_0;
        }
      }
      spell_choice->spell_value = type_AI_spellcaster::get_mass_damage_effect(
                                    spell_caster,
                                    damageToEnemies,
                                    damageToAllies);
      spell_choice->spellApproved = 1;
      break;
    case DISPEL:
      type_AI_spellcaster::consider_enchantment(this, spell_choice, this->Side[0]);
      if ( spell_choice->SchoolLevel == ADVANCED )
        type_AI_spellcaster::consider_enchantment(spell_caster, spell_choice, spell_caster->Side[1]);
      if ( spell_choice->SchoolLevel == EXPERT_SSKILL )
      {
        qmemcpy(&spell_choice_copy, spell_choice, sizeof(spell_choice_copy));
        type_AI_spellcaster::consider_enchantment(caster, &spell_choice_copy, caster->Side[1]);
        spell_caster = caster;
        spell_choice->spell_value += spell_choice_copy.spell_value;
      }
      goto LABEL_17;
    case RESURRECTION:
    case ANIMATE_DEAD:
      type_AI_spellcaster::consider_resurrect(this, spell_choice);
      break;
    case SACRIFICE:
      type_AI_spellcaster::consider_sacrifice(this, spell_choice);
      break;
    case TELEPORT:
      type_AI_spellcaster::consider_teleport(this, spell_choice);
      break;
    case SUMMON_FIRE_ELEMENTAL:
    case SUMMON_EARTH_ELEMENTAL:
    case SUMMON_WATER_ELEMENTAL:
    case SUMMON_AIR_ELEMENTAL:
      if ( !this->hexDeadArmyOrWarMachine && combatManager::AbleToSummonElemental(gpCombatManager, Spell, this->Side[0]) )
      {
        creaturesNum = spell_choice->SpellPower * akSpelltraits[spell_choice->SpellID].Effect[spell_choice->SchoolLevel];
        if ( LOBYTE(spell_caster->IsFightValueCapped) )
        {
          spell_choice->spellApproved = 1;
          spell_choice->spell_value = 1000 * creaturesNum;
        }
        else
        {
          AIValue = akCreatureTypeTraits[get_elemental_type(spell_choice->SpellID)].AIValue;
          spell_choice->spellApproved = 1;
          spell_choice->spell_value = creaturesNum * AIValue;
        }
      }
      break;
    default:
LABEL_17:
      DefaultSpell = &akSpelltraits[spell_choice->SpellID];
      if ( (DefaultSpell->Flags & (EXPERTMASSVERSION|SINGLESHOOTINGSTACK|SINGLETARGET)) != 0 )
      {
        if ( DefaultSpell->TargetType < 0
          || (type_AI_spellcaster::consider_enchantment(spell_caster, spell_choice, spell_caster->Side[0]),
              DefaultSpell->TargetType <= 0) )
        {
          type_AI_spellcaster::consider_enchantment(spell_caster, spell_choice, spell_caster->Side[1]);
        }
      }
      break;
  }
}


type_AI_spellcaster::consider_single_enchantment():

Код: Выделить всё
type_spell_choice *__thiscall type_AI_spellcaster::consider_single_enchantment(
        type_AI_spellcaster *this,
        type_spell_choice *spellChoice,
        int side)
{
  type_spell_choice *result; // eax
  int side_0; // edx
  combatManager *CombatManager; // ecx
  type_spell_choice *SpellChoice; // edi
  type_AI_spellcaster *caster; // esi
  enum SpellID SpellID; // eax
  int (__thiscall *enchantment_function)(type_AI_spellcaster *, type_spell_choice *, type_enchant_data); // eax
  int ArmyNum; // ebx
  int StackOffset; // eax
  army *Army; // ebx
  int CreatureType; // eax
  type_AI_spellcaster *caster_1; // edi
  int spellValue; // esi
  int ArmyNum_0; // ebx
  int AISide; // edi
  army *CurrentArmy; // ebx
  int jArmy; // edx
  int AIArmyNum; // esi
  army *__shifted(combatManager,0x577C) pSpellDuration; // eax
  FCreatureFlags Flags; // ecx
  _BOOL1 should_attack_now; // al
  type_enchant_data enchant_data; // [esp-14h] [ebp-34h] BYREF
  int (__thiscall *weighting_function)(type_AI_spellcaster *, type_spell_choice *, type_enchant_data); // [esp+Ch] [ebp-14h]
  int iArmy; // [esp+10h] [ebp-10h]
  int iStackOffset; // [esp+14h] [ebp-Ch]
  type_AI_spellcaster *caster_0; // [esp+18h] [ebp-8h]
  army *Army_0; // [esp+1Ch] [ebp-4h]

  SpellChoice = spellChoice;
  caster = this;
  SpellID = spellChoice->SpellID;
  caster_0 = this;
  enchantment_function = type_AI_spellcaster::get_enchantment_function(this, SpellID);
  CombatManager = gpCombatManager;
  side_0 = side;
  weighting_function = enchantment_function;
  result = 0;
  ArmyNum = gpCombatManager->ArmyNum[side];
  Army_0 = 0;
  iArmy = 0;
  if ( ArmyNum <= 0 )
    return result;
  StackOffset = 0x6EE8 * side;
  for ( iStackOffset = 0x6EE8 * side; ; StackOffset = iStackOffset )
  {
    Army = (CombatManager->Army[0] + StackOffset);
    // SUMMON_BOAT = 0, пробовал менять типы, не помогает
    // StackOffset превратить в индекс тоже не получается
    if ( !*(&CombatManager->Army[SUMMON_BOAT][SUMMON_BOAT].SpellDuration[BLIND] + StackOffset)
      && !*(&CombatManager->Army[SUMMON_BOAT][SUMMON_BOAT].SpellDuration[STONE] + StackOffset)
      && !*(&CombatManager->Army[SUMMON_BOAT][SUMMON_BOAT].SpellDuration[PARALYZE] + StackOffset)
      && (*(&CombatManager->Army[0][0].Flags + StackOffset) & CANNOTMOVE) == 0 )
    {
      CreatureType = *(&CombatManager->Army[0][0].Type + StackOffset);
      if ( CreatureType != _FIRST_AID_TENT_ && CreatureType != _AMMO_CART_ )
      {
        if ( combatManager::ValidSpellTargetArmy(
               CombatManager,
               SpellChoice->SpellID,
               caster->Side[0],
               Army,
               1,
               caster->field_29)
          && !Army->SpellDuration[SpellChoice->SpellID] )
        {
          qmemcpy(&enchant_data, SpellChoice, sizeof(enchant_data));
          caster_1 = caster_0;
          spellValue = (weighting_function)(
                         caster_0,
                         Army,
                         enchant_data.SpellID,
                         enchant_data.SchoolLevel,
                         enchant_data.SpellPower,
                         enchant_data.SpellDuration,
                         enchant_data.Army);
          if ( Army->SpellDuration[MAGIC_MIRROR]
            && side != caster_1->Side[0]
            && spellValue > 0
            && spellChoice->SpellID != DISPEL )
          {
            spellValue = 2 * spellValue * (50 - getMagicMirrorEffect(Army)) / 100;
          }
          if ( spellValue > spellChoice->spell_value )
          {
            side_0 = side;
            Army_0 = Army;
            SpellChoice = spellChoice;
            spellChoice->spell_value = spellValue;
            caster = caster_0;
            spellChoice->monster_position = Army->Position;
            CombatManager = gpCombatManager;
            goto NextIteration;
          }
          caster = caster_0;
          SpellChoice = spellChoice;
        }
        CombatManager = gpCombatManager;
        side_0 = side;
      }
    }
NextIteration:
    iStackOffset += 0x548;
    ArmyNum_0 = CombatManager->ArmyNum[side_0];
    if ( ++iArmy >= ArmyNum_0 )
      break;
  }
  result = Army_0;
  if ( Army_0 )
  {
    AISide = caster->Side[0];
    if ( side_0 == AISide )
    {
      CurrentArmy = &CombatManager->Army[CombatManager->CurrentTroopSide][CombatManager->CurrentTroopIndex];
      if ( Army_0 == CurrentArmy )
        goto ShouldAttackNow;
      AIArmyNum = CombatManager->ArmyNum[AISide];
      jArmy = 0;
      if ( AIArmyNum <= 0 )
        goto ShouldAttackNow;
      pSpellDuration = &CombatManager->Army[AISide][SUMMON_BOAT].SpellDuration[STONE];
      while ( 1 )
      {
        Flags = ADJ(pSpellDuration)->Army[0][0].Flags;
        if ( (Flags & (CANNOTMOVE|SIEGEWEAPON)) == 0
          && !ADJ(pSpellDuration)->Army[SUMMON_BOAT][SUMMON_BOAT].SpellDuration[BLIND]
          // С перечислителями творится что-то странное: не могу поменять 70 на STONE
          && !ADJ(pSpellDuration)->Army[0][0].SpellDuration[70]
          && !ADJ(pSpellDuration)->Army[SUMMON_BOAT][SUMMON_BOAT].SpellDuration[PARALYZE]
          && (Flags & DONE) == 0
          && ADJ(pSpellDuration)->Army != CurrentArmy )
        {
          break;
        }
        ++jArmy;
        ++pSpellDuration;
        if ( jArmy >= AIArmyNum )
          goto ShouldAttackNow;
      }
      if ( (akSpelltraits[spellChoice->SpellID].Flags & DEFENSIVE) != 0 )
ShouldAttackNow:
        should_attack_now = 1;
      else
        should_attack_now = 0;
    }
    else
    {
      should_attack_now = type_AI_spellcaster::should_attack_now(caster, Army_0);
    }
    spellChoice->spellApproved = should_attack_now;
    result = spellChoice;
    if ( spellChoice->SpellID == HASTE )
      spellChoice->spellApproved = 1;
  }
  return result;
}

Функция была сломана из-за неправильного типа this (почему-то IDA упорно хочет combatManager*, но это не он).
Последний раз редактировалось AlexSpl 25 ноя 2021, 01:51, всего редактировалось 3 раз(а).
Вернуться к началу

offlineАватара пользователя
void_17  
имя: имя
Ветеран
Ветеран
 
Сообщения: 548
Зарегистрирован: 25 апр 2021, 15:05
Откуда: Оттуда
Пол: Мужчина
Поблагодарили: 132 раз.

Re: База данных IDA от void17

Сообщение void_17 » 25 ноя 2021, 00:55

igrik писал(а):

Классная тема. Как жаль что не появилась она лет так 5 назад.

Кстати, void_17, я видел вы в ВК делаете "Обучение".
Я недавно малость делал такой "начальный гайд", но у вас это выходит куда лучше.
http://wforum.heroes35.net/showthread.php?tid=5706

Подзабил на это дело с моим товарищем немного. Мыж студенты. :smile16:

Если кто-то захочет помочь — предложка открыта.
Вернуться к началу

offlineАватара пользователя
void_17  
имя: имя
Ветеран
Ветеран
 
Сообщения: 548
Зарегистрирован: 25 апр 2021, 15:05
Откуда: Оттуда
Пол: Мужчина
Поблагодарили: 132 раз.

Re: База данных IDA от void17

Сообщение void_17 » 25 ноя 2021, 01:06

AlexSPL, я смотрю вы уже прямо в базе комментите. Чтобы ничего не пропало, загрузите вашу версию моей базы на сервер as239, оттуда будем вместе работать поочередно. Все в дискорде есть.
Вернуться к началу

offlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 5587
Зарегистрирован: 17 сен 2010, 12:58
Пол: Мужчина
Награды: 14
Высшая медаль (1) Победителю турнира по HMM1_TE (2) Победителю этапа по HMM1 (1) Победителю этапа по HMM2 (1) Лучшему из лучших (1) 2 место 1 этапа по HMM1 (1)
3 место 1 этапа по HMM1 (1) 1 место 2 этапа по HMM2 (1) Победителю турнира по KB (2) Победителю турнира по KB (1) Грандмастер оффлайн-турниров (1) Боевой шлем (1)
Поблагодарили: 2185 раз.

Re: База данных IDA от void17

Сообщение AlexSpl » 25 ноя 2021, 01:34

Добавил ещё одну функцию, которая порядком напрягла, а может, косяк IDA. Всё время пытается исправить тип this на combatManager, имена локальных переменных постоянно сбрасывает, а если ошибёшься, нажмёшь Ctrl+Z, то точно откатит не только последнее. Не очень удобно работать. Я бы просто переписывал эти функции на С++, мне кажется, так даже лучше. В базе из-за "умного" компилятора много мусорных присваиваний, работа с вещественными типами ещё та заноза (ведь double передаётся в качестве аргумента двуми пушами), a потом в тексте появляются COERCE_DOUBLE() :smile3: Лучше просто переписывать функции на C++, оптимизируя и убирая compiler-style фишки.

Кстати, разобрал type_enchant_data. Оказывается, это кусок type_spell_choice (первые пять полей):

Код: Выделить всё
// ===============================================================
// --------------------- AI Spell Weighting ----------------------
// ---------------------------------------------------------------

// class type_AI_spellcaster
#pragma pack(push, 4)
struct type_AI_spellcaster : _Struct_
{
   _ptr_ VMT;                          // 0x00
   Hero* hero[2];                      // 0x04
   int side[2];                        // 0x0C
   int field_14;                       // 0x14
   int field_18;                       // 0x18
   bool hexDeadArmyOrWarMachine;       // 0x1C
   int attackBonus;                    // 0x20
   int defenseBonus;                   // 0x24
   bool isFightValueCapped;            // 0x28
   int field_2C;                       // 0x2C
   int field_30;                       // 0x30
   int field_34;                       // 0x34
   int field_38;                       // 0x38
   int stackIx;                        // 0x3C
   // ...
};

struct type_spell_choice : _Struct_
{
   int spell;                          // 0x00
   int schoolLevel;                    // 0x04
   int spellPower;                     // 0x08
   int spellDuration;                  // 0x0C
   Army* army;                         // 0x10
   int targetHex;                      // 0x14
   int field_18;                       // 0x18
   int spellValue;                     // 0x1C
   bool spellApproved;                 // 0x20
};
#pragma pack(pop)

// struct type_enchant_data
struct type_enchant_data : _Struct_
{
   int spell;
   int schoolLevel;
   int spellPower;
   int spellDuration;
   Army* army;
};

В базе у type_spell_choice перепутаны поля.

Начинает потихоньку складываться цельная картина каста AI. Уже в плагине поправил каст Explosion, Incineration и Death Cloud.

* * *
Ан нет, type_enchant_data заполняется для разных заклов по разному. Например, функция взвешивания ударных одиночных заклинаний превратилась в неправильную (тут по формуле урона можно контролировать):

Код: Выделить всё
int __thiscall type_AI_spellcaster::get_damage_spell_value(type_AI_spellcaster *this, type_enchant_data data)
{
  return type_AI_spellcaster::get_damage_value(
           this,
           data.SchoolLevel,
           data.SpellDuration * akSpelltraits[data.SchoolLevel].Eff_Power
         + akSpelltraits[data.SchoolLevel].Effect[data.SpellPower],
           this->Hero[1],
           data.SpellID);
}

Интересно, как на самом деле реализована структура type_enchant_data? Просто пять безликих полей DWORD?

Меня вот это запутало: qmemcpy(&enchant_data, SpellChoice, sizeof(enchant_data)); Здесь реально идёт копирование пяти первых полей, но это для бафов/дебафов. Как видим из примера с оценкой ударных, она для них заполняется по-другому :smile5:

* * *
В перечислителе SpellID пропущено заклинание Clone. Короче, я добавлю префиксы. И вместе со всеми правками залью базу. Конечно, не очень удобно работать поочерёдно, но как по-другому?
Вернуться к началу

offlineАватара пользователя
AlexSpl  
имя: Александр
Эксперт
Эксперт
 
Сообщения: 5587
Зарегистрирован: 17 сен 2010, 12:58
Пол: Мужчина
Награды: 14
Высшая медаль (1) Победителю турнира по HMM1_TE (2) Победителю этапа по HMM1 (1) Победителю этапа по HMM2 (1) Лучшему из лучших (1) 2 место 1 этапа по HMM1 (1)
3 место 1 этапа по HMM1 (1) 1 место 2 этапа по HMM2 (1) Победителю турнира по KB (2) Победителю турнира по KB (1) Грандмастер оффлайн-турниров (1) Боевой шлем (1)
Поблагодарили: 2185 раз.

Re: База данных IDA от void17

Сообщение AlexSpl » 25 ноя 2021, 04:36

Разбираю конструктор type_AI_spellcaster::type_AI_spellcaster(). Пришлось после того, как увидел оценку заклинания Fire Shield. Так вот, размер этого класса 1040 байт.
Вернуться к началу

offlineАватара пользователя
void_17  
имя: имя
Ветеран
Ветеран
 
Сообщения: 548
Зарегистрирован: 25 апр 2021, 15:05
Откуда: Оттуда
Пол: Мужчина
Поблагодарили: 132 раз.

Re: База данных IDA от void17

Сообщение void_17 » 25 ноя 2021, 04:41

Цитата:
Я бы просто переписывал эти функции на С++

Я, собственно, так и делаю по-тихоньку...
Цитата:
struct type_AI_spellcaster : _Struct_

Зачем так делать? Давайте лучше как NWC пропишем просто class type_AI_spellcaster и сравняем. Мы все же на С++, а не С. Я и думаю переписать эти функции и записать в ai_combat.cpp. Почти доделал armygrp.cpp, кстати.

Цитата:
Army*

AlexSPL, я прошу вас ,умоляю, не поймите неправильно, давайте соблюдать оригинальное имя. :smile11: Так будет проще эти самые .cpp/.h файлы переписывать, так как не придется каждый раз менять Army на army и т.д.
Одно дело для себя писать, другое когда в команде работаем. Нужно ввести стандарты, чтобы код друг друга можно было без проблем копировать и обмениваться.

upd.: я понял, вы в homm3.h пишите. Ну ладно, ваше право, но лучше давайте придерживатся стандарту

И да, не меняйте, пожалуйста, имя указатель this, проще в файлик записывать.
Последний раз редактировалось void_17 25 ноя 2021, 04:49, всего редактировалось 2 раз(а).
Вернуться к началу

Пред.След.

Вернуться в Общий раздел

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 2

cron