MediaWiki:Javascript-ungames-TestGame.js
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, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """).replace(/'/g, "'");
}