forked from sent/waves
441 lines
12 KiB
JavaScript
441 lines
12 KiB
JavaScript
/*
|
|
* Module for handling States
|
|
*
|
|
* All states should be get and set through the StateManager ($SM).
|
|
*
|
|
* The manager is intended to handle all needed checks and error catching.
|
|
* This includes creating the parents of layered/deep states so undefined states
|
|
* do not need to be tested for and created beforehand.
|
|
*
|
|
* When a state is changed, an update event is sent out containing the name of the state
|
|
* changed or in the case of multiple changes (.setM, .addM) the parent class changed.
|
|
* Event: type: 'stateUpdate', stateName: <path of state or parent state>
|
|
*
|
|
* Original file created by: Michael Galusha
|
|
*/
|
|
|
|
var StateManager = {
|
|
|
|
MAX_STORE: 99999999999999,
|
|
|
|
options: {},
|
|
|
|
init: function(options) {
|
|
this.options = $.extend(
|
|
this.options,
|
|
options
|
|
);
|
|
|
|
//create categories
|
|
var cats = [
|
|
'features', // big features like buildings, location availability, unlocks, etc
|
|
'stores', // little stuff, items, weapons, etc
|
|
'character', // this is for player's character stats such as perks
|
|
'income',
|
|
'timers',
|
|
'game', // mostly location related: fire temp, workers, population, world map, etc
|
|
'playStats', // anything play related: play time, loads, etc
|
|
'previous', // prestige, score, trophies (in future), achievements (again, not yet), etc
|
|
'outfit', // used to temporarily store the items to be taken on the path
|
|
'config',
|
|
'wait', // mysterious wanderers are coming back
|
|
'cooldown' // residual values for cooldown buttons
|
|
];
|
|
|
|
for(var which in cats) {
|
|
if(!$SM.get(cats[which])) $SM.set(cats[which], {});
|
|
}
|
|
|
|
//subscribe to stateUpdates
|
|
$.Dispatch('stateUpdate').subscribe($SM.handleStateUpdates);
|
|
},
|
|
|
|
//create all parents and then set state
|
|
createState: function(stateName, value) {
|
|
var words = stateName.split(/[.\[\]'"]+/);
|
|
//for some reason there are sometimes empty strings
|
|
for (var j = 0; j < words.length; j++) {
|
|
if (words[j] === '') {
|
|
words.splice(j, 1);
|
|
j--;
|
|
}
|
|
}
|
|
var obj = State;
|
|
var w = null;
|
|
for(var i=0, len=words.length-1;i<len;i++){
|
|
w = words[i];
|
|
if(obj[w] === undefined ) obj[w] = {};
|
|
obj = obj[w];
|
|
}
|
|
obj[words[i]] = value;
|
|
return obj;
|
|
},
|
|
|
|
//set single state
|
|
//if noEvent is true, the update event won't trigger, useful for setting multiple states first
|
|
set: function(stateName, value, noEvent) {
|
|
var fullPath = $SM.buildPath(stateName);
|
|
|
|
//make sure the value isn't over the engine maximum
|
|
if(typeof value == 'number' && value > $SM.MAX_STORE) value = $SM.MAX_STORE;
|
|
|
|
try{
|
|
eval('('+fullPath+') = value');
|
|
} catch (e) {
|
|
//parent doesn't exist, so make parent
|
|
$SM.createState(stateName, value);
|
|
}
|
|
|
|
//stores values can not be negative
|
|
if(stateName.indexOf('stores') === 0 && $SM.get(stateName, true) < 0) {
|
|
eval('('+fullPath+') = 0');
|
|
Engine.log('WARNING: state:' + stateName + ' can not be a negative value. Set to 0 instead.');
|
|
}
|
|
|
|
if(!noEvent) {
|
|
Engine.saveGame();
|
|
$SM.fireUpdate(stateName);
|
|
}
|
|
},
|
|
|
|
//sets a list of states
|
|
setM: function(parentName, list, noEvent) {
|
|
$SM.buildPath(parentName);
|
|
|
|
//make sure the state exists to avoid errors,
|
|
if($SM.get(parentName) === undefined) $SM.set(parentName, {}, true);
|
|
|
|
for(var k in list){
|
|
$SM.set(parentName+'["'+k+'"]', list[k], true);
|
|
}
|
|
|
|
if(!noEvent) {
|
|
Engine.saveGame();
|
|
$SM.fireUpdate(parentName);
|
|
}
|
|
},
|
|
|
|
//shortcut for altering number values, return 1 if state wasn't a number
|
|
add: function(stateName, value, noEvent) {
|
|
var err = 0;
|
|
//0 if undefined, null (but not {}) should allow adding to new objects
|
|
//could also add in a true = 1 thing, to have something go from existing (true)
|
|
//to be a count, but that might be unwanted behavior (add with loose eval probably will happen anyways)
|
|
var old = $SM.get(stateName, true);
|
|
|
|
//check for NaN (old != old) and non number values
|
|
if(old != old){
|
|
Engine.log('WARNING: '+stateName+' was corrupted (NaN). Resetting to 0.');
|
|
old = 0;
|
|
$SM.set(stateName, old + value, noEvent);
|
|
} else if(typeof old != 'number' || typeof value != 'number'){
|
|
Engine.log('WARNING: Can not do math with state:'+stateName+' or value:'+value+' because at least one is not a number.');
|
|
err = 1;
|
|
} else {
|
|
$SM.set(stateName, old + value, noEvent); //setState handles event and save
|
|
}
|
|
|
|
return err;
|
|
},
|
|
|
|
//alters multiple number values, return number of fails
|
|
addM: function(parentName, list, noEvent) {
|
|
var err = 0;
|
|
|
|
//make sure the parent exists to avoid errors
|
|
if($SM.get(parentName) === undefined) $SM.set(parentName, {}, true);
|
|
|
|
for(var k in list){
|
|
if($SM.add(parentName+'["'+k+'"]', list[k], true)) err++;
|
|
}
|
|
|
|
if(!noEvent) {
|
|
Engine.saveGame();
|
|
$SM.fireUpdate(parentName);
|
|
}
|
|
return err;
|
|
},
|
|
|
|
//return state, undefined or 0
|
|
get: function(stateName, requestZero) {
|
|
var whichState = null;
|
|
var fullPath = $SM.buildPath(stateName);
|
|
|
|
//catch errors if parent of state doesn't exist
|
|
try{
|
|
eval('whichState = ('+fullPath+')');
|
|
} catch (e) {
|
|
whichState = undefined;
|
|
}
|
|
|
|
//prevents repeated if undefined, null, false or {}, then x = 0 situations
|
|
if((!whichState || whichState == {}) && requestZero) return 0;
|
|
else return whichState;
|
|
},
|
|
|
|
//mainly for local copy use, add(M) can fail so we can't shortcut them
|
|
//since set does not fail, we know state exists and can simply return the object
|
|
setget: function(stateName, value, noEvent){
|
|
$SM.set(stateName, value, noEvent);
|
|
return eval('('+$SM.buildPath(stateName)+')');
|
|
},
|
|
|
|
remove: function(stateName, noEvent) {
|
|
var whichState = $SM.buildPath(stateName);
|
|
try{
|
|
eval('(delete '+whichState+')');
|
|
} catch (e) {
|
|
//it didn't exist in the first place
|
|
Engine.log('WARNING: Tried to remove non-existant state \''+stateName+'\'.');
|
|
}
|
|
if(!noEvent){
|
|
Engine.saveGame();
|
|
$SM.fireUpdate(stateName);
|
|
}
|
|
},
|
|
|
|
removeBranch: function(stateName, noEvent) {
|
|
for(var i in $SM.get(stateName)){
|
|
if(typeof $SM.get(stateName)[i] == 'object'){
|
|
$SM.removeBranch(stateName +'["'+ i +'"]');
|
|
}
|
|
}
|
|
if($.isEmptyObject($SM.get(stateName))){
|
|
$SM.remove(stateName);
|
|
}
|
|
if(!noEvent){
|
|
Engine.saveGame();
|
|
$SM.fireUpdate(stateName);
|
|
}
|
|
},
|
|
|
|
//creates full reference from input
|
|
//hopefully this won't ever need to be more complicated
|
|
buildPath: function(input){
|
|
var dot = (input.charAt(0) == '[')? '' : '.'; //if it starts with [foo] no dot to join
|
|
return 'State' + dot + input;
|
|
},
|
|
|
|
fireUpdate: function(stateName, save){
|
|
var category = $SM.getCategory(stateName);
|
|
if(stateName === undefined) stateName = category = 'all'; //best if this doesn't happen as it will trigger more stuff
|
|
$.Dispatch('stateUpdate').publish({'category': category, 'stateName':stateName});
|
|
if(save) Engine.saveGame();
|
|
},
|
|
|
|
getCategory: function(stateName){
|
|
var firstOB = stateName.indexOf('[');
|
|
var firstDot = stateName.indexOf('.');
|
|
var cutoff = null;
|
|
if(firstOB == -1 || firstDot == -1){
|
|
cutoff = firstOB > firstDot ? firstOB : firstDot;
|
|
} else {
|
|
cutoff = firstOB < firstDot ? firstOB : firstDot;
|
|
}
|
|
if (cutoff == -1){
|
|
return stateName;
|
|
} else {
|
|
return stateName.substr(0,cutoff);
|
|
}
|
|
},
|
|
|
|
//Use this function to make old save games compatible with new version
|
|
updateOldState: function(){
|
|
var version = $SM.get('version');
|
|
if(typeof version != 'number') version = 1.0;
|
|
if(version == 1.0) {
|
|
// v1.1 introduced the Lodge, so get rid of lodgeless hunters
|
|
$SM.remove('outside.workers.hunter', true);
|
|
$SM.remove('income.hunter', true);
|
|
Engine.log('upgraded save to v1.1');
|
|
version = 1.1;
|
|
}
|
|
if(version == 1.1) {
|
|
//v1.2 added the Swamp to the map, so add it to already generated maps
|
|
if($SM.get('world')) {
|
|
World.placeLandmark(15, World.RADIUS * 1.5, World.TILE.SWAMP, $SM.get('world.map'));
|
|
}
|
|
Engine.log('upgraded save to v1.2');
|
|
version = 1.2;
|
|
}
|
|
if(version == 1.2) {
|
|
//StateManager added, so move data to new locations
|
|
$SM.remove('room.fire');
|
|
$SM.remove('room.temperature');
|
|
$SM.remove('room.buttons');
|
|
if($SM.get('room')){
|
|
$SM.set('features.location.room', true);
|
|
$SM.set('game.builder.level', $SM.get('room.builder'));
|
|
$SM.remove('room');
|
|
}
|
|
if($SM.get('outside')){
|
|
$SM.set('features.location.outside', true);
|
|
$SM.set('game.population', $SM.get('outside.population'));
|
|
$SM.set('game.buildings', $SM.get('outside.buildings'));
|
|
$SM.set('game.workers', $SM.get('outside.workers'));
|
|
$SM.set('game.outside.seenForest', $SM.get('outside.seenForest'));
|
|
$SM.remove('outside');
|
|
}
|
|
if($SM.get('world')){
|
|
$SM.set('features.location.world', true);
|
|
$SM.set('game.world.map', $SM.get('world.map'));
|
|
$SM.set('game.world.mask', $SM.get('world.mask'));
|
|
$SM.set('starved', $SM.get('character.starved', true));
|
|
$SM.set('dehydrated', $SM.get('character.dehydrated', true));
|
|
$SM.remove('world');
|
|
$SM.remove('starved');
|
|
$SM.remove('dehydrated');
|
|
}
|
|
if($SM.get('ship')){
|
|
$SM.set('features.location.spaceShip', true);
|
|
$SM.set('game.spaceShip.hull', $SM.get('ship.hull', true));
|
|
$SM.set('game.spaceShip.thrusters', $SM.get('ship.thrusters', true));
|
|
$SM.set('game.spaceShip.seenWarning', $SM.get('ship.seenWarning'));
|
|
$SM.set('game.spaceShip.seenShip', $SM.get('ship.seenShip'));
|
|
$SM.remove('ship');
|
|
}
|
|
if($SM.get('punches')){
|
|
$SM.set('character.punches', $SM.get('punches'));
|
|
$SM.remove('punches');
|
|
}
|
|
if($SM.get('perks')){
|
|
$SM.set('character.perks', $SM.get('perks'));
|
|
$SM.remove('perks');
|
|
}
|
|
if($SM.get('thieves')){
|
|
$SM.set('game.thieves', $SM.get('thieves'));
|
|
$SM.remove('thieves');
|
|
}
|
|
if($SM.get('stolen')){
|
|
$SM.set('game.stolen', $SM.get('stolen'));
|
|
$SM.remove('stolen');
|
|
}
|
|
if($SM.get('cityCleared')){
|
|
$SM.set('character.cityCleared', $SM.get('cityCleared'));
|
|
$SM.remove('cityCleared');
|
|
}
|
|
$SM.set('version', 1.3);
|
|
}
|
|
},
|
|
|
|
/******************************************************************
|
|
* Start of specific state functions
|
|
******************************************************************/
|
|
//PERKS
|
|
addPerk: function(name) {
|
|
$SM.set('character.perks["'+name+'"]', true);
|
|
Notifications.notify(null, Engine.Perks[name].notify);
|
|
},
|
|
|
|
hasPerk: function(name) {
|
|
return $SM.get('character.perks["'+name+'"]');
|
|
},
|
|
|
|
//INCOME
|
|
setIncome: function(source, options) {
|
|
var existing = $SM.get('income["'+source+'"]');
|
|
if(typeof existing != 'undefined') {
|
|
options.timeLeft = existing.timeLeft;
|
|
}
|
|
$SM.set('income["'+source+'"]', options);
|
|
},
|
|
|
|
getIncome: function(source) {
|
|
var existing = $SM.get('income["'+source+'"]');
|
|
if(typeof existing != 'undefined') {
|
|
return existing;
|
|
}
|
|
return {};
|
|
},
|
|
|
|
collectIncome: function() {
|
|
var changed = false;
|
|
if(typeof $SM.get('income') != 'undefined' && Engine.activeModule != Space) {
|
|
for(var source in $SM.get('income')) {
|
|
var income = $SM.get('income["'+source+'"]');
|
|
if(typeof income.timeLeft != 'number')
|
|
{
|
|
income.timeLeft = 0;
|
|
}
|
|
income.timeLeft--;
|
|
|
|
if(income.timeLeft <= 0) {
|
|
Engine.log('collection income from ' + source);
|
|
if(source == 'thieves') $SM.addStolen(income.stores);
|
|
|
|
var cost = income.stores;
|
|
var ok = true;
|
|
if (source != 'thieves') {
|
|
for (var k in cost) {
|
|
var have = $SM.get('stores["' + k + '"]', true);
|
|
if (have + cost[k] < 0) {
|
|
ok = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(ok){
|
|
$SM.addM('stores', income.stores, true);
|
|
}
|
|
changed = true;
|
|
if(typeof income.delay == 'number') {
|
|
income.timeLeft = income.delay;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(changed){
|
|
$SM.fireUpdate('income', true);
|
|
}
|
|
Engine._incomeTimeout = Engine.setTimeout($SM.collectIncome, 1000);
|
|
},
|
|
|
|
//Thieves
|
|
addStolen: function(stores) {
|
|
for(var k in stores) {
|
|
var old = $SM.get('stores["'+k+'"]', true);
|
|
var short = old + stores[k];
|
|
//if they would steal more than actually owned
|
|
if(short < 0){
|
|
$SM.add('game.stolen["'+k+'"]', (stores[k] * -1) + short);
|
|
} else {
|
|
$SM.add('game.stolen["'+k+'"]', stores[k] * -1);
|
|
}
|
|
}
|
|
},
|
|
|
|
startThieves: function() {
|
|
$SM.set('game.thieves', 1);
|
|
$SM.setIncome('thieves', {
|
|
delay: 10,
|
|
stores: {
|
|
'wood': -10,
|
|
'fur': -5,
|
|
'meat': -5
|
|
}
|
|
});
|
|
},
|
|
|
|
//Misc
|
|
num: function(name, craftable) {
|
|
switch(craftable.type) {
|
|
case 'good':
|
|
case 'tool':
|
|
case 'weapon':
|
|
case 'upgrade':
|
|
case 'special':
|
|
return $SM.get('stores["'+name+'"]', true);
|
|
case 'building':
|
|
return $SM.get('game.buildings["'+name+'"]', true);
|
|
}
|
|
},
|
|
|
|
handleStateUpdates: function(e){
|
|
|
|
}
|
|
};
|
|
|
|
//alias
|
|
var $SM = StateManager;
|