Implementing Mythotopia Game Options

The Sample Game Object

I built a sample game object for Mythopia. It works pretty well for what’s going on now. The fun part is that it isn’t like a pre-built raw-programmed object, it’s generated by a controller object that takes an option object and outputs a hopefully playable game state. This is basically what I need to start working with it, so it’s pretty satisfying.

I decided to enter the data for the map spaces and cards into a Google Docs chart, then I saves it as comma-separated values and put it through a CSV to JSON converter. I used convertcsv.com because it let me use “/” separated values to set up nested objects, which was important since I decided to use an array of objects to represent connections between different spaces. There was probably a more efficient way to communicate that information but since I’m working with data instead of a graphical interface I can’t imagine what it would be.

"iracund": {
	"name": "Iracund",
	"resource": "food",
	"strength": 2,
	"connection": [
		{"to": "palmain"},
		{"to": "diclesium",	"type": "mountains"},
		{"to": "fadge"},
		{"to": "quean"},
		{"to": "blore"}
	]
}

So things are clipping along.

App Structure

At this point I’m thinking the client-side program is going to handle a lot of the rule parsing. I know I wasn’t originally going to do it that way but I think it might be better for my purposes. If the client-side program made the player pick appropriate cards, presented available options, that sort of thing, it might work better, but it would have to be real interactive with the server side to work out. But my thought is that the object sent back to the server should be something like this:

{
	"action": "invade",
	"province": "iracund",
	"player": "James",
	"cardsUsed": {
		"iracund", "armyStart", "ship"
	}
}

And theoretically that should be enough for the server-side programming to apply the action, remove the cards from the player’s hand, apply any applicable reserve characteristics to the action, etc. etc. Theoretically. I made two actions so far, ~placeArmies~ and ~removeArmies~ but they need some error-checking mechanisms built in to make sure the player doesn’t end up with more army counters than he started with. ~placeArmies~ should, again theoretically, be all that’s needed to handle both the “Invade a Province” action and the “Place Armies” action described in the rulebook but I have to think through possible repercussions of combining those two actions. There’ll still be plenty of state-parsing before these action can be applied, of course, but I think it will be best to have some really grainy brute-force functions sitting at the core of this thing to change the actual game state.

I think the next step is to write all these game state functions. Right now my node server can (but does not) generate a new game with the game controller, applies a ~placeArmies~ function to one of the spaces, and displays on port 3000 the text version of the space’s object, with the army counters listed. I feel pretty good about that.

Also to do is to make some CSS and View files so I can show the whole gamestate in a prettier way, that would be nice.

Mythotopia, An introduction

The game

Kelly and I went to Foam Brain games in Troy and they had this brown bag sale where you spent $25 and just got whatever was in the bag, which was pretty much guaranteed to have an MSRP of greater than $25. I was so happy with the first one that I got that I got two more, plus I love the thrill of the unknown purchase.

One of the games we picked up was called Mythotopia. It uses a combination of card-drafting/deckbuilding mechanics and map-based maneuvering. It doesn’t have any really out-of-turn actions which I think makes it an excellent candidate for my first attempt at a Node-served asynchronous multiplayer game, since full turn resolution can occur without the interjection of opposing players.

First pass

So I’ve been thinking about how to structure this. Since it’s all Node-y instead of the purely client side javascript approach I’ve taken with Doomball, I think it’s important to more effectively differentiate between the different concerns. So we’re going MVC on this one, or as close as I can get with my weird incomplete understanding of the approach.

Model

The main and most important model is going to be the game as a whole. This is going to include information about the map (more complicated since I want to make it so you can play custom maps) and the state of the game on that map, including tokens in reserve and the board state that the players have. It’s also going to include objects for the players in the game, and those object will include subobjects for the hand of cards the player has, the cards the player has in reserve, the cards in the players’ decks, that sort of thing.

These are going to be straight objects because I want to handle the various gamestate manipulations through functions in the controller. That way, instead of using methods like I used in Doomball, I can use separate functions and leave the game state as a pure object with no prototype functions which should make it easier to pull it out of the database and load it up.

Controller

This is going to be where I hold the functions that change the game state, I’ll probably have it so they get passed the objects they need to manipulate directly. Not sure how to pull the functions out of the modules they’re in yet, that’s something I’ll have to figure out.

View

This is going to be the Node/Express part of the thing but since I’m doing gamestate manipulation serverside it’s going to be totally focussed on presenting an effective GUI to the player without handling gamestate changes. Doomball has a lot of gameplay functional stuff being handled by class changes to HTML elements which I guess is ok (I don’t like it) for a totally clientside program but I think it’ll be more graceful if the view is just passed the game object and then displays it. Obviously it’ll have to detect the player accessing the game and hide the information of the other players though.

I want to use like jquery type stuff to use elements as input options and then they’re going to feed information to an action object that gets passed back to the controller, which turns it into an actual game state change, reflects it in the game object, and then sends the game object back to the user.

Next steps

I think this is going to be a pretty effective approach. There’s also the problems of like user authentication and profile/save game management but those seem like more vanilla Node tasks and things I can look up stuff on how to do but making Mythotopia into an online game is a little more unique and interesting.

The next thing I want to do is produce a mocked-up object to represent a game that is about to begin. I think this’ll give me a good idea on how to approach further steps and set up my server effectively.

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.