diff --git a/script/Button.js b/script/Button.js index 845c690..35d46ef 100644 --- a/script/Button.js +++ b/script/Button.js @@ -24,6 +24,9 @@ var Button = { el.append($("
").addClass('cooldown')); + // waiting for expiry of residual cooldown detected in state + Button.cooldown(el, 'state'); + if(options.cost) { var ttPos = options.ttPos ? options.ttPos : "bottom right"; var costTooltip = $('
').addClass('tooltip ' + ttPos); @@ -43,6 +46,8 @@ var Button = { return el; }, + saveCooldown: true, + setDisabled: function(btn, disabled) { if(btn) { if(!disabled && !btn.data('onCooldown')) { @@ -61,29 +66,57 @@ var Button = { return false; }, - cooldown: function(btn) { + cooldown: function(btn, option) { var cd = btn.data("cooldown"); + var id = 'cooldown.'+ btn.attr('id'); if(cd > 0) { - milliseconds = cd * 1000; - if (Engine.options.doubleTime){ - milliseconds /= 2; + // param "start" takes value from cooldown time if not specified + var start, left; + switch(option){ + // a switch will allow for several uses of cooldown function + case 'state': + if(!$SM.get(id)){ + return; + } + start = Math.min($SM.get(id), cd); + left = (start / cd).toFixed(4); + break; + default: + start = cd; + left = 1; } - - $('div.cooldown', btn).stop(true, true).width("100%").animate({width: '0%'}, milliseconds, 'linear', function() { - var b = $(this).closest('.button'); - b.data('onCooldown', false); - if(!b.data('disabled')) { - b.removeClass('disabled'); - } + Button.clearCooldown(btn); + if(Button.saveCooldown){ + $SM.set(id,start); + // residual value is measured in seconds + // saves program performance + btn.data('countdown', Engine.setInterval(function(){ + $SM.set(id, $SM.get(id, true) - 0.5, true); + },500)); + } + var time = start; + if (Engine.options.doubleTime){ + time /= 2; + } + $('div.cooldown', btn).width(left * 100 +"%").animate({width: '0%'}, time * 1000, 'linear', function() { + Button.clearCooldown(btn, true); }); btn.addClass('disabled'); btn.data('onCooldown', true); } }, - clearCooldown: function(btn) { - $('div.cooldown', btn).stop(true, true); + clearCooldown: function(btn, ended) { + var ended = ended || false; + if(!ended){ + $('div.cooldown', btn).stop(true, true); + } btn.data('onCooldown', false); + if(btn.data('countdown')){ + window.clearInterval(btn.data('countdown')); + $SM.remove('cooldown.'+ btn.attr('id')); + btn.removeData('countdown'); + } if(!btn.data('disabled')) { btn.removeClass('disabled'); } diff --git a/script/events.js b/script/events.js index ee70bd5..11d077d 100644 --- a/script/events.js +++ b/script/events.js @@ -955,6 +955,7 @@ var Events = { Engine.event('game event', 'event'); Engine.keyLock = true; Engine.tabNavigation = false; + Button.saveCooldown = false; Events.eventStack.unshift(event); event.eventPanel = $('
').attr('id', 'event').addClass('eventPanel').css('opacity', '0'); if(options != null && options.width != null) { @@ -988,6 +989,7 @@ var Events = { Engine.log(Events.eventStack.length + ' events remaining'); Engine.keyLock = false; Engine.tabNavigation = true; + Button.saveCooldown = true; if (Events.BLINK_INTERVAL) { Events.stopTitleBlink(); } diff --git a/script/outside.js b/script/outside.js index 6ab8e2b..4fdfa0e 100644 --- a/script/outside.js +++ b/script/outside.js @@ -171,6 +171,8 @@ var Outside = { cooldown: Outside._GATHER_DELAY, width: '80px' }).appendTo('div#outsidePanel'); + + Outside.updateTrapButton(); }, getMaxPopulation: function() { diff --git a/script/state_manager.js b/script/state_manager.js index b57fdcd..d5b63eb 100644 --- a/script/state_manager.js +++ b/script/state_manager.js @@ -28,17 +28,18 @@ var StateManager = { //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 + '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 + '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 + 'outfit', // used to temporarily store the items to be taken on the path 'config', - 'wait' // mysterious wanderers are coming back + 'wait', // mysterious wanderers are coming back + 'cooldown' // residual values for cooldown buttons ]; for(var which in cats) { diff --git a/script/world.js b/script/world.js index 948fa3b..f0f21d9 100644 --- a/script/world.js +++ b/script/world.js @@ -1,5 +1,5 @@ var World = { - + RADIUS: 30, VILLAGE_POS: [30, 30], TILE: { @@ -101,7 +101,7 @@ var World = { cost: { 'bolas': 1 } } }, - + name: 'World', options: {}, // Nothing for now init: function(options) { @@ -109,12 +109,12 @@ var World = { this.options, options ); - + // Setup probabilities. Sum must equal 1. World.TILE_PROBS[World.TILE.FOREST] = 0.15; World.TILE_PROBS[World.TILE.FIELD] = 0.35; World.TILE_PROBS[World.TILE.BARRENS] = 0.5; - + // Setpiece definitions World.LANDMARKS[World.TILE.OUTPOST] = { num: 0, minRadius: 0, maxRadius: 0, scene: 'outpost', label: _('An Outpost') }; World.LANDMARKS[World.TILE.IRON_MINE] = { num: 1, minRadius: 5, maxRadius: 5, scene: 'ironmine', label: _('Iron Mine') }; @@ -128,12 +128,12 @@ var World = { World.LANDMARKS[World.TILE.BOREHOLE] = { num: 10, minRadius: 15, maxRadius: World.RADIUS * 1.5, scene: 'borehole', label: _('A Borehole')}; World.LANDMARKS[World.TILE.BATTLEFIELD] = { num: 5, minRadius: 18, maxRadius: World.RADIUS * 1.5, scene: 'battlefield', label: _('A Battlefield')}; World.LANDMARKS[World.TILE.SWAMP] = { num: 1, minRadius: 15, maxRadius: World.RADIUS * 1.5, scene: 'swamp', label: _('A Murky Swamp')}; - + // Only add the cache if there is prestige data if($SM.get('previous.stores')) { World.LANDMARKS[World.TILE.CACHE] = { num: 1, minRadius: 10, maxRadius: World.RADIUS * 1.5, scene: 'cache', label: _('A Destroyed Village')}; } - + if(typeof $SM.get('features.location.world') == 'undefined') { $SM.set('features.location.world', true); $SM.setM('game.world', { @@ -141,37 +141,37 @@ var World = { mask: World.newMask() }); } - + // Create the World panel this.panel = $('
').attr('id', "worldPanel").addClass('location').appendTo('#outerSlider'); - + // Create the shrink wrapper var outer = $('
').attr('id', 'worldOuter').appendTo(this.panel); - + // Create the bag panel $('
').attr('id', 'bagspace-world').append($('
')).appendTo(outer); $('
').attr('id', 'backpackTitle').appendTo(outer); $('
').attr('id', 'backpackSpace').appendTo(outer); $('
').attr('id', 'healthCounter').appendTo(outer); - + Engine.updateOuterSlider(); - + // Map the ship and show compass tooltip World.ship = World.mapSearch(World.TILE.SHIP,$SM.get('game.world.map'),1); World.dir = World.compassDir(World.ship[0]); // compass tooltip text Room.compassTooltip(World.dir); - + //subscribe to stateUpdates $.Dispatch('stateUpdate').subscribe(World.handleStateUpdates); }, - + clearDungeon: function() { Engine.event('progress', 'dungeon cleared'); World.state.map[World.curPos[0]][World.curPos[1]] = World.TILE.OUTPOST; World.drawRoad(); }, - + drawRoad: function() { var findClosestRoad = function(startPos) { // We'll search in a spiral to find the closest road tile @@ -226,7 +226,7 @@ var World = { xIntersect = closestRoad[0] + xDist; yIntersect = closestRoad[1]; } - + for(var x = 0; x < Math.abs(xDist); x++) { if(World.isTerrain(World.state.map[closestRoad[0] + (xDir*x)][yIntersect])) { World.state.map[closestRoad[0] + (xDir*x)][yIntersect] = World.TILE.ROAD; @@ -239,14 +239,14 @@ var World = { } World.drawMap(); }, - + updateSupplies: function() { var supplies = $('div#bagspace-world > div'); - + if(!Path.outfit) { Path.outfit = {}; } - + // Add water var water = $('div#supply_water'); if(World.water > 0 && water.length === 0) { @@ -257,7 +257,7 @@ var World = { } else { water.remove(); } - + var total = 0; for(var k in Path.outfit) { var item = $('div#supply_' + k.replace(' ', '-'), supplies); @@ -278,18 +278,18 @@ var World = { item.remove(); } } - + // Update label var t = _('pockets'); if($SM.get('stores.rucksack', true) > 0) { t = _('rucksack'); } $('#backpackTitle').text(t); - + // Update bagspace $('#backpackSpace').text(_('free {0}/{1}', Math.floor(Path.getCapacity() - total) , Path.getCapacity())); }, - + setWater: function(w) { World.water = w; if(World.water > World.getMaxWater()) { @@ -297,7 +297,7 @@ var World = { } World.updateSupplies(); }, - + setHp: function(hp) { if(typeof hp == 'number' && !isNaN(hp)) { World.health = hp; @@ -307,35 +307,35 @@ var World = { $('#healthCounter').text(_('hp: {0}/{1}', World.health , World.getMaxHealth())); } }, - + createItemDiv: function(name, num) { var div = $('
').attr('id', 'supply_' + name.replace(' ', '-')) .addClass('supplyItem') .text(_('{0}:{1}',_(name), num)); - + return div; }, - + moveNorth: function() { Engine.log('North'); if(World.curPos[1] > 0) World.move(World.NORTH); }, - + moveSouth: function() { Engine.log('South'); if(World.curPos[1] < World.RADIUS * 2) World.move(World.SOUTH); }, - + moveWest: function() { Engine.log('West'); if(World.curPos[0] > 0) World.move(World.WEST); }, - + moveEast: function() { Engine.log('East'); if(World.curPos[0] < World.RADIUS * 2) World.move(World.EAST); }, - + move: function(direction) { var oldTile = World.state.map[World.curPos[0]][World.curPos[1]]; World.curPos[0] += direction[0]; @@ -352,7 +352,7 @@ var World = { } } }, - + keyDown: function(event) { switch(event.which) { case 38: // Up @@ -412,14 +412,14 @@ var World = { World.moveEast(); } }, - + checkDanger: function() { World.danger = typeof World.danger == 'undefined' ? false: World.danger; if(!World.danger) { if($SM.get('stores["i armour"]', true) === 0 && World.getDistance() >= 8) { World.danger = true; return true; - } + } if($SM.get('stores["s armour"]', true) === 0 && World.getDistance() >= 18) { World.danger = true; return true; @@ -436,7 +436,7 @@ var World = { } return false; }, - + useSupplies: function() { World.foodMove++; World.waterMove++; @@ -501,15 +501,15 @@ var World = { } return true; }, - + meatHeal: function() { return World.MEAT_HEAL * ($SM.hasPerk('gastronome') ? 2 : 1); }, - + medsHeal: function() { return World.MEDS_HEAL; }, - + checkFight: function() { World.fightMove = typeof World.fightMove == 'number' ? World.fightMove : 0; World.fightMove++; @@ -522,7 +522,7 @@ var World = { } } }, - + doSpace: function() { var curTile = World.state.map[World.curPos[0]][World.curPos[1]]; @@ -538,17 +538,17 @@ var World = { } } }, - + getDistance: function(from, to) { from = from || World.curPos; to = to || World.VILLAGE_POS; return Math.abs(from[0] - to[0]) + Math.abs(from[1] - to[1]); }, - + getTerrain: function() { return World.state.map[World.curPos[0]][World.curPos[1]]; }, - + narrateMove: function(oldTile, newTile) { var msg = null; switch(oldTile) { @@ -587,7 +587,7 @@ var World = { Notifications.notify(World, msg); } }, - + newMask: function() { var mask = new Array(World.RADIUS * 2 + 1); for(var i = 0; i <= World.RADIUS * 2; i++) { @@ -596,33 +596,33 @@ var World = { World.lightMap(World.RADIUS, World.RADIUS, mask); return mask; }, - + lightMap: function(x, y, mask) { var r = World.LIGHT_RADIUS; r *= $SM.hasPerk('scout') ? 2 : 1; World.uncoverMap(x, y, r, mask); return mask; }, - + uncoverMap: function(x, y, r, mask) { mask[x][y] = true; for(var i = -r; i <= r; i++) { for(var j = -r + Math.abs(i); j <= r - Math.abs(i); j++) { - if(y + j >= 0 && y + j <= World.RADIUS * 2 && - x + i <= World.RADIUS * 2 && + if(y + j >= 0 && y + j <= World.RADIUS * 2 && + x + i <= World.RADIUS * 2 && x + i >= 0) { mask[x+i][y+j] = true; } } } }, - + applyMap: function() { var x = Math.floor(Math.random() * (World.RADIUS * 2) + 1); var y = Math.floor(Math.random() * (World.RADIUS * 2) + 1); World.uncoverMap(x, y, 5, $SM.get('game.world.mask')); }, - + generateMap: function() { var map = new Array(World.RADIUS * 2 + 1); for(var i = 0; i <= World.RADIUS * 2; i++) { @@ -647,11 +647,11 @@ var World = { x = World.RADIUS - r; y = World.RADIUS + (7 * r) - t; } - + map[x][y] = World.chooseTile(x, y, map); } } - + // Place landmarks for(var k in World.LANDMARKS) { var landmark = World.LANDMARKS[k]; @@ -659,10 +659,10 @@ var World = { var pos = World.placeLandmark(landmark.minRadius, landmark.maxRadius, k, map); } } - + return map; }, - + mapSearch: function(target,map,required){ var max = World.LANDMARKS[target].num; if(!max){ @@ -681,7 +681,7 @@ var World = { // items are listed as they appear in the map, tl-br // each item has relative coordinates and a compass-type direction targets[index] = { - x : i - World.RADIUS, + x : i - World.RADIUS, y : j - World.RADIUS, } index++; @@ -694,7 +694,7 @@ var World = { } return targets; }, - + compassDir: function(pos){ var dir = ''; var horz = pos.x < 0 ? 'west' : 'east'; @@ -708,9 +708,9 @@ var World = { } return dir; }, - + placeLandmark: function(minRadius, maxRadius, landmark, map) { - + var x = World.RADIUS, y = World.RADIUS; while(!World.isTerrain(map[x][y])) { var r = Math.floor(Math.random() * (maxRadius - minRadius)) + minRadius; @@ -728,20 +728,20 @@ var World = { map[x][y] = landmark; return [x, y]; }, - + isTerrain: function(tile) { return tile == World.TILE.FOREST || tile == World.TILE.FIELD || tile == World.TILE.BARRENS; }, - + chooseTile: function(x, y, map) { - + var adjacent = [ y > 0 ? map[x][y-1] : null, y < World.RADIUS * 2 ? map[x][y+1] : null, x < World.RADIUS * 2 ? map[x+1][y] : null, x > 0 ? map[x-1][y] : null ]; - + var chances = {}; var nonSticky = 1; for(var i in adjacent) { @@ -764,7 +764,7 @@ var World = { chances[tile] = cur; } } - + var list = []; for(var t in chances) { list.push(chances[t] + '' + t); @@ -774,7 +774,7 @@ var World = { var n2 = parseFloat(b.substring(0, b.length - 1)); return n2 - n1; }); - + var c = 0; var r = Math.random(); for(var i in list) { @@ -784,14 +784,14 @@ var World = { return prob.charAt(prob.length - 1); } } - + return World.TILE.BARRENS; }, - + markVisited: function(x, y) { World.state.map[x][y] = World.state.map[x][y] + '!'; }, - + drawMap: function() { var map = $('#map'); if(map.length === 0) { @@ -840,7 +840,7 @@ var World = { } map.html(mapString); }, - + die: function() { if(!World.dead) { World.dead = true; @@ -858,8 +858,8 @@ var World = { Engine.activeModule = Room; $('div.headerButton').removeClass('selected'); Room.tab.addClass('selected'); - Engine.setTimeout(function(){ - Room.onArrival(); + Engine.setTimeout(function(){ + Room.onArrival(); $('#outerSlider').animate({opacity:'1'}, 600, 'linear'); Button.cooldown($('#embarkButton')); Engine.keyLock = false; @@ -868,7 +868,7 @@ var World = { }); } }, - + goHome: function() { // Home safe! Commit the changes. $SM.setM('game.world', World.state); @@ -889,31 +889,29 @@ var World = { Engine.event('progress', 'ship'); } World.state = null; - - // Clear the embark cooldown - var btn = Button.clearCooldown($('#embarkButton')); + if(Path.outfit['cured meat'] > 0) { - Button.setDisabled(btn, false); + Button.setDisabled($('#embarkButton'), false); } - + for(var k in Path.outfit) { $SM.add('stores["'+k+'"]', Path.outfit[k]); if(World.leaveItAtHome(k)) { Path.outfit[k] = 0; } } - + $('#outerSlider').animate({left: '0px'}, 300); Engine.activeModule = Path; Path.onArrival(); Engine.restoreNavigation = true; }, - + leaveItAtHome: function(thing) { return thing != 'cured meat' && thing != 'bullets' && thing != 'energy cell' && thing != 'charm' && thing != 'medicine' && typeof World.Weapons[thing] == 'undefined' && typeof Room.Craftables[thing] == 'undefined'; }, - + getMaxHealth: function() { if($SM.get('stores["s armour"]', true) > 0) { return World.BASE_HEALTH + 35; @@ -924,14 +922,14 @@ var World = { } return World.BASE_HEALTH; }, - + getHitChance: function() { if($SM.hasPerk('precise')) { return World.BASE_HIT_CHANCE + 0.1; } return World.BASE_HIT_CHANCE; }, - + getMaxWater: function() { if($SM.get('stores["water tank"]', true) > 0) { return World.BASE_WATER + 50; @@ -942,23 +940,25 @@ var World = { } return World.BASE_WATER; }, - + outpostUsed: function(x, y) { x = typeof x == 'number' ? x : World.curPos[0]; y = typeof y == 'number' ? y : World.curPos[1]; var used = World.usedOutposts[x + ',' + y]; return typeof used != 'undefined' && used === true; }, - + useOutpost: function() { Notifications.notify(null, _('water replenished')); World.setWater(World.getMaxWater()); // Mark this outpost as used World.usedOutposts[World.curPos[0] + ',' + World.curPos[1]] = true; }, - + onArrival: function() { Engine.tabNavigation = false; + // Clear the embark cooldown + Button.clearCooldown($('#embarkButton')); Engine.keyLock = false; // Explore in a temporary world-state. We'll commit the changes if you return home safe. World.state = $.extend(true, {}, $SM.get('game.world')); @@ -977,16 +977,16 @@ var World = { World.updateSupplies(); $('#bagspace-world').width($('#map').width()); }, - + setTitle: function() { document.title = _('A Barren World'); }, - + copyPos: function(pos) { return [pos[0], pos[1]]; }, - + handleStateUpdates: function(e){ - + } };