A Rudimentary Tactics Game in JavaScript

The JavaScript below implements a very simple turn-based tactics game. To see it in action, copy it into an .html file and open that file in a web browser that runs JavaScript. Or, for an online version, visit http://thiscouldbebetter.neocities.org/tacticsgame.html.

There are two teams, each with two units. The active unit can move, attack, or pass, by either clicking the buttons with the mouse or pressing the keyboard shortcuts given on those buttons. Attacks affect only the grid cell directly in front of the attacking unit. Each unit has only one hit point. The game continues until only one team remains.

TurnBasedTacticsGame

UPDATE 2018/08/08 – Added unit types with multiple moves and variable range and damage and an obstacle terrain type. Added a status panel for the selected unit. Refactored slightly to match my more recent code standards. I have also moved the code into a Github repository available at “https://github.com/thiscouldbebetter/TacticsGame“.


<html>
<body>
<div id="divMain" />
<script type="text/javascript">
 
// main
 
function main()
{
	var actionMovePerform = function(direction)
	{
		var world = Globals.Instance.world;
		var moverActive = world.moverActive();
		var targetPos = moverActive.targetPos;
		if (targetPos == null)
		{
			var moverOrientation = moverActive.orientation;
	 
			if (moverOrientation.equals(direction) == true)
			{
				var moverPosNext = moverActive.pos.clone().add
				(
					direction
				).trimToRangeMax
				(
					world.map.sizeInCellsMinusOnes
				);
				
				var terrain = world.map.terrainAtPos(moverPosNext);
				var movePointsToTraverse = terrain.movePointsToTraverse;
				if (moverActive.movePoints >= movePointsToTraverse)
				{
					if (world.moverAtPos(moverPosNext) == null)
					{
						moverActive.pos.overwriteWith
						(
							moverPosNext
						);  
						moverActive.movePoints -= movePointsToTraverse;
					}
				}
			}
	 
			moverOrientation.overwriteWith
			(
				direction
			);
		}
		else
		{
			var targetPosNext = targetPos.clone().add
			(
				direction
			).trimToRangeMax
			(
				world.map.sizeInCellsMinusOnes
			);
			
			var targetDisplacementNext = targetPosNext.clone().subtract
			(
				moverActive.pos
			);
			
			var targetDistanceNext = targetDisplacementNext.magnitude();
			if (targetDistanceNext <= moverActive.defn().attackRange)
			{
				targetPos.overwriteWith(targetPosNext)
			}
		}
	}
 
	var actions = 
	[
		new Action
		(
			"Attack",
			"F", // keyCode
			function perform()
			{
				var world = Globals.Instance.world;
				var moverActive = world.moverActive();
				if (moverActive.targetPos == null)
				{
					moverActive.targetPos = moverActive.pos.clone().add
					(
						moverActive.orientation
					);
				}
				else
				{
					var moverTarget = world.moverAtPos
					(
						moverActive.targetPos
					);
				 
					if (moverTarget != null)
					{
						moverTarget.integrity -= moverActive.defn().attackDamage;
					}
	 
					moverActive.movePoints = 0;
					
					moverActive.targetPos = null;
				}
			}
		),		
		new Action
		(
			"Down",
			"S", // keyCode
			function perform()
			{
				actionMovePerform(new Coords(0, 1));
			}
		),
		new Action
		(
			"Left",
			"A", // keyCode
			function perform()
			{
				actionMovePerform(new Coords(-1, 0));
			}
		),
		new Action
		(
			"Right",
			"D", // keyCode
			function perform()
			{
				actionMovePerform(new Coords(1, 0));
			}
		),
		new Action
		(
			"Up",
			"W", // keyCode
			function perform()
			{
				actionMovePerform(new Coords(0, -1));
			}
		),
		new Action
		(
			"Pass",
			"P", // keyCode
			function perform()
			{
				var world = Globals.Instance.world;
				var moverActive = world.moverActive();
 
				moverActive.movePoints = 0;
			}
		),
	];
 
	var actionNamesStandard = [ "Attack", "Up", "Down", "Left", "Right", "Pass" ];
 
	var moverDefns = 
	[
		new MoverDefn
		(
			"Slugger", 
			"A", 
			3, // integrityMax
			1, // movePointsPerTurn
			1, // attackRange
			2, // attackDamage
			actionNamesStandard
		),
 
		new MoverDefn
		(
			"Sniper", 
			"B",  
			2, // integrityMax
			1, // movePointsPerTurn
			3, // attackRange
			1, // attackDamage
			actionNamesStandard
		),
		
		new MoverDefn
		(
			"Sprinter", 
			"C", 
			1, // integrityMax
			3, // movePointsPerTurn
			1, // attackRange
			1, // attackDamage
			actionNamesStandard
		),		
	];
 
	var mapTerrains = 
	[
		new MapTerrain("Open", ".", 1, "White"),
		new MapTerrain("Blocked", "x", 100, "LightGray"),
	];
 
	var map = new Map
	(
		new Coords(20, 20), // cellSizeInPixels
		new Coords(20, 20), // pos
		mapTerrains,
		// cellsAsStrings
		[
			"........",
			"....x...",
			"....x...",
			"....x...",
			"........",
			"...xxx..",
			"........",
			"........",
		]
	);
 
	var factions = 
	[
		new Faction("Green", "LightGreen"),
		new Faction("Red", "Pink"),
	];
 
	var world = new World
	(
		actions,
		moverDefns,
		map,
		factions,
		// movers
		[
			new Mover
			(
				"Slugger", // defnName
				"Green", // faction
				new Coords(1, 0), // orientation
				new Coords(1, 1) // pos
			),
 
			new Mover
			(
				"Sniper", // defnName
				"Red", // faction
				new Coords(1, 0), // orientation
				new Coords(3, 1) // pos
			),
			
			new Mover
			(
				"Sprinter", // defnName
				"Red", // faction
				new Coords(1, 0), // orientation
				new Coords(1, 3) // pos
			),
			 
			new Mover
			(
				"Sniper", // defnName
				"Green", // faction
				new Coords(1, 0), // orientation
				new Coords(3, 3) // pos
			),
 
			new Mover
			(
				"Slugger", // defnName
				"Red", // faction
				new Coords(1, 0), // orientation
				new Coords(5, 3) // pos
			),
			
			new Mover
			(
				"Sprinter", // defnName
				"Green", // faction
				new Coords(1, 0), // orientation
				new Coords(5, 1) // pos
			),			
		]
	);
 
	Globals.Instance.initialize
	(
		new Display(new Coords(300, 200)),
		world
	);
}
 
 
// extensions
 
function ArrayExtensions()
{
	// extension class
}
{
	Array.prototype.addLookups = function(keyName)
	{
		for (var i = 0; i < this.length; i++)
		{
			var item = this[i];
			var key = item[keyName];
			this[key] = item;
		}
 
		return this;
	}
 
	Array.prototype.remove = function(itemToRemove)
	{
		var indexToRemoveAt = this.indexOf(itemToRemove);
		if (indexToRemoveAt != -1)
		{
			this.removeAt(indexToRemoveAt);
		}
	}
 
	Array.prototype.removeAt = function(indexToRemoveAt)
	{
		this.splice
		(
			indexToRemoveAt, 1
		);
	}
}
 
// classes
 
function Action(name, keyCode, perform)
{
	this.name = name;
	this.keyCode = keyCode;
	this.perform = perform;
}
{
	Action.prototype.toControl = function()
	{
		var returnValue = new ControlButton
		(
			"button" + this.name, // name
			this.name + " (" + this.keyCode + ")", // text
			new Coords(50, 12), // size
			new Coords(), // pos
			this.perform.bind(this)
		);
 
		return returnValue;
	}
}
 
function Camera(viewSize, pos)
{
	this.viewSize = viewSize;
	this.pos = pos;
}
 
function Control()
{
	// static class
}
{
	Control.doesControlContainPos = function(control, posToCheck)
	{
		var posToCheckRelative = posToCheck.clone().subtract
		(
			control.posAbsolute()
		);
 
		var returnValue = posToCheckRelative.isInRangeMax
		(
			control.size
		);
 
		return returnValue;
	}
 
	Control.controlPosAbsolute = function(control)
	{
		var returnValue = (control.parent == null ? new Coords(0, 0) : control.parent.posAbsolute());
		returnValue.add(control.pos);
		return returnValue;
	}
 
	Control.toControlsMany = function
	(
		controllables, posOfFirst, spacing
	)
	{
		var returnValues = [];
 
		for (var i = 0; i < controllables.length; i++)
		{
			var controllable = controllables[i];
 
			var control = controllable.toControl();
 
			control.pos.overwriteWith
			(
				spacing
			).multiplyScalar
			(
				i
			).add
			(
				posOfFirst
			);
 
			returnValues.push(control);
		}
 
		return returnValues;
	}
}
 
function ControlButton(name, text, size, pos, click)
{
	this.name = name;
	this.text = text;
	this.size = size;
	this.pos = pos;
	this.click = click;
}
{
	ControlButton.prototype.containsPos = function(posToCheck)
	{
		return Control.doesControlContainPos(this, posToCheck);
	}
 
	ControlButton.prototype.draw = function(display)
	{
		var posAbsolute = this.posAbsolute();
 
		display.drawRectangle
		(
			posAbsolute, 
			this.size, 
			display.colorFore,
			display.colorBack
		);
 
		display.drawTextAtPos
		(
			this.text,
			posAbsolute
		);
	}
 
	ControlButton.prototype.mouseClick = function(mouseClickPosAbsolute)
	{
		if (this.containsPos(mouseClickPosAbsolute) == true)
		{
			this.click();
		}
	}
 
	ControlButton.prototype.posAbsolute = function()
	{
		return Control.controlPosAbsolute(this);
	}
}
 
function ControlContainer(name, size, pos, children)
{
	this.name = name;
	this.size = size;
	this.pos = pos;
	this.children = children;
 
	for (var i = 0; i < this.children.length; i++)
	{
		var child = this.children[i];
		child.parent = this;
	}
}
{
	ControlContainer.prototype.containsPos = function(posToCheck)
	{
		return Control.doesControlContainPos(this, posToCheck);
	}
 
	ControlContainer.prototype.draw = function(display)
	{
		display.drawRectangle
		(
			this.posAbsolute(),
			this.size,
			display.colorFore, // border
			display.colorBack // fill
		);
 
		var children = this.children;
		for (var i = 0; i < children.length; i++)
		{
			var child = children[i];
			child.draw(display);
		}
	}
 
	ControlContainer.prototype.mouseClick = function(mouseClickPosAbsolute)
	{
		if (this.containsPos(mouseClickPosAbsolute) == true)
		{
			for (var i = 0; i < this.children.length; i++)
			{
				var child = this.children[i];
				child.mouseClick(mouseClickPosAbsolute);
			}
		}
	}
 
	ControlContainer.prototype.posAbsolute = function()
	{
		return Control.controlPosAbsolute(this);
	}
}
 
function ControlLabel(name, pos, text)
{
	this.name = name;
	this.pos = pos;	
	this.text = text;
}
{
	ControlLabel.prototype.containsPos = function(posToCheck)
	{
		return Control.doesControlContainPos(this, posToCheck);
	}
 
	ControlLabel.prototype.draw = function(display)
	{ 
		display.drawTextAtPos
		(
			this.text,
			this.posAbsolute()
		);
	}
 
	ControlLabel.prototype.posAbsolute = function()
	{
		return Control.controlPosAbsolute(this);
	}
}

function ControlLabelDynamic(name, pos, textFunction)
{
	this.name = name;
	this.pos = pos;
	this.textFunction = textFunction;
}
{
	ControlLabelDynamic.prototype.containsPos = function(posToCheck)
	{
		return Control.doesControlContainPos(this, posToCheck);
	}
 
	ControlLabelDynamic.prototype.draw = function(display)
	{ 
		display.drawTextAtPos
		(
			this.text(),
			this.posAbsolute()
		);
	}
 
	ControlLabelDynamic.prototype.posAbsolute = function()
	{
		return Control.controlPosAbsolute(this);
	}
	
	ControlLabelDynamic.prototype.text = function()
	{
		return this.textFunction();
	}
}
 
function Coords(x, y)
{
	this.x = x;
	this.y = y;
}
{
	Coords.prototype.add = function(other)
	{
		this.x += other.x;
		this.y += other.y;
		return this;
	}
 
	Coords.prototype.clone = function()
	{
		return new Coords(this.x, this.y);
	}
 
	Coords.prototype.divide = function(other)
	{
		this.x /= other.x;
		this.y /= other.y;
		return this;
	}
 
	Coords.prototype.divideScalar = function(scalar)
	{
		this.x /= scalar;
		this.y /= scalar;
		return this;
	}
 
	Coords.prototype.equals = function(other)
	{
		var returnValue = 
		(
			this.x == other.x
			&& this.y == other.y
		);
 
		return returnValue;
	}
 
	Coords.prototype.isInRangeMax = function(rangeMax)
	{
		var returnValue = 
		(
			this.x >= 0
			&& this.x <= rangeMax.x
			&& this.y >= 0
			&& this.y <= rangeMax.y
		);
 
		return returnValue;
	}
	
	Coords.prototype.magnitude = function()
	{
		return Math.sqrt(this.x * this.x + this.y * this.y);
	}
 
	Coords.prototype.multiply = function(other)
	{
		this.x *= other.x;
		this.y *= other.y;
		return this;
	}
 
	Coords.prototype.multiplyScalar = function(scalar)
	{
		this.x *= scalar;
		this.y *= scalar;
		return this;
	}
 
	Coords.prototype.overwriteWith = function(other)
	{
		this.x = other.x;
		this.y = other.y;
		return this;
	}
 
	Coords.prototype.overwriteWithXY = function(x, y)
	{
		this.x = x;
		this.y = y;
		return this;
	}
 
	Coords.prototype.subtract = function(other)
	{
		this.x -= other.x;
		this.y -= other.y;
		return this;
	}
 
	Coords.prototype.trimToRangeMax = function(rangeMax)
	{
		if (this.x < 0)
		{
			this.x = 0;
		}
		else if (this.x > rangeMax.x)
		{
			this.x = rangeMax.x;
		}
 
		if (this.y < 0)
		{
			this.y = 0;
		}
		else if (this.y > rangeMax.y)
		{
			this.y = rangeMax.y;
		}
 
		return this;
	}
}
 
function Display(viewSize)
{
	this.viewSize = viewSize;
 
	this.fontHeight = 10;
	this.colorBack = "White";
	this.colorFore = "LightGray";
	this.colorHighlight = "Gray";
 
	// temporary variables
 
	this._drawPos = new Coords();
	this._drawPos2 = new Coords();
	this._mapCellPos = new Coords();
	this._zeroes = new Coords(0, 0);
}
{
	Display.prototype.clear = function()
	{
		this.drawRectangle
		(
			this._zeroes,
			this.viewSize,
			this.colorFore, // border
			this.colorBack // fill
		);
	}
 
	Display.prototype.drawCircle = function
	(
		center, radius, colorBorder, colorFill
	)
	{
		this.graphics.beginPath();
		this.graphics.arc
		(
			center.x, center.y,
			radius,
			0, Math.PI * 2  
		);
 
		this.graphics.fillStyle = colorFill;
		this.graphics.fill();
 
		this.graphics.strokeStyle = colorBorder;
		this.graphics.stroke();
	}
 
	Display.prototype.drawLine = function
	(
		posFrom, posTo, color
	)
	{
		this.graphics.beginPath();
		this.graphics.moveTo(posFrom.x, posFrom.y);
		this.graphics.lineTo(posTo.x, posTo.y);
		this.graphics.strokeStyle = color;
		this.graphics.stroke();
	}
	
	Display.prototype.drawRectangle = function
	(
		pos, size, colorBorder, colorFill
	)
	{
		this.graphics.fillStyle = colorFill;
		this.graphics.fillRect
		(
			pos.x, pos.y, size.x, size.y
		);
 
		this.graphics.strokeStyle = colorBorder;
		this.graphics.strokeRect
		(
			pos.x, pos.y, size.x, size.y
		);
	}
 
	Display.prototype.drawTextAtPos = function(text, pos, color)
	{
		if (color == null)
		{
			color = this.colorFore;
		}
 
		this.graphics.fillStyle = color;
		this.graphics.fillText
		(
			text,
			pos.x, 
			pos.y + this.fontHeight
		);
	}
 
	Display.prototype.initialize = function()
	{
		this.canvas = document.createElement("canvas");
		this.canvas.width = this.viewSize.x;
		this.canvas.height = this.viewSize.y;
	 
		var divMain = document.getElementById("divMain");
		divMain.appendChild(this.canvas);
 
		this.graphics = this.canvas.getContext("2d");
		this.graphics.font = "" + this.fontHeight + "px sans-serif";
	}
}
 
function Faction(name, color)
{
	this.name = name;
	this.color = color;
}
 
function Globals()
{
	// do nothing
}
{
	// instance
 
	Globals.Instance = new Globals();
 
	Globals.prototype.initialize = function(display, world)
	{
		this.world = world;
 
		this.display = display;
		this.display.initialize();
 
		this.inputHelper = new InputHelper();
		this.inputHelper.initialize();
 
		this.world.initialize();
	}
 
	Globals.prototype.update = function()
	{
		this.world.update();
	}
}
 
function InputHelper()
{
	// do nothing
}
{
	InputHelper.prototype.initialize = function()
	{
		this.isMouseClicked = false;
		this.mousePos = new Coords();
 
		document.onkeydown = this.handleEventKeyDown.bind(this);
		document.onkeyup = this.handleEventKeyUp.bind(this);
 
		var canvas = Globals.Instance.display.canvas;
		canvas.onmousedown = this.handleEventMouseDown.bind(this);
		canvas.onmouseup = this.handleEventMouseUp.bind(this);
	}
 
	// events
 
	InputHelper.prototype.handleEventKeyDown = function(event)
	{
		this.keyCodePressed = String.fromCharCode(event.keyCode);
		Globals.Instance.update();
	}
 
	InputHelper.prototype.handleEventKeyUp = function(event)
	{
		this.keyCodePressed = null;
	}
 
	InputHelper.prototype.handleEventMouseDown = function(event)
	{
		this.isMouseClicked = true;
		this.mousePos.overwriteWithXY
		(
			event.offsetX,
			event.offsetY
		);
		Globals.Instance.update();
	}
 
	InputHelper.prototype.handleEventMouseUp = function(event)
	{
		this.isMouseClicked = false;
	}
}
 
function Map(cellSizeInPixels, pos, terrains, cellsAsStrings)
{
	this.cellSizeInPixels = cellSizeInPixels;
	this.pos = pos;
	this.terrains = terrains;
	this.cellsAsStrings = cellsAsStrings;
 
	this.cellSizeInPixelsHalf = this.cellSizeInPixels.clone().divideScalar(2);
 
	this.terrains.addLookups("codeChar");
 
	this.sizeInCells = new Coords
	(
		this.cellsAsStrings[0].length,
		this.cellsAsStrings.length
	);
 
	this.sizeInCellsMinusOnes = this.sizeInCells.clone().subtract
	(
		new Coords(1, 1)
	);
}
{
	Map.prototype.terrainAtPos = function(cellPos)
	{
		var terrainChar = this.cellsAsStrings[cellPos.y][cellPos.x];
		var terrain = this.terrains[terrainChar];
		return terrain;
	}
	
	// drawable
	
	Map.prototype.draw = function(display)
	{
		var map = this;
		var sizeInCells = map.sizeInCells;
		var mapCellSizeInPixels = map.cellSizeInPixels;
		var cellPos = display._mapCellPos;
		var drawPos = display._drawPos;
 
		for (var y = 0; y < sizeInCells.y; y++)
		{
			cellPos.y = y;
 
			for (var x = 0; x < sizeInCells.x; x++)
			{
				cellPos.x = x;
 
				var cellTerrain = map.terrainAtPos
				(
					cellPos
				);
 
				drawPos.overwriteWith
				(
					cellPos
				).multiply
				(
					mapCellSizeInPixels
				).add
				(
					map.pos
				);
 
				display.drawRectangle
				(
					drawPos,
					mapCellSizeInPixels,
					this.colorFore, // border
					cellTerrain.color // fill
				);
			}
		}
	}
	
}
 
function MapTerrain(name, codeChar, movePointsToTraverse, color)
{
	this.name = name;
	this.codeChar = codeChar;
	this.movePointsToTraverse = movePointsToTraverse;	
	this.color = color;
}
 
function Mover(defnName, factionName, orientation, pos)
{
	this.defnName = defnName;
	this.factionName = factionName;
	this.orientation = orientation;
	this.pos = pos;
}
{
	Mover.prototype.defn = function()
	{
		return Globals.Instance.world.moverDefns[this.defnName];
	}
 
	Mover.prototype.faction = function()
	{
		return Globals.Instance.world.factions[this.factionName];
	}
	
	Mover.prototype.name = function()
	{
		return this.factionName + " " + this.defnName;
	}
 
	Mover.prototype.initialize = function()
	{
		var defn = this.defn();
		this.integrity = defn.integrityMax;
		this.movePoints = defn.movePointsPerTurn;
	}
	
	// drawable
	
	Mover.prototype.draw = function(display, map, isMoverActive)
	{
		var mover = this;
		var moverDefn = mover.defn();
 
		var mapCellSizeInPixels = map.cellSizeInPixels;
		var mapCellSizeInPixelsHalf = map.cellSizeInPixelsHalf;
 
		var drawPos = display._drawPos;
		var drawPos2 = display._drawPos2;
 
		drawPos.overwriteWith
		(
			mover.pos
		).multiply
		(
			mapCellSizeInPixels
		).add
		(
			map.pos
		).add
		(
			mapCellSizeInPixelsHalf
		);
 
		var radius = mapCellSizeInPixelsHalf.x;
 
		var colorStroke = (isMoverActive == true ? display.colorHighlight : display.colorFore);
 
		display.drawCircle
		(
			drawPos,
			radius,
			colorStroke,
			mover.faction().color
		);
 
		drawPos2.overwriteWith
		(
			mover.orientation
		).multiplyScalar
		(
			radius
		).add
		(
			drawPos
		);
 
		display.drawLine(drawPos, drawPos2, colorStroke);
 
		drawPos.subtract(mapCellSizeInPixelsHalf);
 
		display.drawTextAtPos(" " + moverDefn.codeChar, drawPos, colorStroke);
		
		if (isMoverActive == true)
		{
			if (this.targetPos != null)
			{
				drawPos.overwriteWith
				(
					this.targetPos
				).multiply
				(
					mapCellSizeInPixels
				).add
				(
					map.pos
				).add
				(
					mapCellSizeInPixelsHalf
				);
				display.drawCircle(drawPos, radius / 2, colorStroke, "Red");
			}
		}
	}	
}
 
function MoverDefn
(
	name,
	codeChar,
	integrityMax,
	movePointsPerTurn,
	attackRange,
	attackDamage,
	actionNamesAvailable
)
{
	this.name = name;
	this.codeChar = codeChar;
	this.integrityMax = integrityMax;
	this.movePointsPerTurn = movePointsPerTurn;
	this.attackRange = attackRange;
	this.attackDamage = attackDamage;
	this.actionNamesAvailable = actionNamesAvailable;
}
{
	MoverDefn.prototype.actionsAvailable = function()
	{
		var returnValues = [];
 
		var actionsAll = Globals.Instance.world.actions;
 
		for (var i = 0; i < this.actionNamesAvailable.length; i++)
		{
			var actionName = this.actionNamesAvailable[i];
			var action = actionsAll[actionName];
			returnValues.push(action);
		}
 
		return returnValues;
	}
}
 
function World(actions, moverDefns, map, factions, movers)
{
	this.actions = actions;
	this.moverDefns = moverDefns;
	this.map = map;
	this.movers = movers;
	this.factions = factions;
 
	this.actions.addLookups("name");
	this.factions.addLookups("name");
	this.moverDefns.addLookups("name");
 
	this.moversToRemove = [];
}
{
	World.prototype.moverActive = function()
	{
		var returnValue = null;
 
		if (this.indexOfMoverActive != null)
		{
			returnValue = this.movers[this.indexOfMoverActive];
		}
 
		return returnValue;
	}
 
	World.prototype.moverActiveAdvanceIfNeeded = function()
	{
		var moverActive = this.moverActive();
 
		if (moverActive == null)
		{		   
			this.moversReplenish();
			this.indexOfMoverActive = 0;
			moverActive = this.moverActive();
		}
		else if (moverActive.movePoints <= 0)
		{
			this.indexOfMoverActive++;
			if (this.indexOfMoverActive >= this.movers.length)
			{
				this.moversReplenish();
				this.indexOfMoverActive = 0;
			}
 
			moverActive = this.moverActive();
		}
 
		return moverActive;
	}
 
	World.prototype.moverAtPos = function(posToCheck)
	{
		var returnValue = null;
 
		for (var i = 0; i < this.movers.length; i++)
		{
			var mover = this.movers[i];
			if (mover.pos.equals(posToCheck) == true)
			{
				returnValue = mover;
				break;
			}
		}
 
		return returnValue;
	}
 
	World.prototype.moversReplenish = function()
	{
		for (var i = 0; i < this.movers.length; i++)
		{
			var mover = this.movers[i];
			mover.movePoints = mover.defn().movePointsPerTurn;
		}
	}
 
	World.prototype.initialize = function()
	{
		for (var i = 0; i < this.movers.length; i++)
		{
			var mover = this.movers[i];
			mover.initialize();
		}
 
		var moverActive = this.moverActiveAdvanceIfNeeded();
 
		this.containerMain = new ControlContainer
		(
			"containerMain",
			new Coords(90, 180), // size
			new Coords(200, 10), // pos
			[
				new ControlContainer
				(
					"containerActions",
					new Coords(70, 90), // size
					new Coords(10, 10), // pos
					// children
					Control.toControlsMany
					(
						moverActive.defn().actionsAvailable(),
						new Coords(10, 10), // posFirst
						new Coords(0, 12) // spacing
					)
				),
				
				new ControlContainer
				(
					"containerSelection",
					new Coords(70, 60), // size
					new Coords(10, 110), // pos
					// children
					[
						new ControlLabelDynamic
						(
							"labelFaction", // name
							new Coords(5, 5), // pos
							function textFunction() 
							{ 
								return Globals.Instance.world.moverActive().factionName
							}
						),
						
						new ControlLabelDynamic
						(
							"labelDefnName", // name
							new Coords(5, 15), // pos
							function textFunction() 
							{ 
								return Globals.Instance.world.moverActive().defnName
							}
						),
						
						new ControlLabelDynamic
						(
							"labelIntegrity", // name
							new Coords(5, 25), // pos
							function textFunction() 
							{ 
								var moverActive = Globals.Instance.world.moverActive();
								var moverDefn = moverActive.defn();
								return "Health:" + moverActive.integrity + "/" + moverDefn.integrityMax;
							}
						),
						
						new ControlLabelDynamic
						(
							"labelIntegrity", // name
							new Coords(5, 35), // pos
							function textFunction() 
							{ 
								var moverActive = Globals.Instance.world.moverActive();
								var moverDefn = moverActive.defn();
								return "Moves:" + moverActive.movePoints + "/" + moverDefn.movePointsPerTurn;
							}
						)
						
					]
				),
			]
		);
		
		this.update();
	}
 
	World.prototype.update = function()
	{
		this.update_Input();
 
		this.update_MoversIntegrityCheck();
 
		this.moverActiveAdvanceIfNeeded();
 
		this.update_VictoryCheck();
 
		this.draw(Globals.Instance.display);
	}
 
	World.prototype.update_Input = function()
	{
		var inputHelper = Globals.Instance.inputHelper;
		if (inputHelper.isMouseClicked == true)
		{
			inputHelper.isMouseClicked = false;
			this.containerMain.mouseClick
			(
				inputHelper.mousePos
			);
		}
		else if (inputHelper.keyCodePressed != null)
		{
			var moverActive = this.moverActive();
			if (moverActive != null)
			{
				var keyCodePressed = inputHelper.keyCodePressed;
				var moverActions = moverActive.defn().actionsAvailable();
				for (var i = 0; i < moverActions.length; i++)
				{
					var moverAction = moverActions[i];
					if (moverAction.keyCode == keyCodePressed)
					{
						moverAction.perform();
						break;
					}
				}
			}
		}
	}
 
	World.prototype.update_MoversIntegrityCheck = function()
	{
		this.moversToRemove.length = 0;
 
		for (var i = 0; i < this.movers.length; i++)
		{
			var mover = this.movers[i];
			if (mover.integrity <= 0)
			{
				this.moversToRemove.push(mover);
			}
		}
 
		for (var i = 0; i < this.moversToRemove.length; i++)
		{
			var mover = this.moversToRemove[i];
			this.movers.remove(mover);
		}
	}
 
	World.prototype.update_VictoryCheck = function()
	{
		var factionNamesPresent = [];
 
		for (var i = 0; i < this.movers.length; i++)
		{
			var mover = this.movers[i];
			var moverFactionName = mover.factionName;
			if (factionNamesPresent[moverFactionName] == null)
			{
				factionNamesPresent[moverFactionName] = moverFactionName;
				factionNamesPresent.push(moverFactionName);
 
				if (factionNamesPresent.length > 1)
				{
					break;
				}
			}		   
		}
 
		if (factionNamesPresent.length < 2)
		{
			var factionNameVictorious = factionNamesPresent[0];
			alert("The " +  factionNameVictorious + " team wins!");
		}
	}
	
	// drawable
	
	World.prototype.draw = function(display)
	{
		var world = this;
		display.clear();
 
		var map = world.map;
 
		map.draw(display);
		
		var movers = this.movers;
		for (var i = 0; i < movers.length; i++)
		{
			var mover = movers[i];
			mover.draw
			(
				display, 
				map, 
				false // isMoverActive
			);
		}   
 
		var mover = world.moverActive();
		mover.draw
		(
			display, 
			map, 
			true // isMoverActive
		);
 
		world.containerMain.draw(display, display._zeroes);
	}
}
 
// run
 
main();
 
</script>
</body>
</html>

This entry was posted in Uncategorized and tagged , , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s