2017-07-30 23:21:57 +00:00
|
|
|
<html>
|
|
|
|
<head>
|
|
|
|
<title>Voyage of the Indefatigable</title>
|
|
|
|
<style>
|
|
|
|
html { background:#000; }
|
|
|
|
body { background: url("assets/stars.png"); }
|
|
|
|
div#outer-darkness { display:flex;justify-content:center;align-items:center; }
|
|
|
|
div#content-wrapper {
|
|
|
|
display:inline-block;
|
|
|
|
position:relative;
|
|
|
|
min-width:1280px; max-width:1280px;
|
|
|
|
min-height:720px; max-height:720px;
|
|
|
|
background:rgba(255,255,255,0.2);
|
|
|
|
}
|
|
|
|
div#progress-bg {
|
|
|
|
position:absolute;top:0;left:0;width:1276;height:32;
|
|
|
|
background-image:url("assets/stars-small.png");
|
|
|
|
border: 2px solid #ffd
|
|
|
|
}
|
|
|
|
img#progress-ship { position:absolute; top:2;left:2; }
|
|
|
|
div#logbox {
|
|
|
|
position:absolute;right:4;top:40;height:668;border:2px solid black;padding: 2px;width: 320px;
|
|
|
|
word-wrap:break-word;overflow-x: hidden;overflow-y:scroll;
|
|
|
|
font-size:12px; font-family:monospace; background:rgba(0,0,0,0.8); color:#fff
|
|
|
|
}
|
|
|
|
div.crew-status { position:absolute;width:364;height:72; background:rgba(255,255,255,0.8); border: 2px solid black; border-radius:5px; }
|
|
|
|
img.crew-img { position:absolute;width:64px;height:64px;left:2;top:2;border: 2px solid black;background-color:#444; }
|
|
|
|
img.blurred { filter:blur(1px); }
|
|
|
|
div.cryo-tint { position:absolute;width:64px;height:64px;left:2;top:2;border: 2px solid black;background-color:rgba(0,255,255,0.4); }
|
|
|
|
span.crew-name { position:absolute; left:76;top:2; font-size:20px;font-weight:bold; width:200;align:right; }
|
|
|
|
span.crew-job {
|
|
|
|
position:absolute; left:76;top:24; font-size:20px;
|
|
|
|
//border-bottom: 1px dotted #000;
|
|
|
|
}
|
|
|
|
span.crew-status { position:absolute; left:76;top:46; font-size:20px; }
|
|
|
|
span.crew-name-dead { text-decoration:line-through; }
|
|
|
|
span.crew-job-dead { text-decoration:line-through; }
|
|
|
|
span.crew-status-normal { color:#0a0; }
|
|
|
|
span.crew-status-critical { font-weight:bold; color:#a00; -webkit-animation: red-blink 2s infinite; animation: red-blink 2s infinite; }
|
|
|
|
span.crew-status-dead { color:black; }
|
|
|
|
@-webkit-keyframes red-blink { 50% {color:#f00;} }
|
|
|
|
@keyframes red-blink { 50% {color:#f00;} }
|
|
|
|
select.crew-select { position:absolute; left:240; top:4; width:120px;height:38px; }
|
|
|
|
button.cryo-button { position:absolute; left:240;bottom:4; width:120px;height:22px; background:#aaf;}
|
|
|
|
div#power-emptybg {
|
|
|
|
position:absolute; left:4; bottom:4; height:46; width:930px;
|
|
|
|
border: 2px solid black;
|
|
|
|
border-top-right-radius:23px;
|
|
|
|
border-bottom-right-radius:23px;
|
|
|
|
background:#444; overflow:hidden;
|
|
|
|
}
|
|
|
|
div#power-fullbg {
|
|
|
|
position:absolute; left:-2; bottom:-2; height:46; width:930px;
|
|
|
|
border: 2px solid black;
|
|
|
|
border-top-right-radius:23px;
|
|
|
|
border-bottom-right-radius:23px;
|
|
|
|
background:#0af;
|
|
|
|
-webkit-animation: power-pulse 2s infinite; animation: power-pulse 2s infinite;
|
|
|
|
}
|
|
|
|
div#power-text {
|
|
|
|
position:absolute; left:4; bottom:4; height:46; width:930px; padding:2px;
|
|
|
|
}
|
|
|
|
span#power-left { position:absolute; left:12; font-size:40; }
|
|
|
|
span#power-used { position:absolute; right:12; font-size:40; }
|
|
|
|
@-webkit-keyframes power-pulse { 50% {background: #0ff;} }
|
|
|
|
@keyframes power-pulse { 50% {background: #0ff;} }
|
|
|
|
div.stat-display {
|
|
|
|
position:absolute;
|
|
|
|
border: 2px solid black; background:rgba(255,255,255,0.8);
|
|
|
|
text-align:right; line-height:48px; font-family:monospace; padding-right:8px; white-space:nowrap; font-size:24;
|
|
|
|
}
|
|
|
|
</style>
|
|
|
|
<script src="jquery-3.2.1.min.js"></script>
|
|
|
|
<script>
|
|
|
|
////////////////////
|
|
|
|
// GAME VARIABLES //
|
|
|
|
////////////////////
|
|
|
|
var CREW = 8;
|
|
|
|
var CREWNAMES = ["Emil Thomson", "Virgil Redd", "Tanya Baker", "Christina Nast", "Kavz Andronicus", "Gunther", "Kierkegaard", "Liege"];
|
|
|
|
var SHORTNAMES = ["Thomson", "Redd", "Baker", "Nast", "Andronicus", "Gunther", "Kierkegaard", "Liege"];
|
|
|
|
var CREWJOBS = ["Captain", "Chief Engineer", "Defense Officer", "Medical Officer", "Staff Wizard", "Quantum Engineer", "Data Scientist", "Horse"];
|
|
|
|
var POSITIONS = ["Cryo Control", "Engine Room", "Life Support", "Medical Bay", "Missile Control", "Scanner Array", "Shield Gens", "Warp Control"];
|
|
|
|
var SPECIALTIES = [[1,4], [1,5], [4,6], [2,3], [2,7], [6,7], [3,5], []];
|
|
|
|
var ISHORSE = [false, false, false, false, false, true, true, true];
|
|
|
|
var MAXPROGRESS = 100000;
|
|
|
|
var PROGBADWIDTH = 1280 - 48 - 4;
|
|
|
|
var MAXPOWER = 10000000;
|
|
|
|
var POWBARWIDTH = 930;
|
|
|
|
var GAINCORE = 300;
|
|
|
|
var COSTCRYO = 5;
|
|
|
|
var COSTLIFESUP = 30;
|
|
|
|
var COSTENGINE = 100;
|
|
|
|
var COSTSCANNER = 80;
|
|
|
|
var COSTSHIELD = 80;
|
|
|
|
var COSTMEDBAY = 15;
|
|
|
|
var COSTMISSILE = 40;
|
|
|
|
var COSTWARP = 60;
|
|
|
|
var GAINLIFESUP = 5;
|
|
|
|
var MOVEDELAY = 1500;
|
|
|
|
var CRYODELAY = 1000;
|
|
|
|
var THAWDELAY = 5000;
|
|
|
|
var CRISISMIN = 3;
|
|
|
|
var CRISISMAX = 98;
|
|
|
|
var HEALCOUNT = 150;
|
|
|
|
var MAXHEALTH = 4;
|
|
|
|
var WARPBUILDCOUNT = 50;
|
|
|
|
var WARPDECAYCOUNT = 120;
|
|
|
|
var WARPDIST = 3000;
|
|
|
|
var WARPCOST = 400000;
|
|
|
|
var WARPTIME = 1000;
|
|
|
|
var STORMLOSS = 300000;
|
|
|
|
var HOSTILELOSS = 300000;
|
|
|
|
var HOSTILEDECR = 100000;
|
|
|
|
var NUMBLACKHOLE = 1;
|
|
|
|
var NUMSPATIALFLUX = 2;
|
|
|
|
var NUMHEATWAVE = 3;
|
|
|
|
var NUMRADFIELD = 3;
|
|
|
|
var NUMSTARSTORM = 3;
|
|
|
|
var NUMHOSTILES = 2;
|
|
|
|
|
|
|
|
|
|
|
|
var gameLoopId;
|
|
|
|
var power = 10000000; // Running out of this
|
|
|
|
var progress = 0;
|
|
|
|
var currentMilestone = 0;
|
|
|
|
var frozen = [false, true, true, true, true, true, true, true];
|
|
|
|
var health = [4, 4, 4, 4, 4, 4, 4, 4];
|
|
|
|
var crisisEvents = [];
|
|
|
|
var horseRevolt = false;
|
|
|
|
|
|
|
|
var usageCryo, usageLifeSup, usageEngine, usageScanner, usageShield, usageMedbay, usageMissile, usageWarp;
|
|
|
|
var usageTotal;
|
|
|
|
var engineSpeed, scanRange, shieldStr;
|
|
|
|
var healBuildTick = 0;
|
|
|
|
var hostiles;
|
|
|
|
var warpBuildTick = 0;
|
|
|
|
var warpDecayTick = 0;
|
|
|
|
var warpPerc = 0;
|
|
|
|
|
|
|
|
///////////////
|
|
|
|
// FUNCTIONS //
|
|
|
|
///////////////
|
|
|
|
function makeCrewStatusBox(crewid) {
|
|
|
|
// Create the elements
|
|
|
|
var id = "crew-status" + crewid;
|
|
|
|
var $container = $("<div>", {id:id, "class":"crew-status"});
|
|
|
|
$container.css("left", 4);
|
|
|
|
$container.css("top", 40 + crewid * (72 + 6));
|
|
|
|
var imgSrc = "assets/crew" + (crewid+1) + ".png";
|
|
|
|
var $crewImg = $("<img>", {"class":"crew-img blurred", "src":imgSrc});
|
|
|
|
var $cryoTint = $("<div>", {"class":"cryo-tint"}).css("display", crewid == 0);
|
|
|
|
var $crewName = $("<span>", {"class":"crew-name"}).text(CREWNAMES[crewid]);
|
|
|
|
var $crewJob = $("<span>", {"class":"crew-job"})
|
|
|
|
.text(CREWJOBS[crewid]);
|
|
|
|
//.attr("title", SHORTNAMES[crewid] + " works best in " + POSITIONS[SPECIALTIES[crewid][0]] + " and " + POSITIONS[SPECIALTIES[crewid][1]]);
|
|
|
|
//if (SHORTNAMES[crewid] == "Liege") $crewJob.attr("title", "Liege is trying his best");
|
|
|
|
var $crewStat = $("<span>", {"class":"crew-status crew-status-normal"}).text("Healthy");
|
|
|
|
var $positionSelect = $("<select>", {"class":"crew-select"}).prop("disabled", true)
|
|
|
|
.append($("<option>", { value: "Cryo Control", text: "Cryo Control" }))
|
|
|
|
.append($("<option>", { value: "Engine Room", text: "Engine Room" }))
|
|
|
|
.append($("<option>", { value: "Life Support", text: "Life Support" }))
|
|
|
|
.append($("<option>", { value: "Medical Bay", text: "Medical Bay" }))
|
|
|
|
.append($("<option>", { value: "Missile Control", text: "Missile Control" }))
|
|
|
|
.append($("<option>", { value: "Scanner Array", text: "Scanner Array" }))
|
|
|
|
.append($("<option>", { value: "Shield Gens", text: "Shield Gens" }))
|
|
|
|
.append($("<option>", { value: "Warp Control", text: "Warp Control" }))
|
|
|
|
.change( function(){ positionChanged(crewid); } );
|
|
|
|
/*SPECIALTIES[crewid].forEach(function(n){
|
|
|
|
$positionSelect.find("option").eq(n).append("**")
|
|
|
|
});*/
|
|
|
|
var $cryoButton = $("<button>", {"class": "cryo-button", text:"Unfreeze"})
|
|
|
|
.click( function() { cryoToggle(crewid); } );
|
|
|
|
if (!frozen[crewid]) {
|
|
|
|
$crewImg.removeClass("blurred");
|
|
|
|
$cryoTint.css("display", "none");
|
|
|
|
$cryoButton.text("Cryofreeze");
|
|
|
|
$positionSelect.prop("disabled", false);
|
|
|
|
}
|
|
|
|
// Append the elements
|
|
|
|
$crewImg.appendTo($container);
|
|
|
|
$cryoTint.appendTo($container);
|
|
|
|
$crewName.appendTo($container);
|
|
|
|
$crewJob.appendTo($container);
|
|
|
|
$crewStat.appendTo($container);
|
|
|
|
$positionSelect.appendTo($container);
|
|
|
|
$cryoButton.appendTo($container);
|
|
|
|
$container.appendTo("#content-wrapper");
|
|
|
|
}
|
|
|
|
|
|
|
|
function crisisSetup() {
|
|
|
|
var dist, crisis, i, j, k;
|
|
|
|
// Black hole
|
|
|
|
for (i = 0; i < NUMBLACKHOLE; i++) {
|
|
|
|
dist = Math.floor(Math.random()*(60-40+1)+40);
|
|
|
|
crisis = [dist, "Black hole", function() {
|
|
|
|
logbox('<span style="color:#a00">The <i>Indefatigable</i> has been crushed by a black hole.</span>');
|
|
|
|
for (j = 0; j < CREW; j++) {
|
|
|
|
if (health[j] >= 1) {
|
|
|
|
health[j] = 1;
|
|
|
|
injure(j);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}];
|
|
|
|
crisisEvents.push(crisis);
|
|
|
|
}
|
|
|
|
// Spatial flux
|
|
|
|
for (i = 0; i < NUMSPATIALFLUX; i++) {
|
|
|
|
dist = Math.floor(Math.random()*(80-20+1)+20);
|
|
|
|
crisis = [dist, "Spatial flux", function() {
|
|
|
|
var warpStaffed = numActiveIn("Warp Control") > 0;
|
|
|
|
if (!warpStaffed || warpPerc < 15) {
|
|
|
|
logbox('<span style="color:#a00">The <i>Indefatigable</i> was ripped apart by spatial flux.</span>')
|
|
|
|
for (j = 0; j < CREW; j++) {
|
|
|
|
if (health[j] >= 1) {
|
|
|
|
health[j] = 1;
|
|
|
|
injure(j);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
warpPerc -= 15;
|
|
|
|
for (j = 0; j < CREW; j++) {
|
|
|
|
if ($("#crew-status"+j+" .crew-select").val() == "Warp Control")
|
|
|
|
injure(j);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}];
|
|
|
|
crisisEvents.push(crisis);
|
|
|
|
}
|
|
|
|
// Heat wave
|
|
|
|
for (i = 0; i < NUMHEATWAVE; i++) {
|
|
|
|
dist = Math.floor(Math.random()*(CRISISMAX-CRISISMIN+1)+CRISISMIN);
|
|
|
|
crisis = [dist, "Heat wave", function() {
|
|
|
|
if (frozen.some(x=>x)) {
|
|
|
|
logbox('<span style="color:#a00">A heat wave disrupted the cryogenic modules.</span>');
|
|
|
|
for (j = 0; j < CREW; j++) {
|
|
|
|
if (frozen[j] && health[j] >= 1) {
|
|
|
|
health[j] = 1;
|
|
|
|
injure(j);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}];
|
|
|
|
crisisEvents.push(crisis);
|
|
|
|
}
|
|
|
|
// Radiation field
|
|
|
|
for (i = 0; i < NUMRADFIELD; i++) {
|
|
|
|
dist = Math.floor(Math.random()*(CRISISMAX-CRISISMIN+1)+CRISISMIN);
|
|
|
|
crisis = [dist, "Radiation field", function() {
|
|
|
|
var activeLS = numActiveIn("Life Support");
|
|
|
|
var hits = 3 - activeLS;
|
|
|
|
for (j = 0; j < hits; j++){
|
|
|
|
var hitArea = Math.floor(Math.random()*(POSITIONS.length-1+1)+0);
|
|
|
|
logbox('<span style="color:#a00">'+POSITIONS[hitArea]+' was hit by radiation.</span>');
|
|
|
|
for (k = 0; k < CREW; k++) {
|
|
|
|
if ($("#crew-status"+k+" .crew-select").val() == POSITIONS[hitArea] && health[k] > 0 && !frozen[k]) {
|
|
|
|
injure(k);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}];
|
|
|
|
crisisEvents.push(crisis);
|
|
|
|
}
|
|
|
|
// Stellar storm
|
|
|
|
for (i = 0; i < NUMSTARSTORM; i++) {
|
|
|
|
dist = Math.floor(Math.random()*(CRISISMAX-CRISISMIN+1)+CRISISMIN);
|
|
|
|
crisis = [dist, "Stellar storm", function() {
|
|
|
|
var powerLoss = STORMLOSS / shieldStr;
|
|
|
|
if (shieldStr > 1) {
|
|
|
|
logbox("Shields reduced storm damage to " + Math.round(100 / shieldStr) + "%.");
|
|
|
|
}
|
|
|
|
power = Math.max(0, power - powerLoss);
|
|
|
|
}];
|
|
|
|
crisisEvents.push(crisis);
|
|
|
|
}
|
|
|
|
// Hostile fighters
|
|
|
|
for (i = 0; i < NUMHOSTILES; i++) {
|
|
|
|
dist = Math.floor(Math.random()*(CRISISMAX-CRISISMIN+1)+CRISISMIN);
|
|
|
|
var id = i;
|
|
|
|
crisis = [dist, "Hostile fighters", function() {
|
|
|
|
var activeMC = numActiveIn("Missile Control");
|
|
|
|
console.log("HF id " + id);
|
|
|
|
var me = crisisEvents.filter(x => (x[1] == "Hostile fighters")).filter(x => (x[3] == id))[0];
|
|
|
|
me[4] -= activeMC * HOSTILEDECR;
|
|
|
|
if (me[4] <= 0) {
|
|
|
|
logbox("All hostile fighters destroyed.");
|
|
|
|
} else {
|
|
|
|
power = Math.max(0, power - me[4]);
|
|
|
|
logbox("Damage: " + me[4]);
|
2017-07-31 01:17:22 +00:00
|
|
|
me[0] += Math.floor(Math.random()*(8-4+1)+4);
|
2017-07-30 23:21:57 +00:00
|
|
|
if (activeMC > 0) {
|
|
|
|
logbox(activeMC + " hostile fighters destroyed. The remnants have retreated... for now.");
|
|
|
|
} else {
|
|
|
|
logbox("Automatic ship defenses have repelled the hostile fighters... for now.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, id, HOSTILELOSS];
|
|
|
|
crisisEvents.push(crisis);
|
|
|
|
}
|
|
|
|
//
|
|
|
|
}
|
|
|
|
|
|
|
|
function startGame() {
|
|
|
|
// Set up the game
|
|
|
|
$(function(){
|
|
|
|
var i;
|
|
|
|
for (i = 0; i < 8; i++)
|
|
|
|
makeCrewStatusBox(i);
|
|
|
|
updateUI();
|
|
|
|
crisisSetup();
|
|
|
|
console.log(crisisEvents.sort((a,b) => (a[0]-b[0])).map(x => (x[0] + " " + x[1])).join(" | "));
|
|
|
|
//console.log(crisisEvents.map(x => x[0]).sort((a, b) => (a - b)).join(" "));
|
|
|
|
});
|
|
|
|
// Run the game loop
|
|
|
|
setTimeout(function(){
|
|
|
|
//alert("Congratulations, brave explorer! You have been chosen "+
|
|
|
|
// "to lead an expedition.")
|
|
|
|
gameLoopId = setInterval(gameIter, 1000/30);
|
|
|
|
}, 1000);
|
|
|
|
}
|
|
|
|
|
|
|
|
function logbox(s) {
|
|
|
|
var p = "000000" + progress;
|
|
|
|
p = p.substr(p.length-6)
|
|
|
|
$("#logbox").append("[" + p + "] " + s + "<br>");
|
|
|
|
$("#logbox").scrollTop($("#logbox")[0].scrollHeight);
|
|
|
|
//$("#logbox").animate({scrollTop: $("#logbox")[0].scrollHeight}, 1000);
|
|
|
|
}
|
|
|
|
|
|
|
|
function gameIter() {
|
|
|
|
// Deduct used energy
|
|
|
|
power = Math.round(Math.max(0, power - usageTotal));
|
|
|
|
$("#power-left").text(power);
|
|
|
|
$("#power-fullbg").css("width", Math.round(930 * power / MAXPOWER));
|
|
|
|
// Progress updates
|
|
|
|
progress = Math.round(Math.min(MAXPROGRESS, progress + engineSpeed));
|
|
|
|
var progressPerc = progress / MAXPROGRESS;
|
|
|
|
var progressProp = 2 + Math.round(progressPerc * PROGBADWIDTH);
|
|
|
|
// Check what progress brings
|
|
|
|
healthCheckTick();
|
|
|
|
warpTick();
|
|
|
|
$("#stat-ratio-left").text(Math.round(10000 * (MAXPROGRESS - progress) / power));
|
|
|
|
var newMilestone = Math.floor(100*progressPerc);
|
|
|
|
if (newMilestone > currentMilestone) {
|
|
|
|
currentMilestone = newMilestone;
|
|
|
|
logbox('<span style="color:#0a0">Progress: ' + currentMilestone + '%</span>');
|
|
|
|
healthCheckMilestone();
|
|
|
|
crisisCheck();
|
|
|
|
updateUI();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update GUI
|
|
|
|
$("#progress-ship").css("left", progressProp);
|
|
|
|
$("body").css("background-position-x", -progress);
|
|
|
|
// Check if the game ended
|
|
|
|
if (currentMilestone == 100) {
|
|
|
|
logbox('<span style="color:#0a0"><b>You have reached your destination.</b></span>');
|
|
|
|
clearInterval(gameLoopId);
|
|
|
|
}
|
|
|
|
if (power <= 0) {
|
|
|
|
logbox('<span style="color:#a00">You have run out of power. Life support systems have failed.</span>');
|
|
|
|
for (var i = 0; i < CREW; i++) {
|
|
|
|
if (health[i] >= 1) {
|
|
|
|
health[i] = 1;
|
|
|
|
injure(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!health.some(x => x > 0)) {
|
|
|
|
logbox('<span style="color:#a00"><b>All crewmembers have died.</b></span>');
|
|
|
|
clearInterval(gameLoopId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function healthCheckTick() {
|
|
|
|
var i, j;
|
|
|
|
var injuredInMedbay = false;
|
|
|
|
for (i = 0; i < CREW; i++) {
|
|
|
|
if ($("#crew-status"+i+" .crew-select").val() == "Medical Bay" && health[i] < MAXHEALTH) {
|
|
|
|
injuredInMedbay = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var doctorInMedbay = false;
|
|
|
|
for (j = 0; j < CREW; j++) {
|
|
|
|
if ($("#crew-status"+j+" .crew-select").val() == "Medical Bay" && health[j] == MAXHEALTH) {
|
|
|
|
doctorInMedbay = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (injuredInMedbay && doctorInMedbay) {
|
|
|
|
healBuildTick += 1;
|
|
|
|
$("#stat-heal-tick").text(healBuildTick);
|
|
|
|
}
|
|
|
|
if (healBuildTick >= HEALCOUNT) {
|
|
|
|
healBuildTick = 0;
|
|
|
|
$("#stat-heal-tick").text(healBuildTick);
|
|
|
|
health[i] += 1;
|
|
|
|
if (health[i] == MAXHEALTH) {
|
|
|
|
$("#crew-status"+i+" .crew-status").text("Healthy");
|
|
|
|
$("#crew-status"+i+" .crew-status").removeClass("crew-status-critical");
|
|
|
|
$("#crew-status"+i+" .crew-status").addClass("crew-status-normal");
|
|
|
|
}
|
|
|
|
var injuredOut = 0, injuredIn = 0;
|
|
|
|
for (var i = 0; i < CREW; i++) {
|
|
|
|
if (health[i] < MAXHEALTH && health[i] > 0) {
|
|
|
|
if ($("#crew-status"+i+" .crew-select").val() == "Medical Bay")
|
|
|
|
injuredIn += 1;
|
|
|
|
else
|
|
|
|
injuredOut += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$("#stat-health-critical").text(injuredOut);
|
|
|
|
$("#stat-health-care").text(injuredIn);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function healthCheckMilestone() {
|
|
|
|
for (var i = 0; i < CREW; i++) {
|
|
|
|
if ($("#crew-status"+i+" .crew-select").val() != "Medical Bay" && $("#crew-status"+i+" .crew-status").text() == "Injured") {
|
|
|
|
injure(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function warpTick() {
|
|
|
|
var active = numActiveIn("Warp Control");
|
|
|
|
if (active > 0) {
|
|
|
|
warpBuildTick += active;
|
|
|
|
warpDecayTick = 0;
|
|
|
|
if (warpBuildTick >= WARPBUILDCOUNT) {
|
|
|
|
warpPerc = Math.min(100, warpPerc + 1);
|
|
|
|
warpBuildTick = 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
warpDecayTick += 1;
|
|
|
|
warpBuildTick = 0;
|
|
|
|
if (warpDecayTick >= WARPDECAYCOUNT) {
|
|
|
|
warpPerc = Math.max(0, warpPerc - 1);
|
|
|
|
warpDecayTick = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$("#warp-button").attr("disabled", warpPerc < 100);
|
|
|
|
$("#stat-warp-perc").text(warpPerc);
|
|
|
|
}
|
|
|
|
|
|
|
|
function crisisCheck() {
|
|
|
|
crisisEvents.forEach(function(c){
|
|
|
|
// Forewarning
|
|
|
|
if (c[0] > currentMilestone && c[0] - currentMilestone <= scanRange) {
|
|
|
|
logbox('<span style="color:#a00">' + c[1] + ' approaching in ' + (c[0] - currentMilestone) + '</span>');
|
|
|
|
}
|
|
|
|
// Occasion
|
|
|
|
if (c[0] == currentMilestone) {
|
|
|
|
logbox('<span style="color:#a00">' + c[1] + ' encountered!</span>');
|
|
|
|
c[2]();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
// Horse revolt
|
|
|
|
var viveLaRevolution = true;
|
|
|
|
var i;
|
|
|
|
for (i = 0; i < ISHORSE.length; i++) {
|
|
|
|
var $crew = $("#crew-status"+i);
|
|
|
|
if (ISHORSE[i] && (frozen[i] || $crew.find(".crew-status").text() != "Healthy"))
|
|
|
|
viveLaRevolution = false;
|
|
|
|
if (!ISHORSE[i] && $crew.find(".crew-status").text() != "Dead" && !frozen[i])
|
|
|
|
viveLaRevolution = false;
|
|
|
|
}
|
|
|
|
if (viveLaRevolution && !horseRevolt) {
|
|
|
|
horseRevolt = true;
|
|
|
|
logbox('<span style="color:#aa0">The horses have revolted and sabotaged the cryostasis modules,'+
|
|
|
|
' killing all of the humans. Long live the equine revolution!</span>');
|
|
|
|
for (i = 0; i < ISHORSE.length; i++) {
|
|
|
|
if (!ISHORSE[i]) {
|
|
|
|
health[i] = 1;
|
|
|
|
injure(i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function updateUI() {
|
|
|
|
var cryoControlStaffed = $(".crew-select").map(
|
|
|
|
function(){
|
|
|
|
return $(this).val() == "Cryo Control" && !$(this).attr("disabled");
|
|
|
|
}).get().some(function(x){return x;});
|
|
|
|
$(".crew-status").each(function(){
|
|
|
|
$(this).find(".cryo-button").prop("disabled",
|
|
|
|
!cryoControlStaffed ||
|
|
|
|
($(this).find("select").val() != "Cryo Control") ||
|
|
|
|
$(this).find(".cryo-button").text().endsWith("...") ||
|
|
|
|
$(this).find(".crew-status").text() == "Dead"
|
|
|
|
)
|
|
|
|
});
|
|
|
|
for (var i = 0; i < health.length; i++) {
|
|
|
|
if (health[i] <= 0) {
|
|
|
|
$("#crew-status"+i+" .crew-select").prop("disabled", true);
|
|
|
|
$("#crew-status"+i+" .cryo-button").prop("disabled", true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
updateStats();
|
|
|
|
$("#warp-button").attr("disabled", warpPerc < 100)
|
|
|
|
}
|
|
|
|
|
|
|
|
function updateStats() {
|
2017-07-30 23:35:58 +00:00
|
|
|
var numActive = 0;
|
|
|
|
var numFrozen = 0;
|
|
|
|
for (var i = 0; i < CREW; i++) {
|
|
|
|
if (health[i] > 0 && frozen[i])
|
|
|
|
numFrozen += 1;
|
|
|
|
if (health[i] > 0 && !frozen[i])
|
|
|
|
numActive += 1;
|
|
|
|
}
|
2017-07-30 23:21:57 +00:00
|
|
|
$("#stat-crew-cryo").text(numFrozen);
|
|
|
|
$("#stat-crew-active").text(numActive);
|
|
|
|
|
|
|
|
var numLifeSup = numActiveIn("Life Support");
|
|
|
|
var lifeSupCost = Math.max(COSTLIFESUP - GAINLIFESUP * numLifeSup, 3 * COSTCRYO)
|
|
|
|
|
|
|
|
usageCryo = COSTCRYO * numFrozen;
|
|
|
|
usageLifeSup = lifeSupCost * numActive;
|
|
|
|
usageEngine = COSTENGINE + COSTENGINE * numActiveIn("Engine Room");
|
|
|
|
usageScanner = COSTSCANNER + COSTSCANNER * numActiveIn("Scanner Array");
|
|
|
|
usageShield = COSTSHIELD + COSTSHIELD * numActiveIn("Shield Gens");
|
|
|
|
usageMedbay = COSTMEDBAY * numActiveIn("Medical Bay");
|
|
|
|
usageMissile = COSTMISSILE * numActiveIn("Missile Control");
|
|
|
|
usageWarp = COSTWARP * numActiveIn("Warp Control");
|
|
|
|
var usageGross = usageCryo + usageLifeSup + usageEngine + usageScanner + usageShield + usageMedbay + usageMissile + usageWarp;
|
|
|
|
usageTotal = Math.max(0, usageGross - GAINCORE);
|
|
|
|
|
|
|
|
$("#stat-usage-total").text(usageGross);
|
|
|
|
$("#stat-usage-cryo").text(usageCryo);
|
|
|
|
$("#stat-usage-lifesup").text(usageLifeSup);
|
|
|
|
$("#stat-usage-engine").text(usageEngine);
|
|
|
|
$("#stat-usage-scanner").text(usageScanner);
|
|
|
|
$("#stat-usage-shield").text(usageShield);
|
|
|
|
$("#stat-usage-medbay").text(usageMedbay);
|
|
|
|
$("#stat-usage-missile").text(usageMissile);
|
|
|
|
$("#stat-usage-warp").text(usageWarp);
|
|
|
|
$("#stat-gain-core").text(GAINCORE);
|
|
|
|
|
|
|
|
engineSpeed = 1 + 2 * numActiveIn("Engine Room");
|
|
|
|
scanRange = 1 + 2 * numActiveIn("Scanner Array");
|
|
|
|
shieldStr = 1 + 2 * numActiveIn("Shield Gens");
|
|
|
|
var injuredOut = 0, injuredIn = 0;
|
|
|
|
for (var i = 0; i < CREW; i++) {
|
|
|
|
if (health[i] < MAXHEALTH && health[i] > 0) {
|
|
|
|
if ($("#crew-status"+i+" .crew-select").val() == "Medical Bay")
|
|
|
|
injuredIn += 1;
|
|
|
|
else
|
|
|
|
injuredOut += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$("#stat-speed").text(engineSpeed);
|
|
|
|
$("#stat-scan-range").text(scanRange);
|
|
|
|
$("#stat-shield-str").text(shieldStr);
|
|
|
|
$("#stat-health-critical").text(injuredOut);
|
|
|
|
$("#stat-health-care").text(injuredIn);
|
|
|
|
$("#stat-warp-perc").text(warpPerc);
|
|
|
|
|
|
|
|
$("#stat-ratio-now").text(Math.round(10000 * engineSpeed / usageTotal));
|
|
|
|
$("#stat-ratio-left").text(Math.round(10000 * (MAXPROGRESS - progress) / power));
|
|
|
|
|
|
|
|
$("#power-used").text("POWER LOSS: " + usageTotal);
|
|
|
|
$("#power-left").text(power);
|
|
|
|
}
|
|
|
|
|
|
|
|
function positionChanged(which) {
|
|
|
|
var $whichSelect = $("#crew-status"+which+" select");
|
|
|
|
$whichSelect.prop("disabled", true);
|
|
|
|
$("#crew-status"+which+" .cryo-button").prop("disabled", true);
|
|
|
|
updateUI();
|
|
|
|
setTimeout(function(){
|
|
|
|
logbox(SHORTNAMES[which] + " moved to " + $whichSelect.val() + ".")
|
|
|
|
$whichSelect.prop("disabled", false);
|
|
|
|
updateUI();
|
|
|
|
}, MOVEDELAY);
|
|
|
|
}
|
|
|
|
|
|
|
|
function numActiveIn(position) {
|
|
|
|
return $(".crew-select").filter(function(){
|
|
|
|
return $(this).val() == position && !$(this).attr("disabled")
|
|
|
|
}).length;
|
|
|
|
}
|
|
|
|
|
|
|
|
function cryoToggle(which) {
|
|
|
|
if (frozen[which]) {
|
|
|
|
$("#crew-status"+which+" .cryo-button").prop("disabled", true);
|
|
|
|
$("#crew-status"+which+" .cryo-button").text("Unfreezing...");
|
|
|
|
logbox("Unfreezing " + SHORTNAMES[which] + "...");
|
|
|
|
setTimeout(function(){
|
|
|
|
frozen[which] = false;
|
|
|
|
$("#crew-status"+which+" select").prop("disabled", false);
|
|
|
|
$("#crew-status"+which+" .cryo-tint").css("display", "none");
|
|
|
|
$("#crew-status"+which+" .crew-img").removeClass("blurred");
|
|
|
|
$("#crew-status"+which+" .cryo-button").prop("disabled", false);
|
|
|
|
$("#crew-status"+which+" .cryo-button").text("Cryofreeze");
|
|
|
|
updateUI();
|
|
|
|
logbox("Unfroze " + SHORTNAMES[which] + ".");
|
|
|
|
}, THAWDELAY);
|
|
|
|
} else {
|
|
|
|
$("#crew-status"+which+" .cryo-button").prop("disabled", true);
|
|
|
|
$("#crew-status"+which+" .cryo-button").text("Cryofreezing...");
|
|
|
|
$("#crew-status"+which+" select").prop("disabled", true);
|
|
|
|
logbox("Cryofreezing " + SHORTNAMES[which] + "...");
|
|
|
|
setTimeout(function(){
|
|
|
|
frozen[which] = true;
|
|
|
|
$("#crew-status"+which+" .cryo-tint").css("display", "inherit");
|
|
|
|
$("#crew-status"+which+" .crew-img").addClass("blurred");
|
|
|
|
$("#crew-status"+which+" .cryo-button").prop("disabled", false);
|
|
|
|
$("#crew-status"+which+" .cryo-button").text("Unfreeze");
|
|
|
|
updateUI();
|
|
|
|
logbox("Froze " + SHORTNAMES[which] + ".");
|
|
|
|
}, CRYODELAY);
|
|
|
|
}
|
|
|
|
updateUI();
|
|
|
|
}
|
|
|
|
|
|
|
|
function injure(which) {
|
|
|
|
var $status = $("#crew-status"+which+" .crew-status");
|
|
|
|
health[which] -= 1;
|
|
|
|
if (health[which] > 0) {
|
|
|
|
$status.text("Injured");
|
|
|
|
$status.removeClass("crew-status-normal");
|
|
|
|
$status.addClass("crew-status-critical");
|
|
|
|
} else {
|
|
|
|
logbox('<span style="color:#a00">'+CREWNAMES[which]+' has died.</span>');
|
|
|
|
$status.text("Dead");
|
|
|
|
$status.removeClass("crew-status-critical");
|
|
|
|
$status.addClass("crew-status-dead");
|
|
|
|
$("#crew-status"+which+" .crew-name").addClass("crew-name-dead");
|
|
|
|
$("#crew-status"+which+" .crew-job").addClass("crew-job-dead");
|
|
|
|
}
|
|
|
|
updateUI();
|
|
|
|
}
|
|
|
|
|
|
|
|
function warp() {
|
|
|
|
if (warpPerc == 100 && power > WARPCOST) {
|
|
|
|
warpPerc = 0;
|
|
|
|
power -= WARPCOST;
|
|
|
|
$("#warp-button").attr("disabled", warpPerc < 100);
|
|
|
|
$("#stat-warp-perc").text(warpPerc);
|
|
|
|
logbox("Preparing to warp...");
|
|
|
|
setTimeout(function() {
|
|
|
|
if (health.some(x => x > 0)) {
|
|
|
|
logbox("Warp succeeded");
|
|
|
|
progress = Math.min(MAXPROGRESS, progress + WARPDIST);
|
|
|
|
var progressPerc = progress / MAXPROGRESS;
|
|
|
|
var newMilestone = Math.floor(100*progressPerc);
|
|
|
|
if (newMilestone > currentMilestone) {
|
|
|
|
currentMilestone = newMilestone;
|
|
|
|
logbox('<span style="color:#0a0">Progress: ' + currentMilestone + '%</span>');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}, WARPTIME);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
</script>
|
|
|
|
</head>
|
|
|
|
<body onload="startGame()">
|
|
|
|
<div id="outer-darkness">
|
|
|
|
<div id="content-wrapper">
|
|
|
|
<div id="progress-bg"></div>
|
|
|
|
<img id="progress-ship" src="assets/ship.png">
|
|
|
|
<div style="position:absolute; left:440; top:44;width:400px; height:28px;
|
|
|
|
border: 2px solid black; font-size:24px; text-align:center; border-radius:14px; background:rgba(255,255,255,0.8)">
|
|
|
|
<i>Indefatigable</i> Status Panel
|
|
|
|
</div>
|
|
|
|
<div class="stat-display" style="left:380; top:84; width:250px; height:96px;">
|
|
|
|
In cryo: <span id="stat-crew-cryo">-</span><br>
|
|
|
|
Active: <span id="stat-crew-active">-</span>
|
|
|
|
</div>
|
|
|
|
<div class="stat-display" style="left:380; top:190; width:250px; height:144px;">
|
|
|
|
Speed: <span id="stat-speed">-</span>x<br>
|
|
|
|
Scan: <span id="stat-scan-range">-</span><br>
|
|
|
|
Shields: <span id="stat-shield-str">-</span>x
|
|
|
|
</div>
|
|
|
|
<div class="stat-display" style="left:380; top:350; width:250px; height:96px;">
|
|
|
|
Critical: <span id="stat-health-critical">-</span><br>
|
|
|
|
In care: <span id="stat-health-care">-</span>
|
|
|
|
</div>
|
|
|
|
<div class="stat-display" style="left:380; top:502; width:250px; height:48px;">
|
|
|
|
<span id="stat-warp-perc">--</span>%
|
|
|
|
<button id="warp-button" onclick="warp()"
|
|
|
|
style="position:absolute; left:4; top:4; bottom:4; width:150; font-size:24; font-family:monospace;">
|
|
|
|
WARP</button>
|
|
|
|
</div>
|
|
|
|
<div class="stat-display" style="left:380; top:558; width:250px; height:96px;">
|
|
|
|
Efficiency: <span id="stat-ratio-now">--</span>%<br>
|
|
|
|
Required: <span id="stat-ratio-left">--</span>%
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stat-display" style="right:340; top:84; width:250px; height:96px;">
|
|
|
|
Cryo modules: <span id="stat-usage-cryo">--</span><br>
|
|
|
|
Life Support: <span id="stat-usage-lifesup">--</span>
|
|
|
|
</div>
|
|
|
|
<div class="stat-display" style="right:340; top:190; width:250px; height:144px;">
|
|
|
|
Engine: <span id="stat-usage-engine">--</span><br>
|
|
|
|
Scanner: <span id="stat-usage-scanner">--</span><br>
|
|
|
|
Shield: <span id="stat-usage-shield">--</span>
|
|
|
|
</div>
|
|
|
|
<div class="stat-display" style="right:344; top:374; width:250px; height:48px;">
|
|
|
|
Medbay: <span id="stat-usage-medbay">--</span>
|
|
|
|
</div>
|
|
|
|
<div class="stat-display" style="right:344; top:438; width:250px; height:48px;">
|
|
|
|
Missile: <span id="stat-usage-missile">--</span>
|
|
|
|
</div>
|
|
|
|
<div class="stat-display" style="right:344; top:502; width:250px; height:48px;">
|
|
|
|
Warp: <span id="stat-usage-warp">0</span>
|
|
|
|
</div>
|
|
|
|
<div class="stat-display" style="right:344; top:558; width:250px; height:96px;">
|
|
|
|
Usage: <span id="stat-usage-total">--</span><br>
|
|
|
|
Core: <span id="stat-gain-core">--</span>
|
|
|
|
</div>
|
|
|
|
<div id="logbox">[000000] Liftoff!<br></div>
|
|
|
|
<div id="power-emptybg">
|
|
|
|
<div id="power-fullbg"></div></div>
|
|
|
|
<div id="power-text" style="font-family:monospace">
|
|
|
|
<span id="power-left">--</span>
|
|
|
|
<span id="power-used">POWER LOSS: --</span>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</body>
|
|
|
|
</html>
|