Heroes of Might and Magic Community
visiting hero! Register | Today's Posts | Games | Search! | FAQ/Rules | AvatarList | MemberList | Profile


Age of Heroes Headlines:  
5 Oct 2016: Heroes VII development comes to an end.. - read more
6 Aug 2016: Troubled Heroes VII Expansion Release - read more
26 Apr 2016: Heroes VII XPack - Trial by Fire - Coming out in June! - read more
17 Apr 2016: Global Alternative Creatures MOD for H7 after 1.8 Patch! - read more
7 Mar 2016: Romero launches a Piano Sonata Album Kickstarter! - read more
19 Feb 2016: Heroes 5.5 RC6, Heroes VII patch 1.7 are out! - read more
13 Jan 2016: Horn of the Abyss 1.4 Available for Download! - read more
17 Dec 2015: Heroes 5.5 update, 1.6 out for H7 - read more
23 Nov 2015: H7 1.4 & 1.5 patches Released - read more
31 Oct 2015: First H7 patches are out, End of DoC development - read more
5 Oct 2016: Heroes VII development comes to an end.. - read more
[X] Remove Ads
LOGIN:     Username:     Password:         [ Register ]
HOMM1: info forum | HOMM2: info forum | HOMM3: info mods forum | HOMM4: info CTG forum | HOMM5: info mods forum | MMH6: wiki forum | MMH7: wiki forum
Heroes Community > Heroes 3.5 - WoG and Beyond > Thread: How to edit HotA?
Thread: How to edit HotA? This Popular Thread is 118 pages long: 1 2 3 4 5 ... 20 40 60 80 100 ... 114 115 116 117 118 · «PREV / NEXT»
AlexSpl
AlexSpl


Responsible
Supreme Hero
posted October 31, 2024 07:42 PM

It's a complex task. Basically, you have to check if AI consider to cast a spell at all. Then, if yes, you modify this method -

int (__thiscall *__stdcall type_AI_spellcaster::get_enchantment_function(
       int spell))(type_AI_spellcaster *this, const army *our_army, type_enchant_data caster)
{
   int (__thiscall *result)(type_AI_spellcaster *, const army *, type_enchant_data); // eax

   switch ( spell )
   {
       case SPELL_MAGIC_ARROW:
       case SPELL_ICE_BOLT:
       case SPELL_LIGHTNING_BOLT:
       case SPELL_IMPLOSION:
       case SPELL_TITANS_LIGHTNING_BOLT:
           result = type_AI_spellcaster::get_damage_spell_value;
           break;
       case SPELL_SHIELD:
           result = type_AI_spellcaster::get_shield_value;
           break;
       case SPELL_AIR_SHIELD:
           result = type_AI_spellcaster::get_air_shield_value;
           break;
       case SPELL_FIRE_SHIELD:
           result = type_AI_spellcaster::get_fire_shield_value;
           break;
       case SPELL_PROTECTION_FROM_AIR:
           result = type_AI_spellcaster::get_air_protection_value;
           break;
       case SPELL_PROTECTION_FROM_FIRE:
           result = type_AI_spellcaster::get_fire_protection_value;
           break;
       case SPELL_PROTECTION_FROM_WATER:
           result = type_AI_spellcaster::get_water_protection_value;
           break;
       case SPELL_PROTECTION_FROM_EARTH:
           result = type_AI_spellcaster::get_earth_protection_value;
           break;
       case SPELL_ANTI_MAGIC:
           result = type_AI_spellcaster::get_antimagic_value;
           break;
       case SPELL_DISPEL:
           result = type_AI_spellcaster::get_dispel_value;
           break;
       case SPELL_MAGIC_MIRROR:
           result = type_AI_spellcaster::get_backlash_value;
           break;
       case SPELL_CURE:
           result = type_AI_spellcaster::get_cure_value;
           break;
       case SPELL_BLESS:
           result = type_AI_spellcaster::get_bless_value;
           break;
       case SPELL_CURSE:
           result = type_AI_spellcaster::get_curse_value;
           break;
       case SPELL_BLOODLUST:
           result = type_AI_spellcaster::get_blood_lust_value;
           break;
       case SPELL_PRECISION:
           result = type_AI_spellcaster::get_precision_value;
           break;
       case SPELL_WEAKNESS:
           result = type_AI_spellcaster::get_weakness_value;
           break;
       case SPELL_STONE_SKIN:
           result = type_AI_spellcaster::get_stone_skin_value;
           break;
       case SPELL_DISRUPTING_RAY:
           result = type_AI_spellcaster::get_disruptive_ray_value;
           break;
       case SPELL_PRAYER:
           result = type_AI_spellcaster::get_prayer_value;
           break;
       case SPELL_MIRTH:
           result = type_AI_spellcaster::get_mirth_value;
           break;
       case SPELL_SORROW:
           result = type_AI_spellcaster::get_sorrow_value;
           break;
       case SPELL_FORTUNE:
           result = type_AI_spellcaster::get_fortune_value;
           break;
       case SPELL_MISFORTUNE:
           result = type_AI_spellcaster::get_misfortune_value;
           break;
       case SPELL_HASTE:
           result = type_AI_spellcaster::get_haste_value;
           break;
       case SPELL_SLOW:
           result = type_AI_spellcaster::get_slow_value;
           break;
       case SPELL_SLAYER:
           result = type_AI_spellcaster::get_slayer_value;
           break;
       case SPELL_FRENZY:
           result = type_AI_spellcaster::get_frenzy_value;
           break;
       case SPELL_COUNTERSTRIKE:
           result = type_AI_spellcaster::get_counterstrike_value;
           break;
       case SPELL_BERSERK:
           result = type_AI_spellcaster::get_berserk_value;
           break;
       case SPELL_HYPNOTIZE:
           result = type_AI_spellcaster::get_hypnotize_value;
           break;
       case SPELL_FORGETFULNESS:
           result = type_AI_spellcaster::get_forgetfulness_value;
           break;
       case SPELL_BLIND:
       case SPELL_PARALYZE:
           result = type_AI_spellcaster::get_blind_or_paralyze_value;
           break;
       case SPELL_CLONE:
           result = type_AI_spellcaster::get_clone_value;
           break;
       case SPELL_POISON:
           result = type_AI_spellcaster::get_poison_value;
           break;
       case SPELL_DESEASE:
           result = type_AI_spellcaster::get_disease_value;
           break;
       case SPELL_AGE:
           result = type_AI_spellcaster::get_age_value;
           break;
       default:
           result = type_AI_spellcaster::unimplemented;
           break;
   }
   return result;
}

You see the default case, which is for any spell that hasn't its own weighting function. First what you should do is to write something like this -

case SPELL_AGE:
case SPELL_QUICKSAND:
   result = type_AI_spellcaster::get_age_value;
   break;
default:
   result = type_AI_spellcaster::unimplemented;
   break;


to force AI considering Quicksand with the Age spell weighting function for evaluation. Age spell has very simple weighting function, so you don't break the game. To do it in hex you have to change an index in the corresponding indirect table for the above switch statement. Might be a small patch, or might not


 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
phoenix4ever
phoenix4ever


Legendary Hero
Heroes is love, Heroes is life
posted October 31, 2024 08:10 PM

Damn seems pretty complicated to me and I hoped it could be done through simple hex editing.
Oh well, maybe it can help someone else.
Nice work.

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
Karyoplasma
Karyoplasma


Hired Hero
posted October 31, 2024 08:17 PM

at one point i'll have to learn how to make c++ plugins.

is there a hooking template or even a modloader like monomod for unity games? that would make it easier to start.

just a general inquiry, i don't have any immediate plans as i'm a bit out of order currently as i broke my hand being an idiot lol

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
AlexSpl
AlexSpl


Responsible
Supreme Hero
posted October 31, 2024 08:22 PM
Edited by AlexSpl at 20:33, 31 Oct 2024.

You can start here. It's in Russian, but Google Translate is quite good for the task. Once you built your first plugin (see the example), it's a matter of time when you'll start to write your own. Many people started from zero. Now they write their non-trivial mods.

There you can find many examples, like this one. It's called FairWait. Speed of your troops is halved if you wait. There are also many not-so-experimental examples.

Many plugins to download can be found here. These were tested and used in real (online) games. I'm sure you find there one you'll like. Note that they are for Complete/SoD with the HD mod. It's how things officially are.

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
AlexSpl
AlexSpl


Responsible
Supreme Hero
posted October 31, 2024 09:45 PM

About the future of modding. First of all, you have to decide which core of the game you like to modify. Why not VCMI if it's open source? Well, it's not the original game, though it's very close to it. 99% of players will be OK with VCMI. I recommend to modify VCMI, if you started to play Heroes recently. If you want to make mods for the original game and HotA, I suggest you to learn NH3API. Actually, this API isn't yet published, but it will standardize all the modding around the original game. It's the main purpose of NH3API. Modders will get access to the original methods of the game with the original names. There was only one such an attempt I know of (see H3API by RoseKavalier). NH3API will provide that much and more (I hope).  All the plugins written before will be easily transferred to the NH3API. It's the greatest attempt to standardize H3 modding, I doubt someone ever start such an ambitious task in the near future, so I reckon it will have all chances to become a new robust platform for all the H3 modders around the world.

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
AlexSpl
AlexSpl


Responsible
Supreme Hero
posted November 01, 2024 12:02 PM

Looked closer at the type_AI_spellcaster class methods. The above method is used only for evaluation of spells with ID 15 and more (starting from Magic Arrow). To evaluate Quicksand and other spells with ID less than 15 you have to add a case to void __thiscall type_AI_spellcaster::consider_spell(type_AI_spellcaster *this, type_spell_choice *choice) method.

switch ( iSpell )
{
   case SPELL_EARTHQUAKE:
       type_AI_spellcaster::consider_earthquake(this, choice);
       break;
   case SPELL_CHAIN_LIGHTNING:
       type_AI_spellcaster::consider_chain_lightning(this, choice);
       break;
...


In other words, you have to write

case SPELL_QUICKSAND:
   type_AI_spellcaster::consider_quicksand(this, choice);
   break;


for Quicksand, for example. The main difficulty here is to write consider_quicksand(this, choice); But if you just want AI to cast it, you simply may write (let Quicksand has constant weight of 1,000) -

choice->value = 1000;
choice->cast_now = 1;


And, voila, AI casts Quicksand simply because it knows this spell.

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
Karyoplasma
Karyoplasma


Hired Hero
posted November 01, 2024 10:38 PM

Thanks for all the info, AlexSPL. I'll be checking it out.

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
SilverG
SilverG


Known Hero
posted November 03, 2024 02:49 PM

Hello,

in  the HD.exe where can I find the cost for hiring a hero... 2500gold seems too cheap.


Thank you.

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
AlexSpl
AlexSpl


Responsible
Supreme Hero
posted November 03, 2024 03:02 PM

See int gHeroGoldCost @ 27814Ch.

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
SilverG
SilverG


Known Hero
posted November 03, 2024 03:10 PM

Thanks.

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
VIP
VIP


Known Hero
posted November 03, 2024 03:48 PM

I wrote with help: functions HotA for H3 Complete / Chronicles .

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
Csaros
Csaros


Hired Hero
posted November 05, 2024 01:44 PM
Edited by Csaros at 13:57, 05 Nov 2024.

Thanks a lot for help Alex!

I am now trying to make an artifact provide the magi -2 mana cost bonus. Do you know how to make an artifact check work?

My invalid code looks like this:

0xe558b: sandwiched in a jump to free space right after the Magi check, before comparison of 83 FE 01 at 0xe558c

0xf83f7 (free space):
 68 8B 00 00 00 push x8b (artifact ID)
 8B 4D C4 - mov ecx, [ebp-0x3c] (same as the function used to look for hero data in the Magic Channel ability check)
 E8 5B 10 FE FF call artifact check at 4D9460
 84 C0 test al, al
 74 03 je 3
 83 EE 02 sub esi, 2 (reduce mana cost)
 E9 7B D1 FE FF (jump back to original function, to the point marked in bold)

Do you know how can I fix it? I suppose the value of ecx should be different - what should I set it to for the function to find the artifact?

Thank you from the top!

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
AlexSpl
AlexSpl


Responsible
Supreme Hero
posted November 05, 2024 03:20 PM

This method looks like this -

int __thiscall hero::GetManaCost(hero *this, SpellID iWhichSpell, const armyGroup *enemy, char magic_terrain)
{
   int v6; // eax
   type_artifact *equipped; // ecx
   EComboArtifact m_targetCombo; // eax
   TSkillMastery SpellSchoolLevel; // eax
   int v10; // esi

   if ( iWhichSpell == SPELL_TITANS_LIGHTNING_BOLT )
       return 0;
   if ( iWhichSpell == SPELL_ARMAGEDDON )
   {
       v6 = 0;
       equipped = this->equipped;
       do
       {
           if ( equipped->type == ARTIFACT_ARMAGEDDONS_BLADE )
               goto LABEL_9;
           ++v6;
           ++equipped;
       }
       while ( v6 < 19 );
       m_targetCombo = akArtifactTraits[128].m_targetCombo;
       if ( m_targetCombo != COMBO_NONE && hero::IsWieldingArtifact(this, combo_artifacts[m_targetCombo].type) )
       {
LABEL_9:
           SpellSchoolLevel = eMasteryExpert;
           goto LABEL_11;
       }
   }
   SpellSchoolLevel = hero::GetSpellSchoolLevel(this, akSpellTraits[iWhichSpell].m_school, magic_terrain);
LABEL_11:
   v10 = akSpellTraits[iWhichSpell].m_manaCost[SpellSchoolLevel];
   if ( enemy )
   {
       if ( armyGroup::IsMember(enemy, CREATURE_PEGASUS) || armyGroup::IsMember(enemy, CREATURE_SILVER_PEGASUS) )
           v10 += 2;
       if ( armyGroup::IsMember(&this->heroArmy, CREATURE_MAGE) || armyGroup::IsMember(&this->heroArmy, CREATURE_ARCH_MAGE) )
           v10 -= 2;
   }
   if ( v10 < 1 )
       return 1;
   return v10;
}


Make sure your jmp (5 bytes) didn't overwrite the next instruction. Make sure you didn't break the stack (push/pop). If it's not the reason I'll look into it later.

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
AlexSpl
AlexSpl


Responsible
Supreme Hero
posted November 05, 2024 03:35 PM
Edited by AlexSpl at 15:48, 05 Nov 2024.

Basically, you have to write this -

if ( hero::IsWieldingArtifact(this, YOUR_ARTIFACT_ID) )
   v10 -= 2


v10 being the resulting mana cost. Probably, you just broke the stack (common mistake).

This is also might be incorrect. Check where your artifact ID is really stored.

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
AlexSpl
AlexSpl


Responsible
Supreme Hero
posted November 05, 2024 03:56 PM



Do you keep this pop edi in your free space, btw?

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
Csaros
Csaros


Hired Hero
posted November 05, 2024 04:24 PM
Edited by Csaros at 16:24, 05 Nov 2024.

I didn't touch the rest of the code; all the pops and comparisons etc. are in the right places. I used a short jump overwriting 83ee02 immediately before 83fe01 to an empty space nearby, used that to place my 83ee02 as well as the jump to the free space. The effect works for magi and archmagi, only the artifact ID is not checked correctly. I think my ecx assignment may be incorrect.

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
AlexSpl
AlexSpl


Responsible
Supreme Hero
posted November 05, 2024 04:33 PM

What you are using ecx for? You already pushed your artifact ID to the stack?

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
AlexSpl
AlexSpl


Responsible
Supreme Hero
posted November 05, 2024 04:39 PM
Edited by AlexSpl at 16:55, 05 Nov 2024.

Oh, okay, you are trying to use this pointer, try edi instead. It is __thiscall, you push artifact ID to the stack, and this to ecx. I believe, [ebp-0x3c] is not your this (pointer to hero; hero*).

Well, my bad, edi is also rewritten. Try ebx. Should work.

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
Phoenix4ever
Phoenix4ever


Legendary Hero
Heroes is love, Heroes is life
posted November 05, 2024 05:50 PM
Edited by Phoenix4ever at 17:50, 05 Nov 2024.

Hi Alex

I might as well ask you directly, since you seem to know (almost) everything about modding H3.

So you know you can buy skills in universities, for 2000 gold, right.
Is there any way to add a function to universities, that allows you to pay 2000 gold to delete a skill?
Maybe Witch Huts could also allow you to delete a certain skill? Or maybe even adventure map scholars?
Or is there any other way to achieve this?

Skills like Pathfinding, Navigation and generally bad skills like Eagle Eye, Learning and Ballistics will usually become useless sooner or later, so it would be nice with some way to delete skills. (Also if it costs some gold, as with the university example.)

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
Csaros
Csaros


Hired Hero
posted November 05, 2024 09:22 PM

The mov ecx,ebx worked! Thanks a lot, Alex!

 Send Instant Message | Send E-Mail | View Profile | Quote Reply | Link
Jump To: « Prev Thread . . . Next Thread » This Popular Thread is 118 pages long: 1 2 3 4 5 ... 20 40 60 80 100 ... 114 115 116 117 118 · «PREV / NEXT»
Post New Poll    Post New Topic    Post New Reply

Page compiled in 0.1954 seconds