Newer
Older
/********************************************************************************
metodLD
committed
This file contains the variables and functions that forms the core of the game.
Anything that is needed game-wide is kept here.
********************************************************************************/
/**********************************************************************
* Game Wide Constants
**********************************************************************/
/* General Constants */
var EPILOGUES_ENABLED = true;
var EPILOGUE_BADGES_ENABLED = true;
var BASE_FONT_SIZE = 14;
var BASE_SCREEN_WIDTH = 100;
/* Game Wide Constants */
var HUMAN_PLAYER = 0;
/* Directory Constants */
var IMG = 'img/';

ReformCopyright
committed
var backgroundImage;
/*var OPP = 'opponents/';
#The "OPP" folder abbreviation was used to slightly shorten a few lines in spniSelect that looked for opponents in the opponents folder.
#Now that opponents can be specified in any folder, this is no longer required.*/
/* Gender Images */
var MALE_SYMBOL = IMG + 'male.png';
var FEMALE_SYMBOL = IMG + 'female.png';
var includedOpponentStatuses = {};
/* game table */
var tableOpacity = 1;
$gameTable = $('#game-table');
/* useful variables */
var BLANK_PLAYER_IMAGE = "opponents/blank.png";
/* player array */

ReformCopyright
committed
var players = Array(5);

ReformCopyright
committed
/* Current timeout ID, so we can cancel it when restarting the game in order to avoid trouble. */
var timeoutID;
/**********************************************************************
* Game Wide Global Variables
**********************************************************************/
var table = new Table();
/**********************************************************************
* Screens & Modals
**********************************************************************/
/* Screens */
$titleScreen = $('#title-screen');
$selectScreen = $('#main-select-screen');
$individualSelectScreen = $('#individual-select-screen');
$groupSelectScreen = $('#group-select-screen');
$gameScreen = $('#game-screen');
$epilogueScreen = $('#epilogue-screen');
metodLD
committed
$galleryScreen = $('#gallery-screen');
/* Modals */
$searchModal = $('#search-modal');
$groupSearchModal = $('#group-search-modal');
$creditModal = $('#credit-modal');
$versionModal = $('#version-modal');
$gameSettingsModal = $('#game-settings-modal');
/* Screen State */
$previousScreen = null;
/********************************************************************************
* Game Wide Utility Functions
********************************************************************************/
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
/* Fetch a possibly compressed file.
* Attempts to fetch a compressed version of the file first,
* then fetches the uncompressed version of the file if that isn't found.
*/
function fetchCompressedURL(baseUrl, successCb, errorCb) {
/*
* The usual Jquery AJAX request function doesn't play nice with
* the binary-encoded data we'll get here, so we do the XHR manually.
* (I would use fetch() were it not for compatibility issues.)
*/
var req = new XMLHttpRequest();
req.open('GET', baseUrl+'.gz', true);
req.responseType = 'arraybuffer';
req.onload = function(ev) {
if (req.status < 400 && req.response) {
var data = new Uint8Array(req.response);
var decompressed = pako.inflate(data, { to: 'string' });
successCb(decompressed);
} else if (req.status === 404) {
$.ajax({
type: "GET",
url: baseUrl,
dataType: "text",
success: successCb,
error: errorCb,
});
} else {
errorCb();
}
}
req.onerror = function(err) {
$.ajax({
type: "GET",
url: baseUrl,
dataType: "text",
success: successCb,
error: errorCb,
});
}
req.send(null);
}
/**********************************************************************
***** Player Object Specification *****
**********************************************************************/
metodLD
committed
/************************************************************
metodLD
committed
* Creates and returns a new player object based on the
* supplied information.
*
* folder (string), the path to their folder
* first (string), their first name.
* last (string), their last name.
ReformCopyright
committed
* labels (string or XML element), what's shown on screen and what other players refer to them as.
* Can vary by stage.
* size (string): Their level of endowment
* intelligence (string or XML element), the name of their AI algorithm.
* Can vary by stage.
* gender (constant), their gender.
* clothing (array of Clothing objects), their clothing.
* timer (integer), time until forfeit is finished.
* state (array of PlayerState objects), their sequential states.
* xml (jQuery object), the player's loaded XML file.
************************************************************/

ReformCopyright
committed
function createNewPlayer (id, first, last, labels, gender, size, intelligence, timer, scale, tags, xml) {
var newPlayerObject = {id:id,
folder:'opponents/'+id+'/',
first:first,
ReformCopyright
committed
labels:labels,

ReformCopyright
committed
scale:scale,
xml:xml,
getImagesForStage: function(stage) {
if(!this.xml) return [];
var imageSet = {};
var folder = this.folder;
this.xml.find('stage[id="'+stage+'"] state').each(function () {
imageSet[folder+$(this).attr('img')] = true;
});
return Object.keys(imageSet);
},
ReformCopyright
committed
getByStage: function (arr) {
if (typeof(arr) === "string") {
return arr;
}
var bestFitStage = -1;
var bestFit = null;
ReformCopyright
committed
for (var i = 0; i < arr.length; i++) {
var startStage = arr[i].getAttribute('stage');
startStage = parseInt(startStage, 10) || 0;
ReformCopyright
committed
if (startStage > bestFitStage && startStage <= this.stage) {
bestFit = $(arr[i]).text();
bestFitStage = startStage;
}
}
ReformCopyright
committed
return bestFit;
},
getIntelligence: function () {
return this.getByStage(this.intelligence) || eIntelligence.AVERAGE;
ReformCopyright
committed
updateLabel: function () {
if (this.labels) this.label = this.getByStage(this.labels);
}
ReformCopyright
committed
initPlayerState(newPlayerObject);
/*******************************************************************
* (Re)Initialize the player properties that change during a game
*******************************************************************/
function initPlayerState(player) {
player.out = player.finished = player.exposed = false;
player.forfeit = "";

ReformCopyright
committed
player.stage = player.current = player.consecutiveLosses = 0;
player.timeInStage = -1;
player.markers = {};
if (player.xml !== null) {
/* Load in the legacy "start" lines, and also
* initialize player.chosenState to the first listed line.
* This may be overridden by later updateBehaviour calls if
* the player has (new-style) selected or game start case lines.
*/
player.allStates = parseDialogue(player.xml.find('start'), player);
player.chosenState = player.allStates[0];
loadOpponentWardrobe(player);
}
ReformCopyright
committed
player.updateLabel();
/**********************************************************************
***** Overarching Game Flow Functions *****
**********************************************************************/
/************************************************************
* Loads the initial content of the game.
************************************************************/
function initialSetup () {
/* start by creating the human player object */

ReformCopyright
committed
var humanPlayer = createNewPlayer("human", "", "", "", eGender.MALE, eSize.MEDIUM, eIntelligence.AVERAGE, 20, undefined, [], null);
/* enable table opacity */
tableOpacity = 1;
$gameTable.css({opacity:1});
/* load the all content */
loadTitleScreen();
selectTitleCandy();
/* Make sure that the config file is loaded before processing the
opponent list, so that includedOpponentStatuses is populated. */
loadConfigFile().always(loadSelectScreen);
save.loadCookie();
return $.ajax({
type: "GET",
url: "config.xml",
dataType: "text",
success: function(xml) {
var _epilogues = $(xml).find('epilogues').text();
if(_epilogues.toLowerCase() === 'false') {
EPILOGUES_ENABLED = false;
console.log("Epilogues are disabled.");
$("#title-gallery-edge").hide();
} else {
console.log("Epilogues are enabled.");
EPILOGUES_ENABLED = true;
}
var _epilogue_badges = $(xml).find('epilogue_badges').text();
if(_epilogue_badges.toLowerCase() === 'false') {
EPILOGUE_BADGES_ENABLED = false;
console.log("Epilogue badges are disabled.");
} else {
console.log("Epilogue badges are enabled.");
EPILOGUE_BADGES_ENABLED = true;
}
var _debug = $(xml).find('debug').text();
if (_debug === "true") {
DEBUG = true;
console.log("Debugging is enabled");
}
else {
DEBUG = false;
console.log("Debugging is disabled");
}
$(xml).find('include-status').each(function() {
includedOpponentStatuses[$(this).text()] = true;
console.log("Including", $(this).text(), "opponents");
});
function enterTitleScreen() {
$warningScreen.hide();
$titleScreen.show();
}
/************************************************************
* Transitions between two screens.
************************************************************/
function screenTransition (first, second) {
first.hide();
second.show();
}
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
/************************************************************
* Switches to the next screen based on the screen provided.
************************************************************/
function advanceToNextScreen (screen) {
if (screen == $titleScreen) {
/* advance to the select screen */
screenTransition($titleScreen, $selectScreen);
} else if (screen == $selectScreen) {
/* advance to the main game screen */
$selectScreen.hide();
loadGameScreen();
$gameScreen.show();
}
}
/************************************************************
* Switches to the last screen based on the screen provided.
************************************************************/
function returnToPreviousScreen (screen) {
if (screen == $selectScreen) {
/* return to the title screen */
$selectScreen.hide();
$titleScreen.show();
}
}
/************************************************************
* Resets the game state so that the game can be restarted.
************************************************************/
function resetPlayers () {
for (var i = 0; i < players.length; i++) {
if (players[i] != null) {
initPlayerState(players[i]);
}

ReformCopyright
committed
timers[i] = 0;
updateAllBehaviours(null, SELECTED);
}
/************************************************************
* Restarts the game.
************************************************************/
function restartGame () {
KEYBINDINGS_ENABLED = false;

ReformCopyright
committed
clearTimeout(timeoutID); // No error if undefined or no longer valid
timeoutID = autoForfeitTimeoutID = undefined;
stopCardAnimations();
/* enable table opacity */
tableOpacity = 1;
$gameTable.css({opacity:1});
$gamePlayerClothingArea.show();
$gamePlayerCardArea.show();
/* trigger screen refreshes */
updateSelectionVisuals();
updateAllGameVisuals();
selectTitleCandy();
/* there is only one call to this right now */
$epilogueSelectionModal.hide();
$gameScreen.hide();
$epilogueScreen.hide();

ReformCopyright
committed
loadClothing();
$titleScreen.show();
}
/**********************************************************************
***** Interaction Functions *****
**********************************************************************/
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
/************************************************************
* The player clicked the credits button. Shows the credits modal.
************************************************************/
function showCreditModal () {
$creditModal.modal('show');
}
/************************************************************
* The player clicked the version button. Shows the version modal.
************************************************************/
function showVersionModal () {
$versionModal.modal('show');
}
/************************************************************
* The player clicked on a table opacity button.
************************************************************/
function toggleTableVisibility () {
if (tableOpacity > 0) {
$gameTable.fadeOut();
tableOpacity = 0;
} else {
$gameTable.fadeIn();
tableOpacity = 1;
}
}
function forceTableVisibility(state) {
if (!state) {
$gameTable.fadeOut();
tableOpacity = 0;
} else {
$gameTable.fadeIn();
tableOpacity = 1;
}
}
/**********************************************************************
***** Utility Functions *****
**********************************************************************/
/************************************************************
* Returns a random number in a range.
************************************************************/
function getRandomNumber (min, max) {
return Math.floor(Math.random() * (max - min) + min);
}
ReformCopyright
committed
/************************************************************
* Changes the first letter in a string to upper case.
************************************************************/
String.prototype.initCap = function() {
return this.substr(0, 1).toUpperCase() + this.substr(1);
}
/**********************************************************************
* Returns the width of the visible screen in pixels.
**/
{
/* fetch all game screens */
var screens = document.getElementsByClassName('screen');
/* figure out which screen is visible */
{
/* this screen is currently visible */
return screens[i].offsetWidth;
}
}
}
/**********************************************************************
* Automatically adjusts the size of all font based on screen width.
**/
{
/* resize font */
var screenWidth = getScreenWidth();
document.body.style.fontSize = (14*(screenWidth/1000))+'px';

ReformCopyright
committed
if (backgroundImage && backgroundImage.height && backgroundImage.width) {
var w = window.innerWidth, h = window.innerHeight;
if (h > (3/4) * w) {
h = (3/4) * w;
} else {

ReformCopyright
committed
}
var ar = backgroundImage.width / backgroundImage.height;
if (ar > 4/3) {
var scale = Math.sqrt(16/9 / ar);
$("body").css("background-size", "auto " + Math.round(scale * h) + "px");

ReformCopyright
committed
} else {
var scale = Math.sqrt(ar);
$("body").css("background-size", Math.round(scale * w) + "px auto");

ReformCopyright
committed
}
}
/* set up future resizing */
window.onresize = autoResizeFont;
/* Get the number of players loaded, including the human player.*/
function countLoadedOpponents() {
return players.reduce(function (a, v) { return a + (v ? 1 : 0); }, 0);
}