diff options
Diffstat (limited to 'src/cosmic-cargo/static')
35 files changed, 1832 insertions, 0 deletions
diff --git a/src/cosmic-cargo/static/Assets/Map.png b/src/cosmic-cargo/static/Assets/Map.png Binary files differnew file mode 100644 index 0000000..c5ef943 --- /dev/null +++ b/src/cosmic-cargo/static/Assets/Map.png diff --git a/src/cosmic-cargo/static/Assets/Ship_1.png b/src/cosmic-cargo/static/Assets/Ship_1.png Binary files differnew file mode 100644 index 0000000..4db0d4f --- /dev/null +++ b/src/cosmic-cargo/static/Assets/Ship_1.png diff --git a/src/cosmic-cargo/static/Assets/Ship_2.png b/src/cosmic-cargo/static/Assets/Ship_2.png Binary files differnew file mode 100644 index 0000000..243d9f8 --- /dev/null +++ b/src/cosmic-cargo/static/Assets/Ship_2.png diff --git a/src/cosmic-cargo/static/Assets/Ship_Destroyed.png b/src/cosmic-cargo/static/Assets/Ship_Destroyed.png Binary files differnew file mode 100644 index 0000000..f694666 --- /dev/null +++ b/src/cosmic-cargo/static/Assets/Ship_Destroyed.png diff --git a/src/cosmic-cargo/static/Assets/SoundEffects/Alert.wav b/src/cosmic-cargo/static/Assets/SoundEffects/Alert.wav Binary files differnew file mode 100644 index 0000000..88972ca --- /dev/null +++ b/src/cosmic-cargo/static/Assets/SoundEffects/Alert.wav diff --git a/src/cosmic-cargo/static/Assets/SoundEffects/Boioioing.wav b/src/cosmic-cargo/static/Assets/SoundEffects/Boioioing.wav Binary files differnew file mode 100644 index 0000000..508305b --- /dev/null +++ b/src/cosmic-cargo/static/Assets/SoundEffects/Boioioing.wav diff --git a/src/cosmic-cargo/static/Assets/SoundEffects/Computerize.wav b/src/cosmic-cargo/static/Assets/SoundEffects/Computerize.wav Binary files differnew file mode 100644 index 0000000..0891361 --- /dev/null +++ b/src/cosmic-cargo/static/Assets/SoundEffects/Computerize.wav diff --git a/src/cosmic-cargo/static/Assets/SoundEffects/EnemyDetected.wav b/src/cosmic-cargo/static/Assets/SoundEffects/EnemyDetected.wav Binary files differnew file mode 100644 index 0000000..cfba998 --- /dev/null +++ b/src/cosmic-cargo/static/Assets/SoundEffects/EnemyDetected.wav diff --git a/src/cosmic-cargo/static/Assets/SoundEffects/Explosion.wav b/src/cosmic-cargo/static/Assets/SoundEffects/Explosion.wav Binary files differnew file mode 100644 index 0000000..5ad2009 --- /dev/null +++ b/src/cosmic-cargo/static/Assets/SoundEffects/Explosion.wav diff --git a/src/cosmic-cargo/static/Assets/SoundEffects/GloopGloop.wav b/src/cosmic-cargo/static/Assets/SoundEffects/GloopGloop.wav Binary files differnew file mode 100644 index 0000000..30f5f85 --- /dev/null +++ b/src/cosmic-cargo/static/Assets/SoundEffects/GloopGloop.wav diff --git a/src/cosmic-cargo/static/Assets/SoundEffects/InvalidSelection.wav b/src/cosmic-cargo/static/Assets/SoundEffects/InvalidSelection.wav Binary files differnew file mode 100644 index 0000000..8063c1f --- /dev/null +++ b/src/cosmic-cargo/static/Assets/SoundEffects/InvalidSelection.wav diff --git a/src/cosmic-cargo/static/Assets/SoundEffects/LaserDamage.wav b/src/cosmic-cargo/static/Assets/SoundEffects/LaserDamage.wav Binary files differnew file mode 100644 index 0000000..b14a5d9 --- /dev/null +++ b/src/cosmic-cargo/static/Assets/SoundEffects/LaserDamage.wav diff --git a/src/cosmic-cargo/static/Assets/SoundEffects/LowFuel.wav b/src/cosmic-cargo/static/Assets/SoundEffects/LowFuel.wav Binary files differnew file mode 100644 index 0000000..e316705 --- /dev/null +++ b/src/cosmic-cargo/static/Assets/SoundEffects/LowFuel.wav diff --git a/src/cosmic-cargo/static/Assets/SoundEffects/MoveCursor.wav b/src/cosmic-cargo/static/Assets/SoundEffects/MoveCursor.wav Binary files differnew file mode 100644 index 0000000..37c0964 --- /dev/null +++ b/src/cosmic-cargo/static/Assets/SoundEffects/MoveCursor.wav diff --git a/src/cosmic-cargo/static/Assets/SoundEffects/Ping.wav b/src/cosmic-cargo/static/Assets/SoundEffects/Ping.wav Binary files differnew file mode 100644 index 0000000..c6c2dd3 --- /dev/null +++ b/src/cosmic-cargo/static/Assets/SoundEffects/Ping.wav diff --git a/src/cosmic-cargo/static/Assets/SoundEffects/Pong.wav b/src/cosmic-cargo/static/Assets/SoundEffects/Pong.wav Binary files differnew file mode 100644 index 0000000..65bc56a --- /dev/null +++ b/src/cosmic-cargo/static/Assets/SoundEffects/Pong.wav diff --git a/src/cosmic-cargo/static/Assets/SoundEffects/Refuel.wav b/src/cosmic-cargo/static/Assets/SoundEffects/Refuel.wav Binary files differnew file mode 100644 index 0000000..6186a3f --- /dev/null +++ b/src/cosmic-cargo/static/Assets/SoundEffects/Refuel.wav diff --git a/src/cosmic-cargo/static/Assets/SoundEffects/SelectNoise.wav b/src/cosmic-cargo/static/Assets/SoundEffects/SelectNoise.wav Binary files differnew file mode 100644 index 0000000..8e7007e --- /dev/null +++ b/src/cosmic-cargo/static/Assets/SoundEffects/SelectNoise.wav diff --git a/src/cosmic-cargo/static/Assets/SoundEffects/TimeWarp.wav b/src/cosmic-cargo/static/Assets/SoundEffects/TimeWarp.wav Binary files differnew file mode 100644 index 0000000..819cc00 --- /dev/null +++ b/src/cosmic-cargo/static/Assets/SoundEffects/TimeWarp.wav diff --git a/src/cosmic-cargo/static/Assets/SoundEffects/TractorBeam.wav b/src/cosmic-cargo/static/Assets/SoundEffects/TractorBeam.wav Binary files differnew file mode 100644 index 0000000..9e29f75 --- /dev/null +++ b/src/cosmic-cargo/static/Assets/SoundEffects/TractorBeam.wav diff --git a/src/cosmic-cargo/static/Assets/SoundEffects/Wave.wav b/src/cosmic-cargo/static/Assets/SoundEffects/Wave.wav Binary files differnew file mode 100644 index 0000000..0205eb3 --- /dev/null +++ b/src/cosmic-cargo/static/Assets/SoundEffects/Wave.wav diff --git a/src/cosmic-cargo/static/Assets/encounter.ogg b/src/cosmic-cargo/static/Assets/encounter.ogg Binary files differnew file mode 100644 index 0000000..e72ef29 --- /dev/null +++ b/src/cosmic-cargo/static/Assets/encounter.ogg diff --git a/src/cosmic-cargo/static/Assets/endgame.ogg b/src/cosmic-cargo/static/Assets/endgame.ogg Binary files differnew file mode 100644 index 0000000..6fb3048 --- /dev/null +++ b/src/cosmic-cargo/static/Assets/endgame.ogg diff --git a/src/cosmic-cargo/static/Assets/font.ttf b/src/cosmic-cargo/static/Assets/font.ttf Binary files differnew file mode 100644 index 0000000..5cf4045 --- /dev/null +++ b/src/cosmic-cargo/static/Assets/font.ttf diff --git a/src/cosmic-cargo/static/Assets/gameover.ogg b/src/cosmic-cargo/static/Assets/gameover.ogg Binary files differnew file mode 100644 index 0000000..5b71695 --- /dev/null +++ b/src/cosmic-cargo/static/Assets/gameover.ogg diff --git a/src/cosmic-cargo/static/Assets/music.ogg b/src/cosmic-cargo/static/Assets/music.ogg Binary files differnew file mode 100644 index 0000000..5f1b334 --- /dev/null +++ b/src/cosmic-cargo/static/Assets/music.ogg diff --git a/src/cosmic-cargo/static/Assets/shop.ogg b/src/cosmic-cargo/static/Assets/shop.ogg Binary files differnew file mode 100644 index 0000000..7e8f7eb --- /dev/null +++ b/src/cosmic-cargo/static/Assets/shop.ogg diff --git a/src/cosmic-cargo/static/audio.js b/src/cosmic-cargo/static/audio.js new file mode 100644 index 0000000..3abc79a --- /dev/null +++ b/src/cosmic-cargo/static/audio.js @@ -0,0 +1,40 @@ +var audio = { + "bgm": new Audio("Assets/music.ogg"), + "shop": new Audio("Assets/shop.ogg"), + "encounter": new Audio("Assets/encounter.ogg"), + "endgame": new Audio("Assets/endgame.ogg"), + "gameover": new Audio("Assets/gameover.ogg"), + "alert": new Audio("Assets/SoundEffects/Alert.wav"), + "select": new Audio("Assets/SoundEffects/SelectNoise.wav"), + "move": new Audio("Assets/SoundEffects/MoveCursor.wav"), +} +var music = ["bgm", "shop", "endgame", "gameover", "encounter"] +var sfx = ["alert", "select", "move"] + +function play_audio(sound, loop = false){ + var curr_audio = audio[sound]; + if(curr_audio != undefined){ + if(loop){ + curr_audio.addEventListener('ended', loop_audio_listener, false) + } else { + curr_audio.removeEventListener("ended", loop_audio_listener, false) + } + // play if paused or if not music + if(curr_audio.paused || !music.includes(sound)){ + curr_audio.currentTime = 0; + curr_audio.play(); + } + } +} + +function pause_audio(sound){ + var curr_audio = audio[sound]; + if(curr_audio != undefined){ + curr_audio.pause(); + } +} + +function loop_audio_listener(){ + this.currentTime = 0; + this.play(); +}
\ No newline at end of file diff --git a/src/cosmic-cargo/static/credits.md b/src/cosmic-cargo/static/credits.md new file mode 100644 index 0000000..ec60daf --- /dev/null +++ b/src/cosmic-cargo/static/credits.md @@ -0,0 +1 @@ +Font: https://www.dafont.com/early-gameboy.font
\ No newline at end of file diff --git a/src/cosmic-cargo/static/draw.js b/src/cosmic-cargo/static/draw.js new file mode 100644 index 0000000..abce8d7 --- /dev/null +++ b/src/cosmic-cargo/static/draw.js @@ -0,0 +1,381 @@ +var username = undefined; // for saving score +function draw() { + switch(gameState){ + case "title": + draw_title(); + break; + case "main": + draw_main(); + break; + case "event": + draw_event(); + break; + case "shop": + draw_shop(); + break; + case "status": + draw_status(); + break; + case "event_result": + draw_event_result(); + break; + case "shop_result": + draw_shop_result(); + break; + case "options": + draw_options(); + break; + case "gameover": + draw_gameover(); + break; + case "win": + draw_win(); + break; + case "setup": + draw_setup(); + break; + } +} +function draw_setup(){ + color(3); + ctx.fillRect(0, 0, width, height); + var names = party.reduce( (acc, el) => { + if(acc == ""){ + return el.name + } else { + return `${acc}, ${el.name}` + } + }, "") + let text = `You are a space trucker in the distant year 2019. Your latest mission: transport essential cargo from Octilion to a new colony on Replaris. Without a shipment of goods within the next Earth-year, they won't survive. You assemble your most able crew: ${names}.`; + font(8, text, 3, 7, true); + font(8, ")press any key)", 30, height - 2); +} +function draw_win(){ + color(3); + ctx.fillRect(0, 0, width, height); + // game over text + font(12, "You've arrived!", 3, 12); + font(8, "Score:", 3, 24); + font(8, `${getAliveMembers().length} alive members * 400`, 3, 34); + font(8, `${ship.cargo} tons of cargo * 100`, 3, 44); + font(8, `${Math.floor(ship.credits)} credits`, 3, 54); + font(8, `Took ${ship.current_day} days )${10*(350-ship.current_day)})`, 3, 64); + var total = getAliveMembers().length*400 + ship.cargo*100 + ship.credits + (10*(350-ship.current_day)); + font(8, `Total: ${Math.floor(total)}`, 3, 120); + // set score + if(!submittedScore){ + username = submitScore("cosmic cargo", total, username); + submittedScore = true + } +} +function draw_gameover(){ + // background + color(3); + ctx.fillRect(0, 0, width, height); + // game over text + font(12, "Game over!", 3, 12); + let allDead = getAliveMembers().length == 0; + if(allDead){ + font(8, "Your crew all died", 3, 24); + } else { + font(8, "You ran out of fuel", 3, 24); + } + font(8, "Score:", 3, 44); + font(8, `${Math.floor(ship.credits)} credits`, 3, 54); + font(8, `Took ${ship.current_day} days`, 3, 64); + var total = ship.credits + ship.current_day; + font(8, `Total: ${Math.floor(total)}`, 3, 120); + if(!submittedScore){ + username = submitScore("cosmic cargo", total, username); + submittedScore = true + } +} +function draw_stars(w, h){ + color(1); + for(var x = 0; x < w; x++){ + for(var y = 0; y < h; y++){ + let chance = 1; // chance% of drawing a star + if(random_chance(0.01)){ + ctx.fillRect(x, y, 1, 1); + } + } + } +} +function draw_title(){ + // background + color(3); + ctx.fillRect(0, 0, width, height); + // stars + draw_stars(width, height); + // title + font(13, "COSMIC CARGO", 5, 20); + font(8, "A space trucking game", 3, 30); + // Only display this if the game is loaded + if(imagesLoaded){ + font(8, ")press any key)", 27, height - 20); + } +} +function draw_main(){ + // background + color(3); + ctx.fillRect(0, 0, width, height); + // ship rectangle + draw_stars(width, height/2); + ctx.beginPath(); + ctx.lineWidth = "2"; + ctx.rect(1, height/2, width-2, height/2 -1); + ctx.stroke(); + // ship + if(t % 6 < 3){ + ctx.drawImage(images["ship1"], 20, 28); + } else { + ctx.drawImage(images["ship2"], 20, 28); + } + // map + ctx.drawImage(images["map"], 0, height/2); + // map rectangle + color(1) + // Day and distance notification + font(8, `Day ${ship.current_day}`, 3, 82); + font(8, `${Math.floor(ship.fuel)} Fuel`, 3, 129); + font(8, `${ship.distance} lightyears`, 3, 139); + // map progress + color(0); + ctx.lineWidth = "1"; + // Draw dashed + ctx.setLineDash([2]) + ctx.beginPath(); + ctx.moveTo(9, 104) + ctx.lineTo(43, 92) + ctx.lineTo(76, 112) + ctx.lineTo(98, 81) + ctx.lineTo(147, 103) + ctx.stroke(); + // Draw indicators + ctx.beginPath(); + var coords = line_to_from_progress(.11, 43, 92, 76, 112); + ctx.arc(coords[0], coords[1], 1.5, 0, 2 * Math.PI); + ctx.fill(); + ctx.beginPath(); + coords = line_to_from_progress(.11, 98, 81, 147, 103); + ctx.arc(coords[0], coords[1], 1.5, 0, 2 * Math.PI); + ctx.fill(); + // Planet 1 + ctx.beginPath(); + ctx.arc(9, 104, 1.5, 0, 2 * Math.PI); + ctx.fill(); + // Planet 2 + ctx.beginPath(); + ctx.arc(43, 92, 1.5, 0, 2 * Math.PI); + ctx.fill(); + // Planet 3 + ctx.beginPath(); + ctx.arc(76, 112, 1.5, 0, 2 * Math.PI); + ctx.fill(); + // Planet 4 + ctx.beginPath(); + ctx.arc(98, 81, 1.5, 0, 2 * Math.PI); + ctx.fill(); + // Planet 5 + ctx.beginPath(); + ctx.arc(147, 103, 1.5, 0, 2 * Math.PI); + ctx.fill(); + + // Draw solid on top + let progress = ship.distance / ship.end_distance; + ctx.setLineDash([0]) + ctx.beginPath(); + ctx.moveTo(9, 104) + var shipCoords = [-20, -20]; + if(progress < 0.25){ + shipCoords = line_to_from_progress(progress, 9, 104, 43, 92); + ctx.lineTo(shipCoords[0], shipCoords[1]) + } else { + ctx.lineTo(43, 92) + if (progress < 0.5){ + shipCoords = line_to_from_progress(progress - .25, 43, 92, 76, 112); + ctx.lineTo(shipCoords[0], shipCoords[1]) + } + else { + ctx.lineTo(76, 112) + if(progress < 0.75){ + shipCoords = line_to_from_progress(progress - .5, 76, 112, 98, 81); + ctx.lineTo(shipCoords[0], shipCoords[1]) + } else { + ctx.lineTo(98, 81) + shipCoords = line_to_from_progress(progress - .75, 98, 81, 147, 103); + ctx.lineTo(shipCoords[0], shipCoords[1]) + } + } + } + ctx.stroke(); + draw_ship_indicator(shipCoords); +} +function draw_ship_indicator(coords){ + ctx.beginPath(); + ctx.arc(coords[0], coords[1], 2, 0, 2 * Math.PI); + ctx.fill(); +} +function line_to_from_progress(norm_progress, x1, y1, x2, y2){ + var newX = x1 + (x2-x1)*4*norm_progress + var newY = y1 + (y2-y1)*4*norm_progress + return [newX, newY] +} +function draw_event(){ + // background + color(3); + ctx.fillRect(0, 0, width, height); + font(12, `${currentEvent.name}`, 3, 13); + font(8, `${currentEvent.desc}`, 3, 25, true); + let choices = get_choices(currentEvent) + var i = 0; + for(let choice of choices){ + font(8, choice, 10, 100 + 10*i); + if(selectedChoice == i){ + font(8, `~`, 3, 100 + 10*i); + } + i++; + } +} +function draw_options(){ + // background + color(3); + ctx.fillRect(0, 0, width, height); + font(12, `Adjust volume`, 5, 13); + + let music_volume = `${audio[music[0]].volume*100}`.replace(/\.\d*/,"") + let sfx_volume = `${audio[sfx[0]].volume*100}`.replace(/\.\d*/,"") + font(8, `MUSIC: ${(music_volume)}`, 10, 22); + if(selectedChoice == 0){ + font(8, `~`, 4, 22); + } + font(8, `SFX: ${sfx_volume}`, 10, 32); + if(selectedChoice == 1){ + font(8, `~`, 4, 32); + } +} +function draw_status(){ + // background + color(3); + ctx.fillRect(0, 0, width, height); + // person list + var i = 0; + for(let person of party){ + var c = 2 + if(person.status == "Dead" || person.status == "Missing"){ + c = 0 + } + font(8, person.name, 3, 7 + 12*i, false, c); + font(8, person.status, 100, 7 + 12*i, false, c); + i++; + } + + font(8, `Cargo: ${ship.cargo} tons`, 3, 106); + font(8, `Credits: ${ship.credits}`, 3, 118); + font(8, `Fuel: ${Math.floor(ship.fuel)}`, 3, 130); + font(8, `Day ${ship.current_day}`, 3, 142); + font(8, `${ship.distance} of ${ship.end_distance}`, 75, 142); +} +function draw_shop(){ + // background + color(3); + ctx.fillRect(0, 0, width, height); + font(12, "Shop", 3, 13); + font(8, `Welcome to ${currentShop.name}! Enjoy your stay!`, 3, 25, true); + let choices = shop_choices(currentShop); + var i = 0; + for(let choice of choices){ + font(8, choice, 10, 100 + 10*i); + if(selectedChoice == i){ + font(8, `~`, 4, 100 + 10*i); + } + i++; + } +} +function draw_event_result(){ + // background + color(3); + ctx.fillRect(0, 0, width, height); + font(8, `${eventResult}`, 3, 11, true); + font(8, ")press any key)", 26, height - 8); +} +function draw_shop_result(){ + // background + color(3); + ctx.fillRect(0, 0, width, height); + font(8, `${shopResult}`, 3, 11, true); + font(8, ")press any key)", 26, height - 8); +} + +function split_into_parts(what, size){ + var parts = []; + var line = ""; + for(var word of what.split(" ")){ + if(line.length + word.length > size){ + parts.push(line.trim()); + line = ""; + } + line += " " + word; + } + if(line){ + parts.push(line); + } + return parts; +} +function font(size, what, x, y, wrap = false, c = 2) { + ctx.font = size + "px Gameboy"; + color(c); + if(wrap){ + // Match up to 22 characters, making sure to not end a line mid word + //var parts = what.match(/.{1,21}[\b\.'\?\)]/g); + var parts = split_into_parts(what, 21); + parts.forEach((element, i) => { + ctx.fillText(element.trim(), x, y + i*size); + }); + } else { + ctx.fillText(what, x, y); + } +} +function color(c) { + var color = ""; + switch (c){ + case 0: + color = "#FA0000"; + break; + case 1: + color = "#b4b4b4"; + break; + case 2: + color = "#fafafa"; + break; + case 3: + color = "#000000"; + break; + } + ctx.fillStyle = color; + ctx.strokeStyle = color; +} +function loadImages(imagefiles) { + loadcount = 0; + loadtotal = imagefiles.length; + imagesLoaded = false; + + // Load the images + var loadedimages = []; + for (var i=0; i<imagefiles.length; i++) { + // Create the image object + var image = new Image(); + + // Add onload event handler + image.onload = function () { + loadcount++; + if (loadcount == loadtotal) { + // Done loading + imagesLoaded = true; + } + }; + image.src = imagefiles[i]; + loadedimages[i] = image; + } + return loadedimages; +}
\ No newline at end of file diff --git a/src/cosmic-cargo/static/events.js b/src/cosmic-cargo/static/events.js new file mode 100644 index 0000000..67da728 --- /dev/null +++ b/src/cosmic-cargo/static/events.js @@ -0,0 +1,746 @@ +var events = []; +var event_cooldown = []; +var karen_finale = false; + +/** + * Creates all established events and stores them in the events array + */ +function generate_events(){ + events = []; + event_cooldown = []; + karen_finale = false; + events = [ + new SpaceEvent("Pirates!", "Pirates have boarded your ship demanding payment! Fight?", + [["Pay",function(ship, party){ + let loot = Math.floor(ship.credits *.2); + ship.credits -= loot; + return `The pirates take ${loot} credits, and your party remains unscathed.` + }], + ["Fight",function(ship, party){ + // Higher chance to win if there are more members in your party + let alive = party.filter( el => (el.status != "Dead")); + if(random_chance(1-(1/alive.length))){ + return "You fight the pirates off successfully!" + } else { + let loot = Math.floor(ship.credits *.2); + ship.credits -= loot; + let injured = random_choice(alive); + injured.status = getStatus(injured.status, -1); + return `The pirates overtake you. They steal ${loot} credits, and ${injured.name} is now ${injured.status}.` + } + }]]), + new SpaceEvent("Dolik Attack!", "A group of doliks have invaded your ship!", + [["Surrender",function(ship, party){ + let exterminated = random_choice(party.filter( el => (el.status != "Dead"))); + exterminated.status = "Dead" + return `The doliks eliminate ${exterminated.name} and move on, finding nothing of interest on your ship.` + }], + ["Try to resist",function(ship, party){ + // Higher chance to win if there are more members in your party + let alive = party.filter( el => (el.status != "Dead")); + if(alive.length == 1 || random_chance(0.2)){ + let talker = random_choice(alive); + return `${talker.name} tries to convince the doliks to leave. Somehow it works!` + } else { + let talker = random_choice(alive); + var fodder = random_choice(alive); + while(fodder.name == talker.name){ + fodder = random_choice(alive); + } + talker.status = "Dead"; + fodder.status = "Dead" + return `${talker.name} tries to convince the doliks to leave. It Fails! They loudly synthesize the word "ELIMINATE" as they murder ${talker.name} and ${fodder.name}.` + } + }]]), + new SpaceEvent("Planet Plague", "An encounter with an asteroid causes your ship to take on foreign bacteria which infects your crew", + [["OK", function(ship, party){ + let alive = party.filter( el => (el.status != "Dead")); + let sick = random_choice(alive); + sick.status = getStatus(sick.status, -1); + if(alive.length >= 2 && random_chance(0.4)){ + var fodder = random_choice(alive); + while(fodder.name == sick.name){ + fodder = random_choice(alive); + } + fodder.status = getStatus(fodder.status, -1); + return `${sick.name} catches the illness and is now feeling ${sick.status}. It spreads to ${fodder.name}, who is now ${fodder.status}` + } else { + return `${sick.name} catches the illness and is now feeling ${sick.status}.` + } + }]]), + new SpaceEvent("Engine Fault", "Your engine breaks down and requires repair.", + [["Go on without it", function(ship, party){ + ship.speed -= 1; + let injured = random_choice(getAliveMembers()); + injured.status = getStatus(injured.status, -1); + return `The engine blow out hurt ${injured.name} and your speed has been impacted. It will take you longer to arrive.` + }], + ["Try to repair it", function(ship, party){ + var days = random_int(25)+1; + ship.current_day += days; + let injured = random_choice(getAliveMembers()); + injured.status = getStatus(injured.status, -1); + return `The engine blow out hurt ${injured.name}. It took ${days} days but you manage to get the engine back in working order.` + }]]), + new SpaceEvent("Wormhole", "You spot a wormhole in front of you.", + [["Enter it", function(ship, party){ + var sign = random_int(2); + let diff = 100 + random_int(100); + if(sign == 0){ + ship.distance += diff + return `You find your ship transported ${diff} lightyears further along your trip`; + } else { + ship.distance -= diff + return `You find your ship transported ${diff} lightyears backwards along your path`; + } + }], + ["Ignore it", function(ship, party){ + return `You pass by the wormhole, never knowing what secrets it might hold.` + }]]), + new SpaceEvent("Space Sheriff", "You hear a siren and see flashing lights coming up behind you in the mirror", + [["Pull over", function(ship, party){ + if(random_chance(0.25)){ + return `The officer speeds by you. He must be chasing someone ahead of you.`; + } else { + let credits = Math.min(75, ship.credits); + ship.credits -= credits; + return `The officer tells you that your taillight is out and fines you ${credits} credits.`; + } + }], + ["Try to outrun him", function(ship, party){ + if(random_chance(0.1)){ + return `You take off fast and manage to escape his pursuit.` + } else { + let injured = random_choice(getAliveMembers()); + injured.status = getStatus(injured.status, -1); + let credits = Math.min(200, ship.credits); + ship.credits -= credits; + return `The sheriff catches up to you and says, "Running from an officer is a serious offense. By the way, your taillight is out," as he hands you a fine for ${credits} credits.` + } + }]]), + new SpaceEvent("Lost Ship", "A ship in the distance looks like it needs help. You approach and they say they ran out of fuel.", + [["Offer 10 fuel", function(ship, party){ + ship.fuel -= 10; + return "The ship is gracious for your help" + }], + ["You have no extra", function(ship, party){ + let hurt = random_choice(getAliveMembers()); + hurt.status = getStatus(hurt.status, -1); + return `The crew on the other ship gets violent. They attack and ${hurt.name} is now ${hurt.status}.` + }]]), + new SpaceEvent("Med-Outpost", "You arrive at the medical outpost. For 200 credits, they can heal your entire team.", + [["Heal up", function(ship, party){ + getAliveMembers().forEach(element => { + element.status = getStatus(element.status, 1); + }); + return "Your team feels a lot better now." + }], + ["Pass", function(ship, party){ + return `Your team is doing just fine. You assure them things will be alright.` + }]]), + new SpaceEvent("Aurora", "A fantastical light-show happens outside the windows of your ship, almost as if some magical being is present.", + [["Try to make contact", function(ship, party){ + var i = random_int(100); + var healed = getAliveMembers().find(el => el.status != "Good" && el.status != "Dead"); + if(i < 25 && healed){ + healed.status = getStatus(healed.status, 1); + return `You don't notice anything outside changing, but ${healed.name} feels a bit better now.` + } else if(i < 50){ + ship.fuel = Math.min(100, ship.fuel + 30) + } else if(i < 75){ + ship.cargo += 10; + } else { + ship.credits += 100; + } + return `You don't notice anything outside changing, but something feels different.` + }]]), + new SpaceEvent("Space Anomaly", "While traveling through a subspace field, your scanners pick up a strange signal that cannot be decoded.", + [["Investigate it", function(ship, party){ + if (random_chance(.2)){ + return "While approaching the anomaly, you and your crew hears a voice speak in your heads. 'I thank you mortals for waking me from my long slumber. I must now consume the loop of time. You will be remembered forever...'. The voice fades. You ponder your actions and continue your journey."; + } + if (random_chance(.6)){ + let warp_bonus = random_int(250); + ship.distance += warp_bonus; + + return `Approaching the anomaly you and your crewmen find yourselves losing control of the ship! The craft is flung through a wormhole. Upon regaining control, you find yourself ${warp_bonus} lightyears closer to your destination!`; + } + loot = random_int(200); + ship.credits += loot; + return `Narrowing in on the anomaly reveals a cache of resources worth about ${loot} credits!`; + }], + ["Ignore it", function(ship, party){ + return "You ponder what secrets the anomaly might've held as you choose to safely direct your ship to continue on your journey."; + }]]), + new SpaceEvent("Black Market", "While parked, a cloaked ship reveals itself to you. They seem to want your cargo, and are willing to pay handsomely for it.", + [["Sell 5 Cargo", function(ship, party){ + if (ship.cargo >= 5){ + ship.cargo -= 5; + ship.credits += 625; + return "As quick as it appeared, the ship cloaks and fades away. The exchange went without issue. 5 cargo for an easy 625 credit bonus. You just hope what you have left will be enough for the colony..."; + } + else if (ship.cargo <= 0){ + return "Unfortunately, you don't have any cargo left to sell. Picking up on this the opposing ship quickly cloaks away..."; + } + else { + remaining_cargo = ship.cargo; + bonus_credits = remaining_cargo * 125; + ship.cargo -= remaining_cargo; + ship.credits += bonus_credits; + return `While you didn't have quite enough cargo, the visitors were still more than happy to make a trade. In exchange for the rest of your cargo you recieved ${bonus_credits} credits!`; + } + }], + ["Sell 1 Cargo", function(ship, party){ + if (ship.cargo >= 1){ + ship.cargo -= 1; + ship.credits += 125; + return "As quickly as it appeared, the ship disappears. The exchange went without issue. 1 cargo for an easy 125 credit bonus."; + } + else { + return "Unfortunately, you don't have any cargo left to sell. Picking up on this the opposing ship quickly cloaks away..."; + } + }], + ["No thanks.", function(ship, party){ + return "As quickly as it appeared, the mysterious vessel disappears back into the abyss of space. Your ship's communications hub pings and a message appears: 'We'll be in touch...' ."; + }]]), + new SpaceEvent("Comet", "A comet flies towards your ship.", + [["Engage the shields", function(ship, party){ + ship.speed -= 1 + return "The shields get overloaded by the collision and the surge takes one of your engines offline." + }], + ["Take it head on", function(ship, party){ + var hit = random_choice(getAliveMembers()); + hit.status = getStatus(hit.status, -1); + return `The collision shakes up ${hit.name} who is now ${hit.status}.` + }]]), + new SpaceEvent("Snake", "A spectral snake comes through the ship's teleporter and interrupts your activities on the bridge.", + [["Attack it", function(ship, party){ + if(getAliveMembers().some((el) => el.name == "Space Cat")){ + return `Before you are able to land a blow on the snake, Space Cat calls out to the Snake 'Jimmy! I haven't seen you since college!' The two go into the galley and catch up over some drinks.` + } + return "With a swift kick to the snake's head, it collapses and dies a little too easily and standing over its corpse, you begin to realize that you are the true monster." + }], + ["Ignore it", function(ship, party){ + if(getAliveMembers().some((el) => el.name == "Space Cat")){ + return `Space Cat calls out to the Snake 'Jimmy! I haven't seen you since college!' The two go into the galley and catch up over some drinks.` + } + var scared = random_choice(getAliveMembers()); + scared.status = getStatus(scared.status, -1); + return `You ignore the snake, but it still scares ${scared.name} who is now ${scared.status}. ${scared.name} hates snakes.` + }]]), + new SpaceEvent("Droids", "A group of Class-C enforcement droids request to board.", + [["Let them", function(ship, party){ + ship.cargo -= 3; + return "The droids search your cargo for contraband. They look around and realize that this is not the ship they are looking for, though some cargo now seems to be missing." + }], + ["Deny their request", function(ship, party){ + var scared = random_choice(getAliveMembers()); + scared.status = getStatus(scared.status, -1); + return `The droids get upset and call ${scared.name} mean names, who is now ${scared.status}.` + }]]), + new SpaceEvent("Comedian", "A wandering comedian enters into your path and you let her on the ship.", + [["Talk to her", function(ship, party){ + var jokes = ["What's the deal with astronaut food? It's coarse and rough and irritating. That one is more of an observation.", + "You ever worry about aliens? Not me, I'm safe, I'd never get eaten. You know why? Cause I'd taste funny! Hah!", + "Where do astronauts leave their spaceships? At parking meteors", + "What did the astronaut see on his skillet? Unidentified FRYing objects. ", + ] + return random_choice(jokes); + }], + ["Ignore her", function(ship, party){ + var hurt = random_choice(getAliveMembers()); + hurt.status = getStatus(hurt.status, -1); + return `"What's your problem?" the comedian says and gives ${hurt.name} the punch line. They are now ${hurt.status}.` + }]]), + new SpaceEvent("Bounty Hunter", "A bounty hunter hails your ship and informs you that you are harboring a fugitive. He requests that you either give them up, or he will come on board.", + [["Comply", function(ship, party){ + var hurt = random_choice(getAliveMembers()); + hurt.status = "Missing"; + ship.credits += 150; + return `You deliver ${hurt.name} to the hunter and they fly off into space, never to be seen again. You earn a few credits for not giving him a hard time` + }], + ["Refuse", function(ship, party){ + var hurt = random_choice(getAliveMembers()); + hurt.status = getStatus(hurt.status, -1); + let cargo = Math.min(random_int(5), ship.cargo) + 2; + return `The bounty hunter boards your ship and picks a fight. ${hurt.name} becomes ${hurt.status} in the process. The rumble also takes out ${cargo} cargo.` + }]]), + new SpaceEvent("Mutiny", "Your crew demands higher wages, or else there will be a fight.", + [["Pay them", function(ship, party){ + var hurt = random_choice(getAliveMembers()); + hurt.status = getStatus(hurt.status, -1); + var fodder = random_choice(getAliveMembers()); + while(fodder.name == hurt.name){ + fodder = random_choice(getAliveMembers()); + } + fodder.status = getStatus(fodder.status, -1); + ship.credits -= Math.min(getAliveMembers().length * 40, ship.credits) + return `${hurt.name} and ${fodder.name} get hurt in the commotion, but you settle it. Each member demands 40 credits, which you deliver.` + }], + ["Fight", function(ship, party){ + var hurt = random_choice(getAliveMembers()); + hurt.status = getStatus(hurt.status, -1); + ship.credits -= Math.min(getAliveMembers().length * 15, ship.credits) + return `Not all the crew is against you. ${hurt.name} joins your side and is injured in the spat. Their status is now ${hurt.status}. In the end, you settle things by paying each remaining crew member 15 credits.` + }]]), + new SpaceEvent("Mechanic", "An amateur mechanic offers to upgrade your ship, though he warns you that any official repairman will revert his changes if you go through with it.", + [["Upgrade )200 credits)", function(ship, party){ + if(ship.credits < 200){ + return "Sorry, you just don't have the cash." + } + ship.credits -= 200; + ship.speed += 5; + ship.rate = 3.0 + return "That baby should be traveling much faster now. You'll make it to wherever you are going months ahead of time." + }], + ["Leave", function(ship, party){ + return `Alright, your loss.` + }]]), + new SpaceEvent("Dysentery", "Someone in your party contracts dysentery.", + [["Uh oh", function(ship, party){ + let hurt = random_choice(getAliveMembers()); + hurt.status = "Dead"; + return `${hurt.name} has died of dysentery.` + }]]), + new SpaceEvent("Event Horizon", "During your travels your navigation module attempts to guide your ship into the center of a black hole, claiming it as a shortcut.", + [["Worth a shot!", function(ship, party){ + if (random_chance(.6)){ + ship.speed -= 4; + ship.rate = 2.7; + cargo_loss = random_int(3) + 1; + + if (ship.cargo > cargo_loss){ + ship.cargo -= cargo_loss; + return `This is a disaster! Losing control, your ship bounces around the event horizon before successfully warping away. The engine is now severly damaged, fuel is leaking, and ${cargo_loss} cargo was lost.`; + } + + ship.cargo = 0; + return "This is a disaster! Losing control, your ship bounces around the event horizon before successfully warping away. The engine is now severly damaged, fuel is leaking, and all cargo was lost."; + } + + let distance_bonus = random_int(300) + 100; + ship.distance += distance_bonus; + return `Somehow you successfully navigate through the event horizon! You find yourself ${distance_bonus} lightyears closer to your destination!`; + }], + ["Too risky.", function(ship, party){ + return "You choose not to risk your ship and find an alternative route."; + }]]), + new SpaceEvent("Asteroids", "An astroid shower rains down on your ship, causing severe damage!", + [["This is troubling...", function(ship, party){ + ship.speed -= 1; + ship.rate = 2.7; + ship.cargo -= 1; + let injured = random_choice(getAliveMembers()); + injured.status = getStatus(injured.status, -1); + return `Damage report indicates the ship is moving slower, fuel is leaking, some cargo was damaged, and ${injured.name} got hurt too.`; + }]]), + new SpaceEvent("Make a Wish!", "You see a shooting star! Time to make a wish... I wish for...", + [["Fuel!", function(ship, party){ + ship.fuel = Math.min(ship.fuel + 25, 100); + return "It seems that star was perfect for fuel scooping! Your fuel reserves are full!"; + }], + ["Credits!", function(ship, party){ + if (random_chance(.3)){ + ship.credits += 100; + return "Your pocket seems oddly heavy. Reaching in you find a cool extra 100 credits! Score!" + } + + let delay = random_int(10) + 5; + ship.days += delay; + return `The shooting star curses you for your greed. Systems on your ship have randomly shut down, delaying your progress by ${delay} days.`; + }], + ["A quick journey!", function(ship, party){ + let bonus_distance = random_int(100) + 10; + ship.distance += bonus_distance; + return `A lot of time seems to have passed in only a moment and you realize that you are ${bonus_distance} lightyears closer to the colony!`; + }], + ["Repairs!", function(ship, party){ + //party.forEach(element => { + // element.status = getStatus(element.status, 1); + //}); + + ship.speed = 10; + ship.rate = 2.5; + return "Your ship's damaged systems spring to life and seem to be working as normal!"; + }]]), + new SpaceEvent("Space Whale", "Improbably, a majestic sperm whale is spawned into existence in front of you. Sadly, without oxygen it is doomed to die", + [["Fascinating", function(ship, party){ + ship.days += 1; + return "Struck in awe by the great beast, you don't notice your ship drifting from course, resulting in a delay of one day."; + }]]), + new SpaceEvent("Creature", "Your scanners detect the hulking mass of a tentacled monstrosity. It appears to be dead...", + [["Investigate", function(ship, party){ + if (random_chance(.1)){ + if (random_chance(.1)){ + party.forEach(element => { + element.status = "Dead"; + });wizaed + + return "Oh no, you walked into a trap! The creature moves effortlessly through space and envelops your ship, you and your crew are helpless as you are brought to what looks like an orifice and swallowed. A dark abyss awaits you..."; + } + + ship.fuel -= 5; + return "Oh no, you fell for the beast's charade! The creature quickly reaches out to consume your ship, but you manage to engage warp just in time. Your ships's fuel reserves are significantly depleted."; + } + + let bonus_credits = random_int(100) + 25; + let bonus_cargo = random_int(2) + 1; + ship.credits += bonus_credits; + return `The creature appears to be dead, though there are a lot of destroyed ships around it. You scavange what you can and find ${bonus_credits} credits and ${bonus_cargo} cargo!`; + }], + ["Leave it alone.", function(ship, party){ + return "You leave the strange beast alone and continue your travels. You notice on your scanners the anomaly has since disappeared..."; + }]]), + new SpaceEvent("Investment", "At a rest stop, you and your crew are pulled to the side of the building by a shifty man who lets you in on the investment of a lifetime for only 100 credits!", + [["Go on...", function(ship, party){ + if (ship.credits >= 100){ + ship.credits -= 100; + + if (random_chance(.5)){ + let bonus_credits = random_int(100) + 50 + ship.credits += bonus_credits + 100; + return `This deal is too good to pass up. You invest 100 credits and are told to give it some time. A few days later a message arrives revealing you are ${bonus_credits} credits richer.`; + } + + return "This deal is too good to pass up. You invest 100 credits and are told to give it some time. You never hear back."; + } + + ship.credits += 25; + return "Realizing you are short on credits, the man takes pity on you for listening in on his presentation and gives you 25 credits"; + }], + ["Not interested", function(ship, party){ + let injured = random_choice(getAliveMembers()); + injured.status = getStatus(injured.status, -1); + return `The man doesn't take this well and motions to his muscle to convince you. A brawl ensues leaving ${injured.name} ${injured.status}, but your credits safe!`; + }]]), + new SpaceEvent("Distress", "Your scanner picks up a distress beacon from a nearby planet. You investigate and find a colony that is experiencing famine and seeking cargo.", + [["Spare 5", function(ship, party){ + ship.cargo -= 5; + return "You give the colony 3 cargo to get by, and hope they can recover."; + }], + ["Others need our help more...", function(ship, party){ + let alive_party = getAliveMembers(); + + if (alive_party.length >= 3){ + let p1 = alive_party[0]; + let p2 = alive_party[1]; + let p3 = alive_party[2]; + p1.status = getStatus(p1.status, -1); + p2.status = getStatus(p2.status, -1); + p3.status = getStatus(p3.status, -1); + + return `The colonists despair and attack the grounded crewmen! ${p1.name}, ${p2.name}, and ${p3.name} are hurt while returning to the ship.`; + } + else if (alive_party.length >= 2){ + let p1 = alive_party[0]; + let p2 = alive_party[1]; + p1.status = getStatus(p1.status, -1); + p2.status = getStatus(p2.status, -1); + + return `The colonists despair and attack the grounded crewmen! ${p1.name}, and ${p2.name} are hurt while returning to the ship.`; + } + else { + return "The colonists desperately attempt to board your landed ship and seize control. Upon gaining entry, they find you are the only crew member. One colony is saved, yet another colony is lost..."; + } + }]]), + new SpaceEvent("Red Alert", "The 'RED ALERT' alarm suddenly roars to life! Your crewmen rush to the helm to investigate.", + [["'Computer, status'", function(ship, party){ + if (random_chance(.50)){ + let victim = random_choice(getAliveMembers()); + victim.status = getStatus(victim.status, -1); + + return `A small breach in the hull reveals a leak! Your crewmen manage to repair the leak before losing too much oxygen although ${victim.name} is not feeling so great.`; + } + + if (random_chance(.50)){ + let victim = random_choice(getAliveMembers()); + victim.status = getStatus(victim.status, -1); + ship.cargo -= 1 + random_int(5); + + return `It seems someone forgot to close the liftgate at the last stop... You manage to reseal it, but lose some cargo during the effort. ${victim.name} is not feeling so great.`; + } + let victim = random_choice(getAliveMembers()); + victim.status = getStatus(victim.status, -1); + return `It seems someone forgot to close the liftgate at the last stop... You manage to successfully reseal it without losing any cargo. ${victim.name} feels a little sick after.`; + + }]]), + new SpaceEvent("Space Cat", "While stopped for a break, you see a sight most odd. A cat, in a space suit, is floating around on a crate.", + [["What...?", function(ship, party){ + ship.cargo += 1; + if (party.length < 7 && random_chance(.3)){ + party.push({ + name: "Space Cat", + status: getStatus(""), + }); + return "You decide to recruit the cat and gain a new crew member! You also scored an additional cargo!"; + } + return "You take the crate and get 1 cargo. As you reach for the cat it waves goodbye, and flys off."; + }]]), + new SpaceEvent("The Wizard", "A video message appears on your ships console. What appears to be a man dressed as a wizard asks you to consider stopping at his moon workshop. Are you interested?", + [["Well, alright.", function(ship, party){ + let lost_days = random_int(50) + 1; + ship.days += lost_days; + return `Arriving at the location, you quickly determine this is a tourist trap. While leaving, businessmen attempt to sell you timeshares for condos. Shrugging them off, you leave. However, due to time dialation you lost ${lost_days} days`; + }], + ["There's no time!", function(ship, party){ + let injured = random_choice(getAliveMembers()); + injured.status = getStatus(injured.status, -1); + return `The wizard somehow sends a magic fireball through the radio, injuring ${injured.name}.`; + }]]), + ]; +} + +/** + * Preset event to call during specific days of travel + */ +function asteroid_event(){ + return new SpaceEvent("Astroid Belt", "Your ship is approaching an asteroid belt that is difficult to navigate.", + [["Try to fly through", function(ship, party){ + if(random_chance(0.33)){ + return "Your mastery at piloting your ship allowed you to navigate the field with no difficulties." + } else if(random_chance(0.5)){ + let lost = Math.floor(ship.cargo * ((random_int(10)+2)/100)); // (2-11% of cargo) + ship.cargo -= lost; + return `Your trip through the asteroid belt was nearly successful except for a minor collision towards the end in which you lost ${lost} tons of cargo` + } else { + let lost = Math.floor(ship.cargo * ((random_int(10)+10)/100)); // (11-20% of cargo) + ship.cargo -= lost; + return `A rogue asteroid slams into the cargo bay doors. You lose ${lost} tons of cargo in the collision.` + } + }], + ["Look for a new route", function(ship, party){ + let days = random_int(20)+1; + let fuel = Math.floor(days/2); + ship.current_day += days + ship.fuel -= fuel; + return `You took ${days} days to find a safe route, and used some fuel in the process.` + }], + ["Hire a navigator", function(ship, party){ + let charge = Math.min(random_int(75)+50, ship.credits); + return `You pay for the local taxi service to escort you across the belt. They do so without difficulty, but charge you ${charge} credits.` + }]]); +} + +function hostiles_event(){ + return new SpaceEvent("Hostile Area", "Your ship reaches a hostile zone, full of marauders.", + [["Try to fly through", function(ship, party){ + if(random_chance(0.33)){ + return "Your piloting mastery allowed you to navigate the area with no difficulties." + } else if(random_chance(0.5)){ + let lost = Math.floor(ship.cargo * ((random_int(10)+2)/100)); // (2-11% of cargo) + ship.cargo -= lost; + return `Your trip through the hostile zone was mostly smooth, except for a minor conflict towards the end in which you lost ${lost} tons of cargo` + } else { + let lost = Math.floor(ship.cargo * ((random_int(10)+10)/100)); // (11-20% of cargo) + ship.cargo -= lost; + return `The marauders manage to hinder your voyage. You lost ${lost} tons of cargo during the trip.` + } + }], + ["Look for a new route", function(ship, party){ + let days = random_int(20)+1; + let fuel = Math.floor(days/2); + ship.current_day += days + ship.fuel -= fuel; + return `You took ${days} days to find a safe route, and used some fuel in the process.` + }], + ["Hire a navigator", function(ship, party){ + let charge = Math.min(random_int(75)+50, ship.credits); + + ship.credits -= charge; + return `You pay for the local taxi service to escort you across the hostile zone. They do so without difficulty, but charge you ${charge} credits.` + + }]]); +} + +/** + * Returns 'Rogue Karen' event if Karen is in the party + */ +function return_karen(){ + return new SpaceEvent("Rogue Karen", "A member of your crew is acting awfully suspicious...", + [["What's going on?", function(ship, party){ + let karen = getAliveMembers().find(el =>el.name == "Karen"); + let kid = random_choice(getAliveMembers()); + + if (getAliveMembers().length > 2){ + while(kid.name == "Karen"){ + kid = random_choice(getAliveMembers()); + } + } + + if (karen != undefined){ + karen.status = "Missing"; + kid.status = "Missing"; + ship.credits -= Math.floor(ship.credits * .5); + events.push(karens_revenge()); + return `Oh no! Crewman Karen has stolen half of your hard-earned credits and claims ${kid.name}, the youngest of your crew, as a hostage. She hijacks an escape pod and disappears, taking her bounty and the kid with her. You should have seen it coming.`; + } + + return `Oh, it's just crewman ${kid.name} dancing around and being a goof.`; + }]]); +} + +/** + * Adds 'Revenge' event after 'Rouge Karen' + */ +function karens_revenge(){ + + return new SpaceEvent("Karen's Revenge", "An armada of ships blocks your path and you're hailed. It is Karen. She demands your credits and her 'kids'", + + [["Surrender", function(ship, party){ + if (getAliveMembers().length > 2){ + let kid1 = random_choice(getAliveMembers()); + let kid2 = random_choice(getAliveMembers()); + + while (kid1.name == kid2.name){ + kid2 = random_choice(getAliveMembers()); + } + + kid1.status = "Missing"; + kid2.status = "Missing"; + ship.credits = 0; + karen_finale = true; + return `Giving in to her demands, you hand over ${kid1.name} and ${kid2.name} along with the rest of your credits. With a smirk, Karen and her aramda fly off. She will rue this day...`; + } + else if (getAliveMembers().length > 1){ + let kid1 = random_choice(getAliveMembers()); + kid1.status = "Missing"; + ship.credits = 0; + karen_finale = true; + return `Giving in to her demands, you hand over ${kid1.name} along with the rest of your credits. With a smirk, Karen and her aramda fly off. She will rue this day...`; + } + + karen_finale = true; + return `Seeing that her 'kids' are either dead or missing, Karen is happy enough with taking the rest of your credits. With a smirk, Karen and her aramda fly off. She will rue this day...`; + }], + ["Not without a fight!", function(ship, party){ + if (random_chance(.2)){ + let victim = random_choice(getAliveMembers()); + victim.status = getStatus(victim.status, -1); + karen_finale = true; + return `Through your superior navigation skills, you manage to fly through the armada and take out a few of her ships! Though ${victim.name} is ${victim.status} after the attempt. Hopefully this is the last you see of Karen...`; + } + + let alive = getAliveMembers(); + + for (i = 0; i < alive.length; i++){ + alive[i].status = "Dead"; + } + + return "There are too many of them! The ship is overtaken as you succumb to Karen's assault. Your last sight is that of the commanding ship firing into your fuel storage. Never trust Karen."; + }]]); +} + +/** + * Adds 'Karen's Requeim' event after 'Karen's Revenge' and only at 3000 lightyears + */ +function karen_requiem(){ + + return new SpaceEvent("Karen's Requiem", "At your destination, you again find Karen's gang, only this time attacking the colony. Karen hails you, sparing them in exchange for all of your cargo.", + + [["This ends here!", function(ship, party){ + if (talk_count >= 3){ + ship.credits += 1000; + return "You are a cargo trucker. No more, no less. But that is exactly what you needed to be today. Grabbing your CB radio you message every nearby trucker. Soon, an army of trucks stands with you. Karen surrenders all she has for her life. The colony is saved."; + } + + let alive = getAliveMembers(); + + for (i = 0; i < alive.length; i++){ + alive[i].status = "Dead"; + } + + return "You are a cargo trucker. No more, no less. But that is exactly what you needed to be today. Grabbing your CB radio you message every nearby trucker in the sector... but no ones comes. Perhaps you didn't talk to enough truckers... You don't stand a chance."; + }], + ["Surrender the cargo...", function(ship, party){ + ship.cargo = 0; + return "It is a hopeless situation. In exchange for your life you surrender your cargo. Karen smirks and leaves with her armada. You have no choice but to face the colony and deliver them the terrible news..."; + }]]); +} + +/** + * Returns a random event from the events array + * Removes the chosen event from the events array and puts it in the event_cooldown array + * Once the length of the events array is less than or equal to the length of the cooldown array + * the events array is refilled with the elements within the event_cooldown array. + * The event_cooldown array is then reset + */ +function get_event(){ + if (events.length <= 0){ + events = events.concat(event_cooldown); + event_cooldown = []; + } + else if (events.length <= Math.floor(event_cooldown.length * .5)){ + events.push(event_cooldown.pop()); + } + + let event = random_choice(events); + let event_index = events.indexOf(event); + + if (event.name != "Karen's Revenge"){ + event_cooldown.push(event); + } + + if (event_index != -1){ + events.splice(event_index, 1); + } + + return event; +} + +/** + * Represents an available psudo random event the player may experience + * @constructor + * @param {string} name - the name of the event as shown to the player + * @param {string} desc - a description of the event shown to the player + * @param {array} choices - an array of tuple choices with index 0 holding the choice name and index 1 a callback function that + * is passed the ship and party stats, which returns a description of the consequences + */ +function SpaceEvent(name, desc, choices){ + this.name = name; + this.desc = desc; + this.choices = choices; +} + +/** + * Returns a string array of all available strings for this event + * @param {Event} event - an Event object + */ +function get_choices(event){ + var c = [] + for (i = 0; i < event.choices.length; i++){ + c.push(event.choices[i][0]); + } + return c; +} + +/** + * Handles event logic dependent on the players choice after an event occurs. + * Essentially calls the event callback function with the ship and party stats. + * Returns a string dictating what occurred as a result of the choice. + * @param {*} event + * @param {*} ship + * @param {*} party + */ +function handle_event(event, choice_num, ship, party){ + var result = event.choices[choice_num][1](ship, party); + return result; +} + +function test_events(){ + // All tests currently pass... will quickly develop validator tomorrow... + + // Created a test event with choices + var event1 = new SpaceEvent("Testing in progress...", "The captain would like to know if this test was successful...", [["Yes",function(ship, party){return "Hooray!"}], ["No",function(ship, party){return "Welp..."}]]); + events.push(event1); + // Should return the event1 object literal + console.log("Testing draw event method...") + console.log(get_event()); + // Should return ["Yes", "No"] + console.log("Testing get_choices method..."); + console.log(get_choices(event1)); + // Should return "Horray!" and then "Welp" + console.log("Testing handle event method...") + var e = get_event(); + console.log(handle_event(e, 0)); + console.log(handle_event(e, 1)); +}
\ No newline at end of file diff --git a/src/cosmic-cargo/static/index.html b/src/cosmic-cargo/static/index.html new file mode 100644 index 0000000..6f0b64e --- /dev/null +++ b/src/cosmic-cargo/static/index.html @@ -0,0 +1,56 @@ +<!doctype html> +<html lang="en"> + +<head> + <title>Cosmic Cargo</title> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> + <style> + @font-face { + font-family: 'Gameboy'; + src: url('Assets/font.ttf'); + } + canvas { + transform-origin: top left; + transform: scale(4, 4); + position: absolute; + left: 0; + top: 0; + } + .desc{ + position: relative; + left: 650px; + width: 500px; + } + </style> +</head> + +<body style="padding:0; margin:0; overflow:hidden;"> + <canvas id="canvas"></canvas> + <script src="audio.js"></script> + <script src="events.js"></script> + <script src="shop.js"></script> + <script src="random.js"></script> + <script src="draw.js"></script> + <script src="main.js"></script> + <script src="/highscore.js"></script> + +<div class="desc"> +<p> +Welcome to Cosmic Cargo! A galactic-scale story generator! Embark on an interplanetary journey where you are tasked to deliver a shipment of goods to a new colony 3000 light years away. Your trip is dangerous, watch out for pirates, dangerous aliens, space anomalies, and more! +Made for #GBJAM 7. This is the first game any of us in our team have made before. We hope you enjoy! +</p> +<p> +Controls: +<ul> + <li>Arrow keys - Navigate menu items, change volume</li> + <li>Z - Select menu option</li> + <li>X - Toggle status screen (from main screen)</li> + <li>Space/Enter - Toggle options screen (from main screen)</li> +</ul> +</p> +<p><a href="https://seafarerscafe.itch.io/cosmic-cargo">view on itch.io</a></p> +</div> +</body> + +</html>
\ No newline at end of file diff --git a/src/cosmic-cargo/static/main.js b/src/cosmic-cargo/static/main.js new file mode 100644 index 0000000..8fb40be --- /dev/null +++ b/src/cosmic-cargo/static/main.js @@ -0,0 +1,479 @@ +var gameInterval, canvas, ctx, width, height; +var t, gameState, ship, party, images, imagesLoaded, + currentEvent, lastEventDay, + selectedChoice, doAction, eventResult, doneShopping, shopResult, talk_count, talked, submittedScore; +var FPS = 10; +window.onload = function () { + canvas = document.getElementById("canvas"); + ctx = canvas.getContext("2d"); + document.addEventListener("keydown", keyPush); + window.addEventListener('resize', resizeCanvas, false); + window.addEventListener('orientationchange', resizeCanvas, false); + + var imagesArray = loadImages(["Assets/Ship_1.png", "Assets/Ship_2.png", "Assets/Ship_Destroyed.png", "Assets/Map.png"]) + images = { + "ship1": imagesArray[0], + "ship2": imagesArray[1], + "shipDestroyed": imagesArray[2], + "map": imagesArray[3] + }; + init(); + resizeCanvas(); +} +function resizeCanvas() { + canvas.width = width; + canvas.height = height; +} +function init() { + submittedScore = false; + t = 0 // The frame of the game (time basically) + lastEventT = 0 + width = 160 + height = 144 + lastEventDay = 1 + gameState = "title"; + gameInterval = setInterval(game, 1000 / FPS); + doneShopping = false; + ship = { + fuel: 100, + cargo: 50, + credits: 1000, + distance: 0, + end_distance: 3000, + speed: 10, // How many lightyears traveled in a day + current_day: 1, + next_shop: 1, + // hostiles / asteroid + next_zone: 0, + rate: 2.5 + } + party = createParty(6); + + for(var song of music){ + audio[song].volume = 0.4 + } + for(var song of sfx){ + audio[song].volume = 0.1 + } + generate_events(); + + // If Karen is in the Party add the 'Rouge Karen' event + for (i = 0; i < party.length; i++){ + if (party[i].name == "Karen"){ + events.push(return_karen()); + } + } + + talk_count = 0; +} + +function createParty(size){ + party = []; + for(var i = 0; i < size; i++){ + party.push({ + name: getRandomName(), + status: getStatus(""), + }); + } + return party; +} +var randomNames; +var randomNameIndex = 0; +function getRandomName(){ + if(randomNames == undefined){ + randomNames = [ + "Mark", + "Matt", + "Bryce", + "Tanner", + "Shawn", + "Karen", + "Marge", + "Scoob", + "Ice Man", + "Thunder", + "Bandit", + "Rocky", + "Foxworthy", + "Cable Guy", + "Gus", + "Turntable", + "Hooch", + "Bugeyes", + "Kitty Kat", + "Hollywood", + "Skeeter", + "Archimedes", + "Ace", + "Doc", + "Flying Hog", + "Fatcat", + "Twitchy", + "Stank", + "Wagon Wheel", + "Ford", + "Bandit", + "The Gambler", + "Gygax", + "Janet", + "Jill", + "Stephanie", + "Albino Rhino", + "Rose", + "Martha", + "Ohio Queen", + "Charlie", + "Frank", + "Dale", + "Hank", + "Beatrice", + "Roxanne", + "Beatsy", + "Caroline", + "Mitch", + "Gerty", + "Ma'am", + "Smokey", + "Hoss", + "Bonney", + "Clyde", + "Sundace", + "Butch", + "Big Dunc", + "Chad", + "Diego", + "Slim-Jim", + "Dick", + "Rasputin", + "Gaston", + "Short Round", + "Long Schlong", + "Curtis", + "Egg", + ]; + shuffle_array(randomNames); + } + return randomNames[randomNameIndex++]; +} +function getAliveMembers(){ + return party.filter( el => (el.status != "Dead" && el.status != "Missing")); +} +function getStatus(currentStatus, delta = undefined){ + let statuses = { + "": { // Unset status just make good + "up": "Good", + "down": "Good" + }, + "Good": { + "up": "Good", + "down": "Dire" + }, + "Dire": { + "up": "Good", + "down": "Dead" + }, + "Dead": { + "up": "Dead", + "down": "Dead" + }, + }; + if(delta == undefined){ // Initial + return "Good" + } else if(delta == 0){ + return currentStatus; + } else if(delta > 0){ + return statuses[currentStatus]["up"]; + } + else if(delta < 0){ + return statuses[currentStatus]["down"]; + } +} + +function game() { + update(); + draw(); +} + +function update() { + if (ship.speed < 0){ + ship.speed = 0; + } + + switch(gameState){ + case "main": + t++; + if(getAliveMembers().length == 0 || ship.fuel <= 0){ + gameState = "gameover" + play_audio("alert"); + pause_audio("bgm"); + pause_audio("shop"); + pause_audio("encounter"); + play_audio("gameover", true); + return; + } + + ship.distance++; + if(ship.distance >= (ship.end_distance - 1) && karen_finale){ + currentEvent = karen_requiem(); + selectedChoice = 0; + doAction = false; + eventResult = undefined; + gameState = "event"; + pause_audio("bgm"); + play_audio("alert"); + play_audio("encounter", true); + karen_finale = false; + return; + } + + if(ship.distance >= ship.end_distance){ + gameState = "win" + play_audio("alert"); + pause_audio("bgm"); + pause_audio("shop"); + pause_audio("encounter"); + play_audio("endgame", true); + return; + } + + + if(t % ship.speed == 0){ + ship.current_day++; + // Should run out of fuel around 176 days + if(ship.current_day % 3 == 0){ + ship.fuel -= ship.rate; + } + // Check for event for today + if(lastEventDay + 3 < ship.current_day){ + // chance any day 3 days after last event will be another event + if(random_chance(0.15)){ + lastEventDay = ship.current_day; + currentEvent = get_event(); + selectedChoice = 0; + doAction = false; + eventResult = undefined; + gameState = "event"; + pause_audio("bgm"); + play_audio("alert"); + play_audio("encounter", true); + return + } + } + // Check for shop day + let progress = ship.distance/ship.end_distance + if(progress > ship.next_shop * .25){ + ship.next_shop++; + currentShop = new Shop(ship); + selectedChoice = 0; + doAction = false; + doneShopping = false; + shopResult = undefined; + gameState = "shop"; + pause_audio("bgm"); + play_audio("alert"); + play_audio("shop", true); + return + } else if(progress > ship.next_zone * .5 + .36){ + if(ship.next_zone == 0){ + currentEvent = hostiles_event() + } else { + currentEvent = asteroid_event() + } + lastEventDay = ship.current_day; + ship.next_zone++; + selectedChoice = 0; + doAction = false; + eventResult = undefined; + gameState = "event"; + play_audio("alert"); + return + } + } + break; + case "event": + if(doAction){ + eventResult = handle_event(currentEvent, selectedChoice, ship, party); + gameState = "event_result"; + } + break; + case "shop": + if(doAction){ // TODO: Refactor this into method within shop.js + if (selectedChoice == 0){ + shopResult = purchase_fuel(ship, currentShop); + gameState = "shop_result"; + } + else if (selectedChoice == 1){ + shopResult = fix_engine(ship, currentShop); + gameState = "shop_result"; + } + else if (selectedChoice == 2){ + shopResult = truck_talk(currentShop); + talked = true; + gameState = "shop_result"; + } + else if (selectedChoice == 3){ + shopResult = leave(); + doneShopping = true; + + if (talked){ + talk_count += 1; + } + + talked = false; + + gameState = "shop_result"; + } + } + break; + case "gameover": + clearInterval(gameInterval); + break; + } +} +function keyPush(e) { + if (gameState == "gameover" || gameState == "win") { + gameState = "title"; + pause_audio("gameover"); + pause_audio("endgame"); + init(); + return; + } + + e.preventDefault() + if(imagesLoaded && gameState == "title") { + play_audio("select"); + play_audio("bgm", true); + gameState = "setup"; + return; + } else if(gameState == "setup" || gameState == "event_result"){ + pause_audio("encounter"); + play_audio("select"); + play_audio("bgm", true); + gameState = "main"; + return; + } else if (gameState == "shop_result"){ + play_audio("select"); + if (doneShopping == true){ + gameState = "main"; + pause_audio("shop"); + play_audio("bgm", true); + return; + } + gameState = "shop"; + selectedChoice = 0; + doAction = false; + shopResult = undefined; + return; + } + + switch (e.keyCode) { + case 38: // up + if(gameState == "event"){ + selectedChoice--; + if(selectedChoice < 0){ + selectedChoice = get_choices(currentEvent).length-1; + } + play_audio("move"); + } + else if(gameState == "shop"){ + selectedChoice--; + if(selectedChoice < 0){ + selectedChoice = shop_choices(currentShop).length-1; + } + play_audio("move"); + } else if(gameState == "options"){ + if(selectedChoice == 1){ + selectedChoice = 0; + } else { + selectedChoice = 1 + } + play_audio("move"); + } + break; + case 40: // down + if(gameState == "event"){ + selectedChoice++; + if(selectedChoice >= get_choices(currentEvent).length){ + selectedChoice = 0; + } + play_audio("move"); + } + else if(gameState == "shop"){ + selectedChoice++; + if(selectedChoice >= shop_choices(currentShop).length) { + selectedChoice = 0; + } + play_audio("move"); + } else if(gameState == "options"){ + if(selectedChoice == 1){ + selectedChoice = 0; + } else { + selectedChoice = 1 + } + play_audio("move"); + } + break; + case 39: // right + if(gameState == "options"){ + if(selectedChoice == 0){ + var music_volume = audio[music[0]].volume + music_volume = Math.min(1, music_volume + .05).toFixed(2) + for(var song of music){ + audio[song].volume = music_volume + } + } else { + var sfx_volume = audio[sfx[0]].volume + sfx_volume = Math.min(1, sfx_volume + .05).toFixed(2) + for(var song of sfx){ + audio[song].volume = sfx_volume + } + play_audio("move"); + } + } + break; + case 37: // left + if(gameState == "options"){ + if(selectedChoice == 0){ + var music_volume = audio[music[0]].volume + music_volume = Math.max(0, music_volume - .05).toFixed(2) + for(var song of music){ + audio[song].volume = music_volume + } + } else { + var sfx_volume = audio[sfx[0]].volume + sfx_volume = Math.max(0, sfx_volume - .05).toFixed(2) + for(var song of sfx){ + audio[song].volume = sfx_volume + } + play_audio("move"); + } + } + break; + case 90: // z + if(gameState == "event" || gameState == "shop"){ + doAction = true; + play_audio("select"); + } + break; + case 88: // x + if(gameState == "status"){ + gameState = "main"; + play_audio("select"); + } else if(gameState == "main"){ + gameState = "status"; + play_audio("select"); + } + break; + case 32: // space + case 13: // enter + if(gameState == "options"){ + gameState = "main"; + play_audio("select"); + } else if(gameState == "main"){ + selectedChoice = 0; + gameState = "options"; + play_audio("select"); + } + break; + } +} diff --git a/src/cosmic-cargo/static/random.js b/src/cosmic-cargo/static/random.js new file mode 100644 index 0000000..152db0a --- /dev/null +++ b/src/cosmic-cargo/static/random.js @@ -0,0 +1,18 @@ +function random_choice(arr){ + return arr[random_int(arr.length)]; +} + +function random_int(max) { + return Math.floor(Math.random() * max); +} + +function random_chance(percentage_of_success){ + return Math.random() < percentage_of_success; +} + +function shuffle_array(array) { + for (let i = array.length - 1; i > 0; i--) { + const j = random_int(i + 1); + [array[i], array[j]] = [array[j], array[i]]; + } +}
\ No newline at end of file diff --git a/src/cosmic-cargo/static/shop.js b/src/cosmic-cargo/static/shop.js new file mode 100644 index 0000000..cec84a3 --- /dev/null +++ b/src/cosmic-cargo/static/shop.js @@ -0,0 +1,111 @@ +var names = ["Johnny's", "Clarney's", "The Mysterious", "The Belt-line", "Astro's", "Solar Star's", "Randal's", "Mitchard's"]; +var descriptors = ["Galactic Truck Stop!", "World of Wonders!", "Gas and Grill", "Land of the Lost!", "Sunken Astroid", "Spittoon", "Diner and More!", "Pool Party", "Restaurant at the End of the Universe"]; +var trucker_talk = ["I would be wary of pirates around these parts. Good folk have been getting robbed as of late.", + "You new around here? Haven't seen you around these parts.", + "I used to be a trucker like you, until I took a torpedo to the wing.", + "It's a harsh life out in space. Never know what day might be your last out here.", + "How do you do? I'm the owner of this here establishment. Good to see a friendly face.", + "Beware of people named Karen. Maybe you'll understand when you're old like me. Ha!", + "My darn engine keeps acting up on me. Who makes these things anyway?", + "Look kid, are you going to buy something?", + "Hackers are nerdy, pasty, tubby, little geeks with triple thick glasses... Should be easy to catch if you're looking for one", + "Buzz off.", + "Boy you sure come accross some crazy stuff out there on the road, eh?", + "If a reptilian woman approaches you while you're here, just pretend like you can't understand her. I promise you it's for your own good.", + "Life on the interstellar highway sure is lonely. I miss my cat.", + "Can you believe this place? No rooms available for ole Rusty eh? Fine! I'll go make my own truck stop, with blackjack and hookers! In fact, forget the truck stop and the blackjack..", + "Those Doliks mean business. They won't hesistate to murder people in cold blood if they're in a bad mood.", + "I coulda swore I saw a red convertible drift past me the other day. My old eyes must be playing tricks on me!", + "What, so everyone's suppose to sleep every single night now? You realize that sleeping takes up half of all time?", + "Damn cops are all over the place out there. Make sure things are on the up and up if you see 'em snooping around.", + "Space age technology and we're still clinging to our CB radios. Heh. Imagine that.", + "Hey we got a rule around these parts! The new guy pays first round!"] + +/** + * Creates a shop object with a randomized name and unit prices for each resource + * @Constructor + */ +function Shop(ship){ + this.name = generate_name(); + this.fuel_price = Math.floor(calculate_price(2, 4, 1) * .9* (100 - Math.floor(ship.fuel))); + this.engine_price = calculate_price(3, 5, 1) * 10 * (10 - ship.speed); + this.message = generate_trucker_message(); +} + +/** + * Generates a fun name for the shop object + */ +function generate_name(){ + let name = names[Math.floor(Math.random() * names.length)]; + let desc = descriptors[Math.floor(Math.random() * descriptors.length)]; + return name + " " + desc; +} + +/** + * Calculates out a semi-random value for the price of 1 unit of a resource + * @param {int} min - The minimum value of the resource + * @param {int} max - The maximum value of the resource + * @param {int} modifier - A value multiplied against a random value and added to the unit price + */ +function calculate_price(min, max, modifier){ + return Math.floor((Math.random() * (max - min + 1) + min) + (Math.random() * modifier)); +} + +/** + * Randomly chooses the 'talk' option message for this stop + */ +function generate_trucker_message(){ + return random_choice(trucker_talk); +} + +/** + * If the current credits owned by the player is greater than the cost, subtract + * the cost from the credits and return true, otherwise return false + * @param {Ship} ship - The Ship object which contains a reference to credits + * @param {int} cost - The total cost as calculated through the calculate_cost method + */ +function purchase_fuel(ship, shop){ + if (ship.fuel == 100){ + return "Your ship is currently full of fuel." + } + + if (ship.credits >= shop.fuel_price){ + ship.credits -= shop.fuel_price; + ship.fuel = 100; + return `You refuel your ship. You have ${ship.credits} credits remaining!`; + } + return "You do not have enough credits to refuel your ship!"; +} + +function fix_engine(ship, shop){ + if (ship.speed == 10 && ship.rate == 2.5){ + return "Your engines are all fine." + } + + if (ship.credits >= shop.engine_price){ + ship.credits -= shop.engine_price; + ship.speed = 10; + ship.rate = 2.5; + return `You fix your engines. You have ${ship.credits} credits remaining!`; + } + return "You do not have enough credits to repair your engines!"; +} + +/** + * Flavor text of talking with a trucker at this shop + * @param {Shop} shop - The current shop the player is at + */ +function truck_talk(shop){ + return shop.message; +} + +/** + * The player leaves the shop + */ +function leave(){ + return "You leave the shop."; +} + +function shop_choices(shop){ + return [`Refuel )${shop.fuel_price} credits)`, `Repair )${shop.engine_price} Credits)`, "Talk", "Leave"]; +}
\ No newline at end of file |