Skip to content

Latest commit

 

History

History
984 lines (835 loc) · 80.9 KB

orders.md

File metadata and controls

984 lines (835 loc) · 80.9 KB

Orders

Improvement: global mod scripts can register custom orders (#615). The runtime resolves their qualified names and allocates internal IDs after the original catalogue. aigeneric.infoOf combines original and registered metadata for stack priorities, player eligibility, retaliation, avoidance and disruption. The original catalogue remains generated and immutable. Custom callbacks use the scripting bridge in hooks.Scripts, since the engine doesn't link Luau.

What each object is doing: flying in formation, escorting, docking, exploding, or following the player's controls. An object keeps a stack of orders, the current one on top, which the AI, the mission scripts and the player's controls push and pop, and object_orders runs the current one.

aigeneric.zig holds the stack and runs the orders, ai.zig the steering they turn by, aifuncs.zig the orders that fly a ship, a capital ship's lurch and orders 44 and 45, aieject.zig and tractor.zig those of the ejection, launch.zig the launches and jump.zig the jumps, aiioncan.zig the ion cannon, ai/formations.zig the formations, and ai/orders.zig lists every order with its flags, priorities and routines; make order-tables transcribes that table from the executable. The names below are those make ghidra-annotate gives the Ghidra project, which names each order's routines order_ and the order's name, with _init and _exit for those two.

Ported so far: the stack (order_push, order_pop, orders_clear, orders_pop_all), the queue (order_queue), what runs them (object_orders, orders_update, order_retaliate), and the steering (ai_steer, ai_roll_upright) with its avoidance. The orders says which orders are ported, Player Control being the player's controls. Not ported: the multiplayer orders, which still hold their place on the stack, pushing one still popping and starting what it should, and the orders sent to and from the other machines in a network game (#55).

OpenReliant keeps each object's stack and order state in its slot rather than allocating them with its first order, and hands a fatal "Cannot set ai" back to its caller as an error. Fix: where the game stops with it, OpenReliant logs it in the game's words and the order is not taken (aigeneric.give).

The order table

order_groups (0x4E06E0) points at the records of each hundred order numbers: order n is record n % 100 of group n / 100. Group 0 holds orders 0 to 45, group 1 orders 100 to 122, and group 2 one empty record, order 200. Each record is an OrderRecord:

Offset Size Field
0x00 4 init: runs before the order's first update; null for none
0x04 4 update: runs each time object_orders runs the order
0x08 4 exit: runs when the order is popped or replaced after it has started; null for none
0x0C 4 Flags
0x10 4 Name: the developers' name, which fatal errors show
0x14 4 Priority
Flag Name Meaning
0x1 players The order may be given to a player's ship. A player's ship refuses the other orders numbered below 100.
0x2 to 0x10 Unknown. Set on some orders; nothing in the payload tests them.
0x20 one_shot The order runs its update once and pops itself, and the order below carries on without starting again. Its init never runs.
0x40 retaliate While it runs, the ship can turn on its attacker (see Retaliation).
0x80 avoidance While it runs, avoidance_scan (0x00492190) lists the objects the ship could hit, unless the object has no_avoidance, which SetShipAvoidance sets to disable "Avoidance code": up to ten objects with listed components whose collision spheres, widened by a constant, overlap its own, at GameObject offsets 0x6B4 (the count) and 0x6B8, and up to ten others for which 0x00401980, which projects the two objects' motion, answers yes for a time of 50 updates and a margin of 2000 units, at 0x6E0 and 0x6E4.
0x400 send_flight While it runs, a multiplayer game sends the ship's steering inputs, throttle, rates and velocity.

Most orders have priority 0. Warp In, Warp Out, Land, Jump In, Jump Out, the fixed gate jumps, Launch, Dock and Friendly Fire have 1; Eject Player 97, Eject and Eject Spin 98, and Explode 99.

The stack

An object's stack, orders (0x684), holds up to 20 OrderEntry records, the current order first, with order_count (0x680) saying how many. It is allocated with the object's first order, together with 0x90 bytes at order_state (0x68C) where the current order keeps whatever it needs between updates.

Offset Size Field
0x00 2 The order's number
0x02 2 Target kind: 0 a ship, 1 a flight group, 2 a squad, as in the mission's object table
0x04 2 Target: the ship's slot, or the flight group's or squad's index; -1 for none
0x06 2 Target component, or -1 for the whole ship
0x08 2 A running count from 0x5185A8 while the byte at 0x5185B1 is set, as SetAI and SetupLaunch number the orders they give, otherwise zero
0x0A 16 The order's own data, zero when the order is pushed

player_controls keeps the mouse's stick position in the first two words of its data.

Fix: the game takes a target's slot on trust, and a mission's SetAI can aim an order at any ship number, past the last of the 400 slots too. OpenReliant reads a target's slot only where it is one of the slots (aigeneric.Target.slotIn), so an order aimed past them aims at nothing.

order_push (0x0040CC10) takes a slot, an order and its target, and:

  1. fails if the ship refuses the order (order_refused, 0x0040CA00): the ship is a player's, its slot being below player_slots (0x58832C), which is 1 in a single-player game and the player count or 8 in multiplayer, and the order is numbered below 100 without players;
  2. succeeds at once if the current order is the same order with the same target;
  3. fails unless the current order gives way (order_give_way, 0x0040CA50);
  4. removes any equal order, with the same target, from deeper in the stack;
  5. fails if the stack holds 20 orders;
  6. pushes the order with its data zeroed. Unless the order is one-shot, it marks the order as starting (order_starting, 0x688), sets the word at 0x620 to -1 and zeroes the state.

An object that is exploding, whose pilot has ejected, or with object flag 0x10000000 takes no order. Otherwise the current order gives way at once when there is none, when it has yet to start, or when the new order is one-shot. A started order gives way to Explode, and to any order when its own priority is zero or the new order's is higher, running its exit as it does. Pushing any other order on it is a fatal error, "Cannot set ai %s on ship %s: Still %s".

order_pop (0x0040CE70) runs the current order's exit if it has started and removes it. Unless the popped order was one-shot, the order below starts again: it is marked as starting and the state is zeroed. orders_pop_all (0x0040CF80) pops every order, and orders_clear (0x0040CF50) drops them all at once when the current order gives way to clearing, so only that order's exit runs.

A mod's script can stop a pop through order_pop's hook (Scripting), which leaves the order on the stack. OpenReliant's orders_pop_all then stops at the first pop that leaves the stack no shorter. The orders a mod registers that OpenReliant removes, as the mod's scripts close or the object goes, pop without the hook.

The script command SetAI pushes an order aimed at the ship, flight group or squad it names, on each ship it applies to, and ClearAI clears the orders of each ship that is not a player's.

Targets that name several ships

order_target_walk (0x00401CB0) hands each ship an order's target names to a routine, until the routine says to stop: the ship itself, as the target names it; each ship of a flight group, whole; and each ship of a squad (squad_walk, 0x00401D80): its members in turn from its first, while they are its own, a ship as the member names its component, a flight group's ships whole, and a squad's own walk. Dock, Escort, the search for a new target, the search for a pod to scoop up, Dark Reign shoot and Launch walk their targets so. A squad that two members name is walked twice.

Fix: the game walks a squad that holds itself, directly or through other squads, round for ever, reads a member no record stands for from address zero, and stops with a fatal error at a member of a kind it does not know. A walk also doubles with each squad of a chain whose squads each name the next one twice, so a long chain hangs the game. OpenReliant passes over a squad the walk is already inside, goes into no more than 4096 squads in one walk (bind.Mission.SquadPath), and passes over such a member. The triggers' count of a squad's members, for_each_ship and in_squad walk squads the same way (Script VM).

Running orders

object_orders (0x0040C5F0) runs an object's current order:

  1. It starts the queued orders that are due (see below).

  2. With a retaliate order, it runs order_retaliate.

  3. It clears the object's afterburner and reverse_thrust, so an order that burns sets them again each time it runs.

  4. A one-shot order runs its update and pops itself, and object_orders then runs the order below. Any other order runs its init first if it is starting, then its update.

    Fix: the game runs the update of the order it found before the init, even where the init popped that order or pushed another, and then marks the order now on top as started, so that order's own init never runs. Of the game's orders, only Rotate Boridin breakaway warp projector (37) pops itself in its init. OpenReliant runs the update only while the order is still on top, and the order that took its place starts on its next run.

    Fix: after a one-shot order's update, the game pops the order on top and runs the orders again, whatever the pop did, so a one-shot order whose update pushes another order runs again and again until the stack overflows. OpenReliant runs the orders again only where the pass left the stack shorter, which also stops a one-shot order whose pop a script stops.

  5. Afterwards it clears the throttle and both burns while the object's engines are disabled (DisableEngines), both burns when it has no afterburner fuel, and reverse thrust unless the object has can_reverse.

It runs from two places:

  • orders_update (0x0040C8F0) runs object_orders once a frame for every object that is not disabled (DisableObject), the player's ship included, and after it, where the object's guns are not disabled, its turrets (object_step_turrets). mission_frame (0x004924B0), mission_run's work for each frame, calls it (see the game loop).
  • simulation_step runs it for the player's ship, before the objects move, while the ship's current order is Player Control.

So the orders of AI ships run once a frame, and the player's controls once a frame and once each simulation step.

Retaliation

Damage of kinds 0, 1 and 5 adds to an object's recent_damage (0x690), which orders_update zeroes every 500 ticks, and each hit records the attacker's slot in last_attacker (0x694). While the current order has retaliate, order_retaliate (0x0040C520) pushes Fight (105), aimed at the attacker, once recent_damage reaches 0.7 (0x004DC484) of the ship's full armor, six times its armor class. It does so only when the attacker is on the other side, is not already the current order's target, and both ships are fighters (class 1, the word at +0x28 of the combat stats, see Objects), and not while the ship has do_not_disturb (DoNotDisturb).

The queue

An object's orders can wait in a queue of up to 20 for their tick, queued_orders (0xB90) with queued_order_count (0xB8C): Dark Reign shoot queues the ion cannon's order there (The ion cannon), and in a network game the orders from the other machines wait there, as do Fight's next maneuver and Find Scoop Up's Scoop Up on the machine that sends them. A QueuedOrder is the order's entry, a byte the sender passes, and the tick it is due. order_queue (0x00402660) adds one due a given number of ticks after frame_start (see the game loop). An equal order already queued, the same order at the same target, stays if it is due no sooner, and is replaced otherwise.

object_orders takes each queued order that is due by mission_ticks and has a priority no lower than the current order's, pushes it with its data, and removes it from the queue. It removes a due order without pushing it while the object has not been created.

Fixes: a full queue is a fatal error in the game; OpenReliant logs it and leaves the order out. The game writes the queued order's data over the order on top of the stack even where the push did not take; OpenReliant writes it to the order pushed alone.

aigeneric.zig ports the queue (queue, startQueued). Not ported: the orders sent to and from the other machines (#55).

Steering

Most orders that fly a ship steer with ai_steer (0x00401380), which takes a point to aim at, a limit, an ease and flags. It sets the pitch, yaw and roll inputs from the point's direction in the ship's frame, through ai_steer_angles (0x00401710), or ai_steer_axes (0x00401690) when the word at +0x24 of the ship's flight stats is nonzero. It takes (1 - ease) * 6 times each turn rate off its input and multiplies the result by 11.46, the degrees in a radian over five, so an input fills at five degrees off; then it holds each input within the limit, at most 1. While frames take more than 10 ticks, turns of less than a sixteenth of a turn (22.5 degrees) are halved first, along with the limit.

ai_steer_angles banks the ship round: within 18 degrees of the nose it simply yaws at the point, further off it rolls to bring the point overhead, and it pitches only once the roll is within 0.8 radians of where it wants it. A ship flying backwards turns toward the other way about.

ai_steer_axes turns the ship flat: it pitches and yaws at the point together and never rolls, and with the point behind it, it yaws hard to the side the point lies on. No file sets the word: the executable's ship_flight_stats (0x004F9E70) holds it for each ship type, and stats_load_ships leaves it. The capital ships and most other types turn flat; the fighters bank, as do some support ships, the Nanny and the limpet car among them. The missiles' own flight stats (missile_flight_stats) turn every missile flat but the fuel pod. create/flight.zig lists the types.

Flag Meaning
0x1 First avoid_near (0x004028F0) moves the point around the objects with components in the ship's first avoidance list.
0x2 First avoid_ahead (0x00402DC0) moves the point around the objects in the second list, projected along their motion.
0x4 Unless avoidance took over, the ship also rolls toward the world's Y axis (ai_roll_upright).
0x8 Pitch stays at 0.2 or more.

When avoidance moves the point, ai_steer steers with a limit of 1, no ease and without flag 0x8, and returns true. The lists are what avoidance_scan builds, and a ship with no_avoidance avoids nothing.

Fix: the game follows a null pointer for a ship with no flight stats, a stand-in, that steers, arrives or avoids what lies ahead, and for a collision course with such a ship on either side; OpenReliant leaves the ship's inputs as they are, counts it as not arrived, avoids nothing for it, and takes it for no collision course.

Arriving

ai_arrive (0x00402140), through ai_arrive_steer (0x00402160), brings a ship to a point, turned as an orientation, at a least throttle: Follow Curve arrives so at a path's start, and Dock and Formation use it too. It goes by where the ship stands next. Within 2000 of the point it has arrived: its throttle is the least it was given and its turning inputs nothing. Otherwise:

  • Behind the point along the way the orientation faces, within 18 degrees of that line, and itself facing that way within 11, it steers at the point with ai_steer, flags 0x3, and rolls to stand as the orientation stands: its roll input is the roll between their up axes less 12 times its roll rate, in degrees over 40.
  • Otherwise it steers with ai_steer, flags 0x3, round the circle through it that meets the orientation's line at the point, in the plane of that line and the ship: aiming half a radian round the circle ahead of itself, or at the point once within the circle's last half radian. The circle's radius is at least twice speed_per_pitch_rate (flight stats +0x20); standing ahead of the point, the ship aims across the far side of the smallest.

Either way its throttle is the way left, straight or round the circle, over 4 times its cruise speed through its inertia (4 * cruise / (1 - inertia)), less 0.1, and never less than the least it was given.

Avoidance

avoidance_scan (0x00492190) runs for each object in mission_frame's pass that draws them, the ones it draws, while the object's current order has the avoidance flag and it has no no_avoidance. It empties the ship's two lists (GameObject + 0x6B4 and +0x6E0, a count and ten slots each) and fills them from the objects that are not standing in, disabled or jumping, not planets, not the ship itself or what it fights, and where neither names the other in its first pass-through slot:

  • an object that lists components goes on the first list while its sphere, 10000 wider than the two radii, overlaps the ship's where the step takes them both;
  • any other goes on the second, where the ship lists no components, while the ship is on course to hit it within 50 steps by 2000 (ai_collision_course, 0x00401980).

ai_collision_course first asks that the two be within reach of each other over the steps, at their cruise speeds, with both radii and the margin. Against a target without components, the target must then lie ahead of the ship, and the ship close on it by its velocity less twice the target's, to within both radii and the margin. Against a target with components, the ship's next position must instead lie within the ship's cruise speed times the steps, and the margin, of a box of one of the target's parts: of the parts in its root's child list, every part whatever it is linked to, shown and with a collision tree, each whose sphere, that reach wider, holds the position has each box of its tree tested, and the position is within reach of a box where it stands nearer to the box's centre, squared, than the box's half size and the reach, each squared, together.

Fix: the game also hands back the part and the box through two pointers it does not test, which every caller passes null. Avoid Target (order_avoid_target, 0x0040B330) asks only that its target's type is below 0x100, not that it lacks components, so the game writes to address zero once the ship comes within reach of a box of a ship that lists components. OpenReliant hands back neither.

avoid_near (0x004028F0) works the first list, from the line between where the ship goes next and the point it steers at. For each object not standing in, exploding or disabled, not farther behind along that line than both radii, closing along it, and to be met within 250 steps: it takes the object's box where it will then be, widened by the ship's radius, in the object's frame and scaled by its visibility. Where the line crosses it, the point moves onto the box widened again by the ship's radius: onto the face nearest where the line enters, at whichever is nearest the point of four spots of it, each at the entry along one of the face's two axes and at an edge of the box along the other. The point is scaled by the visibility again on its way back to the world, rather than unscaled. The heading stays the one to the first point, but each object after the first tests the line to the point the one before moved.

avoid_ahead (0x00402DC0), for a ship that lists no components, works the second list: each object is taken where it will be once the ship has flown to where it is now at the ship's cruise speed. Where the line to the point passes within 1000 of it, or 500 for an object of another side, the point moves as far ahead of the ship as the object will be, and above or below the ship, away from the object along the ship's own up and down axis, by both radii and 2000, or 1000 for another side's.

The orders

Each order's routines are named after it: order_fly_init and order_fly for Fly, for example. Many take their target's validity from order_target_valid (0x00401870): a targetable object that is not cloaked, exploding, disabled or ejected, nor has object flag 0x10000000, and, when the target is a component, one that is there and neither hidden nor has node flag 0x10. Random choices come from object_random (0x004ADD10), each object's own generator: a seed at +0x638, set from C's rand() when the object is created, that steps as seed * 0x343FD + 0x269EC3, bits 16 to 30 of it over 32767 giving a number from 0 to 1.

Every order, by its number, with what it does and whether OpenReliant runs it. A multiplayer order OpenReliant does not run yet holds its place on the stack and does nothing (#55).

The Order column holds the developers' names. Scripts and ai/orders.zig use them in lower case, with underscores, but for the orders the table names nothing, or names as an earlier one, which take OpenReliant's own names, for what they do differently (tablegen's own_names): 3 launch_jack_hammer, 40 jump_in_spread, 41 jump_out_spread, 106 abandoned and 110 fire_ion_cannon. Order 200 has no name, and goes by its number.

Number Order What it does Ported
0 Do Nothing Zeroes the throttle and the turning inputs. Yes
1 Fly Aimlessly On starting, takes a figure from 1 to 3 from the ship's own random numbers, keeps where the ship will be next and how it will be turned, its X axis reversed where its next number is odd, and sets the throttle to 0.4 and up to 0.3 more by rand(). Each update it steers with flags 0x3 for point n of its figure, from 1, and for the next once within 1000 of it. Point n lies t = n twentieths of a turn round, (cos t - 1)(figure + 1) times 25000 along the kept X axis and sin(figure t) times 50000 along the kept Z axis from where the order began: figure 1 is a circle, and figures 2 and 3 are wider loops that swing ahead and back two and three times on the way round. It never ends. Improvement: OpenReliant computes the sine and cosine rather than reading the engine's tables (sr_sin, sr_cos). Yes
2 Launch Missile One-shot: launches a missile at the target from the first of the ship's racks with missiles left that is not a Jack Hammer's (Missiles). Yes
3 (nameless) One-shot: as Launch Missile, from the first rack of Jack Hammers. Yes
4 Warp In A ship arrives through its own tunnel, emerges, then posts JumpedIn (Gates). Yes
5 Warp Out A ship aligns with its target, opens its tunnel and enters it, then queues Warp In with the same sequence number. Yes
6 Fly Flies at the speed in its data, or at full throttle for zero. With a target it flies to it and pops within 2000 units; otherwise it keeps the heading it had when it started, steering at a point 20000 units along it. It steers with flags 0x7 and halves the throttle while avoiding. An object without flight stats is moved along that heading instead; Improvement: OpenReliant draws it gliding on between the ticks (The game loop). Yes
7 Run Away Flies away from the target at half throttle, for a point on the far side of the ship from the target, 100000 times as far from the ship as the target is. It moves the point round what is near (avoid_near), then steers at it with flags 0x3 and an ease of 0.1, which go round what is near and ahead again. Pops when the target's slot holds a stand-in. Yes
8 Land The player's ship lands on its carrier, which ends the mission (Landing). Yes
9 Escort On starting, takes the ship its target names, or the ship at the order's number among a flight group's or a squad's ships, counting round them again past the last (escort_count_place, 0x0040AA50); OpenReliant takes none where the group has no ships, which the game walks for ever (Fix). Each update, it pops once that ship's slot holds a stand-in; otherwise it steers for a point 10000 ahead of the ship: within 5000 of it with half its turn and flags 0x4, and farther off with its full turn and flags 0x3. Its throttle is the escorted ship's speed over its own cruise speed, and 0.0001 more for each unit the escorted ship lies ahead along its own heading. Yes
10 Find New Target Walks the ships its target names, weighing each it can aim at, cloaked or not, by the square of its node's distance from where the ship will be next (Picking a fight). It fights the lightest to fight, pushing Fight, or Torpedo (103) for a ship of the torpedo class; with none, it mills round the lightest to mill round, pushing Mill (120); with neither it pops. Yes
11 Explode A destroyed object's end, by what it is and in one of three styles (Destruction). Yes
12 Ripper grabs target object A Ripper carries its target off (The Ripper). Yes
13 Object Attach On starting, keeps where the ship will stand next in the frame its target will stand in next. Each update it puts the ship there in the target's next frame, turned as the target will be, and gives it the target's turn, velocity, speed and rates of turn, so that it rides the target. Yes
14 Formation Regroup The ships of a flight group meet at their places in its formation about its middle, 10000 back along Z, then turn to face their places, the formation's leader moving the group on (Formations). Yes
15 Patrol Route The ships of a flight group fly round the waypoints of a route, in formation about their leader where they have points in one, and otherwise alone (Formations). Fix: in formation it differs from the original's, whose ships hang back short of their places, so that the formation stops going round the route. Yes
16 Toggle Cloak One-shot: cloaks or uncloaks the ship if its model's header allows a cloak, and the ships being launched from it do the same. Yes
17 Ship Follow Curve Flies the path of the mission's curves from the curve in its data (Following a path). Yes
18 Slow Rotate Zero throttle, yaw input 0.1. Yes
19, 40 Jump In The ship arrives beside its target, flying in from far behind it; 40 then spreads the ships out for 200 ticks, each rolling and pitching away by its place in its group (Jumps). Yes
20, 41 Jump Out The ship turns to where it goes, charges and jumps: to its target, where Jump In of the matching number brings it in, or out of the mission where it names none; a ship jumping with the player's goes in formation behind it (Jumps). Yes
21 Find Scoop Up Starts at its first step, as 115 and 116 do (0x0040B1C0). The first goes on to the second, where a multiplayer game waits for every player unless the target is a ship (ai_sequence_sync, 0x00401000). The second walks the ships its target names for the nearest to where the ship will be next that it can aim at, ejected or not (0x0040B140), and pushes Scoop Up (107) at it; with none it pops. Once Scoop Up is done, the order starts again from its first step. Yes
22 to 24 Random Spin Slow, Medium, Fast On starting, zero throttle and each turning input 0.1 plus a random number times 0.3, 0.5 or 0.9. Its update does nothing. Yes
25 Fixed Gate Jump In The ship comes in through the tunnel at the object its target names, out beyond its mouth, cut by its portal (Gates). Yes
26 Fixed Gate Jump Out The ship goes out through the nearest gate's tunnel, the player's riding the worm, then comes in through its target's (25) (Gates). Yes
27 Formation On starting, keeps its place abreast of the target, 3000 apart: the ship SetAI numbers n flies n / 2 + 1 places out, to the target's left where n is even and to its right where it is odd. Each update it pops once it can aim at the target no more; otherwise it arrives at its place in the frame the target will stand in next, turned as the target will be, at a least throttle of 0 (Arriving). Yes
28 Fixed Gate Open A tunnel grows open at the object (Gates). Yes
29 Fixed Gate Close The object's tunnel shrinks away and goes. Yes
30 Eject The pilot leaves the ship in its cockpit, which becomes the pod, and the rest of the ship a new object; the pod clears the ship, and the player's waits to be picked up (Ejection). Yes
31 Fixed Gate Collapse The gate comes down in fireballs, its tunnel burning out (Gates). Yes
32 Match Speed Sets the throttle to the target's speed over the ship's cruise speed. Pops when the target is no longer valid. Yes
33 Dark Reign shoot Picks the nearest ship of its target it can fire at and queues 110 at it; with none, pops (The ion cannon). Yes
34 Move to spawn pos A deathmatch's (deathmatch.cpp). Not read yet. No (#55)
35 Turns object lights on Turns on the light maps of the parts flagged lightmap (the second pass of their surfaces), heard (bigon), and pops. The rogue base (165) turns on a surface of its first part each 100 ticks, four of them, then those of its other parts, heard at each step, and pops after 500 ticks. Where the light maps aren't drawn (Lmaps, 0x5D5618) it pops at once. A ship type shares its meshes, so every ship of the type goes with it. Fix: the game turns on only the level of detail each part is drawn at, and a second pass for every surface, drawing those with no second texture flat; OpenReliant turns them on at every level, and only where a surface has a second texture. Yes
36 Make Boridin section break away The Boridin hides the nine parts its breakaway section takes (Bor break off section, its core, cylinder, projector, projector generator and that generator's wreck, two launch doors and Object02), its Bor main body streams great grey smoke for 5000 ticks, and the first Boridin breakaway among the objects stands where the section stood, 4000 along its nose, turned as it was, so that it seems to come away from the Boridin. Its update does nothing. Fix: with no breakaway, the game stops with an assertion; OpenReliant logs it. Yes
37 Rotate Boridin breakaway warp projector The ship's Bor brkawy proj (its name ending in a space) loops its Rotate Proj track from where it stands, at four times its pace, and the order pops. No shipped mission gives it. Yes
38 Start warp projection from Boridin The Boridin breakaway projects a warp tunnel ahead of it, with six beams, and never ends (The Boridin's projection). Yes
39 Make ripper drop what it's carrying A Ripper lets go of what it carries (The Ripper). Yes
42 Turns object lights off Turns off the light maps order 35 turns on, at every level, and pops. Yes
43 Huuuuuuuge explosion The Uber Explode at the object, of size 50000 over 1500 ticks (Effects), then it pops. Yes
44 Immediately set ship to zero velocity and rotation object_stop (0x00403000), then it pops. Yes
45 Fly ship backwards Throttle -0.5, no turning. Yes
100 Player Control The player's controls fly the ship (Controls). Yes
101 Multiplayer Control Disables the object once it has object flag 0x10000000. No (#55)
102 Avoid Target While on course to hit its target (ai_collision_course, 75 steps ahead with a margin of 2000), pitches at full input away from it at half throttle. Once clear, it flies straight at full throttle until 50 ticks after it started, or 50 to 150 after it last turned away, the extra drawn from its own random numbers, and pops. It pops at once where its target is not a ship. No shipped mission gives it. Yes
103 Torpedo Find New Target's for a ship of the torpedo class. On starting, full throttle. Each update it pops once it can aim at its target no more; otherwise it steers with a limit of 2 and no ease at the target's node, led along the target's nose by the target's speed times the ticks the torpedo takes to get there at its top speed, less its own velocity times half those ticks, 25 at most. It goes off against what it meets (Collisions). Yes
104 Launch The ship leaves its carrier, in the style the carrier's type picks (Launches). Yes
105 Fight Fights its target by running combat maneuvers, one after another. Yes
106 Eject The ship a pilot has left: destroyed 200 ticks on. Yes
107 Scoop Up A nanny ship or the Antanov takes the player's pod aboard with its tractor beams (Ejection). Yes
108 Eject Spin An AI pilot's ship spins, unpowered, for 200 ticks; then the pilot ejects (Eject). Yes
109 Dock Docks at a port of its target (Docking). Yes
110 Dark reign shoot Fires the ion cannon of the Dark Reign, the Boridin or the rogue base at its ship, which it destroys; ion_cannons_hold_lock keeps its lock (The ion cannon). Yes
111 Ripper end drop object A Ripper draws its forearms back once it has let go (The Ripper). Yes
112 Ripper attach cargo pod to Mammoth A Ripper fits a cargo pod onto a Mammoth (The Ripper). Yes
113 Eject fighter attack A Sabre flies at the player's pod and shoots it down (Ejection). Yes
114 Disrupted A Havoc's shockwave gives it (Effects). On starting, sets object flag 0x8 (unpowered), keeps the tick to end at, the duration in its data (a word) after frame_start, takes the push in its data after that (three floats) as a knock in the ship's own frame, though the shockwave gives it in the world's, and knocks each turn rate by up to 0.05 either way at random, which the ship tumbles by. It also plays fifteen electric rays over the ship, each from its centre out to its radius in a random direction, 90 either way, with a jitter of 0.6, flickering, dimming as they go dark, and lasting as long as the order, white (0.8, 0.8, 1) and blue (0.3, 0.5, 1) in turn. It pops past that tick, and its exit clears the flag. Yes
115, 116 Make capship list left, right A capital ship struck by a torpedo lurches (Collisions): its update (capship_list, 0x0040C3A0) sets the roll and yaw inputs to 0.01 and 0.006 over the ship's roll and yaw rates, to the left for 115 and the right for 116, then 200 ticks on to 0.006 and 0.0048 the other way, and 300 ticks after that clears the roll and pops. Yes
117 Friendly Fire The carrier recalls the player's ship for destroying a friend, and it lands (Friendly fire). Yes
118 Eject Player The player's ship drifts, unpowered, for 400 to 599 ticks, then explodes, unless the pilot ejects first (Destruction, Ejection). Yes
119 Ship Follow Curve Backwards Flies the path of Ship Follow Curve (17) backwards, from its end to its start. Yes
120 Mill On starting, where it can aim at its target, cloaked or not, keeps the tick and a circle facing from the target's node to where the ship will be next. Each update it pops once it can aim at the target no more or 500 ticks have passed; otherwise it flies at full throttle, steering with flags 0x3 for a point on the circle 50000 from the node, which comes round from the ship's side by 0.000005 of its cruise speed a tick. Improvement: OpenReliant computes the sine and cosine rather than reading the engine's tables (sr_sin, sr_cos). Yes
121 Deathmatch Respawn Effect A deathmatch's (deathmatch.cpp). Not read yet. No (#55)
122 Deathmatch Dark Reign target It has no routines: nothing to run. Yes
200 (nameless) It has no routines: nothing to run. Yes

Following a path

Ship Follow Curve (17) and Ship Follow Curve Backwards (119) fly a ship along a path of the mission's curves (The director's camera): from the curve its data names, on through the curve that carries the path on from each curve's end, over the seconds its data gives. The data holds the curve (+0x0A), the seconds (+0x0E) and the ship that carries the path (+0x12), whose offset from where the mission placed it, as the order starts, moves the whole path (curve_ride). Their state:

Offset What it holds
+0x00 The routine motion_follow gets its point from: 0x00403200, or 0x00403600 backwards
+0x04 The fastest the ship moves, as a share of its top speed: 1
+0x08 The curve it flies now
+0x0C The step
+0x10 The mission's tick (mission_ticks) the curve began
+0x14 The curve's share of the order's seconds, in ticks, as its length is to the path's
+0x18 The path's length
+0x28 Where the carrying ship stood as the order started
+0x34 The share of the way along the curve to the next place a point marks, 0 for none
Step What it does
0 It arrives (ai_arrive) at the path's start, turned toward the path's point 4 ticks on and at the throttle the path keeps between them over its cruise speed; the curve's clock holds at its start. The order backwards arrives at the path's end instead, turned toward its point 4 ticks back, its motion motion_forward
1 In a multiplayer game it waits for the other players (ai_sequence_sync, 0x00401000, not ported: #55); in a game of one, it goes on
2 It flies motion_follow along the path, or motion_follow_backwards where its motion was astern; backwards, motion_follow. The path moves it on to step 3
3 The order pops, in a multiplayer game once the other players are there too (not ported: #55), and its exit gives the ship motion_forward, or motion_backward after motion_follow_backwards

The path's routine, each update of the motion, gives the curve's point as far along as its ticks have gone by mission_ticks, which runs on through a frame's simulation steps (curve_point), carried with the carrying ship. Forward, past a place a point marks, the point has ShipReached, with the ship (event_post_ship_reached, 0x0045AC10), one place an update. At the curve's end the ship it ends at has ShipReached too, and the next curve begins; with none, the step moves on. Backwards, past the curve's start, the curve before it begins, found by walking the path again from the order's curve, or at that curve the step moves on.

Fix: the game divides by nothing for a path of no length and for a curve given no ticks, and follows a path that comes round on itself for ever as it walks it; OpenReliant gives a curve of a path of no length all the order's ticks, takes a curve given no ticks to its end going forward and past its start going backwards, as the game's endless share does, and stops measuring such a path, and walking it for the curve before one, after as many curves as the mission has (Curves). Flying forward, the order counts the curves it has moved on to, in bytes of its state the game's follow orders leave alone, and the path ends once it has taken as many as the mission has. Walking the path backwards, the game takes a curve's end for a ship unless its whole reference, kind and all, is 0x0000FFFF (0x0040359E), and so walks on from a curve that ends at no ship to one that starts or ends at none; OpenReliant stops at an end whose index is 0xFFFF. It holds a curve's ticks at 65535, where the game takes them round from nothing. The game moves the step on, and forward posts the end's ShipReached, at every move past the path's end, so that a second move before the order's update, a collision's or that of a second step in the same pass, leaves the order running, to fly the path again once the step comes round; OpenReliant moves it on once.

Formations

Formation Regroup (14) and Patrol Route (15) fly a flight group's ships in the formation their points give them (Formations): a ship's place is its point's place about the formation's origin. The formation's leader is the first of the mission's ships of the group that stands at the formation's point nearest its origin (formation_leader, 0x00404230), and the leader's update moves the whole group on (regroup_sync, 0x00403DE0; patrol_sync, 0x00404330): once every ship of the group still in the mission is ready, they go on together. Missions give Formation Regroup with SetAI and Patrol Route with SetPatrolRoute (Commands); no shipped mission gives either.

Both set the throttle by a pace (formation_set_pace, 0x00404210): a pace of p is a speed of 200p, over the ship's top speed. They turn a ship toward a point in one of two ways (formation_face, 0x004044D0): with ai_steer eased by 0.1 and held within a limit, or on the level (object_turn_level, 0x00404750). On the level, its yaw input is five degrees, in radians, toward the side the point lies on, or the angle to it off the nose on the level where that is less; its pitch input the angle up or down to it, less 6 times its pitch rate, times 0.05 over its flight stats' pitch rate; and its roll input nothing. Either way the point is ahead once the cosine of its angle off the nose is 0.9 or more.

Formation Regroup gathers the group at its places about its middle: the middle of the box round where its ships stood at the mission's last sync. Each ship meets the group at its place about the middle, 10000 back along Z, and its throttle goes to nothing. Its state:

Offset What it holds
+0x00 Where it meets the group
+0x0C Its place about the group's middle, 10000 on along Z from there
+0x18 The formation's leader
+0x1C The step
+0x1D Whether it is ready
+0x1E Whether it flies straight to its meeting point, rather than along a curve
+0x1F How many of the leader's updates in a row the group has been ready
+0x20 The curve it flies along, laid out as the mission's are (Curves)
+0x64 How it follows the curve: whether it has come to the curve's start (+0x6E), and the step along it it flies toward (+0x74)
Step What it does
0 It turns to face its meeting point with ai_steer at a limit of 0.75, and is ready once that lies ahead
1 It flies to its meeting point, straight with ai_steer at a limit of 0.75, or along its curve, and is ready within two of its radii of it, its throttle then nothing
2 It turns to face its place, and is ready once that lies ahead
3 It lets go of the turns and the order pops

Once the group has been ready for 15 of the leader's updates in a row, each ship is ready no more and goes on to its next step; while any is not ready, the count starts again. Setting off for its meeting point, a ship flies along a curve where the point lies from 20000 to 400000 away, and otherwise straight, at a pace of 0.3. The curve runs from where the ship stands to its meeting point and names no ships, its leaving tangent half the way across, turned a right angle about Y, so that the ship swings out to the side (regroup_lay_curve, 0x00403F20). The ship first flies to the curve's start at a pace of 0.3, with ai_steer at a limit of 0.5, then toward the curve's point at each 32nd of the way in turn, the next once within two of its radii of one, its nose turned straight at the point each update (curve_path_follow, 0x00404BE0). Past the curve's end the order pops, its throttle and its turns nothing; the ship is at its meeting point and ready before that.

A ship in no formation, or in no flight group or an empty one, takes none of this: its state stays empty, so that it turns toward the world's origin, its throttle as it was.

Patrol Route flies the group round the waypoints of a route: the route's waypoints in the order the mission lists them (Binding), from the one the order's target names, and after the last its first again (waypoint_next, 0x00404000). Its state:

Offset What it holds
+0x00 The leader's mark, the point ahead of it that it turns toward
+0x0C Where the leader stood at the mission's last sync
+0x18 The point it flies to at its waypoint
+0x24 Its place about the leader
+0x30 That place 8 of the leader's radii on ahead of the leader
+0x3C The way from the leader to its mark
+0x48 The waypoint
+0x4C The formation's leader
+0x50 The leader's state
+0x54 Its formation point
+0x56 The mode: 1 in formation, 2 alone, and 0, which nothing sets, turning to face its place ahead
+0x57 Whether the group's ships lie near their places alike, within 5000 of each other, which nothing reads
+0x58 How far it stood from its place ahead as it set off, which nothing reads
+0x5C The leader's pace
+0x60 The size of its formation: how far apart the corners of the box round its points lie
+0x64 Whether it is in place
+0x65 Whether the leader is to aim at its waypoint again
+0x66 Whether the leader has come to its waypoint

Each waypoint starts the pace at 0.15. A ship without a point in a formation flies alone: to its place in a line of its group's ships through the waypoint, along the way from the next waypoint, three widths of the group's widest apart, the group's first nearest the next waypoint. It steers there with ai_steer at a limit of 0.5, and within two of its radii sets off for the next waypoint.

In formation, each ship's place is its point's place, turned as the leader is, about where the leader stands, and it turns on the level toward that place 8 of the leader's radii on ahead of the leader. Turned to it and within 3.2 of its radii of its place, it slides 0.005 of the way there each update. Until it is in place, it flies at 3 of the leader's paces while its place lies ahead of it, and at a twentieth of the leader's pace while its place lies behind it; within two of its radii of its place and turned to it, it is in place, and flies at the leader's pace and a tenth more. The leader is always in place, flies at its own pace, and has come to its waypoint within half its formation's size of it. It turns on the level toward its mark.

Once every ship of the group is in place, each is no longer. Where the leader has come to its waypoint, each sets off for the next waypoint, stopped, its places taken anew. Otherwise the leader aims at its waypoint again: it turns toward it, takes its mark 8 of its radii along its nose turned by its yaw input, at the waypoint's height, and its pace by how far it must turn: 0.5 under 12 degrees, 0.25 under 60, and otherwise 0.15. Each ship reads whether the leader has come to its waypoint in turn, and the leader setting off clears it, so that the ships after the leader in the group's list aim again rather than set off; they keep their places about the leader all the same.

Fix: the game slides a ship toward its place by placing it (object_place), which drops the move its step has left to make, so that the ship moves by the slide alone, 0.005 of the way each frame, and hangs back where that pull meets the leader's pace: as far back as the leader's speed over 0.005 times the frame rate. At 60 frames a second that lies beyond two of a Sabre's radii at the pace each waypoint starts at, so that the ships are never all in place and the formation never moves on. OpenReliant slides the ship and the move it has left together, so that it flies on into its place.

Fix: the leader reads and writes the order state of each ship of its group, whatever order the ship follows, so that a ship given another order since has that order's state taken for the formation's and changed; OpenReliant counts and moves only the ships that follow the leader's order, and a ship whose leader follows another takes its pace as nothing. The game looks for the leader, and measures a formation's size, among the points from the formation's first to the mission's last, so that a later formation's point nearer its origin can take the lead, which no ship of the group stands at; OpenReliant looks among the formation's own points. Where no ship stands at the lead, Patrol Route follows a null pointer, as Formation Regroup does for a ship in a formation but no flight group; OpenReliant has the ship fly the route alone, and leaves Formation Regroup's state empty. The game takes the group's spread from its first ship, and where that ship is no longer in the mission, from whatever its stack holds; OpenReliant takes it from the first it counts.

Fix: Formation Regroup's state runs past the 0x90 bytes the game allocates for it: the curve's next marker lies at +0x94. The game looks for the points that mark places on that curve by its address's place among the mission's curves, which it isn't one of, and so takes the points of whatever curve that happens to give; OpenReliant keeps no marker, and takes it that none does. Where the curve ends the order, the game goes on to check the meeting point in the state of the order below; OpenReliant stops there.

Fix: the game takes a Patrol Route's waypoint wherever the order's target points, past the mission's waypoints or before them, and reads past the table's ends for the waypoint after the last and before the first; OpenReliant ends an order whose target is past the waypoints, and stays within them. The game follows a null pointer for a ship with no flight stats, a stand-in, whose pace or level turn the orders set; OpenReliant leaves its throttle and its inputs as they are.

Improvement: the points along a curve are computed rather than taken from the table of the curves' weights, as for the curves' lengths (Curves); the five degrees and the turns by which the leader picks its pace are exact; and the angle up or down comes from std.math.atan2 rather than the engine's table (sr_atan2).

Docking

Dock (109) brings a ship into a berth at another's port. Its target names the ship and the port by its component: the port is the ship's docking points (SHP attachment kind 9), counted part by part over its root's children. Where the target names no port, or names a flight group or a squad, the init (order_dock_init, 0x00406B80) takes the first free port of the ships it names, in the order it walks them: the first port at which no other object's current order is Dock (0x00406A90). It then picks a style by what the ship is and what it docks at, from a table of an init, an update and an exit each (0x004E1618): the limpet car's (2), or at the Czar docked its own (3); the limpet pod's (4); at a Nanny, the Nanny's (1); and otherwise the station's (0). The data's first byte holds the style.

The station's style finds the docking points (0x00406C80): the ship's own, its first, and the port, whose part plays its deploy track at 4 from its start. The port is found by counting the component down at each docking point, so a component past the station's docking points, or a negative one other than -1, which the init's search leaves alone, names none. A ship without a docking point stops the game with "Docking information not defined on %s". The check of the station's port reads the ship's own node again (0x00406E2A, +0x10 where the port's is +0x20), so a station without the port goes on to the berth with the port's node null, as order_push cleared the state, and faults there. The berth (0x00406E70) is where the ship's origin stands docked: the port, less the ship's own docking point turned by the port's orientation, in the frame the station's part is drawn at, and turned as the port is.

Fix: the game takes the ship's own docking point in its part's own frame alone (the node's +0x18 and +0x3C, 0x00406E97), which berths a ship whose point is on a part hung from another part off by that part's place; OpenReliant carries it up the parts it hangs from. Every shipped model has its docking points on parts that hang from the root.

The station style's state:

Offset What it holds
+0x00 The routine motion_follow gets its point from as the ship slides in: 0x00406F20
+0x04 The fastest the ship slides, a share of its top speed: 0.5
+0x08 The step: the station style's, or the limpet car's (Limpet docking)
+0x0C The mission's tick (mission_ticks) the slide ends at
+0x10, +0x14 The ship's own docking point's node and place on it
+0x20, +0x24, +0x30 The port's node, its place on it and its orientation
+0x54 Whether the ship came from the port's right, which mirrors the way round
+0x58 Where the ship stood as it began to slide in

The init (0x00407010) picks the first step by where the ship stands in the port's frame: more than 100000 behind the port, step 3 where it stands within a fifth of that to the side, and step 2 further out; nearer, step 1 behind the port and step 0 ahead of it. Steps 0 to 4 steer for a point in the port's frame, mirrored across it for a ship that came from its right, at full throttle (ai_steer, no flags), rolling the ship to stand as the port stands (its roll input the roll between them less twice its roll rate, in degrees over 40, within 1), each on to the next within 2000 of its point. Holding the roll leaves the ship its pitch and yaw to come round with: a ship that turns flat, as capital ships do, flies the whole way round, while a banking one turns only while the point lies within 18 degrees of its nose or its tail.

Step Point, in the port's frame
0 100000 to the side
1 100000 to the side and 100000 behind
2 Twice the ship's cruise speed over its yaw rate to the side, 100000 behind
3 50000 behind, on the port's line
4 10000 behind, on the port's line
5 The ship latches on: attached, flying motion_follow, the station stopped dead, for 1000 ticks
6 It slides in: the path's point stands on the port's line behind the berth, as far back as the ship stood as it latched on, times the square of the share of the 1000 ticks left, with the port's up as the way up. Past them, the ship's motion is motion_backward, and the step moves on
7 It is stopped dead, set in its berth, heard docking (sound 0x37, dock), and has its Docked (event_post); the order pops

Its exit, the Nanny's too (0x00407D10), leaves the ship's first pass-through slot empty. The ship stays attached.

Fix: the exit leaves the motion as it is, so a ship whose order ends while it slides in goes on flying motion_follow, which then reads the next order's state as its path. OpenReliant puts a ship still on motion_follow back on motion_forward, as Ship Follow Curve's exit does.

Fix: where the ship has no docking point the game stops, and where the station has no port that the component names it faults. OpenReliant logs either, and the order ends.

Fix: the game goes on reading the frames of a station that has gone; OpenReliant ends the order, and until then the slide in holds the ship where it is.

Nanny docking

dock_nanny_init (0x004073E0) enables carrier pass-through and plain motion. It stores the selected port's point, raised by the docking ship's height. dock_nanny_run (0x00407510) opens entry door 0 or 1, approaches a point 1500 above and 20000 ahead of the port, then steers into it. Within 500 units it stops and closes the door. After strictly more than 500 ticks, create.rearm refits missiles through the shared create.arm path and restores countermeasures, afterburner fuel, gun charge and rounds. Player ships retain their chosen loadout, including modded missile racks. Hull and shields are not repaired.

Exit door 4 or 3 opens. After 400 ticks, Docked is posted and forward motion resumes. The ship uses its afterburner for another 150 ticks, then the exit door closes and the order ends. The player's locked Nanny view (43) returns to the cockpit. Fix: each door closes from its own animation time instead of using the first port's time. Invalid ports or missing carriers end the order without dereferencing missing nodes.

Limpet docking

dock_limpet_init (0x00407D30) finds the docking points and works out the berth as the station style does, and keeps the limpet car's step in the same word, +0x08, starting at 0 (0x00407D5E). dock_limpet_run (0x00407D70) approaches 10000 behind the berth, then uses the station style's slide callback for 1000 mission ticks at half speed. It stops the carrier during entry. At the berth, clamp parts 2 and 3 play rotate at speed 4. A visible pod mesh (part 0) is hidden and replaced by a separate limpet-pod object, attached to the carrier. If it was already hidden, the mesh shows again and the first existing limpet pod is retired, as in the original. Three 400-tick waits cover clamp rotation, departure and clamp return.

Step What the limpet car does
0 It flies to the point 10000 behind the berth
1 It latches on, and the slide starts
2 It slides in
3 It is set in its berth, heard docking, and takes or leaves the pod
4 Its clamps turn, for 400 ticks
5 It backs away, for 400 ticks
6 Its clamps turn back, for 400 ticks, and the order ends

dock_way (0x00406F20) moves the word on by one at the slide's end, whichever style's step it holds (0x00406FF4). When the carrier explodes, the order ends at step 0, and the limpet car is destroyed with it at steps 1 to 4.

The order posts Docked if the pod mesh is hidden, Undocked otherwise. Its exit restores forward motion, clears attachment and clears the first two pass-through entries. The separate pod's update (0x004084B0) destroys it if its carrier explodes. Undocked is also exposed to mod scripts through the event declarations.

At the Czar docked, the limpet car's own style (dock_limpet_czar_run, 0x00408220) runs dock_limpet_run for steps 0 to 2, then takes or leaves no pod. At step 3 it is set in its berth, heard docking, and its clamps turn, and it has its Docked at once. It holds on for 12000 ticks before it backs away, and the order ends with its Undocked. Its init and its exit are the limpet car's (0x00408210, 0x004084A0).

Landing

Land (8) brings the player's ship down on its carrier, and ends the mission. PERMISSION TO LAND gives it (permission_to_land, 0x00453DE0), which frame_controls calls outside a multiplayer mission: heard at most once in 500 of the timer's ticks (0x0052987C), it aims Land at the ship the player launched from (player_carrier). In training, the training missions (30 to 35) and the simulator's training, the flight instructor clears the ship to land where the script's variable 10 (landing_cleared, 0x0052A418) is set. In any other, unless the ship is landing already, the carrier refuses it, or clears it where variable 10 is set. Mission 1's script sets it as the Reliant jumps in. The pilot asks on the radio, and the answer comes as a report 300 ticks later (Reports); where the radio's reports are all taken, nothing answers and nothing lands.

The init (order_land_init, 0x0040EAC0) picks a style by the carrier's type, from a table of an init and an update each (land_styles, 0x004E1FE8): the Reliant's (1) and the Yamato's (0). Any other stops the game with "Cannot land on %s". The state's first word holds the style. The Reliant's init (0x0040F5C0) sets the first step, due in 700 ticks, or at once where the ship is being sent home for its friendly fire (ending 6 or 7). Its update (0x0040F940) keeps its state as:

Offset What it holds
+0x00 The style
+0x04 The frame's tick the step waits for
+0x0C The step
+0x10 The middle of the tube the ship lands in
Step What it does
0 Until the step is due, the player flies on (player_controls). Then the cutaway: the scene becomes the landing's (0x00587CD4 = 3), every object but the ship and its carrier is disabled (flag 0x400) and those two enabled, the ship flies by its nose (motion_plain) and passes through everything, and the upper door of the carrier's first launch tube, part 6, plays opendoor2 at 1.23 with the door's sound (0x35, dooropen) where it stands
1 The ship steers at a point 3000 over the tube's middle (ai_steer, no flags), its throttle 0.0001 for each unit it has to go, at most 0.5. Within 200 of it, it stops, its turns nothing, for 50 ticks
2 It sinks along its own Y axis, which points below it (motion_downward)
3 Its throttle is its distance from the tube's middle over 15000. Once it is less than 100 over it, along the carrier's Y axis, it stops, with the landing's sound (0x3E, shipland), for 270 ticks
4 The mission is over (mission_over)

The cutaway (0x0040F600) switches to one of the landing's views at random, and moves the carrier to (0, -1000000, 0), turned as the world is. The ship lands in the carrier's first launch tube, the one the launch drops ships out of through its lower door (Launches): its middle is halfway between the middles of the tube's lower door, part 0, and its upper door, part 6, each the middle of the bounds of the level its part drew last. The ship stands 20000 ahead of it and 5000 above in the carrier's frame, looking at the point 3000 over it, stopped, its power shared evenly. The carrier's orders are all popped, and it stops. The approach leaves the ship pitched about 6 degrees nose down, as it looked at the point over the tube from where it started, and it sinks along its own Y axis, so that it comes to rest ahead of the tube's middle, tilted as it came.

Improvement: the ship levels out as it approaches: the point it steers at rises from the game's height to the ship's own as it nears the tube, eased in and out over the way it comes, so that it arrives level. It stops dead over the tube, and sinks straight down it, coming to rest level. Once its top is below the underside of the tube's upper door, the door closes over it, its opening played back as fast as the launch opens the tube's doors, 2 a step, with the game's sound of a door closing (0x36, doorclos). --original lands it as the game does, and leaves the door open.

Fix: a landing on a ship that nothing lands on ends at once, where the game stops, and PERMISSION TO LAND does nothing where the player's ship launched from no carrier, where the game reads through a null pointer.

Improvement: the player's ship lands in a hangar bay of any other carrier that ships launch from as one (Hangar bays), the Victorious or a mod's carrier among them, so that PERMISSION TO LAND ends a mod's mission on the carrier the mission launched the player from. The landing aims at a gate: the component of its order's target, which a mission's SetAI names with component(carrier, gate), or gate 0 when the order aims at the carrier as a whole, as the player's request does. Aimed at the whole carrier, the landing is due 700 ticks on, as the Reliant's is; aimed at a gate, it begins at once, so that a mission's script lands the ship at its own moment. A carrier whose model has no launch point for the gate is one that nothing lands on. A ship other than the player's lets the landing go at once, as on the Yamato.

The cutaway is the Reliant's: one of its two views, picked at random, standing by the bay (The landing's views); the carrier moved away and stopped, every other object disabled, and the ship passing through everything. The ship is set out from the mouth of the bay, where the way out from the launch point along its Z axis leaves the carrier's bounds, by six tenths of the bay's depth from the point to the mouth, and at least twenty times the ship's radius, so that the distance fits the carrier and the ship rather than one model. It looks in at the point, and the gate's doors open as the launch opens them. The ship then steers at the launch point, upright, its throttle 0.0001 for each unit it has to go, at most the throttle that flies it at the Yamato's bay speed of 100, and stops within 200 of the point, sounding its landing. The doors close behind it, and 270 ticks later the mission is over.

The Yamato's style lands the ship in the Yamato's landing bay, ship type 0xD5 (Jap Yam Landing Bay, yamhanger.shp), whose part 6 is the hangar and part 1 the pad. Its init (land_yamato_init, 0x0040EB60) sets the first step, due in 700 ticks, and for the player's ship makes the bay in the cutaway slot, passing through everything. Its update (land_yamato_update, 0x0040EE80, steps from the table at 0x0040F59C) keeps the pad's due time at +0x08, and once the ship stands on the pad, where it stands on it, and how it is turned. A ship other than the player's lets its landing go at once.

Step What it does
0 Until the step is due, the player flies on. Then the cutaway, and the ship flies by its nose and passes through everything; the scene becomes the landing's, and every other object is disabled
1 The ship steers at a point 1000 above the hangar's middle, upright, at a throttle that flies it at 100 by its cruise speed, until it is within 2000 of it
2 It steers 500 ahead along its nose from the point 500 beyond the pad's middle, level with its own underside, its throttle 0.0001 for each unit it has to go less 0.02, at most the bay's, and stops within 500 of it
3 Stopped, it turns straight along the bay and level: its yaw and its pitch inputs are each the angle off the bay's Z axis less 4 times its rate of turn, times 0.3 over its type's rate. Once both are within 0.05, it stands on the pad and rides it
4 It waits 200 ticks
5 The pad plays its floor track (0x004E2048), lowering the ship, with the landing's sound (shipland)
6 400 ticks later, the mission is over

The cutaway (land_yamato_cutaway, 0x0040EBC0) moves the bay to (0, -1000000, 0), turned as the world is, lights its hangar through mask 0x3B, and switches to one of the Yamato's landing's views at random, which it keeps (0x0051863C) and nothing reads. The ship stands in the bay at a fifth of the bay's width and height from their minimums and three tenths of its length, looking at the hangar's middle, stopped at a throttle of 0.5 with its power shared evenly.

Fix: where the bay's model lacks the hangar, the game reads past its list of parts; OpenReliant leaves the ship turned as it was.

Improvement: the angles come from std.math.atan2 rather than the engine's table (sr_atan2). OpenReliant has no parent frames, so the ship on the pad is put back on it each frame (ailand.hold), as the launches hold their ships (Launches).

Friendly fire

The player's hits on friends (friendly_fire.zig): object_damage and object_armor_damage hand a blow of the player's ship on a friendly object, a shot's, a Screamer's or a collision's, to friendly_fire_warning (0x00474C80), the armour's only while the object is not exploding, as component_damage does a shot's or a Screamer's on a friend's component. The damage after the difficulty's scaling mounts (friendly_fire_damage, 0x00562CEC), and once past 800 (0x004DC5EC) and 3000 game ticks after the last (friendly_fire_quiet_until, 0x00562CE8), Moose warns the player on the radio, one of ff_001 to ff_004, and the damage counts from nothing again. The count of warnings (friendly_fire_count, 0x00562CF0) would pick ff_005 to ff_008 for a second and ff_009 to ff_012 for a third, but the game takes a second back to the first, so those are never said. friendly_fire_reset (0x00474B20) clears the three as mission_run starts a mission.

Destroying a friend sends the player home: armour below zero from such a blow, but a collision with a pilot's pod that anything could harm, or a friend's component destroyed by a shot or a Screamer, calls player_friend_destroyed (0x00474E00), which marks the player's ship (+0x678, 1), as the script's FriendlyFire (0x57) does. Each frame after the orders, mission_frame (0x0049298C) runs friendly_fire_send_home (0x00474B40) for a player's ship in the action and marked: while the mission goes on and the ship's current order has no priority, the mission's ending becomes 6 (7 where the mark is 3), and the ship takes Friendly Fire (117), aimed as its current order is, and is sent off (flag 0x10000000).

Friendly Fire keeps its stage (+0x00) and the frame's tick it ends at (+0x04):

Stage What happens
0 Its init (0x00474E60) has Moose say one of ff_013 to ff_023; the player flies on (player_controls) for 300 ticks
1 For 700 ticks the ship is steered toward the carrier the player launched from (ai_steer, limit 1, no ease, no flags), the player's controls keeping 0.7 of the throttle and of each turn, the steering 0.3 of the turns
2 The order ends and the ship, no longer sent off, takes Land on the carrier, which lands it at once (Landing), and Jump Out at itself before it where the carrier is farther than 1000000; it is sent off again. The game also clears the carrier's flag 0x20

Fix: the game reads the carrier through a null pointer where the player launched from none; OpenReliant lets the ship fly on as the player has it, and then ends the order.

Not ported: a multiplayer game's side of it (#55). There, destroying a pilot's pod only a player could harm marks the ship 2 and tells the others, whose carrier then aborts the mission (abrt_001, said by pilot 27 for the Reliant and 28 for any other); and another player's ship sent off jumps out.

The Ripper

The Ripper (type 0x1F) carries cargo pods in the tractor beams of its four back pincers (airipper.cpp). Mission 1 has one lift sixteen pods onto a Mammoth at Fort Sherman: its script orders the first grab, and two triggers that resume where they stopped (InterruptTriggerCode) answer each RipperGrabbedObject with the next drop and each RipperDroppedObject with the next grab.

Two tables of 150 entries serve every Ripper, set up as a mission loads (0x0040FC90) and let go as it ends (0x0040FCF0). rippercargo (0x00518648) pairs a Ripper with what it carries, filled in turn from next_rippercargo (0x00518AFC); what a Ripper carries is the first entry naming it (0x00412340). A set of beams (0x74 bytes each at 0x00518B00, the next at 0x00518640) is four tractor beams (tractor_beam_mesh, over laser2), one from the first point of each pincer's first point list, the first two reaching for the middle of the pod's first pair of points and the last two for that of its second (SHP). An order shows them as it runs (0x00412200), aimed and faded as a tractor's beams are: each from its pincer's point in the pincer's frame, to the middle of its pair of the pod's points taken in the frame the pod's Cargo pod part hangs from (the frame's parent, +0x10), which for the shipped pods is the pod's own place, so the beams stay on a pod that is disabled and not framed. In OpenReliant, a set keeps where its beams come from and how solid they are, and drawing makes the beams as it first draws the set (airipper.GripShown) and keeps them apart from the game's sets (airipper.Drawing).

The Ripper's tracks play on every part of it that has them (0x0049A400): ready to grab, its forearms reaching out; grab pod, its pincers closing; and cabin turn. A step that waits for the forearms or the pincers played backwards waits for the root's first child's track to be back at its start; one that waits for the cabin to turn back waits for the Ripper Cabin part's. Each wait for the other players between the steps (0x00401000) passes at once in a single-player game. A Ripper comes to rest where its turning inputs and its throttle are within 0.025, and its rates of turn within 0.02; Ripper end drop object's last turn needs its inputs and rates below those values.

Ripper grabs target object (12) lifts its target aboard. Its init (0x0040FD10) makes the target invulnerable, takes the beams, flies the Ripper by motion_plain, held (attached), and has the two pass through each other. The Ripper stops 2500 above the target's component, in its frame, where the target names one; in mission 26 (Rules by mission number), 1000 below the target, which it lifts from below; else at the target. Its steps (0x0040FF80), each timed from its start:

Step What happens
1 The Ripper steers at the point (limit 0.3): at full throttle while more than 2000 beyond where it stops, at 0.2 nearer, and not at all within 2000 of the point, or 100 lifting from below; near it and not facing it within 0.7, it holds still. At rest, it plays ready to grab at 15
2 From below, it turns to face the target until at rest
3 150 ticks: the beams come on, as the square of the time; then the target is heard (sound 0x3D) and no longer frozen
4 300 ticks: the target is drawn halfway to 300 from the Ripper, toward it
5 500 ticks: the target turns to match the Ripper, easing (cosine_ease)
6 300 ticks: it is drawn the rest of the way; then heard (sound 0x3C), and the Ripper plays grab pod at 10
7 150 ticks; then the Ripper plays cabin turn at 4.5
8 500 ticks
9 The target's part Cargo pod hides and the Ripper's shows; the target is disabled and as invulnerable as before; the Ripper carries it and flies astern (motion_backward); the order ends, with the Ripper's RipperGrabbedObject

Should the target go first, the Ripper takes back its motion and the order ends. Its exit (0x00410B50) lets the Ripper go and frees the beams.

Make ripper drop what it's carrying (39) (0x00410B90, 0x00410C00) stops the Ripper, flies it by motion_plain, held. Where the order names a ship, the Ripper fits what it carries to it instead (order 112). Else, at rest (its speed and rates below 0.05, by their signs), it plays grab pod from 350 at -10, plays sound 0x3C on no object (-1), and lets go: what it carries stands where its own Cargo pod is, and shows in its place; the order ends, and Ripper end drop object (111) takes over. What it drops stays disabled.

Ripper end drop object (111) (0x00410E60, 0x00410E90) lets the Ripper go; it plays ready to grab from 350 at -6, then backs away astern at 0.2 for 50 ticks, and plays cabin turn from 400 at -4.5. Once its cabin is round, it turns, still flying astern, to put its tail to a point 10000 ahead of it (limit 2) until at rest, and flies on (motion_forward): neither it nor what it dropped passes through the other any more, it carries nothing, and it has its RipperDroppedObject.

Ripper attach cargo pod to Mammoth (112) (0x00411200, 0x00411420) fits what the Ripper carries to the component its target names. The Ripper flies astern to 2500 above the component, in its frame, or below it on a Sharov or a Boridin: at full throttle beyond 2000, then by motion_plain at 0.2 until within 300. At rest, it faces the component (limit 2) and plays grab pod from 350 at -10. Then the ship is no longer frozen, and the pod stands where the Ripper's own was; over 150 ticks the beams come on, and it is heard (sound 0x3D), shown in place of the Ripper's and enabled. Over 1000 ticks it eases onto the component, keeping its turn for the first 0.15 of the time, then turning, easing, until half of it, to the component's orientation turned a quarter back about its X on a Mammoth, a Sharov or a Boridin and about its Z on another. Heard as it arrives (sound 0x3C), the Ripper plays ready to grab from 350 at -6, then cabin turn from 400 at -4.5, and once its cabin is round turns to put its tail to a point 10000 behind it. Then the pod is gone into the ship: disabled, no longer targetable and hidden, and the component shows; the Ripper flies on (motion_forward) and has its RipperDroppedObject, and it carries nothing from then on.

A Mammoth's cargo slots stay hidden until the Ripper fills them because the mission's script hides them: it disables the components as it makes the ship (DisableObject), which hides each assembly. Mission 1 disables a squad that holds the Loadup Mammoth's components 3 to 24 as it creates the ship's flight group.

Fix: where the next rippercargo entry is still taken, the game stops with "Ripper Grab AI error: Too many rippers doing their stuff at once."; where a Ripper carries nothing, it stops with "Can't find the object the ripper grabbed!"; and for a component of a type its table of the Mammoth and the Stalag lacks, it reads the grab point's height past the table's end. OpenReliant logs the first and takes the entry, ends the order for the second, and stands 2500 above the component for the third.

Fix: Ripper attach cargo pod to Mammoth never clears the Ripper's rippercargo entry once the pod is fitted, so what the Ripper carries stays that pod: each later drop or fit by the same Ripper takes the pod it fitted first out of the ship again, while the pod last grabbed stays hidden where it was lifted, and RipperDroppedObject names the wrong pod. OpenReliant clears the entry, as Ripper end drop object does.

Fix: as its cabin turns back, Ripper attach cargo pod to Mammoth waits for the root's first child, a forearm with no cabin turn track that the step before has already seen at rest, so the Ripper turns away while its cabin is still turning. OpenReliant waits for the Ripper Cabin part, as Ripper end drop object does.

Fix: Make ripper drop what it's carrying takes a negative index other than -1 for a ship's slot, and its sound 0x3C, which follows an object, plays on none; both read before the objects' table. OpenReliant takes such an index for none, and plays nothing.

Improvement: the pod's turn as it fits divides the share past 0.15 by the span to 0.5, where the game multiplies it by its rounded reciprocal (0x004DC540).

Improvement: the beams' orders place what they carry once a tick, and the game draws it there; OpenReliant draws it on between the ticks, as far as a tick of the step would take it (create.Slot.glide), as the target is drawn in (steps 4 and 6) and as the pod is carried onto the component. --no-smooth-motion leaves it where each tick places it.

Picking a fight

Find New Target's walk visits each ship with 0x0040AE90, which passes over one the ship cannot aim at, cloaked or not. It counts the objects with stats whose current order is aimed at that ship and component: Fight, and Mill. The ship then weighs as one to fight the square of the distance times one more than the ships that fight it, and as one to mill round, times one more than those and one more than those that mill round it. The lightest of each is kept, but to fight only a ship that is not cloaked, not the target set aside for the searcher (+0x6AC, which the radio's menu sets to the player's target for 3000 ticks, 0x0045517F), and for a fighter one that fewer than two others fight. A set-aside target whose time is up (+0x6B0 before game_ticks) is set aside no more, but is passed over as one to fight this walk still.

Quirk: the game weighs the one to fight by 0.7 more (0x004DC484) where the count of objects it has just walked through equals the player's slot, which never happens; it looks meant to favour the player's ship (#314). OpenReliant weighs as the game does.