let world = null; let currentRoom = null; let currentDialog = null; let currentFoe = null; let dirButtons = []; let mode = "explore"; let actions = []; let time = 9*60*60; //time is calculated in seconds let date = 1; let newline = " "; let player = new Player(); let playerAttacks = []; let struggles = []; let killingBlow = null; let deaths = []; let respawnRoom; let noLog = false; let logPadLine = undefined; let MIDNIGHT = 0; let MORNING = 21600; let NOON = 43200; let EVENING = 64800; function toggleLog() { noLog = !noLog; if (noLog) { document.getElementById("log-button").innerHTML = "Log: Disabled"; } else { document.getElementById("log-button").innerHTML = "Log: Enabled"; } } function join(things) { if (things.length == 1) { return things[0].description("a"); } else if (things.length == 2) { return things[0].description("a") + " and " + things[1].description("a"); } else { let line = ""; line = things.slice(0,-1).reduce((line, prey) => line + prey.description("a") + ", ", line); line += " and " + things[things.length-1].description("a"); return line; } } function pickRandom(list) { return list[Math.floor(Math.random() * list.length)]; } function pick(list, attacker, defender) { if (list.length == 0) return null; else { let sum = list.reduce((sum, choice) => choice.weight == undefined ? sum + 1 : sum + choice.weight(attacker, defender), 0); let target = Math.random() * sum; for (let i = 0; i < list.length; i++) { sum -= list[i].weight == undefined ? 1 : list[i].weight(attacker, defender); if (sum <= target) { return list[i]; } } return list[list.length-1]; } } function filterValid(options, attacker, defender) { let filtered = options.filter(option => option.conditions == undefined || option.conditions.reduce((result, test) => result && test(attacker, defender), true)); return filtered.filter(option => option.requirements == undefined || option.requirements.reduce((result, test) => result && test(attacker, defender), true)); } function filterPriority(options) { let max = options.reduce((max, option) => option.priority > max ? option.priority : max, -1000); return options.filter(option => option.priority == max); } function round(number, digits) { return Math.round(number * Math.pow(10,digits)) / Math.pow(10,digits); } function updateExploreCompass() { for (let i = 0; i < dirButtons.length; i++) { let button = dirButtons[i]; button.classList.remove("active-button"); button.classList.remove("inactive-button"); button.classList.remove("disabled-button"); if (currentRoom.exits[i] == null) { button.disabled = true; button.classList.add("inactive-button"); button.innerHTML = ""; } else { if (currentRoom.exits[i].conditions.reduce((result, test) => result && test(player.prefs), true)) { button.disabled = false; button.classList.add("active-button"); button.innerHTML = currentRoom.exits[i].name; } else { button.disabled = true; button.classList.add("disabled-button"); button.innerHTML = currentRoom.exits[i].name; } } } } function updateExploreActions() { updateActions(); let actionButtons = document.querySelector("#actions"); actionButtons.innerHTML = ""; for (let i = 0; i < actions.length; i++) { let button = document.createElement("button"); button.innerHTML = actions[i].name; button.classList.add("active-button"); button.classList.add("action-button"); button.addEventListener("click", function() { actions[i].action(); }); actionButtons.appendChild(button); } } function updateExplore() { updateExploreCompass(); updateExploreActions(); } function updateEaten() { let list = document.getElementById("eaten"); while(list.firstChild) { list.removeChild(list.firstChild); } if (player.health > 0) struggles = filterValid(currentFoe.struggles, currentFoe, player); else struggles = [submit(currentFoe)]; for (let i = 0; i < struggles.length; i++) { let li = document.createElement("li"); let button = document.createElement("button"); button.classList.add("eaten-button"); button.innerHTML = struggles[i].name; button.addEventListener("click", function() { struggleClicked(i); } ); button.addEventListener("mouseover", function() { struggleHovered(i); } ); button.addEventListener("mouseout", function() { document.getElementById("eaten-desc").innerHTML = ""; } ); li.appendChild(button); list.appendChild(li); } } function updateCombat() { let list = document.getElementById("combat"); while(list.firstChild) { list.removeChild(list.firstChild); } if (player.health > 0) if (currentFoe.playerAttacks == undefined) playerAttacks = filterValid(player.attacks, player, currentFoe); else playerAttacks = filterValid(currentFoe.playerAttacks.map(attack => attack(player)), player, currentFoe); else playerAttacks = [pass(player)]; if (playerAttacks.length == 0) playerAttacks = [player.backupAttack]; for (let i = 0; i < playerAttacks.length; i++) { let li = document.createElement("li"); let button = document.createElement("button"); button.classList.add("combat-button"); button.innerHTML = playerAttacks[i].name; button.addEventListener("click", function() { attackClicked(i); } ); button.addEventListener("mouseover", function() { attackHovered(i); } ); button.addEventListener("mouseout", function() { document.getElementById("combat-desc").innerHTML = ""; } ); li.appendChild(button); list.appendChild(li); } document.getElementById("stat-foe-name").innerHTML = "Name: " + currentFoe.name; document.getElementById("stat-foe-health").innerHTML = "Health: " + currentFoe.health + "/" + currentFoe.maxHealth; document.getElementById("stat-foe-stamina").innerHTML = "Stamina: " + currentFoe.stamina + "/" + currentFoe.maxStamina; document.getElementById("stat-foe-str").innerHTML = "Str: " + currentFoe.str; document.getElementById("stat-foe-dex").innerHTML = "Dex: " + currentFoe.dex; document.getElementById("stat-foe-con").innerHTML = "Con: " + currentFoe.con; } function updateDialog() { let list = document.getElementById("dialog"); while(list.firstChild) { list.removeChild(list.firstChild); } for (let i = 0; i < currentDialog.choices.length; i++) { let activated = currentDialog.choices[i].node.requirements == undefined || currentDialog.choices[i].node.requirements.reduce((result, test) => result && test(player, currentFoe), true); let li = document.createElement("li"); let button = document.createElement("button"); button.classList.add("dialog-button"); button.innerHTML = currentDialog.choices[i].text; button.addEventListener("click", function() { dialogClicked(i); }); if (!activated) { button.classList.add("disabled-button"); button.disabled = true; } li.appendChild(button); list.appendChild(li); } } function updateDisplay() { document.querySelectorAll(".selector").forEach(function (x) { x.style.display = "none"; }); switch(mode) { case "explore": document.getElementById("selector-explore").style.display = "flex"; updateExplore(); break; case "combat": document.getElementById("selector-combat").style.display = "flex"; updateCombat(); break; case "dialog": document.getElementById("selector-dialog").style.display = "flex"; updateDialog(); break; case "eaten": document.getElementById("selector-eaten").style.display = "flex"; updateEaten(); break; } document.getElementById("time").innerHTML = "Time: " + renderTime(time); document.getElementById("date").innerHTML = "Day " + date; document.getElementById("stat-name").innerHTML = "Name: " + player.name; document.getElementById("stat-health").innerHTML = "Health: " + round(player.health,0) + "/" + round(player.maxHealth,0); document.getElementById("stat-cash").innerHTML = "Cash: $" + round(player.cash,0); document.getElementById("stat-stamina").innerHTML = "Stamina: " + round(player.stamina,0) + "/" + round(player.maxStamina,0); document.getElementById("stat-str").innerHTML = "Str: " + player.str; document.getElementById("stat-dex").innerHTML = "Dex: " + player.dex; document.getElementById("stat-con").innerHTML = "Con: " + player.con; document.getElementById("stat-arousal").innerHTML = "Arousal: " + round(player.arousal,0) + "/" + round(player.arousalLimit(),0); document.getElementById("stat-stomach").innerHTML = "Stomach: " + round(player.stomach.fullness(),0) + "/" + round(player.stomach.capacity, 0); if (player.prefs.pred.anal || player.prefs.scat) document.getElementById("stat-bowels").innerHTML = "Bowels: " + round(player.bowels.fullness(),0) + "/" + round(player.bowels.capacity, 0); else document.getElementById("stat-bowels").innerHTML = ""; if (player.prefs.pred.cock) document.getElementById("stat-balls").innerHTML = "Balls: " + round(player.balls.fullness(),0) + "/" + round(player.balls.capacity, 0); else document.getElementById("stat-balls").innerHTML = ""; if (player.prefs.pred.unbirth) document.getElementById("stat-womb").innerHTML = "Womb: " + round(player.womb.fullness(),0) + "/" + round(player.womb.capacity, 0); else document.getElementById("stat-womb").innerHTML = ""; if (player.prefs.pred.breast) document.getElementById("stat-breasts").innerHTML = "Breasts: " + round(player.breasts.fullness(),0) + "/" + round(player.breasts.capacity, 0); else document.getElementById("stat-breasts").innerHTML = ""; } function advanceTimeTo(newTime, conscious=true) { advanceTime((86400 + newTime - time) % 86400, conscious); } function advanceTime(amount, conscious=true) { time = (time + amount); date += Math.floor(time / 86400); time = time % 86400; player.restoreHealth(amount); player.restoreStamina(amount); update(player.stomach.digest(amount)); update(player.bowels.digest(amount)); update(player.balls.digest(amount)); update(player.womb.digest(amount)); update(player.breasts.digest(amount)); stretchOrgans(amount); if (conscious) { update(player.buildArousal(amount)); } } function renderTime(time) { let suffix = (time < 43200) ? "AM" : "PM"; let hour = Math.floor((time % 43200) / 3600); if (hour == 0) hour = 12; let minute = Math.floor(time / 60) % 60; if (minute < 9) minute = "0" + minute; return hour + ":" + minute + " " + suffix; } function move(direction) { if (noLog) clearScreen(); else clearLogBreak(); let target = currentRoom.exits[direction]; if (target == null) { alert("Tried to move to an empty room!"); return; } moveTo(target,currentRoom.exitDescs[direction]); } function updateActions() { actions = []; currentRoom.objects.forEach(function (object) { object.actions.forEach(function (action) { if (action.conditions == undefined || action.conditions.reduce((result, cond) => result && cond(player), true)) actions.push(action); }); }); } function moveToByName(roomName, desc="You go places lol", loading=false) { moveTo(world[roomName], desc, loading); } function moveTo(room,desc="You go places lol", loading=false) { currentRoom = room; if (!loading) advanceTime(30); update([desc,newline]); currentRoom.visit(); updateDisplay(); } function next_step(stage) { document.querySelector("#character-step-" + (stage - 1)).style.display = "none"; document.querySelector("#character-step-" + stage).style.display = "block"; } window.addEventListener('load', function(event) { document.getElementById("character-step-1-next").addEventListener("click", function() { next_step(2); }); document.getElementById("character-load").addEventListener("click", startLoaded, false); document.getElementById("start-button").addEventListener("click", start, false); }); function start() { applySettings(generateSettings()); transformVorePrefs(player.prefs); document.getElementById("create").style.display = "none"; document.getElementById("game").style.display = "block"; document.getElementById("stat-button-status").addEventListener("click", status, false); document.getElementById("log-button").addEventListener("click", toggleLog, false); document.getElementById("load-button").addEventListener("click", loadGameButton, false); document.getElementById("save-button").addEventListener("click", saveGameButton, false); loadCompass(); loadDialog(); setupStrechableOrgans(); world = createWorld(); currentRoom = world["Bedroom"]; respawnRoom = currentRoom; update(new Array(50).fill(newline)); update(["Welcome to Feast."]); moveTo(currentRoom,""); updateDisplay(); } // copied from Stroll LUL function generateSettings() { let form = document.forms.namedItem("character-form"); let settings = {}; for (let i=0; i= 1; if (player.prefs.pred == undefined) player.prefs.pred = {}; player.prefs.pred[tokens[1]] = settings[key] >= 2; } } } } // turn things like "1" into a number function transformVorePrefs(prefs) { let prey = false; for (let key in prefs.vore) { if (prefs.vore.hasOwnProperty(key)) { switch(prefs.vore[key]) { case "0": prefs.vore[key] = 0; break; case "1": prefs.vore[key] = 0.5; prey = true; break; case "2": prefs.vore[key] = 1; prey = true; break; case "3": prefs.vore[key] = 2; prey = true; break; } } } prefs.prey = prey; return prefs; } function saveSettings() { window.localStorage.setItem("settings", JSON.stringify(generateSettings())); } function retrieveSettings() { return JSON.parse(window.localStorage.getItem("settings")); } function clearScreen() { let log = document.getElementById("log"); let child = log.firstChild; while (child != null) { log.removeChild(child); child = log.firstChild; } } function update(lines=[]) { let log = document.getElementById("log"); for (let i=0; i 0) update(status.concat([newline])); } } } function attackHovered(index) { document.getElementById("combat-desc").innerHTML = playerAttacks[index].desc; } function struggleClicked(index) { if (noLog) clearScreen(); else clearLogBreak(); let struggle = struggles[index]; let result = struggle.struggle(player); update(result.lines.concat([newline])); if (result.escape == "stay") { changeMode("combat"); } else if (result.escape == "escape") { changeMode("explore"); } else { let digest = pick(filterValid(currentFoe.digests, currentFoe, player), currentFoe, player); if (digest == null) { digest = currentFoe.backupDigest; } update(digest.digest(player).concat([newline])); if (player.health <= -100) { killingBlow = digest; update(currentFoe.finishDigest().concat([newline])); respawn(respawnRoom); } } } function struggleHovered(index) { document.getElementById("eaten-desc").innerHTML = currentFoe.struggles[index].desc; } function startDialog(dialog) { if (noLog) clearScreen(); else clearLogBreak(); currentDialog = dialog; changeMode("dialog"); update(currentDialog.text.concat([newline])); currentDialog.visit(); updateDisplay(); } function dialogClicked(index) { currentDialog = currentDialog.choices[index].node; update(currentDialog.text.concat([newline])); currentDialog.visit(); if (currentDialog.choices.length == 0 && mode == "dialog") { changeMode("explore"); updateDisplay(); } } function loadDialog() { dialogButtons = Array.from( document.querySelectorAll(".dialog-button")); for (let i = 0; i < dialogButtons.length; i++) { dialogButtons[i].addEventListener("click", function() { dialogClicked(i); }); } } function loadCompass() { dirButtons[NORTH_WEST] = document.getElementById("compass-north-west"); dirButtons[NORTH_WEST].addEventListener("click", function() { move(NORTH_WEST); }); dirButtons[NORTH] = document.getElementById("compass-north"); dirButtons[NORTH].addEventListener("click", function() { move(NORTH); }); dirButtons[NORTH_EAST] = document.getElementById("compass-north-east"); dirButtons[NORTH_EAST].addEventListener("click", function() { move(NORTH_EAST); }); dirButtons[WEST] = document.getElementById("compass-west"); dirButtons[WEST].addEventListener("click", function() { move(WEST); }); dirButtons[EAST] = document.getElementById("compass-east"); dirButtons[EAST].addEventListener("click", function() { move(EAST); }); dirButtons[SOUTH_WEST] = document.getElementById("compass-south-west"); dirButtons[SOUTH_WEST].addEventListener("click", function() { move(SOUTH_WEST); }); dirButtons[SOUTH] = document.getElementById("compass-south"); dirButtons[SOUTH].addEventListener("click", function() { move(SOUTH); }); dirButtons[SOUTH_EAST] = document.getElementById("compass-south-east"); dirButtons[SOUTH_EAST].addEventListener("click", function() { move(SOUTH_EAST); }); document.getElementById("compass-look").addEventListener("click", look, false); } function look() { update([currentRoom.description]); } function status() { let lines = []; lines.push("You are a " + player.species); lines.push(newline); if (player.stomach.contents.length > 0) { lines.push("Your stomach bulges with prey."); player.stomach.contents.map(function(prey) { let state = ""; let healthRatio = prey.health / prey.maxHealth; if (healthRatio > 0.75) { state = "is thrashing in your gut"; } else if (healthRatio > 0.5) { state = "is squirming in your belly"; } else if (healthRatio > 0.25) { state = "is pressing out at your stomach walls"; } else if (healthRatio > 0) { state = "is weakly squirming"; } else { state = "has stopped moving"; } lines.push(prey.description("A") + " " + state); }); lines.push(newline); } if (player.bowels.contents.length > 0) { lines.push("Your bowels churn with prey."); player.bowels.contents.map(function(prey) { let state = ""; let healthRatio = prey.health / prey.maxHealth; if (healthRatio > 0.75) { state = "is writhing in your bowels"; } else if (healthRatio > 0.5) { state = "is struggling against your intestines"; } else if (healthRatio > 0.25) { state = "is bulging out of your lower belly"; } else if (healthRatio > 0) { state = "is squirming weakly, slipping deeper and deeper"; } else { state = "has succumbed to your bowels"; } lines.push(prey.description("A") + " " + state); }); lines.push(newline); } if (player.parts.cock) { if (player.balls.contents.length > 0) { lines.push("Your balls are bulging with prey."); player.balls.contents.map(function(prey) { let state = ""; let healthRatio = prey.health / prey.maxHealth; if (healthRatio > 0.75) { state = "is writhing in your sac"; } else if (healthRatio > 0.5) { state = "is struggling in a pool of cum"; } else if (healthRatio > 0.25) { state = "is starting to turn soft"; } else if (healthRatio > 0) { state = "is barely visible anymore"; } else { state = "has succumbed to your balls"; } lines.push(prey.description("A") + " " + state); }); lines.push(newline); } else { if (player.balls.waste > 0) { lines.push("Your balls are heavy with cum."); lines.push(newline); } } } if (player.parts.unbirth) { if (player.womb.contents.length > 0) { lines.push("Your slit drips, hinting at prey trapped within."); player.womb.contents.map(function(prey) { let state = ""; let healthRatio = prey.health / prey.maxHealth; if (healthRatio > 0.75) { state = "is thrashing in your womb"; } else if (healthRatio > 0.5) { state = "is pressing out inside your lower belly"; } else if (healthRatio > 0.25) { state = "is still trying to escape"; } else if (healthRatio > 0) { state = "is barely moving"; } else { state = "is dissolving into femcum"; } lines.push(prey.description("A") + " " + state); }); lines.push(newline); } else { if (player.womb.waste > 0) { lines.push("Your slit drips, holding back a tide of femcum."); lines.push(newline); } } } if (player.parts.breast) { if (player.breasts.contents.length > 0) { lines.push("Your breasts are bulging with prey."); player.breasts.contents.map(function(prey) { let state = ""; let healthRatio = prey.health / prey.maxHealth; if (healthRatio > 0.75) { state = "is struggling to escape"; } else if (healthRatio > 0.5) { state = "is putting up a fight"; } else if (healthRatio > 0.25) { state = "is starting to weaken"; } else if (healthRatio > 0) { state = "is struggling to keep their head free of your milk"; } else { state = "has succumbed, swiftly melting into milk"; } lines.push(prey.description("A") + " " + state); }); lines.push(newline); } else { if (player.breasts.waste > 0) { lines.push("Your breasts slosh with milk."); lines.push(newline); } } } update(lines); } function checkOverfill(organ,returnValue=false, returnPercent=false){ let percentFilled = (round(player[organ].fullness(),0) / player[organ].capacity); if (returnValue == false){ if (percentFilled > 1){ return (true); }else{ return (false); } }else{ if (returnPercent == true){ return (round(player[organ].fullness(),0)); }else{ return (round(player[organ].fullness(),0) - player[organ].capacity); } } } var strechableOrgans = ["stomach","bowels","balls","womb","breasts"]; function setupStrechableOrgans(){ strechableOrgans = ["stomach"]; if (player.prefs.pred.anal || player.prefs.scat){ //these if conditions are copied from the if statements above that define if the stat menu shows stats for a set organ strechableOrgans.push("bowels"); }if (player.prefs.pred.cock){ strechableOrgans.push("balls"); }if (player.prefs.pred.unbirth){ strechableOrgans.push("womb"); }if (player.prefs.pred.breast){ strechableOrgans.push("breasts"); } } function stretchOrgans(time){ for (i=0; i= 0) { k = n; } else { k = len + n; if (k < 0) {k = 0;} } var currentElement; while (k < len) { currentElement = O[k]; if (searchElement === currentElement || (searchElement !== searchElement && currentElement !== currentElement)) { return true; } k++; } return false; }; }