Jump directly to main content

Project 1: Basic Mechanic Hack In



With the basic mechanics now thought of thanks to my research, and constantly evolving Spreadsheet GDD, I can now add them (in their most basic sense) into my game!

Disclaimer! Anything written in this article is not indicative of how the mechanic will work in it's final stages. The code examples here are also specifically examples of the hackyness of this stage, and extremely far from what the codebase will be.

Still developing on front-end

For ease, and speed of development I opted to continue the development work front-end to benefit from console logs, and alert pop-ups when a condition that shouldn't get hit, gets hit. I also don't have to think about any data to be sent, so can happily hack away changing everything at a whim.

'Basic' Mechanics?

My game has a number of mechanics, each with more complexity than is 'needed' to have a playable game, this complexity is what will allow me to balance, and create distinctions between similar items.

Keeping this in mind, I just need a somewhat playable experience that uses the absolute basics of my core/fundamental mechanics so I can get a big boost in progress of making the game itself. The complexity will be added later on, once the codebase exists, has been tidied up, and made a bit more efficient (foreshadowing for a future article, how does he do it).

The Hack-in

Since my only concern at this point was to get the mechanics in, and make each of the most basic 'playable' moves I added very basics.

These are my current mechanics (still attempting to be vague), which I believe I'm mostly settled on, likely with only minor changes to the limits and conditions for each field location. When the game's closer to a testing phase I'll do a full writeup of each.

Summon a unit

Once a unit is available to be played a check is done to see if there is enough 'resource' to add it to the field, for the hack-in I've only done a requirement of one per unit, but it does need to be of the same 'alliedType'.

playCardToField(index){
	let UnitPlayed	= availableUnits[index];

	// Check if there's space to add unit
	if(unitArea.length >= maxUnits){
		return 0;
	}

	if(UnitPlayed.cost > playerResource.length){
		return 0;
	}else{
		let canPlay = false;
		let needsResource = 1;
		let usedResource = 0;
		playerResource.forEach(function(ResourceUnit, key){
			if(UnitPlayed.alliedType == ResourceUnit.alliedType && ResourceUnit.cantAttack == false && needsResource > usedResource)
			{
				ResourceUsed.push(key);
				usedResource++;
				canPlay = true;
			}
		});

		if(!canPlay){
			return 0;
		}
	}

	// Mark resource as used (so they can't be used for another unit)
	ResourceUsed.forEach(function(UnitKey, key){
		playerResource[UnitKey].used = true;
	});

	// Remove from playable units
	availableUnits.splice(index, 1);
	// Add unit to field
	unitArea.push(UnitPlayed);
}

This code is essentially the same for adding shields, and 'resource' too, just with a few minor changes, as the hack-in mode in me let me just straight copy-and-paste functions.

Special Events + Check

A Special event, is what I ended up naming events that have specific options to progress/end such as 'Attacking' and 'Inspecting' which I used for testing in the hack-in.

If the game has been won, or there is a unit being inspected, or attacking a variable within the eventListeners is set.

let specialEvent = false;
if(inspectUnit !== null || attackingUnit !== null || gameWin){
	specialEvent = true;
}

This variable is then checked when a certain target is selected, and depending on if specialEvent is true (if so it typically will check for the correct event, i.e. attacking as below) it'll trigger a different function call, or if false just prevent a function call.

playerField.forEach(function(item, index){
	let clickable = item.clickable;

	if(clickableCheck(x,y,clickable)){
		if(attackingUnit !== null && item == attackingUnit[0]){
			endAttack();
		}
		if(!specialEvent && item.cantAttack != true){
			startAttack(index);
		}
	}
});

The attacking/inspectingUnit, event data is set when one is triggered, explained in the text below.

Attacking

Attacking is split into three phases.

When an available unit is selected, it sets that unit in the 'attacking' variable in startAttack().

startAttack(index){
	attackingUnit = [playerField[index], index];
}

If player then selects an opposing shield or unit, while an 'attacking' unit is set, the makeAttack() function passes the (jank incoming) array, arrayName, and the targets array index to perform checks.

If a shield is destroyed, the opposing player gets whatever unit was used as a shield added to their 'availableUnits', as a catch-up mechanic.

If units are of equal power, they are both unalived, if one is stronger they get to live, while the other, not so much.

makeAttack(index, array = null, name = null){
	if(array == null){ array = opponentField; name = 'opponentField' }
	let defendingUnit = array[index];

	// If hitting shield, don't calc combat damage
	if(name == 'opponentShield'){
		if(array[index].tapped){
			array[index].tapped = false;
			opponentAvailableUnits.push(array[index]);
			array.splice(index, 1);
		}else{
			array[index].tapped = true;
		}

		playerField[attackingUnit[1]].tapped = true;
		this.endAttack();
		return 1;
	}

	// Do calc combat damage for each unit if unit vs unit fight
	if(defendingUnit.atk <= attackingUnit[0].atk){
		array.splice(index, 1);
	}
	if(attackingUnit[0].atk <= defendingUnit.atk){
		playerField.splice(attackingUnit[1], 1);
	}else{
		playerField[attackingUnit[1]].tapped = true;
	}
	this.endAttack();
}

Once the makeAttack() has completed, it then calls endAttack(), which sets the 'attacking' variable to null that will allow non-specific interactions again, then checks the opposing player's shield, if it's destroyed the gameWin state is set. endAttack() can also be called by reselecting the 'attacking' unit, as a means to not force players into combat.

endAttack(){
	attackingUnit = null;
	if(opponentShield.length <= 0){
		gameWin = 1;
	}
}

That's all. My vagueness remains for the time being (inc. in the code examples, as I've changed variable names for example), I only hope that I'm not getting people excited for a gametype that isn't what I'm making. I need to keep the vagueness thoguh, as once I annouce what it is I will feel a mix of pressure, and pre-success that I believe will detriment my development, and potentially get me to quit before completion.