[4.5.6] AI Directional Based Proxy

Craigery

Active member
I have written an AI script that will change the monsters Action Step when the player gets close enough to it. It will detect a different area depending on which direction the monster is facing.

To continue you will need to enable the userScreenBytes variables as see in this helpful tutorial by chronicleroflegends. This allows you to change how far the monsters will look in front of them on a screen by screen basis, so if you want different visibility depending on your screen its easy to change. The only draw back is you need to set userScreenByte values for every screen.

How to Setup This Code:
Copy and paste this code to a new ASM file called AI_DirectionalProxy.asm and save in your BASE_4_5\Game\AI_Scripts folder.
;;userScreenByte1 = Facing Proxy Distance
;;userScreenByte2 = Peripheral Proxy Distance

;;Load Player X and Y
TXA
STA temp
PHA
LDX player1_object

;;Load and Save X with offset to center of player, and Y
LDA Object_x_hi,x
ADC #$08 ;;this moves the X to the player center
STA tempA
LDA Object_y_hi,x
STA tempB

LDA Object_screen,x
STA temp1

;;Load Monster direction
PLA
TAX
LDX temp

;LDA temp1
;CMP Object_screen,x
;BEQ +doneProxy

;;Check if Left Right
LDA Object_x_hi,x
ADC #$08 ;;adds 8 pixels so it is centered on 16 pixel wide monster.
STA tempC
LDA Object_y_hi,x
STA tempD

LDA Object_direction,x
CMP #%00000110
BEQ +isLeftUp
CMP #%00000010
BEQ +isRightDown

;;check Up Down
LDA Object_y_hi,x
STA tempC
LDA Object_x_hi,x
ADC #$08 ;;adds 8 pixels so it is centered on 16 pixel wide monster.
STA tempD

LDA Object_direction,x
CMP #%00000100
BEQ +isLeftUp
CMP #%00000000
BEQ +isRightDown
JMP +doneProxy

+isLeftUp
LDA tempC
CMP tempA
BCC +doneProxy
LDA tempC
SBC userScreenByte1
CMP tempA
BCC +checkPeripheralU
JMP +doneProxy

+isRightDown
LDA tempC
CMP tempA
BCS +doneProxy
ADC userScreenByte1
CMP tempA
BCS +checkPeripheralU
JMP +doneProxy

+checkPeripheralU
LDA tempD
SBC userScreenByte2
CMP tempB
BCC +checkPeripheralD
JMP +doneProxy

+checkPeripheralD
LDA tempD
ADC userScreenByte2
CMP tempB
BCS +doAttack
JMP +doneProxy

+doAttack
ChangeActionStep temp, #$05

+doneProxy

In NESMaker go to Settings -> Script Settings -> Assign our new AI_DirectionProxy.ASM to an empty spot under AI Behaviors.
Be sure to change the Project Label to something like DirectionProxy.
Under your monsters Object Details set the Action of your action step to the new Directional Proxy and set your EndAction/EndAnimation to Repeat. The code only looks for the player once so we need to to repeat.

Here is How it Works:
First we determine the monster's facing direction. Then we compare the player X position against the monster's X position, against minus (left) or plus (right) the userScreenByte1's value (this is how many pixels in the monster's facing directions we should look for the player), then we will check the monster's peripheral vision being userScreenByte2's value it will check on each side. If the player is within that XY area it will change to Action Step 5.

Our Variables:
userScreenByte1
is the distance, in pixels, the monster will search in the direction they are facing.
userScreenByte2 is the peripheral vision of the monster. The userScreenByte2 value is checked on each side of the monsters peripheral vision, so if userScreenByte2 is set to search 32 pixels, it will search 32 going in each direction for a total of 64 pixels.

ProxyImage01.png

Things to know:
  • The center point for this script is the monsters feet. My monster is 16 pixels wide, so I have moved the center point 8 pixels to the right to actually be center. Your monster may be different.
  • You don't have to use the userScreenBytes variables, you could have a set value in there if that works for you. In my game I needed to to be flexible like if I wanted monsters to see farther in later levels to make it more difficult.
  • If you leave userScreenByte1 or 2 at 0 it won't detect anything.
  • When the player is seen it changes to Action Step 5. You may want to make Action Step 5 a MoveToPlayer script or ShootAtPlayer.

This code was very difficult for me learn and write, but I finally got my head wrapped around it and am getting more confident in my ASM skills with NESMaker. I hope some other people get some use out of this!
 
Last edited:

Bucket Mouse

Active member
Very good! Stealth games are now possible in NESMaker and that's exciting!

Does it still work when the monster is moving? It'd be weird for the monster to be completely blind whenever it's walking.
If not, maybe the Move scripts could be modified to jump to this one as a subroutine.
 

Craigery

Active member
It will run the code check once per Repeat. So if Action Step 0 is MoveLeft with a short timer and then Action Step 1 runs the monster it should continue to move left. Because we set the EndAction to Repeat it should update wherever the monster is positioned.
 

SciNEStist

Well-known member
a cool idea for an addition to this would be checking line of sight.

a good method for that would be for the enemy to fire a super fast invisible projectile at the player after it goes into the collision area. if it hits the player, that triggers the monsters detection instead of doing damage. that way you know theres not a wall between player and monster.
 

PasseGaming

Active member
a cool idea for an addition to this would be checking line of sight.

a good method for that would be for the enemy to fire a super fast invisible projectile at the player after it goes into the collision area. if it hits the player, that triggers the monsters detection instead of doing damage. that way you know theres not a wall between player and monster.
Is that something that NESmaker can do? ... or better question is that something that is reasonably done?
 

SciNEStist

Well-known member
well within the realm of possible, yeah. you would just need to restrict it so it doesnt fire off too many at once, modify the collision script so it branches off regular monster projectiles. one issue would be that i cant think of a way to trace it back to the monster that fired it off, so you would just have to script it to alert all monsters or somehting like that.

it also wouldnt be the most realistic, cause even just the corner of a tile would block the detection.
 

Craigery

Active member
a cool idea for an addition to this would be checking line of sight.

a good method for that would be for the enemy to fire a super fast invisible projectile at the player after it goes into the collision area. if it hits the player, that triggers the monsters detection instead of doing damage. that way you know theres not a wall between player and monster.
interesting idea. You would have to put a tall object when facing Left/Right, and a wide object when facing Up/Down, otherwise if the monster just shot a 16x16 object that wouldn't be a very big range.

I set up the userScreenBytes so that you could fake that a bit by having shorter viewing distance on some screens where there may be more visual obstructions or whatever for the monster. Though I suppose you could have a very narrow FOV for the monster and also do a check for any Solid tiles.
 

Bucket Mouse

Active member
I have improved upon your script! Let's say there's a large object in the area that the player would think of hiding behind. If the monster moves close to the object, it should logically block their vision -- but the script doesn't allow for that.

But what if the player was standing on a special tile that the proxy script was programmed to ignore? You would just make them the tiles behind that object, and the monster would not see the player as long as they're standing on those tiles.

You can make this happen by adding these lines below Line 23 (LDX Temp):

Code:
CheckCollisionPoint tempA, tempB, #$0C, #$01 ;; checks if player is on special tile
    BNE +notBehindObject
    JMP +doneProxy
    +notBehindObject

I've made Tile 12 (0C in hex) the special tile here, but you can make it anything.
 

Jonny

Well-known member
It's really nice to see people expanding on ideas and sharing. The code and programming concepts here will come in useful for a weapon 'thing' I'm working on. :devilish: *evil laugh*
 

Bucket Mouse

Active member
Made another adjustment. That macro that looks at the tile needs to know if the tile's collision point is an even or an odd number, because it can't do both at once. I picked odd numbers (01), but I've been informed there's a subroutine that allows the macro to know ahead of time: getPointColTable, from locationFinders.asm.

Unfortunately the subroutine stores the number in tempA, which this script is already using for something else. So I had to find another temp variable for your own use of tempA. I changed every instance of tempA to temp2.

Code:
;;userScreenByte1 = Facing Proxy Distance
;;userScreenByte2 = Peripheral Proxy Distance

;;Load Player X and Y
TXA
STA temp
PHA
LDX player1_object

;;Load and Save X with offset to center of player, and Y
LDA Object_x_hi,x
ADC #$08 ;;this moves the X to the player center
STA temp2
LDA Object_y_hi,x
STA tempB

LDA Object_screen,x
STA temp1

;;Load Monster direction
PLA
TAX
LDX temp

JSR getPointColTable
CheckCollisionPoint temp2, tempB, #$0C, tempA ;; checks if player is on special tile
    BNE +notBehindObject
    JMP +doneProxy
    +notBehindObject

;LDA temp1
;CMP Object_screen,x
;BEQ +doneProxy

;;Check if Left Right
LDA Object_x_hi,x
ADC #$08 ;;adds 8 pixels so it is centered on 16 pixel wide monster. Change if necessary
STA tempC
LDA Object_y_hi,x
STA tempD

LDA Object_direction,x
CMP #%00000110
BEQ +isLeftUp
CMP #%00000010
BEQ +isRightDown

;;check Up Down
LDA Object_y_hi,x
STA tempC
LDA Object_x_hi,x
ADC #$08 ;;adds 8 pixels so it is centered on 16 pixel wide monster.
STA tempD

LDA Object_direction,x
CMP #%00000100
BEQ +isLeftUp
CMP #%00000000
BEQ +isRightDown
JMP +doneProxy

+isLeftUp
LDA tempC
CMP temp2
BCC +doneProxy
LDA tempC
SBC userScreenByte1 ;; User Screen Bytes set the visual distance for the screen
CMP temp2
BCC +checkPeripheralU
JMP +doneProxy

+isRightDown
LDA tempC
CMP temp2
BCS +doneProxy
ADC userScreenByte1
CMP temp2
BCS +checkPeripheralU
JMP +doneProxy

+checkPeripheralU
LDA tempD
SBC userScreenByte2
CMP tempB
BCC +checkPeripheralD
JMP +doneProxy

+checkPeripheralD
LDA tempD
ADC userScreenByte2
CMP tempB
BCS +doAttack
JMP +doneProxy

+doAttack
ChangeActionStep temp, #$05 ;; Action Step 5 must be attack action

+doneProxy
 
Last edited:

GokiburiGeemu

New member
Hi!
I tried the script and it doesnt seem to be advancing to state 5. I setted an init value of the to vyte variables to 32 pixela but no luck. Do I have to define something related to the collisions so that the ai detects the player?
 

SHLIM

Active member
hi guys, like the user above im having issues getting this to work. Ive created the user varuiables, given byte 1 & 2 a distance of 128 and added all snippets of code. Getting no errors but my player doesnt ever seem to advance to state 5.
Im using the adventure mod on 4.5.9
 
Top Bottom