Repository navigation
Expand file tree
/
Copy pathtemplates.lua
More file actions
2141 lines (2099 loc) · 49.8 KB
/
Copy pathtemplates.lua
File metadata and controls
2141 lines (2099 loc) · 49.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
-- FASM sources for the injected routines, split to fit UCP's assembler budget.
-- Value mapping supplied by init.lua.
return {
-- accuracySet(unitID): explicit regular-ammunition accuracy, respecting AI-only
-- and fortification overrides. Native cow orders keep their original accuracy.
accuracy_set_code = [[
accuracySet:
push ebx
mov ebx, [esp+8]
push ebx
call PROFILE
add esp, 4
cmp eax, MAXPROFILES
jae ac_no
cmp dword [INACCSETT+eax*4], 0
je ac_no
cmp dword [AIONLYT+eax*4], 0
je ac_ownerok
push ebx
call ISAIOWNED
add esp, 4
test eax, eax
jz ac_no
ac_ownerok:
imul ebx, ebx, 0x490
cmp word [ebx+UNITARRAY+0x3B0], 0
jne ac_no
mov eax, 1
pop ebx
ret
ac_no:
xor eax, eax
pop ebx
ret
]],
-- Replace the native ground scatter with its exact input point. Applying the
-- configured radius later, once per projectile, avoids repeated native drift.
ground_aim_hook_code = [[
groundAimHook:
pushad
mov ebp, esp
push dword [ebp+0x24]
call ACCURACYSET
add esp, 4
test eax, eax
jz ga_pass
mov eax, [ebp+0x24]
imul eax, eax, 0x490
add eax, UNITARRAY
mov ecx, [ebp+0x28]
mov word [eax+0xBE], cx
mov ecx, [ebp+0x2C]
mov word [eax+0xC0], cx
mov ecx, [ebp+0x30]
mov word [eax+0xC2], cx
popad
ret 0x10
ga_pass:
popad
push ecx
mov eax, [esp+8]
jmp RESUME
]],
-- ESI = UnitState + unitID*stride here. Target prediction is complete; only
-- the native random error remains. Return with the original function's ABI.
aim_error_hook_code = [[
aimErrorHook:
pushad
mov eax, esi
sub eax, UNITSTATE
xor edx, edx
mov ecx, 0x490
div ecx
push eax
call ACCURACYSET
add esp, 4
test eax, eax
jz ae_pass
popad
pop edi
pop esi
pop ebp
ret 0xC
ae_pass:
popad
movzx eax, word [esi+0x6CE]
jmp RESUME
]],
-- profile(unitID) -> table index. Preserve other registers. Positive structure
-- height AND the native wall/fortification tile flags distinguish standing on
-- the structure from terrain elevation or standing on the ground beside it.
profile_index_code = [[
profileIndex:
push ebx
push ecx
push edx
mov eax, [esp+16]
cmp eax, 1
jl pf_invalid
cmp eax, MAXUNITS
jge pf_invalid
imul eax, eax, 0x490
add eax, UNITARRAY
mov ebx, eax
movzx eax, word [ebx+0x8E]
cmp eax, MAXTYPES
jae pf_invalid
cmp dword [FORTIFIEDT+eax*4], 0
je pf_done
cmp word [ebx+0xBC], 0
jle pf_done
movsx ecx, word [ebx+0xC4]
cmp ecx, 399
ja pf_done
movsx edx, word [ebx+0xC6]
cmp edx, 399
ja pf_done
lea edx, [edx+edx*2]
mov edx, [TILEROWS+edx*4]
add edx, ecx
cmp edx, 80400
jae pf_done
test dword [TILEFLAGS+edx*4], 0x10000100
jz pf_done
add eax, MAXTYPES
jmp pf_done
pf_invalid:
mov eax, MAXPROFILES
pf_done:
cmp eax, MAXPROFILES
jae pf_return
if HASDECOR = 1
push eax
push dword [esp+20]
call DECORPROFILE
add esp, 8
end if
pf_return:
pop edx
pop ecx
pop ebx
ret
]],
-- The established tick boundary owns profile-change firing-state transitions.
-- Native target scoring calls profileIndex directly and cannot trigger them.
profile_code = [[
profile:
push ecx
push edx
mov ecx, [esp+12]
push ecx
call PROFILEINDEX
add esp, 4
cmp eax, MAXPROFILES
jae p_return
mov ecx, [esp+12]
cmp dword [PROFILESTATET+ecx*4], eax
je p_return
mov [PROFILESTATET+ecx*4], eax
; Do not release an old conditional volley after stepping off its trigger.
; Keep the main cooldown so changing ground/wall state cannot bypass reload.
mov dword [PENDINGT+ecx*4], 0
mov dword [PENDCDT+ecx*4], 0
mov dword [SYNCWAITT+ecx*4], 0
p_return:
pop edx
pop ecx
ret
]],
-- Shared helpers plus the volley routine.
-- volley(unitID, entityType, x, y, z, count, spread) -- cdecl, caller cleans
-- Sets REENTRY while it runs so the fire hook lets our own shots through.
volley_code = [[
; volley(unitID, entityType, x, y, z, count, spread) -- cdecl, caller cleans.
; Sets REENTRY while it runs so the fire hook lets our own shots straight through.
; Callers set S_HEIGHT and S_MULTI first; S_MULTI needs the candidate list that
; scanUnit leaves behind.
volley:
push ebp
mov ebp, esp
push ebx
push esi
push edi
mov dword [REENTRY], 1
; Raise the firing point. The game reads the origin height straight off the
; shooter, so lifting buildingHeight for the duration of the volley is the
; way to shoot from the top of a siege tower rather than from its feet.
mov eax, [ebp+0x08]
imul eax, eax, 0x490
add eax, UNITARRAY
mov [S_SHOOTER], eax
movzx ecx, word [eax+0x3B0]
mov [S_SAVECOW], ecx
cmp dword [S_EXPLICIT], 0
je v_keepcow
mov word [eax+0x3B0], 0
v_keepcow:
movsx ecx, word [eax+0xBC]
mov [S_SAVEBH], ecx
add ecx, [S_HEIGHT]
mov word [eax+0xBC], cx
mov esi, [ebp+0x1C]
cmp esi, 1
jge v_ok
mov esi, 1
v_ok:
xor edi, edi
v_loop:
mov eax, [ebp+0x10]
mov [S_SHOTX], eax
mov eax, [ebp+0x14]
mov [S_SHOTY], eax
mov eax, [ebp+0x18]
mov [S_SHOTZ], eax
cmp dword [S_MULTI], 0
je v_scatter
mov ecx, [S_NCAND]
test ecx, ecx
jz v_scatter
; pick a fresh victim for this projectile
call RND
xor edx, edx
div ecx
mov eax, [S_CANDS+edx*4]
mov ebx, eax
imul ebx, ebx, 0x490
add ebx, UNITARRAY
movsx ecx, word [ebx+0xB6]
mov [S_SHOTX], ecx
movsx ecx, word [ebx+0xB8]
mov [S_SHOTY], ecx
movsx ecx, word [ebx+0xBA]
movsx edx, word [ebx+0xBC]
add ecx, edx
add ecx, 30
mov [S_SHOTZ], ecx
; aim the shooter at this victim, otherwise the projectile damages whoever
; the previous shot was aimed at
mov edx, [S_SHOOTER]
mov word [edx+0x344], ax
mov ecx, [ebx+0x98]
mov [edx+0xA0], ecx
; fall through: a random victim is still aimed at imperfectly
v_scatter:
mov dword [S_MOVED], 0
mov ecx, [S_INACC]
test ecx, ecx
jz v_fan
v_disk:
call scat
mov [SCATY], eax
mov ecx, [S_INACC]
call scat
mov ebx, eax
imul ebx, ebx
mov edx, [SCATY]
imul edx, edx
add ebx, edx
mov ecx, [S_INACC]
mov edx, ecx
imul edx, edx
cmp ebx, edx
ja v_disk
mov edx, [SCATY]
add [S_SHOTX], edx
add [S_SHOTY], eax
mov dword [S_MOVED], 1
v_fan:
cmp dword [S_MULTI], 0
jne v_settle ; random targets: each shot is aimed properly
test edi, edi
jz v_settle
mov ecx, [ebp+0x20]
test ecx, ecx
jz v_settle
call scat
add [S_SHOTX], eax
mov ecx, [ebp+0x20]
call scat
add [S_SHOTY], eax
mov dword [S_MOVED], 1
v_settle:
cmp dword [S_MOVED], 0
je v_fire
call FIXSCATTER
v_fire:
; Cow is an output entity type, not a native unit-dispatch mode. Route it
; through the siege cow path for its launch height, muzzle and ground-target
; metadata. The generic path would give it arrow-style target/height data.
mov eax, [ebp+0x0C]
cmp eax, 23
jne v_dispatch
mov edx, [S_SHOOTER]
mov word [edx+0x3B0], 1
mov eax, 2
cmp word [edx+0x8E], 40
jne v_dispatch
mov eax, 3
v_dispatch:
push dword [S_SHOTZ]
push dword [S_SHOTY]
push dword [S_SHOTX]
push eax
push dword [ebp+0x08]
mov ecx, UNITSTATE
call FIREPROJ
inc edi
cmp edi, esi
jl v_loop
mov dword [REENTRY], 0
mov eax, [S_SHOOTER]
mov ecx, [S_SAVEBH]
mov word [eax+0xBC], cx
mov ecx, [S_SAVECOW]
mov word [eax+0x3B0], cx
pop edi
pop esi
pop ebx
pop ebp
ret
; ecx = spread -> eax in [-spread, +spread]
scat:
push ebx
push ecx
call RND
pop ecx
lea ebx, [ecx*2+1]
xor edx, edx
div ebx
sub edx, ecx
mov eax, edx
pop ebx
ret
]],
-- Entry hook on the game's "unit fires a projectile" dispatcher.
-- Entry stack: [esp]=ret [+4]=unitID [+8]=entityType [+0xC]=x [+0x10]=y [+0x14]=z
-- rnd -> eax in [0, 0x7FFF). Clobbers eax and edx only, so a caller may keep
-- anything it needs in ecx or a callee-saved register across the call.
-- fixScatter: after an aim point has been nudged, pull it back onto the map and
-- take the ground height there. Without this the shot keeps the height of the
-- thing it was originally aimed at, and an arcing projectile - a catapult or
-- trebuchet rock - is asked to reach a point that is not on the ground. The
-- solver has no answer and the rock never appears.
fixscatter_code = [[
fixScatter:
push ebx
mov eax, [S_SHOTX]
cmp eax, 8
jge fs_xlo
mov eax, 8
fs_xlo:
cmp eax, MAPMICROMAX
jle fs_xhi
mov eax, MAPMICROMAX
fs_xhi:
mov [S_SHOTX], eax
mov ecx, [S_SHOTY]
cmp ecx, 8
jge fs_ylo
mov ecx, 8
fs_ylo:
cmp ecx, MAPMICROMAX
jle fs_yhi
mov ecx, MAPMICROMAX
fs_yhi:
mov [S_SHOTY], ecx
sar eax, 3
sar ecx, 3
mov edx, ecx
lea edx, [edx+edx*2]
mov edx, [edx*4+TILEROWS]
add edx, eax
movzx ebx, byte [edx+TERRAINH]
mov [S_SHOTZ], ebx
pop ebx
ret
]],
rand_code = [[
rnd:
mov eax, [SEED]
imul eax, eax, 1103515245
add eax, 12345
mov [SEED], eax
shr eax, 16
and eax, 0x7FFF
ret
]],
-- setStagger(unitID, unitType) -- cdecl. Rolls the wait before this unit's next
-- staggered projectile and stores it.
stagger_code = [[
setStagger:
push ebp
mov ebp, esp
push ebx
mov ebx, [ebp+0x0C]
mov ecx, [STAGMAXT+ebx*4]
sub ecx, [STAGMINT+ebx*4]
inc ecx
cmp ecx, 1
jge ss_span
mov ecx, 1
ss_span:
mov [S_TMP], ecx
call RND
xor edx, edx
div dword [S_TMP]
mov eax, edx
add eax, [STAGMINT+ebx*4]
cmp eax, 1
jge ss_store
mov eax, 1
ss_store:
mov ecx, [ebp+0x08]
mov [PENDCDT+ecx*4], eax
pop ebx
pop ebp
ret
]],
-- chooseInterval(unitID, unitType) -> eax = ticks between shots in the unit's
-- current state, or 0 if it should hold fire. Works out whether the unit is
-- moving and whether it is fixed to a wall, since both change the rate.
interval_code = [[
chooseInterval:
push ebp
mov ebp, esp
push ebx
push esi
push edi
mov eax, [ebp+0x08]
mov edx, [ebp+0x0C]
; Is this unit actually moving? The game has no "walking" flag, so compare
; its interpolated position with last tick's. A slow unit does not advance
; every single tick, so a change keeps it counted as moving for a while.
mov esi, eax
imul esi, esi, 0x490
add esi, UNITARRAY
movzx ecx, word [esi+0xB6]
shl ecx, 16
movzx edi, word [esi+0xB8]
or ecx, edi
cmp ecx, [LASTPOST+eax*4]
mov [LASTPOST+eax*4], ecx
je ci_still
mov dword [MOVECDT+eax*4], MOVEHYST
jmp ci_known
ci_still:
mov ecx, [MOVECDT+eax*4]
test ecx, ecx
jz ci_known
dec ecx
mov [MOVECDT+eax*4], ecx
ci_known:
cmp dword [MOVECDT+eax*4], 0
jne ci_moving
mov ebx, [ISTANDT+edx*4]
jmp ci_state
ci_moving:
mov ebx, [IMOVET+edx*4]
ci_state:
push edx
push eax
call CHECKATTACHED
add esp, 4
pop edx
mov eax, [ebp+0x08]
cmp dword [S_ATTACHED], 0
je ci_done
mov ecx, [ATTINTT+edx*4]
cmp ecx, -1
je ci_done ; -1 means "same rate as usual"
mov ebx, ecx
ci_done:
mov eax, ebx
pop edi
pop esi
pop ebx
pop ebp
ret
]],
-- crewOk(unitID, unitType) -> eax = 1 when the unit may shoot. Siege engines
-- need their current crew aboard, unless the engine is fixed to a wall and configured to carry on
-- without it.
crew_code = [[
crewOk:
push ebp
mov ebp, esp
push esi
push edi
mov eax, [ebp+0x08]
mov edx, [ebp+0x0C]
; A siege engine only shoots while its crew is aboard. The cooldown is left
; untouched while unmanned, so it opens fire as soon as engineers arrive.
; References and the lifetime "engineers sent" count are not crew aboard.
mov ecx, [MANNEDT+edx*4]
test ecx, ecx
jz ck_yes
cmp dword [S_ATTACHED], 0
je ck_count
cmp dword [ATTCREWT+edx*4], 0
jne ck_yes ; fixed to a wall: crew no longer required
ck_count:
mov esi, [ebp+0x08]
imul esi, esi, 0x490
add esi, UNITARRAY
movsx edi, word [esi+0x3B4]
cmp edi, ecx
jl ck_no
ck_yes:
mov eax, 1
pop edi
pop esi
pop ebp
ret
ck_no:
xor eax, eax
pop edi
pop esi
pop ebp
ret
]],
fire_hook_code = [[
cmp dword [REENTRY], 0
jne h_pass
pushad
mov ebp, esp
mov eax, [ebp+0x24]
cmp eax, 1
jl h_pop
cmp eax, MAXUNITS
jge h_pop
push eax
call PROFILE
add esp, 4
cmp eax, MAXPROFILES
jae h_pop
cmp dword [AIONLYT+eax*4], 0
je h_notaionly
push eax
push dword [ebp+0x24]
call ISAIOWNED
add esp, 4
mov ecx, eax
pop eax
test ecx, ecx
jz h_pop ; the player's own unit: do not touch its shot
h_notaionly:
; Select the ammunition slot BEFORE remapping. A rock-to-mangonel change
; must not replace a player's cow order or multiply its native single cow.
cmp dword [ebp+0x28], 23
je h_cow
cmp dword [ebp+0x28], 2
je h_checkcow
cmp dword [ebp+0x28], 3
jne h_regular
h_checkcow:
mov ebx, [ebp+0x24]
imul ebx, ebx, 0x490
cmp word [ebx+UNITARRAY+0x3B0], 0
jne h_cow
h_regular:
cmp dword [NATIVECYCLET+eax*4], 0
je h_unscheduled
lea ecx, [ebp+0x24] ; original native dispatch arguments
push ecx
push eax
push dword [ebp+0x24]
call NATIVERELEASE
add esp, 12
jmp h_block
h_unscheduled:
cmp dword [SUPPRESST+eax*4], 0
jne h_block
mov ecx, [REMAPT+eax*4]
mov ebx, [SPRITET+eax*4]
mov [CURRENTVARIANT], ebx
mov edx, [COUNTT+eax*4]
jmp h_count
h_cow:
mov ebx, [COWSPRITET+eax*4]
mov [CURRENTVARIANT], ebx
mov ecx, [COWREMAPT+eax*4]
mov edx, [COWCOUNTT+eax*4]
test edx, edx
jnz h_count
cmp ecx, -1
je h_pop ; completely preserve unconfigured cow shots
mov edx, 1
h_count:
test edx, edx
jz h_nocount
; The native mangonel dispatches seven projectiles in one update. A count
; replaces that volley instead of multiplying each of its seven calls.
mov ebx, [ebp+0x24]
cmp dword [NATIVESEENT+ebx*4], 0
jne h_block
mov dword [NATIVESEENT+ebx*4], 1
jmp h_fire
h_nocount:
cmp ecx, -1
jne h_single
cmp dword [HEIGHTT+eax*4], 0
jne h_single
cmp dword [INACCT+eax*4], 0
je h_pop
h_single:
mov edx, 1
h_fire:
mov dword [S_EXPLICIT], 0
cmp ecx, -1
je h_originaltype
mov dword [S_EXPLICIT], 1
jmp h_type
h_originaltype:
mov ecx, [ebp+0x28]
h_type:
mov ebx, [HEIGHTT+eax*4]
mov [S_HEIGHT], ebx
mov dword [S_MULTI], 0
mov ebx, [INACCT+eax*4]
mov [S_INACC], ebx
push dword [SPREADT+eax*4]
push edx
push dword [ebp+0x34]
push dword [ebp+0x30]
push dword [ebp+0x2C]
push ecx
push dword [ebp+0x24]
call VOLLEY
add esp, 28
h_block:
popad
mov ecx, [esp]
add esp, 0x18
xor eax, eax
jmp ecx
h_pop:
popad
h_pass:
push ebx
push esi
push edi
mov edi, [esp+0x14]
jmp RESUME
]],
-- Restrict the existing native acquisition owner, before wind-up/ammunition.
-- Installed only for an explicit manual-only profile. Pass-through replays
-- the complete six-byte prologue; thiscall failure returns without a shot.
acquire_hook_code = [[
acquirePolicy:
pushfd
pushad
mov ebx, [esp+40]
push ebx
call PROFILE
add esp, 4
cmp eax, MAXPROFILES
jae ap_pass
cmp dword [AUTOTARGETT+eax*4], 0
jne ap_pass
cmp dword [AIONLYT+eax*4], 0
je ap_order
push ebx
call ISAIOWNED
add esp, 4
test eax, eax
jz ap_pass
ap_order:
push ebx
call MANUALORDER
add esp, 4
test eax, eax
jnz ap_pass
popad
popfd
xor eax, eax
ret 4
ap_pass:
popad
popfd
sub esp, 0x40
push ebx
push esi
push edi
jmp RESUME
]],
-- The game's Catapult/Trebuchet modes return before its automatic unit-list
-- loop. Only our synchronous scheduled ACQUIRE call opts past that gate.
priority_gate_hook_code = [[
nativeAutoGate:
cmp dword [FORCEACQUIRE], 0
je nag_native
cmp dword [FORCEACQUIRE], edi
je nag_continue
nag_native:
mov eax, [esp+0x10]
cmp eax, 2
je nag_reject
cmp eax, 3
je nag_reject
nag_continue:
jmp NATIVEGATECONT
nag_reject:
jmp NATIVEREJECT
]],
-- This is the native acquisition owner's weapon-range load. Only the
-- module's synchronous opt-in acquisition substitutes its profile range;
-- ordinary game acquisition replays the original table load unchanged.
priority_range_hook_code = [[
nativeTargetRange:
cmp dword [FORCEACQUIRE], 0
je ntr_native
cmp dword [FORCEACQUIRE], edi
jne ntr_native
pushad
mov eax, edi
xor edx, edx
mov ecx, 0x490
div ecx
push eax
call PROFILEINDEX
add esp, 4
cmp eax, MAXPROFILES
jae ntr_fallback
mov eax, [RANGET+eax*4]
mov [esp+28], eax ; saved EAX; imul in native code squares it
popad
jmp NATIVERANGECONT
ntr_fallback:
popad
ntr_native:
mov eax, [NATIVERANGET+eax*4]
jmp NATIVERANGECONT
]],
-- The game's rounded-tile distance check remains untouched for ordinary
-- acquisition. During an opt-in scheduled shot, strict_range checks each
-- candidate's exact native coordinates before the owner scores it.
priority_candidate_hook_code = [[
nativeCandidateRange:
cmp dword [FORCEACQUIRE], 0
je ncr_native
cmp dword [FORCEACQUIRE], edi
jne ncr_native
cmp dword [S_RANGE8SQ], 0
je ncr_native
pushad
movsx eax, word [ebx+esi+0x6CA]
sub eax, [esp+32+0x18]
imul eax, eax
movsx ecx, word [ebx+esi+0x6CC]
sub ecx, [esp+32+0x14]
imul ecx, ecx
add eax, ecx
cmp eax, [S_RANGE8SQ]
popad
ja ncr_reject
jmp NATIVECANDCONT
ncr_native:
cmp edx, [esp+0x34]
jg ncr_reject
jmp NATIVECANDCONT
ncr_reject:
jmp NATIVECANDREJECT
]],
-- The native owner already computed Chebyshev distance plus its attention
-- term. Adjust only that score; let its shooter/target-type, LOS and engaged
-- candidate policy run unchanged. One bonus point equals one tile (8 micro).
priority_score_hook_code = [[
nativeTargetScore:
pushad
movzx eax, word [esi+edi+0x6A2]
cmp eax, MAXTYPES
jae nts_done
cmp byte [PRIORITYTYPET+eax], 0
je nts_done ; ordinary shooters avoid profile/decor work
mov eax, edi ; native acquisition keeps unitID * 0x490 in EDI
xor edx, edx
mov ecx, 0x490
div ecx
push eax
call PROFILEINDEX ; read-only, no firing-state transition
add esp, 4
cmp eax, MAXPROFILES
jae nts_done
mov edx, [PRIORITYPTRT+eax*4]
test edx, edx
jz nts_done
movzx ecx, word [ebx+esi+0x6A2] ; candidate native unit type
cmp ecx, MAXTYPES
jae nts_done
movzx ecx, byte [edx+ecx]
shl ecx, 3
sub dword [esp+8], ecx ; saved EBP is the native base score
jns nts_done
mov dword [esp+8], 0
nts_done:
popad
cmp eax, 4 ; replay the displaced native branch
jne nts_else
jmp NATIVESCORECONT
nts_else:
jmp NATIVESCOREELSE
]],
-- A human's explicit native attack takes precedence over automatic search.
manual_order_code = [[
manualOrder:
push ebx
push dword [esp+8]
call ISAIOWNED
add esp, 4
test eax, eax
jnz mo_no
mov ebx, [esp+8]
imul ebx, ebx, 0x490
add ebx, UNITARRAY
movzx eax, word [ebx+0x8E]
cmp dword [NATIVESTARTT+eax*4], 0
je mo_no
movzx eax, word [ebx+0x39C]
cmp eax, 4
je mo_yes
cmp eax, 5
je mo_yes
cmp eax, 9
je mo_yes
cmp eax, 22
je mo_yes
cmp eax, 23
je mo_yes
cmp eax, 34 ; native structure/entity order
jne mo_no
mo_yes:
mov eax, 1
pop ebx
ret
mo_no:
xor eax, eax
pop ebx
ret
]],
-- Target picking for forced shooters.
-- pickTarget(unitID, unitType) -> eax = 1 when a target was set, 0 otherwise
-- Saves the unit's own targeting fields first; restoreTarget puts them back so
-- driving a unit's aim never disturbs the order it was actually given.
pick_code = [[
pickTarget:
push ebp
mov ebp, esp
push ebx
push esi
push edi
mov eax, [ebp+0x08]
imul eax, eax, 0x490
add eax, UNITARRAY
mov [S_UNITPTR], eax
mov esi, eax
call pk_saveorder
mov ecx, [ebp+0x08]
mov [S_SELF], ecx
mov dword [S_NCAND], 0
movsx ecx, word [esi+0xC4]
mov [S_TX], ecx
movsx ecx, word [esi+0xC6]
mov [S_TY], ecx
movzx ecx, word [esi+0x96]
mov [S_OWNER], ecx
cmp ecx, 8
jbe pk_teamok
xor ecx, ecx
pk_teamok:
mov ecx, [TEAMTBL+ecx*4]
mov [S_TEAM], ecx
mov edx, [ebp+0x0C]
mov ecx, [RANGET+edx*4]
mov [S_R], ecx
mov eax, ecx
imul eax, ecx
mov [S_R2], eax
mov dword [S_RANGE8SQ], 0
cmp dword [STRICTRANGET+edx*4], 0
je pk_range_ready
shl eax, 6 ; exact native coordinates: eight per tile
mov [S_RANGE8SQ], eax
pk_range_ready:
mov ecx, [WALLMINT+edx*4]
mov eax, ecx
imul eax, ecx
mov [S_WMIN2], eax
; Automatic policies must not replace a human's explicit attack order.
push dword [ebp+0x08]
call MANUALORDER
add esp, 4
test eax, eax
jz pk_policy
pk_nativeorder:
push dword [ebp+0x08]
mov ecx, UNITSTATE
call ACQUIRE ; native validity, range and target prediction
mov esi, [S_UNITPTR]
call pk_saveorder ; keep native cleanup of stale/reused orders
test eax, eax
jz pk_fail ; native acquisition must accept the order
call pk_inrange ; respect the configured range on scheduled shots
test eax, eax
jz pk_fail
mov eax, 2 ; keep the chosen target; no random-target scan
jmp pk_out
pk_policy:
cmp dword [AUTOTARGETT+edx*4], 0
je pk_fail ; no search without an explicit human order
mov ebx, edx
shl ebx, 2
add ebx, ORDERT
xor edi, edi
pk_next:
mov dword [S_NCAND], 0
cmp edi, 4
jge pk_fail
movzx ecx, byte [ebx+edi]
inc edi
test ecx, ecx
jz pk_fail
cmp ecx, 1
je pk_units
cmp ecx, 2
je pk_walls
cmp ecx, 5
je pk_cluster
mov [S_CLASS], ecx
call SCANBLD
test eax, eax
jz pk_next
mov esi, [S_UNITPTR]
mov word [esi+0x336], ax
mov ecx, eax
imul ecx, ecx, 0x32C
add ecx, BLDBASE
mov ecx, [ecx+0xD8]
mov [esi+0x3A0], ecx
mov word [esi+0x39C], 9
jmp pk_ok
pk_cluster:
push dword [ebp+0x08]
call ISAIOWNED
add esp, 4
test eax, eax
jz pk_units ; humans without an order use ordinary unit aim
mov edx, [ebp+0x0C]
mov ecx, [DRADT+edx*4]
mov eax, ecx
imul eax, ecx
mov [S_DR2], eax
mov ecx, [DMINT+edx*4]
mov [S_DMIN], ecx
call SCANUNIT
call SCANCLUSTER
test eax, eax
jz pk_next
jmp pk_aim
pk_units:
if HASPRIORITY = 1
mov edx, [ebp+0x0C]
cmp dword [MULTIT+edx*4], 0
jne pk_scanunits ; random volleys need the module candidate array
cmp dword [PRIORITYPTRT+edx*4], 0
je pk_scanunits
mov eax, [ebp+0x08]
imul eax, eax, 0x490
mov [FORCEACQUIRE], eax
push dword [ebp+0x08]
mov ecx, UNITSTATE
call ACQUIRE ; use native candidate list and full native policy
mov dword [FORCEACQUIRE], 0
test eax, eax
jz pk_next
mov esi, [S_UNITPTR]
movzx edx, word [esi+0x344]
cmp edx, 0xFFFE ; Mangonel-style native coordinate result
je pk_nativecoords