With game mechanics in place it was time to start making Lunar Lander into an actual game, starting with answering the question posed above, "How did I do?".
Scoring
I described the scoring scheme from the arcade version of Lunar Lander in my Targets log. Following their lead I implemented the following:
- A "Good Landing" is defined as one where the LEM:
- Is upright.
- Lands inside a target zone.
- Has a vertical velocity is less than 100. (I will probably tighten these up.)
- Has a horizontal velocity is less than 50.
- I did not implement the Hard Landing" for 15 points.
- The score for a good landing is 50 points x the target multiplier.
- A crash is worth 5 points. (I'm thinking about making this only if the LEM hits a landing zone.)
- There is also a 50 "gallon" fuel bonus for a good landing.
Good Landings
I had already gotten collision detection working, so now I needed to add code to determine if the LEM was within a "landing zone". To accomplish this I added a couple of tables that enumerated the left and right x coordinates of the six zones. To save me from having to multiply the 50 points bonus x the target multiplier on a machine with no multiply I made a little table with the score value for each target as well.
/ The left endpoints of landing zones.
lzx, 50 / 5x
322 / 2x
466 / 3x
632 / 1x
1320 / 4x
1642 / 5x
0 / End of data marker.
/ The right endpoints of landing zones.
rzx, 144 / 5x
416 / 2x
574 / 3x
1104 / 1x
1414 / 4x
1724 / 5x
/ Scores for successful landings by target multiplier.
skz, 372 / 5 x 50 = 250
144 / 2 x 50 = 100
226 / 3 x 50 = 150
62 / 1 x 50 = 50
310 / 4 x 50 = 200
372 / 5 x 50 = 250
These are of course all in octal format. I added the following code to make the "soft landing" call.
/ Check for a "soft" landing. On entry scx will have the "center" x value of the LEM.
lac rot / Get the LEM's rotation. Must be upright.
sas (2 / Upright?
jmp sex / No - Set for explosion.
init lx, lzx / Yes - Setup for landing zone check. Left point.
init rx, rzx / Right point.
init sc, skz / Score.
lx, lac . / Get the left landing zone end point.
sza i / Not Zero?
jmp sex / No - End of landing zone table. Set for explosion.
dac dgx / Yes - Save left end.
rx, lac . / Get the right landing zone end point.
dac dgy / Save right end.
lac scx / Get the x center of the LEM.
sub dgx / Check against left edge of landing zone.
spa / Positive?
jmp nlz / No - Point is outside landing zone.
lac scx / Yes - Check right edge of landing zone.
sub dgy
sma / Negative?
jmp nlz / No - Point is outside landing zone.
jmp cvv / Yes - Hit landing zone check velocity.
nlz, idx lx / Check next landing zone.
idx rx
idx sc
jmp lx
cvv, lac vy / Yes - Make sure verticle velocity < 100.
sub (144
sma / Negative?
jmp sex / No - Landing too hard.
lac vx / Yes - Check horizontal velocity.
spa / Positive?
cma / No - Compliment want absolute value.
sub (62 / Yes - Make sure horizontal velocity < 50.
sma / Negative?
jmp sex / No - Too much drift.
In the above code a jump to sex is NOT a good thing.
Fuel
The limiting factor in Lunar Lander is fuel. When you run out of fuel the game is over. Implementing fuel ended up being fairly trivial. I created a variable ful and initialized it to 999 "units" (lets say gallons just for fun) at the beginning of a new game. Every time through the main loop I subtract 1 if there is still fuel and the thrust button is pressed.
/ Process inputs.
pip, dap pix / Set return address.
...
ckt, dzm thr / Set thrust to off.
lac cin / Refetch the input.
and (100000 / Check for thrust.
sza i / Is thrust?
jmp pix / No - Keep going.
lac ful / Yes - Apply thrust velocities if fuel.
sza i / Is fuel?
jmp pix / No - Don't apply velocities.
lac vy / Yes - Apply velocities (already calculated).
add tv
dac vy
lac vx
sub th
dac vx
lac ful / Reduce the fuel by 1.
sub (1
dac ful
lac (1 / Indicate that thrust is on.
dac thr
pix, jmp . / Return
Updating Status
I added Fuel (F:) and Score (S:) to the status bar at the top of the screen on the left had side.
/ Draw the fuel status.
dfs, dap dfx / Set return address.
lac (372000 / Set top of fuel position.
dac dgy
lac (-361000 / F
/cma
dac dgx
jsp c15
lac (-344000 / :
dac dgx
jsp c10
lac ful
dac num / Save the number.
lac (-265000 / Set the x coordinate of the last letter.
dac dgx
jsp ddg / Display the number.
dfx, jmp . / Return.
/ Draw the score.
drs, dap dxs / Set return address.
lac (346360 / Set top of score position.
dac dgy
lac (-361000 / S
dac dgx
jsp c14
lac (-344000 / :
dac dgx
jsp c10
lac sco
dac num / Save the number.
lac (-265000 / Set the x coordinate of the last letter.
dac dgx
jsp ddg / Display the number.
dxs, jmp . / Return.
Now here is where my refactoring effort really paid off. On a "Good Landing" I was able to easily add a little "animation" of the Fuel and Score being updated. I found that just updating these values and immediately carrying on with the game was pretty anticlimactic. Here is the code.
/ Good landing!
sc, lac . / Show the score increasing. Get the score update for this landing site.
dac tmp / Save.
slp, lac sco / Get the score.
add (1
dac sco / Update by one.
jsp drs / Show the score.
jsp dtr / Show terrain.
lac tmp / Decrease new score by one.
sub (1
dac tmp
sza / Zero?
jmp slp / No - Keep showing score increasing.
lac (62 / Yes - Add 50 to fuel.
dac tmp
sfp, lac ful / Get the score.
add (1
dac ful / Update by one.
jsp dfs / Show the fuel.
jsp dtr / Show terrain.
lac tmp / Decrease new fuel by one.
sub (1
dac tmp
sza / Zero?
jmp sfp / No - Keep showing fuel increasing.
jmp nxr / Yes - Restart round.
I was able to show only the elements I wanted to while performing the animated update by calling the appropriate "drawing" subroutines (jsp drs (Score), jsp dsf (Fuel), and jsp dtr (Terrain)).
Here is fuel consumption and scoring in action.
I feel like I could do more here, but OK for now.
Michael Gardi
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.