Last days I had several intense CTL/EVENTS testing sessions. Chaged the code forward and backward, adding “play_self” and “play_other” OpCodes here and there; commented, changed handlers, added and removed undocumented code a lot of time. It was hard and fun, sometimes frustrating. I undestands things a bit better now, and I add my consideration for the good of the community. Feel free to correct, comment and eventually use any part of this in the way you think it's better. This is a VERY long post, thus take your time if you're interested in deep CTL analysis
EVENTS HANDLING All units use the same way to handle events. There is a “base” function (.func 121) that handles some events (66, 68, 69, 61, 65, 78, 48, 54, 1, 25, 57, 26, 9, 14, 22, 24, 48, 2, 5, 11, 10, 8, 16, 28, 29, 17) in the same way for all units. There is a “specialized” function for each unit type (Ground units 122, Artillery 123, Wizards 124, Archers 125... didn't verified it there are other functions like “monster” or the like) that handles other events in a different way for for each unit type. The specialized function always call the “.func 121” base function after doing his event handling. Each unit has his “personal” event handler, that can “override or complete” events controlled by “base” and “specialized” functions (or handle events unhandled) and then simply “call” the specialized function An example of event handler for an infantry or cavalry is
.func 14 clear_ctrl_flag 8 do get_event on_event 4 #44 set_return_func_iftrue 127 end_event 6844 call 122 @0xABC test_more_events while return_from_event_handler
In this simple handler example, we want the troup to do something different from “standard” for event 4 (the event generated by “search_and_attack_enemy 4, 0, 4096”) and then “call 122” to make the rest of the events to be handled in “standard way”. If you use “end_event 6844”, this particular event (in our example “event 4”) will not be handled by other handlers, the script will jump to the 0xABC label and the next event will be handled. If you use “end_event 3567”, the event will be eventually handled by other “on_event 4” statements in “base” and “specialized” functions.
This is the way events for enemy troups are handled in all maps and thus the way we can do for our campaigns. Make an handler to manage some events, using “ end_event 6844” to completely ovveride en event or “ end_event 3567” to make some things happens before standard handling, and then call the correct specialized function (122, 123, 124, 125) to handle “standard” event actions.
I looked for event understanding since I started to make my personal campaign, because I felt immediatley that is the place where the secret of the perfect AI live. This were all my “discoveries”
EVENTS - 61. Hit by ranged attack (spell too). The event seems to fire only if at least one shot landed (no matter if no casuality done because the unit is saved by armor or shield of plotos). Thus if a unit is under fire but for that round not a single hit reach the target, the event isn'f fired. - 5. Event fired when unit is being charged, in the moment the charge start (before the actual fight start, but after the “Charge!” cry is sent) - 10. The actual hand to hand fight started (no matter who was the charger) - 12. “Lost a hand to hand fight round or charged in the flank/rear” AND “failed the leadership test”. Usually this send back to “.func 126” (Artillery) or “.func 132” (all other unit type) that contains istruction to make the unit rout. - 22. Friendly unit completely destroyed - 52. Some heavy damage sustained by ranged attacks. I cannot say the correct value, but if a unit loses something around 25% of its alive members, this event fire up. - 79. Hit by a spell (fired before event 61). - 15. Enemy flee the hand to hand combat (rout) - 16. Unit succesfully regroup after routing - 25. Regroup after pursuing an enemy.
CTL OPCODES - #5b and #5d. This codes send the unit routing no matter what (I tried immune to fear and will never rout, but the code still send the unit retreating. I will try to update my “ctldis.py” and call this cod “unit_always_rout1” and “unit_always_rout1”. - test_self_afraid. First I thought this was a “test if the unit fail a fear test”, while it simply means that the unit is “vulnerable to fear”. Unit with “Will never rout” and/or “immune to fear”, always pass this test, while all other units always fail the test - search_and_attack_enemy. Big issue here. This opcode, diffently from what I thought and undestood at first read, DO NOT make the unit really start to go to the fight, but only verify if there is some possible enemy in sight, set the conditional flag to true and fire the arg1 event for the unit. Thus “search_and_attack_enemy 4, 0, 4096” will fire event 4 for this unit if there is an enemy in sight to fight, but the unit will stand still and go on doing what it was doing. The event handler, for event “4”, usually contains “#44” and “set_return_func_iftrue 127”. In this “.func 127” there are these opcodes: “#14 4, -1”, “#48”, and finally “#4f”. This three codes will make the unit move versus the designed enemy and fight if they “contact”. To make the unit “charge” when at range, it is first tested that the enemy unit is at the right range to be charged (#58) and then (if true) the charge action is launched (#59). A better name for “search_and_attack_enemy” could be "test_enemy_to_attack_in_sight". All this, in B101.CTL, this is performed by this code. In “.func 0” there is this:
search_and_attack_enemy 4, 0, 4096
that fire the event “4” that is handled by “.func 14” with this code:
And then all the functions called starting from ".func 127"
.func 127 #14 4, -1 send_event_to_stored_unit 20 wait_unit_flag1_clear 16392 play_self 1 #48 #4f ;now the unit start to walk toward the enemy do for 2 set_timer 10 #58 ;#58 => test if the enemy is in range for charging goto_iftrue 129 wait_for_timer test_unit_flag1 16 iftrue restore_ip endif next #49 always
.func 134 hold sleep wait_unit_flag1_clear 8 #50 restore_ip
“.func 134” seems to be the end of the battle and is fired if the charge failed (#59 returned false) or the “unit_flag1 16” is 0 (I think this mean the unit is still fighting, pursuing or locked to attack an enemy). - search_and_shot_enemy. Apply the same consideration made on "search_and_attack_enemy". A better name could be "test_enemy_to_shot_in_sight" - #44. It's used to make some test and always followed by some sort of “set_return_func” statement to make the unit start a fight (usually .func 127). I'm sure the test is failed if the unit is routing. I can reasonably suppose that “#44” is a test for being sure the unit is “able to fight” (unit alive and not routing/fleeing/paralyzed/pursuing). I will try to update my “ctldis.py” and call this opcode “test_unit_can_fight” - #14. This is probably the not-documented opcode most used out there... being able to understand what this damn code do, could be an big improvement of CTL understanding - return. What's the meaning of any istruction after a “return” statement? It look like “wasted code”, isn't it? As an example, in B101, you find in “.func 122” a “return” statement followed by some othere statemens that I do not see how they can be executed and thus I cannot see their pourpose. - #58. Verify if the enmy unit is at the right range to be charged. I will try to update my “ctldis.py” and call this opcode “test_charge_range” - #59. Charge. I will try to update my “ctldis.py” and call this opcode “charge2” (because there still is another “charge” OpCode) UNIT FLAGS I find that a lot of “test_unit_flagX n” and “wait_unit_flagX_clear n” are made without apparently being any “set_unit_flagX n” (or clear) being anywhere. Is it possible that some of these flag are set/cleared by default in some situations? There are “wait_unit_flag3_clear, wait_unit_flag3_set, test_unit_flag3”, but there isn't any “set” or “clear” for flag3... What does this mean? In B101.CTL there is “wait_unit_flag3_clear 8192”... this means that flag3 update is hardcoded in the game. This make manipulation of flags a bit dangerous if not perfectly understood, and thus I suggest to use “unit registers” instead of “unit_flags”, unless you know what you're doing
Hope this hepls someone to improve in CTL understanding
I didn't undestand everything you wrote, but I think it will be better when I got all the "files" and play with them for a while... Once I did some experiments, I can put down a list of "questions" if needed.
I played a lot with CTL, thus I can create now a map with an "immobile" enemy. This will allow me to walk the map all around to verify all object are correctly placed, blocking movement and eventyally line of sight.
I think that after an initial slow start, we will push ahead with all the maps very quickly
We will need a good "file send/recive" method... I can send you my private email by PM if files aren't too big.
I'm still here, ready to work on btb files I though the better way to do this:
1) You send me the "original" .bmp file 2) You send me a "modified" .bmp file with indication about where to put things (like a simple colored "circle" on the image: green circle is a "tree", red circle is a "big rock" and so on...) 3) If needed, you send me the sprites too (if the object to be put is not a "standard" object but a new object not included in original Dark Omen)
With these three things, I can try to copy a .prj file from a similar dimension map, add and remove objects in the prj file and then edit a btb.xml file to create the correct boundaries for all object on the map
I need only some explanation about the region chunks in BTB file that I didn't understand completely
CTL EVENTS By now I'm good confident about these 6 Charge button clicked (one way to charge, not the only one) 9 Target selected (spell or fire) 16 Regroup 25 Regroup after pursuing 61 Hit by ranged fire or spell 73 Spell cast 71 Seems to be enemy in range or approaching. Fire multiple times until the unit start to fire or is engaged 52 Unit suffered loss from range (do not fire always, probably when the loss are of some minimum value) 79 Hit by spell (Spells fire both this event and event 61) 58 Enemy approaching/charghing 65 Target lost/Enemy lost (ranged/melee) 64 Charged
It's very difficult to follow the path of those damn absurd function calling eachother on and back... I will like to meet these "great" programmers and ask them WHY LIKE THIS?
By the way
Event 3 Goblin archer event 3 change the return function to 110, where there are some "play_self 23" ("target recived"), conditioned to some unknown tests (probably somethig that always fail, like testing it's a player unit, because goblin never say "Target recived" with human voice). Does it means event 3 is "target recived" like event?
Event 4 Goblin infantry event 4 send back to 139 with some test that can send to 129 where it "play_self 2" (charge!) after a test (probably verify that it's a player and not an enemy) I think event 4 can be the start of a charge obtained not clicking the charge button (that is event 6) but clicking on opponent banner or by a "search_and_attack_enemy"
Event 69 Goblin archers event 69 has the chance to send back to 111 that send to 115 where there are some "play_self 10" (Open Fire). 69 is probably the start firing event for archers
I think I've to give up on this... I cannot do more than this... too many nested and conditioned calls (undocumented/untranslated conditions istructions)...
Trying to figure out how to lessen the starting gold, I decided to include only 1 member to all starting units, thus the player will be forced to spend all coins for initial replenish In this issue, I had to discover the right price for each unit and finally I come out with the right formula. You probablyl already discovered this, but I didn't find the proper topic and didn't want to bother you with this Here is my discover
1) Each unit type has a basic “Multiplier” that we will call “M” Infantry: M = 1 Ranged Unit: M = 1,5 Cavalry Unit: M = 2,5 Didn't worked other units types, but i should not be too difficult
2) Each armor value add cost to the unit with this forumula. Only the "base armor" enter in the formula, thus if you buy an upgrade to the unit, the value stay unchanged at the Win32edit "Base armor" value AV(0) = 0 AV(n) = AV(n-1) + n*10 Thus we have these results AV(0) = 0 AV(1) = 10 AV(2) = 30 AV(3) = 60 AV(4) = 100 AV(5) = 150
3) In Wh32Edit we can change the “Point Value” It's a bit tricky, because it seems that in the unit cost calculation, this value is set to the closer value between 0, 10, 20 or 30. Thus, if you put 22, it will be 20, if you put 8, it will be 10 We call this PV
4) The final cost of a unit follow this formula T = Unit type (Infantry, Ranged unit, Cavalry) AL=Armor level PV=Point Value (0, 10, 20 or 30) Price(T,AL,P) = 10*M + PV*M + AV(AL)
Example: A cavalry unit, with “Armor Level” equals to 4 (mounted to not change this value) with Point Value 28 (that will be converted to 30) Price(Cavalry, 4, 30 ) = 10*2,5 + 30*2,5 + 100 = 25 + 75 + 100 = 200
Oh... that can be done If you send me the .BTB file and .BMP file and clear me a bit better what to do (how to correctly create the boundary in out.xml) I can give it some try until you're satisfied
If I remember correctly, boundaries are in "Region Chunk" but I sincerly didn't understand what it exactly contains... If you explain me and help me verifying my work on the first maps, I can try the job
I don't know I how can I help you sincerly. I'm a decent computer programmer (not so close to machine as some of you, more a php high level, than a bit to bit low level programmer), but a TERRIBLE graphic and I really don't know how to click when it come to use GIMP for something that isn't simply "change a color here and there"
The best I cand do, for now, is continuing to have some experience in campaing creation, trying to figure out things aren't documented yet (like all the damn event id that if known can completely change the power of our IA units).
Is there a way to change the recruit cost for unit members? I tryed to value the "Unit Cost" in "PLYR_ALL.ARM" and in "B101MRC.ARM" but the recruit cost stay at the standard value (for example 50gc for Grudgebringer Infantry)
Solved! It was "Point Value" to be changed and not "Unit Cost"
I think I found them all... at least the game started to crash at every "play_self" with argument bigger than 189, until 197... then I stopped to try These are all the play_self value I found. It would be nice to have a good way to know the audio file corresponding to each phrase, but I should have to decode all of them and ear every single file... and I really do not want to By the way, from 91 to 169, they're all tutorial message... 78 messages, that are EXACTLY the audio files that start with "T_KZ" (from "T_KZ001.mad" to "T_KZ073" with some message with more than one version like "T_KZ040A.mad" and "T_KZ040B.MAD"). They're in the right order, thus we can use these file in our CTL and thus we have 78 free message to add to our campaigns
Here are the values (add them to the wiki if you think it's usefull or you can send me the user and password to update it and I'll create a page with all this values). My English didn't allow me to understand every single pharse and I put three question marks in the place I didn't undestand
1 Attack! 2 Charge! 3 Crush them! 4 Retreat! 5 We fear the enemy 6 We terrified 7 Stand firm man 8 No mercy 9 Watch your back 10 Open fire 11 Hold fire 12 Enemy sighted 13 Forward 14 I protest my lord 15 Weapon pass through it 16 I have it 17 Regroup 18 Target destroyed 19 Hold! 20 We're under fire 21 ??? 22 Target lost 23 Target recived 24 Enemy lost 25 Turn on them 26 Aiiiiieeeeeee 27 Flee the battle 28 Spell negated 29 ??? 30 Help us 31 Enemy destroyed 32 All is lost 33 We are victorious 34 We're under attack 35 I see 36 The game crash 37 To the north 38 No, to the east 39 They're all around us 40 They're defiling the secret stone. Stop them 41 Man, to the slaughter. Let none of them escape 42 Stand far man. That still burn 43 What sorcery is this? The man in black seems to command them 44 They come from all side. Back to back we must fight these restless dead 45 Hold the high ground man. Purge the evil with steel, iron and fire 46 Helgart is ours. To the tower 47 Hey Bernard. This pass is ideal for an ambush 48 Stand fast man. It's a trap 49 Yha! Orcs. They come for my tank, they should not have it 50 This tower is plagued by a pestilence that I may cure, with stell and fire 51 Be ready blood sucker. I come to lay you rest once and for all 52 Eyes shot man. The forest is alive with goblins 53 Get thos goblins. They're running with the loot 54 Bye Kislev. My axe will tast troll blood before this day's done 55 Target the Hand, it must be destroyed 56 Protect your flanks. Undead on charriot approach 57 Beware man. Remember ??? warnings 58 The Grail ride out. Destroy the Grail 59 In the name of Sigmar, I will destroy you Dread King 60 One by one your minions follow. I come for you Dread King 61 The Black Grail il gone. Honor to victory 62 The Dread King is defeated 63 They're heading for the trading post. Let's spill that green blood 64 Get the loot (Greenskin voice) 65 It's an ambush (Greenskin voice) 66 We got the loot. Ron. (Greenskin voice) 67 Smash ??? (Greenskin voice) 68 More ????? Enemies everywhere (Greenskin voice) 69 ???? (Greenskin voice) 70 ???? (Greenskin voice) 71 Dead to the living (Undead voice) 72 Another time we shall meet ??? (Undead voice) 73 ??? When you will have the chance (Undead voice) 74 You will die horribly (Undead voice) 75 ??? kill them my slaves (Undead voice) 76 The power of darkness will ??? you all (Undead voice) 77 ??? soul will join our dark crusade (Undead voice) 78 ??? my army of darkness (Undead voice) 79 I live again (Undead voice) 80 Empty 81 Give up your life and lie with the dead, I've got you surrounded (Undead voice) 82 Rise brothers. Rise and feast upon the living (Undead voice) 83 ??? what blood feast ??? Slay the living (Undead voice) 84 Come with us, warm with the dead (Undead voice) 85 I live to destroy once more ahahahah (Undead voice) 86 ahahahah... death comes to all things (Undead voice) 87 ??? of the city of the dead (Undead voice) 88 ??? in the shadow of the Dread King (Undead voice) 89 ??? crush the living (Undead voice) 90 ??? give me power (Undead voice) 91 Tutorial message 1 92 Tutorial message 2 93 Tutorial message 3 94 Tutorial message 4 95 Tutorial message 5 96 Tutorial message 6 97 Tutorial message 7 98 Tutorial message 8 99 Tutorial message 9 100 Tutorial message 10 101 Tutorial message 11 102 Tutorial message 12 103 Tutorial message 13 104 Tutorial message 14 105 Tutorial message 15 106 Tutorial message 16 107 Tutorial message 17 108 Tutorial message 18 109 Tutorial message 19 110 Tutorial message 20 111 Tutorial message 21 112 Tutorial message 22 113 Tutorial message 23 114 Tutorial message 24 115 Tutorial message 25 116 Tutorial message 26 117 Tutorial message 27 118 Tutorial message 28 119 Tutorial message 29 120 Tutorial message 30 121 Tutorial message 31 122 Tutorial message 32 123 Tutorial message 33 124 Tutorial message 34 125 Tutorial message 35 126 Tutorial message 36 127 Tutorial message 37 128 Tutorial message 38 129 Tutorial message 39 130 Tutorial message 40 131 Tutorial message 41 132 Tutorial message 42 133 Tutorial message 43 134 Tutorial message 44 135 Tutorial message 45 136 Tutorial message 46 137 Tutorial message 47 138 Tutorial message 48 139 Tutorial message 49 140 Tutorial message 50 141 Tutorial message 51 142 Tutorial message 52 143 Tutorial message 53 144 Console tutorial message 1 145 Console tutorial message 2 146 Console tutorial message 3 147 Console tutorial message 4 148 Console tutorial message 5 149 Console tutorial message 6 150 Console tutorial message 7 151 Console tutorial message 8 152 Console tutorial message 9 153 Console tutorial message 10 154 Console tutorial message 11 155 Console tutorial message 12 156 Console tutorial message 13 157 Console tutorial message 14 158 Console tutorial message 15 159 Console tutorial message 16 160 Console tutorial message 17 161 Console tutorial message 18 162 Console tutorial message 19 163 Console tutorial message 20 164 Console tutorial message 21 165 Console tutorial message 22 166 Console tutorial message 23 167 Console tutorial message 24 168 Console tutorial message 25 169 Console tutorial message 26 170 Argh (dying) 171 Argh (dying) 172 Argh (dying) 173 Argh (dying) 174 Argh (dying) 175 Argh (dying) 176 Argh (dying) 177 Argh (dying) 178 Argh (dying) 179 Argh (dying) 180 Argh (dying) 181 Argh (dying) 182 Argh (dying) 183 Argh (dying) 184 Argh (dying) 185 Argh (dying) 186 Argh (dying) 187 Argh (dying) 188 Argh (dying) 189 Argh (dying)
Now to the harder task of trying to find event ids