WC2 Cinematics reverse-engineering thread


Rear Admiral
Just a space for collecting any findings.
Anyone looking for more condensed information might want to prefer the thread over here: https://www.wcnews.com/chatzone/threads/wing-commander-toolbox.27769

INCIDENT.S00 SceneGroup/ScriptGroup:
            <OffsetChunk file="INCIDENT.S00-ContainerBlock078-ContainerGroup-SceneGroup-ScriptGroup-OffsetChunk.bin" />
                <SymbolItem Text="doit" />
I recognized that the SymbolItem Text (eg doit) always relates to the contents of the .bin files.
eg: ScriptGroups called doit always have the following bytes in the .bin
01 00 04 00 00 00 23 88 16 22 0E FB FF
Any .bin of ScriptGroups called WARPSHOT contains
01 00 04 00 00 00 23 88 00 89 4E 88 17 22 0E F7 FF
Of course, I haven't verified all 114 containers, so I might be wrong.

We have 114 Container Blocks. Except the blocks mentioned below, they all contain "doit".
Block 14: LoseFinale
Block 15: LoseFinale
Block 16: LoseFinale
Block 28: WHAMMO
Block 31: _TECH_TALKS_
Block 32: _TECH_WALKS_UP_
Block 35: _GUNSHOT_
Block 36: _TECH_FALLS_
Block 37: _DEAD_TECH_
Block 38: WARPSHOT
Block 39: WARPSHOT
Block 40: WARPSHOT
Block 41: WARPSHOT
Block 42: WARPSHOT
Block 43: WARPSHOT
Block 45: _CALL_AND_RAISE_
Block 49: _DISMISS_
Block 50: thefuneral
Block 52: LockedOnJazz
Block 53: _THREE_FOR_JAZZ_
Block 54: Thrak_Walk
Block 55: Kneel
Block 56: Kneel
Block 57: Kneel
Block 58: Terran_Grasp
Block 59: _SPIRIT_DEATH_
Block 60: _SHADOW_DEATH_
Block 61: Olympus
Block 62: WARPSHOT
Block 63: WARPSHOT
Block 65: WARPSHOT
Block 69: hello
Block 73: _EJECT_DEATH_
Block 74: _EJECT_RESCUE_
Block 75: _EST_NIVEN_
Block 76: LockedOnJazz
Block 80: _JAZZ_PULLS_GUN_
Block 87: null
Block 95: _kiss_
Block 96: _kiss_
Block 97: _kiss_
Block 98: runthething
Block 99: runthething
Block 100: runthething
Block 101: runthething
Block 102: runthething
Block 104: _JAZZ_RESCUE_
Block 105: dumdedum
Block 113: runit
Block 114: Kneel


Rear Admiral

#background images, not moving, displayed permanently
24 00 46 25 90 01 47                      23 0E FE FF 
24 00 46 24 00    47                      23 0E FE FF
24 00 46 25 C8 00 47                      23 0E FE FF

#sprites for "ORIGIN Presents...", not moving, shown and hidden
24 3C    46 24 40 47 9E 00 78 01 00 00 03 23 0E FE FF
25 8C 00 46 24 40 47 9E 00 78 01 01 00 03 23 0E FE FF

#sprites for "A Chris Roberts game", not moving, shown and hidden
24 32    46 24 46 47 9E 00 78 01 02 00 03 23 0E FE FF
24 47    46 24 46 47 9E 00 78 01 03 00 03 23 0E FE FF
25 83 00 46 24 46 47 9E 00 78 01 04 00 03 23 0E FE FF
25 DC 00 46 24 49 47 9E 00 78 01 05 00 03 23 0E FE FF

#sprites for "Directed by Stephen Beeman", not moving, shown and hidden
24 5A    46 24 37 47 9E 00 78 01 06 00 03 23 0E FE FF
25 BE 00 46 24 3A 47 9E 00 78 01 07 00 03 23 0E FE FF
24 46    46 24 4F 47 9E 00 78 01 08 00 03 23 0E FE FF
25 A0 00 46 24 4C 47 9E 00 78 01 09 00 03 23 0E FE FF

#WC2 Logo, not moving, shown and hidden
24 14    46 24 03 47 9E 00 78 01 00 00 02 23 0E FE FF
This is the Intro-Sequence again, Visualized in a different way.
We see three different ways of data-definition.
The first three lines are now more or less known. 23 0E FE FF is still not known, but ends every line in the file.

The part
9E 00 78 01 00 00 03
exists for all sprites that appear and disappear.
The fifth part references the actual sprites, where 00 is ORIGIN and 09 is "Beeman"
9E 00 78 01 00 00 03 #ORIGIN
9E 00 78 01 01 00 03 #Presents...
9E 00 78 01 02 00 03 #asprite
Their sequence is zero-based.

The last line - displaying the WC2 Logo - is again different, in that the 13th byte is 02 instead of 03 and the index for the logo should be 0A, but is 00.

So, the three background sprites (Starfields and Tiger's Claw) are not referenced in this part at all.
The WC2 Logo is referenced, but not indexed according to all the other sprites.
Last edited:


Rear Admiral

As most other files, this consists of a series of "Groups", most notably the TextGroup.
But, as you can see in the snippet below, this comes without any specific information on the timings of backgrounds, sprites, text color, music, etc.
In WC1, a cinematic description contains of
  • Background sprite
  • Foreground sprite(s)
  • Delay
  • Text color
  • branching information (eg Pilot still alive, ...)
  • facial expressions
  • Lip animation codes
  • the actual text :)
So, all this information must be available in WC2 somehow, too.
The text is easy, UnnamedCharacter has that already decoded.

              <SymbolItem Text="Observation Deck, TCS Concordia." />
              <SymbolItem Text="ë, please join us.&#xA;Have you heard about Specialist McGuffin?" />
              <SymbolItem Text="Somebody blew him away in the Commo Room." />
              <SymbolItem Text="Do they have any clue&#xA;who might have done it?" />
              <SymbolItem Text="Nothing yet.&#xA;At least, nothing they're letting out..." />
              <SymbolItem Text="Could this be connected to the explosion on the flight deck?" />
              <SymbolItem Text="That's possible.  Strange things have happened lately..." />
              <SymbolItem Text="...and it all started right about&#xA;the time you came aboard, ë." />
              <SymbolItem Text="Are you suggesting something, Major Colson?" />
              <SymbolItem Text="Not at all, Captain.&#xA;Not at all." />
              <SymbolItem Text="Briefing Room, TCS Concordia." />
              <SymbolItem Text="Before we begin,&#xA;I wish to make a statement." />
              <SymbolItem Text="You've all heard about the death&#xA;of Specialist McGuffin." />
              <SymbolItem Text="Some of what you've heard is true.&#xA;McGuffin was murdered by a spy..." />
              <SymbolItem Text="...a traitor, who was broadcasting&#xA;important data to the Kilrathi." />
              <SymbolItem Text="We have reason to believe&#xA;the traitor is a fighter pilot." />
              <SymbolItem Text="That is why the security team searched&#xA;all of the pilots' quarters this morning." />
              <SymbolItem Text="Is that why Stingray isn't on the flight roster today?" />
              <SymbolItem Text="There will be no loose talk&#xA;on this subject, Major." />
              <SymbolItem Text="Stingray will return to duty tomorrow." />
              <SymbolItem Text="With that out of the way,&#xA;let us get down to business." />
              <SymbolItem Text="In a few minutes, the Concordia is jumping into the Ghorah Khar system." />
              <SymbolItem Text="Reconnaissance near Niven indicates&#xA;that the Kilrathi moved through the Niven system..." />
              <SymbolItem Text="...but their real target is Ghorah Khar,&#xA;the rogue Kilrathi colony that joined&#xA;the Confederation ten years ago." />
              <SymbolItem Text="Angel assigns scout wings to clear&#xA;a route from the jump point to Ghorah Khar.&#xA;Your assignment is the last." />
              <SymbolItem Text="å, you're back in your Ferret&#xA;for this one." />
              <SymbolItem Text="You'll be flying a wide scout pattern&#xA;to the Concordia's port side." />
              <SymbolItem Text="Pilots, you are dismissed." />
              <SymbolItem Text=" " />
              <SymbolItem Text="Welcome back, sir." />
              <SymbolItem Text="Looks like it was a tough battle, sir!" />
              <SymbolItem Text="Glad you made it back alive, sir." />
              <SymbolItem Text="Colonel Devereaux's Office, TCS Concordia." />
              <SymbolItem Text="What happened to your flight recorder,&#xA; å?" />
              <SymbolItem Text="My flight recorder?" />
              <SymbolItem Text="The Flight Deck officer said it was damaged,&#xA;that the data disk was destroyed." />
              <SymbolItem Text="I must have taken a hit in combat..." />
              <SymbolItem Text="...but that doesn't matter.&#xA;Angel, there are Kilrathi stealth fighters&#xA;in this system!" />
              <SymbolItem Text="You are joking with me, non?" />
              <SymbolItem Text="Angel, don't you believe me?&#xA;Those stealth fighters were real!" />
              <SymbolItem Text="What am I supposed to do?" />
              <SymbolItem Text="You have no flight recorder to prove&#xA;an encounter with these invisible fighters." />
              <SymbolItem Text="Dammit, I trashed ç of those ships!" />
              <SymbolItem Text="But there is no proof!" />
              <SymbolItem Text="I will enter a record&#xA;that you killed ç Drakhri fighters..." />
              <SymbolItem Text="...but no one will believe&#xA;this story of stealth ships." />
              <SymbolItem Text="Dammit, I killed one of those bastards!" />
              <SymbolItem Text="But there is no proof!" />
              <SymbolItem Text="I will enter a record that&#xA;you killed a Drakhri ship..." />
              <SymbolItem Text="...but no one will believe this story&#xA;of stealth fighters." />
              <SymbolItem Text="It was a tough fight...&#xA;I couldn't nail a single one of them!" />
              <SymbolItem Text="Angel, you have to tell Tolwyn&#xA;about this immediately!" />
              <SymbolItem Text="I am sorry, å,&#xA;but I cannot take this to the Admiral." />
              <SymbolItem Text="You've never believed&#xA;the stealth fighters exist, have you?" />
              <SymbolItem Text="Not now, and not ten years ago,&#xA;when they destroyed the Tiger's Claw at &#xA; K'Tithrak Mang..." />
              <SymbolItem Text="Angel, do you really believe I'm responsible&#xA;for the destruction of the Tiger's Claw?" />
              <SymbolItem Text="Get some rest, å.&#xA;We'll talk about this later." />
              <SymbolItem Text="Dammit, Jeannette!&#xA;Tell me what you think!" />
              <SymbolItem Text="Please, ë.  I must go to the Bridge now.&#xA;We will talk later." />
              <SymbolItem Text="Do you have anything to report, å?" />
              <SymbolItem Text="It was an uneventful patrol, Angel." />
              <SymbolItem Text="You are dismissed, Captain." />
All the other information mentioned above seems to be contained in the SequenceGroup
I'm using parts of SERIES.S00-ContainerBlock006-ContainerGroup-SequenceGroup-ScriptGroup-OffsetChunk.bin as an example, matching the XML above.
I'm skipping the header here for brevity, the following snippet starts at offset 114.
23 92 00 00 9E 0C 85 2E 90 2E 24 00 28 FF
Both appearances of 2E refer to the background music.
Change this to 3F, and you have piano music from the observation deck as background music for the cutscene at Niven (wc2 Origin s2 m2, for those who want to verify)

If byte #12 (0x00) is changed to 0x01, debris passes by outside of the window behind Doomsday just as if we were in space. It only moves very slow, though.
Also very interesting: debris is shown in the closeup and the full view. So, it seems to be a cutscene-wide setting, not just related to a single screen.

In general, I think that "sections" in the binary most of the times start with 0x15 (of course, many others, too) and with 0xFF.
The following part is at offset 192
28 88 06 24 22 28 FF
byte 5 (0x22) is background for bluehair closeups and directly references the filename CLOSEUP.V00-Block034-Image000 (and Image001) - see the screenshot for a different background.

Doomsday's closeup backgrounds are at offset 219:
28 88 06 24 21 28 FF
This time the value is 0x21, referencing to CLOSEUP.V00-Block033-Image000

The other groups' binaries are very small and also seem to contain duplicate information, just like most of the XML snippets
PlaneGroup: 51 out of 51 are similar
01 00 FF FF FF FF
SpriteGroup: 30 out of 51 are similar

SceneGroup: 49 out of 51 are similar
[CODE]01 00 04 00 00 00 23 24 0F 81 26 04 24 00 1B 0F 07 00 88 16 0E 43 00 26 04 24 01 1B 0F 07 00 88 17 0E 36 00 26 04 24 02 1B 0F 07 00 88 18 0E 29 00 26 04 24 03 1B 0F 07 00 88 19 0E 1C 00 26 04 24 04 1B 0F 07 00 88 1A 0E 0F 00 26 04 24 05 1B 0F 07 00 88 1B 0E 02 00 83 00 00 FF 0E A9 FF
All of this took the better part of the weekend. Not too much of a result here, but a first step.
Unnamed's theory about the stack-based interpreter makes total sense. I just can't figure out, which commands there are in total.
The 0x24 and 0x25 are great for orientation, however. If it wouldn't have been for that information, I wouldn't have much of a progress at all...



Rear Admiral
More on the block at offset 187.
15 24 00 28 FF #byte 3 (0x00) changes flightsuit of bluehair in closeup and full-view (screenshot)
28 88 06 24 22 28 FF #byte 5 (0x22) is background for bluehair closeups (mentioned in previous post)
28 88 08 24 22 28 FF
1E 24 0A 24 01 00 28 FF #byte 3 (0x0A) changing this to 0x09 shows Doomsday looking into the other direction in closeups



Veteran Spaceman
There are 186 opcodes in total, but you also need to know the encoding of each, which isn't consistent. A disassembler is pretty much a prerequisite; trying to investigate these files as data is pretty much doomed.
The offsets in my disassemblies seem to be off by a bit compared to your hex-editor screenshots, so take them with a grain of salt. Looking at INCIDENT.S00(78), the code for the tigerscreen sprite begins with $24 $00, meaning PUSH_CONSTB 0, followed by $46, SET_SPRITE_FIELD_2B - setting whatever field that is to zero. Then $25, $90, $01 (PUSH_CONSTW 400) and $47 (SET_SPRITE_FIELD_2D) to set that sprite field to 400.
The $23, $0E, $FE, $FF thing is interesting. It corresponds to two instructions (YIELD and JUMP_RELATIVE -2). The first instruction makes the routine exit. But the JUMP_RELATIVE instruction creates a small infinite loop. I've toyed with various execution models because I can't experiment, and it looks like there is some sort of coroutine system (hence the name YIELD), and the infinite loop may be related to that. I am not sure about the specifics though.

The tigerscreen code in full looks like:

Contents of sprite section of entry point 78:
005b PUSH_CONSTW 400
005f YIELD
0060 JUMP_RELATIVE code_005f
You'll notice that there is no reference to a graphics file here. Some of the other sprites perform that setup in their sprite routine, but not this one. The setup for tigerscreen comes later:
Contents of sequence section of entry point 78:
005c LINK 1
005e YIELD
005f SELECT_SPRITE starfield
0061 MUSIC2 0
0063 SET_SPRITE_DAT 1, 0, 0, field.v00(1)
0068 SELECT_SPRITE starfield2
006a MUSIC2 0
006c SET_SPRITE_DAT 1, 0, 0, field.v00(1)
0071 SELECT_SPRITE tigerscreen
0073 MUSIC2 0
0075 SET_SPRITE_DAT 4, 0, 87, tiger.v00(0)
007f SELECT_PLANE bigscreen
(much more code to perform the scrolling sequence)
The contents of the bigscreen plane is set up in its routine:
Contents of plane section of entry point 78:
0008 Opcode121 0, tigerscreen
000b Opcode121 0, starfield
000e Opcode121 0, starfield2
0011 YIELD
0012 JUMP_RELATIVE code_0011
and the whole thing is started off from the scene section:
Contents of scene section of entry point 78:
0004 YIELD
0005 RUN_SEQUENCE __flicker__
0008 JUMP_RELATIVE code_0004
which is called from SERIES.S00 as I showed in the other thread.


Unknown Enemy
I utterly love this. Someone comes along, registers to the forum, and in his first posts explains how he's been disassembling WC2 cutscenes, something that no one else has ever been able to do. Goodness, I wish we had more new users in general, but new users like this are the best :D. This stuff is wonderful, please keep it up!


2nd Lieutenant
It seems appropriate that we should start compiling a list of all the instructions; so here is the beginning of it.

Dec Hex              Operands           Stack Behaviour   Flow Control  Description
--- ---              --------           ---------------   ------------  -----------

0   00  ADD                             Pop,Pop  Push     Next          Adds two numeric values, returning a new numeric value.
1   01  SUB                             Pop,Pop  Push     Next          Subtracts one value from another, returning a new numeric value.
2   02  MUL                             Pop,Pop  Push     Next          Multiplies two values.
3   03  DIV                             Pop,Pop  Push     Next          Divides two values.
35  23  YIELD                                             Yield/Return
36  24  PUSH_CONSTB  int8               None     Push     Next          Pushes the supplied int8 value onto the stack.
37  25  PUSH_CONSTW  int16              None     Push     Next          Pushes the supplied int16 value onto the stack.
See the attached CSV file for the complete list (rename from txt).



Veteran Spaceman
I'll just paste in my opcode list, as of today. A few things are recent and uncertain. This doesn't cover the opcode formats which are ostensibly the second parameter here, but some of these are actually deferred to the main loop code, so this doesn't tell the full story. One important opcode format detail is the variable-length integer. It is completely silly and inefficient, but for some reason they used it anyway. In some opcodes. It uses repeated bytes of $FF to signify multiples of 255 and then a remainder. Thus, $28 $FF $19 is POP_SET_GLOBAL 280 (255 + 25) and $28 $FF $FF $19 is POP_SET_GLOBAL 535. The list also doesn't say when values are popped off the stack.
    Opcode("ADD", "", false),
   Opcode("SUB", "", false),
   Opcode("IMUL", "", false),
   Opcode("IDIV", "", false),
   Opcode("IMOD", "", false),
   Opcode("LOG_AND", "", false),
   Opcode("LOG_OR", "", false),
   Opcode("LOG_NOT", "", false),
   Opcode("RAND_RANGE", "", false),
   Opcode("SHL", "", false),
   Opcode("SHR", "", false),
   Opcode("BIN_AND", "", false),
   Opcode("BIN_OR", "", false),
   Opcode("BIN_XOR", "", false),
   Opcode("JUMP_RELATIVE", "o", false),
   Opcode("JUMP_IF_FALSE", "o", false),
   Opcode("UNUSED16", "", false),
   Opcode("UNUSED17", "", false),
   Opcode("UNUSED18", "", false),
   Opcode("UNUSED19", "", false),
   Opcode("UNUSED20", "", false),
   Opcode("UNUSED21", "", false),
   Opcode("UNUSED22", "", false),
   Opcode("COMPARE_LT", "", false),
   Opcode("COMPARE_GT", "", false),
   Opcode("COMPARE_LE", "", false),
   Opcode("COMPARE_GE", "", false),
   Opcode("COMPARE_EQ", "", false),
   Opcode("COMPARE_NE", "", false),
   Opcode("IS_ZERO", "", false),
   Opcode("IS_NONZERO", "", false),
   Opcode("SELECT_SPRITE", "e", false),
   Opcode("SELECT_PLANE", "e", false),
   Opcode("SELECT_SEQUENCE", "b", false),
   Opcode("VM_RETVAL_ZERO", "", false),
   Opcode("YIELD", "", false),
   Opcode("PUSH_CONSTB", "B", false),
   Opcode("PUSH_CONSTW", "w", false),
   Opcode("PUSH_GLOBAL", "v", false),
   Opcode("PUSH_ARRAY_INDEX", "w", false),
   Opcode("POP_SET_GLOBAL", "v", false),
   Opcode("POP_SET_ARRAY_INDEX", "w", false),
   Opcode("CYCLE_SPRITE_FWD", "", false),
   Opcode("CYCLE_SPRITE_BKWD", "", false),
   Opcode("PUSH_SPRITE_FIELD_0", "", false),
   Opcode("PUSH_SPRITE_VISIBLE", "", false),
   Opcode("PUSH_SPRITE_FIELD_4", "", false),
   Opcode("PUSH_SPRITE_FIELD_A", "", false),
   Opcode("PUSH_SPRITE_CEL", "", false),
   Opcode("PUSH_SPRITE_MAX_CEL", "", false),
   Opcode("PUSH_SPRITE_FIELD_1C", "", false),
   Opcode("PUSH_SPRITE_FIELD_22", "", false),
     Opcode("PUSH_SPRITE_FIELD_2B", "", false),
   Opcode("PUSH_SPRITE_FIELD_2D", "", false),
   Opcode("PUSH_SPRITE_FIELD_2F", "", false),
   Opcode("PUSH_SPRITE_FIELD_31", "", false),
   Opcode("PUSH_SPRITE_SCALE_FACTOR", "", false),
   Opcode("PUSH_SPRITE_FIELD_35", "", false),
   Opcode("PUSH_SPRITE_FIELD_28", "", false),
   Opcode("PUSH_SPRITE_FIELD_29", "", false),
   Opcode("PUSH_SPRITE_FIELD_2A", "", false),
   Opcode("PUSH_SPRITE_LOCAL", "b", false),
   Opcode("SET_SPRITE_FIELD_0", "", false),
   Opcode("SET_SPRITE_VISIBLE", "", false),
   Opcode("SET_SPRITE_FIELD_4", "", false),
   Opcode("SET_SPRITE_FIELD_A", "", false),
   Opcode("SET_SPRITE_CEL", "", false),
   Opcode("SET_SPRITE_MAX_CEL", "", false),
   Opcode("SET_SPRITE_FIELD_1C", "", false),
   Opcode("SET_SPRITE_FIELD_22", "", false),
   Opcode("SET_SPRITE_FIELD_2B", "", false),
   Opcode("SET_SPRITE_FIELD_2D", "", false),
   Opcode("SET_SPRITE_FIELD_2F", "", false),
   Opcode("SET_SPRITE_FIELD_31", "", false),
   Opcode("SET_SPRITE_SCALE_FACTOR", "", false),
   Opcode("SET_SPRITE_FIELD_35", "", false),
   Opcode("SET_SPRITE_FIELD_28", "", false),
   Opcode("SET_SPRITE_FIELD_29", "", false),
   Opcode("SET_SPRITE_FIELD_2A", "", false),
   Opcode("SET_SPRITE_LOCAL", "b", false),
   Opcode("PUSH_PLANE_FIELD_0", "", false),
   Opcode("PUSH_PLANE_FIELD_1", "", false),
   Opcode("PUSH_PLANE_FIELD_17", "", false),
   Opcode("PUSH_PLANE_TOP", "", false),
   Opcode("PUSH_PLANE_FIELD_1B", "", false),
   Opcode("PUSH_PLANE_FIELD_1D", "", false),
   Opcode("PUSH_PLANE_FIELD_1F", "", false),
   Opcode("PUSH_PLANE_LOCAL", "b", false),
   Opcode("PUSH_PLANE_FIELD_21", "", false),
   Opcode("PUSH_PLANE_FIELD_14", "", false),
   Opcode("PUSH_PLANE_FIELD_15", "", false),
   Opcode("PUSH_PLANE_FIELD_16", "", false),
   Opcode("SET_PLANE_FIELD_0", "", false),
   Opcode("SET_PLANE_FIELD_1", "", false),
   Opcode("SET_PLANE_FIELD_17", "", false),
   Opcode("SET_PLANE_TOP", "", false),
   Opcode("SET_PLANE_FIELD_1B", "", false),
   Opcode("SET_PLANE_FIELD_1D", "", false),
   Opcode("SET_PLANE_FIELD_1F", "", false),
   Opcode("SET_PLANE_LOCAL", "b", false),
   Opcode("SET_PLANE_FIELD_21", "", false),
   Opcode("SET_PLANE_FIELD_14", "", false),
   Opcode("SET_PLANE_FIELD_15", "", false),
   Opcode("SET_PLANE_FIELD_16", "", false),
   Opcode("PUSH_SEQ_FIELD_0", "", false),
   Opcode("SET_SEQ_FIELD_0", "", false),
   Opcode("PUSH_SEQ_FIELD_17", "", false),
   Opcode("DELAY_SEQ", "", false),
   Opcode("RUN_PLANE", "e", false),
   Opcode("RUN_SPRITE", "e", false),
   Opcode("PRINT_VIEWPORT", "b", false),
   Opcode("GET_TEXT", "E", false),
   Opcode("PUSH_SEQ_LOCAL", "b", false),
   Opcode("SET_SEQ_LOCAL", "b", false),
   Opcode("UNUSED114", "", false),
   Opcode("UNUSED115", "", false),
   Opcode("PUSH_SCENE_LOCAL", "", false), // These won't work, because LINK can't create
   Opcode("SET_SCENE_LOCAL", "", false),  // the necessary globals
   Opcode("UNUSED118", "", false),
   Opcode("LINK", "b", false), // Allocate local variables for current routine
   Opcode("SET_SPRITE_DAT", "bbbV", false),
   Opcode("ADD_TO_PLANE", "be", false), // Other subfunctions too, but never used in game
   Opcode("Opcode122", "be", false), // Several subfunctions used in game
   Opcode("LIST_FUNCS", "b", false), // Several subfunctions
   Opcode("Opcode124", "", false),
   Opcode("AWAIT_KEYPRESS", "", false), // I Think???
   Opcode("DELAY", "", false),
   Opcode("SET_GFVIEW_TOP", "B", false),
   Opcode("VIEWPORT_STUFF1", "", false),
   Opcode("SET_STYLE_COLOR", "", false),
   Opcode("SELECT_FONT", "bb", false),
   Opcode("SET_PALETTE", "bbb", false),
   Opcode("INIT_FX", "e", false),
   Opcode("MUSIC3", "e", false),
   Opcode("SET_???", "", false),
   Opcode("SAY_WITH", "Ee", false),
   Opcode("RUN_SEQUENCE", "e", false),
   Opcode("RUN_SCENE", "e", false),
   Opcode("MOUTH_STUFF", "e", false), // Only called from one location
   Opcode("VIEWPORT_STUFF2", "", false),
   Opcode("FADE_OUT", "", false),
   Opcode("SET_ANIM_DELAY", "", false),
   Opcode("SET_UNUSED1", "", false),
   Opcode("SET_ANIM_DELAY2", "", false), // Different units, also sets something else
   Opcode("MUSIC4", "e", false),
   Opcode("ALLOC_FX", "b", false),
   Opcode("MUSIC1", "w", false),
   Opcode("DISCARD_FX", "b", false),
   Opcode("CLEAR_VIEWPORT", "b", false),
   Opcode("SET_UNUSED2", "", false),
   Opcode("DISCARD_FONT", "", false),
   Opcode("DISCARD_MUSIC", "b", false),
   Opcode("UNINIT_FX", "", false),
   Opcode("DISCARD_SPRITE", "V", false),
   Opcode("DISCARD_SPRITES", "", false),
   Opcode("PUSH_???", "", false),
   Opcode("PUSH_???", "", false),
   Opcode("SKIP_TO_NEXT?", "", false), // Detects ESC presses. Scripts use this periodically  and jump to the end if true
   Opcode("MUSIC2", "b", false),
   Opcode("SET_FX_STUFF", "", false),
   Opcode("UNUSED160", "", false),
   Opcode("MEM_REPORT", "", false), // On screen and in log file
   Opcode("PRELOAD_SPEECH", "e", false),
   Opcode("IS_DEBUG_ON", "", false),
   Opcode("MANIP_SKIP", "", false), // Various subfunctions based on TOS
   Opcode("WIPE", "b", false),
   Opcode("NAV_MAP", "eE", false),
   Opcode("SHOW_COMM", "", false),
   Opcode("COPY_SPRITE", "ee", false),
   Opcode("IS_WINGMAN_DAMAGED?", "", false),
   Opcode("SET_PLANE_FIELD_25", "", false),
   Opcode("PUSH_PLANE_FIELD_25", "", false),
   Opcode("COPY_SPRITE2", "ee", false),
   Opcode("FREE_SPRITE", "e", false),
   Opcode("SET_PRINT_RECT", "b", false),
   Opcode("SCROLL_CREDITS", "b", false),
   Opcode("SPEAK_AUDIO", "", false),
   Opcode("LOG_CONST", "w", false),
   Opcode("SET_VOLUME", "b", false),
   Opcode("PRINT_VIEWPORT2", "", false),
   Opcode("MIDI_STUFF", "", false),
   Opcode("ALLOC_COMM", "", false),
   Opcode("FREE_COMM", "", false),
   Opcode("GRAPHICS_???", "", false),
   Opcode("POP", "", false),
   Opcode("LOG_POPPED", "E", false),


Rear Admiral
Opcode("SKIP_TO_NEXT?", "", false), // Detects ESC presses. Scripts use this periodically and jump to the end if true
Ha, finally I know why skipping cinematics sometimes takes multiple keypresses until it finally does skip.


2nd Lieutenant
Scene Transitions

There appears to be three kinds of transition wipes: line, diagonal, and iris. They are conveniently all located in the same file: CAMPAIGN.S00; Block 000; Sequence Script; Entry 020 (doshow). And they are selected using OpCode 165; "SELECT_WIPE wipeIndex". As for the wipe index, the following values are possible:
  • 0: wipe down; horizontal line wipe (from top to bottom)
  • 1: wipe left; vertical line wipe (from right to left)
  • 2: wipe up; horizontal line wipe (from bottom to top)
  • 3: wipe right; vertical line wipe (from left to right)
  • 4: wipe diagonal (from top left to bottom right)
  • 5: wipe diagonal (from top right to bottom left)
  • 6: wipe diagonal (from bottom right to top left)
  • 7: wipe diagonal (from bottom left to top right)
  • 8: iris from center to edges
  • 9: iris from edges to center
.entry ;  20

  _0011:  c038         388                      ; get global[<uintvar:index>]
  _0014:  c036           1                      ; push constant, byte
  _0016:  c027                                  ; ==
  _0017:  c015       _0022                      ; jz
  _001a:  c038         390                      ; get global[<uintvar:index>]
  _001d:  c165           8                      ; select wipe <int8:index>
  _001f:  c014       _00bb                      ; jmp
  _0022:  c038         388                      ; get global[<uintvar:index>]
  _0025:  c036           2                      ; push constant, byte
  _0027:  c027                                  ; ==
  _0028:  c015       _0033                      ; jz
  _002b:  c038         390                      ; get global[<uintvar:index>]
  _002e:  c165           9                      ; select wipe <int8:index>
  _0030:  c014       _00bb                      ; jmp
And some examples:





Veteran Spaceman
Scene Transitions

There appears to be three kinds of transition wipes: line, diagonal, and iris. They are conveniently all located in the same file: CAMPAIGN.S00; Block 000; Sequence Script; Entry 020 (doshow). And they are selected using OpCode 165; "SELECT_WIPE wipeIndex". As for the wipe index, the following values are possible:
Problem: In practice, SERIES does not call the the WIPE opcode directly, but uses it indirectly through the standard library. SERIES doesn't even call doshow directly, but rather some other higher-level things (INCIDENT does call doshow directly). When doing it this way, you set global 388 (and possibly global 390, to set the duration it seems, but this is never used in the game), but the wipe indices you put there do not match those passed to the WIPE opcode! This same issue is true of other things like setting faces and background scenes: You can't directly match the high-level variable settings (in SERIES/INCIDENT) to the graphics files used and their indices without reading the code.

So, using your list as a basis, but permuting it according to the doshow routine, we get:
  • 0: no transition animation
  • 1: iris from center to edges
  • 2: iris from edges to center
  • 3: wipe right; vertical line wipe (from left to right)
  • 4: wipe left; vertical line wipe (from right to left)
  • 5: wipe down; horizontal line wipe (from top to bottom)
  • 6: wipe up; horizontal line wipe (from bottom to top)
  • 7: wipe diagonal (from bottom left to top right)
  • 8: wipe diagonal (from top left to bottom right)
  • 9: wipe diagonal (from top right to bottom left)
  • 10: wipe diagonal (from bottom right to top left)
Notice the top one... according to your list there is no way to specify "no wipe" when calling WIPE directly while there is when setting global 388. But this whole thing is kind of a WTF.