;; ;; AI.S - Artificial intelligence for gameplay ;; ;; Kevin Martin ;; 05/14/94 - created, rev 0.03 (KJM) ;; 07/23/94 - serious work begun, rev 0.06 (KJM) ;; 09/15/94 - more cleanup, rev 0.09 (KJM) ;; 09/17/94 - ugly hacks, rev 0.09 (KJM) ;; 11/05/94 - gutted and rewritten, rev 0.21 (KJM) ;; 11/06/94 - greatly enhanced, rev 0.21 (KJM) ;; 11/07/94 - added $Release feature, rev 0.21 (KJM) ;; 12/14/94 - further rework, rev 0.43 (KJM) ;; 01/26/95 - greatly enhanced, rev 0.55 (KJM) ;; ;; The AI module is more strongly code-controlled than most of the system, ;; for increased power and flexibility. ;; ;; At each tick, GAMEPLAY calls AIGAME. For each active computer player ;; that is allowed action (GFp[12]action is not set, but trROM_fl_strategy ;; is), AIPLAY is called. AIPLAY checks the currently allowable triggers ;; for its player. If any of the allowed triggers are active, code is ;; called which makes a decision. That code may or may not set a new ;; strategy, but should almost always disallow the trigger, so that the ;; trigger will not be firing on every tick. Triggers are reset by ;; strategy code before almost any call to Sleep (apexit). ;; ;; Strategy code selects a new track for the computer player, based on ;; current difficulty, various conditions, and in most cases, a random ;; value. A new set of allowable triggers is set and the strategy code ;; returns via APEXIT. ;; INCLUDE macro.i INCLUDE cpumap.i INCLUDE memmap.i Setup "ai" INCLUDE ai.i INCLUDE char.i INCLUDE eeprom.i INCLUDE game.i INCLUDE iface.i INCLUDE misc.i INCLUDE object.i INCLUDE track.i INCLUDE version.i INCLUDE screen.i ; *DEV AREA |ai$$both|, CODE, READONLY aicodebase EQU . ;; AIINIT ;; Initializes the artificial intelligence module, but leaves it inactive. aiinit pushm "r1,r8-r9" ldv r9,airam mov r1,#0 ; clear everything str r1,[r9,#aiflags] ldv r8,(eepromram + ee_new) ; get base difficulty from EEPROM ldrb r1,[r8,#ee_difficulty] cmp r1,#7 ; out of range? movhi r1,#7 ; yep, limit it to extra hard str r1,[r9,#difficulty] mov r1,#0 str r1,[r9,#aieasy] popm "r1,r8-r9" qret ;; AIINIT ends ;; AISTART ;; Called at the start of every round. Sets the initial strategy and such ;; for each active computer player. Completely resets the brain records. aistart entry pushm "r1-r3,r9,r10" ldv r9,airam ldv r10,(airam + brain1) ldv r3,gameram ldr r3,[r3,#roundnum] mov r1,#aifl_start ldv lr,gameram ldr lr,[lr,#gpflag] tst lr,#GFdemo bne as009 ldr r2,[r12,#tick] tst r2,#1 orrne r1,r1,#aifl_throw as009 str r1,[r9,#aiflags] mov r2,#2 ; reset both brains as000 ; reset one brain mov r1,#0 str r1,[r10,#brRAM_flags] strb r1,[r10,#brRAM_trigtime] strb r1,[r10,#brRAM_lasttrack] ; assume we start in stance mov r1,#0xff strb r1,[r10,#brRAM_trigrand] ldv r1,Trig_All ; wake up on anything str r1,[r10,#brRAM_triggers] adrl r1,gate00 ; strategy starts here str r1,[r10,#brRAM_address] ldr r1,[r12,#tick] str r1,[r10,#brRAM_starttick] ldr r1,[r9,#difficulty] ;; ldv r3,ifaceram ldr r3,[r3,#opp_prog] cmp r3,#6 ; higher difficulty for last three addcs r1,r1,#1 ;; cmp r1,#7 ; limit high movcs r1,#7 strb r1,[r10,#brRAM_difficulty] adrl r3,block_base ldrb lr,[r3,r1] ; find starting block percentage strb lr,[r10,#brRAM_blockpct] adrl r3,block_rate ldrb lr,[r3,r1] ; find block adjustment rate strb lr,[r10,#brRAM_pcttick] add r10,r10,#brRAMsize ; point to next brain, if any subs r2,r2,#1 ; another brain record? bne as000 ; yep ; *DEV - initialize for APAUDIT (debugger board only) IF $Release = 0 ; mov r1,#0xd0000 ; add r2,r1,#4 ; str r2,[r1] ENDIF popm "r1-r3,r9,r10" ret ;; AISTART ends ;; AIGAME ;; In: R9 = Game RAM ;; R10 = object RAM records ;; R11 = character RAM records ;; Does computer's thinking during main game loop. Called once per tick. ;; Must preserve R9-R12. aigame entry pushm "r9-r11" ldr r6,[r9,#gpflag] ldv r9,airam ldrb r0,[r11,#chRAM_computer] cmp r0,#0 ; is player 1 computer? beq ai000 ; nope, skip player 1 ldr r0,[r11,#chRAM_cw1] tst r0,#chRAM_cw1_inactive bne ai000 ; player 1 is inactive tst r6,#GFp1action ; did player 1 already act or action blocked? bne ai000 ; yep, skip player 1 ; Player 1 is computer-controlled. add r8,r9,#brain1 ; point to player 1's brain data bl aiplay ai000 ; done with player 1 ldrb r0,[r11,#chRAM2_computer] cmp r0,#0 ; is player 2 computer? beq aiexit ; nope, skip player 2 ldr r0,[r11,#chRAM2_cw1] tst r0,#chRAM_cw1_inactive bne aiexit ; player 2 is inactive tst r6,#GFp2action ; did player 2 already act or action blocked? bne aiexit ; yep, skip player 2 ; Player 2 is computer-controlled. add r10,r10,#obRAMsize ; point to player 2's records add r11,r11,#chRAMsize add r8,r9,#brain2 ; point to player 2's brain data bl aiplay aiexit ldr r2,[r9,#aiflags] ; clear aifl_start after both bic r2,r2,#aifl_start ; players have had a chance at it str r2,[r9,#aiflags] popm "r9-r11" ret ;; AIGAME ends ;; AIPLAY (local) ;; In: R6 = gameplay flags ;; R8 = address of brain RAM record ;; R9 = ai RAM ;; R10 = player's object RAM record ;; R11 = player's character RAM record ;; Artificial intelligence for the given computer-controlled player. ;; Called by AIGAME for each computer player that is presently allowed ;; any action. ;; Must preserve R6,R9-R12. aiplay entry push r6 ;; ldr r7,[r12,#tick] str r7,[r9,#tickhack] ;; ldv r7,objectram cmp r7,r10 addeq r7,r7,#obRAMsize ; R7=opponent's object RAM record ldrb r1,[r8,#brRAM_pcttick] subs r1,r1,#1 ; adjust block percentage yet? strb r1,[r8,#brRAM_pcttick] bne ap000 ; nope adrl r3,block_rate ; yep, reset pcttick ldrb r1,[r8,#brRAM_difficulty] ldrb r1,[r3,r1] strb r1,[r8,#brRAM_pcttick] ldrb r1,[r8,#brRAM_blockpct] ; adjust block percentage upwards add r1,r1,#1 cmp r1,#255 ; limit high movcs r1,#255 strb r1,[r8,#brRAM_blockpct] ap000 ldr r2,[r10,#obRAM_cw1] tst r2,#obRAM_cw1_pushback ; are we being pushed back? bne apexit ; yep, skip all triggers ldr r1,[r8,#brRAM_triggers] ; Check our allowable triggers to see if any of them "fire" ; tst r1,#Trig_blocked ; allow this trigger? ; beq ap010 ; nope ldr r2,[r8,#brRAM_flags] tst r2,#brRAM_fl_blocked ; did we block recently? beq ap010 ; nope bic r2,r2,#brRAM_fl_blocked str r2,[r8,#brRAM_flags] ; Blocked trigger always branches to APBLOCK b apblock ap010 ; done with Trig_blocked ; tst r1,#Trig_nomercy ; allow this trigger? ; beq ap015 ; nope ldr r2,[r9,#aiflags] tst r2,#aifl_mercy ; did no-mercy mode just start? beq ap015 ; nope bic r2,r2,#aifl_mercy str r2,[r9,#aiflags] ; No-mercy trigger always branches to APMERCY b apmercy ap015 ; tst r1,#Trig_start ; allow this trigger? ; beq ap018 ldr r2,[r9,#aiflags] tst r2,#aifl_start ; did the round just start? movne r1,#Trig_start bne apwake ; yep ap018 ; done with Trig_nomercy ; tst r1,#Trig_hit ; allow this trigger? ; beq ap020 ldr r2,[r8,#brRAM_flags] tst r2,#brRAM_fl_hit ; did we get hit recently? beq ap020 ; nope bic r2,r2,#brRAM_fl_hit str r2,[r8,#brRAM_flags] ; Hit trigger always branches to APHIT b aphit ap020 ; done with Trig_hit ldr r2,[r10,#obRAM_trackbase] ldr r2,[r2,#trROM_cw1] ; check track ROM flags tst r2,#trROM_cw1_strategy ; full AI allowed here? beq apexit ; nope, skip the optional triggers tst r1,#Trig_attack ; allow this trigger? beq ap030 ; nope ldr r2,[r7,#obRAM_trackbase] ldr r2,[r2,#trROM_cw1] ; check opponent's track tst r2,#trROM_cw1_attack ; is opponent attacking? movne r1,#Trig_attack bne apwake ; yep, wake up ap030 ; done with Trig_attack tst r1,#Trig_launch ; allow this trigger? beq ap040 ; nope ldr r2,[r7,#obRAM_trackbase] ldr r2,[r2,#trROM_cw1] ; check opponent's track tst r2,#trROM_cw1_launch ; is opponent launching a missile? movne r1,#Trig_launch bne apwake ; yep, wake up ap040 ; done with Trig_launch tst r1,#Trig_shield ; allow this trigger? beq ap050 ; nope ldr r2,[r7,#obRAM_trackbase] ldr r2,[r2,#trROM_cw1] ; check opponent's track tst r2,#trROM_cw1_shield ; is opponent generating a shield? movne r1,#Trig_shield bne apwake ; yep, wake up ap050 ; done with Trig_shield tst r1,#Trig_jumptwd ; allow this trigger? beq ap060 ; nope ldrb r2,[r7,#obRAM_trackid] ; check opponent's track ID cmp r2,#9 ; jumping towards? moveq r1,#Trig_jumptwd beq apwake ; yep, wake up ap060 ; done with Trig_jumptwd tst r1,#Trig_jumpaway ; allow this trigger? beq ap070 ; nope ldrb r2,[r7,#obRAM_trackid] ; check opponent's track ID cmp r2,#10 ; jumping away? moveq r1,#Trig_jumpaway beq apwake ; yep, wake up ap070 ; done with Trig_jumpaway tst r1,#Trig_teleport ; allow this trigger? beq ap080 ; nope ldr r2,[r7,#obRAM_trackbase] ldr r2,[r2,#trROM_cw1] ; check opponent's track tst r2,#trROM_cw1_teleport ; is opponent teleporting? movne r1,#Trig_teleport bne apwake ; yep, wake up ap080 ; done with Trig_teleport tst r1,#Trig_near ; allow this trigger? beq ap085 ; nope ldv r2,gameram ldr r2,[r2,#d2now] ; distance according to POSITIONFIX ldrb r3,[r8,#brRAM_trignear] cmp r2,r3 ; are we near enough? movle r1,#Trig_near ble apwake ; yep, wake up ap085 ; done with Trig_near tst r1,#Trig_random ; allow this trigger? beq ap090 ; nope bl rand8 ldrb r2,[r8,#brRAM_trigrand] cmp r0,r2 ; chance succeeded? movcc r1,#Trig_random bcc apwake ; yep, wake up ap090 ; done with Trig_random tst r1,#Trig_time ; allow this trigger? beq ap100 ; nope ldr r2,[r12,#tick] ldr r3,[r8,#brRAM_starttick] sub r3,r2,r3 ; how long has passed ldrb r2,[r8,#brRAM_trigtime] cmp r3,r2 ; long enough? movcs r1,#Trig_time bcs apwake ; yep, wake up ap100 ; done with Trig_time tst r1,#Trig_raise ; allow this trigger? beq ap110 ; nope ldrb r2,[r10,#obRAM_cw2] tst r2,#obRAM_cw2_raise ; Raise-1 happened? movne r1,#Trig_raise bne apwake ; yep, wake up ap110 ; done with Trig_raise tst r1,#Trig_trans ; allow this trigger? beq ap130 ; nope ldrb r2,[r8,#brRAM_lasttrack] ldrb r3,[r10,#obRAM_trackid] cmp r2,r3 ; did our track ID change? movne r1,#Trig_trans bne apwake ; yep, wake up cmp r3,#0 ; are we in stance? cmpne r3,#1 ; or walking? cmpne r3,#2 moveq r1,#Trig_trans beq apwake ; yep, wake up ap130 ; done with Trig_trans tst r1,#Trig_always ; always wake up? movne r1,#Trig_always bne apwake ; yep, wake up ; No allowable trigger fired, simply return. b apexit apwake ;; bl apaudit ;; ; Wake up the brain and resume strategy code. ldr pc,[r8,#brRAM_address] ; return control to strategy code apexit ; Update brain RAM record before exiting ldrb r4,[r10,#obRAM_trackid] ; get our current track ID strb r4,[r8,#brRAM_lasttrack] ; remember it ; ldrb r4,[r7,#obRAM_trackid] ; get opponent's current track ID ; strb r4,[r8,#brRAM_lastopptrack] ; remember it pop r6 ret ;; AIPLAY ends ;; APAUDIT (local *DEV) ;; For debugging purposes, audits a wake-up call, by recording the ;; trigger mask, followed by the strategy address. ;; Note: this only works on the debugger board. apaudit qret ; *DEV IF $Release = 1 qret ELSE entry pushm "r0-r2" mov r0,#0xd0000 ldr r2,[r0] str r1,[r2],#4 ldr lr,[r8,#brRAM_address] str lr,[r2],#4 cmp r2,#0xf0000 ; limit high movcs r2,#0xf0000 str r2,[r0] popm "r0-r2" ret ENDIF ; RELEASE ;; APAUDIT ends ;; DECISION (local) ;; In: R0 = character ID ;; R1 = difficulty setting (0-5) ;; R2 = address of decision table ;; Out: R1 = new track ID (0xff means none) ;; Makes a random decision from the given table, for the given character ;; and adjusted difficulty setting. decision entry pushm "r0,r2-r4" ldrb r3,[r2],#1 ; retrieve entry count and multiple flag tst r3,#0x80 ; separate tables for each character? bic r3,r3,#0x80 ; always clear multchar flag beq de000a ; skip character indexing if unneeded tst r3,#0x40 ; separate tables for each difficulty? bic lr,r3,#0x40 ; temporarily clear multdiff flag moveq r4,lr,lsl #1 ; R4=entry*2 (character size) addne r4,lr,lr,lsl #1 ; multiply by three movne r4,r4,lsl #2 ; R4=entry*12 (character size w/ multdiff) mla r2,r0,r4,r2 ; index into tables by character ID de000a ; done with character index tst r3,#0x40 ; separate tables for each difficulty? bic r3,r3,#0x40 ; always clear multdiff flag beq de000b ; skip difficulty indexing if unneeded mov r4,r3,lsl #1 ; R4=entry*2 (difficulty size) mla r2,r1,r4,r2 ; index into tables by difficulty level de000b ; done with difficulty index bl rand8 ; get 8-bit random value in R0 de001 ; compare random value against each entry ldrb r4,[r2],#2 ; retrieve this pair's chance ldrb r1,[r2,#-1] ; retrieve this pair's track ID cmp r0,r4 ; does this chance succeed? bls de002 ; yep sub r0,r0,r4 ; nope, subtract the chance from the value subs r3,r3,#1 ; more entries? bne de001 ; yep mov r1,#0xff ; nope, never found anything de002 ; result code is in R1 popm "r0,r2-r4" ret ;; DECISION ends ;; The remainder of this module is strategy code. ;; When strategy code is resumed from APWAKE: ;; R1 = trigger code (Trig_*) ;; At all times: ;; R6 = gameplay flags ;; R7 = opponent's object RAM record ;; R8 = address of brain RAM record ;; R9 = ai RAM ;; R10 = player's object RAM record ;; R11 = player's character RAM record ;; Macros for strategy code. ; Decide decisiontable ; Makes a decision from the specified table. This consists of loading ; R0-R2 with the character ID, adjusted difficulty, and the decision ; table address, and then calling DECISION. The result is in R1. MACRO Decide $m1 ldrb r0,[r11,#chRAM_id] ; get our character ID ldrb r1,[r8,#brRAM_difficulty] adrl r2,$m1 bl decision ; make a random decision MEND ;; GATE00 ;; Initial strategy for all characters at beginning of round. ;; Chooses between: ;; - walking towards ;; - missile weapon * ;; - teleport * ;; - jump towards ;; - any rushing attack * ;; - waiting around a bit gate00 bl AISAFE ; ldrb r2,[r7,#obRAM_trackid] ; cmp r2,#55 ; are we getting up? ; beq gate01a ;; ; If we're corner-trapping the opponent, consider jumping away. ; Check left side first ldv r2,gameram ldr r3,[r2,#d1now] ldr r4,[r2,#bkshift] add r3,r3,r4 ; R3: E1=D1+bkshift cmp r3,#30 ; enough room on the left? bge g00_a ; yep ldr r2,[r2,#d2now] cmp r2,#40 ; characters too close? bge g00_a ; nope ldr r2,[r7,#obRAM_X] ; opponent's position ldr r3,[r10,#obRAM_X] ; our position cmp r2,r3 ; is opponent trapped in the left corner? bge g00_a ; nope ldr r2,[r12,#tick] and r2,r2,#255 cmp r2,#111 ; 1-in-256 chance beq gate07 ; jump away g00_a ; check for right corner trap ldv r2,gameram ldr r3,[r2,#bkwidth] ldr r4,[r2,#bkshift] sub r3,r3,r4 ; bkwidth-bkshift ldr r4,[r2,#d3now] add r3,r4,r3 ; R3: E3=D3+bkwidth-bkshift cmp r3,#30 ; enough room on the right? bge g00_b ; yep ldr r2,[r2,#d2now] cmp r2,#40 ; characters too close? bge g00_b ; nope ldr r2,[r7,#obRAM_X] ; opponent's position ldr r3,[r10,#obRAM_X] ; our position cmp r2,r3 ; is opponent trapped in the right corner? ble g00_b ; nope ldr r2,[r12,#tick] and r2,r2,#255 cmp r2,#211 ; 1-in-256 chance beq gate07 ; jump away g00_b ; done with corner traps Decide g00_decide cmp r1,#0xff ; no decision? (sanity checking) beq apexit ; go back to sleep adrl r2,g00_jump ; choose destination from decision ldr pc,[r2,r1,lsl #2] b apexit ;; GATE00 ends g00_decide DCB 4 ; allchar ; alldiff DCB 150,000 , 015,001 , 015,002 , 075,003 ALIGN g00_jump DCD gate01 DCD gate02 DCD gate03 DCD gate09 ;; GATE09 ;; Waits a bit, until something happens. gate09 bl AISAFE ldrb r0,[r10,#obRAM_trackid] cmp r0,#0 ; already waiting? beq g09_01 ; skip this ldrb r0,[r10,#obRAM_aoindex] mov r1,#0 ; waiting bl settrack g09_01 ldv r1,(Trig_launch :OR: Trig_jumptwd :OR: Trig_random :OR: Trig_near :OR: Trig_attack) str r1,[r8,#brRAM_triggers] ;; adrl r2,waittbl ldrb r1,[r8,#brRAM_difficulty] ldrb r1,[r2,r1] ;; ldr r2,[r9,#swerve] cmp r2,#10 movgt r2,#10 strcs r2,[r9,#swerve] cmp r2,#-5 movlt r2,#-5 strlt r2,[r9,#swerve] mov r2,r2,lsl #1 sub r1,r1,r2 cmp r1,#2 movlt r1,#2 ;; ldv r2,ifaceram ldr r2,[r2,#opp_prog] cmp r2,#3 movcs r1,r1,lsr #1 ;; ; mov r1,#15 ; random strb r1,[r8,#brRAM_trigrand] mov r1,#50 ; how near strb r1,[r8,#brRAM_trignear] adrl r1,gate04 str r1,[r8,#brRAM_address] b apexit ;; GATE09 ends ;; GATE01 ;; Walks towards the opponent, until near or something happens. gate01 bl AISAFE ldrb r0,[r10,#obRAM_trackid] cmp r0,#1 ; already walking? beq g01_01 ; skip this ldrb r0,[r10,#obRAM_aoindex] mov r1,#1 ; walking forward bl settrack g01_01 ldv r1,(Trig_launch :OR: Trig_jumptwd :OR: Trig_random :OR: Trig_near :OR: Trig_attack) str r1,[r8,#brRAM_triggers] mov r1,#5 ; random strb r1,[r8,#brRAM_trigrand] mov r1,#40 ; how near strb r1,[r8,#brRAM_trignear] adrl r1,gate04 str r1,[r8,#brRAM_address] b apexit ;; GATE01 ends ;; GATE02 ;; Fires a missile weapon at the opponent. gate02 bl AISAFE ;; ldr r2,[r9,#aiflags] tst r2,#aifl_throw ldrne r2,[r12,#tick] tstne r2,#16 bne gate09 ldv r2,gameram ldr r2,[r2,#d2now] cmp r2,#80 ; too close? bls g4b ; try close attack gate02z ;; ldv r2,ifaceram ldr r2,[r2,#opp_prog] cmp r2,#2 bcs gate02y ldr r2,[r12,#tick] and r2,r2,#28 cmp r2,#16 bne gate09 gate02y ;; bl AISAFE ldrb r2,[r11,#chRAM_id] cmp r2,#13 ; character out of range? bcs gate00 ; abort strategy ldr r0,[r12,#tick] tst r0,#8 adreql r1,g02_data1 adrnel r1,g02_data2 ldrb r1,[r1,r2] ; find missile track ldrb r0,[r10,#obRAM_aoindex] bl settrack ldrb r2,[r10,#obRAM_trackid] cmp r2,r1 ; track succeeded? ldv r1,Trig_trans,eq ; yep, wait for it to end ldv r1,Trig_always,ne ; nope, back to gate00 next time str r1,[r8,#brRAM_triggers] adrl r1,gate00 str r1,[r8,#brRAM_address] b apexit ;; GATE02 ends g02_data1 ; special attacks far DCB 180,170,180 DCB 180,189,172 DCB 180,170,180 DCB 180,182,180 DCB 180 g02_data2 ; special attacks far DCB 172,172,183 DCB 172,185,185 DCB 170,172,183 DCB 172,172,183 DCB 172 ALIGN ;; GATE03 ;; Jumps towards the opponent, waiting to decide on a weapon/kick/punch. ;; If we're too close to the opponent, this branches to gate02 instead. gate03 bl AISAFE ;T ldr r2,[r12,#tick] and r2,r2,#7 cmp r2,#5 bne gate02 ; b gate02 ;T ldv r2,gameram ldr r2,[r2,#d2now] cmp r2,#90 blt gate02 ldrb r0,[r10,#obRAM_aoindex] mov r1,#9 ; jumping towards bl settrack ldv r1,(Trig_jumptwd :OR: Trig_near :OR: Trig_trans) str r1,[r8,#brRAM_triggers] mov r1,#30 ; how near strb r1,[r8,#brRAM_trignear] adrl r1,gate05 str r1,[r8,#brRAM_address] b apexit ;; GATE03 ends ;; GATE04 ;; After walking towards the opponent (gate01). gate04 bl AISAFE cmp r1,#Trig_attack beq gate04d ; decide how to get away cmp r1,#Trig_launch beq gate03 ; try to jump over projectile cmp r1,#Trig_jumptwd bne g4a ;; ldr r2,[r9,#aiflags] tst r2,#aifl_throw ldrne r2,[r12,#tick] tstne r2,#32 bne gate09 ;; ldv r2,gameram ldr r2,[r2,#d2now] cmp r2,#65 ; opponent too far away? bgt g4a ; yep ldr r2,[r12,#tick] and r2,r2,#15 cmp r2,#15 ; 1-in-16 chance ; tst r2,#4 ; 1-in-2 chance beq gate04a ; try a vertical jump kick tst r2,#2 bne g4b tst r2,#1 moveq r1,#33 ; roundhouse movne r1,#22 ; FP ldrb r0,[r10,#obRAM_aoindex] b g04_00 g4a cmp r1,#Trig_random bne g4aa ldr r2,[r12,#tick] and r2,r2,#3 cmp r2,#3 beq gate02 ; try a missile weapon bne gate09 ; wait around some g4aa cmp r1,#Trig_near bne gate00 ; unknown trigger g4b ldv r2,gameram ldr r2,[r2,#d2now] cmp r2,#6 ; close enough to throw or grab? bcc gate04b ; yep, try it ldrb r0,[r10,#obRAM_aoindex] ;; ldv r2,ifaceram ldr r2,[r2,#opp_prog] cmp r2,#2 bcs g4c ldr r2,[r12,#tick] tst r2,#64 bne gate09 g4c ;; ldrb r2,[r7,#obRAM_trackid] cmp r2,#4 ; opponent is crouching? cmpne r2,#15 ; crouching blocked? bne g4d ldr r2,[r12,#tick] and r2,r2,#(29 + 64) ; tst r2,#4 cmp r2,#28 moveq r1,#29 ; sweep 'em every time beq g04_00 g4d ldr r2,[r12,#tick] and r2,r2,#15 ; pick something adrl r1,g04_data1 ldrb r1,[r1,r2] cmp r1,#0xff bne g04_00 ; Try a special attack ldv r2,ifaceram ldr r2,[r2,#opp_prog] cmp r2,#2 bcc gate09 ;; ldr r2,[r12,#tick] tst r2,#32 ldrb r2,[r11,#chRAM_id] adreql r1,g04_data2 adrnel r1,g04_data3 ldrb r1,[r1,r2] cmp r1,#0xff moveq r1,#31 ; no close attack g04_00 bl settrack ldrb r2,[r10,#obRAM_trackid] cmp r2,r1 ; did the track succeed? ldv r1,Trig_trans,eq ; yep ldv r1,Trig_always,ne ; nope str r1,[r8,#brRAM_triggers] adrl r1,gate00 str r1,[r8,#brRAM_address] b apexit ;; GATE04 ends g04_data1 ; normal attacks DCB 30,31,30,28,25,22,0xff,0xff DCB 25,31,0xff,28,25,33,0xff,29 g04_data2 ; special attacks near DCB 183,171,170,171,184,183,0xff,171,171,102,102,102,102 g04_data3 ; special attacks near DCB 0xff,183,171,173,187,185,0xff,171,171,102,102,102,102 ALIGN ;; GATE04A ;; Jump straight up and kick/punch the incoming opponent. gate04a bl AISAFE ldrb r0,[r10,#obRAM_aoindex] mov r1,#7 ; vertical jump bl settrack ldv r1,Trig_time str r1,[r8,#brRAM_triggers] ldr r1,[r12,#tick] str r1,[r8,#brRAM_starttick] ldv r1,gameram ldr r1,[r1,#d2now] mov r2,#13 bl udiv add r0,r0,#5 cmp r0,#5 movlt r0,#5 cmp r0,#20 movgt r0,#20 strb r0,[r8,#brRAM_trigtime] adrl r1,gate04c str r1,[r8,#brRAM_address] b apexit ;; GATE04A ends ;; GATE04C ;; Kick/punch the incoming opponent in the air. gate04c bl AISAFE ldrb r0,[r10,#obRAM_aoindex] ldr r2,[r12,#tick] and r2,r2,#3 cmp r2,#3 moveq r1,#36 ; vertical jump punch movne r1,#38 ; vertical jump kick bl settrack ldv r1,Trig_trans str r1,[r8,#brRAM_triggers] adrl r1,gate00 str r1,[r8,#brRAM_address] b apexit ;; GATE04C ends ;; GATE04B ;; Throw or grab the opponent. gate04b bl AISAFE ldrb r0,[r10,#obRAM_aoindex] bl objground ; CC if on ground movcs r1,#33 ; uppercut them in the air bcs g04b1 ldr r2,[r12,#tick] and r2,r2,#3 cmp r2,#3 moveq r1,#74 ; attempting grab movne r1,#110 ; attempting throw (or close BP) g04b1 bl settrack ldv r1,Trig_trans str r1,[r8,#brRAM_triggers] adrl r1,gate00 str r1,[r8,#brRAM_address] b apexit ;; GATE04B ends ;; GATE04D ;; React to an incoming attack. gate04d bl AISAFE ldv r2,gameram ldr r2,[r2,#d2now] cmp r2,#100 ; not near enough to matter? bgt gate00 ldr r2,[r12,#tick] and r2,r2,#3 cmp r2,#0 beq gate02z ; 1-in-4: fire back cmp r2,#1 beq gate09 ; 1-in-4: get hit ;; ldr r2,[r9,#aiflags] tst r2,#aifl_throw ldrne r2,[r12,#tick] tstne r2,#8 bne gate09 ; if throwing, 1-in-2: just get hit ;; ldv r2,ifaceram ldr r2,[r2,#opp_prog] cmp r2,#2 bcc gate09 ;; ldr r2,[r7,#obRAM_trackbase] ldrb r2,[r2,#trROM_hittype] ; find incoming hittype cmp r2,#2 cmpne r2,#3 cmpne r2,#4 cmpne r2,#6 cmpne r2,#9 cmpne r2,#12 cmpne r2,#13 moveq r1,#3 adreql r3,gate06 beq gate04d1 ; crouch under these attacks cmp r2,#7 cmpne r2,#8 cmpne r2,#11 cmpne r2,#14 bne gate02 ; bne gate00 ; unknown attack type ldr r1,[r10,#obRAM_X] ; our position ldr r2,[r7,#obRAM_X] ; opponent cmp r1,r2 ; are we on right? ldv r2,gameram ;; ldr r3,[r2,#d1now] ldr r4,[r2,#bkshift] add r0,r3,r4 ; R0: E1=D1+bkshift ;; ldr r3,[r2,#bkwidth] ldr r4,[r2,#bkshift] sub r3,r3,r4 ; bkwidth-bkshift ldr r4,[r2,#d3now] add r3,r4,r3 ; R3: E3=D3+bkwidth-bkshift ;; movge r0,r3 ; use R3 if we're on the right ; ldrlt r2,[r2,#d1now] ; yep ; ldrge r2,[r2,#d3now] ; nope cmp r0,#70 ; too near the edge? mov r1,#10 ; (normally jump away) movlt r1,#9 ; yep, jump towards adrl r3,gate00 gate04d1 ; react bl AISAFE ldrb r0,[r10,#obRAM_aoindex] bl settrack ldrb r2,[r10,#obRAM_trackid] cmp r2,r1 ; did the track succeed? ldv r1,Trig_trans,eq ; yep ldv r1,Trig_always,ne ; nope str r1,[r8,#brRAM_triggers] str r3,[r8,#brRAM_address] b apexit ;; GATE04D ends ;; GATE05 ;; After jumping towards the opponent (gate03). gate05 bl AISAFE cmp r1,#Trig_trans ; our transition? beq gate00 ; resume normal strategy ; If opponent is jumping towards or is near enough, kick them. ldrb r0,[r10,#obRAM_aoindex] mov r1,#42 ; air kick towards bl settrack ldv r1,Trig_trans ; AI won't run again until we hit the ground str r1,[r8,#brRAM_triggers] adrl r1,gate00 str r1,[r8,#brRAM_address] b apexit ;; GATE05 ends ;; GATE06 ;; Crouching under an attack. gate06 bl AISAFE ldv r1,(Trig_time :OR: Trig_jumptwd) str r1,[r8,#brRAM_triggers] ldr r1,[r12,#tick] str r1,[r8,#brRAM_starttick] mov r1,#28 strb r1,[r8,#brRAM_trigtime] adrl r1,gate00 str r1,[r8,#brRAM_address] b apexit ;; GATE06 ends ;; GATE07 ;; Jumping away from opponent. gate07 bl AISAFE ldrb r0,[r10,#obRAM_aoindex] mov r1,#10 ; jumping away bl settrack ldrb r2,[r10,#obRAM_trackid] cmp r2,r1 ; did the track succeed? ldv r1,Trig_trans,eq ; yep ldv r1,Trig_always,ne ; nope str r1,[r8,#brRAM_triggers] adrl r1,gate00 str r1,[r8,#brRAM_address] b apexit ;; GATE07 ends ;; APHIT (local) ;; Trig_hit always branches here. This strategy waits for the end of ;; the reaction track(s), and then resumes normal strategy. aphit ldrb r1,[r10,#obRAM_trackid] cmp r1,#0 ; back to stance yet? beq gate00 ; yep, resume strategy ldv r1,Trig_trans ; nope, keep waiting str r1,[r8,#brRAM_triggers] adrl r1,aphit ; come back to same code str r1,[r8,#brRAM_address] b apexit ;; APHIT ends ;; APBLOCK (local) ;; Trig_blocked always branches here. This strategy waits for the end ;; of the block track(s), and then resumes normal strategy. apblock ldrb r1,[r10,#obRAM_trackid] mov r2,#0 ; assume we're done cmp r1,#4 ; crouched goes to standing moveq r2,#5 cmp r1,#12 ; standing blocked goes to standing moveq r2,#13 cmp r1,#15 ; crouching blocked goes to crouched moveq r2,#16 cmp r1,#89 ; check for blocked reactions cmpne r1,#94 cmpne r1,#95 cmpne r1,#96 cmpne r1,#97 cmpne r1,#85 ; block-and-attack cmpne r1,#90 beq apb00 ; just wait for end of current track cmp r2,#0 ; are we done here? beq gate00 ; yep ldrb r0,[r10,#obRAM_aoindex] mov r1,r2 bl settrack ; set our new track ldrb r2,[r10,#obRAM_trackid] cmp r2,r1 ; did track succeed? apb00 ldv r1,Trig_trans,eq ; EQ set above or by CMP ldv r1,Trig_always,ne str r1,[r8,#brRAM_triggers] adreql r1,apblock ; come back to same code adrnel r1,gate00 str r1,[r8,#brRAM_address] b apexit ;; APBLOCK ends ;; APMERCY ;; Trig_nomercy always branches here. apmercy bl AISAFE ldr r2,[r12,#tick] and r2,r2,#3 cmp r2,#1 bne apexit ldv r1,Trig_time str r1,[r8,#brRAM_triggers] ldr r1,[r12,#tick] str r1,[r8,#brRAM_starttick] mov r1,#120 strb r1,[r8,#brRAM_trigtime] adrl r1,apmercy2 str r1,[r8,#brRAM_address] b apexit apmercy2 cmp r1,#Trig_time bne apexit ; ldv r2,gameram ; ldr r2,[r2,#gpflag] ; tst r2,#GFmercy ; tsteq r2,#GFmercy2 ; bne apexit ;; pushm "r0-r11,lr" mov r0,#512 ldrb r2,[r10,#obRAM_player] sub r2,r2,#1 ldr r4,[r10,#obRAM_highbase] mov r8,r10 ldv r9,gameram ldv r10,objectram ldv r11,(charram + char1) bl apmercy0 apmercy0 add lr,lr,#0xc entry bl hefinish popm "r0-r11,lr" ;; ; ldrb r0,[r10,#obRAM_aoindex] ; mov r1,#200 ; bl settrack mov r1,#0 str r1,[r8,#brRAM_triggers] adrl r1,apexit str r1,[r8,#brRAM_address] b apexit ;; APMERCY ends AISAFE pushm "r0-r1" ldr r0,[r9,#tickhack] ldr r1,[r12,#tick] cmp r0,r1 popm "r0-r1" qret eq b apexit LTORG aicodesize EQU . - aicodebase aidatabase EQU . waittbl DCB 40,35,30,25,18,12,9,5 ; Base values for block percentage, indexed by difficulty block_base DCB 64,92,114,128,150,180,222,250 ; Block% adjustment rate (+1% per indicated number of ticks), by difficulty block_rate DCB 20,10,10,6,4,3,2,1 ALIGN ; Skew settings for computer players, indexed by difficulty (external) skew_diff DCD 0x0dc00 DCD 0x0e800 DCD 0x0f800 DCD 0x11000 ; medium DCD 0x11c00 DCD 0x12600 DCD 0x13200 DCD 0x13c00 aidatasize EQU . - aidatabase END ; AI.S