Currently working on AI diplomacy, declaration of war and overland movement changes.
This is complicated and will most likely take a long time, maybe even a full week or more.
So far I managed to
-Make the AI consider the actual relative military strength of the players for threatening, proposing treaties, declaring wars, breaking treaties. Also to add a difficulty level based penalty to all of the above.
-Increase the diplomatic bonus for Peaceful and Lawful wizards, decrease it for Chaotic.
-Fix some bug that caused the AI to declare war or random nonplayer targets.
-Achieve that Peaceful and Lawful wizards never attack player towns without war until their units are attacked at least once. They still attack nodes, though. Of course only if there is no treaty to stop it.
-That towns of wizards holding a pact or alliance are not considered as attack targets, instead of trying then retracting the movement and wasting the unit's turn repeatedly.
-That Astrologer now displays the real strength of player's armies instead of just a sum of maintenance costs
-And that Astrologer displays a shorter bar for power because it was near full right at the beginning, the scope was too small. It also now adds casting skill to this, with a higher weight than power base.
-Make militarist and expansionist wizards declare war automatically if they have military superiority (and/or bad relations) with another.
-Make all wizards declare war based on the difference in military strength and relations, but this is somewhat random and works the opposite of the above : The bigger the difference in strength the less likely war is, in both directions. Weak wizards are not worth wasting time on, while overly strong ones would be suicide. Relations work the normal way ofc.
-Remove the insecticide change that dumbed down the AI to consider all player units 4 times weaker than they really are to force suicidal aggression.
-fixed a whole ton of bugs with AI item creation.
-Peaceful and lawful wizards skip the "Aggression over time" procedure that makes them attack towns without being attacked first and without a war declaration based purely on random chance. Other wizards can still do it.
-That significant negative diplomatic actions that result in treaties broken when they existed no longer cause a hidden, permanent, diplomatic penalty with ALL other unrelated wizards.
-And increased the "safety" period after forming a new treaty before the AI can consider cancelling it or declare war. This should help making peace treaties actually matter and last some time, as they are also included.
Look forward to 0.7, but as I said, it will take time.
Today's main progress :
Alliance allows walking near towns.
Allies will reward the player for killing units of shared enemies.
Myrran default wizards will unpick Myrran before proceeding with picks because always having the same wizard(s) there is not fun.
0.7 is up!
I'm really tired from all the work so I haven't done more than the minimum necessary testing. If you discover a bug, please report it.
This update is huge. It's probably the biggest one in the mod's history, and I could even say, the game's history as well, since it unlocks an entire implemented but unfortunately not working feature : The AI can initiate an audience with the player and ask for treaties, spells, etc. Yes, this also means in the original game when the AI was trying to complain about something, in most cases it didn't go through and you got a war on you without ever knowing the reason.
Forming an alliance will now have 2 new benefits as well : You'll be allowed to move through their territory, and you will get gold rewards if you kill units belonging to your ally's enemy. The latter might need adjustment on the amount of gold, but for now I'm happy I managed to even implement it.
Astrologer army strength display and power display is updated to have better representations of the player's true power. Army strength, in particular, is now based on the true calculated military value of units, which the AI is using for decisions, and which has also been significantly improved.
Calculated astrologer army strength is now used in all diplomatic decisions where military strength was checked, instead of the original, rather silly checks that only considered units standing on their home continent, and rated them based on costs.
Additionally, all such diplomatic checks now have a difficulty level modifier.
War declaration has been altered too. Militarist and Expansionist wizards will declare war on anyone weaker than them unless they are also peaceful or lawful. All wizards declare war on other wizards similarly powerful as them, but of course relations and personality is a modifier to this, so you can avoid the war if you have a good relation and/or they are peaceful/lawful. Difficulty level has double the normal modifier in this check.
Quite a few other bugs have been also fixed, including AI item creation.
Strategic combat has been altered to divide damage on troops more evenly, and consider the relative value of units when dealing the damage, better units will take less damage usually. The original was coded to maximalize causalities on the winner's side, and although Kryub tried to make it weight the damage based on unit costs, this was halfway done and had no effect, as well as removing the original weights, so overall made it worse.
I've also reversed Kryub's change that made the AI undervalue player units and due to this, go on suicide attacks for no reason at all.
Additionally, the player is now allowed to have 1 more city per land size before the AI starts to consider them overextending than before, the number is now 4*(land size setting).
Another major change : Myrran wizards will first unpick their Myrran trait before proceeding with spending picks. This ensures that you'll have a greater variety of wizards on Myrror instead of always the only one (or four) that have the trait by default.
0.7
-Ore power is now correctly displayed in your fortress town detailed display.
-The Invisibility unit enchantment and item power now grants the Invisibility unit ability to units. The invisibility unit ability is refreshed from the unit base stats when unit stats are calculated so the spell can be properly dispelled despite this change. This should fix Invisible units not appearing invisible in combat or overland. If there are any remaining inconsistencies between the effects of the 3 different kinds of invisibility, it should also get rid of those. Note that the Invisibility unit ability will not show up on the unit if granted that way as abilities are displayed directly from the unit template (except for the ones where I changed this behavior, like immunites or immolation), but there is no need to, because the spell or item power will be displayed.
-Halflings now cannot build Animist's Guilds
-Itemmake.exe now sets the “artifact” flag on items if any power costs more than 400 to match changes in wizards.exe. Pre-defined items are updated.
-Restored check for AI Create Artifact and Gift of Gods to always get an item marked as artifact if taking a predefined one.
-Restored check for AI Enchant Item to always get a non-artifact item if taking a predefined one.
-When using Create Artifact, the AI can get items costing 2250-4500, instead of only picking in the range of 15000-30000 mana and failing to find any, generating an item with random stats instead after failure.
-Undead fantastic units no longer cost 50% more to maintain, instead they have the normal cost of maintenance.
-Minor adjustments to Mana and Production multipliers for AI difficulty on Easy/Normal/Hard.
-Gift of the Gods no longer has a requirement of the item being an artifact and only containing enchantments the receiving wizard can add himself. Instead it has a requirement of the item having a value of 3000-6000. This should also ensure that the event will always grant a powerful, valuable item.
-Fixed bug : Stroke of Genius reduces remaining cost of Spell of Mastery to zero if above 32767.
-Fixed bug : There was no check for Spell Charges at AI item creation, the AI was able to get any premade items even with spells they didn't have available.
-Fixed Insecticide exclusive bug : All units have an equal priority of taking damage in strategic combat instead of highest defense units being selected less frequently. Instead, highest cost units take damage least frequently which might have been what this change was trying to achieve but didn't. This should help the AI preserve their stronger units.
-Strategic Combat : damage taken by the winning army is more evenly distributed instead of the same unit taking all the damage until it dies or damage runs out. This change allows a strategic combat to end with more than one damaged, but surviving unit. It should help the AI preserve more troops though wars and clearing encounters.
-Fixed bug : when finding spells in lairs, books work the opposite of intended. 2 or less books allow rares and very rares, higher doesn't instead of the other way.
-When finding spells in lairs, 1 book allows commons only, 2 allows uncommons too like in all other cases, instead of both allowing both.
-Astrologer now uses a new formula for Power Rating = (Power Base)/8+(Casting Skill/2) instead of Power Rating = Power Base which resulted in a full bar for most of the game on high difficulties.
-Astrologer now uses a new formula for Army Rating = (Sum[1+(Unit Rating)/20])/30 instead of (Sum[Unit maintenance gold+Unit maintenance power]*2)/5. For reference, Unit Rating of a Death Knight is around 500, something like a Stag Beetle is about 100, Skeletons or Spearmen are less than 5. The old formula was pretty flawed, especially considering undead have zero maintenance so they don't add to the total at all. Thanks to the new, revised unit rating for the AI, this should be a much more accurate representation of the actual military power a wizard controls.
-Astrologer research rating is unchanged. For reference, each spell adds points equal to their number within their realm, so a common is 1-10 points, an uncommon 11-20 and so on. While the actual impact of high rarirty spells is higher than this, I see no reason to change the calculation, it's reasonable. It does seem to check all realms, including arcane, as though it had 40 spells, which makes me think the unused bytes after the spells are reserved for the 26 unused arcane spell slots.
-Experimental : The AI will no longer consider the player's armies 4 times weaker than they actually are. (Reversal of Insecticide change)
-Experimental : The AI will no longer consider towns a valid attack target if he holds and Alliance or Wizard's Pact with the owner (instead of trying to attack them and then cancel the movement.)
-Experimental : Restored orignial 10x priority to towns controlled by wizards the AI is at war with, compared to towns controlled by “Peace” status wizards, compared to insecticides 7 multiplier.
-Experimental : The AI will no longer consider towns a valid attack target if he isn't at war and has Peaceful or Lawful personality.
-Secondary realm now plays a role in selecting a wizard's personality and objective.
-The AI is no longer cheating by making a specific uncommon spell always researchable (based on the primary realm, the best uncommon summon spell).
-Fixed bug : AI checks for spell ID FFD5 insteaf of 00D5 for Spell of Mastery at F424Eh (failed insecticide fix, 1.31 checked for “5A = Fire Elemental, lol”)
-Peaceful and Lawful wizards skip the “get aggressive over time” procedure, which sets a hidden variable that increases the chance of them attacking you.
-Phantom Warriors can now move 2.
-Fixed Bug : Ranger hero has Doombolt instead of Lucky.
-Fixed bug. Correct effect is : When Wizard A is allied with Wizard B who is at war with Wizard C, there is a chance Wizard A declares war on Wizard C. Old code was When Wizard A is allied with Wizard B who is at war with [The Player], there is a chance Wizard A declares war on Wizard C if they aren't also allied. This only worked correctly when Wizard C was the player, which was not gurateed.
-In the previous scenario, Wizard B can now be the player. Originally it wasn't possible, the player's allies would not declare war on the player's enemies, unless directly requested.
-The chance for such a declaration of war is now 1:15 instead of 1:10.
-When threatening a wizard, if they paid gold to avoid the war, they'll not consider declaring war themselves for 10 turns longer than before.
-When forming a new treaty, the wizard will not consider breaking it or declaring war for 15-40 turns instead of only 7-15.
-Personality modifiers for declaration of war table changed. (Higher = less chance of war)
Maniacal : 0->0
Ruthless : 10->10
Aggressive : 20->30
Chaotic : 30->10
Lawful : 40->60
Peaceful : 50->80
-Old Declaration of war formula which was replaced : War if Relation+Personality Modifier+Military Situation Modifier+Hidden Diplomacy Modifier<-150 = war.
Where Military Modifier is calculated from the relative army strength on the starting continent of the wizard declaring the war. Yes, units anywhere in the rest of the world are ignored. And yes this means if you respected your treaties and stayed away from their continent, you'll have no army there and the AI will most likely think they have an overwhelming advantage (assuming it even checks for advantage and not disadvantage which would be even more stupid)
-Broken treaties between two wizards no longer cause everyone not invloved to receive a penalty to the “Hidden Diplomacy Modifier” towards the wizard performing the act.
-Adjusted AI overland unit value formula (divided by an additional 16 because strong units were hitting the cap too early so their realtive power towards weaker troops was not properly reflected)
-Militarist and Expansionist wizards check for a need on war based on relative military strength every turn instead of at a 5% random chance.
-Peaceful wizards will not declare war or break treaties due to their Militarist or Expansionist objective. (Lawful wizards were already not doing that)
-New declaration of war formula for the Militarist and Expansionist extra check :
If (Target Army Strength/Own Army Strength)*150+Relation+Hidden Relation+Personality Modifier-(Difficulty level*10)<50 then war is declared.
For example if your opponent is chaotic (+10), You have half their army strength only(+75) and play Extreme (-30) then you're barely safe at +55 with a neutral relation but if the gap in army strength grows, or relations worsen even a little, war will be declared.
In short, they'll attack anyone who has a bad relation with them, and/or has too weak military strength and looks easy to defeat. They'll also break their treaties to be able to do so if needed. The only way to be safe from these wizards is to have an army comparable to theirs or maintain a high positive relation. If they are lawful or peaceful, this process does not apply, they'll never declare a war just because they are more powerful.
-New declaration of war formula for the generic check :
200*abs(Own army strength-Enemy army strength)/(enemy army strength+own army strength)+Relation+Hidden Relation+Personality Modifier-(Difficulty level*20)<0 then war is declared.
This check happens randomly at a 5% chance per turn and is done for all personalities.
For example if you have half the enemy army strength (+66), against an aggressive wizard (+30), at impossible difficulty and neutral realtions you are barely safe (total +16). If the wizard was Chaotic, or your army was stronger, they would declare a war.
This formula gives the highest chance of war when at equal military strength, and the chance decreases sharply as the gap widens in either direction.
-Threatening military effectiveness now uses new formula instead of the original “who has more expensive units on my continent” check : 100*(Target Army Strength/Own Army Strength)-100-10*Difficulty. Other factors unchanged, but personality modifier table changes apply here too, so it's much easier to threathen peaceful and lawful wizards than others. In general, you would want an equal or stronger army to threathen (preferably stronger).
-Propose Treaty military modifier now uses the same value as Threaten instead of that continent based crap.
-Propose Treaty now only gets a random(30) added chance instead of random(100)
-Propose Treaty now only gets half as much boost from the diplomacy variable “Willingness to listen to treaties”
-Having a stronger army than another wizard no longer has a 5% chance to drop relations by 10 at the end of every turn for no reason at all.
-Killing a hero now grants a 50% higher diplomatic penalty, killing units 50% lower, settlers and engineers 100% higher.
-Attempting to exchange spells reduces diplomatic willingness by a lower amount than originally.
-There is a ¼ chance instead of 1/8 for the AI to offer peace if other conditions are met.
-There is a ½ chance instead of ¼ for the AI to offer a spell instead of gold, if able, when asking for peace.
-Major bugfix : Diplomatic relations no longer need to be severely negative for the AI to take diplomatic actions at all, including positive actions. This is why only peace treaty ever worked. This also means that checks to need of war for hatred will be made more often, but those have their own check for negative relations so the check was redundant there anyway.
-Chance to run the procedure that considers offering a treaty is roughly 6 times higher.
-Changed combat attack targeting priorty weight of ranged units, it is now 3+(ranged strength/4) instead of 3+(ranged/2)
-Offering gold as a tribute is now twice as effective.
-Offering spells as a tribute is now less random, instead of random(8)+random(8), the effectiveness is 8+random(8).
-AI secondary Wizard's Pact offer is now based on total army power instead of total number of towns controller. Same for Peace, and gold/spells offered along with the proposal.
-AI is now able to offer a Wizard's Pact down to -50 relationship instead of +0 if the player has greater overall army power rating.
-AI has twice the normal chance to offer something when proposing a treaty (but only if the military situation roll is passed)
-AI summoning priority : Skeletons 50->10
-AI summoning priority : Ghouls 5->10
-AI summoning priority : Hell Hounds 32->10
-AI summoning priority : Night Stalker 20->30
-Warship can now carry 8 units like all other ships
-AI Drain Life and Syphon life targeting : units with too high resistance are now considered invalid targets instead of valid but zero priority ones.
-Metal Fires now grants the intended +3 missile attack strength
-Amazon hero defense lowered by 2. This hero is far too effective for a non-champion, having Might, Blademaster, Thrown attacks and Charmed all in one.
-Banishing or Defeating a wizard now uses diplomatic action code “being too powerful” again instead of none. Having a positive relation seems to prevent it from creating a warning.
-Overextension warning minimum number of towns is now (Land Size*4) instead of (Land Size*3). You are allowed 4 on Tiny, 8 on Small, 12 on Fair, 16 on Large and 20 on Huge land without a penalty.
-Chance for the AI to ignore a negative event is slightly lowered (random(50)>event severity instead of random*75))
-Implemented a trigger for the event type “Thank you for attacking the forces of my enemy” which was available in the game but unused.
-Implemented a trigger for the event type “Thank you for attacking the forces of my enemy, here is some gold” which was available in the game but unused. This event replaces the previous one if and only if you have an alliance with the wizard. Gold is awarded immediately after the battle which triggered the event and it comes from the gold reserves of the wizard offering it (so you get less if they are low on money).
-Having an Alliance allows you to have units near that wizard's town without triggering a penalty.
-AI will always unpick the default Myrran trait first and take Cult Leader, before deciding what else to unpick and what to pick from extra picks left. This ensures that the requirement of having a Myrran wizard will not be fulfilled by the same 1 (or 4) faces in every game. Taking a 2 pick retort instead is necessary to make sure the wizard does not have a significant re-pick advantage over others which would result in them taking Myrror first 75% of the time anyway. Cult Leader was chosen because it both provides an advantage the AI can use well, is realm neutral, and isn't directly overpowering or annoying. Runemaster would be unfun with extra dispel chances, Guardian does not suit most default Myrran wizards who play death and sorcery, Warlord is too good, and Chaneller...well that would be the other reasonable option but Cult Leader feels better for an AI.
-Fixed “Break your alliance with” AI request incorrectly filling the name parameter.
-Astrologer army strength is now a minimum of 1 to prevent division by zero crashes.
I have to say these changes sound absolutely amazing, thanks for putting the massive amount of work in.
I have to ask how you are able to understand the code and mod the changes, could you give a very brief outline of how you do it? Also I am wondering whether you would consider making a repository for your code so others can see each change and where it occurs in the code.
(October 26th, 2015, 06:31)MrBiscuits Wrote: I have to say these changes sound absolutely amazing, thanks for putting the massive amount of work in.
I have to ask how you are able to understand the code and mod the changes, could you give a very brief outline of how you do it? Also I am wondering whether you would consider making a repository for your code so others can see each change and where it occurs in the code.
Basically it's 6 steps
1. Disassemble the code with a disassembler (IDA is recommended)
2. Figure out what the referenced data structures contain (This one is hard but you only need to do it once per data structure)
3. You need to be able to read and understand 8086 assembly code and write new code for some of the more advanced tasks
4. You need to find where the code you want to change is within the exe and what is the problem with it in case of fixing a bug.
5. If the change needed isn't something as simple as replacing "add 3" with "subtract 2" you need to find useless parts of the code that do nothing and remove them, then rearrange code so that the empty space is where new code is to be inserted, all of this using just the hex editor. Including recalculating all offsets for all jumps pointing to the moved code.
6. After step 5 is done, you use an assembler to compile the code you want to insert and add it into the space you made.
Then you run the game and see if it works, and use the disassembler to make sure all offsets and code was properly inserted. Miscalculating the offset even by 1 makes the code unstable and do totally random things.
It usually takes many times of doing step 5 and 6 until the result works as intended, as it's quite easy to overlook some detail.
I'm not going to make a repository because it would increase the amount of work many many times.
Instead, after the mod is complete (or at least reaches 1.0), I will make a differential file of this and 1.40n and split up the changes into individual files addressing various fixes, then gather the general purpose ones and apply them to 1.40n and release it as a "Raid 1.50" bugfix release. The individual files will also be available for public.
Unfortunately a lot of the AI logic won't be compatible because some changes depend on others.
For example the change of Lionheart uses the space gained through the removal of Black Channels. The new AI logic for battle spell choices is inserted to where the choice for Word of Recall was, as that spell is now gone. A lot of AI changes are related to spells having new effect, and it would be a huge work trying to separate all of these from changes involving the unchanged spells. However, where it's obvious which change the code belongs to, will all be separated, eventually. I want to make sure everything works perfectly before I start doing that, there is no point separating individual changes when there still is a chance they need to be further altered.
Also, although I name the identified variables and such to make the disassembled code readable, those names are only saved for the original wizards.exe. The changed exe will not have the names inserted because it is a different, just similar exe file that has to be disassembler from zero every time I open it after a change. I don't think the disassembler has the function to export names from one exe to another, changed copy of the same file, although it would be nice if it could do that.
For reference, the two hardest things to do so far were
-Making the AI use the "grant gold for killing enemies" event which was completely unused. This required the making of over 160 bytes of space, and writing new code of that size (roughly 50 lines). Doesn't sound that much but the truth is 160 bytes are a lot when a single instruction is 2-6 bytes and you have to remove existing code to make the space. Fortunately compiled code such as wizards.exe often contains redundant calculations, but not always and not everywhere.
-Making Spell Ward counter combat spells of the same realm. This needed about 80 new bytes in a much tighter area than the previous, and it had to use data I wasn't yet familiar with, the "combat copy of city enchantments" one. I found that out the hard way, after seeing the normal city data is swapped out during combat from the memory.
Compared to this, changing alliance to ignore your units near towns was a single byte change, had to make the jump related to it go to the "end of procedure" instead of the "give the warning" and that's it. Of course, figuring out all the diplomacy variables to the point that I was able to recognize this code is giving the warning took weeks of reading the code and identifying parts and effects.
Below is the code I inserted for the gift gold event, and it had to be separated to 2 parts because that's how I was able to get enough free space.
cmp byte ptr [bp+0Ch],8
jnz skipall ; Action type=killed units in combat
cmp di,0
jnz skipall ; Performed by the player
mov cx,1
allwizardsloop:
cmp cx,si
jz nextwizard ; Skip the wizard being hurt
mov ax,cx
mov dx, 4C8h
imul dx
add ax, si
mov bx, ax
cmp byte ptr [bx-5FDEh], 3 ; 'üE
jnz nextwizard ; Skip if they aren't at war with the victim
mov ax,[bp-2] ;Use base diplamatic value not charismatic one
neg ax ; as a positive value
sar ax,1 ; Effect/8
cmp ax,1
jl nextwizard ; No effect if it's too weak
mov bx,cx
cmp byte ptr[bx-5FCCh],0 ; Skip if wizard already wanting to talk about something else
jnz nextwizard
mov byte ptr[bx-5FCCh],6 ; Event type 6 thanks for attack
mov bx,cx
add [bx-5FE4h],ax ; add the points
shl bx,1
mov [bx-5FD8h],20 ; counts as though it was a +20 effect to avoid overwriting with trivial things
mov [bx-5FC6h],si ; set victim as referred wizard
push ax
mov ax,cx
mov dx, 4C8h
imul dx
mov bx, ax
pop ax
add [bx-5FE4h],ax ; add the points to other side too
cmp byte prt [bx-5FDEh],2
jnz nextwizard ; done if no alliance, otherwise
mov dx, [bx-5DE0h] ; wizard's gold
sar dx,1 ; halve it, not giving more than half owned
cmp dx,0
jz nextwizard ; can't give gold if not having any
call alliance
alliance:
push bx
mov bl,64h ; Multiply effect strength by 100
mul bl
pop bx
cmp ax,dx
jle ok
mov ax,dx ; max gold given is what we have/2
ok:
sub [bx-5DE0h],ax ; deduce spent gold
mov bx,cx
mov byte ptr[bx-5FCCh],2 ; Event type 2 pay gold instead of just saying thanks
shl bx, 1
mov [bx-5EFAh], ax ; Offer this much gold*100
add [-5DE0h],ax ; player gain the gold
mov [bx-5FA2h],si ; set victim as referred wizard
retn
As you can see, the code tells nothing about where it goes or how the space is made for inserting, so it's not really useful for the public.
(October 26th, 2015, 06:31)MrBiscuits Wrote: I have to say these changes sound absolutely amazing, thanks for putting the massive amount of work in.
I have to ask how you are able to understand the code and mod the changes, could you give a very brief outline of how you do it? Also I am wondering whether you would consider making a repository for your code so others can see each change and where it occurs in the code.
Basically it's 6 steps
1. Disassemble the code with a disassembler (IDA is recommended)
2. Figure out what the referenced data structures contain (This one is hard but you only need to do it once per data structure)
3. You need to be able to read and understand 8086 assembly code and write new code for some of the more advanced tasks
4. You need to find where the code you want to change is within the exe and what is the problem with it in case of fixing a bug.
5. If the change needed isn't something as simple as replacing "add 3" with "subtract 2" you need to find useless parts of the code that do nothing and remove them, then rearrange code so that the empty space is where new code is to be inserted, all of this using just the hex editor. Including recalculating all offsets for all jumps pointing to the moved code.
6. After step 5 is done, you use an assembler to compile the code you want to insert and add it into the space you made.
Then you run the game and see if it works, and use the disassembler to make sure all offsets and code was properly inserted. Miscalculating the offset even by 1 makes the code unstable and do totally random things.
It usually takes many times of doing step 5 and 6 until the result works as intended, as it's quite easy to overlook some detail.
I'm not going to make a repository because it would increase the amount of work many many times.
Instead, after the mod is complete (or at least reaches 1.0), I will make a differential file of this and 1.40n and split up the changes into individual files addressing various fixes, then gather the general purpose ones and apply them to 1.40n and release it as a "Raid 1.50" bugfix release. The individual files will also be available for public.
Unfortunately a lot of the AI logic won't be compatible because some changes depend on others.
For example the change of Lionheart uses the space gained through the removal of Black Channels. The new AI logic for battle spell choices is inserted to where the choice for Word of Recall was, as that spell is now gone. A lot of AI changes are related to spells having new effect, and it would be a huge work trying to separate all of these from changes involving the unchanged spells. However, where it's obvious which change the code belongs to, will all be separated, eventually. I want to make sure everything works perfectly before I start doing that, there is no point separating individual changes when there still is a chance they need to be further altered.
Also, although I name the identified variables and such to make the disassembled code readable, those names are only saved for the original wizards.exe. The changed exe will not have the names inserted because it is a different, just similar exe file that has to be disassembler from zero every time I open it after a change. I don't think the disassembler has the function to export names from one exe to another, changed copy of the same file, although it would be nice if it could do that.
For reference, the two hardest things to do so far were
-Making the AI use the "grant gold for killing enemies" event which was completely unused. This required the making of over 160 bytes of space, and writing new code of that size (roughly 50 lines). Doesn't sound that much but the truth is 160 bytes are a lot when a single instruction is 2-6 bytes and you have to remove existing code to make the space. Fortunately compiled code such as wizards.exe often contains redundant calculations, but not always and not everywhere.
-Making Spell Ward counter combat spells of the same realm. This needed about 80 new bytes in a much tighter area than the previous, and it had to use data I wasn't yet familiar with, the "combat copy of city enchantments" one. I found that out the hard way, after seeing the normal city data is swapped out during combat from the memory.
Compared to this, changing alliance to ignore your units near towns was a single byte change, had to make the jump related to it go to the "end of procedure" instead of the "give the warning" and that's it. Of course, figuring out all the diplomacy variables to the point that I was able to recognize this code is giving the warning took weeks of reading the code and identifying parts and effects.
Below is the code I inserted for the gift gold event, and it had to be separated to 2 parts because that's how I was able to get enough free space.
cmp byte ptr [bp+0Ch],8
jnz skipall ; Action type=killed units in combat
cmp di,0
jnz skipall ; Performed by the player
mov cx,1
allwizardsloop:
cmp cx,si
jz nextwizard ; Skip the wizard being hurt
mov ax,cx
mov dx, 4C8h
imul dx
add ax, si
mov bx, ax
cmp byte ptr [bx-5FDEh], 3 ; 'üE
jnz nextwizard ; Skip if they aren't at war with the victim
mov ax,[bp-2] ;Use base diplamatic value not charismatic one
neg ax ; as a positive value
sar ax,1 ; Effect/8
cmp ax,1
jl nextwizard ; No effect if it's too weak
mov bx,cx
cmp byte ptr[bx-5FCCh],0 ; Skip if wizard already wanting to talk about something else
jnz nextwizard
mov byte ptr[bx-5FCCh],6 ; Event type 6 thanks for attack
mov bx,cx
add [bx-5FE4h],ax ; add the points
shl bx,1
mov [bx-5FD8h],20 ; counts as though it was a +20 effect to avoid overwriting with trivial things
mov [bx-5FC6h],si ; set victim as referred wizard
push ax
mov ax,cx
mov dx, 4C8h
imul dx
mov bx, ax
pop ax
add [bx-5FE4h],ax ; add the points to other side too
cmp byte prt [bx-5FDEh],2
jnz nextwizard ; done if no alliance, otherwise
mov dx, [bx-5DE0h] ; wizard's gold
sar dx,1 ; halve it, not giving more than half owned
cmp dx,0
jz nextwizard ; can't give gold if not having any
call alliance
alliance:
push bx
mov bl,64h ; Multiply effect strength by 100
mul bl
pop bx
cmp ax,dx
jle ok
mov ax,dx ; max gold given is what we have/2
ok:
sub [bx-5DE0h],ax ; deduce spent gold
mov bx,cx
mov byte ptr[bx-5FCCh],2 ; Event type 2 pay gold instead of just saying thanks
shl bx, 1
mov [bx-5EFAh], ax ; Offer this much gold*100
add [-5DE0h],ax ; player gain the gold
mov [bx-5FA2h],si ; set victim as referred wizard
retn
As you can see, the code tells nothing about where it goes or how the space is made for inserting, so it's not really useful for the public.
Thanks for your swift and comprehensive reply. It's more complicated than I originally thought!
I downloaded IDA and got it to dissemble the Wizard.exe, but I'm struggling to navigate to even start looking at the code. I have a tab called "Hex view A" that just contains lines like these and none of the sort of machine code that you wrote above:
seg038:0CC0 82 00 74 13 83 3E AA 82 00 7E 0C 0E E8 10 FF C7 é.tâ>¬é.~Þ Ã
How for example would I look at the machine code for where the program "starts" ? And then how would you follow the code from that point?
I don't understand why you need to create space before adding new code. What would happen if you added code without freeing up the space first? Is there no way of adding things without removing other things first and just change the reference to point at the new space?
Point 5 in your list looks the most difficult to me, I hadn't quite realised how restricted you were when making changes. I wonder if some custom tools could be written to make this stage much easier and less painstaking. Or somehow find an alternative solution to stage 5 in your list.
Sorry for all the question, I've done a fair bit of coding but never anything quite like this before. I'm going from "maybe I could help out" to "how on earth is that even possible". If this is cluttering up your mod thread too much maybe we could move this discussion to a new topic instead.
Also I am curious, do you think it would be possible to rewrite the code from scratch using the disassembled code? Not that I think it would be a good idea and it is obviously a huge amount of work, it's just something that I'm wondering about. It might be easier in the long run than messing around with hex code for each tiny change.
And I'm glad that you are going to split up the changes and add them as a bugfix release. I had visions of you arriving in a whirlwind, make this amazing mod and then disappearing without anyone knowing how you managed to do any of it.
Hex view : This just shows you the bytes in the file. So if you want to find the machine code for some instructions, you can look here.
Ida view : This is the disassembled code. If it's not open by default then try View-subviews-disassembly.
Names : A list of stuff you have named after you identified them. To name a procedure or data reference, right click it and select rename.
Functions : A list of the functions/procedures in the file.
Strings : these are just the string constants found in the exe. Not all of them are actually here, but most are. This view is useful if you know the text the game displays and want to find where it gets displayed. For example you want to allow building when there are already 9 units in town then you search for that message.
There are more views but they aren't very important for now.
Quote:I don't understand why you need to create space before adding new code. What would happen if you added code without freeing up the space first?
You can't. If you add even a single extra byte, everything in the file after that will be at a different address than where the loader looks for it and the game would not even load, as well as all jumps between the two parts being off by one byte.
Quote:Is there no way of adding things without removing other things first and just change the reference to point at the new space?
You can jump to a new space, yes, but you need to MAKE the new space first. There aren't miraclous unused large spaces in the file everywhere for us, sadly. And, it has to be in the same segment, otherwise the process is ultra-difficult. Also if the jump you modify is a byte value then it has to be within +-127 bytes, otherwise you need to make +1 byte space for a larger jump instruction.
Quote: I hadn't quite realised how restricted you were when making changes. I wonder if some custom tools could be written to make this stage much easier and less painstaking. Or somehow find an alternative solution to stage 5 in your list.
Not really. You literally have to look for "adds 2 to the value, then throws the result away and overwrite it with 5." style of coding where you can remove the "adds 2" part without changing the overall effect. In other words, use your brain, so it cannot be done automatically, only humans are able to do this part.
In easy cases one such place can yield 9-15 bytes of space and you can find 6-8 of them near together.
In hard cases, you have to actually re-code stuff to make it use less space through code optimalization.
For example to add the "Turns = x" display in combat, I had to recode it from
set position X=45
set position Y=60
set text "mana"
call text display
set position X=55
set position Y=60
convert amount of mana to text
call text display
set position X=45
set position Y=60
set text "skill"
call text display
set position X=55
set position Y=60
convert amount of skill to text
call text display
into this
set a=55, b=60, c="mana", amount = amount of mana
call "displaytext"
set b=70, c="skill", amount = amount of skill
call "displaytext"
set b=80, c="turns", amount = amount of turns
call "displaytext"
displaytext:
set position X = a, position Y=b
set text "c"
call text display
set position X= a+20
convert amount to text
call text display
as you can see "displaytext" is the original second half which displayed the "skill" line, except it uses the passed parameters instead of the original contants. The first half to display the "Mana" line is removed, and calls the second half 3 times, with 3 sets of parameters to display 3 lines that are no longer limited. To further restrict the options, "Turns" had to exist as a text constant available and ready to use in the data segment, fortunately it did, as it is displayed elsewhere. You can see it uses the text belonging to another game part because "Mana:" and "Skill:" has a ":" but "Turn" does not, it was only available without it. Otherwise I would have also needed free room in the data segment to add new text (I guess there are some unused texts like "as swiftness spell" which kryub made not to appear").
In this particular case there was no space that could be made, and as far calls are unmovable, I had to re-use the same one to be ran once more than originally to display more lines of text. Took about 4 hours to get it work.
Assuming you have a large block of free space somewhere (I actually do because I replaced a huge AI routine somewhere so the original is not called anymore) you could in theory get away with only making 3 bytes of space, and using a near call to it IF you are within the same segment. There are about 160 segments so guess what the chances are for that? Yeah... my nice huge empty space will not be useful, it's in a segment where I don't need to add stuff at least at the moment.
By the way, if you are looking for a variable or a procedure in particular, it might be faster to ask than to try find it amongst the 3500 procedures the game has yourself. Even I only identified about one third of them so far.
I feel like we are getting really offtopic here, though, I think you should make a thread for this instead if you have more questions to ask.
(October 26th, 2015, 08:26)Seravy Wrote: I feel like we are getting really offtopic here, though, I think you should make a thread for this instead if you have more questions to ask.
Hey Serevy, would it be possible for you to make a short guide on how to alter the wizard/hero portraits, names, and spellbooks/retorts.
Something clear and easy for a layman to follow (preferably with images).
I ask because when you 'finish' CoM I have a few places I'd like to post links.
Let me explain with an example: I know a webcomic that's really into fantasy wargaming, and will (probably) give space on the front page for a week or so. If I include a little guide from the mod's author that would allow a fan to insert characters from the comic into MoM, then I would be CERTAIN that they'd post it.
(October 27th, 2015, 04:06)Hadriex Wrote: Hey Serevy, would it be possible for you to make a short guide on how to alter the wizard/hero portraits, names, and spellbooks/retorts.
Something clear and easy for a layman to follow (preferably with images).
I ask because when you 'finish' CoM I have a few places I'd like to post links.
Let me explain with an example: I know a webcomic that's really into fantasy wargaming, and will (probably) give space on the front page for a week or so. If I include a little guide from the mod's author that would allow a fan to insert characters from the comic into MoM, then I would be CERTAIN that they'd post it.
Cool, let's see then. Will something like this do?
Names, books and retorts are in Magic.exe.
2AFE0 : Name of the wizard
Following that, there are 5 bytes for the 5 types of books, word values so there will be a 0 between the actual numbers of each type of book.
Following that an ID code for the retort, unfortunately I don't have my list anymore but it goes from 0 to 11h as there are 17 retorts total. Also don't remember if 00 or FF is the "no retort" option because I never used it. There are exactly 14 wizard slots.
To insert the images, you need to use the tweaker.
Use open LBX, select the LBX you want to change.
It'll show the list of entries in the file, you can go through them and see if any are wizard images.
If yes, you check the size, make an equal size png, and import it.
To import, you need to place the file into the folder before opening the LBX, as the tweaker does not update the list of files real time.
I wasn't able to change animation due to a tweaker bug yet, but I did manage to hack the LBX so that the animations are replaced by single images, and from that point the rest is the same. Use the already altered LBX files that have the Touhou wizards and you are good to go.
The game uses 256 colors. The tweaker will apply the palette for you, but it isn't perfect, it generally yields a better result to take palette from one of the original images in that LBX file, and apply it to your new image. Some of the color codes actually are used for system functions like flags, pixels that end up with that color code can appear the wrong color in the game. If you identify such a color, your best bet is to remove it from the palette and make the image not use it at all.
There are a large number of files that contain wizard portraits or animations of some sort, including : HALOFAM.LBX, MAGIC.LBX, WIN.LBX, LOSE.LBX, SPLMASTER.LBX, SPELLOSE.LBX, WIZLAB.LBX, CONQUEST.LBX, DIPLOMAC.LBX, MOODWIZ.LBX, LILWIZ.LBX and WIZARDS.LBX.
To change hero portraits, you modify PORTRAITS.LBX. The default names used for them are in NAMES.LBX.
Finally, the HELP.LBX contains all help texts for the game. You need to modify this if you want right clicking on the wizard to pop up information about your new wizard(s).