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


<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 moverOrientation = moverActive.orientation;

		if (moverOrientation.equals(direction) == true)
		{
			var moverPosNext = moverActive.pos.clone().add
			(
				direction
			).trimToRangeMax
			(
				world.map.sizeInCellsMinusOnes
			);

			if (world.moverAtPos(moverPosNext) == null)
			{
				moverActive.pos.overwriteWith
				(
					moverPosNext
				);	
				moverActive.movePoints--;
			}
		}

		moverOrientation.overwriteWith
		(
			direction
		);
	}

	var actions = 
	[
		new Action
		(
			"Attack",
			"F", // keyCode
			function perform()
			{
				var world = Globals.Instance.world;
				var moverActive = world.moverActive();

				var cellPosTarget = moverActive.pos.clone().add
				(
					moverActive.orientation
				);
				var moverTarget = world.moverAtPos
				(
					cellPosTarget
				);

				if (moverTarget != null)
				{
					moverTarget.integrity--;
				}

				moverActive.movePoints = 0;
			}
		),
		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
		(
			"MoverDefnA", 
			"A", 
			1, // integrityMax
			1, // movePointsPerTurn
			actionNamesStandard
		),

		new MoverDefn
		(
			"MoverDefnB", 
			"B",  
			1, // integrityMax
			1, // movePointsPerTurn
			actionNamesStandard
		),
	];

	var mapTerrains = 
	[
		new MapTerrain("MapTerrain0", ".", "White"),
	];

	var map = new Map
	(
		new Coords(20, 20), // cellSizeInPixels
		new Coords(20, 20), // pos
		mapTerrains,
		// cellsAsStrings
		[
			"........",
			"........",
			"........",
			"........",
			"........",
			"........",
			"........",
			"........",
		]
	);

	var factions = 
	[
		new Faction("Green", "LightGreen"),
		new Faction("Red", "Pink"),
	];

	var world = new World
	(
		actions,
		moverDefns,
		map,
		factions,
		// movers
		[
			new Mover
			(
				"MoverDefnA", // defnName
				"Green", // faction
				new Coords(1, 0), // orientation
				new Coords(1, 1) // pos
			),

			new Mover
			(
				"MoverDefnB", // defnName
				"Red", // faction
				new Coords(1, 0), // orientation
				new Coords(3, 1) // pos
			),

			new Mover
			(
				"MoverDefnB", // defnName
				"Green", // faction
				new Coords(1, 0), // orientation
				new Coords(3, 3) // pos
			),

			new Mover
			(
				"MoverDefnA", // defnName
				"Red", // faction
				new Coords(1, 0), // orientation
				new Coords(5, 3) // pos
			),
		]
	);

	Globals.Instance.initialize
	(
		new DisplayHelper(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(displayHelper)
	{
		var posAbsolute = this.posAbsolute();

		displayHelper.drawRectangle
		(
			posAbsolute, 
			this.size, 
			displayHelper.colorFore,
			displayHelper.colorBack
		);

		displayHelper.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(displayHelper)
	{
		displayHelper.drawRectangle
		(
			this.posAbsolute(),
			this.size,
			displayHelper.colorFore, // border
			displayHelper.colorBack // fill
		);

		var children = this.children;
		for (var i = 0; i < children.length; i++)
		{
			var child = children[i];
			child.draw(displayHelper);
		}
	}

	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, text, pos)
{
	this.name = name;
	this.text = text;
	this.pos = pos;
}
{
	ControlLabel.prototype.containsPos = function(posToCheck)
	{
		return Control.doesControlContainPos(this, posToCheck);
	}

	ControlLabel.prototype.draw = function(displayHelper, parentPosAbsolute)
	{
		this.posAbsolute.overwriteWith(this.pos).add(parentPosAbsolute);

		displayHelper.drawTextAtPos
		(
			this.text,
			this.posAbsolute
		);
	}

	ControlLabel.prototype.posAbsolute = function()
	{
		return Control.controlPosAbsolute(this);
	}
}

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.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 DisplayHelper(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);
}
{
	DisplayHelper.prototype.clear = function()
	{
		this.drawRectangle
		(
			this.zeroes,
			this.viewSize,
			this.colorFore, // border
			this.colorBack // fill
		);
	}

	DisplayHelper.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();
	}

	DisplayHelper.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();
	}

	DisplayHelper.prototype.drawMap = function(map)
	{
		var sizeInCells = map.sizeInCells;
		var mapCellSizeInPixels = map.cellSizeInPixels;
		var cellPos = this.mapCellPos;
		var drawPos = this.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
				);

				this.drawRectangle
				(
					drawPos,
					mapCellSizeInPixels,
					this.colorFore, // border
					cellTerrain.color // fill
				);
			}
		}
	}

	DisplayHelper.prototype.drawMover = function(mover, map, isMoverActive)
	{
		var moverDefn = mover.defn();

		var mapCellSizeInPixels = map.cellSizeInPixels;
		var mapCellSizeInPixelsHalf = map.cellSizeInPixelsHalf;

		var drawPos = this.drawPos;
		var drawPos2 = this.drawPos2;

		drawPos.overwriteWith
		(
			mover.pos
		).multiply
		(
			mapCellSizeInPixels
		).add
		(
			map.pos
		).add
		(
			mapCellSizeInPixelsHalf
		);

		var radius = mapCellSizeInPixelsHalf.x;

		var colorStroke = (isMoverActive == true ? this.colorHighlight : this.colorFore);

		this.drawCircle
		(
			drawPos,
			radius,
			colorStroke,
			mover.faction().color
		);

		drawPos2.overwriteWith
		(
			mover.orientation
		).multiplyScalar
		(
			radius
		).add
		(
			drawPos
		);

		this.drawLine(drawPos, drawPos2, colorStroke);

		drawPos.subtract(mapCellSizeInPixelsHalf);

		this.drawTextAtPos(" " + moverDefn.codeChar, drawPos, colorStroke);

	}

	DisplayHelper.prototype.drawMoversForMap = function
	(
		movers, map
	)
	{
		for (var i = 0; i < movers.length; i++)
		{
			var mover = movers[i];
			this.drawMover
			(
				mover, 
				map, 
				false // isMoverActive
			);
		}	
	}

	DisplayHelper.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
		);
	}

	DisplayHelper.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
		);
	}

	DisplayHelper.prototype.drawWorld = function(world)
	{
		this.clear();

		var mapSizeInCells = world.map.sizeInCells;
		var mapCellSizeInPixels = this.viewSize.clone().divide
		(
			mapSizeInCells
		);

		this.drawMap(world.map);
		this.drawMoversForMap(world.movers, world.map);

		this.drawMover(world.moverActive(), world.map, true);

		world.containerActions.draw(this, this.zeroes);
	}

	DisplayHelper.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(displayHelper, world)
	{
		this.world = world;

		this.displayHelper = displayHelper;
		this.displayHelper.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.displayHelper.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;
	}
}

function MapTerrain(name, codeChar, color)
{
	this.name = name;
	this.codeChar = codeChar;
	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.initialize = function()
	{
		var defn = this.defn();
		this.integrity = defn.integrityMax;
		this.movePoints = defn.movePointsPerTurn;
	}
}

function MoverDefn
(
	name, codeChar, integrityMax, movePointsPerTurn, actionNamesAvailable
)
{
	this.name = name;
	this.codeChar = codeChar;
	this.integrityMax = integrityMax;
	this.movePointsPerTurn = movePointsPerTurn;
	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.containerActions = new ControlContainer
		(
			"containerMain",
			new Coords(70, 100), // size
			new Coords(200, 10), // pos
			// children
			Control.toControlsMany
			(
				moverActive.defn().actionsAvailable(),
				new Coords(10, 10), // posFirst
				new Coords(0, 12) // spacing
			)
		);

		this.update();
	}

	World.prototype.update = function()
	{
		this.update_Input();

		this.update_MoversIntegrityCheck();

		this.moverActiveAdvanceIfNeeded();

		this.update_VictoryCheck();

		Globals.Instance.displayHelper.drawWorld(this);
	}

	World.prototype.update_Input = function()
	{
		var inputHelper = Globals.Instance.inputHelper;
		if (inputHelper.isMouseClicked == true)
		{
			inputHelper.isMouseClicked = false;
			this.containerActions.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!");
		}
	}
}

// run

main();

</script>
</body>
</html>

Advertisements
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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s