Map Construction and Savegame Structure, Alpha Release

mapConstructor(attacker, defender)

This is a function that takes as its input the names of the two sides, an attacker and defender, and outputs a renderable game object that pits the two sides against each other. I need to revise it, though, so that it takes a different sort of object so that I can use it to load save games and attach the right prototype functions to the different object types, and I also need to write a function to strip certain information out of the game state and turn it into an object that can be passed into the constructor.

I like this one, I think it’s tidy and does what it’s supposed to, though I might prefer to iterate through the two lists and cut five or six lines out of the thing.

var mapConstructor = function(attacker, defender){
	var attArmy = armyList[attacker]["attacker"];
	var defArmy = armyList[defender]["defender"];
	var map = defArmy["map"].concat(attArmy.map);
	var board = new Map(map);
	attArmy["army"].forEach(function(item){
		var unit = item.split(" ");
		var type = unit[0];
		var y = unit[1];
		var x = unit[2];
		board.spaces[y][x].contains = new Unit(units[attacker][type]);
		board.spaces[y][x].contains.player = "player1"
	});
	defArmy["army"].forEach(function(item){
		var unit = item.split(" ");
		var type = unit[0];
		var y = unit[1];
		var x = unit[2];
		
		board.spaces[y][x].contains = new Unit(units[defender][type]);
		board.spaces[y][x].contains.player = "player2"
	});
	return board
}

This is the format in units.js, which puts all the army lists from the separate files into one big object that contains all the different armies. So far there are six, because that’s as far as I got with my art assets.

"undead": {
	"attacker": {
		"map": [
			"^.....",
			"^^..=.",
			"^.....",
			"......"],
		"army": [
			"ghosts 5 2",
			"ghosts 5 3",
			"ghosts 5 4",
			"ghosts 5 5"...]
	},
	"defender": {
		"map": [
			"......",
			"#....#",
			"^#..#^",
			"^.##.^"],
		"army": [
			"zombies 2 1",
			"zombies 2 2",
			"zombies 2 3",
			"zombies 2 4"...]
	}
}

I’ve also got the turn order mechanic working, I’ll write that up in a future post. It’s a little sloppy but it seems to work out so far. You can run a simple match between two sides, but the game objective isn’t in place yet and half the special abilities don’t do anything so plenty of work to do there.

That said, I plan on releasing the current build as 0.0.1 since it is playable. Super alpha but a release nonetheless.

Project Overview and To-Do List

altclick.js

  • alphabet
    • converts columns to letters
  • Map.prototype.switchActivePlayer()
    • changes value of string “activePlayer”
    • goes through spaces in map and activates new activePlayer’s units
      • Move to Map.js
  • Unit.prototype.inactive()
    • sets Unit.inactive as “true”
      • Move to Unit.js
      • Rename
      • Change to “Unit.active”
  • Unit.prototype.notInactive()
    • sets Unit.inactive as “false”
      • Move to Unit.js
      • Rename
      • Change to “Unit.active”
  • section establishing sampleMap
    • generates sample Map
    • places units on Map
    • establishes variable names for certain units
    • sets Unit.player for generated units
      • Move to a sampleMap.js file
      • Assign variable names to all units
      • Make game-legal for further testing
  • Globals
    • sets activeGame as sampleMap
    • creates variables for active space
    • creates variables for target space
      • set up Init.js to establish activeGame more dynamically
      • rename active space and target space
      • set up active/target space as objects instead of just lists of variables
  • $(document).ready()
    • renders the map
    • div#bucket.on click
      • sets target element as clicked element
      • unit in target element?
        • no current active unit or same team as active unit?
          • sets active unit
            • make sure target unit isn’t inactive
        • targetable unit?
          • attack target unit
          • inactive active unit
            • clear active unit
            • inactive active unit first
      • or else is a unit active?
        • space movable?
          • move unit
          • render active & target spaces
            • make so it can only move once and then it can just attack or deactivate or reload needs to handle deactivating units
  • setActiveElement(element)
    • sets global active element variables
      • needs to clear variable is not passed an argument
      • change values of activeElement object
  • setTargetElement(element)
    • sets global target element variables
      • needs to clear if no argument
      • change values of targetElement object
  • getXY(element)
    • returns object containing elements coordinates
  • stripClass(classname)
    • removes class from all DOM elements that have this class
      • should take multiple classes
  • setMovable()
    • strips “movable” class from all DOM elements
    • uses Map.movableSpaces to get reachable spaces for unit in current activeSpace
    • adds “movable” to DOM elements matching coordinates of objects returned by Map.movableSpaces
  • setTargetable()
    • strips “targetable” elements
    • uses Map.availableTargets to get spaces targetable by activeSpace variable
    • adds Targetable to spaces that match objects returned
  • renderSpaces()
    • replaces targetElement and activeElement with new renders of the spaces, stripping activespace class from active unit coincidentally
    • strips “movable” and “targetable”
      • retain “activespace” class if activeunit has only moved and not attacked yet
  • renderMap()
    • replaces current map render with full map render based on the activeGame object

humanUnits.js

  • contains objects to generate all human units available

index.js

  • click handling, basic getXY(element) function
    • delete this sucker

Map.js

  • Map(map, name)
    • builds map object based on array of strings
    • sets:
      • name, if passed as argument
      • height, based on length of array
      • width, based on length of strings
      • pushes Space objects into array of spaces based on terrain value of characters in row strings
      • activePlayer as “player1”
  • Map.render()
    • sets up div with id map name + “grid”
    • steps through each space and adds the render() value of that space to the div
    • closes the div and returns the completed string
      • change so it’s name based on map name, that’s unnecessary
  • Map.spacesInRangeOf(y, x)
    • sets space variable as Space object in [y][x] of the map
      • just pass a space as an argument
    • checks if the space has a unit in it
    • takes the units range and moves that many spaces out in concentric circles
    • if a space matches certain criteria and isn’t already in the set, it gets pushed into the array that gets returned
  • Map.availableTargets(y, x)
    • gets all the spaces in range of a space and check to see if they have an enemy unit
    • if they do, they’re added to an array and returned
      • pass space as argument
  • Map.movableSpaces(y, x)
    • basically does the same thing as spacesInRangeOf but makes sure they don’t have another unit and it’s not impassable terrain
    • currently lets you move into forests but not through them, ideally forests would drop the move value by 1 but just for that route
      • make it so forests take 2 movement points
      • pass space as argument
  • Map.adjacentSpaces(y, x)
    • uses a Unit.isAdjacentTo test to build an array of spaces adjacent to a space at given coordinates
      • pass space as argument
  • var map
    • just a sample map, needs more options
  • var terrain
    • object for converting the maps to map objects

orcUnits.js

  • has all the orc units in it

renderUnitCard.js

  • converted unit objects into html but it’s not really necessary since unit objects has .render(), on the other hand the problems with converting JSON types might make a more functional approach more practical
    • delete since i can always bring it back in github if it doesn’t work

Space.js

  • Space(x, y, terrain)
    • builds the space object, just basically x, y, and whatever terrain gets passed along in the array passed to the map constructor as the third property -Space.render()
    • returns a div element that adds classes based on properties
      • remove moveable and targetable tests, as that is handled entirely in the DOM and not the object stat
    • adds a render of what is contained in the package
  • Space.isAdjacentTo(space)
    • tests to see if a space is adjacent to a given space, just uses math, nothing weird -Space.canMoveTo(space)
    • checks if a space is adjacent to a given space and that it does not contain a unit and it’s not a mountain space
      • handled entirely by Map.movableSpaces, can probably remove this or replace the logic in Map.movableSpaces with this, replacing logic would make it more succinct
  • Space.containsEnemyUnit(space)
    • checks if a given space contains a unit, and if it has a different player than originating space
      • see if this is actually used, good utility function if it is
  • Space.isElevated
    • sees whether the unit has the special ability that lets them shoot over allies or if they are on terrain that does so
  • Space.isEntrenched
    • checks if terrain adds defense
      • may want to add checks to see if nearby allies are providing leadership, or the unit has moved and has the special ability that gives them extra defense, maybe return an aggregate defense total to use in the rendered unit card
      • if so, name it something different

test.js

  • makes new instances of all the units in humans, orcs, and undead and writes them to the page
  • used in index.html to display all the units for checking
    • can i make this into a loop thing instead of writing each one individually

Token.js

  • Token()
    • builds token objects? i’m not using this for anything, I should probably delete it

turnorder.js

  • this is where I’m going to build the turn order function which will probably get dragged into a different file at some point

undeadunits.js

  • has all the undead units

Unit.js

  • Unit()
    • gets passed an object from one of the unit lists and constructs a unit model out of it
    • generates:
      • name of unit
      • race of unit
      • uses setStat() to generate range, attack, defense, and move
      • adds special ability
      • sets “tokens” as an empty array
      • sets the graphic path for the unit
      • sets hits to 0
      • sets unit as “inactive” as a default
    • returns an object
      • potentially problems with “inactive” by default, at least for generating objects for the unit selection steps
  • Unit.attacks(target)
    • makes a unit attack a target
    • flips a coin for each attack and adds to target’s hits
  • Unit.render()
    • returns a string that contains a div element that represents a whole unit box
      • inactive could be an img instead of a div
  • createStatBox(stat, unit)
    • returns a div that has the right classes to be displayed in the proper corner for each of the four main stats, as well as adding a little asterisk if it’s got the special attribute
      • attach to Unit.prototype to get ride of the argument, unless I decide to kick the prototype chain
  • setStat(unit, stat)
    • sets the stat object for the Unit object, sets a default if no argument is passed by the unit template
      • reverse arguments or add to Unit.prototype to get rid of the argument
  • coinFlip()
    • 50% chance of returning 0 or 1

Map.prototype.availableTargets & Friends

Sample Attack Range

A major part of the Doomball project is the ability to attack enemy units. This is a pretty big part of a lot of games, of course, and I guess I could have just stripped something out of someone else’s game but how boring is that, right?

In Doomball a lot of pieces have a range of greater than 1, so it can’t just be adjacent spaces, and then terrain effects the range measurements too. A unit on a hill can fire over friendly units, you can fire at a unit in a forest, but you can’t draw line of site through a forest. You can’t fire through mountains at all. So I started with the most complicated and important function, ~Map.prototype.spacesInRangeOf(y,x)~, which returns a set of objects representing the spaces that are in range of the originating unit, taking into account the unit’s range and the properties of both the terrain it’s on and the terrain between itself and the spaces.

Map.prototype.spacesInRangeOf = function(y, x){
  var space = this.spaces[y][x];
    if (space.contains){
      var spacesInRange = [space];
      var range = space.contains.range.value;
      var elevated = space.isElevated();
      var showStoppers = [];
      while (range > 0) {
        var nextRing = spacesInRange.forEach(function(tempSpace){
          var adjacentSpaces = this.adjacentSpaces(tempSpace.y, tempSpace.x);
          adjacentSpaces.forEach(function(candidate){
            if (spacesInRange.indexOf(candidate) == -1) {
              if (candidate.terrain.dropsLOS) {
                showStoppers.push(candidate)
              } else if (!candidate.terrain.blocksLOS) {
                if (!candidate.contains || (space.isElevated() && candidate.contains.player == space.contains.player)) {
                  spacesInRange.push(candidate); 
                } else if (candidate.contains.player != space.contains.player) {
                  showStoppers.push(candidate)
                }
              }
            }
          })
        }, this)
      range -= 1
    }
  spacesInRange.shift();
  spacesInRange = spacesInRange.concat(showStoppers);
  return spacesInRange
}
}

The showStoppers set is important, because when a unit stops further line of site from being drawn past it, for example enemy units or a forest space, they’re still added to the set of spaces in range but are no longer taken into consideration for the purposes of drawing further line of site. This worked out well in practice. ~Space.prototype.isElevated()~ is a nice little utility function that checks if the unit has the Special characteristic for its Range attribute, or if it’s on a hill. Or both I suppose but they’re redundant in that case. ~Map.prototype.adjacentSpaces()~ uses the prototype function ~isAdjacentTo()~ to give a set of spaces adjacent to a given space, and then the spacesInRange set is shuttled into the availableTarget space which returns the spaces that contain enemy units.

var spacesInRange = activeGame.availableTargets(y, x);
console.log(spacesInRange);
spacesInRange.forEach(function(space){
	$(".row" + space.y + ".col" + space.x).addClass("targetable")
})

Then I added this bit to the ~$(document).ready()~ section of the index.js file to make the whole thing work, and through it into the part for when you click on a unit. This is a lot more terse than the current code I’ve got put together for the movement section, which I need to go back and tidy up. A big part of the tightening of this thing is going to involve pulling out sections of code and turning them into separate functions. Clean Code concepts and such.

This was a big prototype project for me. I found it was more useful to throw prototype methods onto the ~Map~ objects instead of the ~Space~ or ~Unit~ objects because it let me go through all the spaces without having to do any mathematical iteration. I used a lot of forEach(), too.

Next thing to work on is letting dudes actually attack each other. I’d also like to show all potential spaces in movement range instead of moving your guys one space at a time, but I’m not sure how to handle forests. They use an extra movement point when you’re going through them so it’s going to have to be a different approach than this range thing.

Fixing Multiple Unit Selection Bug in Doomball

Multiple Unit Selection Bug

So I linked Kelly to the work I did last night and she sent me back this screenshot. That’s a nasty little gap in the middle there, and it’s a big problem. I clicked around myself and found that you can actually select several units at a time and have them possess the ~activeSpace~ class, which is not good since they will all move to the target space you pick and their empty spaces will be whatever the first one in the stack was sitting on, which throws off the display of the grid and makes more than one unit disappear.

if (activeGame.spaces[y][x].contains) {
	$(this).addClass("activeSpace");
	$("div#grid .gridSpace").each(function(){
		$(this).removeClass("movable");
		var targetClasses = $(this).attr("class").split(" ");
		var targetX = targetClasses[2].charAt(3);
		var targetY = targetClasses[1].charAt(3);
		if (activeGame.spaces[y][x].canMoveTo(activeGame.spaces[targetY][targetX])) {
			$(this).addClass("movable")
		}
	})
}

So what I need to do is take out the activeSpace class from whatever space has it from the get go.

if ($(".activeSpace")) {
	$(".activeSpace").removeClass("activeSpace")
}

Nice and easy. Let’s see if it works.

Yeah, that did it. Nice.

Successful Implementation of Move Mechanic

If you visit the testGrid you’ll see that you can now click on dudes and move them around at will. There isn’t currently anything preventing you from moving around all over the damn place and the thing is faction agnostic at this point, so it doesn’t have any attacking functions or anything, but it’s a pretty good start. Very happy, kind of tired.

The separate .render() function is important to how this works, because it replaces the HTML of the updated spaces with a fresh render of each space. Since the transfer of the Unit object occurs before the render takes place, it’s an adequate representation, and it should transfer clean with whatever temporary effects there are in play.

Right now the movability of the space is only represented in the rendered page and has no analog in the actual object code, which I think works fine. I suppose if I wanted to present the game as like an ASCII telnet thing I’d want to transfer that functionality into something a little more hard-coded, but I suppose it’d basically return true or false in reaction to like a “3,0 to 2,0” kind of query. It wouldn’t be that hard to do.

A telnet client would actually be a fun way to make this thing multiplayer without having to worry about handshaking or anything like that. How would you do a grid in ASCII?

 _   _   _
/S\_/W\_/ \
\_/O\_/L\_/
/ \_/ \_/ \
\_/ \_/ \_/

Yeah I guess that would work fine. You’d want to pop it up a bit and have a little more room for characters so that you could show more information but no biggie.