Expansion (#707)

This additional content explores the aftermath of an event in The Ensign. A large dungeon, The Executioner, reveals the fate of one of the Wanderers’ most powerful weapons though multiple wings, each ending with a unique boss encounter. The fabricator allows the player to craft powerful items out of alien alloy, introducing new combat mechanics.
This commit is contained in:
Michael Townsend
2023-06-09 12:38:16 -04:00
committed by GitHub
parent 431b72fab8
commit 7ee96fd108
18 changed files with 5123 additions and 2038 deletions
+14 -1
View File
@@ -52,6 +52,14 @@ div#stores {
border: 1px solid #EEE; border: 1px solid #EEE;
} }
div#blueprints:before {
background: #272823;
}
div#blueprints {
border: 1px solid #EEE;
}
div#weapons:before { div#weapons:before {
background: #272823; background: #272823;
} }
@@ -173,10 +181,15 @@ body.noMask #buttons > .button {
color: black; color: black;
} }
.endGame { .endGame, .outro {
color:#272823; color:#272823;
} }
#wait-btn {
border-color: black;
color: black;
}
#theEnd { #theEnd {
color: #272823; color: #272823;
} }
+36
View File
@@ -0,0 +1,36 @@
div#fabricateButtons {
position: relative;
top: 5px;
left: 0;
}
div#fabricateButtons::before {
content: attr(data-legend);
position: relative;
top: -5px;
}
div#blueprints::before {
content: attr(data-legend);
position: absolute;
top: -13px;
background-color: white;
}
div#blueprints {
position: absolute;
top: 0px;
right: 237px;
border: 1px solid black;
cursor: default;
padding: 5px 10px;
width: 200px;
}
div.blueprintRow {
position: relative;
}
div.blueprintRow .row_key {
float: none;
}
+51 -4
View File
@@ -46,9 +46,9 @@ div#wrapper {
} }
div#saveNotify { div#saveNotify {
position: absolute; position: fixed;
top: 20px; top: 10px;
right: 0px; right: 20px;
background: white; background: white;
opacity: 0; opacity: 0;
} }
@@ -147,7 +147,7 @@ span.customSelectOptions {
} }
div.headerButton { div.headerButton {
font-size: 18px; font-size: 17px;
cursor: pointer; cursor: pointer;
float: left; float: left;
border-left: 1px solid black; border-left: 1px solid black;
@@ -561,6 +561,53 @@ body.noMask #description {
margin-left: -50%; margin-left: -50%;
} }
.fighter.shield > .label::before {
content: '(';
position: absolute;
left: -8px;
}
.fighter.shield > .label::after {
content: ')';
position: absolute;
right: -8px;
}
.fighter.energised > .label {
font-size: 2em;
font-style: bold;
}
.fighter.meditation > .label {
font-size: 1.5em;
opacity: 0.3;
}
@keyframes exploding {
0% { transform: translate(0, 0); }
25% { transform: translate(-10px, 0); }
75% { transform: translate(10px, 0); }
100% { transform: translate(0, 0); }
}
.fighter.exploding > .label {
animation: exploding 200ms linear infinite;
}
.fighter.venomous > .label {
font-size: 1.5em;
font-style: bold;
}
.fighter.enraged > .label {
font-size: 1.5em;
font-style: italic;
}
.fighter.boost > .label {
font-style: italic;
}
#description .bullet { #description .bullet {
padding: 0px 20px 0px 20px; padding: 0px 20px 0px 20px;
bottom: 25px; bottom: 25px;
+20
View File
@@ -137,6 +137,19 @@
padding-top:10%; padding-top:10%;
} }
.outroContainer {
padding-top:10%;
width: 800px;
margin: 0 auto;
}
.outro {
font-size: 1.5rem;
color: #FFF;
opacity: 0;
margin-bottom: 40px;
}
.endGame { .endGame {
font-size:48px; font-size:48px;
color:#FFFFFF; color:#FFFFFF;
@@ -152,3 +165,10 @@
.endGameOption:hover { .endGameOption:hover {
text-decoration: underline; text-decoration: underline;
} }
#wait-btn {
border-color: #fff;
color: #fff;
margin: 0 auto;
opacity: 0;
}
+3
View File
@@ -60,6 +60,7 @@
<script src="script/path.js"></script> <script src="script/path.js"></script>
<script src="script/ship.js"></script> <script src="script/ship.js"></script>
<script src="script/space.js"></script> <script src="script/space.js"></script>
<script src="script/fabricator.js"></script>
<script src="script/prestige.js"></script> <script src="script/prestige.js"></script>
<script src="script/scoring.js"></script> <script src="script/scoring.js"></script>
<!-- Event modules --> <!-- Event modules -->
@@ -69,6 +70,7 @@
<script src="script/events/encounters.js"></script> <script src="script/events/encounters.js"></script>
<script src="script/events/setpieces.js"></script> <script src="script/events/setpieces.js"></script>
<script src="script/events/marketing.js"></script> <script src="script/events/marketing.js"></script>
<script src="script/events/executioner.js"></script>
<script type='text/javascript'> <script type='text/javascript'>
var oldIE = false; var oldIE = false;
@@ -84,6 +86,7 @@
<link rel="stylesheet" type="text/css" href="css/world.css" /> <link rel="stylesheet" type="text/css" href="css/world.css" />
<link rel="stylesheet" type="text/css" href="css/ship.css" /> <link rel="stylesheet" type="text/css" href="css/ship.css" />
<link rel="stylesheet" type="text/css" href="css/space.css" /> <link rel="stylesheet" type="text/css" href="css/space.css" />
<link rel="stylesheet" type="text/css" href="css/fabricator.css" />
<script src="script/localization.js"></script> <script src="script/localization.js"></script>
<!-- Google tag (gtag.js) --> <!-- Google tag (gtag.js) -->
+5 -1
View File
@@ -20,7 +20,8 @@ var Button = {
}) })
.data("handler", typeof options.click == 'function' ? options.click : function() { Engine.log("click"); }) .data("handler", typeof options.click == 'function' ? options.click : function() { Engine.log("click"); })
.data("remaining", 0) .data("remaining", 0)
.data("cooldown", typeof options.cooldown == 'number' ? options.cooldown : 0); .data("cooldown", typeof options.cooldown == 'number' ? options.cooldown : 0)
.data('boosted', options.boosted ?? (() => false));
el.append($("<div>").addClass('cooldown')); el.append($("<div>").addClass('cooldown'));
@@ -68,6 +69,9 @@ var Button = {
cooldown: function(btn, option) { cooldown: function(btn, option) {
var cd = btn.data("cooldown"); var cd = btn.data("cooldown");
if (btn.data('boosted')()) {
cd /= 2;
}
var id = 'cooldown.'+ btn.attr('id'); var id = 'cooldown.'+ btn.attr('id');
if(cd > 0) { if(cd > 0) {
if(typeof option == 'number') { if(typeof option == 'number') {
+28 -26
View File
@@ -75,7 +75,7 @@
debug: false, debug: false,
log: false, log: false,
dropbox: false, dropbox: false,
doubleTime: true doubleTime: false
}, },
init: function(options) { init: function(options) {
@@ -227,6 +227,9 @@
if($SM.get('stores.compass', true) > 0) { if($SM.get('stores.compass', true) > 0) {
Path.init(); Path.init();
} }
if ($SM.get('features.location.fabricator')) {
Fabricator.init();
}
if($SM.get('features.location.spaceShip')) { if($SM.get('features.location.spaceShip')) {
Ship.init(); Ship.init();
} }
@@ -596,7 +599,10 @@
activeModule: null, activeModule: null,
travelTo: function(module) { travelTo: function(module) {
if(Engine.activeModule != module) { if(Engine.activeModule == module) {
return;
}
var currentIndex = Engine.activeModule ? $('.location').index(Engine.activeModule.panel) : 1; var currentIndex = Engine.activeModule ? $('.location').index(Engine.activeModule.panel) : 1;
$('div.headerButton').removeClass('selected'); $('div.headerButton').removeClass('selected');
module.tab.addClass('selected'); module.tab.addClass('selected');
@@ -612,22 +618,21 @@
stores.animate({right: -(panelIndex * 700) + 'px'}, 300 * diff); stores.animate({right: -(panelIndex * 700) + 'px'}, 300 * diff);
} }
if(Engine.activeModule == Room || Engine.activeModule == Path) { if(Engine.activeModule == Room || Engine.activeModule == Path || Engine.activeModule == Fabricator) {
// Don't fade out the weapons if we're switching to a module // Don't fade out the weapons if we're switching to a module
// where we're going to keep showing them anyway. // where we're going to keep showing them anyway.
if (module != Room && module != Path) { if (module != Room && module != Path && module != Fabricator) {
$('div#weapons').animate({opacity: 0}, 300); $('div#weapons').animate({opacity: 0}, 300);
} }
} }
if(module == Room || module == Path) { if(module == Room || module == Path || module == Fabricator) {
$('div#weapons').animate({opacity: 1}, 300); $('div#weapons').animate({opacity: 1}, 300);
} }
Engine.activeModule = module; Engine.activeModule = module;
module.onArrival(diff); module.onArrival(diff);
Notifications.printQueue(module); Notifications.printQueue(module);
}
}, },
/* Move the stores panel beneath top_container (or to top: 0px if top_container /* Move the stores panel beneath top_container (or to top: 0px if top_container
@@ -651,8 +656,7 @@
else { else {
stores.animate({ stores.animate({
top: top_container.height() + 26 + 'px' top: top_container.height() + 26 + 'px'
}, }, {
{
queue: false, queue: false,
duration: 300 * transition_diff duration: 300 * transition_diff
}); });
@@ -703,28 +707,25 @@
switch(e.which) { switch(e.which) {
case 38: // Up case 38: // Up
case 87: case 87:
if(Engine.activeModule == Outside || Engine.activeModule == Path) {
Engine.activeModule.scrollSidebar('up');
}
Engine.log('up'); Engine.log('up');
break; break;
case 40: // Down case 40: // Down
case 83: case 83:
if (Engine.activeModule == Outside || Engine.activeModule == Path) {
Engine.activeModule.scrollSidebar('down');
}
Engine.log('down'); Engine.log('down');
break; break;
case 37: // Left case 37: // Left
case 65: case 65:
if (Engine.tabNavigation) { if (Engine.tabNavigation) {
if(Engine.activeModule == Ship && Path.tab) if (Engine.activeModule == Ship && Fabricator.tab) {
Engine.travelTo(Fabricator);
}
else if ((Engine.activeModule == Ship || Engine.activeModule == Fabricator) && Path.tab) {
Engine.travelTo(Path); Engine.travelTo(Path);
}
else if (Engine.activeModule == Path && Outside.tab) { else if (Engine.activeModule == Path && Outside.tab) {
Engine.activeModule.scrollSidebar('left', true);
Engine.travelTo(Outside); Engine.travelTo(Outside);
}else if(Engine.activeModule == Outside && Room.tab){ }
Engine.activeModule.scrollSidebar('left', true); else if (Engine.activeModule == Outside && Room.tab) {
Engine.travelTo(Room); Engine.travelTo(Room);
} }
} }
@@ -733,13 +734,16 @@
case 39: // Right case 39: // Right
case 68: case 68:
if (Engine.tabNavigation){ if (Engine.tabNavigation){
if(Engine.activeModule == Room && Outside.tab) if (Engine.activeModule == Room && Outside.tab) {
Engine.travelTo(Outside); Engine.travelTo(Outside);
}
else if (Engine.activeModule == Outside && Path.tab){ else if (Engine.activeModule == Outside && Path.tab){
Engine.activeModule.scrollSidebar('right', true);
Engine.travelTo(Path); Engine.travelTo(Path);
}else if(Engine.activeModule == Path && Ship.tab){ }
Engine.activeModule.scrollSidebar('right', true); else if(Engine.activeModule == Path && Fabricator.tab) {
Engine.travelTo(Fabricator);
}
else if ((Engine.activeModule == Path || Engine.activeModule == Fabricator) && Ship.tab){
Engine.travelTo(Ship); Engine.travelTo(Ship);
} }
} }
@@ -910,11 +914,9 @@ function inView(dir, elem){
} }
function scrollByX(elem, x){ function setYPosition(elem, y) {
var elTop = parseInt( elem.css('top'), 10 ); var elTop = parseInt( elem.css('top'), 10 );
elem.css( 'top', ( elTop + x ) + "px" ); elem.css('top', `${y}px`);
} }
+320 -42
View File
@@ -8,8 +8,18 @@ var Events = {
_FIGHT_SPEED: 100, _FIGHT_SPEED: 100,
_EAT_COOLDOWN: 5, _EAT_COOLDOWN: 5,
_MEDS_COOLDOWN: 7, _MEDS_COOLDOWN: 7,
_HYPO_COOLDOWN: 7,
_SHIELD_COOLDOWN: 10,
_STIM_COOLDOWN: 10,
_LEAVE_COOLDOWN: 1, _LEAVE_COOLDOWN: 1,
STUN_DURATION: 4000, STUN_DURATION: 4000,
ENERGISE_MULTIPLIER: 4,
EXPLOSION_DURATION: 3000,
ENRAGE_DURATION: 4000,
MEDITATE_DURATION: 5000,
BOOST_DURATION: 3000,
BOOST_DAMAGE: 10,
DOT_TICK: 1000,
BLINK_INTERVAL: false, BLINK_INTERVAL: false,
init: function(options) { init: function(options) {
this.options = $.extend( this.options = $.extend(
@@ -133,11 +143,59 @@ var Events = {
if((Path.outfit['medicine'] || 0) !== 0) { if((Path.outfit['medicine'] || 0) !== 0) {
Events.createUseMedsButton().appendTo(healBtns); Events.createUseMedsButton().appendTo(healBtns);
} }
if((Path.outfit['hypo'] || 0) !== 0) {
Events.createUseHypoButton().appendTo(healBtns);
}
if ((Path.outfit['stim'] ?? 0) > 0) {
Events.createStimButton().appendTo(healBtns);
}
if($SM.get('stores["kinetic armour"]', true) > 0) {
Events.createShieldButton().appendTo(healBtns);
}
$('<div>').addClass('clear').appendTo(healBtns); $('<div>').addClass('clear').appendTo(healBtns);
Events.setHeal(healBtns); Events.setHeal(healBtns);
// Set up the enemy attack timer // Set up the enemy attack timers
Events._enemyAttackTimer = Engine.setInterval(Events.enemyAttack, scene.attackDelay * 1000); Events.startEnemyAttacks();
Events._specialTimers = (scene.specials ?? []).map(s => Engine.setInterval(
() => {
const enemy = $('#enemy');
const text = s.action(enemy);
Events.updateFighterDiv(enemy);
if (text) {
Events.drawFloatText(text, $('.hp', enemy))
}
},
s.delay * 1000
));
},
startEnemyAttacks: (delay) => {
clearInterval(Events._enemyAttackTimer);
const scene = Events.activeEvent().scenes[Events.activeScene];
Events._enemyAttackTimer = Engine.setInterval(Events.enemyAttack, (delay ?? scene.attackDelay) * 1000);
},
setStatus: (fighter, status) => {
fighter.data('status', status);
if (status === 'enraged' && fighter.attr('id') === 'enemy') {
Events.startEnemyAttacks(0.5);
setTimeout(() => {
fighter.data('status', 'none');
Events.startEnemyAttacks();
}, Events.ENRAGE_DURATION);
}
if (status === 'meditation') {
Events._meditateDmg = 0;
setTimeout(() => {
fighter.data('status', 'none');
}, Events.MEDITATE_DURATION);
}
if (status === 'boost') {
setTimeout(() => {
fighter.data('status', 'none');
}, Events.BOOST_DURATION);
}
}, },
setPause: function(btn, state){ setPause: function(btn, state){
@@ -261,6 +319,43 @@ var Events = {
return btn; return btn;
}, },
createUseHypoButton: function(cooldown) {
if (cooldown == null) {
cooldown = Events._HYPO_COOLDOWN;
}
var btn = new Button.Button({
id: 'hypo',
text: _('use hypo'),
cooldown: cooldown,
click: Events.useHypo,
cost: { 'hypo': 1 }
});
if((Path.outfit['hypo'] ?? 0) > 0) {
Button.setDisabled(btn, true);
}
return btn;
},
createShieldButton: function() {
var btn = new Button.Button({
id: 'shld',
text: _('shield'),
cooldown: Events._SHIELD_COOLDOWN,
click: Events.useShield
});
return btn;
},
createStimButton: () => new Button.Button({
id: 'use-stim',
text: _('boost'),
cooldown: Events._STIM_COOLDOWN,
click: Events.useStim
}),
createAttackButton: function(weaponName) { createAttackButton: function(weaponName) {
var weapon = World.Weapons[weaponName]; var weapon = World.Weapons[weaponName];
var cd = weapon.cooldown; var cd = weapon.cooldown;
@@ -270,10 +365,11 @@ var Events = {
} }
} }
var btn = new Button.Button({ var btn = new Button.Button({
id: 'attack_' + weaponName.replace(' ', '-'), id: 'attack_' + weaponName.replace(/ /g, '-'),
text: weapon.verb, text: weapon.verb,
cooldown: cd, cooldown: cd,
click: Events.useWeapon, click: Events.useWeapon,
boosted: () => $('#wanderer').data('status') === 'boost',
cost: weapon.cost cost: weapon.cost
}); });
if(typeof weapon.damage == 'number' && weapon.damage > 0) { if(typeof weapon.damage == 'number' && weapon.damage > 0) {
@@ -290,15 +386,16 @@ var Events = {
return btn; return btn;
}, },
drawFloatText: function(text, parent) { drawFloatText: function(text, parent, cb) {
$('<div>').text(text).addClass('damageText').appendTo(parent).animate({ $('<div>').text(text).addClass('damageText').appendTo(parent).animate({
'bottom': '50px', 'bottom': '70px',
'opacity': '0' 'opacity': '0'
}, },
300, 700,
'linear', 'linear',
function() { function() {
$(this).remove(); $(this).remove();
cb && cb();
}); });
}, },
@@ -309,7 +406,8 @@ var Events = {
healBtns = healBtns.children('.button'); healBtns = healBtns.children('.button');
var canHeal = (World.health < World.getMaxHealth()); var canHeal = (World.health < World.getMaxHealth());
healBtns.each(function(i){ healBtns.each(function(i){
Button.setDisabled($(this), !canHeal); const btn = $(this);
Button.setDisabled(btn, !canHeal && btn.attr('id') !== 'shld');
}); });
return canHeal; return canHeal;
}, },
@@ -348,9 +446,27 @@ var Events = {
AudioEngine.playSound(AudioLibrary.USE_MEDS); AudioEngine.playSound(AudioLibrary.USE_MEDS);
}, },
useHypo: btn => {
Events.doHeal('hypo', World.hypoHeal(), btn);
AudioEngine.playSound(AudioLibrary.USE_MEDS);
},
useShield: btn => {
const player = $('#wanderer');
player.data('status', 'shield');
Events.updateFighterDiv(player);
},
useStim: btn => {
const player = $('#wanderer');
player.data('status', 'boost');
Events.dotDamage(player, Events.BOOST_DAMAGE);
Events.updateFighterDiv(player);
},
useWeapon: function(btn) { useWeapon: function(btn) {
if(Events.activeEvent()) { if(Events.activeEvent()) {
var weaponName = btn.attr('id').substring(7).replace('-', ' '); var weaponName = btn.attr('id').substring(7).replace(/-/g, ' ');
var weapon = World.Weapons[weaponName]; var weapon = World.Weapons[weaponName];
if(weapon.type == 'unarmed') { if(weapon.type == 'unarmed') {
if(!$SM.get('character.punches')) $SM.set('character.punches', 0); if(!$SM.get('character.punches')) $SM.set('character.punches', 0);
@@ -436,29 +552,103 @@ var Events = {
} }
attackFn($('#wanderer'), dmg, function() { attackFn($('#wanderer'), dmg, function() {
if($('#enemy').data('hp') <= 0 && !Events.won) { const enemy = $('#enemy');
const enemyHp = enemy.data('hp');
const scene = Events.activeEvent().scenes[Events.activeScene];
const atHealth = scene.atHealth ?? {};
const explosion = scene.explosion;
for (const [k, action] of Object.entries(atHealth)) {
const hpThreshold = Number(k);
if (enemyHp <= hpThreshold && enemyHp + dmg > hpThreshold) {
action(enemy);
}
}
if(enemyHp <= 0 && !Events.won) {
// Success! // Success!
if (explosion) {
Events.explode(enemy, $('#wanderer'), explosion);
}
else {
Events.winFight(); Events.winFight();
} }
}
}); });
} }
}, },
damage: function(fighter, enemy, dmg, type) { explode: (enemy, player, dmg) => {
Events.clearTimeouts();
enemy.addClass('exploding');
setTimeout(() => {
enemy.removeClass('exploding');
$('.label', enemy).text('*');
Events.damage(enemy, player, dmg, 'ranged', () => {
if (!Events.checkPlayerDeath()) {
Events.winFight();
}
});
}, Events.EXPLOSION_DURATION);
},
dotDamage: (target, dmg) => {
const hp = Math.max(0, target.data('hp') - dmg);
target.data('hp', hp);
if(target.attr('id') == 'wanderer') {
World.setHp(hp);
Events.setHeal();
Events.checkPlayerDeath();
}
else if(hp <= 0 && !Events.won) {
Events.winFight();
}
Events.updateFighterDiv(target);
Events.drawFloatText(`-${dmg}`, $('.hp', target));
},
damage: function(fighter, enemy, dmg, type, cb) {
var enemyHp = enemy.data('hp'); var enemyHp = enemy.data('hp');
const maxHp = enemy.data('maxHp');
var msg = ""; var msg = "";
const shielded = enemy.data('status') === 'shield';
const energised = fighter.data('status') === 'energised';
const venomous = fighter.data('status') === 'venomous';
const meditating = enemy.data('status') === 'meditation';
if(typeof dmg == 'number') { if(typeof dmg == 'number') {
if(dmg < 0) { if(dmg < 0) {
msg = _('miss'); msg = _('miss');
dmg = 0; dmg = 0;
} else { } else {
msg = '-' + dmg; if (energised) {
enemyHp = ((enemyHp - dmg) < 0) ? 0 : (enemyHp - dmg); dmg *= this.ENERGISE_MULTIPLIER;
}
if (meditating) {
Events._meditateDmg = (Events._meditateDmg ?? 0) + dmg;
msg = dmg;
}
else {
msg = (shielded ? '+' : '-') + dmg;
enemyHp = Math.min(maxHp, Math.max(0, enemyHp + (shielded ? dmg : -dmg)));
enemy.data('hp', enemyHp); enemy.data('hp', enemyHp);
if(fighter.attr('id') == 'enemy') { if(fighter.attr('id') == 'enemy') {
World.setHp(enemyHp); World.setHp(enemyHp);
Events.setHeal(); Events.setHeal();
} }
}
if (venomous && !shielded) {
Events._dotTimer = setInterval(() => {
Events.dotDamage(enemy, Math.floor(dmg / 2));
}, Events.DOT_TICK);
}
if (shielded) {
// shields break in one hit
enemy.data('status', 'none');
}
Events.updateFighterDiv(enemy); Events.updateFighterDiv(enemy);
// play variation audio for weapon type // play variation audio for weapon type
@@ -478,11 +668,18 @@ var Events = {
} else { } else {
if(dmg == 'stun') { if(dmg == 'stun') {
msg = _('stunned'); msg = _('stunned');
enemy.data('stunned', Events.STUN_DURATION); enemy.data('stunned', true);
setTimeout(() => enemy.data('stunned', false), Events.STUN_DURATION);
} }
} }
Events.drawFloatText(msg, $('.hp', enemy)); if (energised || venomous) {
// attack buffs only applies to one hit
fighter.data('status', 'none');
Events.updateFighterDiv(fighter);
}
Events.drawFloatText(msg, $('.hp', enemy), cb);
}, },
animateMelee: function(fighter, dmg, callback) { animateMelee: function(fighter, dmg, callback) {
@@ -533,32 +730,47 @@ var Events = {
// Events.togglePause($('#pause'),'auto'); // Events.togglePause($('#pause'),'auto');
var scene = Events.activeEvent().scenes[Events.activeScene]; var scene = Events.activeEvent().scenes[Events.activeScene];
const enemy = $('#enemy');
const stunned = enemy.data('stunned');
const meditating = enemy.data('status') === 'meditation';
if(!$('#enemy').data('stunned')) { if(!stunned && !meditating) {
var toHit = scene.hit; var toHit = scene.hit;
toHit *= $SM.hasPerk('evasive') ? 0.8 : 1; toHit *= $SM.hasPerk('evasive') ? 0.8 : 1;
var dmg = -1; var dmg = -1;
if(Math.random() <= toHit) { if ((Events._meditateDmg ?? 0) > 0) {
dmg = Events._meditateDmg;
Events._meditateDmg = 0;
}
else if(Math.random() <= toHit) {
dmg = scene.damage; dmg = scene.damage;
} }
var attackFn = scene.ranged ? Events.animateRanged : Events.animateMelee; var attackFn = scene.ranged ? Events.animateRanged : Events.animateMelee;
attackFn($('#enemy'), dmg, function() { attackFn($('#enemy'), dmg, Events.checkPlayerDeath);
}
},
checkPlayerDeath: () => {
if($('#wanderer').data('hp') <= 0) { if($('#wanderer').data('hp') <= 0) {
// Failure! Events.clearTimeouts();
clearTimeout(Events._enemyAttackTimer);
Events.endEvent(); Events.endEvent();
World.die(); World.die();
AudioEngine.playSound(AudioLibrary.LOSE_FIGHT); return true;
}
});
} }
return false;
},
clearTimeouts: () => {
clearTimeout(Events._enemyAttackTimer);
Events._specialTimers.forEach(clearTimeout);
clearTimeout(Events._dotTimer);
}, },
endFight: function() { endFight: function() {
Events.fought = true; Events.fought = true;
clearTimeout(Events._enemyAttackTimer); Events.clearTimeouts();
Events.removePause($('#pause'), 'end'); Events.removePause($('#pause'), 'end');
}, },
@@ -605,6 +817,9 @@ var Events = {
if((Path.outfit['medicine'] || 0) !== 0) { if((Path.outfit['medicine'] || 0) !== 0) {
Events.createUseMedsButton(0).appendTo(healBtns); Events.createUseMedsButton(0).appendTo(healBtns);
} }
if (Path.outfit['hypo'] ?? 0 > 0) {
Events.createUseHypoButton(0).appendTo(healBtns);
}
$('<div>').addClass('clear').appendTo(healBtns); $('<div>').addClass('clear').appendTo(healBtns);
Events.setHeal(healBtns); Events.setHeal(healBtns);
} }
@@ -623,7 +838,7 @@ var Events = {
}, },
drawDrop:function(btn) { drawDrop:function(btn) {
var name = btn.attr('id').substring(5).replace('-', ' '); var name = btn.attr('id').substring(5).replace(/-/g, ' ');
var needsAppend = false; var needsAppend = false;
var weight = Path.getWeight(name); var weight = Path.getWeight(name);
var freeSpace = Path.getFreeSpace(); var freeSpace = Path.getFreeSpace();
@@ -647,7 +862,7 @@ var Events = {
numToDrop = Path.outfit[k]; numToDrop = Path.outfit[k];
} }
if(numToDrop > 0) { if(numToDrop > 0) {
var dropRow = $('<div>').attr('id', 'drop_' + k.replace(' ', '-')) var dropRow = $('<div>').attr('id', 'drop_' + k.replace(/ /g, '-'))
.text(_(k) + ' x' + numToDrop) .text(_(k) + ' x' + numToDrop)
.data('thing', k) .data('thing', k)
.data('num', numToDrop) .data('num', numToDrop)
@@ -679,7 +894,7 @@ var Events = {
}, },
drawLootRow: function(name, num){ drawLootRow: function(name, num){
var id = name.replace(' ', '-'); var id = name.replace(/ /g, '-');
var lootRow = $('<div>').attr('id','loot_' + id).data('item', name).addClass('lootRow'); var lootRow = $('<div>').attr('id','loot_' + id).data('item', name).addClass('lootRow');
var take = new Button.Button({ var take = new Button.Button({
id: 'take_' + id, id: 'take_' + id,
@@ -790,7 +1005,7 @@ var Events = {
var btn = $(this); var btn = $(this);
var target = btn.closest('.button'); var target = btn.closest('.button');
var thing = btn.data('thing'); var thing = btn.data('thing');
var id = 'take_' + thing.replace(' ', '-'); var id = 'take_' + thing.replace(/ /g, '-');
var num = btn.data('num'); var num = btn.data('num');
var lootButtons = $('#lootButtons'); var lootButtons = $('#lootButtons');
Engine.log('dropping ' + num + ' ' + thing); Engine.log('dropping ' + num + ' ' + thing);
@@ -810,7 +1025,7 @@ var Events = {
}, },
getLoot: function(btn, stateSkipButtonSet) { getLoot: function(btn, stateSkipButtonSet) {
var name = btn.attr('id').substring(5).replace('-', ' '); var name = btn.attr('id').substring(5).replace(/-/g, ' ');
if(btn.data('numLeft') > 0) { if(btn.data('numLeft') > 0) {
var skipButtonSet = stateSkipButtonSet || false; var skipButtonSet = stateSkipButtonSet || false;
var weight = Path.getWeight(name); var weight = Path.getWeight(name);
@@ -867,13 +1082,21 @@ var Events = {
}, },
createFighterDiv: function(chara, hp, maxhp) { createFighterDiv: function(chara, hp, maxhp) {
var fighter = $('<div>').addClass('fighter').text(_(chara)).data('hp', hp).data('maxHp', maxhp).data('refname',chara); var fighter = $('<div>')
.addClass('fighter')
.data('hp', hp)
.data('maxHp', maxhp)
.data('refname',chara);
$('<div>').addClass('label').text(_(chara)).appendTo(fighter);
$('<div>').addClass('hp').text(hp+'/'+maxhp).appendTo(fighter); $('<div>').addClass('hp').text(hp+'/'+maxhp).appendTo(fighter);
return fighter; return fighter;
}, },
updateFighterDiv: function(fighter) { updateFighterDiv: function(fighter) {
$('.hp', fighter).text(fighter.data('hp') + '/' + fighter.data('maxHp')); $('.hp', fighter).text(fighter.data('hp') + '/' + fighter.data('maxHp'));
const status = fighter.data('status');
const hasStatus = status && status !== 'none';
fighter.attr('class', `fighter${hasStatus ? ` ${status}` : ''}`);
}, },
startStory: function(scene) { startStory: function(scene) {
@@ -912,10 +1135,16 @@ var Events = {
var btnsList = []; var btnsList = [];
for(var id in scene.buttons) { for(var id in scene.buttons) {
var info = scene.buttons[id]; var info = scene.buttons[id];
const cost = {
...info.cost
};
if (Path.outfit && Path.outfit['glowstone']) {
delete cost.torch;
}
var b = new Button.Button({ var b = new Button.Button({
id: id, id,
text: info.text, text: info.text,
cost: info.cost, cost,
click: Events.buttonClick, click: Events.buttonClick,
cooldown: info.cooldown cooldown: info.cooldown
}).appendTo(btns); }).appendTo(btns);
@@ -932,6 +1161,17 @@ var Events = {
return (btnsList.length == 1) ? btnsList[0] : false; return (btnsList.length == 1) ? btnsList[0] : false;
}, },
getQuantity: function(store) {
if (store === 'water') {
return World.water;
}
if (store === 'hp') {
return World.health;
}
var num = Engine.activeModule == World ? Path.outfit[store] : $SM.get('stores["'+store+'"]', true);
return isNaN(num) || num < 0 ? 0 : num;
},
updateButtons: function() { updateButtons: function() {
var btns = Events.activeEvent().scenes[Events.activeScene].buttons; var btns = Events.activeEvent().scenes[Events.activeScene].buttons;
for(var bId in btns) { for(var bId in btns) {
@@ -940,11 +1180,16 @@ var Events = {
if(typeof b.available == 'function' && !b.available()) { if(typeof b.available == 'function' && !b.available()) {
Button.setDisabled(btnEl, true); Button.setDisabled(btnEl, true);
} else if(b.cost) { } else if(b.cost) {
const cost = {
...b.cost
};
if (Path.outfit && Path.outfit['glowstone']) {
delete cost.torch;
}
var disabled = false; var disabled = false;
for(var store in b.cost) { for(var store in cost) {
var num = Engine.activeModule == World ? Path.outfit[store] : $SM.get('stores["'+store+'"]', true); var num = Events.getQuantity(store);
if(typeof num != 'number') num = 0; if(num < cost[store]) {
if(num < b.cost[store]) {
// Too expensive // Too expensive
disabled = true; disabled = true;
break; break;
@@ -960,14 +1205,27 @@ var Events = {
// Cost // Cost
var costMod = {}; var costMod = {};
if(info.cost) { if(info.cost) {
for(var store in info.cost) { const cost = {
var num = Engine.activeModule == World ? Path.outfit[store] : $SM.get('stores["'+store+'"]', true); ...info.cost
if(typeof num != 'number') num = 0; };
if(num < info.cost[store]) { if (Path.outfit && Path.outfit['glowstone']) {
delete cost.torch;
}
for(var store in cost) {
var num = Events.getQuantity(store);
if(num < cost[store]) {
// Too expensive // Too expensive
return; return;
} }
costMod[store] = -info.cost[store]; if (store === 'water') {
World.setWater(World.water - cost[store]);
}
else if (store === 'hp') {
World.setHp(World.hp - cost[store]);
}
else {
costMod[store] = -cost[store];
}
} }
if(Engine.activeModule == World) { if(Engine.activeModule == World) {
for(var k in costMod) { for(var k in costMod) {
@@ -1002,6 +1260,14 @@ var Events = {
if (info.link) { if (info.link) {
Events.endEvent(); Events.endEvent();
window.open(info.link); window.open(info.link);
return;
}
// Next Event
if (info.nextEvent) {
const eventData = Events.Setpieces[info.nextEvent] || Events.Executioner[info.nextEvent];
Events.switchEvent(eventData);
return;
} }
// Next Scene // Next Scene
@@ -1104,8 +1370,21 @@ var Events = {
return Events.activeEvent().eventPanel; return Events.activeEvent().eventPanel;
}, },
switchEvent: event => {
if (!event) {
return;
}
Events.eventPanel().remove();
Events.activeEvent().eventPanel = null;
Events.eventStack.shift();
Events.startEvent(event);
},
startEvent: function(event, options) { startEvent: function(event, options) {
if(event) { if(!event) {
return;
}
event.audio && AudioEngine.playEventMusic(event.audio);
Engine.event('game event', 'event'); Engine.event('game event', 'event');
Engine.keyLock = true; Engine.keyLock = true;
Engine.tabNavigation = false; Engine.tabNavigation = false;
@@ -1125,7 +1404,6 @@ var Events = {
if (currentSceneInformation.blink) { if (currentSceneInformation.blink) {
Events.blinkTitle(); Events.blinkTitle();
} }
}
}, },
scheduleNextEvent: function(scale) { scheduleNextEvent: function(scale) {
File diff suppressed because it is too large Load Diff
+244
View File
@@ -0,0 +1,244 @@
/**
* Module that registers the fabricator functionality
*/
const Fabricator = {
_STORES_OFFSET: 0,
name: _('Fabricator'),
Craftables: {
'energy blade': {
name: _('energy blade'),
type: 'weapon',
buildMsg: _("the blade hums, charged particles sparking and fizzing."),
cost: () => ({
'alien alloy': 1
})
},
'fluid recycler': {
name: _('fluid recycler'),
type: 'upgrade',
maximum: 1,
buildMsg: _('water out, water in. waste not, want not.'),
cost: () => ({
'alien alloy': 2
})
},
'cargo drone': {
name: _('cargo drone'),
type: 'upgrade',
maximum: 1,
buildMsg: _('the workhorse of the wanderer fleet.'),
cost: () => ({
'alien alloy': 2
})
},
'kinetic armour': {
name: _('kinetic armour'),
type: 'upgrade',
maximum: 1,
blueprintRequired: true,
buildMsg: _('wanderer soldiers succeed by subverting the enemy\'s rage.'),
cost: () => ({
'alien alloy': 2
})
},
'disruptor': {
name: _('disruptor'),
type: 'weapon',
blueprintRequired: true,
buildMsg: _("somtimes it is best not to fight."),
cost: () => ({
'alien alloy': 1
})
},
'hypo': {
name: _('hypo'),
type: 'tool',
blueprintRequired: true,
buildMsg: _('a handful of hypos. life in a vial.'),
cost: () => ({
'alien alloy': 1
}),
quantity: 5
},
'stim': {
name: _('stim'),
type: 'tool',
blueprintRequired: true,
buildMsg: _('sometimes it is best to fight without restraint.'),
cost: () => ({
'alien alloy': 1
})
},
'plasma rifle': {
name: _('plasma rifle'),
type: 'weapon',
blueprintRequired: true,
buildMsg: _("the peak of wanderer weapons technology, sleek and deadly."),
cost: () => ({
'alien alloy': 1
})
},
'glowstone': {
name: _('glow stone'),
type: 'tool',
blueprintRequired: true,
buildMsg: _('a smooth, perfect sphere. its light is inextinguishable.'),
cost: () => ({
'alien alloy': 1
})
}
},
init: () => {
if (!$SM.get('features.location.fabricator')) {
$SM.set('features.location.fabricator', true);
}
// Create the Fabricator tab
Fabricator.tab = Header.addLocation(_("A Whirring Fabricator"), "fabricator", Fabricator, 'ship');
// Create the Fabricator panel
Fabricator.panel = $('<div>').attr('id', "fabricatorPanel")
.addClass('location');
if (Ship.panel) {
Fabricator.panel.insertBefore(Ship.panel);
}
else {
Fabricator.panel.appendTo('div#locationSlider');
}
$.Dispatch('stateUpdate').subscribe(() => {
Fabricator.updateBuildButtons();
Fabricator.updateBlueprints();
});
Engine.updateSlider();
Fabricator.updateBuildButtons();
},
onArrival: transition_diff => {
Fabricator.setTitle();
Fabricator.updateBlueprints(true);
if(!$SM.get('game.fabricator.seen')) {
Notifications.notify(Fabricator, _('the familiar hum of wanderer machinery coming to life. finally, real tools.'));
$SM.set('game.fabricator.seen', true);
}
AudioEngine.playBackgroundMusic(AudioLibrary.MUSIC_SHIP);
Engine.moveStoresView(null, transition_diff);
},
setTitle: () => {
if(Engine.activeModule == Fabricator) {
document.title = _("A Whirring Fabricator");
}
},
updateBuildButtons: () => {
let section = $('#fabricateButtons');
let needsAppend = false;
if (section.length === 0) {
section = $('<div>').attr({ 'id': 'fabricateButtons', 'data-legend': _('fabricate:') }).css('opacity', 0);
needsAppend = true;
}
for (const [ key, value ] of Object.entries(Fabricator.Craftables)) {
const max = $SM.num(key, value) + 1 > value.maximum;
if (!value.button) {
if (Fabricator.canFabricate(key)) {
const name = _(value.name) + ((value.quantity ?? 1) > 1 ? ` (x${value.quantity})` : '');
value.button = new Button.Button({
id: 'fabricate_' + key,
cost: value.cost(),
text: name,
click: Fabricator.fabricate,
width: '150px',
ttPos: section.children().length > 10 ? 'top right' : 'bottom right'
}).css('opacity', 0).attr('fabricateThing', key).appendTo(section).animate({ opacity: 1 }, 300, 'linear');
}
} else {
// refresh the tooltip
const costTooltip = $('.tooltip', value.button);
costTooltip.empty();
const cost = value.cost();
for (const [ resource, num ] of Object.entries(cost)) {
$("<div>").addClass('row_key').text(_(resource)).appendTo(costTooltip);
$("<div>").addClass('row_val').text(num).appendTo(costTooltip);
}
if (max && value.maxMsg && !value.button.hasClass('disabled')) {
Notifications.notify(Fabricator, value.maxMsg);
}
}
if (max) {
Button.setDisabled(value.button, true);
} else {
Button.setDisabled(value.button, false);
}
}
if (needsAppend && section.children().length > 0) {
section.appendTo(Fabricator.panel).animate({ opacity: 1 }, 300, 'linear');
}
},
updateBlueprints: ignoreStores => {
if(!$SM.get('character.blueprints')) {
return;
}
let blueprints = $('#blueprints');
let needsAppend = false;
if(blueprints.length === 0) {
needsAppend = true;
blueprints = $('<div>').attr({'id': 'blueprints', 'data-legend': _('blueprints')});
}
for (const k in $SM.get('character.blueprints')) {
const id = 'blueprint_' + k.replace(/ /g, '-');
let r = $('#' + id);
if($SM.get(`character.blueprints["${k}"]`) && r.length === 0) {
r = $('<div>').attr('id', id).addClass('blueprintRow').appendTo(blueprints);
$('<div>').addClass('row_key').text(_(k)).appendTo(r);
}
}
if(needsAppend && blueprints.children().length > 0) {
blueprints.prependTo(Fabricator.panel);
}
},
canFabricate: itemKey =>
!Fabricator.Craftables[itemKey].blueprintRequired ||
$SM.get(`character.blueprints['${itemKey}']`),
fabricate: button => {
const thing = $(button).attr('fabricateThing');
const craftable = Fabricator.Craftables[thing];
const numThings = Math.min(0, $SM.get(`stores['${thing}']`, true));
if (craftable.maximum <= numThings) {
return;
}
const storeMod = {};
const cost = craftable.cost();
for (const [ key, value ] of Object.entries(cost)) {
const have = $SM.get(`stores['${key}']`, true);
if (have < value) {
Notifications.notify(Fabricator, _(`not enough ${key}`));
return false;
} else {
storeMod[key] = have - value;
}
}
$SM.setM('stores', storeMod);
$SM.add(`stores['${thing}']`, craftable.quantity ?? 1);
Notifications.notify(Fabricator, craftable.buildMsg);
AudioEngine.playSound(AudioLibrary.CRAFT);
}
};
+9 -3
View File
@@ -16,13 +16,19 @@ var Header = {
return $('div#header div.headerButton').length > 1; return $('div#header div.headerButton').length > 1;
}, },
addLocation: function(text, id, module) { addLocation: function(text, id, module, before) {
return $('<div>').attr('id', "location_" + id) const toAdd = $('<div>').attr('id', "location_" + id)
.addClass('headerButton') .addClass('headerButton')
.text(text).click(function() { .text(text).click(function() {
if(Header.canTravel()) { if(Header.canTravel()) {
Engine.travelTo(module); Engine.travelTo(module);
} }
}).appendTo($('div#header')); });
if (before && $(`#location_${before}`).length > 0) {
return toAdd.insertBefore(`#location_${before}`);
}
return toAdd.appendTo($('div#header'));
} }
}; };
-32
View File
@@ -661,37 +661,5 @@ var Outside = {
Outside.updateWorkersView(); Outside.updateWorkersView();
Outside.updateVillageIncome(); Outside.updateVillageIncome();
} }
},
scrollSidebar: function(direction, reset) {
if( typeof reset != "undefined" ){
$('#village').css('top', '0px');
$('#storesContainer').css('top', '224px');
Outside._STORES_OFFSET = 0;
return false;
}
var momentum = 10;
// If they hit up, we scroll everything down
if( direction == 'up' )
momentum = momentum * -1;
/* Let's stop scrolling if the top or bottom bound is in the viewport, based on direction */
if( direction == 'down' && inView( direction, $('#village') ) ){
return false;
}else if( direction == 'up' && inView( direction, $('#storesContainer') ) ){
return false;
}
scrollByX( $('#village'), momentum );
scrollByX( $('#storesContainer'), momentum );
Outside._STORES_OFFSET += momentum;
} }
}; };
+10 -35
View File
@@ -10,7 +10,8 @@ var Path = {
'bullets': 0.1, 'bullets': 0.1,
'energy cell': 0.2, 'energy cell': 0.2,
'laser rifle': 5, 'laser rifle': 5,
'bolas': 0.5 'plasma rifle': 5,
'bolas': 0.5,
}, },
name: 'Path', name: 'Path',
@@ -67,7 +68,9 @@ var Path = {
}, },
getCapacity: function() { getCapacity: function() {
if($SM.get('stores.convoy', true) > 0) { if($SM.get('stores["cargo drone"]', true) > 0) {
return Path.DEFAULT_BAG_SPACE + 100;
} else if($SM.get('stores.convoy', true) > 0) {
return Path.DEFAULT_BAG_SPACE + 60; return Path.DEFAULT_BAG_SPACE + 60;
} else if($SM.get('stores.wagon', true) > 0) { } else if($SM.get('stores.wagon', true) > 0) {
return Path.DEFAULT_BAG_SPACE + 30; return Path.DEFAULT_BAG_SPACE + 30;
@@ -98,7 +101,7 @@ var Path = {
var needsAppend = false; var needsAppend = false;
if(perks.length === 0) { if(perks.length === 0) {
needsAppend = true; needsAppend = true;
perks = $('<div>').attr({'id': 'perks', 'data-legend': _('perks:')}); perks = $('<div>').attr({'id': 'perks', 'data-legend': _('perks')});
} }
for(var k in $SM.get('character.perks')) { for(var k in $SM.get('character.perks')) {
var id = 'perk_' + k.replace(' ', '-'); var id = 'perk_' + k.replace(' ', '-');
@@ -129,7 +132,9 @@ var Path = {
// Add the armour row // Add the armour row
var armour = _("none"); var armour = _("none");
if($SM.get('stores["s armour"]', true) > 0) if($SM.get('stores["kinetic armour"]', true) > 0)
armour = _("kinetic");
else if($SM.get('stores["s armour"]', true) > 0)
armour = _("steel"); armour = _("steel");
else if($SM.get('stores["i armour"]', true) > 0) else if($SM.get('stores["i armour"]', true) > 0)
armour = _("iron"); armour = _("iron");
@@ -169,7 +174,7 @@ var Path = {
'bayonet': {type: 'weapon' }, 'bayonet': {type: 'weapon' },
'charm': {type: 'tool'}, 'charm': {type: 'tool'},
'medicine': {type: 'tool', desc: _('restores') + ' ' + World.MEDS_HEAL + ' ' + _('hp') } 'medicine': {type: 'tool', desc: _('restores') + ' ' + World.MEDS_HEAL + ' ' + _('hp') }
}, Room.Craftables); }, Room.Craftables, Fabricator.Craftables);
for(var k in carryable) { for(var k in carryable) {
var lk = _(k); var lk = _(k);
@@ -329,35 +334,5 @@ var Path = {
} else if(e.category == 'income' && Engine.activeModule == Path){ } else if(e.category == 'income' && Engine.activeModule == Path){
Path.updateOutfitting(); Path.updateOutfitting();
} }
},
scrollSidebar: function(direction, reset){
if( typeof reset != "undefined" ){
$('#perks').css('top', '0px');
$('#storesContainer').css('top', '206px');
Path._STORES_OFFSET = 0;
return;
}
var momentum = 10;
if( direction == 'up' )
momentum = momentum * -1;
if( direction == 'down' && inView( direction, $('#perks') ) ){
return false;
}else if( direction == 'up' && inView( direction, $('#storesContainer') ) ){
return false;
}
scrollByX( $('#perks'), momentum );
scrollByX( $('#storesContainer'), momentum );
Path._STORES_OFFSET += momentum;
} }
}; };
+13 -9
View File
@@ -826,15 +826,19 @@ var Room = {
} }
for (var k in $SM.get('stores')) { for (var k in $SM.get('stores')) {
var type = null; if (k.indexOf('blueprint') > 0) {
if (Room.Craftables[k]) { // don't show blueprints
type = Room.Craftables[k].type; continue;
} else if (Room.TradeGoods[k]) {
type = Room.TradeGoods[k].type;
} else if (Room.MiscItems[k]) {
type = Room.MiscItems[k].type;
} }
const good =
Room.Craftables[k] ||
Room.TradeGoods[k] ||
Room.TradeGoods[k] ||
Room.MiscItems[k] ||
Fabricator.Craftables[k];
const type = good ? good.type : null;
var location; var location;
switch (type) { switch (type) {
case 'upgrade': case 'upgrade':
@@ -854,7 +858,7 @@ var Room = {
break; break;
} }
var id = "row_" + k.replace(' ', '-'); var id = "row_" + k.replace(/ /g, '-');
var row = $('div#' + id, location); var row = $('div#' + id, location);
var num = $SM.get('stores["' + k + '"]'); var num = $SM.get('stores["' + k + '"]');
@@ -1139,7 +1143,7 @@ var Room = {
if (Room.craftUnlocked(k)) { if (Room.craftUnlocked(k)) {
var loc = Room.needsWorkshop(craftable.type) ? craftSection : buildSection; var loc = Room.needsWorkshop(craftable.type) ? craftSection : buildSection;
craftable.button = new Button.Button({ craftable.button = new Button.Button({
id: 'build_' + k, id: 'build_' + k.replace(/ /g, '-'),
cost: craftable.cost(), cost: craftable.cost(),
text: _(k), text: _(k),
click: Room.build, click: Room.build,
+1
View File
@@ -19,6 +19,7 @@ var Score = {
} }
fullScore = fullScore + $SM.get('stores["alien alloy"]', true) * 10; fullScore = fullScore + $SM.get('stores["alien alloy"]', true) * 10;
fullScore = fullScore + $SM.get('stores["fleet beacon"]', true) * 500;
fullScore = fullScore + Ship.getMaxHull() * 50; fullScore = fullScore + Ship.getMaxHull() * 50;
return Math.floor(fullScore); return Math.floor(fullScore);
}, },
+74 -10
View File
@@ -437,7 +437,81 @@ var Space = {
Engine.GAME_OVER = true; Engine.GAME_OVER = true;
Score.save(); Score.save();
Prestige.save(); Prestige.save();
$('#starsContainer').remove();
$('#content, #notifications').remove();
Space.showExpansionEnding().then(() => {
Space.showEndingOptions();
Engine.options = {};
Engine.deleteSave(true);
});
}
});
}, 2000);
});
}, 2000);
});
},
showExpansionEnding: () => {
return new Promise((resolve) => {
if (!$SM.get('stores["fleet beacon"]')) {
resolve();
return;
}
const c = $('<div>')
.addClass('outroContainer')
.appendTo('body');
setTimeout(() => {
$('<div>')
.addClass('outro')
.html('the beacon pulses gently as the ship glides through space.<br>coordinates are locked. nothing to do but wait.')
.appendTo(c)
.animate({ opacity: 1}, 500);
}, 2000);
setTimeout(() => {
$('<div>')
.addClass('outro')
.html('the beacon glows a solid blue, and then goes dim. the ship slows.<br>gradually, the vast wanderer homefleet comes into view.<br>massive worldships drift unnaturally through clouds of debris, scarred and dead.')
.appendTo(c)
.animate({ opacity: 1}, 500);
}, 7000);
setTimeout(() => {
$('<div>')
.addClass('outro')
.text('the air is running out.')
.appendTo(c)
.animate({ opacity: 1}, 500);
}, 14000);
setTimeout(() => {
$('<div>')
.addClass('outro')
.text('the capsule is cold.')
.appendTo(c)
.animate({ opacity: 1}, 500);
}, 17000);
setTimeout(() => {
Button.Button({
id: 'wait-btn',
text: _('wait'),
click: (btn) => {
btn.addClass('disabled');
c.animate({ opacity: 0 }, 5000, 'linear', () => {
c.remove();
setTimeout(resolve, 3000);
})
}
}).animate({ opacity: 1 }, 500).appendTo(c);
}, 19500)
});
},
showEndingOptions: () => {
$('<center>') $('<center>')
.addClass('centerCont') .addClass('centerCont')
.appendTo('body'); .appendTo('body');
@@ -457,8 +531,6 @@ var Space = {
.appendTo('.centerCont'); .appendTo('.centerCont');
$('<br />') $('<br />')
.appendTo('.centerCont'); .appendTo('.centerCont');
$('#starsContainer').remove();
$('#content, #notifications').remove();
$('<span>') $('<span>')
.addClass('endGame endGameOption') .addClass('endGame endGameOption')
.text(_('restart.')) .text(_('restart.'))
@@ -492,14 +564,6 @@ var Space = {
.click(function() { window.open('https://play.google.com/store/apps/details?id=com.yourcompany.adarkroom'); }) .click(function() { window.open('https://play.google.com/store/apps/details?id=com.yourcompany.adarkroom'); })
.appendTo('.centerCont') .appendTo('.centerCont')
.animate({opacity:1},1500); .animate({opacity:1},1500);
Engine.options = {};
Engine.deleteSave(true);
}
});
}, 2000);
});
}, 2000);
});
}, },
keyDown: function(event) { keyDown: function(event) {
+88 -12
View File
@@ -19,7 +19,8 @@ var World = {
BOREHOLE: 'B', BOREHOLE: 'B',
BATTLEFIELD: 'F', BATTLEFIELD: 'F',
SWAMP: 'M', SWAMP: 'M',
CACHE: 'U' CACHE: 'U',
EXECUTIONER: 'X'
}, },
TILE_PROBS: {}, TILE_PROBS: {},
LANDMARKS: {}, LANDMARKS: {},
@@ -29,11 +30,12 @@ var World = {
MOVES_PER_FOOD: 2, MOVES_PER_FOOD: 2,
MOVES_PER_WATER: 1, MOVES_PER_WATER: 1,
DEATH_COOLDOWN: 120, DEATH_COOLDOWN: 120,
FIGHT_CHANCE: 0.20, FIGHT_CHANCE: 0, //0.20, TODO UNCOMMENT THIS
BASE_HEALTH: 10, BASE_HEALTH: 10,
BASE_HIT_CHANCE: 0.8, BASE_HIT_CHANCE: 0.8,
MEAT_HEAL: 8, MEAT_HEAL: 8,
MEDS_HEAL: 20, MEDS_HEAL: 20,
HYPO_HEAL: 30,
FIGHT_DELAY: 3, // At least three moves between fights FIGHT_DELAY: 3, // At least three moves between fights
NORTH: [ 0, -1], NORTH: [ 0, -1],
SOUTH: [ 0, 1], SOUTH: [ 0, 1],
@@ -98,6 +100,25 @@ var World = {
damage: 'stun', damage: 'stun',
cooldown: 15, cooldown: 15,
cost: { 'bolas': 1 } cost: { 'bolas': 1 }
},
'plasma rifle': {
verb: _('disintigrate'),
type: 'ranged',
damage: 12,
cooldown: 1,
cost: { 'energy cell': 1 }
},
'energy blade': {
verb: _('slice'),
type: 'melee',
damage: 10,
cooldown: 2
},
'disruptor': {
verb: _('stun'),
type: 'ranged',
damage: 'stun',
cooldown: 15
} }
}, },
@@ -127,6 +148,7 @@ var World = {
World.LANDMARKS[World.TILE.BOREHOLE] = { num: 10, minRadius: 15, maxRadius: World.RADIUS * 1.5, scene: 'borehole', label: _('A&nbsp;Borehole')}; World.LANDMARKS[World.TILE.BOREHOLE] = { num: 10, minRadius: 15, maxRadius: World.RADIUS * 1.5, scene: 'borehole', label: _('A&nbsp;Borehole')};
World.LANDMARKS[World.TILE.BATTLEFIELD] = { num: 5, minRadius: 18, maxRadius: World.RADIUS * 1.5, scene: 'battlefield', label: _('A&nbsp;Battlefield')}; World.LANDMARKS[World.TILE.BATTLEFIELD] = { num: 5, minRadius: 18, maxRadius: World.RADIUS * 1.5, scene: 'battlefield', label: _('A&nbsp;Battlefield')};
World.LANDMARKS[World.TILE.SWAMP] = { num: 1, minRadius: 15, maxRadius: World.RADIUS * 1.5, scene: 'swamp', label: _('A&nbsp;Murky&nbsp;Swamp')}; World.LANDMARKS[World.TILE.SWAMP] = { num: 1, minRadius: 15, maxRadius: World.RADIUS * 1.5, scene: 'swamp', label: _('A&nbsp;Murky&nbsp;Swamp')};
World.LANDMARKS[World.TILE.EXECUTIONER] = { num: 1, minRadius: 28, maxRadius: 28, scene: 'executioner', 'label': _('A&nbsp;Ravaged&nbsp;Battleship')};
// Only add the cache if there is prestige data // Only add the cache if there is prestige data
if($SM.get('previous.stores')) { if($SM.get('previous.stores')) {
@@ -135,11 +157,22 @@ var World = {
if(typeof $SM.get('features.location.world') == 'undefined') { if(typeof $SM.get('features.location.world') == 'undefined') {
$SM.set('features.location.world', true); $SM.set('features.location.world', true);
$SM.set('features.executioner', true);
$SM.setM('game.world', { $SM.setM('game.world', {
map: World.generateMap(), map: World.generateMap(),
mask: World.newMask() mask: World.newMask()
}); });
} }
else if (!$SM.get('features.executioner')) {
// Place the Executioner in previously generated maps that don't have it
const map = $SM.get('game.world.map');
const landmark = World.LANDMARKS[World.TILE.EXECUTIONER]
for(let l = 0; l < landmark.num; l++) {
World.placeLandmark(landmark.minRadius, landmark.maxRadius, World.TILE.EXECUTIONER, map);
}
$SM.set('game.world.map', map);
$SM.set('features.executioner', true);
}
// Create the World panel // Create the World panel
this.panel = $('<div>').attr('id', "worldPanel").addClass('location').appendTo('#outerSlider'); this.panel = $('<div>').attr('id', "worldPanel").addClass('location').appendTo('#outerSlider');
@@ -517,6 +550,8 @@ var World = {
return World.MEDS_HEAL; return World.MEDS_HEAL;
}, },
hypoHeal: () => World.HYPO_HEAL,
checkFight: function() { checkFight: function() {
World.fightMove = typeof World.fightMove == 'number' ? World.fightMove : 0; World.fightMove = typeof World.fightMove == 'number' ? World.fightMove : 0;
World.fightMove++; World.fightMove++;
@@ -535,10 +570,13 @@ var World = {
if(curTile == World.TILE.VILLAGE) { if(curTile == World.TILE.VILLAGE) {
World.goHome(); World.goHome();
} else if(curTile === World.TILE.EXECUTIONER) {
const scene = World.state.executioner ? 'executioner-antechamber' : 'executioner-intro';
const sceneData = Events.Executioner[scene];
Events.startEvent(sceneData);
} else if(typeof World.LANDMARKS[curTile] != 'undefined') { } else if(typeof World.LANDMARKS[curTile] != 'undefined') {
if(curTile != World.TILE.OUTPOST || !World.outpostUsed()) { if(curTile != World.TILE.OUTPOST || !World.outpostUsed()) {
Events.startEvent(Events.Setpieces[World.LANDMARKS[curTile].scene]); Events.startEvent(Events.Setpieces[World.LANDMARKS[curTile].scene]);
AudioEngine.playEventMusic(Events.Setpieces[World.LANDMARKS[curTile].scene].audio);
} }
} else { } else {
if(World.useSupplies()) { if(World.useSupplies()) {
@@ -928,18 +966,19 @@ var World = {
Ship.init(); Ship.init();
Engine.event('progress', 'ship'); Engine.event('progress', 'ship');
} }
if (World.state.executioner && !$SM.get('features.location.fabricator')) {
Fabricator.init();
Notifications.notify(null, _('builder knows the strange device when she sees it. takes it for herself real quick. doesnt ask where it came from.'));
Engine.event('progress', 'fabricator');
}
World.redeemBlueprints();
World.state = null; World.state = null;
if(Path.outfit['cured meat'] > 0) { if(Path.outfit['cured meat'] > 0) {
Button.setDisabled($('#embarkButton'), false); Button.setDisabled($('#embarkButton'), false);
} }
for(var k in Path.outfit) { World.returnOutfit();
$SM.add('stores["'+k+'"]', Path.outfit[k]);
if(World.leaveItAtHome(k)) {
Path.outfit[k] = 0;
}
}
$('#outerSlider').animate({left: '0px'}, 300); $('#outerSlider').animate({left: '0px'}, 300);
Engine.activeModule = Path; Engine.activeModule = Path;
@@ -947,13 +986,47 @@ var World = {
Engine.restoreNavigation = true; Engine.restoreNavigation = true;
}, },
redeemBlueprints: () => {
let redeemed = false;
const redeem = (blueprint, item) => {
if (Path.outfit[blueprint]) {
$SM.set(`character.blueprints['${item}']`, true);
delete Path.outfit[blueprint];
redeemed = true;
}
};
redeem('hypo blueprint', 'hypo');
redeem('kinetic armour blueprint', 'kinetic armour');
redeem('disruptor blueprint', 'disruptor');
redeem('plasma rifle blueprint', 'plasma rifle');
redeem('stim blueprint', 'stim');
redeem('glowstone blueprint', 'glowstone');
if (redeemed) {
Notifications.notify(null, 'blueprints feed into the fabricator data port. possibilities grow.');
}
},
returnOutfit: () => {
for(var k in Path.outfit) {
$SM.add('stores["'+k+'"]', Path.outfit[k]);
if(World.leaveItAtHome(k)) {
Path.outfit[k] = 0;
}
}
},
leaveItAtHome: function(thing) { leaveItAtHome: function(thing) {
return thing != 'cured meat' && thing != 'bullets' && thing != 'energy cell' && thing != 'charm' && thing != 'medicine' && return thing != 'cured meat' && thing != 'bullets' && thing != 'energy cell' &&
thing != 'charm' && thing != 'medicine' && thing != 'stim' && thing != 'hypo' &&
typeof World.Weapons[thing] == 'undefined' && typeof Room.Craftables[thing] == 'undefined'; typeof World.Weapons[thing] == 'undefined' && typeof Room.Craftables[thing] == 'undefined';
}, },
getMaxHealth: function() { getMaxHealth: function() {
if($SM.get('stores["s armour"]', true) > 0) { if($SM.get('stores["kinetic armour"]', true) > 0) {
return World.BASE_HEALTH + 75;
} else if($SM.get('stores["s armour"]', true) > 0) {
return World.BASE_HEALTH + 35; return World.BASE_HEALTH + 35;
} else if($SM.get('stores["i armour"]', true) > 0) { } else if($SM.get('stores["i armour"]', true) > 0) {
return World.BASE_HEALTH + 15; return World.BASE_HEALTH + 15;
@@ -971,7 +1044,10 @@ var World = {
}, },
getMaxWater: function() { getMaxWater: function() {
if($SM.get('stores["water tank"]', true) > 0) {
if($SM.get('stores["fluid recycler"]', true) > 0) {
return World.BASE_WATER + 100;
} else if($SM.get('stores["water tank"]', true) > 0) {
return World.BASE_WATER + 50; return World.BASE_WATER + 50;
} else if($SM.get('stores.cask', true) > 0) { } else if($SM.get('stores.cask', true) > 0) {
return World.BASE_WATER + 20; return World.BASE_WATER + 20;