Yes, changing the action scripts directly is the way to go. Many of them muck with the metasprite (stored in $300,x, usually) based on direction, animation counters, etc. The metasprite is an index into the metasprite table (pointer table at $3845c, data following). Unfortunately the metasprite alone is not enough to understand the sprites, since it also depends on the active pattern tables and palettes (as stored in $7e0..$7e7 and $7f0..$7f5).
Background tiles are a similar story, in that the game treats them as metatiles. There's actually a lot that goes on here.
The first 64K of PRG is map screens. Each screen is a 15x16 grid of 16x16 pixel metatiles (a few are only 13x16, if they only ever show up on horizontal or single-screen maps). This accounts for a full 240x256 pixels. The game does some funny math so that objects' y-coordinates can never have an $f in the 16s place - i.e. the y-coordinate counts up 000, ..., 0ef, 100, ..., skipping 0f0...0ff. Each metatile is 16x16, but the NES PPU's nametable uses 8x8 tiles, so this means that a metatile consists of 2x2 PPU tiles. This is actually pretty convenient because the PPU also groups tiles into 2x2 blocks that share the same attributes (i.e. palette) so by mirroring this grouping, the game can avoid needing to worry about incompatible palettes between metatiles - each metatile now has a single dedicated paletted that's no longer shared with neighbors. The MapData graphics subtable has two bytes that tell the game how to parse the metatiles: the tileset (a multiple of 4 between $80 and $ac) and the tileEffects (a number from $b3 to $bd). For the most part there's a one-to-one mapping between tileset and tileEffects, except that tileset $a8 shares tileEffects $b5 with tileset $88, rather than incrementing to $bd like you would expect (you can see a full breakdown of this at https://crystalisrandomizer.com/stable/view/tileset
). The way this ends up working is as follows (and recall that the pattern tables are separate here, so this just maps to tile pattern IDs from 0..ff, and the mapping from there to specific CHR data is determined by the selected pattern table, and the colors by the selected palettes).
Suppose you have metatile T, tileset S, and tileEffects E
* the metatile components tables run from $10000 to $12fff in $400-byte (1K) blocks, and gives four pattern IDs for each metatile. The first $100 bytes is the top-left, followed by top-right, bottom-left, and bottom-right.
* the metatile attribute table follows immediately after from $13000 to $132ff. Each tileset gets a $40-byte block, with four metatiles' worth of palettes packed into each byte. So each metatile gets two bits in this map (low-bits first - tile 0 of tileset $80 maps to ($13000) & $03, tile 1 to ($13000) & $0c, tile 2 to ($13000) & $30, etc), specifying which of the four selected background palettes to use for the tile.
* the tile effects table runs from $13300 to $13e00. Each tileEffects gets a $100-byte block, which maps each metatile to an 8-bit value. The bits have the following meanings:
* $01 = pit (can fall into)
* $02 = cannot walk over (but can maybe fly)
* $04 = impassible (i.e. solid mountain)
* $08 = has alternative (i.e. destroyable walls)
* $10 = foreground tile (e.g. bridge you can go under)
* $20 = slope (player will fall down)
* $40 = slow (thick grass)
* $80 = pain (marsh and spikes)
This is similar to but not exactly the same as the bits in $460,x for objects, which has to do with terrain susceptibility - I believe the bits $26 are at least the same in terms of what the object will walk on. Note also that this is the *only* table indexed by E - all the others are indexed by S.
* the tile alternatives table runs from $13e00 to $13f7f. Each tileset gets a $20-byte block mapping the first $20 metatiles to alternates that are swapped in if the mapdata flag is set (i.e. if the mapdata indicates that a screen has a flag, then that flag is used to determine whether to treat the metatile as the original (clear) or the alternate metatile (set). This is used for clearing destroyable walls/bridges, locked doors (prison, stxy, swan), exploding caves (mezame shrine, sealed cave), as well as the angry sea and the door between the statues of sun and moon. When used, the alternates affect both the graphics and the tile effects, obviously.