Wednesday, August 17, 2011

Metroid Prime Trilogy

In mountain climbing, ascents are categorized technically by the most difficult part, which is called the crux. The idea is that climbers skilled enough to take on the hardest move sequence in climb will be able to complete the rest of the climb as well. It helps preparations to know what the most difficult challenge will be. Inexperienced climbers can learn to cut their teeth on easy ascents and experts can judge how much of a challenge a particular mountain will provide.


Meta Ridley


I think games ought to be rated in a similar way: Plants vs. Zombies would be graded as Easy and VVVVVV would be Hard Very Severe. These aren't knocks on the games anymore than calling a walk in the park easier than a walk in the Himalayas: it's the truth converted into a subjective ranking system. If you think Sonic is just the drive-in place where servers wear roller skates when they serve awesome drinks, you are going to get stuck trying more difficult games. If you want want to hone your competitive Starcraft skills, the lower end of the spectrum won't help.


Obviously there need to be other axes with which to evaluate games such as Entertainment Value, Innovation, Addictiveness, Story, and um, Graphics. (I like good-looking games as much as the next guy, but it seems like there's often an inverse relationship between entertainment value and graphics.) But difficulty is often the primary limiting factor in whether a player will enjoy a particular game or not. Publishing accurate (if not precise) ratings would solve a lot of problems with people being exasperated by surprisingly difficult games. Like, for instance, John Walker's experience with Metroid Prime.


I should be quick to point out that even though I've completed the first two Primes and am on the cusp of the final assault of Corruption, I'm not saying that Mr. Walker is bad at video games. In fact, it's quite likely that he's better at games than I am, but like Edmond Hillary, who had learned the lessons from numerous other explorers, I have many little advantages that are worth more than the sum of their parts. (More on this anon.) Rather the game failed him by not letting him know that it has Hard Very Difficult boss fights.


After the various story-laden introductions, the games settle into an easy-going exploration mode. Sure there are signs everywhere that something has gone wrong: poisonous pools, a dark, life-stealing mirror world, and a creepy, disease-like corruption. But these are offset by a series of organic, lived-in environments. It's telling that I continued to avoid harming the more peaceful creatures even after I found out that they continually respawn in the same locations. For the most part, you have limited ability to alter your surroundings and that makes them seem more real somehow. (And when you do make a permanent change to the world, it seems so much more monumental.) You can blatantly slaughter a whole herd of peaceful Zoomers, they will return to their proscribed paths the next time you travel through the area.


Zoomers are less threatening than they appear.


For the vast majority of the game you explore until you find your way blocked by some obstacle, search for the suit, weapon, morph ball or visor upgrade that overcomes the obstacle, defeat the boss that guards the upgrade, and go back to the areas that are now open to you. It sounds artificial and it is. Almost every location is a room with at most three doors or a hallway. On the other hand, you generally don't notice the linear space because Retro Studios crafted the illusion of space "out-there". Exploration is the core of the game and for the most part, the boss battles are more like puzzles (that need to be explored) than grueling, multi-part slug-fests. Those usually come near the end of the game, but some of the hardest fights for me are the very early bosses that you have to face before you've gathered enough upgrades.


It turns out that three classes of upgrades are not required explicitly to make progress: energy tanks, power bombs, and missile capacity. These tend to be better hidden and/or more challenging to reach than other upgrades so there's a sense in which they are bonus rewards for those who enjoy getting 100% completion in games. You'll still run across enough of each upgrade to avoid getting killed by many of the mid-game boss fights, so a lot of players will just be inclined to skip the harder to find or reach ones. But this is a mistake because you'll want everything you can lay hands on to take on the final few bosses. To go back to the mountaineering metaphor, it's like starting an assault on a peak without enough rope, pitons and carabiners.


I mentioned before how I had some advantages over players of the GameCube version of the first two games and one of them is the wealth of tips and strategies for finishing the games which are now freely available on the internet. Little things like knowing which beam or missile causes the most damage to a particular enemy or knowing where an elusive upgrade can be found make huge differences in the end. There's no shame in looking for tips when you get stuck, in my opinion.


The other set of advantages comes from changes to the games in the Trilogy edition. Aiming with the Wiimote is probably the most intuitive input possible for shooting games, if not quite as accurate as mouse and keyboard. I gather all the games on Normal difficulty are easier than they used to be. Once you get morph ball bombs in the first two games, you also get the spring ball ability that makes little hops much easier than always doing bomb jumps. Oddly enough, having all three games available in one place helps since if you get stuck on one boss, it's easy enough to switch to another game. At one point I was stuck on all three games (on Meta Ridley, Bomb Guardian/Alpha Sandigger, and Mogenar for those who care) and simply by switching from one to another I got past them. It helps to clear the palate and avoid banging your head against a hard surface to see a different difficult part of some other game.


It's a bit unusual from me to feel compelled to finish a game, but I am compelled to finish each of the Metroid Prime games. Unlike Super Mario Galaxy, which is structured as a series of increasingly difficult challenges, the Prime games are structured as preparation for a final challenge. The onscreen result of beating the final boss turns out to be a not-very-interesting1 cutscene and roll the credits. But the offscreen result was more satisfying. Completing a difficult challenge after spending so many hours working toward it provides the same sort of intense satisfaction you might get from hiking to a great view. Skipping straight to the cutscene just can't have that effect any more than driving to the top of a mountain can match walking there.


Retro Studios built up an amazing reputation on the backs of these games and for good reason: they are finely crafted masterpieces. Everything about them is just so well done from the the controls to the music to the menus to the item descriptions to the color scheme. And given the chance to improve things in the Trilogy version, they did from what I've read. The star of the show has to be the environment you get to explore, which is universally and rightly praised. Trying to explain it is sort of like trying to tell someone why the Grand Canyon isn't just a big hole in the ground. You just have to see it for yourself.



Footnote:



1. I've seen the various endings on YouTube and none of them are especially amazing. Ok, so you can see more of Samus' character model and hints of future episodes if you gather 100% of collectibles and scans, but who cares?

Monday, February 14, 2011

Zombie Panic in Wonderland (demo)

So a Spanish developer decided to make a zombie light gun shooter based on an imagined sequel to a Japanese legend, The Wizard of Oz and several Disney princesses in very revealing outfits and put the game on WiiWare. Here's the official description:

A wave of zombies is attacking Wonderland. To save his friends from the spell of the "amorous zombies," the player will have to solve the mystery of the "scented dwarves" - with machine gun in-hand - and put an end to the evil power hidden in the castle in the northern city. Game features include cut scenes, music with lyrics, seven playable characters, hidden levels, final bosses, different weapons, fire, demolitions, explosions and lots of zombies attacking you at all times.


Zombie Panic in Wonderland screenshot


Prior interest: none


With pointer controls, the Wii is an ideal platform for light gun games and by now many of them are rattling around in bargain bins for about the same price as this WiiWare game. I don't particularly need or want a game that seems to trade heavily on sexy anime characters.


Odds of purchase: none


Nothing in the demo really changed my mind about purchasing this game, but I was surprised by a few things. First, the production values are much higher than I'd anticipated. Music, sound, visuals (including completely destructible environments), controls, and even the cut scene to start the level are of the highest quality. Second, the game turns out to be very easy even on the hard difficulty if the demo level is to be trusted. With unlimited ammo, a few very high-powered support weapons, and environment hazards to fend off zombie sumo wrestlers and ninjas, there's no particular feeling of panic. Perhaps the balance shifts in later rounds.


In sum: a quality game that I have no interest in.

Monday, February 7, 2011

Racers' Islands: Crazy Racers (demo)

Racers' Islands: Crazy Racers looks for all the world like a Mario Kart clone. "Crazy" cast of characters? Check. Bright, colorful visuals? Check. Nonsensical power-ups? Check. Slit-screen, four-player mode? Check. Wild, over-the-top tracks? Check. If you are going to steal, steal from the best. Plus this attempt includes some light-gun shooter elements, which seems like a great addition.


Racers' Islands: Crazy Racers screenshots


Prior interest: slight


As a Mario Kart fan, my heart started to race for a moment when I saw a cartoon racer on the demo list.


Odds of purchase: none


Mario Kart sets the standard for kart racers. (It even supplies the genre name.) On the opposite end of the spectrum sit barely-playable cash-ins like M&M's Kart Racing. Mario Kart sells year in and year out because everyone who plays the game loves it and wants a copy. Shovelware succeeds because by the time anyone plays the game, it's too late. Releasing a demo sends the signal that Racers' Islands sits closer to Mario Kart than to the M&M game, but playing the demo reveals the opposite. Unlike retail shovelware, which can rely on box art, junky WiiWare titles need decent-looking screenshots and trailers to attract sales. It's a wonder that Racers' Islands: Crazy Racers bothered to release a free version that demonstrates how poorly the game controls. It honestly felt like I was driving not a race car, but a unicycle.

This seems to me the worst case scenario for the game's publisher. (But I'm glad they let us feel how poor the game plays without spending the cash.)

Thursday, January 27, 2011

Furry Legends (demo)

Furry Legends isn't likely to be what you think it is unless you think it's a puzzle-platformer featuring a creepy-looking, green fuzz ball. Besides its terrible name, the demo also has the misfortune of being released the same day as Fish'em All! It's hard not to draw the conclusion that Nintendo took this week as an opportunity to dump its clunkers at once.


Furry Legends Screenshot


Prior interest: none


This game's marketing failure begins with the lousy name. I think the intent of the word "Furry" was humor and I imagine that "Legends" is supposed to excite interest in the story. But together, the words of the title are just bland. It would have been better to go all humorous ("Wholly Rollers" perhaps?) or all epic ("Saga of Furland"). To try and split the difference just leaves the consumer confused.


Odds of purchase: low


Playing the demo makes clear the developers were going for a wacky story, which seems entirely needless. Gameplay, at least for the first level, tends to be relaxed even when the enemy shows up. You basically roll around and jump with the occasional attack which is triggered by holding "Z", aiming with the analog stick and shaking the WiiMote. Later you get a boost power-up that acts something like the boost ball in Metroid Prime. In fact, the game feels like an extended riff on Samus' morph ball with significantly less weight. I don't know how well a full game of rolling platforming would stand up as the sections in Metroid games charm by offering a change of pace.


I should mention that there seems to be one oversight in part of the demo's level. There's a puzzle that needs to be solved by rolling a stone onto a pressure trigger. But I managed to block the stone from settling on the trigger and it rolled to a part of the level I couldn't seem to retrieve it from. My solution was to restart the level, which probably wasn't what the designers intended.

Wednesday, January 26, 2011

Art of Balance (demo)

Family lore has it that my mother's heirloom, crystal pepper shaker was broken when her father-in-law and some of my uncles were stacking whatever happened to be on the table the first time they visited my parents. Art of Balance, if it had existed then, might have saved the day. It's a simple game of stacking one geometric object (no breakable table settings here) on another and seeing if the whole thing stays up.


Art of Balance screen


Prior interest: none


I thought Art of Balance looked like a newly released game from the makers of Jett Rocket reusing the same engine for water and lighting effects. But it turns out the game was released on WiiWare almost a year ago and several months before Jett Rocket. Clearly, it's a low profile game that could use a little publicity.


Odds of purchase: low


As a cash-strapped consumer, a game like Art of Balance which delivers tons of gameplay for a low pricetag, ought to represent a real temptation. Sadly, its niche has already been filled by Boom Blox Bash Party in my library. (Which, come to think of it, is the biggest problem I have with Jett Rocket: it's too similar to another retail game that I already own.) What it comes down to is when I want to play a physics-based puzzle game I can't imagine picking up Art of Balance because Boom Blox just offers me so much more.

Tuesday, January 25, 2011

Let's Code: Pong (part 1)

Lately I've been playing around with writing games including two simple puzzle games based on ideas suggested by my son. Today, I wanted to present my code for a Pong clone. It's not the most exciting game in the world, but it's dead simple to code and a good "Hello World"-style program for starting to write action games.


The first decision that must be made when starting a new coding project is which framework to use. Since my justification for writing games is to help me keep my programming skills active and to continue learning, I wanted to write in an interesting language that I don't know well: Lua. A quick Google search discovered LÖVE, which is an open-source 2D game engine in active development. Since Pong is a 2D game it's not a big deal that our framework isn't 3D. My other little projects simulate a 3D environment with an isometric viewpoint. In the future, if I do a first-person game or something, I'd want to switch frameworks.


I've also decided to use the love.physics module, which is certainly overkill for Pong but will be necessary for physics-based puzzles I'm working toward. It really isn't as hard to use as the LÖVE manual suggests. The module is based on the Box2D library, which has pretty good documentation. Once the simulated world is set up, the physics library can deal with a bunch of details like ball movement, bouncing off walls and paddles, and so on.


love.physics tutorial


love.physics has a fairly basic tutorial that simulates a ball falling to the ground and being rolled around. If you want to follow along, go ahead and grab a copy of LÖVE and try out the tutorial. Below, I'll cover some of the same territory if you'd prefer just to read. Playing around with the tutorial, I noticed that the ball will eventually roll off the edge of the world and disappear. To solve that, I added a two walls and a ceiling to keep physics objects from getting lost. My philosophy is that if you do something twice, you should think about writing a function instead. If you do something more than twice, then you certainly should write a function. Since I needed four walls rather than just the ground, I wrote a make_wall function:


function make_wall(world, x, y, w, h)
local wall = {}
wall.body = love.physics.newBody(world, x, y, 0, 0)
wall.shape = love.physics.newRectangleShape(wall.body, 0, 0, w, h, 0)
wall.shape:setRestitution(1)
return wall
end


Even if you are new to Lua, this code should be fairly easy to read. make_wall needs 5 inputs: world (the physics world), x, y (the coordinates of the center of the wall), and w, h (the width and height of the wall). Lua has a very lightweight object system and this function could be seen as a constructor for a wall object, which is the return value. As PIL points out, "Tables in Lua are not a data structure; they are the data structure." So wall is initialized as an empty table. Objects in the physics module require a body, which represents the object's center of mass, position, attitude, and velocity, and a shape, which defines the space an object occupies and how it interacts with other objects. When we assign values to wall.body and wall.shape, Lua automatically creates variables (members in OOP terms). Walls should be static, so the mass is set to 0, which is the way Box2D represents infinite mass. The shape of the wall is simply a rectangle centered on the center of mass. In order to make balls bounce off the walls, restitution (essentially bounciness) is set to 1.


In love.load(), I'm going to create my walls thusly:


local walls = {}
...
-- Floor
table.insert(walls, make_wall(world,
love.graphics.getWidth()/2,
love.graphics.getHeight()+1,
love.graphics.getWidth(),
2))

-- Ceiling
table.insert(walls, make_wall(world,
love.graphics.getWidth()/2,
-1,
love.graphics.getWidth(),
2))

This inserts a floor two pixels high1 just below the graphics frame and a ceiling just above it. If we weren't using a physics engine, we could just code the ball to reverse vertical motion when it hits the side of the screen. Standard Pong only needs these two walls, so make_wall didn't save us many lines of code. However, it's easy to imagine variations with different wall configurations.


We only need one ball for our basic game, but it's simple and clean to write a make_wall function. And who knows? We might want to add more balls to the game at some point. Initially, I'd set the mass of the ball based on it's size (using Body:setMassFromShapes) but that causes the physics of the game to change when the size of the ball is changed. It seemed easier to just fix the mass to something (somewhat) sensible like the mass of a tennis ball and adjust the force applied until it felt right. It turns out one Newton works pretty well. The radius of the ball is measured in pixels and it can be adjusted without impacting the physics of the game. Some people would probably want these variables to be made constants, but a) there's not much of a performance gain and b) Lua doesn't support making variables constant. Besides, the simplest way to have a variable remain constant is to not alter it's value. The ball shouldn't have any linear damping (more or less the same as drag or fluid resistance) or friction to slow it down. The way Box2D simulates collisions, I don't need to make both the walls and the ball bouncy, but it doesn't hurt to specify the restitution here as well.


local ball
-- Mass of a tennis ball http://hypertextbook.com/facts/2000/ShefiuAzeez.shtml
local ball_mass = 0.057
local ball_force = 1
local ball_radius = 3

function make_ball(world, x, y, r)
local ball = {}
ball.body = love.physics.newBody(world, x, y, ball_mass, 0)
ball.shape = love.physics.newCircleShape(ball.body, 0, 0, r)

ball.body:setLinearDamping(0)
ball.shape:setFriction(0)
ball.shape:setRestitution(1)

return ball
end


The final type of object we need to make for our Pong game is the players' paddles. Classic Pong doesn't really simulate the ball bouncing off a flat paddle, but has the ball bounce off at an angle determined by the location the ball hits the paddle. In other words, the closer to the edge of the paddle, the steeper the angle the ball with come off the paddle (and the more likely your opponent will miss). This makes the game much more interesting since it matters not just whether a player intercepts the ball, but where on the paddle they do so. At some point experimenting with the number of facets would be interesting.


function make_paddle(world, x, y, w, h)
local paddle = {}
paddle.body = love.physics.newBody(world, x, y, 0, 0)

-- Don't use a rectangle for the paddle since the bounces
-- off a flat surface are boring. In stead, we use a flattened wedge:
-- /|
-- | |
-- \|
paddle.shape = love.physics.newPolygonShape(paddle.body, 0, -h/2,
-w/2, -h/6,
-w/2, h/6,
0, h/2,
w/2, h/2,
w/2, -h/2)
paddle.shape:setRestitution(1)
return paddle
end


Now that we can make a paddle, let's build a player object, which has a paddle and a score. The cpu variable will describe the behavior of the player if it is computer controlled. More on that momentarily.


local players = {}

function make_player(world, x, y, l, cpu)
local player = {}

player.score = 0

player.paddle = make_paddle(world, x, y, 2*ball_radius, l)

player.cpu = cpu

return player
end


It's easy to imagine all sorts of AIs for Pong from carefully calculating the intercept location of the ball to moving more or less at random. Most Pong implementations use some variation of what I call the chase AI in which the computer tries to match the location of the ball on the y-axis with some sort of lag. My approach follows Zeno's Paradox of Achilles and the tortoise. In this case, Achilles (the paddle) does occasionally catch the tortoise (the ball) if the delay is set low enough, the paddle is long enough and the ball is moving slow enough along the y-axis. Set delay to 2 for the classic paradox.


-- Basic Pong AI is to have the paddle chase the ball. Use the delay parameter
-- to create an AI that responds more slowly to vertical movement.
function make_chase_ai (delay)
return function (paddle, ball)
local delta_y = ball.body:getY() - paddle.body:getY()
paddle.body:setY(paddle.body:getY() + delta_y/delay)
end
end


If you're familiar with mostly conventional languages (such as BASIC, Java or C++), you might find it odd that make_chase_ai returns a function. In Lua, functions are first-class values which means the can be assigned to variables, passed to other functions and, as seen here, be return values. What I'm doing here is creating a closure over the delay free variable. It's probably easiest to explain by showing how it's used. Here's how I initialize a computer player in love.load():


players[1] = make_player(world,
10,
love.graphics.getHeight()/2,
12*ball_radius,
make_chase_ai(50))

players[1].paddle.body:setAngle(math.pi)

So I pass the physics world to the make_player function, put the paddle 10 pixels in from the left of the screen and centered vertically, set the length of the paddle to 12 ball radii, and provide an AI that chases the ball with a delay of 502. This isn't the best player in the world, but it's surprising how often he catches the ball at the last moment, which makes for some interesting play. Once the function is created, delay is fixed to 50 and it is assigned to the player's cpu variable. In OOP terms, cpu is a virtual method for the player object.


In passing, notice that I rotated the paddle body 180° since make_paddle creates a wedge facing to the left. I could have created the paddle to have facets on both sides or written a mirror_shape function3 to flip the paddle on its vertical axis. But applying the principle of parsimony it seemed simplest to rotate the paddle and be done with it.


Here's how the other player might be initialized if you want to pit an Achilles AI against the more pedestrian AI above:


players[2] = make_player(world,
love.graphics.getWidth()-10,
love.graphics.getHeight()/2,
12*ball_radius,
make_chase_ai(2))

So this player is positioned on the right side of the screen and has a lower delay, but is otherwise the same as player 1. So what benefit is using a closure instead of just making delay a player attribute like paddle length and position? It might help to see how the AI is executed in love.update:

for _, player in ipairs(players) do
if player.cpu then
player.cpu(player.paddle, ball)
end
end

Let's review what happened:

  1. We called make_chase_ai with parameters of 50 and 2 respectively.
  2. We assigned the output, which is a function, to each player's cpu variable.
  3. We checked to see if a player had a true value in cpu.
  4. And if it did, we executed the AI in each time step.

Each step creates one more layer of abstraction, which means we could easily plug a totally different type of AI or no AI at all into a player. For instance, here's an AI that jumps at the last minute:

function make_jump_ai (min, max)
return function (paddle, ball)
local delta_x = math.abs(ball.body:getX() - paddle.body:getX())
if (delta_x < max and delta_x > min ) then
paddle.body:setY(ball.body:getY())
end
end
end

...

players[2] = make_player(world,
love.graphics.getWidth()-10,
love.graphics.getHeight()/2,
12*ball_radius,
make_jump_ai(15, 20))

When the function is called, it takes just the paddle and ball objects and uses a completely different algorithm to move the paddle around. This allows polymorphism, which in turns allows much simpler code in higher level functions such as love.load and love.update.


This has gone on long enough and I've only covered the setup portion of the game. Next time, we'll set the world in motion and interact with it.




1 - I picked 2 pixels because creating a one pixel tall rectangle causes LÖVE (or more accurately Box2D) to crash.


2 - 50 isn't a magic number. It's just what seemed most interesting given a particular configuration of ball size, screen size, ball speed, and paddle length. For a while I was using 100.


3 - I cannot tell a lie. I wrote a mirror_shape function, but decided not to use it since it was more cumbersome than I imagined when I started writing it.

Tuesday, January 4, 2011

Robox (demo)

Robox might be what would happen if Terry Gilliam (from Monty Python and director of Time Bandits) set out to make a Metroid-type game. If you remember from the Flying Circus show there were occasional animated transitions that start with a live-action scene, end with a live-action scene, and show some seemingly random action using photo-collage and crude, rounded off drawings, you'll have a pretty good mental image of Robox. You play a probe, dropped off on an unknown planet, which loses all of it's natural abilities and must hunt for them via exploration, platforming and solving puzzles. It's a promising start.


Robox screenshot


Prior interest: none/low


Either I hadn't heard of Robox before now or I'd confused it with Frobot. If I had heard of the game, it's likely I'd have been slightly interested in the unique graphical style of the title. Theses days, WiiWare (and the Wii itself) has a full stock of arty 2D platformers, so if you want to sell another, you need to make the gameplay stand out. In turn, the best way to show off unique gameplay is with a good demo, so it's a good thing we have them now.


Odds of purchase: none


Like a Terry Gilliam cartoon, the game shifts from one action to the next unpredictably, but charmingly. The demo begins with a rack of squared-off robots (Roboxen?) lined up for deployment. Starting the game drops one of them into the atmosphere below where you need1 to tilt the Wiimote to avoid cliffs. Upon landing, you have no choice2 but to hold the Wiimote sideways and walk to the right. After a bit, the scene shifts3 to the inside of Robox where you use the pointer control and D-pad to solve an insultingly easy puzzle with a parasite or biological component or whatever. That allows Robox to jump, which is convenient since there is a small mesa just to the right. To the right of the mesa there's a slope4 down which a suddenly spawned boulder rolls and crushes a third of the robot's battery power followed shortly by a second boulder which takes another third. Understand that you move very slowly, jump only so high as to clear the boulder for a fraction of a second, and the boulder falls much faster than you can move. And going uphill is even slower. And there is a new boulder being spawned constantly at that same location over and over again.


Thankfully, your little robot will respawn to the point where he gained the jump ability after every death. Hatefully, however, checkpoints are few and far between and the obstacles ramp up the difficulty exceedingly quickly. There's one bit where a swinging, killer vine lives directly over spike-lined pit. If you mistime your jump and hit the vine, all forward momentum is lost and you will fall into the pit, which is far too deep to escape and so you will die. Which means slowly ambling past increasingly difficult traps once again to try one more time to slip past the vine/pit/spike vortex once again. There's no rhyme or reason to the pacing which goes from pedestrian to punishing in the space of a moment.


Hard games can be fun. Robox might very well be have a good balance between challenge and accomplishment once you've found better upgrades and mastered the system. Even games that work on repetition and memory can be fun if action/consequence loop is tight. But the Robox demo fails to show anything but brutal difficulty. The good news, I suppose, is that having a demo will weed out the uninterested/weak.





1 - To be honest, I don't think you actually need to do anything in this segment. There seem to be no consequences for hitting rocks. Would the sequence continue forever if you put the controller down and walked away? I might try it sometime.


2 - Sure you could go left, but only a few steps before you run into an insurmountable wall.


3 - Sadly, there is no actual animation—just a quite cut to a vaguely circui-board-looking screen and a few pages of expositionary text.


4 - I call it the learning curve.