Объявления

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

Как создать плагин для HD мода

Герои Меча и Магии III: Возрождение Эрафии, Герои Меча и Магии III Дыхание Смерти, Герои Меча и Магии III Клинок Армагеддона, Герои Меча и Магии III Хроники Героев
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: Как создать плагин для HD мода

Сообщение AlexSpl » 07 дек 2021, 21:06

Вот это уже похоже на функцию взвешивания:

Код: Выделить всё
int __fastcall get_poison_value(type_AI_spellcaster* caster, int unused_edx, Army* army, type_enchant_data data)
{
   if (caster->destroyedOrWarMachine)
      return 0;

   int schoolLevel = data.skillMastery;

   if (army->active_spell_duration[SPL_POISON] && schoolLevel <= poisonSpell[army->side][army->index_on_side].expertise)
      return 0;

   float workChance = pCombatManager->SpellCastWorkChance(SPELL_POISON, caster->side[AI_CASTER], army, false, true, caster->isCreatureCast);
   if (!(workChance > 0.0f))
      return 0;

   Army* dummyArmy = new Army;
   CALL_2(Army*, __thiscall, 0x437650, dummyArmy, army);
   int healthBefore = army->creature.hit_points * army->count_current - army->lost_hp;
   
   // First round
   float healthMod = poisonSpellParams[schoolLevel].healthModFirstCast;
   float healthMul = max(dummyArmy->Field<float>(0x4A4) - healthMod, poisonSpellParams[schoolLevel].minHealth);
   dummyArmy->Field<float>(0x4A4) = healthMul;

   double fullHealth = (double)dummyArmy->full_hp * healthMul;

   // Poison, Age & Toughness
   if (dummyArmy->active_spell_duration[SPELL_AGE])
      fullHealth *= ageSpell[army->side][army->index_on_side].healthMod / 100.0;

   if (dummyArmy->active_spell_duration[SPELL_TOUGHNESS])
      fullHealth *= toughnessSpell[army->side][army->index_on_side].healthMod / 100.0;

   int health = (int)(fullHealth + 0.95);

   dummyArmy->creature.hit_points = health;
   
   if (health - 1 < dummyArmy->lost_hp)
      dummyArmy->lost_hp = health - 1;

   // Next 2 rounds
   for (int i = 0; i < max(data.spellDuration - 1, 2); ++i)
   {
      healthMod = poisonSpellParams[schoolLevel].healthMod;
      healthMul = max(dummyArmy->Field<float>(0x4A4) - healthMod, poisonSpellParams[schoolLevel].minHealth);
      dummyArmy->Field<float>(0x4A4) = healthMul;

      fullHealth = (double)dummyArmy->full_hp * healthMul;

      // Poison, Age & Toughness
      if (dummyArmy->active_spell_duration[SPELL_AGE])
         fullHealth *= ageSpell[army->side][army->index_on_side].healthMod / 100.0;

      if (dummyArmy->active_spell_duration[SPELL_TOUGHNESS])
         fullHealth *= toughnessSpell[army->side][army->index_on_side].healthMod / 100.0;

      health = (int)(fullHealth + 0.95);
     
      dummyArmy->creature.hit_points = health;

      if (health - 1 < dummyArmy->lost_hp)
         dummyArmy->lost_hp = health - 1;
   }

   int healthAfter = dummyArmy->creature.hit_points * dummyArmy->count_current - dummyArmy->lost_hp;
   CALL_1(void, __thiscall, 0x43D120, dummyArmy);
   delete [] dummyArmy;

   int effectiveDamage = (int)((healthBefore - healthAfter) * workChance);

   int value = army->get_loss_combat_value(caster->combatParams.attackMod, caster->combatParams.defenseMod, army->can_shoot(),
      effectiveDamage, caster->combatParams.isOpponentDangerous);

   debugStr("{%d %s}\n\nskillMastery = %d\nspellDuration = %d\n\neffectiveDamage = %d\nvalue = %d",
      army->count_current, army->count_current > 1 ? army->creature.name_plural : army->creature.name_single, data.skillMastery, data.spellDuration, effectiveDamage, value);

   return value;
}
Вернуться к началу

offlineRolex  
имя: Alex
Ветеран
Ветеран
 
Сообщения: 898
Зарегистрирован: 22 сен 2020, 18:58
Откуда: УКРАИНА
Пол: Мужчина
Поблагодарили: 53 раз.

Re: Как создать плагин для HD мода

Сообщение Rolex » 07 дек 2021, 21:17

Цитата:
Вот это уже похоже на функцию взвешивания

Эта функция используется в версии 1.02 или вы ее только что доработали?
Вернуться к началу

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: Как создать плагин для HD мода

Сообщение AlexSpl » 07 дек 2021, 21:20

Нет, я сейчас делаю. Пока написал для Poison. Для заклинаний призыва и ударных заклинаний функции взвешивания не нужно писать (их ценность считают оригинальные алгоритмы). Если есть идеи, как улучшить оценку, делитесь.
Вернуться к началу

offlineRolex  
имя: Alex
Ветеран
Ветеран
 
Сообщения: 898
Зарегистрирован: 22 сен 2020, 18:58
Откуда: УКРАИНА
Пол: Мужчина
Поблагодарили: 53 раз.

Re: Как создать плагин для HD мода

Сообщение Rolex » 07 дек 2021, 21:32

Раз Poision уже готов, тогда осталось еще 7-8 заклов: Disease, Age, Fear, Death Blow, Drain Life, Toughness, Behemoth's Claws и, может быть, еще Mobility.
Вернуться к началу

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: Как создать плагин для HD мода

Сообщение AlexSpl » 07 дек 2021, 21:38

Ага. Я ещё хочу сделать мониторинг использованных противником заклинаний и понижать оценку, если у него есть Dispel, Cure или Anti-magic (с условиями для снятия, конечно). Далее, дам небольшой бонус за продолжительность заклинания и средний, если наши отряды достают отряд, который оценивается, за один ход (чем больше отрядов могут ударить, тем выше бонус). Уже лучше оригинальной оценки. Также вынесу множитель для value в NewSpells.ini, чтобы игрок мог зааджастить value самостоятельно, исходя из опыта битв.
Вернуться к началу

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: Как создать плагин для HD мода

Сообщение AlexSpl » 07 дек 2021, 22:18

Бонус за продолжительность Poison может быть таким:

Код: Выделить всё
// 5% for every round after 3rd till 10th round
value = (int)(value * max(1, 1 + (min(data.spellDuration, 10) - 3) * 0.05));

Функции взвешивания для заклов писать - неблагодарное занятие. Как поётся в одной известной песне: "Наша служба и опасна, и трудна, но на первый взгляд как будто не видна" :smile1: Можно возвращать рандом, и среднестатистический игрок ничего не заметит.
Вернуться к началу

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: Как создать плагин для HD мода

Сообщение AlexSpl » 10 дек 2021, 16:09

Эх, много планов. Написал "родные" функции взвешивания всех новых заклинаний, кроме трёх: Disease, Death Blow и Drain Life. Самый сложный кейс - Drain Life. Решил пока отсебятину не добавлять, а посмотреть, как родной AI будет ими пользоваться. Есть проблема с массовыми оригинальными заклинаниями, которым AI вешает уж очень большую ценность. Хотя, например, Expert Stone Skin почти всегда хуже Fear на самый опасный стек. Думаю, как это дело балансировать: для массовых же сумма идёт весов заклинаний, а Fear вешается на один отряд, но которое заклинание сильнее, сразу понятно. Оригинальный AI и кроме этого далёк от идеального. Это по воспоминаниям он хорош, но я провёл столько тестов, что могу с уверенностью сказать, что его, мягко говоря, есть куда улучшать. Также в планах статья по AI, ответственному за заклинания, начиная с базового метода type_spellvalue::get_raw_spell_value(), распадающегося на три функции оценки: бафов/дебафов, ударных заклинаний (одиночные и площадные идут вместе) и массовых (типа Armageddon), "нестандартные" (Land Mine, призыв, телепорт и т.п.) идут особняком. Думаю, я смогу объяснить работу по выбору заклинания AI от начала и до конца, но это очень большая тема и потребует много времени не только для полного понимания происходящего, но и для доступного описания :smile2:

Кстати, функции оценки некоторые вполне приличные. Например, оценка Blind выходит на полный totalCombatValue отряда (на этой функции взвешивания пока базирую функцию взвешивания для Fear). Для справки: полная боевая ценность - это очень большая ценность. Лучше, наверноe, только оценка Fire Shield, - любимчика AI :smile1:

Там два случая, как и для большинства оценок:

Код: Выделить всё
if ( total_combat_value < this->combatParams.enemyActiveStacksCombatValue )
{
   spellDuration = this->combatParams.expectedBattleDuration;
   if ( data.spellDuration < spellDuration )
     durationRatio = data.spellDuration / spellDuration;
   else
     durationRatio = 1.0;
   if ( (army->creature.flags & CF_DONE) != 0
     && (durationRatio = durationRatio - 1.0 / spellDuration, durationRatio < 0.0) )
   {
     SpellDurationRatio = 0.0;
   }
   else
   {
     SpellDurationRatio = durationRatio;
   }
   effectiveCombatValue_0 = total_combat_value * SpellDurationRatio;
}
else
{
   effectiveCombatValue_0 = (0.5 - sqrt(akSpelltraits[SPL_BLIND].effect[data.skillMastery] / 400.0))
                     * total_combat_value;
}

Есть претензии к оценке заклинаний призыва. Почему-то для них вместо Fight Value берётся AI Value. И вообще, отталкиваться от AI Value для заклинаний призыва не самая лучшая идея. Если масштабировать призыв по AI Value, то тех же Magic Elementals вызывалось бы с гулькин нос, и вызванный стек "стандартных" элементалей рвал бы, как Тузик грелку, отряд Magic Elementals или Firebirds. Сейчас более-менее нормальные числа, но мне не нравится, что AI, однажды призвав элементалей, начинает ими спамить. Возможно, это мой косяк, и проблема возникла из-за разрешения кастовать Firebirds и Sprites, не озираясь на тип призванных элементалей. Нужно будет затестить, есть ли такой эффект в оригинале после первого каста элементалей.

* * *
Оценка заклинания Prayer:

Код: Выделить всё
int __thiscall type_AI_spellcaster::get_prayer_value(
        type_AI_spellcaster *const this,
        army *const army,
        type_enchant_data data)
{
  int defenseSkillValue; // edi
  int result; // edi
  int speed; // eax
  int effect; // [esp+Ch] [ebp-4h]
  army *AI_target; // [esp+18h] [ebp+8h]

  effect = akSpelltraits[SPL_PRAYER].effect[data.skillMastery];
  AI_target = army->AI_target;
  defenseSkillValue = type_AI_spellcaster::get_defense_skill_value(this, army, data.spellDuration, effect);
  result = type_AI_spellcaster::get_speed_value(this, army, effect, data.spellDuration) + defenseSkillValue;
  if ( AI_target )
  {
    speed = army::GetSpeed(army);
    if ( army::get_AI_target_time(army, speed) == 1 )
      result += type_AI_spellcaster::get_attack_skill_value(this, army, AI_target, data.spellDuration, effect);
  }
  return result;
}

Как здесь всё просто, оказывается. Оценка бонуса к защите + оценка бонуса к скорости всегда, плюс оценка бонуса к атаке, если отряд может достать назначенный AI вражеский отряд* за один ход без бонуса к скорости, т.е. со своей текущей скоростью, что странно (или нет)**.

*) Этот отряд необязательно (и в большинстве случае не) тот, который будет атакован. AI_target - это отряд, который можно достать, с наилучшей предварительной оценкой. Иногда в качестве AI_target может выступать и вовсе уничтоженный отряд. Тут буду проверять, баг ли это, или такой отряд действительно назначается в качестве AI_target в отcутствие других целей в досягаемости одного хода (один ход в терминах игры называется "time").
**) Но есть функции оценки, где реально происходит симуляция. Т.е. строится "фиктивный" отряд с помощью конструктора army, а при необходимости и "фиктивный" оппонент, и проводится симуляция атаки. Правда, симуляция используется редко (примером может служить Frenzy, от функции взвешивания которого я отталкивался при написании функции взвешивания для Behemoth's Claws), хотя, естественно, и даёт лучшую оценку. В плагине NewSpells я стараюсь использовать симуляцию чаще. По поводу "или нет". Для "или нет" пришлось бы пересчитывать и AI_target (см. последние поля army), чем не заморочились, так что оценка Prayer страдает.
Вернуться к началу

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

Re: Как создать плагин для HD мода

Сообщение void_17 » 11 дек 2021, 05:17

Обновите базу в воскресенье, кстати. Явно вы там уже много нарыли за эту недельку.
Вернуться к началу

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: Как создать плагин для HD мода

Сообщение AlexSpl » 11 дек 2021, 11:03

Для тех, кто разбирается в AI и хочет потестить:

https://handbookhmm.ru/forum/viewtopic.php?f=56&t=529&p=23554#p23554

Кроме "родных" функций взвешивания для новых заклинаний, много мелких фиксов, в т.ч. AI. Исходный код подрос до рекордных для данной темы 3500+ строчек.
Последний раз редактировалось AlexSpl 13 дек 2021, 21:20, всего редактировалось 3 раз(а).
Вернуться к началу

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: Как создать плагин для HD мода

Сообщение AlexSpl » 11 дек 2021, 13:04

Drain Life получилось серьёзным заклинанием. Битва AI vs AI, призыв против Drain Life, затянулась на 50+ раундов :smile1: Предлагаю понижать здоровье целевого отряда на 10% за каждый каст Drain Life до конца битвы. Типа юнит отдаёт часть своих жизненных сил, чтобы получить способность их восполнять. А вообще, конечно, большая часть заклинаний безоговорочно OP в текущем их состоянии и дают фору любому оригинальному заклинанию. Для кампаний - самое то (когда они у AI противника), в кампаниях как раз таки баланс и не нужен, там нужен драйв, а вот для игр по сети/хотсита за кружечкой пиваса :smile16: получение Drain Life, Fear или Age - это автоматический вин.

В принципе, AI для новых заклинаний уже готов. Остаётся переписать функцию взвешивания Disease (сейчас оригинальная неиспользуемая для абилки Zombie) и Drain Life. Для Disease возьму солянку из Slow, Weakness и Disrupting Ray. Для Drain Life буду анализировать отряды, которые достаёт целевой отряд, на предмет наличия иммунных существ и понижать оценку пропорционально. Выше оригинального AI прыгать не будем. Даже если бы очень хотелось, то кроме AI, отвечающего за заклинания, есть AI, отвечающий за атаку, а в оригинальной игре они не очень между собой дружат, и пытаться их помирить в плагине, добавляющем новые заклинания, не стоит, я думаю.

Разработка плагина, который должен был "работать" через неделю, затянулась на три месяца. Зато я с полной уверенностью могу сказать, что ни один скрипт ERM не сделает того, что сделано в данном плагине. Там новые заклинания - детский сад и годятся только для игры Нuman vs Нuman. А сколько я моментов нашёл, связанных с AI, которые точно не могут быть учтены в скриптах ERM (например, знали ли Вы, что функции оценки Dispel, Cure и Anti-Magic реально вызывают метод CancelIndividualSpell пусть и для фиктивного отряда? Так что любые действия, выполняемые в этом методе, должны предваряться проверкой того, а не AI ли балуется). Там работает потому, что алгоритмы позволяют, но с точки зрения AI - полнейший бардак. В плагине NewSpells от версии 1.03 уже закрыты все известные проблемные места, связанные с AI, так что можно им начинать гордиться :smile20:

* * *
Это жутко мотивирует, когда получается что-то реально работающее. Я благодарен Rolex'у за мотивационные пинки под зад, но мне печально оттого, что плагин принят довольно прохладно. Это не говорит о качестве плагина, конечно, а о востребованности плагинов в целом. Что ж, 2021-й на дворе всё-таки. Поэтому (для чего и пишу) разбирать Adventure AI я не буду и поэтому Mobility AI кастовать тоже не научится. Но исходники я прикреплю, естественно (в последние релизы не добавлял по причине большого кол-ва отладочного кода). Как я писал ранее, 20% усилий дают 80% результата, а остальные 80% усилий, соответственно, 20% результата, а в данном плагине речь о AI, который не виден и, скажем по-честному, не так важен игрокам, зато на танцы с ним уходит очень много времени. С одной стороны - стоит того, с другой - кто оценит? Понимаю Ben80 :smile16: Востребованнее голых женщин в интерфейсах рисовать :smile12:
Вернуться к началу

Пред.След.

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

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

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