From c408fc6cf6ce71c8c9d504571b65fc36a3fc5ef9 Mon Sep 17 00:00:00 2001 From: Anachronaut Date: Fri, 4 Sep 2026 11:49:38 -0400 Subject: [PATCH] Thruster flames, so a watcher can see what the pilot is doing Every reading on this screen is a number drawn as a bar - how fast sideways, how fast down, how much sky, how much tank - and all of it says what is happening TO the lander. None of it says what the pilot is doing about it, so somebody watching over a shoulder has to read gauges to work out that a thruster is even lit. A plume hangs off whichever side the engine is pushing from: under the lander to lift, over it to retro, and on the far side from the way it is being pushed sideways, since that is the side the gas leaves. One tile does up and down, because a vertical flip turns one into the other, and a second does the sides, because a flip cannot rotate a tile a quarter turn. Twenty four pixels each, on purpose, so a count of them means something. HELD, NOT FIRED. The engine fires one frame in ten, because that is the tick gravity is applied on, and a flame that honest would be one frame of light six times a second - a fault lamp, not a rocket. What is drawn is the button being down, which is the truthful answer to "is the pilot burning": the tick is how the sum gets done, not what is happening. An empty tank draws nothing, and nor does the retro thruster on the ground, because in both cases the button really is doing nothing. The second of those was a lie the first version told. Red, and that is by elimination again: white is the ceiling warning, cyan the landing pads, magenta the instruments, blue the station, yellow the lander itself - and the lander is counted as exactly forty pixels of yellow, so a yellow flame would have broken it. Three checks, each seen to fail on its own break. Two things the fixtures taught: a lander placed at the world's origin sits BEHIND the two row window, which reads exactly like a flame that is not drawn; and red has to be looked for in a box round the lander rather than a column, because the speed bars go red and one of the four pads is red too. --- Programs/CosmOS/Apps/Lander.asm | 306 +++++++++++++++++++++++++++- Programs/CosmOS/README.md | 2 +- Tests/expected/cosmosCrossDisk.out | 2 +- Tests/expected/cosmosDrives.out | 2 +- Tests/expected/cosmosFault.out | 2 +- Tests/expected/cosmosFlip.out | 2 +- Tests/expected/cosmosGrid.out | 2 +- Tests/expected/cosmosMonitor.out | 2 +- Tests/expected/cosmosMonitorRun.out | 2 +- Tests/expected/cosmosRun.out | 2 +- Tests/expected/cosmosSlowDisk.out | 2 +- Tests/expected/cosmosSprite.out | 2 +- Tests/video.sh | 88 ++++++++ 13 files changed, 404 insertions(+), 12 deletions(-) diff --git a/Programs/CosmOS/Apps/Lander.asm b/Programs/CosmOS/Apps/Lander.asm index 9d2ed10..d8a8280 100644 --- a/Programs/CosmOS/Apps/Lander.asm +++ b/Programs/CosmOS/Apps/Lander.asm @@ -106,6 +106,7 @@ everyFrame: CALL touchdown CALL dock CALL showLander + CALL showFlames CALL showStation CALL showDrift CALL showFall @@ -184,6 +185,49 @@ putTiles: OUTA 0xE7 INIA 0x01 OUTA 0xE8 ; Blit. + + OUTA 0xE0 + SETD.1 FlameDownArtAt + SETD.0 FlameDownArt + STD.0.1 + LDA.1 + OUTA 0xE1 + INCD.1 + LDA.1 + OUTA 0xE2 + INIA 0d4 + OUTA 0xE3 + INIA 0x32 + OUTA 0xE4 + INIA 0xC0 + OUTA 0xE5 ; Tile 203, the plume that hangs under a lifting lander. + RSTA + OUTA 0xE6 + INIA 0x40 + OUTA 0xE7 + INIA 0x01 + OUTA 0xE8 + + OUTA 0xE0 + SETD.1 FlameSideArtAt + SETD.0 FlameSideArt + STD.0.1 + LDA.1 + OUTA 0xE1 + INCD.1 + LDA.1 + OUTA 0xE2 + INIA 0d4 + OUTA 0xE3 + INIA 0x33 + OUTA 0xE4 + RSTA + OUTA 0xE5 ; Tile 204, the same thing lying on its side. + OUTA 0xE6 + INIA 0x40 + OUTA 0xE7 + INIA 0x01 + OUTA 0xE8 RET ; ---- A moon, one column at a time ---- @@ -1448,13 +1492,18 @@ showLander: LDB.1 CCF ADD + SETD.1 ShipTopLow + STQ.1 OUTQ 0xE9 SETD.0 PixelHigh LDA.0 + SETD.1 ShipYAdd INCD.1 LDB.1 ADD ; And the carry, which is the other half of the shift. - OUTQ 0xE9 + SETD.1 ShipTopHigh + STQ.1 + OUTQ 0xE9 ; Kept as well as sent: the flames hang off this row. RET ; ---- Green if it would survive, red if it would not ---- @@ -2080,6 +2129,198 @@ easeAcrossWrap: STQ.0 ; Round the moon, the way every other step along it is. RET +; ---- Flames, so a watcher can see what the pilot is doing ---- +; +; Every reading on this screen is a NUMBER drawn as a bar: how fast sideways, how fast down, +; how much sky, how much tank. All of it says what is happening TO the lander and none of it +; says what the pilot is doing about it, so somebody watching over a shoulder has to read +; gauges to work out that a thruster is even lit. +; +; ---- Shown while the button is held, not while the engine fires ---- +; +; The engine fires one frame in ten, because that is the tick gravity is applied on. A flame +; that honest would be one frame of light six times a second, which is a fault lamp and not a +; rocket. What is drawn is the BUTTON BEING DOWN, and that is the truthful answer to "is the +; pilot burning" - the tick is how the sum gets done, not what is happening. +; +; An empty tank draws nothing, because then the button really is doing nothing. +showFlames: + INIA 0x70 + SETD.0 FlameSlot + STA.0 + INIA 0xCB + SETD.0 FlameTile + STA.0 + SETD.0 ShipX + SETD.1 FlameXLow + CALL copyWord ; Straight under the lander, whichever way it is pushing. + + SETD.0 Fuel + LDA.0 + BRA flameNoLift + SETD.0 Held + LDA.0 + INIB 0x08 ; Up, so the plume is UNDER the lander, pushing it away. + AND + BRQ flameTryDown + SETD.0 FlameAway + SETD.1 FlameOffLow + CALL copyWord + CALL flameBelow + RSTA + BRI flameLiftFlagged +flameTryDown: + SETD.0 Landed + LDA.0 + BNA flameNoLift ; The retro thruster does nothing on the ground, so it shows nothing. + SETD.0 Held + LDA.0 + INIB 0x04 ; Down, so it is over the lander and turned upside down. + AND + BRQ flameNoLift + SETD.0 FlameBack + SETD.1 FlameOffLow + CALL copyWord + CALL flameBelow + INIA 0x02 ; VFLIP. +flameLiftFlagged: + SETD.0 FlameFlags + STA.0 + INIA 0x11 + BRI flameLiftSized +flameNoLift: + RSTA +flameLiftSized: + SETD.0 FlameSize + STA.0 + CALL putFlame + + INIA 0x80 + SETD.0 FlameSlot + STA.0 + INIA 0xCC + SETD.0 FlameTile + STA.0 + SETD.0 ShipTopLow + SETD.1 FlameYLow + CALL copyWord + + SETD.0 Fuel + LDA.0 + BRA flameNoSide + SETD.0 Held + LDA.0 + INIB 0x02 ; Left, so the plume is on the RIGHT, pushing that way. + AND + BRQ flameTryRight + SETD.0 FlameAway + SETD.1 FlameOffLow + CALL copyWord + CALL flameBeside + RSTA + BRI flameSideFlagged +flameTryRight: + SETD.0 Held + LDA.0 + INIB 0x01 ; Right, so it is on the left and turned about. + AND + BRQ flameNoSide + SETD.0 FlameBack + SETD.1 FlameOffLow + CALL copyWord + CALL flameBeside + INIA 0x01 ; HFLIP. +flameSideFlagged: + SETD.0 FlameFlags + STA.0 + INIA 0x11 + BRI flameSideSized +flameNoSide: + RSTA +flameSideSized: + SETD.0 FlameSize + STA.0 + CALL putFlame + RET + +; The lander's row, plus the offset - a whole word, so its sign comes along with it. +flameBelow: + SETD.0 ShipTopLow + LDA.0 + SETD.1 FlameOffLow + LDB.1 + CCF + ADD + SETD.1 FlameYLow + STQ.1 + SETD.0 ShipTopHigh + LDA.0 + SETD.1 FlameOffHigh + LDB.1 + ADD ; And the carry, which is the other half of it. + SETD.1 FlameYHigh + STQ.1 + RET + +; And the lander's column, the same way. +flameBeside: + SETD.0 ShipX + LDA.0 + SETD.1 FlameOffLow + LDB.1 + CCF + ADD + SETD.1 FlameXLow + STQ.1 + INCD.0 + LDA.0 + SETD.1 FlameOffHigh + LDB.1 + ADD + SETD.1 FlameXHigh + STQ.1 + RET + +; One flame, out of the block above. +putFlame: + INIA 0d4 + OUTA 0xE3 + INIA 0xC0 + OUTA 0xE4 + SETD.0 FlameSlot + LDA.0 + OUTA 0xE5 + SETD.0 FlameTile + LDA.0 + OUTA 0xE9 + INIA 0x01 + OUTA 0xE9 ; Scheme one, red, which is what a rocket looks like. + SETD.0 FlameXLow + LDA.0 + OUTA 0xE9 + SETD.0 FlameXHigh + LDA.0 + OUTA 0xE9 + SETD.0 FlameYLow + LDA.0 + OUTA 0xE9 + SETD.0 FlameYHigh + LDA.0 + OUTA 0xE9 + SETD.0 FlameSize + LDA.0 + OUTA 0xE9 ; Nought when nothing is held, which is the off switch. + SETD.0 FlameFlags + LDA.0 + OUTA 0xE9 + RSTA + OUTA 0xE9 + OUTA 0xE9 + OUTA 0xE9 + OUTA 0xE9 ; Its natural size. + OUTA 0xE9 ; And no depth. + RET + ; ---- A key, or a button ---- ; ; Somebody flying on a controller should not have to reach for the keyboard to say "yes, I @@ -3190,6 +3431,38 @@ Carrying: 0x00 Landed: 0x00 +; Where showLander put the lander this frame, which is where the flames have to meet it. +ShipTopLow: + 0x00 +ShipTopHigh: + 0x00 + +; What a flame is being asked to be, before it is written out. +FlameSlot: + 0x00 +FlameTile: + 0x00 +FlameXLow: + 0x00 +FlameXHigh: + 0x00 +FlameYLow: + 0x00 +FlameYHigh: + 0x00 +FlameFlags: + 0x00 +FlameSize: + 0x00 +FlameOffLow: + 0x00 +FlameOffHigh: + 0x00 +FlameAway: + 0x08 0x00 ; A tile past the lander, which is where a plume comes out. +FlameBack: + 0xF8 0xFF ; And a tile the other way, for the engine pointing the other way. + ; Holding onto the station, which is the same kind of rest as sitting on the ground. Docked: 0x00 @@ -3298,6 +3571,37 @@ StationArt: StationArtAt: #Reserve 0d2 +; ---- The plume, which hangs off whichever side the engine is pushing from ---- +; +; Widest where it meets the lander and tapering away, so it reads as coming OUT rather than +; sitting there. One tile for up and down, since a vertical flip turns one into the other, and +; a second for the sides, because a flip cannot rotate a tile a quarter turn. +; +; Twenty four pixels each, on purpose: it makes a count of them mean something. +FlameDownArt: + 0x00 0x01 0x01 0x01 0x01 0x01 0x01 0x00 + 0x00 0x01 0x01 0x01 0x01 0x01 0x01 0x00 + 0x00 0x00 0x01 0x01 0x01 0x01 0x00 0x00 + 0x00 0x00 0x01 0x01 0x01 0x01 0x00 0x00 + 0x00 0x00 0x00 0x01 0x01 0x00 0x00 0x00 + 0x00 0x00 0x00 0x01 0x01 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 +FlameDownArtAt: + #Reserve 0d2 + +FlameSideArt: + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 + 0x01 0x01 0x00 0x00 0x00 0x00 0x00 0x00 + 0x01 0x01 0x01 0x01 0x00 0x00 0x00 0x00 + 0x01 0x01 0x01 0x01 0x01 0x01 0x00 0x00 + 0x01 0x01 0x01 0x01 0x01 0x01 0x00 0x00 + 0x01 0x01 0x01 0x01 0x00 0x00 0x00 0x00 + 0x01 0x01 0x00 0x00 0x00 0x00 0x00 0x00 + 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 +FlameSideArtAt: + #Reserve 0d2 + LanderArt: 0x00 0x00 0x01 0x01 0x01 0x01 0x00 0x00 0x00 0x01 0x01 0x01 0x01 0x01 0x01 0x00 diff --git a/Programs/CosmOS/README.md b/Programs/CosmOS/README.md index 5b2c5f9..b597f9f 100644 --- a/Programs/CosmOS/README.md +++ b/Programs/CosmOS/README.md @@ -727,7 +727,7 @@ from every assembly file in it. Several are old programs written for the bare ma | Grid | The first program to use the screen as a screen. Redefines a tile above the font, fills all 128 map rows, and scrolls it diagonally a pixel at a time. | | Sprite | Moves a ball across the shell's own text, writing not one byte of the map to do it. It leaves the sprite in the table on the way out, because clearing them is the system's job - see below. | | Pad | Says what the controllers are doing, printing a line whenever one changes. It tells apart the three things that look identical from inside a game that is not responding: a pad nobody noticed, a pad mapped to nothing, and a mapping that is wrong. | -| Lander | Lunar Porter, rung one: a lander over a moon that wraps. Flown with a controller if there is one - a held thruster burns every tick it is held for - and with the arrow keys if there is not, where one press is one burn and that is the most the console can say. A bar at the bottom is the sideways drift, drawn as a sprite stretched to the speed - a moon has no air, so a drift never stops by itself and stopping one means cancelling it exactly, which is hard to do blind. It lands or crashes on arrival, and what decides is the speed at the moment it touches: gentler than three quarters of a pixel a frame downwards and half of one sideways, or it is a lander on its side. A fuel gauge sits in the window layer, where the moon turning underneath cannot scroll it away, and every thruster costs a unit of fuel every tick it fires. An empty tank is not an ending: it is a lander still flying that can no longer do anything about where. Four landing pads are carved into the moon after it is generated, levelled to whatever height their first column happened to have, and marked in cyan by an attribute rather than a tile of their own. The lander starts above one, because that is where a porter's day begins. Each pad is a base, told apart by the colour it is drawn in, and landing at one either loads cargo for the base across the moon or delivers what is aboard and pays eighty units of fuel. A landing is not an ending: the lander rests where it is until the throttle opens again, and A or Start says "read it" as readily as a key does. What a base says goes in the window rather than out of the console: the console draws into the map, so a message printed while flying is one the lander then flies over, and printing scrolls the whole world up a row. Opening the throttle wipes the line. Two bars read the speeds and are green while a landing would survive and red while it would not, so "can I put down" is a glance rather than a sum. A third runs up the left edge and is how much sky is under the lander, half a pixel of bar to a pixel of it; it reads nought the moment the lander is down, and it exists because the orbit takes the lander off the top of the screen and a panel that only works while the ground is in sight is no use above it. And gravity here is the pull MINUS the swing outwards, which is what makes an orbit rather than a one way trip: under four pixels a frame sideways the pull wins and the lander falls, over it the swing wins and the lander climbs, and at it they cancel and it circles. Falling buys sideways speed and climbing spends it - at a rate set by the product of the two, so the trade stops by itself as the sideways speed runs out - and that is the cycle: a fall carries the lander past orbital speed and becomes a climb, the climb pays it back and becomes a fall, periapse and apoapse, round and round. Orbital speed is marked on the drift bar, sixty four pixels either side of the middle, because a number nothing points at is folklore. Gravity never falls off and the moon wraps, so a circular orbit is the same length at any height and one orbital speed serves everywhere. There is a ceiling 192 pixels above the world's origin, which is the top of the wide view - it was sixty four while sixty four was all the sky a forty column screen had over the origin, which was a fact about the view rather than about the world. It pins rather than ends: leaving upward is recoverable, so the lander is held there and told so, and one sideways is spent every tick it tries to climb - without that the pin wiped the climb, the trade saw neither fall nor climb, and the speed pushing it up never changed. What kills you out here is running dry a long way from the ground. B, or z on the keyboard, swaps the screen between forty columns and eighty, which is the same map, the same cells and the same engine at half the size: nearly two thirds of the moon at once for the orbit, and twice as big for the landing. The twenty extra rows that buys go to the SKY and not to the moon - the wide view starts twenty four rows above the world's origin rather than at it, so the ground sits near the bottom and the whole flyable band is on the screen. Drawn down from the origin instead it was 71 per cent rock, which is a zoom that shows you more of the thing you cannot fly through. A lander at the ceiling is off the top of a forty column screen, which is where the orbit lives and why the altitude bar had to exist; zoomed out it is in the picture. And there is a station up there, twelve rows above the world's origin - about two thirds of the way from the ground to the ceiling, clear of work near the surface - going round at orbital speed - which needs no physics of its own, because a body at 64 sixteenths is exactly what a circular orbit IS under these rules, at any height, since gravity never falls off and the moon wraps. It is off the top of a forty column screen too, so it is somewhere to go that the zoom is needed to see. Docking it is landing on something that is moving: close enough, and slow enough RELATIVE TO THE STATION, or it is a wreck. Its speed is orbital speed, which is the number the marks on the drift bar point at, so the instrument for it was on the screen before there was anything to dock with. A dock pays eighty units of fuel and holds the lander a tile under the station until a thruster lets go, sliding it into line at half a pixel a frame rather than snapping - a dock is allowed eight pixels out either way, so putting the lander exactly in place the instant it took hold moved it a whole tile in one frame, right at the moment the player was being told they had been careful. It settles squarely under the port, which it did not used to: the lander is drawn from half a screen LESS HALF A TILE, because that is what centres it, and the station was drawn from half a screen exactly, so a perfectly flown dock still looked four pixels out. Getting there is a two burn manoeuvre and not a straight line: climbing SPENDS sideways speed, so a lander cannot rise while matched - it arrives slower than orbital every time, and has to raise the far side of its orbit and then circularise at the top. Which is also why it reads /lander.state if the disk has one: sixteen bytes of position, velocity, station, fuel and screen, laid straight over the numbers it would otherwise start with, so a rendezvous can be examined without flying one first. A disk without the file is the game as it always was. The buffer is a whole block wide because osFileRead lands a file in blocks of 256, and sixteen bytes of room for it wrote over everything that followed. Down is a retro thruster at half the strength of up - one sixteenth a tick against two, which is exactly gravity's own step, so it can stop a climb and hurry a descent and can never turn a landing approach into a crash faster than letting go would. Arresting a rise otherwise meant a sideways burn and a wait for the orbit to come round, which is how a rendezvous really is flown and a lot to ask of somebody who has not flown one. It does nothing at all to a lander on the ground, which is not politeness: touchdown has already had its say and will not speak again, so without that guard a landed lander holding it goes straight through the moon. | +| Lander | Lunar Porter, rung one: a lander over a moon that wraps. Flown with a controller if there is one - a held thruster burns every tick it is held for - and with the arrow keys if there is not, where one press is one burn and that is the most the console can say. A bar at the bottom is the sideways drift, drawn as a sprite stretched to the speed - a moon has no air, so a drift never stops by itself and stopping one means cancelling it exactly, which is hard to do blind. It lands or crashes on arrival, and what decides is the speed at the moment it touches: gentler than three quarters of a pixel a frame downwards and half of one sideways, or it is a lander on its side. A fuel gauge sits in the window layer, where the moon turning underneath cannot scroll it away, and every thruster costs a unit of fuel every tick it fires. An empty tank is not an ending: it is a lander still flying that can no longer do anything about where. Four landing pads are carved into the moon after it is generated, levelled to whatever height their first column happened to have, and marked in cyan by an attribute rather than a tile of their own. The lander starts above one, because that is where a porter's day begins. Each pad is a base, told apart by the colour it is drawn in, and landing at one either loads cargo for the base across the moon or delivers what is aboard and pays eighty units of fuel. A landing is not an ending: the lander rests where it is until the throttle opens again, and A or Start says "read it" as readily as a key does. What a base says goes in the window rather than out of the console: the console draws into the map, so a message printed while flying is one the lander then flies over, and printing scrolls the whole world up a row. Opening the throttle wipes the line. Two bars read the speeds and are green while a landing would survive and red while it would not, so "can I put down" is a glance rather than a sum. A third runs up the left edge and is how much sky is under the lander, half a pixel of bar to a pixel of it; it reads nought the moment the lander is down, and it exists because the orbit takes the lander off the top of the screen and a panel that only works while the ground is in sight is no use above it. And gravity here is the pull MINUS the swing outwards, which is what makes an orbit rather than a one way trip: under four pixels a frame sideways the pull wins and the lander falls, over it the swing wins and the lander climbs, and at it they cancel and it circles. Falling buys sideways speed and climbing spends it - at a rate set by the product of the two, so the trade stops by itself as the sideways speed runs out - and that is the cycle: a fall carries the lander past orbital speed and becomes a climb, the climb pays it back and becomes a fall, periapse and apoapse, round and round. Orbital speed is marked on the drift bar, sixty four pixels either side of the middle, because a number nothing points at is folklore. Gravity never falls off and the moon wraps, so a circular orbit is the same length at any height and one orbital speed serves everywhere. There is a ceiling 192 pixels above the world's origin, which is the top of the wide view - it was sixty four while sixty four was all the sky a forty column screen had over the origin, which was a fact about the view rather than about the world. It pins rather than ends: leaving upward is recoverable, so the lander is held there and told so, and one sideways is spent every tick it tries to climb - without that the pin wiped the climb, the trade saw neither fall nor climb, and the speed pushing it up never changed. What kills you out here is running dry a long way from the ground. B, or z on the keyboard, swaps the screen between forty columns and eighty, which is the same map, the same cells and the same engine at half the size: nearly two thirds of the moon at once for the orbit, and twice as big for the landing. The twenty extra rows that buys go to the SKY and not to the moon - the wide view starts twenty four rows above the world's origin rather than at it, so the ground sits near the bottom and the whole flyable band is on the screen. Drawn down from the origin instead it was 71 per cent rock, which is a zoom that shows you more of the thing you cannot fly through. A lander at the ceiling is off the top of a forty column screen, which is where the orbit lives and why the altitude bar had to exist; zoomed out it is in the picture. And there is a station up there, twelve rows above the world's origin - about two thirds of the way from the ground to the ceiling, clear of work near the surface - going round at orbital speed - which needs no physics of its own, because a body at 64 sixteenths is exactly what a circular orbit IS under these rules, at any height, since gravity never falls off and the moon wraps. It is off the top of a forty column screen too, so it is somewhere to go that the zoom is needed to see. Docking it is landing on something that is moving: close enough, and slow enough RELATIVE TO THE STATION, or it is a wreck. Its speed is orbital speed, which is the number the marks on the drift bar point at, so the instrument for it was on the screen before there was anything to dock with. A dock pays eighty units of fuel and holds the lander a tile under the station until a thruster lets go, sliding it into line at half a pixel a frame rather than snapping - a dock is allowed eight pixels out either way, so putting the lander exactly in place the instant it took hold moved it a whole tile in one frame, right at the moment the player was being told they had been careful. It settles squarely under the port, which it did not used to: the lander is drawn from half a screen LESS HALF A TILE, because that is what centres it, and the station was drawn from half a screen exactly, so a perfectly flown dock still looked four pixels out. Getting there is a two burn manoeuvre and not a straight line: climbing SPENDS sideways speed, so a lander cannot rise while matched - it arrives slower than orbital every time, and has to raise the far side of its orbit and then circularise at the top. Which is also why it reads /lander.state if the disk has one: sixteen bytes of position, velocity, station, fuel and screen, laid straight over the numbers it would otherwise start with, so a rendezvous can be examined without flying one first. A disk without the file is the game as it always was. The buffer is a whole block wide because osFileRead lands a file in blocks of 256, and sixteen bytes of room for it wrote over everything that followed. A plume hangs off whichever side the engine is pushing from - under the lander to lift, over it to retro, and on the far side from the way it is being pushed sideways, since that is where the gas leaves. Every other reading here is a number drawn as a bar and all of them say what is happening TO the lander, so without this a watcher has to read gauges to work out that a thruster is even lit. It is drawn while the BUTTON IS HELD rather than while the engine fires: the engine fires one frame in ten, because that is the tick gravity is applied on, and a flame that honest is a fault lamp rather than a rocket. An empty tank draws none, and neither does the retro thruster on the ground, because in both cases the button really is doing nothing. Down is a retro thruster at half the strength of up - one sixteenth a tick against two, which is exactly gravity's own step, so it can stop a climb and hurry a descent and can never turn a landing approach into a crash faster than letting go would. Arresting a rise otherwise meant a sideways burn and a wait for the orbit to come round, which is how a rendezvous really is flown and a lot to ask of somebody who has not flown one. It does nothing at all to a lander on the ground, which is not politeness: touchdown has already had its say and will not speak again, so without that guard a landed lander holding it goes straight through the moon. | | Depth | Four pillars at four distances and a ball walking past them, behind the near ones and in front of the far ones. The ball is sprite nought and every pillar is numbered after it, so table order puts it in front of all four - what actually decides is the depth buffer, asked a column at a time. | | Flip | Draws a whole screen into the bank that is not being shown, waits, and then shows it in one byte out of one port. It writes nothing else at all - not a tile, not a colour - so it does not ask for the screen to be saved, and the line it printed is still there when it comes back. It deliberately does not put the displayed screen back either, because that is the system's to restore: a program that faulted while flipped could not have. | | Edit | A line editor. | diff --git a/Tests/expected/cosmosCrossDisk.out b/Tests/expected/cosmosCrossDisk.out index 8818efa..0f0e3cb 100644 --- a/Tests/expected/cosmosCrossDisk.out +++ b/Tests/expected/cosmosCrossDisk.out @@ -25,7 +25,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 6814 +Lander.sbx 7477 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosDrives.out b/Tests/expected/cosmosDrives.out index 1142a05..33daceb 100644 --- a/Tests/expected/cosmosDrives.out +++ b/Tests/expected/cosmosDrives.out @@ -15,7 +15,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 6814 +Lander.sbx 7477 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosFault.out b/Tests/expected/cosmosFault.out index 449058f..7595b34 100644 --- a/Tests/expected/cosmosFault.out +++ b/Tests/expected/cosmosFault.out @@ -32,7 +32,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 6814 +Lander.sbx 7477 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosFlip.out b/Tests/expected/cosmosFlip.out index 4950589..6466f8b 100644 --- a/Tests/expected/cosmosFlip.out +++ b/Tests/expected/cosmosFlip.out @@ -22,7 +22,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 6814 +Lander.sbx 7477 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosGrid.out b/Tests/expected/cosmosGrid.out index 81612a2..63970ef 100644 --- a/Tests/expected/cosmosGrid.out +++ b/Tests/expected/cosmosGrid.out @@ -22,7 +22,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 6814 +Lander.sbx 7477 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosMonitor.out b/Tests/expected/cosmosMonitor.out index 24915e1..4b37a85 100644 --- a/Tests/expected/cosmosMonitor.out +++ b/Tests/expected/cosmosMonitor.out @@ -108,7 +108,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 6814 +Lander.sbx 7477 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosMonitorRun.out b/Tests/expected/cosmosMonitorRun.out index f7e26f7..1f67db6 100644 --- a/Tests/expected/cosmosMonitorRun.out +++ b/Tests/expected/cosmosMonitorRun.out @@ -27,7 +27,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 6814 +Lander.sbx 7477 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosRun.out b/Tests/expected/cosmosRun.out index f303482..fe74272 100644 --- a/Tests/expected/cosmosRun.out +++ b/Tests/expected/cosmosRun.out @@ -15,7 +15,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 6814 +Lander.sbx 7477 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosSlowDisk.out b/Tests/expected/cosmosSlowDisk.out index ed554fa..45d4e36 100644 --- a/Tests/expected/cosmosSlowDisk.out +++ b/Tests/expected/cosmosSlowDisk.out @@ -13,7 +13,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 6814 +Lander.sbx 7477 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/expected/cosmosSprite.out b/Tests/expected/cosmosSprite.out index d4db527..f90f0ab 100644 --- a/Tests/expected/cosmosSprite.out +++ b/Tests/expected/cosmosSprite.out @@ -22,7 +22,7 @@ Mode.sbx 48 Flip.sbx 173 Sprite.sbx 442 Depth.sbx 672 -Lander.sbx 6814 +Lander.sbx 7477 Pad.sbx 264 Crash.sbx 632 vars.script 50 diff --git a/Tests/video.sh b/Tests/video.sh index c25dff6..904bbe5 100755 --- a/Tests/video.sh +++ b/Tests/video.sh @@ -2515,6 +2515,94 @@ grep -q "Wrecked against the station" "$BUILD/wreck.out" \ && result ok "and the same approach unmatched is a wreck" "64 sixteenths, eight times the limit" \ || result no "and the same approach unmatched is a wreck" "no wreck in the transcript" +# ---- Thruster flames ---- +# +# Every other reading here is a number drawn as a bar: how fast sideways, how fast down, how +# much sky, how much tank. All of it says what is happening TO the lander and none of it says +# what the PILOT is doing, so somebody watching over a shoulder has to read gauges to work out +# that a thruster is even lit. +# +# A plume hangs off whichever side the engine is pushing from - under the lander to go up, +# over it to go down, and on the far side from the way it is being pushed sideways. Twenty +# four pixels of it, which is the whole tile, so the count says it is not clipped. +# +# Held rather than fired. The engine fires one frame in ten, because that is the tick gravity +# is applied on, and a flame that honest would be one frame of light six times a second - a +# fault lamp, not a rocket. The button being down is the thing being shown. +python3 -c " +import struct +# The same place every time, with an empty tank for the last of them. Down 512 sixteenths and +# not nought: the world's origin is the top of the screen, and the two row window is drawn OVER +# the map there, so a lander placed at the origin is behind the fuel gauge and its flame with +# it - which reads exactly like a flame that is not being drawn. +for name, fuel in (('flamefull', 255), ('flamedry', 0)): + open('$BUILD/%s.state' % name, 'wb').write(struct.pack(' /dev/null +done +for held in none up down right left dry; do + case "$held" in + none) byte='\x00'; disk=flamefull ;; + up) byte='\x08'; disk=flamefull ;; + down) byte='\x04'; disk=flamefull ;; + right) byte='\x01'; disk=flamefull ;; + left) byte='\x02'; disk=flamefull ;; + dry) byte='\x08'; disk=flamedry ;; + esac + python3 -c "open('$BUILD/flame$held.pad','wb').write(b'$byte' * 40000)" + timeout 60 "$EMU" --fast --cycles 1500000 --keyboard "$BUILD/fly.keys" \ + --pad "$BUILD/flame$held.pad" --screen "$BUILD/flame$held.ppm" \ + --disk "$BUILD/$disk.img" --ram-disk 2048 \ + "$BUILD/cosmos.bin" > "$BUILD/flame$held.out" 2>&1 || true +done +read -r FNONE FUP FDOWN FRIGHT FLEFT FDRY < sy else 'above' + if fx < sx: side = 'left' + if fx > sx + 4: side = 'right' + return '%d%s' % (len(lit), side) +print(flame('$BUILD/flamenone.ppm'), flame('$BUILD/flameup.ppm'), + flame('$BUILD/flamedown.ppm'), flame('$BUILD/flameright.ppm'), + flame('$BUILD/flameleft.ppm'), flame('$BUILD/flamedry.ppm')) +" 2>/dev/null || echo "x x x x x x") +EOT +[ "$FNONE" = "0" ] && [ "$FUP" = "24below" ] && [ "$FDOWN" = "24above" ] \ + && result ok "a lifting thruster shows a flame under the lander" "none held draws none, up draws 24 below, down 24 above" \ + || result no "a lifting thruster shows a flame under the lander" "none $FNONE, up $FUP, down $FDOWN" +# Sideways is the one that is easy to get backwards: pushing RIGHT means the plume comes out +# of the LEFT of the lander, because that is the side the gas leaves from. +[ "$FRIGHT" = "24left" ] && [ "$FLEFT" = "24right" ] \ + && result ok "and comes out the far side going sideways" "right draws left, left draws right" \ + || result no "and comes out the far side going sideways" "right $FRIGHT, left $FLEFT" +# An empty tank draws nothing, because then the button really is doing nothing. +[ "$FDRY" = "0" ] \ + && result ok "and an empty tank draws none at all" "the button is doing nothing, and shows it" \ + || result no "and an empty tank draws none at all" "$FDRY with a dry tank" + # ---- Clearing puts the cursor back at the top ---- # # A screen with nothing on it and a cursor half way down it is not a cleared screen. This