This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

scripting engine

scripting engine
by on (#30258)
i've been writing a scripting engine to describe the player AI in the gridiron football game i've been working on. the premise behind it is that for each play i have 11 pointers to what is internally referred to as "behaviors." so to line up in the I-formation:

Code:
;       W
;
;       T
;       G
;    HFQC
;       G
;       T
;       E
;
;      W
off_form_I:
   .dw _off_QB_PosUnderCenter, _off_RB_PosAsHB, _off_RB_PosAsFB
   .dw _off_WR_PosSplitEndTop, _off_WR_PosFlankerBot, _off_TE_PosAsTEBot
   .dw _off_ol_PosAsLT, _off_ol_PosAsLG, _off_ol_PosAsC, _off_ol_PosAsRG, _off_ol_PosAsRT


copy the above pointers into the "script pointer" field for each of the player structs. now behaviors are further broken down into "commands."

Code:
_off_QB_PosUnderCenter:   .db SET_POS_FROM_BALL, $10, $00
   .db QB_STANCE
_off_QB_PosShotgun:   .db SET_POS_FROM_BALL, $40, $00
   .db QB_STANCE


so those two commands position the the player 16 and 64 pixels (respectively) away from the ball (x-coor) followed by setting their stance. 8 pixels = 1 yard. after the positioning scripts are ran for the formation, the scripts pointers for that particular play are written.


commands are grouped based on what they do. ex $Cx commands handle placement/movement:
Code:
...
SET_POS_FROM_BALL      equ $C0   ; SET_POS_FROM_BALL (signed byte x, signed byte y)
SET_POS_FROM_SCREEN   equ $C1   ; SET_POS_FROM_SCREEN (signed byte x, byte y)
MOVE_ABS   equ $C2   ; MOVE_ABS (byte x, byte y)
MOVE_ABS_FAR   equ $C3   ; MOVE_ABS_FAR (word x, byte y)
MOVE_REL   equ $C4   ; MOVE_REL (signed byte x, singed byte y)
MOVE_REL_FAR   equ $C5   ; MOVE_REL_FAR (signed word x, singed byte y)
...


every $2x is a random jump from the current behavior to another behavior. this allows for a degree of randomness in defensive coverage, offensive blocking, turnovers, etc. $Fx commands simulate control statements like loops or if-then/if-then-else.

the problem i have is speed. so far i have a big switch that first decides the type of command with a series of cmp VAL and bcs THERE. THERE is an entry in a jump table. from there it further compares to find the exact command (like say SET_POS_FROM_BALL and SET_POS_FROM_SCREEN). these two commands share most of the same code except FROM_BALL first converts the x-coor argument into the equivilent FROM_SCREEN x-coor argument. MOVE_ABS and MOVE_REL are similar in that they fill the high byte with the high byte of the camera position and jump to their _FAR equivilents.

this seems to be the most straight forward way to do it, but as more and more gamplay features are added, as well as a second team, i know that a series of compares is not gonna cut it. i'm reluctant to use a pointer table for all 255 possible commands because some commands are exactly the same (like $2x) except for the argument. i also have about 64 commands that are really only 4 commands just with the target player changed. ex:

Code:
HANDOFF_QB   equ $50
HANDOFF_HB   equ $51
HANDOFF_FB   equ $52
HANDOFF_WR1   equ $53
...


on top of the AI processing for 22 players, there is the conversion from absolute positions into screen positions. though much simpler then tokumaru's situation since i only scroll left and right. it just seems like all the compares/branches will really take a toll on speed once i start adding other things that need to be handled.

by on (#32723)
pro tip: we live less than 15 miles away from each other.

let's meet up and you can show me what you have so far and i will help you out.

by on (#32732)
i'm guessing mesa or avondale/litchfield? maybe even...shudder...anthem.

thanks for the offer but i think i'll be alright.
Re: scripting engine
by on (#32734)
never-obsolete wrote:
this seems to be the most straight forward way to do it, but as more and more gamplay features are added, as well as a second team, i know that a series of compares is not gonna cut it. i'm reluctant to use a pointer table for all 255 possible commands because some commands are exactly the same (like $2x) except for the argument.
The trick is to rearrange the commands in such a way that the least or most significant bits can uniquely identify the command function, i.e. mask or shift off a part of the byte before indexing the jump table. This way you'll lose at most 50% on duplicate entries which is just as large as the best-case DEX/BEQ series, i.e. the worst case of 129 commands needs 256x2 = 512 bytes for a jump table and (129-1)x4=512 bytes for a DEX/BEQ sequence.
Of course unless you have really few commands you'd want to use a set of CMP #/BCC instructions to do a binary search, plus I'd wager a few of the branches will be out of range and so forth so the real overhead per branch is probably more like six bytes. So really, go for the jump table.

Quote:
on top of the AI processing for 22 players, there is the conversion from absolute positions into screen positions. though much simpler then tokumaru's situation since i only scroll left and right. it just seems like all the compares/branches will really take a toll on speed once i start adding other things that need to be handled.
Are you sure it's a good idea to lay out the action horizontally rather than vertically? I'm should the last guy on earth to talk about football but won't you be more likely to run out of sprites on a scanline this way?

edit: Oh and if you want to come over and discuss the problem I happen live less than 5000 miles away..

by on (#32743)
There are two perspectives: the horizontal field, as seen in Tecmo Bowl, and the vertical field, as seen in Touchdown Fever. If you take the Touchdown Fever approach, you can make the guys 8 pixels wide like in Smash TV, and then you usually have about 7 of them on one line, so there isn't too much of a problem.

by on (#32745)
never-obsolete wrote:
i'm guessing mesa or avondale/litchfield? maybe even...shudder...anthem.

thanks for the offer but i think i'll be alright.


hint: use interrupts that trigger the timeout delay of other interrupts

in this manner you don't have to do every player synchronously per cycle

if you use asynchronous code in this manner, you can easily OOP the fuck out of it, yes using NESASM or what-not.

load balancing is also key. so have the interrupts refer to a scheduler table for each task.

by this time you will be able to do advanced effects such as interrupt triggered sprite table rotation in order to add exponential projection.

exponential projection is like Grand Theft Auto 2.

no matter how close to the screen the object gets or nor matter how far away it just scales up or down, you can use different memory blocks of pre-rendered sprites that are triggered based off current scanline, having set the current scan-line using the exponential projection matrix.

Exponential mathematics on the NES/FC is explained in the "Square Root" thread on this board.

you simply need to tweak the square root code to handle a different constant power of exponential growth other than a decay of power 2.

create a look-up table of all the conversions using scalar*growth^z.

you simply need to have the scalar be the current size of the object and multiply it by the look-up for the growth constant raised by the z axis position.

in this manner you can even do diagonal view football field effects like a twacked Yoshi's Island / Equinox / Solstice over world view.

plus you have room for all your audio effects, soundtrack, crowd noise, even PCM announcers.

don't forget to use a timed interrupt scheduler to pack the finalized triangulated waveform output.

refer here for more info:
http://nesdev.com/bbs/viewtopic.php?p=32746#32746
Re: scripting engine
by on (#32748)
never-obsolete wrote:
this seems to be the most straight forward way to do it, but as more and more gamplay features are added, as well as a second team, i know that a series of compares is not gonna cut it. i'm reluctant to use a pointer table for all 255 possible commands because some commands are exactly the same (like $2x) except for the argument. i also have about 64 commands that are really only 4 commands just with the target player changed.

Well, you just can't beat the speed of a jump table I think... If only a small number of commands have duplicated behavior, their entries can just point to the same place, and you can just send the number of the command along, so that it can be used to clear any ambiguity. The number of the command will probably be inside an index register anyway bacause you have used it to fetch the address from the table, so you can just choose to use it or not.

If there is a lot of repeated stuff, maybe you could divide your commands in 2 parts. The lower part could be used to fetch an address from the jump table (which wouldn't be as large), and the rest could be decoded as necessary. The bits could have specific meanings or you could use CMP commands, but in either case the arguments would be in the upper bits of the command.

A command can even have more than one byte if you wanted to. I suppose you have a pointer you use to read the comands, so nothing stops you from using it inside the code that handles commands in order to fetch extra command bytes. You may have thought of this already though! =)

by on (#32754)
Quote:
Are you sure it's a good idea to lay out the action horizontally rather than vertically? I'm should the last guy on earth to talk about football but won't you be more likely to run out of sprites on a scanline this way?


i had thought about that, but to be honest, the flickering in Tecmo Super Bowl never really bothered me. and after all, the goal of this was to fix some of the short comings of TSB:

-32 teams
-wider range of stat tracking
-weather
-individual stadiums, well at least endzone/midfield logos
-better support for different formations and plays
-trading
-substitutions for positions other then QBs, RBs, WRs, TEs and returners
-4 players

the large amount of sram that would be needed for this project kinda killed it. it was somewhere in the neighborhood of $5000 bytes (uncompressed) just for season stats alone. i've been told to go play a newer TSB, but after the NES version, they all kinda lost that arcade feel.


Quote:
Well, you just can't beat the speed of a jump table I think... If only a small number of commands have duplicated behavior, their entries can just point to the same place, and you can just send the number of the command along, so that it can be used to clear any ambiguity. The number of the command will probably be inside an index register anyway bacause you have used it to fetch the address from the table, so you can just choose to use it or not.

If there is a lot of repeated stuff, maybe you could divide your commands in 2 parts. The lower part could be used to fetch an address from the jump table (which wouldn't be as large), and the rest could be decoded as necessary. The bits could have specific meanings or you could use CMP commands, but in either case the arguments would be in the upper bits of the command.


that is what i ended up doing; the upper nibble was an index into the jump table and then the lower nibble to get to the exact command.

Quote:
A command can even have more than one byte if you wanted to. I suppose you have a pointer you use to read the comands, so nothing stops you from using it inside the code that handles commands in order to fetch extra command bytes. You may have thought of this already though! =)


sorta...except my commands are all single byte with multiple arguments following:

Code:
$90 = Pass (), LOOP
$91 = Pass ($CP), JUMP_TO_PASS
$92 = Pass ($CP, $CP), JUMP_TO_PASS
$93 = Pass ($CP, $CP, $CP), JUMP_TO_PASS


where C=chance of passing to player and P=player index. command $90 would be for a human controlled QB and the rest are for CPU, just with the number of elligible receivers changed. adding commands of multiple bytes would be easy to include since each player has their own command pointer which is manipulated to read the arguments anways. doesn't the z80 do something like this for its "extended" commands?

if someone ever develops a homebrewer's mapper that includes support for more then 8K of battery backed prg-ram, i'd probably pick this up again. then again there's always the SNES.

by on (#32769)
What bothers me about Temco Super Bowl, is I've never owned nor played a copy of a NES/FC football game, only for Atari and ColecoVision.

btw,

Image

by on (#32777)
LOL