Thursday 16 July 2015

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.


No comments:

Post a Comment