The cloak¶
cloak.cpp hides a ship behind a shimmer while its hull fades from sight. Its asserting code runs
from 0x00462B80 to 0x00463773. OpenReliant is
game/cloak.zig, which also holds the
countermeasures. Unverified: that object_uncloak (0x00463780)
to cloak_node_cloaks (0x00463C30), which lie after that code, and cloak_node_reveal
(0x004629D0), which lies before it among the countermeasures', are this file's.
Which objects cloak¶
An object can cloak where its model's header has flag 0x02 (shp.md). For
such a model, and for every ship type's model in a multiplayer mission, model_load builds two
more sets of meshes and gives each part colours of its own
(Meshes):
- The see-through set: the part's own meshes, each group's first pass blended by alpha.
- The shimmer set (
cloak_mesh_build,0x004A3CB0): one group over every polygon, added, lit under a hardware renderer, its texturecloak64with the model's prefix letter. The shimmer's object carries texture coordinates of its own, which the cloak turns each frame.
The part's own colours (+0x110, flag 0x80000) start cleared and stay so until a hit shows the
hull through the cloak. The renderer takes them in place of the mesh's baked colours, so the static
lights such a model carries never show, which OpenReliant fixes
(Static lights).
Beside the ships that cloak, the models of the guns and the missile pods have the flag, and cloak
with the ship that carries them. On the Kafelnikof only seven parts cloak
(kafelnikof_cloaking_parts, 0x004E1998, 20 bytes a name): Kaf bot vent, Kaf comms twr,
Kaf ext vent, Kaf frnt vent, Kaf land plat, Kaf shield gen and Kaf top vent
(cloak_node_cloaks).
The cloak's record¶
object_cloak allocates 0x2C bytes at GameObject + 0x63C, which stay until the cloak has gone.
| Off | Field |
|---|---|
0x00 |
Whether it is going |
0x01 |
Whether it is still coming on or going, which refuses a change of mind |
0x04 |
The shimmer's strength, 0 to 1 |
0x08 |
How solid the hull is, 0 to 1 |
0x0C |
The tick it began to come on |
0x10 |
The tick it began to go |
0x14 |
The tick a hit last showed the hull |
0x1C, 0x20 |
The ticks the shimmer was drawn at, the frame before and this one |
0x24, 0x28 |
The same for the hull |
Coming on and going¶
object_set_cloak (0x00463560) cloaks or uncloaks an object whose model can cloak, where it isn't
already so, through object_toggle_cloak (0x00463600). For the player's ship it also sets the
display's cloak (cloak_state, Devices), whether or not the toggle went
through. Every ship whose current order is Launch from the object follows it.
object_toggle_cloak does nothing while the cloak is still coming on or going. Otherwise it
uncloaks a cloaked object and cloaks another:
object_cloak(0x00463640) sets the object's flag0x100, makes the record, from now, and draws each part that cloaks see-through (cloak_node_see_through,0x00462B80) with a shimmer (cloak_node_shimmer,0x00462EB0) whose texture coordinates are random, 0 to 1. The object soundsCLOAK01, on the player's own voices for the player's ship.object_uncloak(0x00463780) marks the cloak going from now, and soundsCLOAK01the same way.
cloak_frame (0x00463810) runs in mission_frame's pass that draws the objects, for each object
with a cloak, hidden or not. The ticks it has drawn the shimmer and the hull at move back a frame.
For 250 ticks from the start of the change, the shimmer's strength is the share of them gone, and
the hull's solidity the rest; going, the other way round. Once the cloak has come on, every part's
hull is clear and its shimmer whole, as 250 ticks since they were drawn would leave them. Once it
has gone, each part has its own meshes back, solid (cloak_node_restore, 0x00462D30), and the
cloak is dropped.
cloak_drop (0x00463420) frees the parts' callbacks (cloak_node_free, 0x00463470), clears
the flag and frees the record, leaving the parts as they are. An exploding ship's blast
(0x0046C980) and burst (0x00471DB0) drop its cloak first, and object_free drops what it has.
What is drawn¶
The pass draws a cloaked object with its lights and engine glows out, its parts through two per-object callbacks the mesh pipeline runs after culling and before lighting. A callback that returns non-zero leaves its object undrawn.
The hull (cloak_hull_callback, 0x00463B90) is left undrawn under the software renderer. Unless
the game is paused, or the part doesn't cloak, cloak_hull_fade (0x004632F0) runs for the ticks
since it was last drawn: while the hull is clear, what hits showed of it fades by 0.01 a tick;
otherwise its alpha is the hull's solidity. The see-through groups blend by the vertex alpha, which
is the object's colour's plus its own colours'.
The shimmer (cloak_shimmer_callback, 0x00463B20) stands where its part does. Unless the game
is paused it is left undrawn while its strength is nothing, and on the Kafelnikof; otherwise
cloak_shimmer_swirl (0x00463050) turns each texture coordinate about (0, 0) by 0.008 a tick,
over its squared distance from there, for the ticks since it was last drawn, and gives every
vertex the shimmer's colour, unlit. The colour (cloak_colour_at, 0x004631E0) eases by
cosine_ease from black to (185, 203, 82) / 256 as the strength reaches 0.3, then to
(0, 0, 80) / 256 at full strength. cloak_init (0x00463500) fills a table of 1024 of them
(cloak_colours, 0x0054142C), which cloak_colour (0x004632B0) reads, and holds cloak64 for
the mission (cloak_texture, 0x00541428), which cloak_free (0x00463550) lets go.
In mission_frame's pass before the camera's frame, a cloaked object other than the Kafelnikof
wobbles while its cloak changes (cloak_wobble, 0x004639B0). With t the share of the 250
ticks gone, its frame is multiplied by three shears in turn, each 0.15 * sin(pi * t) times
sin(20 * t), sin(26 * t + 2.8) and sin(14 * t + 0.9): of Y by X (mat3_shear_x,
0x004C24F0), of X by Y (mat3_shear_y, 0x004C2550) and of X by Z (mat3_shear_z,
0x004C25B0).
Hits¶
cloak_reveal (0x00463AF0) shows a cloaked object's hull where a hit strikes it. It sets the
record's hit tick, and cloak_node_reveal (0x004629D0) walks the parts that cloak: each vertex of
a part's drawn level within 1.123 times the object's radius of the point gains alpha in the part's
own colours of (reach - distance) / (0.45 * radius), no more than 1 and up to 1 in all. On the
Kafelnikof a part goes by its mesh's smallest extent in place of the radius. What it shows then
fades as the hull is drawn.
- A shot spent on the shields (
bullet_hit) shows it, and the shields don't flare. - A shot on a component, but a Huge Gun's, shows it once the component takes its damage.
- The Nova Cannon's beam shows it on each part it strikes (
nova_strike_parts). - A shield flare (
shield_flare,0x0049F1E0) on a cloaked ship shows it, and nothing more.
Who cloaks¶
The player cloaks with CLOAK SHIP (0x00413CB2, once a press), outside view 13, on a ship whose
model can cloak: the ship uncloaks where it is cloaked and cloaks where it isn't
(player_cloak_set, 0x004153E0). Unless the cloak is still coming on or going, the display
beeps (hud_beep 4 on, 5 off) and Betty says so (0x10, 0x11). player_cloak_set does nothing
in a multiplayer game, nor where the display has no cloak. The ship also uncloaks:
- With FIRE LASERS held, in place of firing, outside a multiplayer game.
- With LAUNCH MISSILE, in place of the launch, once the armed missile's lock allows it, outside a multiplayer game; and as the Kamov lets go of the craft it carries.
- As the display's cloak runs out of charge (Devices).
- With EJECT (Ejection), and as a player's ship jumps out (Jump Out,
0x00416D50, Jumps).
A ship flying the Fight order cloaks as its maneuver asks (fight_update_cloak, 0x00409EC0): the
maneuver's Cloak command asks for the cloak 500 ticks on (Maneuvers), and a new
maneuver asks for none until it does. Each update after calling for help, a ship whose model can
cloak cloaks once that tick has come, and uncloaks while none is asked for. The mission's
Cloak_ship command (cmd_Cloak_ship, 0x00459F60) cloaks or uncloaks a ship too, and so does
the Toggle Cloak order, the other way from how it stands (order_toggle_cloak, 0x0040B640,
Orders).
In OpenReliant¶
cloak.zig ports the cloak, with these differences:
- OpenReliant updates a part's shimmer and hull as it adds them to the scene, not after culling, so a part out of sight changes too.
- The display has no world to reach the ship through, so as the cloak's charge runs out OpenReliant
marks it spent (
hud.State.uncloakSpent), and the next frame's orders uncloak the ship, a frame late. - Improvement: the shimmer's colour is worked out for its strength, not read from the table of 1024.
- Improvement: OpenReliant's shadows (Shadows) fade with the
hull: a cloaking part's see-through hull casts as strongly as it is solid, and none once it is
clear. The ship the camera sits in isn't drawn, so its parts take the hull's solidity for their
shadow alone (
cloak.shadeUnseen).
Cloaking posts the ship's Cloaked event first, and uncloaking a ship with a cloak its Decloaked (Script VM).
Not ported: the mission's Cloak_ship
(#281); the Kamov's craft; and the multiplayer
game's cloak (#55).