Skip to content

Instantly share code, notes, and snippets.

@ExpHP
Last active August 17, 2023 05:22
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ExpHP/88bdef8f28f46fe4af6ab2e013b75896 to your computer and use it in GitHub Desktop.
Save ExpHP/88bdef8f28f46fe4af6ab2e013b75896 to your computer and use it in GitHub Desktop.
anm parser info

Announcement regarding this gist

Much of the information here is old and outdated!

Visit my new site at exphp.github.io/thpages.

Information gleaned from anm_parser

Limitations

I mostly just looked at this one function, so there's a lot I still don't know. Nonetheless, this single function does write to a huge portion of the ANM VM data structure, and almost no field on it is written by more than one command, meaning that a good 75% or so of the structure can be immediately associated with particular commands just by looking at this function.

I still haven't looked at 000 commands, 300-302 (textures).

This may contain many blatant factual errors. I was taking notes at 3AM and this shit is hard, yo.

All data is from TH16.

Commands

411 rotateTime2d(t, mode, theta)

So this is obviously just an alias for rotateTime(t, mode, 0, 0, theta), right?

...wrong. It writes to a completely separate set of data, time, and mode variables. It enables the same flag as rotateTime and doesn't appear to enable any others, which suggests to me that both rotations are applied together. (i.e. it appears you can probably use both rotateTime and rotateTime2d and have the effects stack).

If I had to guess without any further information, this is probably to allow things to be rotated onscreen after an arbitrary 3d rotation has already been applied.

420 moveBezier

This implicitly sets the move mode to 8.

The three input positions are saved to zAnmVM.pos_bezier_1, zAnmVM.pos_bezier_2, and zAnmVM.pos_final. (the last of which is the same variable used by 407 moveTime). Calling 407 moveTime clears pos_bezier_1 and pos_bezier_2.

There is no additional data exclusive to moveBezier beyond those two 3-vectors, and it sets no flags.

Unknown433(t, mode, x, y)

I still haven't fully figured out the set of fields that this accesses.

The implementation of this begins similarly to moveTime, clearing bezier_point_1 and bezier_point_2. However, it has fewer arguments, and appears to write to a completely separate set of data, time, and mode fields.

Unknown434(x, y), Unknown435(t, mode, x, y)

434 is some kind of scale-related thing. 435 appears to be its time counterpart.

Each one writes its own set of variables that aren't used by any other opcode. Both enable the same bitflag that is enabled by all of the scale commands.

415, 416, 425, 426, Unknown427, Unknown428

415, 416, 425, and 426 are all explained on the chinese wiki but the translated descriptions are confusing to me so I haven't named them. 415 is rotation-related, 416 is scale-related. 425 and 426 are... I dunno, uv-related?

All six write to their own disjoint sets of data, time, and mode variables.

427 and 428 appear to be ___Time versions of 425 and 426.

Strangely, the first four commands all share a bit flag (using any of those commands enables the bit). The remaining two do not automatically enable this bit for some reason, even though most other ___Time commands usually do.

Colors and gradients (403, 404, 405, 406, 408, 409, 413, 414, Unknown423)

A tentative set of names I might give these:

403 alpha1(a)
404 rgb1(r, g, b)
405 alpha2(a)
406 rgb2(r, g, b)
408 rgb2Time(t, mode, r, g, b)
409 alpha2Time(t, mode a)
413 rgb1Time(t, mode, r, g, b)
414 alpha1Time(t, mode, a)
423 colorTimeEnabled(twoBit)

The numeral 1 refers to the "main" color, and the numeral 2 refers to the secondary color used by gradients. (Initially I thought 408 and 409 were the time functions for the first color, but this assignment makes more sense)

Calling either 413 rgb1Time or 414 alpha1Time has the side-effect of causing a 2-bit bitfield to be set to 0b01. This appears to be related to a statement on the chinese wiki which suggests that 414 alpha1Time prevents the second gradient color from changing. Unknown423 can be used to set this 2-bit bitfield manually. Presumably, calling Unknown423(0b11) AFTER calling 413 and 414 will allow both gradient colors to change with time.

(Edit: Dammit, no, things still don't make sense! Most time functions do at some point read the data for their non-time counterpart, and 408 and 409 read the data for color 1, while 413 and 414 read the data for color 2. I'm so confused!)

417 (alpha- and time-related)

I have no idea what the wiki is saying about this or how it's supposed to differ from alpha1Time and alpha2Time, but it modifies the same fields as alpha2Time.

438 setOrigin(twoBit)

EDIT 2020-08-01: Chinese wiki is mistaken; this does not toggle visibility.

On a VM that is a root VM (no parent), this determines what coordinates in the D3D surface correspond to (0,0).

  • ins_438(0): No correction (use the the upper left corner)
  • ins_438(1): A setting appropriate for objects always drawn at 640x480 (most game elements). It's the location of ECL's (0,0) during the first few stages of rendering (see TH14 D3D investigation).
  • ins_438(2): A setting appropriate for objects drawn at full resolution (text and UI elements). It's the location of ECL's (0,0) in rendering stages that occur after the 384x448 game region is upscaled to the window resolution.

Calling setLayer also changes this flag; it will typically automatically set it to the origin appropriate for that layer's rendering stage.

430

The description on the wiki makes absolutely no sense.

Judging from the implementation, this is the ___Time version of 429 repeat.

422

This takes some vec3 field—let's call it AnmVM.backup_pos—and copies it into AnmVM.pos. It then sets AnmVM.backup_pos to zero.

backup_pos also appears to be used for some purpose in the code that runs for all opcodes after exiting the switch statement, but I don't know anything more than that.

Unknown418() and Unknown419()

Unknown418 calls a pretty important-looking function that uses many, many of the fields stored on the VM like position, texture size, scale, as well as many of the unknown quantities and bitflags. (this function is definitely on my todo list of useful things to look at!)

The function takes an output pointer to a float[3][4], and it writes what appears to be (based on values seen in memory) four vertices of a quadrilateral in 3d space. After calling that function, Unknown418 copies the xy parts of these vertices into a float[2][4] array stored on the VM. (after dividing the components by 640 and 480)

...sadly, this instruction does not appear to be used in TH16.

Unknown419 seems to be a continuous form of Unknown418. Basically, Unknown419 sets a bitflag that causes ins_3 to do something similar to Unknown418 on every frame. This instruction also does not seem to be used in TH16!

There is one remaining callsite for the important-looking function. This callsite IS used many times per frame... however, I tried hacking it to always write the quadrilateral [(100, 200), (100, 100), (200, 200), (200, 100)], and nothing out of the ordinary happened. Terribly disappointing!

setLayer

When the layer number falls within particular ranges, some bit flags may be changed. In particular, it always simulates a call to setVisible, with different arguments based on the layer number. It sometimes also simulates a call to Unknown313(0b001). See the bitflags listing at the end for more details.

Creating children

500, 501, 502, 503

Using any of these creates a child VM.

There is a single bitflag that is always copied from the parent to child. (see bitflags section at end). The rest are copied from a VM stored in the AnmLoaded type. (Curiously, at first glance, the code for these instructions also appears to be explicitly enabling one particular flag; but that happens before everything gets copied, so it is overwritten)

Aside from the layer, the one bitflag, and a pointer to a "root" VM, nothing else is copied from parent to child.

The specific instruction you use decides where the object is placed:

  • 500 inserts at the back of the world list. This is a list of ANMs that exist in the game world. When you pause the game, these ANMs stop running. They are only allowed to use layers 0 to 35 (if you try to use others, the game will change the layer on you).
  • 501 inserts at the back of the ui list. This is a list of ANMs that exist outside of the game world. These ANMs alre always running. They are only allowed to use layers 36 to 42 (if you try to use others, the game will change the layer on you).
  • 502 inserts at the front of the world list. This probably lets you achieve a different z-ordering.
  • 503 inserts at the front of the ui list.

Unless stated otherwise, all other child creation opcodes insert the child in the same place as 500 does.

505(int, float, float)

This is identical to 500, but then reads two more float args and stores them in the "alternate position" variable for bitflag 530:10 (described later).

504(int) and 506(int, float, float)

Unlike 500-503, this enable the aforementioned flag after copying everything, so it actually gets enabled this time.

This one copies far more state from the parent, including rotation, position, and the alternate position. It still only copies one bitflag.

506 simply combines the effects of 504 and 505.

509 "updateVars"

When a child VM calls this, it will receive the current values of all script variables that are stored per-VM. There's 16 of them in total. I believe they are specifically the following: (this likely contains errors)

  • 10000, 10001, 10002, 10003
  • 10004f, 10005f, 10006f, 10007f
  • 10008f, 10009
  • 10010f, 10011f, 10012f. These are "random" but there's also some data associated with them (presumably scale factors?) which can be modified by scripts by attempting to store these variables. (well, at least, the first two can be modified. 10012f has data, but for some reason you can't modify it!)
  • 10033f, 10034f, 10035f

Unknown508(int)

This one also creates an object. This time, the argument is the index of some special, "global" hardcoded ANM instruction, and looks it up in some static table. A function pointer from this table is immediately called after the child is created, and then a bunch of data from the table is copied over.

This table is used for other purposes besides 508 (in fact I'm not sure if opcode 508 is ever even used!). It is used by Aya's and Cirno's bombs (both use entry 0x3), and ECL 334 calls this with the user-supplied arg. The table has four entries total in TH16.

Unknown507(bool)

This instruction sets a bitflag. Calling Unknown507(1) on a child VM appears to decouple it from its parent's scale and rotation, so that scaling the parent does not scale the child.

Or at least, this is what it looks like from the small amount of code that I've witnessed using this bitflag so far.

6 wait

This one subtracts an amount from the current time, simulating a wait.

Useful for waiting a random amount of time, though ironically it's often used with fixed values.

3, 5 label and -1

When instruction 3 is encountered, it begins by scanning ahead and doing the following:

  • If an instruction with opcode -1 is encountered, it resumes normal execution at the instruction after the -1.
  • If it encounters label(0), then one of two things occurs:
    • If it previously encountered a label(-1), it will immediately resume normal execution at the instruction after the label(-1).
    • If it did not previously encounter a label(-1), it will enter an important-looking part of the code that appears to perform time evolution.

I have tentatively named 3 run, though I know someone else has named it stop! Whatever it is, its (large) implementation will contain crucial details to understanding what many of the bitfields and variables actually do.

Unknown commands that just set bitfields

The following commands have been verified to have no immediate effect inside anm_parser besides setting bitfields.

(Update Aug 2020: Though I still don't intend to update this document much, I've labeled in bold the ones whose purpose I have since identified)

  • Unknown305(bool)
  • Unknown306(bool)
  • Unknown307(bool)
  • Unknown311(bool) (resample mode when scale != 1)
  • Unknown312(twoBit, twoBit) (scroll mode ("texture address mode" in D3D-speak))
  • Unknown313(threeBit) (now partially understood as a resolution scaling mode)
  • Unknown314(bool) (moves around as parent rotates)
  • Unknown315(bool)
  • Unknown316() (enables a bit)
  • Unknown317() (disables that bit)
  • Unknown419(bool)
  • Unknown431(bool)
  • Unknown432(bool)
  • Unknown423(twoBit) (described above)
  • Unknown507(bool) (described above)

Bitflags

There is space for 64 bitflags stored on the VM, contained in positions 0x530-0x538 in TH16. The game almost always uses 32-bit dword operations to manipulate these, so I group them into two blocks of 32, which I will call 530 and 534.

Here is a mapping of bitflags to the commands that modify them. This may contain errors. In the following, 530:2 names the 1 << 2 bit, and 530:(4-6) names a semi-inclusive range of bits.

If you see (unknown) it means I did not spot any usage of it in this function. It may be unused, it may be used by other functions, or it might even be used by this function (and I missed it).

  • 530:0:
    • Set by 310
    • Enabled by 300 texSelect
  • 530:1: Enabled by 316, disabled by 317
  • 530:2: Enabled by 401 rotate, 410 rotateTime, 414 rotateTime2d
  • 530:3: Enabled by 402 scale, 412 scaleTime, 434, 435
  • 530:4: Enabled by 429 repeat, 430
  • 530:(5-9): Set by 303 blendMode
  • 530:9: (unknown)
  • 530:10: "Alternate start pos flag"
    • Switches between two possible vec3s for the starting position (the value corresponding to 400 move). Any command that attempts to read or write this vector will look at this flag to decide which vec3 to access.
  • 530:11: Set by 308 mirrorX
  • 530:12: Set by 309 mirrorY
  • 530:13:
    • Set by 305
    • Usage at 0x40b200.
  • 530:14: (unknown)
  • 530:15: Set by 306
  • 530:16: (unknown)
    • Usage at 0x467489. When disabled, this code computes matrix_410 based on matrix_3d0 and some scale members. When enabled, this code does not overwrite matrix_410, and additionally uses position members somehow.
    • Usage at 0x466f1b.
  • 530:(17-19):
    • Set to 0b01 by 413 rgb1Time and 414 alpha1Time
    • Can be manually set to any value using 423
    • Usage at 0x467952
  • 530:(19-21): (unknown)
  • 530:(21-25): Set by 412 anchor
  • 530:(25-30):
    • Set by 302 renderMode
    • Virtually every 600-series opcode also changes this (in addition to their other effects)
  • 530:(30-32): Set by 312 (second arg)
    • Usage at 0x4651f1. It is compared to a field on the giant ANM struct. If it does not match, sub_465a80 is called, and then the aforementioned field is set to the value stored in the bitflags.
  • 534:(0-2): Set by 312 (first arg)
  • 534:(2-5):
    • Set by 437 rotationSystem
    • Usage at 0x46757f
  • 534:(5-7): (unknown)
    • At 0x46e783, these anms will only be rendered if these flags are 0b00.
    • When an ANM file is about to be unloaded, a function is called to set this to 0b01 on every ANM VM using that ANM file.
    • Usage at 0x4684ce
    • Usage at 0x46e3b9
    • Set to 0b10 at 0x46e6df
  • 534:7:
    • Set by 424 autoRotate
    • Usage at 0x41d414.
    • Usage at 0x445972.
  • 534:8: Set by 431
  • 534:9: Set by 432
  • 534:10:
    • Set by 307
    • Instructions 500-503 and 505 appear to forcibly enable this flag on the child at first glance; but then it gets copied over from the vm on the AnmLoaded.
    • 504, 506, and 508 do forcibly enable this flag on the child.
    • Enabled at 0x405fc. (oops, I think I a digit)
    • Enabled at 0x41901c.
  • 534:11: Set by 311
  • 534:12: (unknown)
    • Usage at 0x46da53. Changes some arguments to a text-drawing function.
    • Enabled at 0x429df2.
    • Enabled at 0x42a570.
  • 534:13:
    • Set by 419
    • Each frame, when ins_3 sees this flag set, it does things that appear to be very similar to instruction 418.
  • 534:(14-16): (unknown)
    • Cleared at 0x46f9e0
    • Cleared at 0x419071
    • Set to 0b10 at 0x45f072 and 0x45f19e.
    • Set to 0b01 at 0x45f2a3.
    • Compared to 0b10 at 0x45f289.
    • Compared to 0b01 at 0x45fa91.
  • 534:16:
    • Set by 507
    • When this flag is 0, some functions contain additional code that take into account the parent (or root?) VM's scale and rotation.
  • 534:17: (unknown)
  • 534:(18-20):
    • Set by 438 setOrigin.
    • 304 setLayer(layer) also affects this:
      • if 3 <= layer <= 19, it calls setOrigin(0b01)
      • if 20 <= layer <= 23, it calls setOrigin(0b10)
      • otherwise, it calls setOrigin(0b00)
    • Set to 0b00 at 0x4071da
    • Usage at 0x406bb1
    • Usage at 0x467f20 (comparison to 0b00)
  • 534:(20-23):
    • Set by 313.
    • 304 setLayer(layer) also affects this:
      • if 20 <= layer <= 31, or if 36 <= layer <= 42, it calls Unknown313(0b001).
      • otherwise, these flags are not modified.
    • Set to 0b001 at 0x406deb.
    • Usage at 0x4687aa. Causes some things to optionally be scaled to the window resolution or half of that.
    • Usage at 0x406a88. Similar effect.
  • 534:23: Set by 314
  • 534:24: Enabled by 415, 416, 425, 426
  • 534:25:
    • Set by 315
    • This flag is copied from parent to child by instructions 500-506.
  • 534:26:
    • Enabled at 0x46f84b.
    • Enabled at 0x46fc9f.
    • Cleared at 0x46f9c2.
    • Usage at 0x46f1d9.
    • Usage at 0x46f22e.
  • 534:(27-32): (unknown)

Info from anm update funcs

After discovering how update funcs work, I decided to look at the AnmManager's update funcs. The AnmManager in TH16 (address 0x4c0f48) has two on_tick functions and 39 (!!!) on_draw functions. Looking at this provided some intimate insight into the workings of layers, and greatly enhanced my understanding of the anmCreate variants.

tl;dr

  • Some of the anmCreate variants are for game elements, and others are for UI.
  • Layers < 36 are reserved for the game world. Layers 36 to 42 are reserved for UI.
  • Different layers may use different coordinate systems.
  • Some specific layer numbers are very special, like layer 34. (I don't know why it's special, but it clearly is)

on_tick functions

There are two on_tick functions, one for each of the two lists that the different anmCreate could have inserted them into.

on_tick__ui

  • Has priority 0x09 (runs pretty early).
  • Runs every drawn frame of the TH16 window.
  • Iterates over the list stored at AnmManager+0xdc.
    • Runs the ANM parser for one frame on each VM.
    • As it is doing so, it rebuilds the per-layer lists for layers 36 <= layer <= 42. (more info about these in on_draw)
    • The game will forcibly change the layer into this range if it is not already:
      • Layers 24 <= layer <= 30 are mapped to 36 <= layer <= 42 by adding 12.
      • Layers 0 <= layer < 24 and invalid layers layer > 42 are all mapped to layer 38.

on_tick__world

  • Has priority 0x21 (runs pretty late; after all game logic).
  • Runs on every frame of the game world. (so it does not run while paused!)
  • Iterates over the list stored at AnmManager+0xe4.
    • Runs the ANM parser for one frame on each VM.
    • As it is doing so, it rebuilds the per-layer lists for layers 0 <= layer < 36. (more info about these in on_draw)
    • If the layer is >= 36, the game will forcibly change it into this range:
      • Layers 36 <= layer <= 42 are mapped to 24 <= layer <= 30 by subtracting 12.
      • Invalid layers (layer > 42) are left as is and will likely crash the program.

Additional notes:

In both of these functions, if bitfield 534:(5-7) is equal to 0 on any given VM, it will skip this VM (it will not run the VM, and it will not add it to a layer list, so it will not be rendered).

Furthermore, if either the ANM parser returns nonzero, or bitfield 534:(5-7) is equal to 0b01 prior to running the VM, then it does... something. Presumably deletes the ANM and all of its children. (I'm just not 100% sure how it gets to the part where it removes the node from e.g. the AnmManager+0xdc list)

The on_draw functions

Each of the 39 on_draw function is responsible for drawing a single layer. Some of them also change the D3D transformation matrices and other stuff, causing different groups of layers to have different properties.

Notice: There are 43 layers in TH16, but only 39 on_draw functions on the AnmManager! The remaining layers are drawn by other things: two are drawn by the Stage object, and one is drawn by a Direct3D-related object stored directly in memory beginning at 0x4c10d0.

The per_layer lists

(NOTE: These details were completely changed in TH17! I'm not 100% sure what happened yet; he removed the dummy AnmVms and I think the linked lists became doubly-linked? I suspect that this change is responsible for the 4 bytes added to sizeof(AnmVm) in TH17)

The per-layer lists are singly-linked lists whose next pointers are stored at AnmVm+0x5a8. As described earlier, they are reconstructed every frame in the on_tick methods, and then iterated over by the on_draw methods.

The lists are stored on the AnmManager in the form of a AnmVm[43] at AnmManager+0x1c6fc30. Yes, that's an array of AnmVms, not an array of pointers! They are dummy VMs serving as list heads that are solely used for their +5a8 fields. (the byte sequence 01 c6 fc 30 never even appears in the binary data because the 5a8 offset is included into literally all interactions with this array).

The layers and various on_draw func priorities

To try and give an idea of what the various layers mean, I've compiled a list of all of the layers and the priority of their on_draw functions (lower runs earlier). There are more things that have on_draw functions (probably occupying the holes in the priority numbers) but I don't really care to look further into it.

AsciiManager has a currently poorly-understood field which is an integer 0, 1, or 2 that classifies ASCII strings into groups that each rendered all at once. One could say these are like "layers" for ASCII. Those groups are included as well.

Most of the non-AnmManager on_draw functions are not responsible for drawing ANMs, because there's already functions to draw each layer. (most of the on_draw functions just draw ascii stuff). There's some exceptions to this, like with Player::on_draw that actually draws an ANM. Not sure what's up with that.

List of on_draw functions:

  • (Unknown) at priority 0x02
  • Stage at priority 0x03: Renders layers 32 and 33
  • AsciiManager at priority 0x04: Draws ASCII group 0
  • AnmManager at priority 0x05: Renders layer 0
  • Stage at priority 0x06
  • AnmManager at priority 0x07: Renders layer 1
  • AnmManager at priority 0x09: Renders layer 2
  • AnmManager at priority 0x0a: Changes coordinate system. Renders layer 3
  • AnmManager at priority 0x0b: Renders layer 4
  • Spellcard at priority 0x0c
  • AnmManager at priority 0x0d: Renders layer 5
  • (Unknown) at priority 0x0f: Draws layer 34
  • AnmManager at priority 0x10: Renders layer 6
  • AnmManager at priority 0x12: Renders layer 7
  • ItemManager at priority 0x13
  • AnmManager at priority 0x14: Renders layer 8
  • AnmManager at priority 0x15: Renders layer 9
  • AnmManager at priority 0x16: Renders layer 10
  • EnemyManager at priority 0x17 (no-op in TH16)
  • AnmManager at priority 0x18: Renders layer 11
  • AnmManager at priority 0x1b: Renders layer 12
  • AnmManager at priority 0x1c: Renders layer 13
  • Player at priority 0x1d
  • AnmManager at priority 0x1f: Renders layer 14
  • AnmManager at priority 0x20: Renders layer 15
  • ItemManager at priority 0x21 (default placement?)
  • AnmManager at priority 0x22: Renders layer 16
  • LaserManager at priority 0x23
  • AnmManager at priority 0x24: Renders layer 17
  • BulletManager at priority 0x25
  • AnmManager at priority 0x27: Renders layer 18
  • ShotType for player at priority 0x28
  • ShotType for subseason at priority 0x29
  • AnmManager at priority 0x2a: Renders layer 19
  • AnmManager at priority 0x2d: Changes coordinate system. Renders layer 20
  • AnmManager at priority 0x2e: Renders layer 21
  • Gui at priority 0x30
  • Gui again at priority 0x33 (default placement? no-op in TH16)
  • AnmManager at priority 0x34: Renders layer 22
  • AsciiManager at priority 0x35: Changes coordinate system. Draws ASCII group 2
  • AnmManager at priority 0x36: Renders layer 23
  • AnmManager at priority 0x37: Changes coordinate system. Renders UI layer 36
  • AnmManager at priority 0x3a: Changes coordinate system. Renders layer 24
  • AnmManager at priority 0x3b: Renders layer 25
  • AnmManager at priority 0x3c: Renders UI layer 37
  • AnmManager at priority 0x3d: Renders layer 26
  • AnmManager at priority 0x3e: Renders layer 27
  • AnmManager at priority 0x3f: Renders UI layer 38
  • AnmManager at priority 0x40: Changes coordinate system. Renders layer 28
  • AnmManager at priority 0x41: Changes coordinate system. Renders UI layer 39
  • (Unknown) at priority 0x44
  • MainMenu at priority 0x45
  • HelpManual at priority 0x48
  • (Unknown) at priority 0x4a. (I think this is the pause menu or game over screen)
  • AnmManager at priority 0x4d: Renders layer 29
  • AnmManager at priority 0x4e: Renders UI layer 40
  • AnmManager at priority 0x4f: Renders layer 30
  • AnmManager at priority 0x50: Renders UI layer 41
  • AsciiManager at priority 0x51: Changes coordinate system. Draws ASCII group 1
  • AnmManager at priority 0x52: Renders layer 31
  • AnmManager at priority 0x53: Renders UI layer 42

Where's layer 33? Noooobody knooooooooows 👻

TH14 D3D investigation

MotherInf (called Supervisor in my ReData) contains a ton of high-level rendering stuff, including some of the most critically important on_draw funcs, such as those that call SetRenderTarget.

In modern games, it has 4 AnmVms, and 3 pointers to IDirect3DSurface9s. The three surfaces are:

  • Two temporary surfaces used for rendering the "game region" of the screen. The game switches back and between them at various steps for reasons, as described soon.
    • Surface 0 is the texture for text.anm entry2.
    • Surface 1 is the texture for text.anm entry3.
  • The third is the D3D Device BackBuffer. The majority of GUI stuff and "full-resolution" graphical elements are drawn directly to this (though a couple are drawn a bit earlier so they get rotated by seija).

The four AnmVms use scripts from text.anm, and they are used to paste stuff rendered from one of the two temporary surfaces to the other. Here's what happens: (DDC)

  • SetRenderTarget(Surface0)
    • Draws the Stage and Layers 0-5, 30-31 at 640x480
  • SetRenderTarget(Surface1)
    • use vm 0 (text.anm scripts 59-61) to copy from Surface 0
    • Draw Layers 6-11 (enemies mostly) at 640x480
  • SetRenderTarget(Surface0)
    • use vm 1 (text.anm scripts 65-67) to copy from Surface 1
    • Draw Layers 12-19, bullets, items, player at 640x480
  • SetRenderTarget(Surface1)
    • use vm 2 (text.anm scripts 62-64) to copy from Surface 0, upscaling to full resolution
    • Draw Layers 20-23, Ascii Group 1 at full resolution (this is probably spell name/hist and in-world ascii)
  • SetRenderTarget(BackBuffer)
    • use vm 3 (text.anm scripts 68-70) to copy from Surface 1, with seija's effects
    • Draw Layers 24-29, 34-35, Ascii Groups 0 & 2 (this is probably gui, pause menu, main menu etc.)

Spreadsheet if you really want to drown in details https://docs.google.com/spreadsheets/d/1ovAPn9Ib7wxFBuTtT63oXgjfzbEq60ikSu7BxhZz-vM/edit#gid=0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment