Friday 31 July 2015

Game#14: Robotron (Day 2)

Shocking cold yesterday and especially today, spend most of today in Bed :(

Have made some minor progress with Robotron though ; the Grunts, Hulks, Enforces and Humans are completed.

Todo : Spheroids (spawn Enforcers), Quarks (spawn Tanks) and Brains. I'll have to spend some time figuring out how they all behave to get a realistic version of the game.

Maybe tomorrow :)

Wednesday 29 July 2015

Game # 14: Robotron

Robotron is a classic. It's the original "totally demented shoot em up".

As you can see, I only currently have the player shooting, grunts (not doing much) and the humans (which you collect for points). If you haven't played Robotron, it looks like this but with about ten times as much going on.

This looks like it's going to family centric, already I have six different families.

One change to the original. The original has dual controllers, one controls where you fire, one controls where you move. I have nicked Jeff "Llamasoft" Minter's idea for his remake "Llamatron" whereby the player automatically fires, and the fire key locks the shooting direction, so you can fire (say) to the left while moving right.

It will be an interesting test of the speed of Construct 2, if I can find a workable control system for a touch screen. When Robotron is going flat out it makes Defender without the planet look like Space Invaders on slow mode.

Tuesday 28 July 2015

Game# 13: How it works

Hi. I'm back from a sunny week in Germany. Remarkably I've lost weight on Holiday, because the place we stayed is on a steep hill in a river valley (Heimbach in the West of Germany), so lots of climbing up and down.

Anyway, how Jawbreaker works.

Globals and Constants

Lots of globals and constants here. Have some quasi constants as well "Pseudo Constants" - the idea here is they are assigned one value at the start, which then is unchanging - obviously this isn't enforceable in C2 but it's a good idea code wise. Event 3 initialises these values which represent various screen positions.

There are some utility functions here, updating the score and lives, and resetting things when a new game/retry occurs. When you start a new level or game, the layout is reloaded.

Finally this game has an FSM, which was grafted on afterwards, lazily :)

Setup

This game doesn't usee the painting at all, pretty much, so this sets up the screen - event 1 draws the border, repositions the vitamin (it is always there, just not always visible) and the level text. Event 2 creates the moving walls (actually solid walls with a black overlay), and event 3,4 and 5 create the monsters and the sweets.

Play State, 

This is the main game stuff. Event 1 initialises everything on layout start. Events 2-4 control the music and the 8 direction controls depending on whether the game is actually being played.

The game code proper starts at event 5.  Event 6 clamps the player into the game space, and 7 and 8 handle the potential horizontal movement - this is only permitted either in the left or right columns, or where you are passing through a gateway (gap in the wall).

Event 9 through 13 handle eating sweets - 10/11 have different effects for eating the different coloured sweets, and 13 handles the end of level.

Event 14 makes the vitamin pill appear on its timer, and 15 and 16 make it go into chase mode on collision with appropriate sound and effects, also setting the timer for "end of chase warning" (fired at 17-18) and "end of chase" (fired at 19-23). This also puts the monsters to reappear at the other end of the game board from where you are, allowing the player to get away (i.e. monsters don't reappear on top of you)

Finally 24-26 handle collisions with monsters - this can either be during the chase mode (26) in which case you score, or not, when you lose a life.

Moveables

This handles the monsters.  Events 2-4 set up the monstes, with a position and a reverse timer - they are driven by wrapped bullets. On event 5, the monsters reverse direction periodically.

Event 6 shouldn't be here (arguably), it's purpose is to make the gateways invisible or visible depending on the horizontal position, otherwise they will appear over the top of the left and right border. I probably should have done his with z-order/layers, but it's a blitz.

Other states

This handles various states. "New level" (1-7) animates the brush coming in and brushing and out using a bullet and a sine. This is messy but it works by firing timers and checking the position.

Event 8 onwards handles lost life, and this makes the player disappear from the top down. Finally Game over, in events 15-18 fades the Game Over text in and goes back to the title page when the fade is completed.

Saturday 18 July 2015

Off on Holiday (again)

Eifel National Park near Heimbach, Germany
I'm off to Germany tomorrow for a week, so I probably won't do the write up for Jawbreaker before I go.

When I get back I'll do that and then finish off my list which is (probably) Robotron, Defender, Lode Runner and (maybe) Tempest. Also thinking of porting Richard Garriott's Akalabeth (aka "Ultima 0") to C2 (I have already ported this to "C")

Then maybe some proper work :) This is just practice for a project, which is itself a rewrite of something I did on an Acorn Archimedes a few years ago.

Fortunately for the sanity of the world, it isn't my 3D signing robot for Special Needs that both sang, and signed "When Goldilocks went to the House of the Bears".

Game # 13 : Complete

Jawbreaker can be played here. The source is in the usual github. I will write it up later on.

Friday 17 July 2015

Game#13 : Jawbreaker (A blitz)

As a blitz, because I'm off for a week tomorrow, yesterday I wrote most of a version of the Atari 2600 game "Jawbreaker" (original below) in a couple of hours. It's nearly finished, except the objects aren't actually moving (yet). Should be available later today, but the write up might be delayed.

When I get back I will move onto Robotron.

Jawbreaker is a Pacman variant. Amazingly they were sued for "copying Pacman" - this was a time when there were umpteen dodgy Pacman clones. Pacman isn't really suited to the Atari 2600 for reasons relating to its hardware, so its a sort of horizontal version, though it has many of the same elements - the sprites only move horizontally as do the gaps in the maze.

This is because it is very difficult to get multiple sprites on the same line, and even if you do it is Space Invaders like (e.g. very repetitive) - Pacman's maze is doable, but the five sprites on the screen at once (4 ghosts, Pacman) flicker because they can be drawn in any vertical position.  This homebrew is much better in terms of its resemblance to the original, but it still flickers, for the same reasons.

14500 Board, how it works.

Interface

This is probably the interesting bit. The interface works through a dictionary which contains settings of the 'controls' (switches and LEDs), and automatically updates the dictionary or display.

Rather than copying umpteen objects, it uses an IOMarker which specifies detail, this expands at event 2. For each "bit" it requires, it creates an interface object which has different animations for different objects, and tells it whether it is input or output and which bit of which data item it is. 6 and 7 allow it to be labelled.

Events 8 through 10 copy data either to or from the interface object to the dictionary. Event 11 makes toggle switches work. Event 12 handles pushbuttons initialises pushbuttons and their labels - they do not have an entry in the dictionary, but call a function "On<actionName>" (this is done in 15).

Event 17 onwards is a short cut. Data is stored in a 256 x 4 bit memory on this machine, and this allows code to be typed in by pressing 0-9A-F which writes it to the current memory and bumps the program counter - otherwise you'd have to set the switches, press load data, and press single step - this does it for you - you can type 0 A 9 F C 3 etc. to load a program in.

InterfaceRefresh

This is related to interface but it copies data from the emulation globals into the interface dictionary - then they are updated to the display by the code in interface. These are the current address in the program counter (8 + 1 + 1) LEDs, the data at that location (4), the Result Register (14500 Accumulator) (1), the 16 output latches (16 LEDs) in 3 to 6). Event 7 maintains the state of the step/run switch in a global for convenience.

Event 8 is added to make things easier - it displays the mnemonic equivalent of the code in data memory, rather than having to read binary.

Commands

These handle the push buttons, they are all "OnXXX" functions. LoadPC copies the address switches into the program counter, Reset resets the program counter, the 14500 MCU and the run latch, Single step executes a single half-instruction (see emulation), Load Data loads the data switches into memory, and OnRunMode starts the program running by setting the run latch. Finally event 8 causes the emulation to free run at one half-instruction per tick if the run latch is set.


Emulation

This is a classic emulation of the MCU, with one tweak. Each single step is a half instruction - for even address instructions (opcode) it loads the opcode into an internal latch (Event 3), and on odd ones, it loads the operand (IO Address) into an external latch (Event 4) - so it requires two single steps to execute each instruction.

The emulations follow the 14500 Specification, except NOP0 (0000) resets the run latch and NOPF (1111) presets the program counter, loading the address switches into the program counter.

A couple of helper functions read input (Starting at 23) - this supports the "wiring" of input pin 15 to the RR (25) and input pins 8-14 to their corresponding output latches (26), these can be then used as data storage. This is my own addition, as it is not fixed on the original board but done by wiring.

App#1 : MC14500 Board Released

It can be "played" here. It's a reasonably faithful mimic of the original, though the Input and Output have been changed - on the original they ware just headers, but I've put switches in and LEDs to display the input and output. Also Pin 15 of Input is permanently connected to the Result Register pin (the 14500 Accumulator) and Pins 8-14 are routed from the output latch so they can be used as 7 bits of data storage.

An emulator in C2 is perfectly feasible, but for anything significant probably needs to be written in pure Javascript. The tilemap would be very good for old fashioned memory mapped screens, but wouldn't be much use for a pixel graphic one.

Note that the source is available in a different github, see the links bar.

Thursday 16 July 2015

App # 1 : MC14500B Evaluation Board

Well, I changed my mind about this. Partly because all the documentation regarding the WDR-1 is in German, and this emulation (on the right) is the Motorola Evaluation kit, which is very similar (the primary difference is memory is arranged as 4 bits rather than 8 bits) and it's in English. I don't speak a lot of German (may improve as I'm spending a week in Germany from Monday, but I'm probably not going to use the German for "Program Counter" much).

Most of this is done, the only bit missing is the actual emulation of the processor.

Seems a bit pointless, but it gave me some opportunities to experiment with ideas for interfaces. It's also an insane thing to program as it has no loops (other than reset to the start) in most implementations, which means everything is state machines. You can have branches and subroutines but none of the hardware is present in the 14500, all it does is pulse a chip pin "Jump" or whatever.

Almost none of the switches and LEDs are placed manually, they are generated by having another object create it, below is what the design screen actually looks like. Each purple IO says what to draw, how many bits, and whether it is input or output. This data is then copied to and from a dictionary, which acts as an intermediary between the interface and the program. Push buttons call a function derived from an instance variable. It works quite well.

Part of Construct 2's Editor Window

Dungeon : how it works

Dungeon is a relatively simple game ; the complexity involved is in changing from something extremely procedural to event driven. The main problem with this is the message area - there is one line that has a sequence of messages "You have been attacked" "The monster has 50 hit points" and so on. This is done by a queue event sheet which displays these in sequence. It is possible to get way ahead of this :) which is slightly odd, but the alternative is to lock the player out until the queue is emptied. In a real game with a similar issue this would probably be done by a scrolling list box.

GlobalsConstants

Mainly defines globals and constants - the space at the top, the size of the map, tile names. (isRunning in 2 is not actually used). This game is not actually set up in the editor - the text items and the tilemaps are positioned programmatically, and Event 4 does this for the text at the top of the screen. Event 6 processes and hides the decision buttons that are displayed when you are offered a bribe to run away, and 8-11 are simple utility functions to refresh the top screen. The RefreshHitPoints function is, or was, going to be optimised slightly but in the end wasn't, because HP is updated so much.

MessageQueue

A utility in its own right. It keeps a queue of messages seperated by colons in instance variable MessageQueue.text. Event 2 positions it on the second line. Event 4 removes a message on its timeout (each being displayed for 2s) and Event 5 replaces it with the head of the queue if it is not empty. Function 6 (AddMessageToQueue) seperates the implementation from the execution, so it is easy to (say) change the seperator if I need to.  I could implement the "scrolling information box" idea here just by replacing this on its own.

Dungeon

All this code is run at start up and its main purpose is to create the tilemaps and objects. There are two tilemaps, one the actual map itself, and the other one with two tiles, solid and transparent, which do the "visible area" bit, where you can only see where you've been. Events 2 and 3 do basic set up.

Event 4-6 creates the rooms, by creating a purple room marker object and moving it if it overlaps with any other room marker object. In Event 6 it is positioned off screen, destroying it does not work because I think destroy is a "request to destroy" rather than "destroy it now". If you stop at this point the screen would be full of purple squares.

Events 7-9 process them. For each room marker on screen, it draws tiles on the tilemap, creates a random gold and monster for each room and initialises the monster. Event 9 keeps moving the monster until it and the gold do not overlap. Event 10 initialises the target the monster is moving towards to where it currently is (i.e. initially it does not move)

Events 11-13 open the paths up and position the player on the path, somewhere. (Probably 12 and 13 should be the other way round !). The path code is simple - attempt a path east and south, and stop when you hit another room, but if you reach the end wall, there is no path. This is done by the recursive ScanPath function, which returns 0 if the wall is not to be drawn and 1 or 2 if it is (2 is for the door). It advances down the prospective path and draws the actual tile after the recursive call, when it "knows" whether the path is to be drawn or not. This code is actually much simpler than the flat original which scanned to the end and drew it retrospectively in Construct 2.

Incidentally, as an idea of how fast a PET was, the code drawing here took about a minute on the original.

Events 22-25 allow you to reset the "look" of the game to the PET original (though the font is still the same)

Player

This handles the player moving. Event 2 opens up the visibility by changing the tile on the top tilemap. Event 3 stops movement if we are waiting for the player to decide whether to accept a bribe or not, otherwise from 5 onwards we handle moving.

Event 5 reads the current tile, 6 handles 'floating' (you can go through walls by holding Shift down), and remembers the last good position in 8. If the current position is bad, it is reset to the last good in 9, this means it cannot leave the paths or rooms without "floating".

Moving reduces your hit points, which is tested for in 10-13. The original game had no concept of maximum hit points, you could heal to 1000000 HP if you were patient enough, so this is added. Different hitpoints are lost depending on whether you are floating or not.

Event 14-16 display the "Gold is near" message if the nearest object is near enough, the first time it is fired, and its collection is processed in 17 and 18. Event 19 autoheals if you aren't moving (originally you had to press "5" for this).

Events 20-24 make the game touch compatible (except there is no float option) and 24-25 check for win (all gold collected) and loss (HP < 1) states. Finally 26 and 27 hide the yes/no buttons when the queue is not empty, a bit of a fudge to improve the synchronisation.

Monsters

Events 2-3 reset all the monster HPs at the start, and 4-7 reset the monster HPs every 1/4 second for every monster except the one you are fighting. This does mean all the others change their HP all the time, but this doesn't matter particularly. Perhaps it could have been done better with an 'initialHP' value - in the original monsters recover when you aren't fighting them.

Event 8-10 are the same as gold, it prints a warning message when monsters are nears, and 11-13 process monster movement, moving them towards xTarget,yTarget if they aren't there already.

Fighting code starts at 15, when not in "offer" mode and colliding with monster the combatants are "wobbled" (brief application of sine to them). Event 16 calculates the damage and updates the scores, and if the player is alive, the option of a bribe (17/18) may be offered, in which case it is put into "offer mode" - the buttons are made visible to allow the player to decide (bribeAttempted is because you get a bribe offer for each monster once only). If not, the rest of the fight code is done in the "ExitFightCode" function because it is duplicated when you decline the bribe (should've been an FSM really).

Events 20-24 process that choice, clicking on a decision button, with the monster doing a bunk for half your gold in 21-22 and carrying on fighting in 23.

Event 23 is the fighting completion code. 27 checks for killing the monster, 28 for levelling up, and if you haven't killed it 29-31 fire, figuring out a new position as an angle from the player position - it attempts to move behind the player and if it can't somewhere random, the new 'move target' is upset in 31).

Event 33 resets a monsters HP to the maximum, here for easy changing, and 35 does the wobble effect, activating the sine for 0.6s using a timer.


Tuesday 14 July 2015

Game#12 : Dungeon completed

Game 12, the PET Dungeon remake, is complete and can be played here.

I have also added a link to its github (it isn't in the one with everything else) this also contains the original source code reverse engineered.

You can see it here. Truth be told, I'm not totally happy with this one. It's event driven, and in the original BASIC code there's quite a lot of "Print this" (wait) "Print that" (wait), a sequence of messages.

I've done this with a queue of messages, but they easily get behind the "action", I'm not really happy with the way this synchronises. I would probably use a state machine to lock all this stuff out if I was doing it for real. I could probably have made more use of WAIT as well, I think,

I will put the write up tomorrow - probably. Next thing will be something even more different, not actually a game at all. Just a quickie before I go on holiday, I don't want to leave Robotron half done.

It's one of these :


Most people, even those into retrocomputing, won't know what this is. It's a German machine, known as the "WDR-1", and it is the only "home micro" commercially available based on the MC14500B Microprocessor.

This is an interesting chip. It's only a one bit microprocessor, and it has no real branching and jumping instructions.

Monday 13 July 2015

Dungeon progress

Making reasonable progress with the Dungeon game. You can wander about a dungeon and collect gold, the monsters don't do anything yet and you can't attack them.

It's basically two tilemaps - the bottom one the maze, the upper one the 'reveal' - it's one of those CRPGs where the game is exposed as you go round (think of it as a primitive version of Sword of Fargoal ?) with sprites on top. These sprites I got from some free RPG tiles I found somewhere.

And it's got more recursive code in it ....

Saturday 11 July 2015

Retrochallenge 2015 Summer : Pet Dungeon

For various reasons, not excluding broadband problems and holidays, my Retrochallenge 2015 for Summer (to reverse engineer the code of the original Simon toy from 1974) will have to be delayed. It's about 40% done (reversing TMS1100 code is interesting to put it mildly) but I won't complete it in time. Not only is it the missus birthday this week, but it's my mum's as well, and we're going on holiday after that....

In its place, as a one week one shot, and because it fits my list - sort of, I'm going to convert this game to Construct 2.

It's one of the earliest semi commercial "RPG" type games, called just "Dungeon". It was sold as part of a tape based magazine called "Cursor" and precedes things like "Temple of Apshai". (I am actually old enough to have owned a Pet 2001 with the chicklet keys. Anyone who thinks game coding on C2 is hard should try that , or the machine I started on which is the same as the Science of Cambridge MK14 ....)

You can read more about it here, an excellent blog written by a total loony nice chap who wants to play every CRPG ever made. I'm not sure if this is possible ....

I will try and improve the graphics a little bit.

It will be quite an interesting exercise because like almost all code written in the 1970s, it is not event driven, but procedural.

This will have its own github as it is sort of seperate from the rest of the games.

Once I've done this, then I will move onto Robotron 2084 (Wahey !) and Defender (even better !), but I'm visiting Germany for a week with the extended family in about a week, so that will be into August.



Centipede ... how it works


There are two interesting things about this game. The first is it uses a finite state machine, the second is the method by which the centipede moves.

Globals

Declares assorted globals.  They are initialised in Event 4 (this is a one restart layout = one set of lives thing). There are some utility functions to update the lives, the score,, add a value to a score. Event 11 is used to randomise the colour on the little score thing that appears when you shoot some things (hence "On created").

Event 12-15 colours an object in the Colourable family by UID, replacing colour 5 (white in this scenario) with orange and adding a "white override" - this is because the poisoned mushrooms are white. The actual colour is derived from the level number.  The colour works by adding a SetColor effect in WebGL (hence it requires WebGL to provide the colour). You could do this by having different frames/animations in the different colours but that's not so much fun as playing with WebGL.

Mushrooms

Most of the code here is concerned with initialisation - it creates equally spaced mushrooms at the top, this is to break up the centipede parts early. Duplicate mushrooms are created at event 5 (I think I forgot to put the While in there .... oops). Events 5 and 6 handle the shooting and destroying of mushrooms (which require 3 hits each), and 7 scores when you finally beat the mushroom.

Finally event 8 causes any new mushroom to be coloured appropriately as per above.

Player

Event 2 initialises the positions, Event locks it in a "player area" at the bottom of the screen, and Event 4 fires a missile if the Ctrl key is pressed. Easy enough so far .... well it isn't now :)

Centipede

The heart of it. Each centipede is a singly linked list effectively, with the "nextUIDInChain" instance variable creating a list. The head of the list, which is also the head of the centipede is marked as such (isLeader instance variable).

It moves as follows. Each segment moves under its own code to a new "target" coordinate, but this target coordinate is passed down the list. So the head tells segment 1 to go where it is, segment 1 tells segment 2 to go where it is and so on.

Event 2 detects being shot. This splits the linked list, which becomes two seperate ones - event 3 stores the UID of the head of the new list.  Event 4 and 5 do the scoring, and create a new mushroom. Evemt 7, destroying the segment, finally breaks the list (the previous entry now points to a non existent UID). The new head is then selected and made up to be a leader (8 and 9).

Events 10-17 create a centipede of a given size. They are created with all the segments on top of each other (12 picks a spot). 13 and 14 create the body, keeping the  uid of the previous part of the chain in "uid", and 15 is the same except it just does the head.


Event 18 onwards moves the centipede(s). It does it with the leaders first (in 18) so that the orders are set up in the correct order.

Event 19 detects if the leader has reach its target. If it has, it calls the "PassNewTargetDown" function which causes each segment in the list to pass its current position on to the next segment as its new target.

Event 20 causes the reversal on hitting a mushroom, or being poisoned, or a collision(21) or being on the edge of the screen (22,23). If none of these applies the new target is one position either space horizontally in the current direction (24). If there was a reversal it is to move down and once that has completed continuing moving horizontally (25).

Event 26 and 27 keep the centipede in the player area at the bottom, making it endlessly cycle in that area.

Event 28 fires for all, and moves the object towards its currently specified target. Event 29 shouldn't be there and doesn't do anything anyway.

Event 29 is a recursive function which passes the target down the linked list - all it does is copy the target from the parameters and then call it with the next segments stuff. I often wondered if recursion worked in C2. It does.

Event 32-37 creates Centipedes or centipede heads dependent on the level, at one of two speeds.

Spider Scorpion and Fleas

This is actually three seperate files really, one for each of the other centipede characters. They all start in a similar fashion with timers for random intervals (e.g. 2 and 3) and the creation starts at 4. The sine behaviour causes the spider's zig zag effect vertically and a simple chaser moves it horizontally, this is done in 5. Every so often the 'target' of the chaser is recalculated by 6.

Events 7 and 8 deal with shooting it - the score depends on how far apart they are, and it spawns a transient score item and a particle effect.

Finally 9 and 10 turn the spider tune off and on as a spider is created or destroyed (there is only one at once).

The Flea initial code is similar (12,13) except they only appear on level 2. The first thing it does is count all the mushrooms in the player area, and if there aren't enough, fires the flea. This drops down the screen using a bullet behaviour, and creates mushrooms on the way down. It stops at the bottom (18) and can be shot (19) with particles and transient score again.

The Scorpion again is very similar. Event 23 creates it (Level 3+) and it moves horizontally, event 24 allow s it to go the other way. Any mushrooms it collides with are poisoned (25) which causes the centipede to crash to the bottom of the screen. They can be destroyed in event 26, but this also unpoisons all mushrooms on the same line (event 27), which isn't strictly accurate as it should only unpoison the ones its just poisoned, but its near enough.

State

This handles the four states of the game. In "new level" it creates a new level (basically centipedes) in 5, recolours all the objects in 6.

The main play state starts at 8. Hitting a segment or animal switches it to "life lost", and destroying all the segments switches it to a new (next) level in 10 and 11. 12 starts the thud thud thud noise.

Life lost starts at 15, where the basic adjustments are made, and the segments are all destroyed. Each mushroom flashes briefly which is set up by a timer in 17 and implemented in 18. When all these are completed (19) it switches either to new level (the same level again) or game over depending on whether there are any lives left, or not.

Finally, 23 and 24 implement the game over state which fades "Game over" onto the screen and restarts the layout when space is pressed.

Title Sheet

Events 3-5 create the wobbling centipede with the letters in (I did not use pin for this because it did not centre properly). Events 6-9 create a vague approximation of the Atari logo with horizontal lines, and 10 and 11 create the mushrooms

Each tick event 13 moves the letters with the circles, and 14 uses time to animate the raster bar. Finally 15 starts the game.

Centipede available to play

..... as the post title, the game is available to play here.

Github is up to date with this version, and I will put the write up in later today or possibly tomorrow. I have a fairly busy day today.

Those of you who know the 2600 version will appreciate my version of the Title page .... those of you who know how the 2600 works will appreciate what an impressive title page this is for a title for the 2600.


Friday 10 July 2015

And ... I'm back with Centipede (Atari 2600 version)

And I now have broadband again. Router appears to have blown up.

In the interim I have been working on Centipede VCS as you can see in the picture. In fact, it is pretty much finished except for the detail that it doesn't launch the right number of centipedes at the start of the level - other than that it is pretty much all working. No sound effects either, so I will finish this maybe tomorrow and then it can be played.


Tuesday 7 July 2015

Sunday 5 July 2015

Game # 11 : Centipede

So, the next game is Centipede. For a change (and because I dropped Pitfall off the list), I'm going to copy the Atari 2600/VCS version of Centipede.

This has the added advantage that my inability to draw, well, anything, doesn't show up too much. Even I can draw a square and fill it in :)

Just.

The first 80x86 Assembler program I wrote was an emulator for the Atari 2600, which is roughly akin to learning to swim by jumping into the middle of the Channel ....... I wrote it in annoyance because the then typical hardware (80386-25 ish) couldn't cope with Stella.

Game#10 Match 3 Completed

Match 3 is completed and can be played here.

The game core hasn't changed much but I've added a swipe left/right type selection screen. There are only four different layouts which are used with increasing difficulty levels, but it would be easy enough to add more in.

As always the source is in Github. One thing about this code is there are very few globals. Only things that actually need to be globals ; other variables are stored in singleton objects like "SelectCtrl".

Selection

Event 1 avoids copyright and requests the key (one value, the highest accessible level), which is processed in 7 and 8.  Events 3-6 create the shops you can see in the picture to the right, tracking the UIDs of first and last level.

The shops are updated on a per tick basis starting at 9. 10-14 move the shop towards a given "targetX" - this allows us to move the shops by just setting this value. Events 15-17 copy the size and positions and the cross visibility of each Selector (the shop). Effectively it responds immediately to changing variables. Event 18,19 and 20 handle taps on the shops, setting the level and running the game. Event 21 detects a touch end with swipe. Event 22/23 and 24/25 are very similar - the check is for direction, the first (or last) shop not being on the screen, and it not already being in motion. If this passses it sets the targetX value to move it.

Main Game

The game is driven by a state machine.

Setup

The primary purpose of setup is given a definition string specifying a level to create the initial display. It also contains the dropping code. Event 4 gets the string and decodes it into specific values. Event 5 takes the last part (defining the screen layout) and works out the level dimensions, and from that it works out how big each cell is and where they are. Events 6 and 7 create the grid where there is an "X" (e.g. event 9) and it also adds the grey behind tile. Where there isn't an "X" it creates a launcher, an object which spawns candies. Event 10 creates a row of blockers at the bottom of the grid. (If you disable comment 13 you will see these things in position, also see 2 posts back). Event 11 and 12 handles Launchers followed by Launchers or Blockers, replacing them with Blockers.

Event 14 onwards handles the dropping when the state is DROPPING. Firstly all the launchers are chosen (16) and if they do not already contain an item one is spawned (17). Event 18 access the items in backwards order up the screen, and if they are moving works out a new position (Event 19), If there is a collision with a stopper (blocker/launcher) or another item then the collision flag is set. In event 23, if there was a collision, the item is stopped and it is locked to a cell, otherwise Y is updated with the new position, moving it down the screen.

Events 26-29 cause the pulsing effect, and 30-33 is a function counting the number of items still moving that are in the game.

Matches

Matches is almost all code rather than events. It is a function that for each square counts the number of duplicates in a horizontal and vertical direction (right and down). Most of the work is done in 4, which calls two functions to scan in the directions and maintains the "best result" in each direction.

Event 6 counts the duplicate objects in a given direction. It keeps moving right (or down) whilst there is an object there in the game and the typeID (basically, the displayed graphic) is the same.

Control

Most of the work is here. We first go into NEWBLOCK state which is the start of a possible set of drops, this just resets the bonus multiplier and goes into DROPPING

DROPPING state starts at Event 5. The dropping is actually done in Setup, so 7,8,9 are just waiting for it to finish. Events 10 and 11 decide what to do next - if there is a match-3 (or ore) we go to REMOVE, otherwise we go to WAITSWAP. The first pass flag indicates the first pass, where you don't score, this is removing lines from the randomly selected items. If you watch the game it sort of "plays itself" for a few seconds creating a item grid with no Match-3s

REMOVING starts at 12. It basically picks H or V (horizontal or vertical) depending which is the best scoring, or randomly if they are the same, and then calls "destroyChunk" to delete them, having picked one match line that fits that (17,20) randomly (18,21). Effectively if there are (say) 3 vertical Matches it will pick on only. Each of these triggers an explosion, when these explosions have all finished (22) it goes back into DROPPING state to see if there are any more to process.

Events 24-29 are its helper function - it deletes n objects from x,y in direction dx,dy using a for loop. The exact behaviour depends on "first pass" - if not setting up it fires the explosions sequentially and adds to the score.

WAITSWAP is basically handling the selection and swapping over of adjacent cells. Standard Drag and Drop was deemed inadequate, so it is handled its own way. Initially, we go to EXIT (end of level) if there are no moves left.

Event 34 handles the "touch down". The calculations in the condition centre it on the middle of the Item (the collision area of the item is smaller than the bounding rectangle) and copies relevant information into a TouchManager object. Event 36 handles the drag part.

When we release in 37 (again with the centreing) we keep the drop target details (39) and if they are adjacent and not diagonal (40) we swap them over, otherwise we return the original item to its start place. Events 41,42 and 43 all deal with these tests failing, and just return the original item to its home point. These movements are handled by an ItemMover object whose responsibility it is to move an item to a position and then destroy itself - a bit like fade does.

When all the moves have finished and we swapped (44,45) we check to see if this has resulted in a Match 3, if so we start all the items moving again and go to NewBlock. If not (47) we return them to their original position.

Event 48 and 49 is a function which creates and sets up and ItemMover, and 50-52 use lerp to move it over time, 52 destroying it when it reaches the end position

Event 55 onwards is the exit state and is mostly graphic - all items explode, all tiles fade away, Layer 3 is made visible and a touch takes you back to the select screen, hopefully with "levelComplete" set - this is set when the score exceeds the target score in Event 29 when we are removing stuff.

Finally 62 is the big red button which abandons the level. You can still complete it.

Levels

I don't do much level design here, there are 4 of them (see event 3,4,5,6,7) which are concatenated with the number of discrete items that exist (6), a required score and maximum moves (all calculated in event 2). Effectively the layouts are used over and over again with harder target scores and more, but not quite enough moves.


Saturday 4 July 2015

Match 3 , core complete

A little progress on Match 3 today. Not as much as I'd like, I spent a lot of time tracking down occasional "stops" where an item just didn't drop.

Turned out in the end that when it dropped it very occasionally collided with the one dropping immediately below, causing it to stop.

Basically the core now works - you can play a level through to the end, all that is left is to complete the front end and the "unlocking" of levels.

Friday 3 July 2015

Match 3 continued ....

Actually doesn't look that different to yesterday but it's advanced quite a lot. The dropping code now has 'removal code' added (this has a state machine) and most of the dragging code added. To complete the game core all I really need is to do is to check the drop position is legal, swap them over, check that that creates a match, and undo it if it doesn't.

Scoring etc. can then be added, but that's not too difficult.

Ended up not using the drag and drop behaviour for this. The problem is that the collision box for the items is narrower than you might think, if not they get stuck coming down .... and it uses the same collision box for DnD. So there is an own-version of the DnD.

Also created an object whose purpose is to move one object to another position and then self destruct - a bit like Fade with moving. I might (not sure) be able to generalise this so it can move anything, at the moment it's just Item-class only, but this might require some tinkering. Not sure :)

Thursday 2 July 2015

Game#10 : Match 3

This is a learning exercise, not an actual efficiency exercise. The picture of a Match 3 game just screams "2 Dimensional Array", but I have decided in a moment of advanced masochism to do it without arrays.

So I had some fun with this. The bright idea worked well - you can see the outline of the game, but the faded purple blocks and arrows are seeders and blockers for the pieces - this is implementing the 'falling down' bit, with the arrows pushing the out and the squares stopping them. These have had their invisibility setting code disabled so you can see them, obviously this won't show in the end bit. I also use a faint pink square on the screen which is the 'board space' and everything resizes to fit in that space (you can see a faint pink behind the arrows in the middle)

I originally did this with Physics, but I wasn't happy with that, then I did it with bullets, and I couldn't get it quite right. The movement is now hard coded (basic physics stuff) and it finally works, though I had some fun with spurious collisions and it took twice as long as I thought.

I'm going to think Frogger with this - that had some code which scanned looking for lines of log bits, and that can be tweaked to check for lines of food.

The little square box in the top right does say 555 (this is a technical joke), it's job is maintenance of semi globals, which belong to the Layout but aren't real globals - things like the position of the grid and so on. I just think that is neater than bazillions of globals.

Wednesday 1 July 2015

Berzerk - how it works

Game and Title Events are fairly straightforward. The first is just a collection of includes, the second displays the title screen (see right) and functions in the same way as Asteroids.

Globals is mostly (err..) globals but does have a reset check - setting resetGame non zero starts a new game. This game restarts the layout when a life is lost or when the room changes.

MazeCreate is in many ways not dissimilar to the Pacman game, in that it tidies up the position, width and heights of the walls (events 7-11). The majority of the rest of the code implements the Berzerk pseudo random number generator explained here. This uses the room coordinates (x and y) to generate a random sequence which is predictable. The nested for conditions (3-6) set this up, and 4,5 and 6 adjust the position of the 'room middle bars' as extracted from the random seed (the "Set Wall to" in event 3)

Player handles the player code. Event 2 cancels firing - the animation and movement is disabled when the fire key is pressed. Events 3 to 11 manipulate an 8Direction behaviour in two ways, once to the set animation, and another to set the current direction, if any, in instance variables lastVectorX/Y so that the player knows which way to fire.

Event 12 handles actual firing, and 13-18 set the correct firing animation (it sort of "points a gun")

Event 19 handles the collision with 'non fatal walls' - these are not visible (there is one on each exit) and collision with this moves to another room. playerX/Y is the new direction (set in 20,21) and changeX/changeY set the direction of movement (event 20 and 21). Evemt 22 9 not wraps it, stops the game, movement, destroys everything except the frame and plays the audio (23 and 24) depending on whether there are any robots or not.

Event 25 handles scrolling - when you move, the current room 'scrolls off' and 25 to 26 does this, restarting the layout (next room) at the end.

Event 27  handles the bonus count (when you destroy all robots in a room) and this is done in the collision code. The rest are functions which update lives and score

Robots handles robots. The first thing we do is create them, one (possibly) in each of the 5 x 3 'quadrants' of the room, providing they aren't too close (1-5). There is a short delay before it starts - when it does (6,7,8) it starts moving, decides whether it is chasing horizontally or vertically and starts a timer which regularly flips the chase direction, and one for trying to fire.

Events 9 to 18 handle setups for the different robot types, which is dependent on the score, see strategywiki.org for what appears when.

Events 19 to 22 handle movement. It moves in the current direction (evaluateMove function works this out) and if it is overlapping a wall (the else in 21 is fired) it toggles the direction it is chasing in. Event 22 causes it to toggle randomly anyway.

Events 23 to 26 handle firing. Each robot keeps a count of how many it can fire total, and how many it has left. When it fires one, it decrements "bulletsLeft", but each bullet knows who fired it. When the bullet is destroyed the "bulletsLeft" value is incremented (in the colllision code). Event 24 works out the angle to the player, but the bullets only fire in diagonal or straight directions - the or in 25 checks this as "near enough" and the direction is ironed out to the nearest 45. Finally 26 doubles the speed if it is a fast shooting robot.

Event 27 is a function which works out which way the robot should move into the dx and dy instance variables, and sets the animation appropriately. It gets a Robot UID as a parameter.

Events 31-33 handle the infamous evil Otto. A timer to create him is set up in 31, He is created in 32 and the sine behaviour adjusted. This handles the vertical position automatically, but event 33 adjusts him horizontally with a simple chase, doubling the speed if there are no robots left.

Collisions handles (surprise...) The first 3 just stop all bullets against walls and other bullets. Events 4 and 5 allow RobotBullets to kill the player, and a robot (if it isn't the spawning robot). Event 6 allows player to kill robots, 7 and 8 stop collision with the wall or Otto, 9 allows Robots to blow up on collision with the players orrobots, and Event 10 fires if the robot hits a wall (which shouldn't happen !)

The consequences are dealt with in 11 on. Event 11 fires if the player dies, and spawns an explosion which for some reason fires in Event 17 (disorganised ....). Event 12 and 13 handle Robot Bullet destruction and the incrementing of "allowed bullets for this robot". Event 14-16 handle scoring for robot death, and the bonus (note Count = 1 because it still exists). Events 17-19 handle life lost at the end of the explosion(either go back to the title screen or restart the same layout)

Finally 20 starts the game off.

Berzerk (Released version)

Evil Otto is about to pounce.
So, Berzerk is complete and ready to run, and the github is up to date  (see the right).

I will add the 'write up' later on today (hopefully).

I have decided, as an aside, to do a Match-3 game next, as its a bit different, and added a couple of other games and sorted it a bit.

So the list is now.
  1. Squash (done)
  2. Pong (done)
  3. Breakout (done)
  4. Galaxians (done, no title screen)
  5. Asteroids (done)
  6. Jet Pac (done)
  7. Frogger (done)
  8. PacMan (done).
  9. Berzerk (done)
  10. Match 3 Game
  11. Centipede - yet another with a very odd movement behaviour.
  12. Robotron - another classic, a demented single screen shoot em up. 
  13. Some sort of RPG/Arcade simple game ; Atic Atac or that sort of thing.
  14. Defender - Robotron's "other half", with a scrolling screen.
  15. Lode Runner (once implemented this on a TI-83 calculator)