Friday, August 17, 2012

Creating a Pentomino game using AS3: Part 30

Today we'll add the ability to overwrite existing saved levels, as well as delete existing levels.

For the overwriting feature, go to saveLevelLocal() function in main.as class and add a new variable named "overwrite" typed integer. Set its value to overwriteIndex(levelObject.name). Using that method, we'll be able to get the index of the existing level in sharedObject with that name (if such level already exists). If the level is not found, the value is set to -1.

Then we check if value doesn't equal -1 - overwrite the level using the index. If the value actually equals 1 - add a completely new level to the database.

public function saveLevelLocal(grid:Array, shapes:Array, levelName:String):void {
var sharedObject:SharedObject = SharedObject.getLocal("myLevels");
if (sharedObject.data.levels == null) sharedObject.data.levels = [];
var levelObject:Object = new Object;
levelObject.grid = grid;
levelObject.shapes = shapes;
levelObject.name = levelName;
levelObject.played = 0;
var overwrite:int = overwriteIndex(levelObject.name);
if (overwrite != -1) {
// overwrite
sharedObject.data.levels[overwrite] = levelObject;
sharedObject.flush();
}else {
// add new
sharedObject.data.levels.push(levelObject);
sharedObject.flush();
}
}

The overwriteIndex() function just loops through all saved levels and compares their names to the provided name:

private function overwriteIndex(name:String):int {
var sharedObject:SharedObject = SharedObject.getLocal("myLevels");
var need:int = -1;
for (var i:int = 0; i < sharedObject.data.levels.length; i++) {
if (sharedObject.data.levels[i].name == name) {
need = i;
break;
}
}
return need;
}

Now open the project in Flash and add a new button in each saved level item MovieClip, give it an id of btn_delete. This button will be used to delete existing levels from the saved library of levels.

Go to saved_levels.as class and in the constructor add 3 click event listeners for these btn_delete buttons.

In their handlers, call deleteLevel() function and pass the level id as the parameter:

public function saved_levels() 
{
savedLevels = SharedObject.getLocal("myLevels");
btn_back.addEventListener(MouseEvent.CLICK, doBack);
if (savedLevels.data.levels != null) levels = savedLevels.data.levels;
tInfo.text = levels.length + " levels (" + savedLevels.size + "B)";
pages = Math.ceil(levels.length / 3);
goPage(1);
btn_previous.addEventListener(MouseEvent.CLICK, function() { goPage(currentPage - 1) } );
btn_next.addEventListener(MouseEvent.CLICK, function() { goPage(currentPage + 1) } );
item1.btn_play.addEventListener(MouseEvent.CLICK, function () { (root as MovieClip).playLevel(levels[3 * (currentPage-1)].grid, levels[3 * (currentPage-1)].shapes); savedLevels.data.levels[3 * (currentPage-1)].played++ } );
item2.btn_play.addEventListener(MouseEvent.CLICK, function () { (root as MovieClip).playLevel(levels[3 * (currentPage-1) + 1].grid, levels[3 * (currentPage-1) + 1].shapes); savedLevels.data.levels[3 * (currentPage-1)+1].played++} );
item3.btn_play.addEventListener(MouseEvent.CLICK, function () { (root as MovieClip).playLevel(levels[3 * (currentPage-1) + 2].grid, levels[3 * (currentPage-1) + 2].shapes); savedLevels.data.levels[3 * (currentPage-1) + 2].played++ } );
item1.btn_edit.addEventListener(MouseEvent.CLICK, function () { (root as MovieClip).editLevel(levels[3 * (currentPage-1)])} );
item2.btn_edit.addEventListener(MouseEvent.CLICK, function () { (root as MovieClip).editLevel(levels[3 * (currentPage-1) + 1])} );
item3.btn_edit.addEventListener(MouseEvent.CLICK, function () { (root as MovieClip).editLevel(levels[3 * (currentPage-1) + 2]) } );
item1.btn_delete.addEventListener(MouseEvent.CLICK, function () { deleteLevel(3 * (currentPage-1))} );
item2.btn_delete.addEventListener(MouseEvent.CLICK, function () { deleteLevel(3 * (currentPage-1) + 1)} );
item3.btn_delete.addEventListener(MouseEvent.CLICK, function () { deleteLevel(3 * (currentPage-1) + 2)} );
}

In the function we delete the item from the levels array using splice() method. Remember to flush() the SharedObject to apply changes. Update tInfo text value and "pages" value. Check if currentPage is still a valid page after deleting this item, and call goPage(currentPage) if so. If such page is now gone, use goPage(pages).

private function deleteLevel(index:int):void {
savedLevels.data.levels.splice(index, 1);
savedLevels.flush();
tInfo.text = levels.length + " levels (" + savedLevels.size + "B)";
pages = Math.ceil(levels.length / 3);
if (currentPage <= pages) {
goPage(currentPage);
}
if (currentPage > pages) {
goPage(pages);
}
}

Now we can overwrite and delete saved levels!

Full saved_levels.as code so far:

package  
{
import fl.controls.TextInput;
import flash.display.MovieClip;
import flash.display.Shape;
import flash.events.MouseEvent;
import flash.net.SharedObject;
import flash.text.TextField;

/**
 * Open-source pentomino game engine
 * @author Kirill Poletaev
 */

public class saved_levels extends MovieClip
{
private var savedLevels:SharedObject;
private var levels:Array = [];
private var pages:int;
private var currentPage:int;

public function saved_levels() 
{
savedLevels = SharedObject.getLocal("myLevels");
btn_back.addEventListener(MouseEvent.CLICK, doBack);
if (savedLevels.data.levels != null) levels = savedLevels.data.levels;
tInfo.text = levels.length + " levels (" + savedLevels.size + "B)";
pages = Math.ceil(levels.length / 3);
goPage(1);
btn_previous.addEventListener(MouseEvent.CLICK, function() { goPage(currentPage - 1) } );
btn_next.addEventListener(MouseEvent.CLICK, function() { goPage(currentPage + 1) } );
item1.btn_play.addEventListener(MouseEvent.CLICK, function () { (root as MovieClip).playLevel(levels[3 * (currentPage-1)].grid, levels[3 * (currentPage-1)].shapes); savedLevels.data.levels[3 * (currentPage-1)].played++ } );
item2.btn_play.addEventListener(MouseEvent.CLICK, function () { (root as MovieClip).playLevel(levels[3 * (currentPage-1) + 1].grid, levels[3 * (currentPage-1) + 1].shapes); savedLevels.data.levels[3 * (currentPage-1)+1].played++} );
item3.btn_play.addEventListener(MouseEvent.CLICK, function () { (root as MovieClip).playLevel(levels[3 * (currentPage-1) + 2].grid, levels[3 * (currentPage-1) + 2].shapes); savedLevels.data.levels[3 * (currentPage-1) + 2].played++ } );
item1.btn_edit.addEventListener(MouseEvent.CLICK, function () { (root as MovieClip).editLevel(levels[3 * (currentPage-1)])} );
item2.btn_edit.addEventListener(MouseEvent.CLICK, function () { (root as MovieClip).editLevel(levels[3 * (currentPage-1) + 1])} );
item3.btn_edit.addEventListener(MouseEvent.CLICK, function () { (root as MovieClip).editLevel(levels[3 * (currentPage-1) + 2]) } );
item1.btn_delete.addEventListener(MouseEvent.CLICK, function () { deleteLevel(3 * (currentPage-1))} );
item2.btn_delete.addEventListener(MouseEvent.CLICK, function () { deleteLevel(3 * (currentPage-1) + 1)} );
item3.btn_delete.addEventListener(MouseEvent.CLICK, function () { deleteLevel(3 * (currentPage-1) + 2)} );
}

private function goPage(pageNum:int):void {
currentPage = pageNum;
tPage.text = "Page " + currentPage + "/" + pages;

// level items
item1.alpha = item2.alpha = item3.alpha = 0;
item1.mouseEnabled = item2.mouseEnabled = item3.mouseEnabled = false;
item1.mouseChildren = item2.mouseChildren = item3.mouseChildren = false;


if (levels[3 * (currentPage-1)] != null) {
item1.alpha = 1;
item1.mouseEnabled = true;
item1.mouseChildren = true;
item1.tTitle.text = levels[3 * (currentPage-1)].name;
countShapes(item1, levels[3 * (currentPage-1)].shapes);
drawPreview(item1.levelPreview, levels[3 * (currentPage-1)].grid, item1.tSubtitle);
item1.tSubtitle2.text = "times played: " + levels[3 * (currentPage-1)].played;
}

if (levels[3 * (currentPage-1)+1] != null) {
item2.alpha = 1;
item2.mouseEnabled = true;
item2.mouseChildren = true;
item2.tTitle.text = levels[3 * (currentPage-1) + 1].name;
countShapes(item2, levels[3 * (currentPage-1) + 1].shapes);
drawPreview(item2.levelPreview, levels[3 * (currentPage-1) + 1].grid, item2.tSubtitle);
item2.tSubtitle2.text = "times played: " + levels[3 * (currentPage-1)+1].played;
}

if (levels[3 * (currentPage-1)+2] != null) {
item3.alpha = 1;
item3.mouseEnabled = true;
item3.mouseChildren = true;
item3.tTitle.text = levels[3 * (currentPage-1) + 2].name;
countShapes(item3, levels[3 * (currentPage-1) + 2].shapes);
drawPreview(item3.levelPreview, levels[3 * (currentPage-1) + 2].grid, item3.tSubtitle);
item3.tSubtitle2.text = "times played: " + levels[3 * (currentPage-1)+2].played;
}

// page navigation
btn_previous.alpha = btn_next.alpha = 0;
btn_previous.mouseEnabled = btn_next.mouseEnabled = false;

if (currentPage > 1) {
btn_previous.alpha = 1;
btn_previous.mouseEnabled = true;
}

if (currentPage < pages) {
btn_next.alpha = 1;
btn_next.mouseEnabled = true;
}
}

private function deleteLevel(index:int):void {
savedLevels.data.levels.splice(index, 1);
savedLevels.flush();
tInfo.text = levels.length + " levels (" + savedLevels.size + "B)";
pages = Math.ceil(levels.length / 3);
if (currentPage <= pages) {
goPage(currentPage);
}
if (currentPage > pages) {
goPage(pages);
}
}

private function countShapes(levelItem:MovieClip, shapes:Array):void {
for (var i:int = 1; i <= 12; i++) {
levelItem["tShape" + i].text = shapes[i - 1];
}
}

private function drawPreview(levelPreview:MovieClip, mapGrid:Array, subtitle:TextField):void {
var columns:int = mapGrid[0].length;
var rows:int = mapGrid.length;
var frameWidth:int = levelPreview.width;
var frameHeight:int = levelPreview.height;
var padding:int = 5;
var fitWidth:int = levelPreview.width - (padding * 2);
var fitHeight:int = levelPreview.height - (padding * 2);
var gridStartX:int;
var gridStartY:int;

// calculate width of a cell:
var gridCellWidth:int = Math.round(fitWidth / columns);

var width:int = columns * gridCellWidth;
var height:int = rows * gridCellWidth;

// calculate side margin
gridStartX = (frameWidth - width) / 2;

if (height < fitHeight) {
gridStartY = (fitHeight - height) / 2;
}
if (height >= fitHeight) {
gridCellWidth = Math.round(fitHeight / rows);
height = rows * gridCellWidth;
width = columns * gridCellWidth;
gridStartY = (frameHeight - height) / 2;
gridStartX = (frameWidth - width) / 2;
}

// draw map
levelPreview.shape.x = gridStartX;
levelPreview.shape.y = gridStartY;
levelPreview.shape.graphics.clear();
var i:int;
var u:int;

var totalCells:int = 0;
for (i = 0; i < rows; i++) {
for (u = 0; u < columns; u++) {
if (mapGrid[i][u] == 1) {
drawCell(u, i, 0xffffff, 1, 0x999999, gridCellWidth, levelPreview.shape);
totalCells++;
}
}
}
subtitle.text = totalCells + " cells";
}

private function drawCell(width:int, height:int, fill:uint, thick:Number, line:uint, gridCellWidth:int, gridShape:MovieClip):void {
gridShape.graphics.beginFill(fill);
gridShape.graphics.lineStyle(thick, line);
gridShape.graphics.drawRect(width * gridCellWidth, height * gridCellWidth, gridCellWidth, gridCellWidth);
}

private function doBack(evt:MouseEvent):void {
(root as MovieClip).gotoAndStop(1);
}
}

}

Thanks for reading!

The results:


No comments:

Post a Comment