MediaWiki:Javascript-ungames-TestGame.js

From Uncyclopedia, the content-free encyclopedia
Jump to navigation Jump to search

Note: After saving, you have to bypass your browser's cache to see the changes.

  • Internet Explorer: hold down the Ctrl key and click the Refresh or Reload button, or press Ctrl+F5.
  • Firefox: hold down the Shift key while clicking Reload; alternatively press Ctrl+F5 or Ctrl-Shift-R.
  • Opera, Konqueror and Safari users can just click the Reload button.
  • Chrome: press Ctrl+F5 or Shift+F5
document.getElementById("javascriptNotice").remove();
document.getElementById("gameContent").style.display = "block";

mw.loader.load("https://en.uncyclopedia.co/w/index.php?title=MediaWiki:Javascript-ungames-TestGame.js/classes.js&action=raw&ctype=text/javascript");


// TODO: make it so customizing these values can make interesting gameplay challanges
var battleMode = "standard";
var placedCreatureLimit = 1;

// Creatures which have been placed down
var characterTeams = {};
var releasedCreatures = {};

// Which character is currently choosing their attacks (this will eventually allow the implementation of 1v1, 2v2, 1v2, etc., as well as POSSIBLE multiplayer, *no promises!*)
var characterTurn = 0;
var healthbars = {};
var creatureSprites = {};
var alliedCharacters = ["0"];
var playingCharacterLength = 0;


/*%fold%*/function onLoaded() {
	try{
		// The id of 0 is always going to be the player
		characterTeams["0"] = [new Creature("orangeKitten", {level: 5}), new Creature("orangeKitten", {level: 5}), new Creature("orangeKitten", {level: 5}), new Creature("moccasin", {level: 7})];
		characterTeams["1"] = [new Creature("storyEatingCat", {level: 2})];
		
		playingCharacterLength = Object.keys(characterTeams).length;
		
		startBattle();
	} catch(e) {
		mw.notify("Error during setup: " + e);
	}
}


/*%fold%*/function startBattle() {
	// Select creatures to be released into battle
	for (let a of Object.keys(characterTeams)) {
		releasedCreatures[a] = [];
		for (let c = 0; c < placedCreatureLimit; c++) {
			if (characterTeams[a].length > 0) {
				let creatureReleased = characterTeams[a].shift();
				releasedCreatures[a].push(creatureReleased);
				creatureReleased.enteredBattlefield(a);
			}
		}
	}
	
	
	// Create healthbars
	healthbars = {};
	
	let healthbarCount = 0;
	let healthbarPositionTopAlly = 0;
	let healthbarPositionTopEnemies = 0;
	
	let spriteCount = 0;
	let spritePositionAlly = 3;
	let spritePositionEnemies = 5;
	
	for (let characterId of Object.keys(releasedCreatures)) {
		healthbars[characterId] = [];
		creatureSprites[characterId] = [];
		let isAlly = alliedCharacters.includes(characterId);
		
		for (let creatureId = 0; placedCreatureLimit > creatureId; creatureId ++) {
			$("div[id=gameUiUnder]").append(`<div id="healthbar-${healthbarCount}" style="position:absolute; top:${isAlly ? healthbarPositionTopAlly : healthbarPositionTopEnemies}em; ${isAlly ? "left:2em" : "right:2em"}"></div>`);
			healthbars[characterId].push(document.getElementById(`healthbar-${healthbarCount}`));
			healthbarCount++;
			
			if (isAlly) healthbarPositionTopAlly += 4;
			else healthbarPositionTopEnemies += 4;
			
			$("div[id=gameSprites]").append(`<div id="creatureSprite-${spriteCount}" style="position:absolute;${isAlly ? "transform:scaleX(-1);bottom:17em;left:"+spritePositionAlly : "top:4.5em;right:"+spritePositionEnemies}em;width:100px;height:100px"></div>`);
			
			let sprite = document.getElementById(`creatureSprite-${spriteCount}`);
			creatureSprites[characterId].push(sprite);
			spriteCount++;
			
			if (isAlly) spritePositionAlly += 9;
			else spritePositionEnemies += 9;
		}
	}
	
	updateCreatureSprites();
	startNewTurn();
}


/*%fold%*/function startNewTurn() {
	let creatureIndex = 0;
	characterTurn = 0;
	updateHealthbars();
	controlNextCreature();
	
	function controlNextCreature() {
		if (releasedCreatures[characterTurn].length <= creatureIndex) {
			creatureIndex = 0;
			characterTurn++;
			
			if (characterTurn >= playingCharacterLength) {
				// All characters have chosen their attacks
				simulateRound();
				return;
			}
		}
		
		// Player chooses their attacks
		if (characterTurn == 0) {
			controlPlayerCreature(creatureIndex, controlNextCreature);
			creatureIndex++;
		}
		// Enemies choose their attacks
		else {
			try{
			let creature = releasedCreatures[characterTurn][creatureIndex];
			// AI enemies will pick a random attack.
			creature.chooseAIAttack();
			
			creatureIndex++;
			controlNextCreature();
			}catch(e){mw.notify(e);}
		}
	}
}


var onPlayerChoseAttack;
// Allows the player to control which attack a creature will do.
/*%fold%*/function controlPlayerCreature(index, onDone) {
	let creature = releasedCreatures[characterTurn][index];
	let attacksHtml = "";
	
	let attackIndex = 0;
	onPlayerChoseAttack = onDone;
	for (let attack of creature.attacks) {
		attacksHtml += `<div class="attackButton" title="${attack.description}" onclick="playerChoseAttack(${index},${attackIndex})">${attack.name}</div>`;
		attackIndex++;
	}
	
	// TODO: check if this creature CAN attack anything (check function below)
	
	document.getElementById("game-myAttackList").innerHTML = attacksHtml;
}


/*%fold%*/function playerChoseAttack(creatureIndex, attackIndex) {
	try{
	document.getElementById("game-myAttackList").innerHTML = "";
	
	let creature = releasedCreatures[characterTurn][creatureIndex];
	creature.chosenAttack = creature.attacks[attackIndex];
	
	let possibleTargets = [];
	if ("targetSelf" in creature.chosenAttack && creature.chosenAttack.targetSelf)
		possibleTargets.push(creature);
	else for (let characterId of Object.keys(releasedCreatures)) {
		if (!alliedCharacters.includes(characterId))
			for (let otherCreature of releasedCreatures[characterId]) {
				if (otherCreature.ability.canTarget(otherCreature, creature))
					possibleTargets.push(otherCreature);
			}
	}
	
	if (possibleTargets.length == 0) {
		// TODO: tell player this attack has no targets
		delete creature.chosenAttack;
	}
	
	else if (possibleTargets.length > 1) {
		// TODO: make it so if there are multiple targets, have the player choose the target
	}
	
	else {
		creature.targetCreature = possibleTargets[0];
		onPlayerChoseAttack();
	}
	}catch(e){mw.notify(e);}
}


/*%fold%*/function updateHealthbars() {
	for (let characterId of Object.keys(healthbars)) {
		let creatureId = 0;
		for (let healthbar of healthbars[characterId]) {
			healthbar.innerHTML = createHealthBar(releasedCreatures[characterId][creatureId]);
			creatureId++;
		}
	}
}


/*%fold%*/function updateCreatureSprites() {
	for (let characterId of Object.keys(creatureSprites)) {
		let creatureId = 0;
		for (let sprite of creatureSprites[characterId]) {
			sprite.innerHTML = createCharacterSpriteForFieldCreature(characterId, creatureId);
			creatureId++;
		}
	}
}


/*%fold%*/function createHealthBar(creature) {
	return `<div class="healthbar"><div><div style="font-size:85%;display:inline; white-space: nowrap">(Lv ${creature.level}) ${creature.name}</div></div><div class="healthbarProgressBackground"></div><div class="healthbarProgress" style="background-color:green;width:${13 * (creature.health / creature.maxHealth)}em"></div><div style="font-size:85%;margin-top:0.2em">Hp:${creature.health}/${creature.maxHealth}</div></div>`;
}

function createCharacterSpriteForFieldCreature(characterId, creatureId) {
	return createCharacterSprite(releasedCreatures[characterId][creatureId], `onClickedFieldCreature(${characterId},${creatureId});`);
}
function createCharacterSprite(creature, onclick, other) {
	return `<img src="${creature.spritePath}" alt="${creature.name}" height="100" onclick="${onclick}"/>`;
}


async function simulateRound() {
	try{
	// First, get the order of attacks
	let allAttckingCreatures = [];
	
	for (let characterId of Object.keys(releasedCreatures)) {
		for (let creature of releasedCreatures[characterId]) {
			if (creature.chosenAttack != null)
				allAttckingCreatures.push(creature);
		}
	}
	
	// Sets the attack order
	allAttckingCreatures.sort((a, b) => {
		let aPriority = a.getAttackPriority();
		let bPriority = b.getAttackPriority();
		return (aPriority != bPriority) ? aPriority - bPriority :
				(a.speed != b.speed) ? a.speed - b.speed : Math.random() - 0.5;
	});
	
	// Simulates the fight that ensures
	for (let creature of allAttckingCreatures) {
		if (creature.health > 0 && creature.targetCreature.health > 0) {
			try{
			await showDialogue(`${creature.isPlayerControlled ? "Your " : creature.isAlly ? "Your allied " : "The opposing "}${creature.name} used ${creature.chosenAttack.name}!`, 2500, 500);
			
			
			await creature.ability.onUsedAttack(creature.chosenAttack, creature);
			let attack = creature.useAttack(creature.chosenAttack);
			
			await creature.targetCreature.takeDamage(attack, creature);
			updateHealthbars();
			
			await creature.targetCreature.ability.afterGettingHit(attack, creature.targetCreature, creature);
			await creature.ability.afterHittingTarget(attack, creature);
			}catch(e){mw.notify(e);}
		}
		
		delete creature.chosenAttack;
		delete creature.targetCreature;
	}
	}catch(e){mw.notify(e);}
	
	updateHealthbars();
	doPostRoundChecks();
}


/*%fold%*/async function doPostRoundChecks() {
	try{
	// Make sure to switch out any creatures which have died
	let creatureSwapQueue = [];
	
	for (let characterId of Object.keys(releasedCreatures)) {
		let creatureId = 0;
		for (let creature of releasedCreatures[characterId]) {
			if (creature.health <= 0) {
				// Tell player a creature has died
				creatureSprites[characterId][creatureId].innerHTML = "";
				await showDialogue(`${characterId == "0" ? "Your " : alliedCharacters.includes(characterId) ? "Your allied " : "The opposing "}${releasedCreatures[characterId][creatureId].name} has been killed!`, 2500, 500);
				
				creatureSwapQueue.push([characterId, creatureId]);
			}
			else if (creature.forcedReplacement != null) {
				creatureSwapQueue.push([characterId, creatureId]);
			}
			creatureId++;
		}
	}
	
	// TODO: make it skip right to the "victory"/"defeat" message if the requirements are met
	for (let i of creatureSwapQueue) {
		// This creature has died, make sure to swap it out, or to end the match if it is the last creature
		let shouldContinue = true;
		let oldCreature = releasedCreatures[i[0]][i[1]];
		
		// Select next creature
		if (releasedCreatures[i[0]][i[1]].forcedReplacement != null) {
			let chosenCreature = releasedCreatures[i[0]][i[1]].forcedReplacement;
			creatureSwitchOut(creatureSprites[i[0]][i[1]], oldCreature, chosenCreature);
		}
		
		else await selectCreatureFromTeam(i[0], i[0] == "0", "Choose a new creature to send out.").then((chosenCreature) => {
			if (chosenCreature == null) {
				shouldContinue = onCharacterDefeated(i[0]);
				return;
			}
			creatureSwitchOut(creatureSprites[i[0]][i[1]], oldCreature, chosenCreature);
		});
		if (!shouldContinue) return;
	}
	
	updateHealthbars();
	setTimeout(startNewTurn);
	}catch(e){mw.notify(e);}
}

/*%fold%*/async function creatureSwitchOut(sprite, oldCreature, newCreature) {
	try{
	let fieldIndex = releasedCreatures[oldCreature.owningCharacterId].indexOf(oldCreature);
	
	releasedCreatures[oldCreature.owningCharacterId][fieldIndex] = newCreature;
	newCreature.enteredBattlefield(oldCreature.owningCharacterId);
	characterTeams[oldCreature.owningCharacterId].splice(characterTeams[newCreature.owningCharacterId].indexOf(newCreature), 1);
	
	// Update sprite
	sprite.innerHTML = createCharacterSpriteForFieldCreature(oldCreature.owningCharacterId, fieldIndex);
	
	if (oldCreature.onSwitchedOut) {
		await oldCreature.onSwitchedOut(newCreature);
		delete oldCreature.onSwitchedOut;
	}
	
	// Creature is still aive, insert it back into the team
	if (oldCreature.health > 0) {
		characterTeams[newCreature.owningCharacterId].push(oldCreature);
	}
	
	}catch(e){mw.notify(e);}
}


// Choose a creature from the team, which has not been released
/*%fold%*/function selectCreatureFromTeam(team, playerChooses, playerChoosesMessage = "Choose a creature.") {
	return new Promise((resolve) => {
		if (characterTeams[team].length == 0) {
			resolve(undefined);
		}
		
		else if (playerChooses) {
			// Show a menu of all your creatures
			let menu = "";
			let index = 0;
			
			showDialogue(playerChoosesMessage, -1, -1);
			
			onCreatureSelectedFromTeamMenuHandler = (team, creature) => {
				hideDialogue();
				resolve(characterTeams[team][creature]);
				document.getElementById("underGame-UI").innerHTML = "";
			}
			for (let creature of characterTeams[team]) {
				menu += `<div style="flex:1${[3,5].includes(index) ? ";flex-direction:column" : ""}"><div>${createHealthBar(creature)}</div>${createCharacterSprite(creature, `onCreatureSelectedFromTeamMenu(${team},${index})`)}</div>`;
				index ++;
			}
			document.getElementById("underGame-UI").innerHTML = "<div style='display:flex;background-color:gray;padding:1em'>" + menu + "</div>";
		}
		else {
			resolve(characterTeams[team][Math.floor(Math.random() * characterTeams[team].length)]);
		}
	});
}

var onCreatureSelectedFromTeamMenuHandler;
/*%fold%*/function onCreatureSelectedFromTeamMenu(team, creature) {
	if (onCreatureSelectedFromTeamMenuHandler)
		onCreatureSelectedFromTeamMenuHandler(team, creature);
}

/*%fold%*/function onCharacterDefeated(characterId) {
	if (characterId == "0") {
		handleDefeat();
		return false;
	}
	
	delete characterTeams[characterId];
	delete releasedCreatures[characterId];
	delete healthbars[characterId];
	delete creatureSprites[characterId];
	if (alliedCharacters.includes(characterId))
		alliedCharacters.splice(alliedCharacters.indexOf(characterId));
	
	if (Object.keys(releasedCreatures).length > alliedCharacters.length) {
		return true;
	}
	
	handleVictory();
	return false;
}



/*%fold%*/function handleDefeat() {
	showDialogue("You have been defeated!", 4500, 1500);
}
/*%fold%*/function handleVictory() {
	showDialogue("You are victorious!", 4500, 1500);
}

var dialogueId = 0;
var dialogueOnclickFunction;
/*%fold%*/function showDialogue(text, displayTime = 2000, skippableAfter = -1) {
	dialogueId++;
	let thisDialogueId = dialogueId;
	
	return new Promise((resolve, cancel) => {
		document.getElementById("dialogue").innerHTML = text;
		document.getElementById("dialogue").style.display = "block";
		let canBeSkippedIn = Date.now() + skippableAfter;
		dialogueOnclickFunction = () => {
			if (thisDialogueId == dialogueId && skippableAfter !== -1 && Date.now() > canBeSkippedIn) {
				dialogueId++;
				hideDialogue()
				resolve();
			}
		};
		if (displayTime > 0) setTimeout(() => {
			if (thisDialogueId == dialogueId) {
				hideDialogue()
				resolve();
			}
			else {
				cancel();
			}
		}, displayTime);
	});
}

/*%fold%*/function hideDialogue() {
	document.getElementById("dialogue").innerHTML = "";
	document.getElementById("dialogue").style.display = "none";
	dialogueOnclickFunction = null;
}


var hoverUi;
/*%fold%*/var statChageBars = {
	"-5": "https://images.uncyclomedia.co/uncyclopedia/en/f/fe/StatChange--5.png",
	"-4": "https://images.uncyclomedia.co/uncyclopedia/en/3/38/StatChange--4.png",
	"-3": "https://images.uncyclomedia.co/uncyclopedia/en/0/00/StatChange--3.png",
	"-2": "https://images.uncyclomedia.co/uncyclopedia/en/b/b2/StatChange--2.png",
	"-1": "https://images.uncyclomedia.co/uncyclopedia/en/3/3a/StatChange--1.png",
	"0": "https://images.uncyclomedia.co/uncyclopedia/en/4/44/StatChange-0.png",
	"1": "https://images.uncyclomedia.co/uncyclopedia/en/c/cf/StatChange-1.png",
	"2": "https://images.uncyclomedia.co/uncyclopedia/en/0/08/StatChange-2.png",
	"3": "https://images.uncyclomedia.co/uncyclopedia/en/7/7a/StatChange-3.png",
	"4": "https://images.uncyclomedia.co/uncyclopedia/en/9/9c/StatChange-4.png",
	"5": "https://images.uncyclomedia.co/uncyclopedia/en/2/21/StatChange-5.png"
}
/*%fold%*/function onClickedFieldCreature(characterId, creatureId) {
	setTimeout(() => {
		if (hoverUi == null) {
			hoverUi = document.createElement("div");
			hoverUi.style = `position:absolute;left:${pagePosition.x-197}px;top:${pagePosition.y-205}px;width:16em;height:17em;background-color:lightgray;border:1px solid black;pointer-events:none;z-index:5;`;
			
			let creature = releasedCreatures[characterId][creatureId];
			let tooltipHtml = `${creature.name}`
			for (let stat of ["attack", "spAttack", "defence", "spDefence", "speed","accuracy","dodge"]) {
				tooltipHtml += `<p>${{attack: "Attack",spAttack: "Special Attack",defence: "Defence",spDefence: "Special Defence",speed: "Speed",accuracy: "Accuracy", dodge: "Evasion"}[stat]}: <img style="clear: right;
float:right" src="${statChageBars[creature.getStatValue(stat)]}"/></p>`;
			}
			hoverUi.innerHTML = tooltipHtml;
			
			document.getElementById("gameSprites").appendChild(hoverUi);
		}
	}, 5);
}

/*%fold%*/function onMouseStoppedHoveringCreature() {
	if (hoverUi != null) {
		hoverUi.remove();
		hoverUi = null;
	}
}

let pagePosition = {x:0,y:0};
document.body.addEventListener("mousemove", () => {
	pagePosition.x = event.pageX;
	pagePosition.y = event.pageY;
});
document.body.addEventListener('click', () => {
	onMouseStoppedHoveringCreature();
	if (dialogueOnclickFunction) dialogueOnclickFunction();
});

// Special functions designed for security

// TODO: When edititing a creature's nickname, make sure to run it though this function, so that it can't hold any html code.
/*%fold%*/function escapeHtml(unsafe){
	return unsafe.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#039;");
}