Christmas Couples: the finished Poux prototype

This is the last step of the prototype of a Flash game likePoux. Read steps 1, 2 and 3 before continuing.

As said, I finished the game. I called it Christmas Couples

Christmas Couples

Let’s see its features:

* Fancy graphics
* Improved scoring system
* Cool sound loop
* Highscores

As you can see, graphics, sound and highscores are developed from third parts, so I focused only on coding and gameplay.

This is a pretty good example of a copycat game. It’s coded with 204 lines and it took about 6 hours to be completed… and now I am going to explain line by line how it’s made.

Ok.. time to show you some actionscript… obviously all in the 1st (2nd to tell the truth…) frame!

stop();
import ab3.rankz.*;
field = Array();
_root.attachMovie("splash","splash",_root.getNextHighestDepth());
_root.attachMovie("playbutton","playbutton",_root.getNextHighestDepth());
_root.attachMovie("soundbutton","soundbutton",_root.getNextHighestDepth());
playbutton._x = 280;
playbutton._y = 285;
soundbutton._x = 430;
soundbutton._y = 335;
music = new Sound(this);
music.attachSound("music");
music.start(0,10000);
playing_the_game = false;
function __rankz_send__(par1, par2, par3, par4) {
	// cannot show you this line or you may hack highscores
	// cannot show you this line or you may hack highscores
	// cannot show you this line or you may hack highscores
	// cannot show you this line or you may hack highscores
	// cannot show you this line or you may hack highscores
	// cannot show you this line or you may hack highscores
	// cannot show you this line or you may hack highscores
	// cannot show you this line or you may hack highscores
	// cannot show you this line or you may hack highscores
	xxxxx.onLoad = function(success) {
		if (success) {
			rankz_t10_receive = new LoadVars();
			// cannot show you this line or you may hack highscores
			// cannot show you this line or you may hack highscores
			rankz_t10_receive.onLoad = function(success) {
				if (success) {
					_rankz_ar_ = rankz_t10_receive.top10.split("");
					for (i=0; i<_rankz_ar_.length; i++) {
						tempv = _rankz_ar_[i].split("");
						ohno["rankz_n"+(i+1)].text = tempv[0];
						ohno["rankz_v"+(i+1)].text = tempv[1];
					}
				}
			};
			rankz_t10_send.sendAndLoad("http://rankz.armorbot.com/get/top10.php",rankz_t10_receive,"POST");
		}
		else {
			// cannot show you this line or you may hack highscores
		}
	};
}
function game_init() {
	removed = 0;
	score = 0;
	interval = 250;
	x2_horiz_pos = 0;
	mult = 1;
	game_over = 0;
	turns = 0;
	tiles_placed = 0;
	for (x=0; x<10; x++) {
		field[x] = Array();
		for (y=0; y<10; y++) {
			field[x][y] = 0;
		}
	}
	_root.createEmptyMovieClip("things",_root.getNextHighestDepth());
	score_obj.score.text = "Score: "+score;
	place_line();
}
playbutton.onPress = function() {
	player_name = splash.your_name.text;
	playing_the_game = true;
	_root.attachMovie("bg","bg",_root.getNextHighestDepth());
	_root.attachMovie("x2_horiz","x2_horiz",_root.getNextHighestDepth(),{_x:10});
	_root.createEmptyMovieClip("bar",_root.getNextHighestDepth());
	_root.attachMovie("score","score_obj",_root.getNextHighestDepth(),{_x:340, _y:10});
	game_init();
	splash.removeMovieClip();
	_root.soundbutton._y = 10000;
	this._y=10000;
};
soundbutton.onPress = function() {
	music.stop();
};
function place_line() {
	for (x=0; x<10; x++) {
		tiles_placed++;
		if (field[x][0] != 0) {
			push_blocks(x);
		}
		tile = things.attachMovie("tile", "tile_"+tiles_placed, tiles_placed, {_x:10+32*x, _y:300});
		num = Math.floor(Math.random()*6)+1;
		tile.gotoAndStop(num);
		field[x][0] = tiles_placed;
	}
}
_root.onEnterFrame = function() {
	if (!game_over) {
		interval--;
		if (interval == 0) {
			turns++;
			interval = 250-turns;
			place_line();
			x2_horiz_pos = Math.floor(Math.random()*10);
			x2_horiz._y = 300-x2_horiz_pos*32;
		}
		bar.clear();
		bar.lineStyle(6,0xfff153);
		bar.moveTo(10,350);
		bar.lineTo(328,350);
		bar.lineStyle(4,0xea8400);
		bar.moveTo(10,350);
		bar.lineTo(10+318*interval/(250-turns),350);
	}
};
function push_blocks(col_number) {
	for (i=9; i>=0; i--) {
		if (!game_over) {
			if (field[col_number][i] != 0) {
				if (i != 9) {
					field[col_number][i+1] = field[col_number][i];
					things["tile_"+field[col_number][i]]._y -= 32;
				}
				else {
					things._alpha = 50;
					game_over = 1;
					bXlnYW1lX25hbWVfdmFyaWFibGU = player_name;
					bXlnYW1lX3Njb3JlX3ZhcmlhYmxl = score;
					__rankz_send__("MTk0MWolZSVhJW4lcw==","cHltcFpTaGI=",bXlnYW1lX25hbWVfdmFyaWFibGU,bXlnYW1lX3Njb3JlX3ZhcmlhYmxl);
					rankz_t10_send = new LoadVars();
					_root.attachMovie("ohno","ohno",_root.getNextHighestDepth(),{_x:75, _y:5});
					again_button = ohno.attachMovie("playagain", "playagain", _root.getNextHighestDepth());
					again_button._x = 100;
					again_button._y = 300;
					again_button.onPress = function() {
						things.removeMovieClip();
						game_init();
						ohno.removeMovieClip();
					};
				}
			}
		}
	}
}
onMouseDown = function () {
	if (playing_the_game) {
		if (!game_over) {
			x_tile_clicked = Math.floor((_root._xmouse-10)/32);
			y_tile_clicked = -Math.floor((_root._ymouse-300)/32);
			if ((x_tile_clicked>=0) and (x_tile_clicked<=9) and (y_tile_clicked>=0) and (y_tile_clicked<=9)) {
				if (field[x_tile_clicked][y_tile_clicked] != 0) {
					remove_tiles(x_tile_clicked,y_tile_clicked,1);
					update_field();
					for (i=1; i<=removed; i++) {
						score += (i*mult);
					}
					score_obj.score.text = "Score: "+score;
					removed = 0;
					mult = 1;
				}
			}
		}
	}
};
function remove_tiles(tx, ty, clicked) {
	tile_type = things["tile_"+field[tx][ty]]._currentframe;
	if (!clicked) {
		things["tile_"+field[tx][ty]].onEnterFrame = function() {
			this._y++;
			this._alpha--;
			if (this._alpha == 0) {
				this.removeMovieClip();
			}
		};
		field[tx][ty] = 0;
		removed++;
		if (ty == x2_horiz_pos) {
			mult = 2;
		}
	}
	if ((field[tx+1][ty] != 0) and (things["tile_"+field[tx+1][ty]]._currentframe == tile_type)) {
		remove_tiles(tx+1,ty);
	}
	if ((field[tx-1][ty] != 0) and (things["tile_"+field[tx-1][ty]]._currentframe == tile_type)) {
		remove_tiles(tx-1,ty);
	}
	if ((field[tx][ty+1] != 0) and (things["tile_"+field[tx][ty+1]]._currentframe == tile_type)) {
		remove_tiles(tx,ty+1);
	}
	if ((field[tx][ty-1] != 0) and (things["tile_"+field[tx][ty-1]]._currentframe == tile_type)) {
		remove_tiles(tx,ty-1);
	}
}
function update_field() {
	for (i=0; i<10; i++) {
		for (j=1; j<10; j++) {
			if ((field[i][j] != 0) and (field[i][j-1] == 0)) {
				falling = j-1;
				while ((field[i][falling] == 0) and (falling>=0)) {
					field[i][falling] = field[i][falling+1];
					things["tile_"+field[i][falling+1]]._y += 32;
					field[i][falling+1] = 0;
					falling--;
				}
			}
		}
	}
}

Line 1: Have to stop in this frame because I installed MochiAds and MochiBot on the previuos frame. Yes, I am going to (try to) monetize this game too…

Line 2: This is a mandatory line if you want to use highscores feature provided by ArmorBot

Line 3: Array containing the game field

Line 4: Attaching the splash movieclip. It contains the splash screen, the one you see when you start the game

Line 5: Attaching the play button

Line 6: Attaching the “Stop Sound” button

Lines 7-10: Adjusting play button and sound button positions.

Line 11: Creating a new sound variable

Line 12: Attaching the sound clip linked as music

Line 13: Playing the sound clip 10,000 time starting from second zero (the beginning)

Lines 15-46: Highscores management. This is the code provided by ArmorBot. I only made some changes to make result appear where I want them to appear and to show top 10 only after the last score was submitted.

Line 47: game_init function. This function is called at every new play.

Line 48: Setting removed tiles to zero

Line 49: Setting the score to zero

Line 50: Setting the interval between two new row insertions to 250 (frames)

Line 51: Setting the initial position of the horizontal green bonus bar

Line 52: Setting multiplier bonus to 1

Line 53: Setting game_over to zero (it’s not game over)

Line 54: Settings turns to zero

Line 55: Setting tile placed to zero

Lines 56-61: Initializing the game field with all zeros. Now the board is empty

Line 62: Creating the movieclip that will contain the objects

Line 63: Initializing the text variable that will display the current score (initially zero as seen on line 49)

Line 64: Calling the place_line function. This function will place a new line (line 81)

Line 66: This is the function to be executed when the player presses “play” on the splash screen

Line 67: Saving the name the player entered in the splash screen in a variable called player_name

Line 68: Setting a variable to tell we are actually playing the game

Line 69: Attaching the background of the game

Line 70: Attaching the horizontal green bonus bar

Line 71: Creating an empty movieclip that will contain the time bar

Line 72: Attaching the movieclip that will contain the score

Line 73: Calling the game_init function we saw at line 47

Line 74: Removing the splash screen

Line 75: Moving the Stop Sound button off the stage. I had to do in this way because removeMovieClip does not work on buttons

Line 76: Same thing with the play button itself

Line 78: Function to call if the player presses the Stop Sound button

Line 79: Stop the music

Line 81: Function to place a new line

Line 82: Counting from 1 to 10 (the number of columns)

Line 83: Increasing tiles_placed variable, to keep count of how many tiles we have placed so far

Line 84: If there is a tile in the spot where we should add a new tile…

Line 85: Call the function push_blocks. This function il push blocks from the bottom, to raise the stack of tiles

Line 87: Attaching the new tile

Line 88: Generation of a random number between 1 and 6 and saving it into num

Line 89: Stopping the tile movieclip at the num-th frame. Obviously I have a different tile on each frame

Line 90: Saving tiles_placed value in the field array

Line 93: Function to be executed at every frame

Line 94: If it’s not game over…

Line 95: Decreasing interval… time is passing…

Line 96: If interval reaches zero…

Line 97: Increasing turns variable

Line 98: Setting the new interval value to 250-turns. This means new lines will be placed faster and faster. How faster? 1 frame faster than the last one

Line 99: Calling the function place_line (line 81)

Line 100: Finding a new random position for the horizontal green bonus bar

Line 101: Moving the horizontal green bonus bar to its new position

Line 103: Clearing the bar movieclip

Line 104-109: Displaying the time bar by drawing two lines, one a bit thicker than another. To know more about Flash drawing routines refer to Create a flash draw game like Line Rider or others – part 1

Line 112: Function to push blocks (called at line 85)

Line 113: Scanning all vertical tiles in the col_number column

Line 114: If it’s not game over…

Line 115: If the place we are scanning is not empty…

Line 116: If the row is not the top last row… (it’s very important because if I don’t have more space where to store tiles, it’s game over)

Lines 117-118: Bringing the tile at ith to the ith+1 position… both in the field array and physically

Line 120: If it was the top last row…

Line 121: Making tiles half transparent (it’s game over man…)

Line 122: Setting game_over to 1 (I told you it’s game over…)

Lines 123-126: Sending scores to the highscore table

Line 127: Attaching the game-over movieclip (the one with the highscores)

Lines 128-130: Placing the “play again” button

Line 131: Function to be executed when the player presses the Play again button

Line 132: Remove the movieclip with the tiles

Line 133: Calling game_init function to start a new game (line 47)

Line 134: Removing the game over movieclip

Line 141: Function ti be executed when the player clicks the mouse

Line 142: Checking if we are playing the game

Line 143: Checking if it’s not game over (I could merge this line with the above one with an and)

Lines 144-145: Determining what tile did I click according to mouse coordinates

Line 146: Checking if it’s really a spot where it could be a tile

Line 147: Checking in the field array if there is a tile in the tile-space I clicked

Line 148: If all conditions are true, then call the remove_tiles function you see at line 161

Line 149: Once the tiles have been removed, call update_field function (line 190)

Lines 150-152: Calculating the score for the last move in this way: 2 tiles removed = 2+1= 3 points; 3 tiles removed = 3+2+1 = 6 points… and so on. Score is then multiplied by the multiplier

Line 153: Updating the score string

Line 154: Reset removed variable to zero

Line 155: Reset mutliplier variable to 1

Line 161: Function remove_tiles called at line 148. Three arguments: the x position, the y position and a flag to determine if the tile to remove is the clicked one (that’s why I called with the last argument set to 1 at line 148)

Line 162: Determining which type of tile (star, snowflake, …) I am going to remove according to its current frame

Line 163: If the tile is not the clicked one

Line 164: Function to be executed at every frame for that tile

Line 165: Moving it down by 1 pixel

Line 166: Making it a bit more transparent

Lines 167-169: If it’s completely transparent, then remove it! This is the animation you see when you click a tile

Line 171: Setting its field position to zero… now there is an empty space

Line 172: Increasing remove variable

Lines 173-175: If the tile was in the horizontal green bonus bar, then set the multiplier at 2

Lines 177-179: If there is a tile of the same type on the right of the tile just clicked (or remove) then recursively call remove_tiles for that tile with the clicked argument at zero

Lines 180-182: Same thing with the left tile

Lines 183-188: Same thing with the above and below tiles.

The clicked flag is used to not remove the clicked tile if there aren’t adjacent tiles of the same type

Line 190: Beginning of the update_field function (the last one!) called at line 149

Lines 191-192: Scanning the entire game field

Line 193: If there is an empty space under the field position we are looking at…

Line 194: Assign to falling the vertical position of the tile that must fall down

Line 195: While there is an empty space under the falling tile and it hasn’t reached the “ground”…

Lines 196-198: Updating field array and physically moving down the tile

Line 199: The tile has moved down so falling must decrease

And it’s over! This is the longest all-in-once explaination of this blog! I hope this will be useful

Now I am submitting the game to some portals, then I’ll release the source code.

Meanwhile… enjoy!

Get the most popular Phaser 3 book

Through 202 pages, 32 source code examples and an Android Studio project you will learn how to build cross platform HTML5 games and create a complete game along the way.

Get the book

214 GAME PROTOTYPES EXPLAINED WITH SOURCE CODE
// 2048
// Dots
// Maze
// Pool
// Poux
// Pudi
// qomp
// Turn
// Zhed