Get the full commented source code of

HTML5 Suika Watermelon Game

Talking about Roguelike game, Actionscript 3, Flash and Game development.

If you are a regular reader, you should know sometimes I try to create Flash game in less than 2KB.

After Sokoban, Bejeweled and a math game with an original concept, it’s time to show you my Roguelike game in less than 2KB, here it is:

The roguelike is a sub-genre of role-playing video games, characterized by random level generation, tile-based graphics and permanent death. Roguelikes descend from the 1980 game Rogue, particularly mirroring Rogue’s character- or sprite-based graphics, turn-based gameplay that gives the player the time to plan each move, and high fantasy setting (source).

Let’s see what I was able to do in only 2KB:

* Random level generation
* Level progression
* Rogue style graphics
* Player energy
* Player score
* Basic enemy AI
* Game over screen

Have a look at it:

You control the green “9” (which also represent your energy) with arrow keys and must kill all red “M” (the monsters).

You kill a monster when you are in a cell adjacent to the monster and move towards it.

A monster hurts you when you end your turn in a cell adjacent to the monster.

But the most interesting thing of the script, unlike my previous 2KB experiments, it I managed to have a quite legible code.

package {

	import flash.display.Sprite;
	import flash.text.TextField;
	import flash.events.KeyboardEvent;

	[SWF(backgroundColor="0x222222")]

	public class Main extends Sprite {
		
		private var rows:Number=38;
		private var cols:Number=52;
		private var monsters:Number=30;
		private var map:Array;
		private var emptyRatio:Number=0.85;
		private var heroRow:Number;
		private var heroCol:Number;
		private var enemyRange:Number=10;
		private var energy:Number=9;
		private var score:Number=0;
		private var level:Number=1;
		private var gameOver:Boolean=false;
		private var gameOverSprite:Sprite;

		public function Main() {
			setup();
			drawMap();
			stage.addEventListener(KeyboardEvent.KEY_UP, movePlayer);
		}

		private function movePlayer(e:KeyboardEvent):void {
			if (! gameOver) {
				var keyPressed:Number=e.keyCode;
				switch (keyPressed) {
					case 37 :
						addTurn(-1,0);
						break;
					case 38 :
						addTurn(0,-1);
						break;
					case 39 :
						addTurn(1,0);
						break;
					case 40 :
						addTurn(0,1);
						break;
				}
			}
			else {
				gameOver=false;
				score=0;
				energy=9;
				level=1;
				setup();
				drawMap();
			}
		}

		private function checkMove(i:Number,j:Number,d:Number):Boolean {
			var deltaRow:Number=(d%2)*(2-d);
			var deltaCol:Number=((d+1)%2)*(d-1);
			if (i+deltaRow>=0 && i+deltaRow<rows && j+deltaCol>=0 && j+deltaCol<cols && map[i+deltaRow][j+deltaCol]==".") {
				map[i][j]=".";
				if (d==1||d==2) {
					map[i+deltaRow][j+deltaCol]="F";
				}
				else {
					map[i+deltaRow][j+deltaCol]="M";
				}
				return true;
			}
			return false;
		}

		private function addTurn(deltaCol,deltaRow):void {
			if (heroRow+deltaRow>=0&&heroRow+deltaRow<rows&&heroCol+deltaCol>=0&&heroCol+deltaCol<cols&&map[heroRow+deltaRow][heroCol+deltaCol]!="#") {
				if (map[heroRow+deltaRow][heroCol+deltaCol]==".") {
					map[heroRow+deltaRow][heroCol+deltaCol]=energy.toString();
					map[heroRow][heroCol]=".";
					heroRow+=deltaRow;
					heroCol+=deltaCol;
				}
				else {
					if (map[heroRow+deltaRow][heroCol+deltaCol]=="M") {
						map[heroRow+deltaRow][heroCol+deltaCol]=".";
						score+=level;
					}
				}
				var monsterCount:Number=0;
				var enemyDir:Number=-1;
				for (var i:Number=0; i<rows; i++) {
					for (var j:Number=0; j<cols; j++) {
						if (map[i][j]=="M") {
							monsterCount++;
							var manhattan:Number=Math.abs(i-heroRow)+Math.abs(j-heroCol);
							if (manhattan==1) {
								energy-=level;
								if (energy<0) {
									energy=0;
								}
								map[heroRow][heroCol]=energy.toString();
							}
							else {
								if (manhattan<enemyRange) {
									if (Math.abs(i-heroRow)>Math.abs(j-heroCol)) {
										if (i>heroRow) {
											enemyDir=3;
										}
										else {
											enemyDir=1;
										}
									}
									else {
										if (j>heroCol) {
											enemyDir=0;
										}
										else {
											enemyDir=2;
										}
									}
								}
								if (enemyDir==-1 || !checkMove(i,j,enemyDir)) {
									var secureCount=0;
									do {
										secureCount++;
										enemyDir=Math.floor(Math.random()*4);
									} while (!checkMove(i,j,enemyDir)&&secureCount<10);
								}
							}
						}
						else {
							if (map[i][j]=="F") {
								map[i][j]="M";
							}
						}
					}
				}
				if (monsterCount==0) {
					level++;
					setup();
				}
				drawMap();
			}
		}

		private function setup():void {
			map=new Array();
			for (var i:Number = 0; i < rows; i++) {
				map[i]=new Array();
				for (var j:Number = 0; j < cols; j++) {
					if (Math.random()>emptyRatio) {
						map[i].push('#');
					}
					else {
						map[i].push('.');
					}
				}
			}
			for (i = 0; i <= monsters+5*level; i++) {
				do {
					var randomRow:Number=Math.floor(Math.random()*rows);
					var randomCol:Number=Math.floor(Math.random()*cols);
				} while (map[randomRow][randomCol]!=".");
				if (i==0) {
					heroRow=randomRow;
					heroCol=randomCol;
					map[randomRow][randomCol]=energy.toString();
				}
				else {
					map[randomRow][randomCol]="M";
				}
			}
		}

		private function drawMap():void {
			for (var k:Number=numChildren-1; k>=0; k--) {
				removeChild(getChildAt(k));
			}
			for (var i:Number = 0; i < rows; i++) {
				for (var j:Number = 0; j < cols; j++) {
					var t:TextField=new TextField();
					t.autoSize="center";
					t.selectable=false;
					addChild(t);
					t.x=j*12+10;
					t.y=i*12+8;
					t.text=map[i][j];
					switch (map[i][j]) {
						case "M" :
							t.textColor=0xff0000;
							break;
						case "." :
							t.textColor=0x666666;
							break;
						case "#" :
							t.textColor=0xFFFFFF;
							break;
						default :
							t.textColor=0x00ff00;
					}
				}
			}
			if (energy==0) {
				gameOver=true;
				gameOverSprite=new Sprite();
				addChild(gameOverSprite);
				gameOverSprite.graphics.lineStyle(1,0x000000,0.6);
				gameOverSprite.graphics.beginFill(0x000000,0.6);
				gameOverSprite.graphics.drawRect(0,0,640,480);
				gameOverSprite.graphics.endFill();
				t=new TextField();
				t.width=500;
				t.text="YOUR SCORE: "+score.toString()+" - PRESS ANY KEY TO RESTART";
				addChild(t);
				t.textColor=0x00ff00;
				t.selectable=false;
			}
		}
	}
}

When looking at the source code, please consider it’s optimized for small compiled swf size.

Download the source code.

Never miss an update! Subscribe, and I will bother you by email only when a new game or full source code comes out.