package
{
import flash.geom.*;
import flash.utils.Dictionary;
public class LevelDefinitions
{
protected static var instance:LevelDefinitions = null;
protected var levelDefinitions:Dictionary = new Dictionary();
static public function get Instance():LevelDefinitions
{
if ( instance == null )
instance = new LevelDefinitions();
return instance;
}
public function LevelDefinitions()
{
}
public function addLevelDefinition(levelID:int, element:LevelDefinitionElement):void
{
if (levelDefinitions[levelID] == null)
levelDefinitions[levelID] = new Array();
(levelDefinitions[levelID] as Array).push(element);
levelDefinitions[levelID].sort(LevelDefinitionElement.sort);
}
public function getNextLevelDefinitionElements(levelID:int, lastTime:Number):Array
{
var returnArray:Array = new Array();
var nextTime:Number = -1;
if (levelDefinitions[levelID] != null)
{
for each (var levelDefElement:LevelDefinitionElement in levelDefinitions[levelID])
{
if (levelDefElement.time > lastTime && nextTime == -1)
{
returnArray.push(levelDefElement);
nextTime = levelDefElement.time;
}
else if (levelDefElement.time == nextTime)
{
returnArray.push(levelDefElement);
}
else if (levelDefElement.time > nextTime && nextTime != -1)
break;
}
}
return returnArray.length == 0?null:returnArray;
}
public function getNextLevelID(levelID:int):int
{
if (levelDefinitions[levelID + 1] == null) return 0;
return levelID + 1;
}
public function startup():void
{
GameObjectManager.Instance.addCollidingPair( CollisionIdentifiers.PLAYER, CollisionIdentifiers.ENEMY);
GameObjectManager.Instance.addCollidingPair( CollisionIdentifiers.ENEMY, CollisionIdentifiers.PLAYERWEAPON);
GameObjectManager.Instance.addCollidingPair( CollisionIdentifiers.PLAYER, CollisionIdentifiers.ENEMYWEAPON);
LevelDefinitions.Instance.addLevelDefinition(
1,
new LevelDefinitionElement(
1,
function():void {(Enemy.pool.ItemFromPool as Enemy).startupBasicEnemy(
ResourceManager.SmallBluePlaneGraphics,
new Point(100, -ResourceManager. SmallBluePlaneGraphics.bitmap.height),
55);}));
LevelDefinitions.Instance.addLevelDefinition(
1,
new LevelDefinitionElement(
4,
function():void
{
for each (var xPos:int in [150, 350])
{
(Enemy.pool.ItemFromPool as Enemy).startupBasicEnemy(
ResourceManager.SmallBluePlaneGraphics,
new Point(xPos, -ResourceManager. SmallBluePlaneGraphics.bitmap.height),
55);
}
}
));
LevelDefinitions.Instance.addLevelDefinition(
1,
new LevelDefinitionElement(
5,
function():void {(Enemy.pool.ItemFromPool as Enemy).startupBasicEnemy(
ResourceManager.SmallBluePlaneGraphics,
new Point(500, -ResourceManager. SmallBluePlaneGraphics.bitmap.height),
55);}));
LevelDefinitions.Instance.addLevelDefinition(
2,
new LevelDefinitionElement(
1,
function():void {(Enemy.pool.ItemFromPool as Enemy).startupBasicEnemy(
ResourceManager.SmallGreenPlaneGraphics,
new Point(100, -ResourceManager. SmallGreenPlaneGraphics.bitmap.height),
55);}));
LevelDefinitions.Instance.addLevelDefinition(
2,
new LevelDefinitionElement(
3,
function():void
{
for each (var xPos:int in [150, 350])
{
(Enemy.pool.ItemFromPool as Enemy).startupBasicEnemy(
ResourceManager.SmallGreenPlaneGraphics,
new Point(xPos, -ResourceManager. SmallGreenPlaneGraphics.bitmap.height),
55);
}
}
));
LevelDefinitions.Instance.addLevelDefinition(
2,
new LevelDefinitionElement(
5,
function():void {(Enemy.pool.ItemFromPool as Enemy).startupBasicEnemy(
ResourceManager.SmallGreenPlaneGraphics,
new Point(500, -ResourceManager. SmallGreenPlaneGraphics.bitmap.height),
55);}));
}
public function shutdown():void
{
}
}
}
Apart from the singelton property there is only one other property: levelDefinitions. This is a dictionary, where the key is the level ID (level 1, 2, 3 etc), and the value is an array of LevelDefinitionElements. Remember that a LevelDefinitionElement defines a function to be called after a certain amount of time has passed in a level. So a call to levelDefinitions[1][0].func() would effectively mean that we are calling the function (levelDefinitions[1][0].func()) stored by the earliest level element (levelDefinitions[1][0].func() - remember they are sorted in accending order by the time property) stored for level 1(levelDefinitions[1][0].func()).
This should become clearer once we look at the startup function. Lets disect one of the enemy placements.
LevelDefinitions.Instance.addLevelDefinition(
1,
new LevelDefinitionElement(
4,
function():void
{
for each (var xPos:int in [150, 350])
{
(Enemy.pool.ItemFromPool as Enemy).startupBasicEnemy(
ResourceManager.SmallBluePlaneGraphics,
new Point(xPos, -ResourceManager.SmallBluePlaneGraphics.bitmap.height),
55);
}
}
));
We are making a call to addLevelDefinition, which essentially just adds a LevelDefinitionElement to the levelDefinitions dictionary. The first variable we pass is the level ID. We are passing 1, which means that we are adding a placement to the first level.
The second variable we pass in is a LevelDefinitionElement. The first variable in that constructor is the time when this LevelDefinitionElement should be executed. We are passing in 4, which means that this LevelDefinitionElement will be executed 4 seconds into the level.
The second variable we pass to the LevelDefinitionElement constructor is an anonymous function. An anonymous function doesn't have a name, so you can only call it through a Function object. Otherwise the code is exactly the same as a normal function. As you can see the function we have here creates two new enemy objects.
The end result of this is that after 4 seconds in the first level two new enemies will be created.
Once you wrap you head around using functions as objects it should be clear that this allows us an amazingly simple way to define our level structure. We are not constrained by a simple XML format - anything that can be done with Actionscript can be scripted to occur at a certain point during a level. We have used the code to create a new enemy here, but the possibilities are endless.
(A small change too is that we have moved the definitions of the colliding GameObject pairs into the startup function from the Application object. This is to keep all game "definition" code in one place.)
Of course we need a way of calling these functions at the correct point in the game. The Level class will take care of that. Lets look at that code now.