Launches

A ship leaves its carrier under order 104, Launch (launch.cpp). order_launch_init (0x00418EB0) prepares the launch, and order_launch (0x004191C0) updates it. Torpedoes and escape pods select their own styles; other ships use the carrier's type and gate. OpenReliant runs all ten styles. launch.zig holds the order, and launch/reliant.zig, launch/torpedo.zig, launch/bay.zig, launch/badanov.zig, launch/escape_pod.zig, launch/rogue_base.zig, launch/stork.zig, launch/yamato.zig and launch/zakov.zig hold the styles.

How a launch is given

A mission's ship record that names a gate (launch_gate, DTE) launches from the first of the mission's ships of the kind it names (launch_from). When the ship is made (mission_ship_create), it gets a Launch order aimed at that ship through the gate, which starts at once (Missions). Every campaign mission launches the player's wing this way, from the Reliant or the Yamato, and from the Badanov too in missions 27 and 271.

A mission's script gives a launch with SetupLaunch (command 0x13). Each ship its first argument names gets a Launch aimed at what the second names. If that is a ship, the gate is the third argument plus one for each ship the command reached before. If it is a flight group or a squad, the launch searches it for a gate (below), using the order's number among those the command gave (Orders).

A launch waits until StartLaunch (command 0x14) starts it: launch_start (0x00418DB0) sets the first byte of the data of the first Launch among the ship's orders. In mission 25 the player's Kamov starts the launch of a torpedo waiting in its tubes with LAUNCH MISSILE (player_launch_missile, 0x00412820, The player's), and a multiplayer game's packets start launches too. WaitForJumpOrLaunch (command 0x3A) holds its thread while any ship it names, one the AI's searches reach, is on a jump, a warp or a launch: Jump In and Jump Out under both their numbers, Warp In, Warp Out, Fixed Gate Jump In and Out, and Launch. The command then runs again with the push of its argument before it.

The order

order_launch_init:

  1. If the order is aimed at a flight group, a squad or a ship without a gate, launch_find_gate (0x00418DF0) searches the ships the target names (order_target_walk, 0x00401CB0, and squad_walk, 0x00401D80, Orders). Each ship it reaches becomes the carrier, with the gate counted from 0 again, and each of its launch points takes one off the order's number, which carries on from ship to ship. The point that takes the number below 0 is the gate. The carrier's slot and the gate are written into the order's target, whose kind stays as it was, and the launch reads the carrier's slot from the target's index from then on.
  2. The ship's type and its carrier's pick the style:
Style Of Routines
0 A ship from the Victorious, the Endeavour, the Mitchell (0x13, 0xA0), the Bremen, the Ramases (0x34, 0x9C), the Pukov, the Kronstadt, the Krasnaya, the Varyag or the Kiev, and from the rogue base's seventh gate on launch_bay_init (0x0041A610), launch_bay_run (0x0041A9C0)
1 A ship from the Yamato launch_yamato_init (0x004192C0), launch_yamato_run (0x00419840)
2 A ship from the Badanov or the Krasny launch_badanov_init (0x00419F60), launch_badanov_run (0x0041A100)
3 A torpedo (0x4A, 0x5C), from anything launch_torpedo_init (0x0041A360), launch_torpedo_run (0x0041A390)
4 An escape pod (0x4D) launch_point_init (0x0041A4B0), launch_pod_run (0x0041A4D0)
5 A ship from the Stork launch_point_init (0x0041A4B0), launch_stork_run (0x0041AD10)
6 A ship from the Reliant launch_reliant_init (0x0041AE20), launch_reliant_run (0x0041B240)
7 The other escape pod (0x90) launch_point_init (0x0041A4B0), launch_pod_other_run (0x0041B690)
8 A ship from the rogue base's first six gates launch_rogue_init (0x0041B770), launch_rogue_run (0x0041B7F0)
9 A ship from the Zakov launch_zakov_init (0x0041B8B0), launch_zakov_run (0x0041B940)

The table of the styles' routines is at 0x004E3C98, 24 bytes a style. From any other carrier, the game stops with the assertion "Error: Trying to launch from %s". 3. The style's first routine places the ship and picks the node it rides: its carrier's root, or the part of a model that holds a launch point. 4. The ship rides the node. The order keeps the ship's position and orientation in the node's frame, the ship passes through its carrier (its first pass-through slot), and it can't be targeted.

Until the style's own steps begin, order_launch destroys a ship whose carrier is gone (a stand-in or exploding) along with it (object_destroyed_net). The exception is an escape pod leaving the Ulysses. Once StartLaunch starts the launch, it waits a random delay of up to 200 ticks, drawn from the ship's own numbers (object_random15), and then the style runs it from step 2. When the player's launch starts, the radio plays a line (radio_launch_line, 0x00456E50, The radio): the flight instructor's in training, and otherwise the bridge officer's of the carrier, the Reliant or the Yamato.

Each frame, mission_frame's pass over the objects places each ship that rides a node (0x00492C14), if its current order is Launch and its carrier isn't exploding. The ship goes back to where the launch placed it on the node, turned as it was. The pass takes the objects in slot order, so a ship riding a node that is updated after it stands where the node was the frame before: the player's ship trails a frame behind the hangar's retainer as it lowers the ship. Improvement: OpenReliant places each riding ship on its node again once every object is framed, so that it keeps with the retainer; --original leaves it a frame behind.

The order's state (LaunchState): the style at +0x00, the tick after which the next step runs at +0x04, the step at +0x08, where the ship stands in the node's frame at +0x0C and how it is turned there at +0x18, whether it rides the node at +0x3C, the node's address at +0x40, the order's number the search counts down at +0x44, and the carrier and the gate the search found at +0x48 and +0x4C.

Launch points

A launch point is a model's attachment of kind 8 (SHP), or of kind 5 (a pod's) where the pod table has no model at the index of the part that holds it. Quirk: the game looks the pod table up by the part's index, not by the attachment's id, which names the pod it mounts. The points are counted part by part in the order of the root's child list, and each part's attachments in order.

launch_attach (0x0041B9F0) places a ship at the launch point that the component of its order's target names. The ship's centre of mass stands at the point and it is turned as the point is, and it rides the part that holds the point. The styles of the torpedoes, the hangar bays, the escape pods, the Stork, the rogue base and the Zakov place their ships this way, and so does the Reliant's style for the player's ship in the hangar.

The Reliant's launch

The Reliant has six tubes, each between a door below and a door above: parts gate and gate + 6 of its root's child list. launch_reliant_init places the ship halfway between the middles of the two doors. Each door's middle is the middle of the bounds of the level its part last drew, moved 400 to the side in the door's frame (0x004DC5A8): to the right for an even gate and to the left for an odd one. The ship is turned as the Reliant will be next, and rides the Reliant's root with its throttle and steering at 0.

For the player's ship, the Reliant becomes the ship the player launched from (0x0057E05C). When that ship explodes, the first Yamato among the objects takes its place (mission_frame, 0x004932D4). The bridge's lines (bridge_line, 0x00453620), PERMISSION TO LAND and REQUEST BACKUP (The radio) and the friendly fire's sending home (Friendly fire) go by it. The hangar (reliant_hang.shp, type 0xD6) is made in the cutaway slot, colliding with nothing, its hull and its two doors reached by no light of the backdrop's but the first ambient (light mask 0x3B), so that their baked colours and their own lights light them. The hangar has two launch points on its retainer, turned half a turn from each other: the ship stands at the first for a gate of odd number, and at the second, the hangar turned half a turn with it, for an even. The hangar is laid over the tube: the ship is placed at the point with the hangar at the origin, the hangar moves by the way from there to the tube, and the ship is placed at the point again, riding the retainer. The camera takes view 0 in the cockpit mode, held there, and the scene shows the launch's cutaway, which leaves the Reliant out (mission_showing, 0x00587CD4, set to 2).

launch_reliant_run runs a step each time the wait the last set has passed:

Step What it does Wait
2 For the player's ship: the engine starts sounding, with a shake of 0.1; a cutaway is picked from the C runtime's rand, one of three, the bay's view taking the camera at once; the tube's upper door shows 100
3 For the player's ship: the hangar's retainer lowers it, playing its deploy track at speed 2, with standard sample 6 250
4 For the player's ship: the retainer rises again, its track backwards. The ship rides its node no more 50
5 The tube's lower door opens (opendoor at speed 2). For the player's ship: the cutaway shows, the hangar's lower door opens (speed 4), with standard sample 5; with the aside cutaway, the camera takes that view and the hangar goes 150
6 The ship drops (motion_downward), at full throttle. For the player's ship, the mission's date is typed out on the display 50
7 For the player's ship, out of the bay's view: the cutaway ends and the hangar goes; with the cutaway from below, the camera takes that view 150
8 The ship drops on 300
9 The ship flies ahead again (motion_forward), steering nothing, at no throttle 200, none for the player's
10 For the player's ship: the date goes, the cockpit mode becomes the one the options' setting picks, the camera leaves a launch view for view 0, no longer held, and the hangar goes. The ship stops passing through the Reliant, its order pops, it can be targeted, and its Launched event is queued

motion_downward (0x004744E0) is the plain flight model along the ship's Y axis, which points below it (Objects): every fighter drops by the first ship type's flight model, the Predator's, so that each leaves its carrier alike.

The cutaways

Three views watch the player's ship go (Camera):

  • The bay (view 0x20), picked at step 2: from within the bay, 750 to the ship's side of its gate, 600 above it and 300 behind, projected wide over the whole screen, looking 54 degrees down from the ship's nose, and tilting further down by 0.0007 radians a tick once the lower door opens. The hangar shows until the launch ends.
  • From below (view 0x21), from step 7: 600 to the right of the ship, 10000 below it and 100 ahead, looking at it as it goes.
  • Aside (view 0x22), from step 5: 1700 to the left of the ship and 6000 below it, looking at it as it goes, the whole scene shown, the Reliant among it.

With the second and the third, the camera stays in the cockpit until its view takes it.

The date

From step 6 to step 10 of the player's launch, the display types out the date of the mission being flown, its language string one of a table of the dates of missions 1 to 28 (0x005023D6): at the foot of the screen, 50 from the left and 30 up, a letter more each time 8 of the game's ticks have passed, with a cursor after it until the whole date shows (Display).

The Yamato's launch

launch_yamato_init (0x004192C0) clears the ship's steering and throttle and places it in child gate + 3 of the carrier's root child list. It stands at the minimum X of that part's last drawn level bounds for gates below 8, maximum X otherwise, centred on Y and Z. Its orientation is a quarter turn about Y, positive for gates below 8 and negative otherwise, multiplied by the bay's world orientation. It rides the carrier's root. The bay mesh shows, and its two doors, children gate * 2 + 31 and gate * 2 + 32, enable portal clipping.

For the player, the carrier becomes player_carrier. A separate hangar (yam_tube.shp, type 0xD4) is created in the cutaway slot, with no collisions. Its first three parts exclude every backdrop light but the first ambient (mask 0x3B). Its front edge is aligned with the bay's maximum X, centred on Y and Z, and turned as the player's ship. The camera is locked in the cockpit, and the scene shows the launch cutaway. The player still rides the carrier's root.

launch_yamato_run (0x00419840) waits strictly past the due tick at every step:

Step What happens Ticks to the next
2 The ship lets go and uses motion_plain. For the player, the four door vents emit steam for 200 ticks, and the shake is 0.1 100
3 For the player, the hangar's two doors play opendoor at speed 4, with sound 0x35 at door 1. The engine starts sounding, and the shake is 0.2 300
4 The throttle becomes 2. The carrier's two doors play opendoor at speed 4. For the player, sound 0x36 plays at the first door, and the shake is 0.3 50
5 For the player, the mission date starts, the hangar goes and the full scene shows. One of three cutaways is picked with the C runtime's rand; the ahead and aside views start at once 150
6 The ship flies on. The beside cutaway starts during the last 50 ticks of this step 300
7 The bay mesh hides and the doors disable portal clipping. Throttle and steering clear, motion_forward resumes, and the launch ends with the ship no longer passing through its carrier. For the player, the date stops and a launch camera returns to the chosen cockpit mode, unlocked

The camera views (camera_set_view, camera_frame):

  • Beside (15): 1500 to the right and 500 ahead of the player, rising from 600 above to 300 below at 0.0013 of that distance per tick. The interpolation is not clamped. It watches the player past step 5's due tick plus 150, or from step 6 onward; before then it looks along the player's orientation.
  • Ahead (16): 40000 ahead and 600 above the player at the switch, with a pitch of 0.33 and a half turn about Y. It stays still.
  • Aside (17): 800 left, 200 below and 5000 ahead of the player at the switch. It stays there and watches the player fly out.

launches_init (0x00418A70) creates six steam emitters. Their template (0x0051D108) has a particle life of 50 ticks plus up to 9, size through 20, 70 and 100, grey colour through 1, 0.75 and 0, and a constant rate of 50 hundredths per tick. The particles leave at speed 10 plus up to 3. The two hull vents point inward along X; the four door vents point inward and back. They stream in array order while the player's step is below 6, including the wait for StartLaunch. The two hull vents burst for 20 to 99 ticks and pause for 20 to 119 ticks, with their first burst due up to 99 ticks after the hangar is created. Every choice uses the original's C runtime random calls. launches_free (0x00418D80) frees them in the original; OpenReliant keeps them as mission-local values in the player's state.

Improvement: the steam's additive colour is scaled to 35% of the original brightness, which reduces saturated white patches and bloom glare. Its size, life, rate, timing and motion stay the same. --launch-steam original restores the original brightness without changing other graphics settings; --original also selects it. --launch-steam soft selects the improvement, which is the default.

Fix: a model missing a bay or door is handled without reading past its child list.

The original also places a camera marker at step 5. None of these three views reads it. OpenReliant uses its hardware-renderer position: 1000 outside the bay's X edge, 1000 above its minimum Y and at minimum Z. The software renderer places it 2000 beyond maximum Z.

The torpedoes

A torpedo's launch (launch_torpedo_init) places it at the launch point of its carrier its gate names, colliding with nothing. At step 2 (launch_torpedo_run) it lets go of its tube, heard (3D sound 0x18), and boosts away along its nose at throttle 2 (motion_plain), steering nothing, from its carrier's velocity, trailing smoke as a torpedo does. After 200 ticks it flies itself again, its order pops, it can be targeted and collides again, and its Launched event is queued; it still passes through the ship that launched it.

Hangar bays

A ship launching from a hangar bay (launch_bay_init, 0x0041A610) is placed at the launch point of its gate, riding the part that holds it. The game keeps the gate's doors in the order's state (+0x50, +0x54). These are up to two parts of the carrier's root's child list, picked by the carrier's type and the gate.

Carrier Gate 0 Gate 1 Gate 2 Gate 3
Victorious, Mitchell (0xA0) 3, 4 5, 6 7, 8 9, 10
Endeavour 13, 14 15, 16 19, 20 17, 18
Bremen 6, 7 4, 5
Pukov, Varyag 15 15 16 16
Krasnaya 3 4 5
Kiev 8 7, as the second door

Other carriers and gates have no doors, among them the Mitchell (0x13), the Ramases, the Kronstadt and the rogue base. From step 2 (launch_bay_run, 0x0041A9C0):

Step What happens Ticks to the next
2 The doors play opendoor forward at speed 4 from where they stand. The sound 0x35 plays at the first door 200
3 The ship lets go and flies out along its nose at throttle 2 (motion_plain), with its carrier's velocity. From the Pukov or the Varyag, it climbs with a pitch input of 0.1 for the last 100 ticks 200, or 400 from the Pukov or the Varyag
4 The doors play opendoor backwards at speed 4, and the sound 0x36 plays at the first door. They stay open while another ship launches from the same carrier through the same doors and is between its delay (step 1) and its own step 4 300
5 The ship flies itself (motion_forward) with its throttle and inputs set to 0. It no longer passes through its carrier, its order pops, it can be targeted again, and its Launched event is posted

OpenReliant looks up the doors from the carrier and the gate each time, rather than keeping them in the order's state.

Fix: when the carrier's model lacks a door part, the game reads past the end of its root's child list. OpenReliant skips that door.

The Badanov and the Krasny

A ship launching from the Badanov or Krasny (launch_badanov_init, 0x00419F60) rides bay part 4 in the carrier's root child list. It waits at one of ten positions: gates 0 to 4 on one side, 5 to 9 on the other. Its position uses the bounds of the part's last drawn level. With size as the bounds' extent and middle as their midpoint, its local coordinates are:

  • X: middle.x plus half of size.y (gates 0 to 4) or minus it (5 to 9),
  • Y: middle.y plus a fifth of size.y,
  • Z: middle.z + (gate - 2) * size.z / 5 for gates 0 to 4, or middle.z + (gate - 7) * size.z / 5 for gates 5 to 9.

It starts with the part's orientation, turns a quarter turn about Y (positive for gates below 5, negative otherwise), then tilts its nose down by 0.37699112 radians about X. The quarter turn here and in the Yamato's style is 0x3FC90FDB, the float nearest to half of pi.

From step 2 (launch_badanov_run, 0x0041A100):

Step What happens Ticks to the next
2 Doors 1 and 2 play opendoor from time zero, once, at speed 4. Sound 0x35 plays at door 1. If that door is already playing a track at nonzero speed, both doors are left unchanged 200 plus 0 to 99, from object_random15
3 The ship releases and uses motion_plain at throttle 2, starting with its carrier's velocity. Its yaw input is (gate remainder 5 - 2.5) * 0.2, negated for gates below 5. The remainder is signed, as in the original 300
4 Throttle and steering clear, and motion_forward resumes. The carrier pass-through entry clears, the order pops, targeting returns and the Launched event is posted

Fix: if the model lacks the bay or its level, OpenReliant leaves the ship in place, riding the carrier's root. The original dereferences the missing part.

The escape pods

Both escape pods use launch_point_init (0x0041A4B0) to wait at the launch point selected by their gate, riding the part that holds it. The Stork uses the same init routine. The two pods have different launch steps, but both keep their carrier pass-through entry afterward.

The first pod (type 0x4D, launch_pod_run, 0x0041A4D0):

Step What happens Ticks to the next
2 The pod releases and uses motion_plain at throttle 2 + rand / RAND_MAX * 0.5. Yaw is (gate - 3) / 12 for gates below 7, or (gate - 7) / 16 otherwise. Sound 0x33 plays at the pod 200
3 Throttle clears and motion_forward resumes. Yaw stays unchanged. The order pops, targeting returns and the Launched event is posted

The other pod (type 0x90, launch_pod_other_run, 0x0041B690):

Step What happens Ticks to the next
2 The sound 0x33 plays at the pod none: step 3 runs at the next update
3 The pod releases and uses motion_plain at throttle 2 200
4 Throttle and yaw clear, and motion_forward resumes. The order pops, targeting returns and the Launched event is posted

The rogue base

A ship using one of the rogue base's first six gates (launch_rogue_init, 0x0041B770) waits 500 behind its launch point along its forward axis, riding the part that holds the point. Later gates use the bay style. At step 2 (launch_rogue_run, 0x0041B7F0), the ship releases with motion_downward at throttle -2. After 100 ticks, throttle and yaw clear, and motion_forward resumes. The carrier pass-through entry clears, the order pops, targeting returns and the Launched event is posted.

The Stork

A ship launching from the Stork is placed at the launch point of its gate (launch_point_init, 0x0041A4B0), riding the part that holds it. The escape pods' styles start with the same routine. From step 2 (launch_stork_run, 0x0041AD10):

Step What happens Ticks to the next
2 The ship lets go and flies out along its nose at throttle 2 (motion_plain) 400
3 Its throttle drops to 0 200, which step 4 doesn't wait for
4 Each part in the ship's root child list plays deploy from its start, once, at speed 4. The ship flies itself (motion_forward) and no longer passes through the Stork. Its order pops, it can be targeted again, and its Launched event is posted

In mission 4, the Storks drop their satellites this way, and each satellite opens out its panels.

The Zakov

A ship launching from the Zakov (launch_zakov_init, 0x0041B8B0) is placed at the launch point its gate names, riding the part that holds it, then moved forward along its nose by how far its bounding box reaches behind its centre (bounds_min). At step 2 (launch_zakov_run, 0x0041B940) it lets go and flies straight out along its nose at throttle 2 (motion_plain). Once more than 100 ticks have passed, it flies itself (motion_forward) with its throttle and yaw input zeroed, its order pops, it can be targeted again, and its Launched event is queued; it still passes through the Zakov.

In OpenReliant

OpenReliant stores a riding node as an object slot and optional part index (create.Slot.riding). The original stores the node's address.

Fix: a launch from an unsupported carrier logs a warning, waits for StartLaunch and then lets the ship go where it stands. A Launch without a target releases the ship immediately. The original reads its carrier through the word before the object table (0x00587CDC): its "Launch Crash Imminent" assertion (0x004191E6) compares a sign-extended index with 0xFFFF and never fires. A missing riding node or Reliant tube door leaves the ship in place.

Improvement: OpenReliant's shadows leave out a mesh that keeps the sun out by its light mask, so the hangar's walls cast none over the ship, which shows lit within them as in the original (Renderer).

The hangar's lights: two red beacons on its hull, which blink (100 of their clock on, 800 off) and cast a light of brightness 2 and range 1000, reaching 2000; and four steady red lights, three on the hull and one on the retainer, which the loader bakes into the hangar's vertex colours and which light nothing else (Rendering). The beacons stand about 2550 from the ship on the retainer, so their light falls short of it, and they stand almost straight ahead of its nose, which the cutaways don't show. Improvement: OpenReliant doubles the beacons' reach, and while they shine, throws a tenth of their flash back off the red walls as an even light on what stands in the hangar, so the ship and its cockpit flash red with them. A light bit of its own (0x40) keeps that light off the hangar's parts. The thrown-back light has no position, so it ends as the ship drops out of the hangar (step 6), before it would reach the wing outside. Only the blinking beacons get the longer reach and the thrown-back light: when the steady lights shine as real lights, they keep their own reach and throw nothing back. --original keeps the beacons' normal reach and throws nothing back.

Not ported: a multiplayer game's starting of launches (#55).

Edit this page on GitHub. The documentation is under CC BY-SA 4.0.