Spaces:
Running
Running
function Game(debugMode, startLevel) { | |
/* private properties */ | |
var __currentCode = ''; | |
var __commands = []; | |
var __playerCodeRunning = false; | |
/* unexposed properties */ | |
this._dimensions = { | |
width: 50, | |
height: 25 | |
}; | |
this._levelFileNames = [ | |
//%LEVELS% | |
]; | |
this._bonusLevels = [ | |
//%BONUS% | |
].filter(function (lvl) { return (lvl.indexOf('_') != 0); }); // filter out bonus levels that start with '_' | |
this._mod = '//%MOD%'; | |
this._viewableScripts = [ | |
'codeEditor.js', | |
'display.js', | |
'dynamicObject.js', | |
'game.js', | |
'inventory.js', | |
'map.js', | |
'objects.js', | |
'player.js', | |
'reference.js', | |
'sound.js', | |
'ui.js', | |
'util.js', | |
'validate.js' | |
]; | |
this._editableScripts = [ | |
'map.js', | |
'objects.js', | |
'player.js' | |
]; | |
this._resetTimeout = null; | |
this._currentLevel = 0; | |
this._currentFile = null; | |
this._currentBonusLevel = null; | |
this._levelReached = 1; | |
this._displayedChapters = []; | |
this._playerPrototype = Player; // to allow messing with map.js and player.js later | |
this._nextBonusLevel = null; | |
/* unexposed getters */ | |
this._getHelpCommands = function () { return __commands; }; | |
this._isPlayerCodeRunning = function () { return __playerCodeRunning; }; | |
this._getLocalKey = function (key) { return (this._mod.length == 0 ? '' : this._mod + '.') + key; }; | |
/* unexposed setters */ | |
this._setPlayerCodeRunning = function (pcr) { __playerCodeRunning = pcr; }; | |
/* unexposed methods */ | |
this._initialize = function () { | |
// Get last level reached from localStorage (if any) | |
var levelKey = this._mod.length == 0 ? 'levelReached' : this._mod + '.levelReached'; | |
this._levelReached = parseInt(localStorage.getItem(levelKey)) || 1; | |
// Fix potential corruption | |
// levelReached may be "81111" instead of "8" due to bug | |
if (this._levelReached > this._levelFileNames.length) { | |
for (var l = 1; l <= this._levelFileNames.length; l++) { | |
if (!localStorage[this._getLocalKey("level" + l + ".lastGoodState")]) { | |
this._levelReached = l - 1; | |
break; | |
} | |
} | |
} | |
// Initialize sound | |
this.sound = new Sound('local'); | |
// this.sound = new Sound(debugMode ? 'local' : 'cloudfront'); | |
// Initialize map display | |
this.display = ROT.Display.create(this, { | |
width: this._dimensions.width, | |
height: this._dimensions.height, | |
fontSize: 20 | |
}); | |
this.display.setupEventHandlers(); | |
var display = this.display; | |
$('#screen').append(this.display.getContainer()); | |
$('#drawingCanvas, #dummyDom, #dummyDom *').click(function () { | |
display.focus(); | |
}); | |
// Initialize editor, map, and objects | |
this.editor = new CodeEditor("editor", 600, 500, this); | |
this.map = new Map(this.display, this); | |
this.objects = this.getListOfObjects(); | |
// Enable controls | |
this.enableShortcutKeys(); | |
this.enableButtons(); | |
this.setUpNotepad(); | |
// Load help commands from local storage (if possible) | |
if (localStorage.getItem(this._getLocalKey('helpCommands'))) { | |
__commands = localStorage.getItem(this._getLocalKey('helpCommands')).split(';'); | |
} | |
// Enable debug features | |
if (debugMode) { | |
this._debugMode = true; | |
this._levelReached = 999; // make all levels accessible | |
__commands = Object.keys(this.reference); // display all help | |
this.sound.toggleSound(); // mute sound by default in debug mode | |
} | |
// Lights, camera, action | |
if (startLevel) { | |
this._currentLevel = startLevel - 1; | |
this._getLevel(startLevel, debugMode); | |
} else if (!debugMode && this._levelReached != 1) { | |
// load last level reached (unless it's the credits) | |
this._getLevel(Math.min(this._levelReached, 21)); | |
} else { | |
this._intro(); | |
} | |
}; | |
this._intro = function () { | |
this.display.focus(); | |
this.display.playIntro(this.map); | |
}; | |
this._start = function (lvl) { | |
this._getLevel(lvl ? lvl : 1); | |
}; | |
this._moveToNextLevel = function () { | |
// is the player permitted to exit? | |
if (typeof this.onExit === 'function') { | |
var game = this; | |
var canExit = this.validateCallback(function () { | |
return game.onExit(game.map); | |
}); | |
if (!canExit) { | |
this.sound.playSound('blip'); | |
return; | |
} | |
} | |
this.sound.playSound('complete'); | |
//we disable moving so the player can't move during the fadeout | |
this.map.getPlayer()._canMove = false; | |
if (this._currentLevel == 'bonus') { | |
if (this._nextBonusLevel) { | |
this._getLevelByPath("levels/bonus/" + this._nextBonusLevel); | |
} else { | |
// open main menu | |
$('#helpPane, #notepadPane').hide(); | |
$('#menuPane').show(); | |
} | |
} else { | |
this._getLevel(this._currentLevel + 1, false, true); | |
} | |
}; | |
this._jumpToNthLevel = function (levelNum) { | |
this._currentFile = null; | |
this._getLevel(levelNum, false, false); | |
this.display.focus(); | |
this.sound.playSound('blip'); | |
}; | |
// makes an ajax request to get the level text file and | |
// then loads it into the game | |
this._getLevel = function (levelNum, isResetting, movingToNextLevel) { | |
var game = this; | |
var editor = this.editor; | |
if (levelNum > this._levelFileNames.length) { | |
return; | |
} | |
this._levelReached = Math.max(levelNum, this._levelReached); | |
if (!debugMode) { | |
localStorage.setItem(this._getLocalKey('levelReached'), this._levelReached); | |
} | |
var fileName = this._levelFileNames[levelNum - 1]; | |
lvlCode = this._levels['levels/' + fileName]; | |
if (movingToNextLevel) { | |
// save level state and create a gist | |
editor.saveGoodState(); | |
editor.createGist(); | |
} | |
game._currentLevel = levelNum; | |
game._currentBonusLevel = null; | |
game._currentFile = null; | |
// load level code in editor | |
editor.loadCode(lvlCode); | |
// restored saved state for this level? | |
if (!isResetting && editor.getGoodState(levelNum)) { | |
// unless the current level is a newer version | |
var newVer = editor.getProperties().version; | |
var savedVer = editor.getGoodState(levelNum).version; | |
if (!(newVer && (!savedVer || isNewerVersion(newVer, savedVer)))) { | |
// restore saved line/section/endOfStartLevel state if possible | |
if (editor.getGoodState(levelNum).endOfStartLevel) { | |
editor.setEndOfStartLevel(editor.getGoodState(levelNum).endOfStartLevel); | |
} | |
if (editor.getGoodState(levelNum).editableLines) { | |
editor.setEditableLines(editor.getGoodState(levelNum).editableLines); | |
} | |
if (editor.getGoodState(levelNum).editableSections) { | |
editor.setEditableSections(editor.getGoodState(levelNum).editableSections); | |
} | |
// restore saved code | |
editor.setCode(editor.getGoodState(levelNum).code); | |
} | |
} | |
// start the level and fade in | |
game._evalLevelCode(null, null, true); | |
game.display.focus(); | |
// store the commands introduced in this level (for api reference) | |
__commands = __commands.concat(editor.getProperties().commandsIntroduced).unique(); | |
localStorage.setItem(this._getLocalKey('helpCommands'), __commands.join(';')); | |
}; | |
this._getLevelByPath = function (filePath) { | |
var game = this; | |
var editor = this.editor; | |
$.get(filePath, function (lvlCode) { | |
game._currentLevel = 'bonus'; | |
game._currentBonusLevel = filePath.split("levels/")[1]; | |
game._currentFile = null; | |
// load level code in editor | |
editor.loadCode(lvlCode); | |
// save next bonus level | |
game._nextBonusLevel = editor.getProperties()["nextBonusLevel"]; | |
// start the level and fade in | |
game._evalLevelCode(null, null, true); | |
game.display.focus(); | |
// store the commands introduced in this level (for api reference) | |
__commands = __commands.concat(editor.getProperties().commandsIntroduced).unique(); | |
localStorage.setItem(this._getLocalKey('helpCommands'), __commands.join(';')); | |
}, 'text'); | |
}; | |
// how meta can we go? | |
this._editFile = function (filePath) { | |
var game = this; | |
var fileName = filePath.split('/')[filePath.split('/').length - 1]; | |
game._currentFile = filePath; | |
$.get(filePath, function (code) { | |
// load level code in editor | |
if (game._editableScripts.indexOf(fileName) > -1) { | |
game.editor.loadCode('#BEGIN_EDITABLE#\n' + code + '\n#END_EDITABLE#'); | |
} else { | |
game.editor.loadCode(code); | |
} | |
}, 'text'); | |
}; | |
this._resetLevel = function( level ) { | |
var game = this; | |
var resetTimeout_msec = 2500; | |
var reset_game_msg = "To reset this level press ^4 again."; | |
if ( this._resetTimeout != null ) { | |
$('body, #buttons').css('background-color', '#000'); | |
window.clearTimeout( this._resetTimeout ); | |
this._resetTimeout = null; | |
if (game._currentBonusLevel) { | |
game._getLevelByPath('levels/' + game._currentBonusLevel); | |
} else { | |
this._getLevel(level, true); | |
} | |
if(game.map._status == reset_game_msg) { | |
game.map.writeStatus(""); | |
} | |
} else { | |
this.map.writeStatus(reset_game_msg); | |
$('body, #buttons').css('background-color', '#900'); | |
this._resetTimeout = setTimeout(function () { | |
game._resetTimeout = null; | |
if(game.map._status == reset_game_msg) { | |
game.map.writeStatus(""); | |
} | |
$('body, #buttons').css('background-color', '#000'); | |
}, resetTimeout_msec ); | |
} | |
}; | |
// restart level with currently loaded code | |
this._restartLevel = function () { | |
this.editor.setCode(__currentCode); | |
this._evalLevelCode(); | |
}; | |
this._evalLevelCode = function (allCode, playerCode, isNewLevel, restartingLevelFromScript) { | |
this.map._clearIntervals(); | |
var game = this; | |
// by default, get code from the editor | |
var loadedFromEditor = false; | |
if (!allCode) { | |
allCode = this.editor.getCode(); | |
playerCode = this.editor.getPlayerCode(); | |
loadedFromEditor = true; | |
} | |
// if we're editing a script file, do something completely different | |
if (this._currentFile !== null && !restartingLevelFromScript) { | |
__currentCode = allCode; | |
this.validateAndRunScript(allCode); | |
return; | |
} | |
// save current display state (for scrolling up later) | |
this.display.saveGrid(this.map); | |
// validate the code | |
// if it passes validation, returns the startLevel function if it pass | |
// if it fails validation, returns false | |
var validatedStartLevel = this.validate(allCode, playerCode, restartingLevelFromScript); | |
if (validatedStartLevel) { // code is valid | |
// reset the map | |
this.map._reset(); // for cleanup | |
this.map = new Map(this.display, this); | |
this.map._reset(); | |
this.map._setProperties(this.editor.getProperties()['mapProperties']); | |
// save editor state | |
if (!restartingLevelFromScript) { | |
__currentCode = allCode; | |
} | |
if (loadedFromEditor && !restartingLevelFromScript) { | |
this.editor.saveGoodState(); | |
} | |
// clear drawing canvas and hide it until level loads | |
var screenCanvas = $('#screen canvas')[0]; | |
$('#drawingCanvas')[0].width = screenCanvas.width; | |
$('#drawingCanvas')[0].height = screenCanvas.height; | |
$('#drawingCanvas').hide(); | |
$('#dummyDom').hide(); | |
// set correct inventory state | |
this.setInventoryStateByLevel(this._currentLevel); | |
// start the level | |
validatedStartLevel(this.map); | |
// Add the computer to bonus levels that lack it | |
if (this._currentLevel == "bonus" && this.map.countObjects("computer") == 0) { | |
this.addToInventory("computer") | |
$('#editorPane, #savedLevelMsg').show(); | |
this.editor.refresh(); | |
} | |
// draw the map | |
this.display.fadeIn(this.map, isNewLevel ? 100 : 10, function () { | |
game.map.refresh(); // refresh inventory display | |
// show map overlays if necessary | |
if (game.map._properties.showDrawingCanvas) { | |
$('#drawingCanvas').show(); | |
} else if (game.map._properties.showDummyDom) { | |
$('#dummyDom').show(); | |
} | |
// workaround because we can't use writeStatus() in startLevel() | |
// (due to the text getting overwritten by the fade-in) | |
if (game.editor.getProperties().startingMessage) { | |
game.map.writeStatus(game.editor.getProperties().startingMessage); | |
} | |
}); | |
this.map._ready(); | |
// start bg music for this level | |
if (this.editor.getProperties().music) { | |
this.sound.playTrackByName(this.editor.getProperties().music); | |
} | |
// activate super menu if 21_endOfTheLine has been reached | |
if (this._levelReached >= 21) { | |
this.activateSuperMenu(); | |
} | |
// finally, allow player movement | |
if (this.map.getPlayer()) { | |
this.map.getPlayer()._canMove = true; | |
game.display.focus(); | |
} | |
} else { // code is invalid | |
// play error sound | |
this.sound.playSound('static'); | |
// disable player movement | |
this.map.getPlayer()._canMove = false; | |
} | |
}; | |
this._callUnexposedMethod = function(f) { | |
if (__playerCodeRunning) { | |
__playerCodeRunning = false; | |
try { | |
res = f(); | |
} finally { | |
__playerCodeRunning = true; | |
} | |
return res; | |
} else { | |
return f(); | |
} | |
}; | |
this._checkObjective = function() { | |
if (typeof(this.objective) === 'function') { | |
var game = this; | |
var objectiveIsMet = this.validateCallback(function() { | |
return game.objective(game.map); | |
}); | |
if (objectiveIsMet) { | |
this._moveToNextLevel(); | |
} | |
} | |
} | |
} | |