| @@ -6,6 +6,14 @@ function attack(attacker, defender, baseDamage) { | |||||
| return damage; | return damage; | ||||
| } | } | ||||
| function isNormal(entity) { | |||||
| return entity.grappled != true; | |||||
| } | |||||
| function isGrappled(entity) { | |||||
| return entity.grappled == true; | |||||
| } | |||||
| function punchAttack(attacker) { | function punchAttack(attacker) { | ||||
| return { | return { | ||||
| name: "Punch", | name: "Punch", | ||||
| @@ -15,7 +23,9 @@ function punchAttack(attacker) { | |||||
| }, | }, | ||||
| attackPlayer: function(defender) { | attackPlayer: function(defender) { | ||||
| return "The " + attacker.description() + " punches you for " + attack(attacker, defender, attacker.str) + " damage"; | return "The " + attacker.description() + " punches you for " + attack(attacker, defender, attacker.str) + " damage"; | ||||
| } | |||||
| }, requirements: [ | |||||
| function(attacker, defender) { return isNormal(attacker) && isNormal(defender); } | |||||
| ] | |||||
| }; | }; | ||||
| } | } | ||||
| @@ -28,6 +38,92 @@ function flankAttack(attacker) { | |||||
| }, | }, | ||||
| attackPlayer: function(defender) { | attackPlayer: function(defender) { | ||||
| return "The " + attacker.description() + " runs past you, then turns and hits you for " + attack(attacker, defender, attacker.str) + " damage"; | return "The " + attacker.description() + " runs past you, then turns and hits you for " + attack(attacker, defender, attacker.str) + " damage"; | ||||
| }, requirements: [ | |||||
| function(attacker, defender) { return isNormal(attacker) && isNormal(defender); } | |||||
| ] | |||||
| }; | |||||
| } | |||||
| function grapple(attacker) { | |||||
| return { | |||||
| name: "Grapple", | |||||
| desc: "Try to grab your opponent", | |||||
| attack: function(defender) { | |||||
| let success = Math.random() < 0.5; | |||||
| if (success) { | |||||
| defender.grappled = true; | |||||
| return "You charge at the " + defender.description() + ", tackling them and knocking them to the ground."; | |||||
| } else { | |||||
| return "You charge at the " + defender.description() + ", but they dodge out of the way!"; | |||||
| } | |||||
| }, | |||||
| attackPlayer: function(defender) { | |||||
| let success = Math.random() < 0.5; | |||||
| if (success) { | |||||
| defender.grappled = true; | |||||
| return "The " + attacker.description() + " lunges at you, pinning you to the floor!"; | |||||
| } else { | |||||
| return "The " + attacker.description() + " tries to tackle you, but you deftly avoid them."; | |||||
| } | |||||
| }, | |||||
| requirements: [ | |||||
| function(attacker, defender) { return isNormal(attacker) && isNormal(defender); } | |||||
| ] | |||||
| }; | |||||
| } | |||||
| function grappleDevour(attacker) { | |||||
| return { | |||||
| name: "Devour", | |||||
| desc: "Try to consume your grappled opponent", | |||||
| attack: function(defender) { | |||||
| let success = Math.random() < 0.25 + (1 - defender.health / defender.maxHealth) * 0.75; | |||||
| if (success) { | |||||
| attacker.stomach.feed(defender); | |||||
| defender.grappled = false; | |||||
| changeMode("explore"); | |||||
| return "You open your jaws wide, stuffing the " + defender.description() + "'s head into your gullet and greedily wolfing them down. Delicious."; | |||||
| } else { | |||||
| return "Your jaws open wide, but the " + defender.description() + " manages to avoid becoming " + attacker.species + " chow."; | |||||
| } | |||||
| }, | |||||
| attackPlayer: function(defender) { | |||||
| let success = Math.random() < 0.5 + (1 - defender.health / defender.maxHealth)*0.5 && Math.random() < 0.5; | |||||
| if(success) { | |||||
| defender.grappled = false; | |||||
| changeMode("eaten"); | |||||
| return "The " + attacker.description() + " forces your head into their sloppy jaws, devouring you despite your frantic struggles. Glp."; | |||||
| } else { | |||||
| return "The " + attacker.description() + " tries to swallow you down, but you manage to resist their hunger."; | |||||
| } | |||||
| }, requirements: [ | |||||
| function(attacker, defender) { return isNormal(attacker) && isGrappled(defender); } | |||||
| ] | |||||
| }; | |||||
| } | |||||
| function grappleRelease(attacker) { | |||||
| return { | |||||
| name: "Release", | |||||
| desc: "Release your opponent", | |||||
| attack: function(defender) { | |||||
| defender.grappled = false; | |||||
| return "You throw the " + defender.description() + " back, dealing " + attack(attacker, defender, attacker.str*1.5) + " damage"; | |||||
| }, requirements: [ | |||||
| function(attacker, defender) { return isNormal(attacker) && isGrappled(defender); } | |||||
| ] | |||||
| }; | |||||
| } | |||||
| function pass(attacker) { | |||||
| return { | |||||
| name: "Pass", | |||||
| desc: "You can't do anything!", | |||||
| attack: function(defender) { | |||||
| return "You do nothing."; | |||||
| }, | |||||
| attackPlayer: function(defender) { | |||||
| return "The " + attacker.description() + " does nothing."; | |||||
| } | } | ||||
| }; | }; | ||||
| } | } | ||||
| @@ -46,7 +142,7 @@ function devourPlayer(attacker) { | |||||
| changeMode("eaten"); | changeMode("eaten"); | ||||
| return "The voracious " + attacker.description() + " pins you down and devours you in seconds."; | return "The voracious " + attacker.description() + " pins you down and devours you in seconds."; | ||||
| } | } | ||||
| } | |||||
| }; | |||||
| } | } | ||||
| function leer(attacker) { | function leer(attacker) { | ||||
| @@ -11,6 +11,7 @@ let time = 9*60*60; | |||||
| let newline = " "; | let newline = " "; | ||||
| let player = new Player(); | let player = new Player(); | ||||
| let playerAttacks = []; | |||||
| let respawnRoom; | let respawnRoom; | ||||
| @@ -20,6 +21,11 @@ let prefs = { | |||||
| } | } | ||||
| }; | }; | ||||
| function filterValid(options, attacker, defender) { | |||||
| let filtered = options.filter(option => option.conditions == undefined || option.conditions.reduce((result, test) => result && test(prefs), true)); | |||||
| return filtered.filter(option => option.requirements == undefined || option.requirements.reduce((result, test) => result && test(attacker, defender), true)); | |||||
| } | |||||
| function round(number, digits) { | function round(number, digits) { | ||||
| return Math.round(number * Math.pow(10,digits)) / Math.pow(10,digits); | return Math.round(number * Math.pow(10,digits)) / Math.pow(10,digits); | ||||
| } | } | ||||
| @@ -96,11 +102,16 @@ function updateCombat() { | |||||
| list.removeChild(list.firstChild); | list.removeChild(list.firstChild); | ||||
| } | } | ||||
| for (let i = 0; i < player.attacks.length; i++) { | |||||
| playerAttacks = filterValid(player.attacks, player, currentFoe); | |||||
| if (playerAttacks.length == 0) | |||||
| playerAttacks = [player.backupAttack]; | |||||
| for (let i = 0; i < playerAttacks.length; i++) { | |||||
| let li = document.createElement("li"); | let li = document.createElement("li"); | ||||
| let button = document.createElement("button"); | let button = document.createElement("button"); | ||||
| button.classList.add("combat-button"); | button.classList.add("combat-button"); | ||||
| button.innerHTML = player.attacks[i].name; | |||||
| button.innerHTML = playerAttacks[i].name; | |||||
| button.addEventListener("click", function() { attackClicked(i); } ); | button.addEventListener("click", function() { attackClicked(i); } ); | ||||
| button.addEventListener("mouseover", function() { attackHovered(i); } ); | button.addEventListener("mouseover", function() { attackHovered(i); } ); | ||||
| button.addEventListener("mouseout", function() { document.getElementById("combat-desc").innerHTML = ""; } ); | button.addEventListener("mouseout", function() { document.getElementById("combat-desc").innerHTML = ""; } ); | ||||
| @@ -248,6 +259,7 @@ function generateSettings() { | |||||
| function applySettings(settings) { | function applySettings(settings) { | ||||
| player.name = settings.name; | player.name = settings.name; | ||||
| player.species = settings.species; | |||||
| for (let key in settings) { | for (let key in settings) { | ||||
| if (settings.hasOwnProperty(key)) { | if (settings.hasOwnProperty(key)) { | ||||
| @@ -309,22 +321,19 @@ function respawn(respawnRoom) { | |||||
| } | } | ||||
| function startCombat(opponent) { | function startCombat(opponent) { | ||||
| changeMode("combat"); | |||||
| currentFoe = opponent; | currentFoe = opponent; | ||||
| changeMode("combat"); | |||||
| update(["Oh shit it's a " + opponent.description()]); | update(["Oh shit it's a " + opponent.description()]); | ||||
| } | } | ||||
| function attackClicked(index) { | function attackClicked(index) { | ||||
| update([player.attacks[index].attack(currentFoe)]); | |||||
| update([playerAttacks[index].attack(currentFoe)]); | |||||
| if (currentFoe.health <= 0) { | if (currentFoe.health <= 0) { | ||||
| update(["The " + currentFoe.description() + " falls to the ground!"]); | update(["The " + currentFoe.description() + " falls to the ground!"]); | ||||
| startDialog(new FallenFoe(currentFoe)); | startDialog(new FallenFoe(currentFoe)); | ||||
| } else { | |||||
| let attacks = currentFoe.attacks.filter(attack => attack.conditions == undefined || attack.conditions.reduce((result, test) => result && test(prefs), true)); | |||||
| attacks = attacks.filter(attack => attack.requirements == undefined || attack.requirements.reduce((result, test) => result && test(currentFoe, player), true)); | |||||
| let attack = pick(attacks); | |||||
| } else if (mode == "combat") { | |||||
| let attack = pick(filterValid(currentFoe.attacks, currentFoe, player)); | |||||
| if (attack == null) { | if (attack == null) { | ||||
| attack = currentFoe.backupAttack; | attack = currentFoe.backupAttack; | ||||
| @@ -344,7 +353,7 @@ function attackClicked(index) { | |||||
| } | } | ||||
| function attackHovered(index) { | function attackHovered(index) { | ||||
| document.getElementById("combat-desc").innerHTML = player.attacks[index].desc; | |||||
| document.getElementById("combat-desc").innerHTML = playerAttacks[index].desc; | |||||
| } | } | ||||
| function struggleClicked(index) { | function struggleClicked(index) { | ||||
| @@ -357,10 +366,7 @@ function struggleClicked(index) { | |||||
| if (result.escape) { | if (result.escape) { | ||||
| changeMode("explore"); | changeMode("explore"); | ||||
| } else { | } else { | ||||
| let digests = currentFoe.digests.filter(digest => digest.conditions == undefined || digest.conditions.reduce((result, test) => result && test(prefs), true)); | |||||
| digests = digests.filter(digest => digest.requirements == undefined || digest.requirements.reduce((result, test) => result && test(currentFoe, player), true)); | |||||
| let digest = pick(digests); | |||||
| let digest = pick(filterValid(currentFoe.digests, currentFoe, player)); | |||||
| if (digest == null) { | if (digest == null) { | ||||
| digest = currentFoe.backupDigest; | digest = currentFoe.backupDigest; | ||||
| @@ -29,7 +29,12 @@ function Player(name = "Player") { | |||||
| this.attacks.push(new punchAttack(this)); | this.attacks.push(new punchAttack(this)); | ||||
| this.attacks.push(new flankAttack(this)); | this.attacks.push(new flankAttack(this)); | ||||
| this.attacks.push(new grapple(this)); | |||||
| this.attacks.push(new grappleDevour(this)); | |||||
| this.attacks.push(new grappleRelease(this)); | |||||
| this.backupAttack = new pass(this); | |||||
| this.str = 15; | this.str = 15; | ||||
| this.dex = 15; | this.dex = 15; | ||||
| this.con = 15; | this.con = 15; | ||||
| @@ -53,6 +58,11 @@ function Anthro() { | |||||
| this.attacks.push(new punchAttack(this)); | this.attacks.push(new punchAttack(this)); | ||||
| this.attacks.push(new flankAttack(this)); | this.attacks.push(new flankAttack(this)); | ||||
| this.attacks.push(new grapple(this)); | |||||
| this.attacks.push(new grappleDevour(this)); | |||||
| this.backupAttack = new pass(this); | |||||
| this.struggles = []; | this.struggles = []; | ||||
| @@ -269,7 +279,7 @@ function plead(predator) { | |||||
| name: "Plead", | name: "Plead", | ||||
| desc: "Ask very, very nicely for the predator to let you go. More effective if you haven't hurt your predator.", | desc: "Ask very, very nicely for the predator to let you go. More effective if you haven't hurt your predator.", | ||||
| struggle: function(player) { | struggle: function(player) { | ||||
| let escape = Math.random() < predator.health / predator.maxHealth; | |||||
| let escape = Math.random() < predator.health / predator.maxHealth && Math.random() < 0.33; | |||||
| if (escape) { | if (escape) { | ||||
| return { | return { | ||||
| @@ -291,7 +301,7 @@ function struggle(predator) { | |||||
| name: "Struggle", | name: "Struggle", | ||||
| desc: "Try to squirm free. More effective if you've hurt your predator.", | desc: "Try to squirm free. More effective if you've hurt your predator.", | ||||
| struggle: function(player) { | struggle: function(player) { | ||||
| let escape = Math.random() > predator.health / predator.maxHealth; | |||||
| let escape = Math.random() > predator.health / predator.maxHealth && Math.random() < 0.33; | |||||
| if (escape) { | if (escape) { | ||||
| return { | return { | ||||