great_wolves/feast:master в master
| @@ -16,7 +16,7 @@ module.exports = { | |||
| 'no-debugger': process.env.NODE_ENV === 'production' ? 'warn' : 'off', | |||
| 'no-useless-constructor': 'off', | |||
| '@typescript-eslint/no-unused-vars': 'off', | |||
| 'quotes': 'off', | |||
| quotes: 'off', | |||
| 'function-paren-newline': ['error', 'multiline-arguments'], | |||
| '@typescript-eslint/member-ordering': ['warn'], | |||
| 'indent': 'off', | |||
| @@ -29,10 +29,10 @@ import { Creature } from '@/game/creature' | |||
| import { ProperNoun, TheyPronouns, FemalePronouns, MalePronouns, ImproperNoun, POV } from '@/game/language' | |||
| import { Place, Direction, World, Choice } from '@/game/world' | |||
| import { Encounter, Side } from '@/game/combat' | |||
| import { LogLine, nilLog } from '@/game/interface' | |||
| import { LogLine, Newline, nilLog } from '@/game/interface' | |||
| import { InstantKillEffect } from '@/game/combat/effects' | |||
| import moment from 'moment' | |||
| import { Town } from '@/game/maps/town' | |||
| import { Newtown } from '@/game/maps/Newtown' | |||
| import Player from './game/creatures/player' | |||
| @Component({ | |||
| @@ -84,7 +84,7 @@ export default class App extends Vue { | |||
| player.items.push(new Items.Mace()) | |||
| player.items.push(new Items.Dagger()) | |||
| this.$data.world = new World(player) | |||
| this.$data.home = Town() | |||
| this.$data.home = Newtown() | |||
| player.location = this.$data.home | |||
| } | |||
| @@ -182,6 +182,16 @@ export default class Combat extends Vue { | |||
| ), | |||
| "center" | |||
| ) | |||
| if (this.encounter.winner === this.world.player.side && this.encounter.rewardGifted === false) { | |||
| this.world.player.wallet.Gold += this.encounter.reward | |||
| this.writeLog( | |||
| new LogLine( | |||
| `You found ` + this.encounter.reward + ` gold.` | |||
| ), | |||
| "center" | |||
| ) | |||
| this.encounter.rewardGifted = true | |||
| } | |||
| } else if (this.encounter.winner !== null && !this.$data.won && !this.$data.continuing) { | |||
| this.$data.won = true | |||
| this.writeLog( | |||
| @@ -190,6 +200,16 @@ export default class Combat extends Vue { | |||
| ), | |||
| "center" | |||
| ) | |||
| if (this.encounter.winner === this.world.player.side && this.encounter.rewardGifted === false) { | |||
| this.world.player.wallet.Gold += this.encounter.reward | |||
| this.writeLog( | |||
| new LogLine( | |||
| `You found ` + this.encounter.reward + ` gold.` | |||
| ), | |||
| "center" | |||
| ) | |||
| this.encounter.rewardGifted = true | |||
| } | |||
| } else { | |||
| if (this.encounter.currentMove.side === Side.Heroes) { | |||
| this.$data.left = this.encounter.currentMove | |||
| @@ -2,7 +2,7 @@ | |||
| <div class="character-layout"> | |||
| <button @click="$emit('exit')" class="profile-exit">Exit</button> | |||
| <div class="character-items"> | |||
| <ItemView @click.native="useItem(item)" :item="item" v-for="(item, index) in subject.items" :key="'item-' + index" /> | |||
| <ItemView @click.native="useItem(item)" :item="item" v-for="(item, index) in subject.items" :key="'item-' + index" /> | |||
| </div> | |||
| <div class="character-containers"> | |||
| <ContainerView :container="container" v-for="(container, index) in subject.containers" :key="'explore-container-' + index" /> | |||
| @@ -36,7 +36,7 @@ import EquipmentView from '@/components/EquipmentView.vue' | |||
| import { Creature } from '@/game/creature' | |||
| import { World } from '@/game/world' | |||
| import { LogEntry } from '@/game/interface' | |||
| import { Item, ItemKind, Equipment } from '@/game/items' | |||
| import { Item, ItemKind, Equipment, KeyItem } from '@/game/items' | |||
| @Component({ | |||
| components: { | |||
| Statblock, ContainerView, ItemView, EquipmentView | |||
| @@ -868,6 +868,8 @@ export class Encounter { | |||
| initiatives: Map<Creature, number> | |||
| currentMove: Creature | |||
| turnTime = 100 | |||
| reward = 50 // Gold | |||
| rewardGifted = false | |||
| constructor (public desc: EncounterDesc, public combatants: Creature[]) { | |||
| this.initiatives = new Map() | |||
| @@ -0,0 +1,38 @@ | |||
| import { NoPassDecider, AI } from '@/game/ai' | |||
| import { CompositionAction, UniformRandomDamageFormula, Damage, DamageType, FractionDamageFormula, Side, Stat, StatDamageFormula, Vigor } from '@/game/combat' | |||
| import { AttackAction } from '@/game/combat/actions' | |||
| import { PairCondition, TogetherCondition } from '@/game/combat/conditions' | |||
| import { ConsumeConsequence, DamageConsequence, DrainConsequence, StatusConsequence } from '@/game/combat/consequences' | |||
| import { LogGroupConsequence } from '@/game/combat/groupConsequences' | |||
| import { PreyTargeter, SideTargeter, SoloTargeter } from '@/game/combat/targeters' | |||
| import { CompositionTest, OpposedStatScorer, TestCategory } from '@/game/combat/tests' | |||
| import { Creature } from '@/game/creature' | |||
| import { LogLine, nilLog } from '@/game/interface' | |||
| import { ImproperNoun, MalePronouns, ProperNoun, Verb } from '@/game/language' | |||
| import { anyVore, Stomach } from '@/game/vore' | |||
| export default class Samuel extends Creature { | |||
| constructor () { | |||
| super( | |||
| new ProperNoun("Samuel"), | |||
| new ImproperNoun("wolf"), | |||
| MalePronouns, | |||
| { | |||
| Power: 20, | |||
| Toughness: 10, | |||
| Agility: 30, | |||
| Reflexes: 10, | |||
| Charm: 30, | |||
| Willpower: 15 | |||
| }, | |||
| anyVore, | |||
| new Set(), | |||
| 10 | |||
| ) | |||
| this.side = Side.Monsters | |||
| this.ai = (new AI([new NoPassDecider()], this)) | |||
| this.actions.push(new AttackAction(new UniformRandomDamageFormula(new Damage({ type: DamageType.Slash, amount: 10, target: Vigor.Health }), 0.5), new Verb("claw", "claws", "claws", "clawed"))) | |||
| } | |||
| } | |||
| @@ -0,0 +1,147 @@ | |||
| import { VoreAI } from '@/game/ai' | |||
| import { DamageType, Side, Stat, StatDamageFormula, StatusEffect, Vigor } from '@/game/combat' | |||
| import { DigestionPowerEffect } from '@/game/combat/effects' | |||
| import { Creature } from '@/game/creature' | |||
| import { LogEntry, LogLine, nilLog } from '@/game/interface' | |||
| import { ImproperNoun, MalePronouns, ObjectPronouns, Preposition, Verb, ProperNoun, Noun } from '@/game/language' | |||
| import { anyVore, ConnectionDirection, Container, ContainerCapability, DefaultContainer, Stomach, Throat, transferDescription, VoreType } from '@/game/vore' | |||
| import * as Words from '@/game/words' | |||
| import * as Onomatopoeia from '@/game/onomatopoeia' | |||
| import { ConsumeConsequence } from '@/game/combat/consequences' | |||
| export default class SheenTheGryph extends Creature { | |||
| constructor () { | |||
| super( | |||
| new ProperNoun("Sheen"), | |||
| new ImproperNoun("gryphon", "gryphons"), | |||
| MalePronouns, | |||
| { | |||
| Power: 60, | |||
| Toughness: 35, | |||
| Agility: 15, | |||
| Reflexes: 10, | |||
| Charm: 20, | |||
| Willpower: 20 | |||
| }, | |||
| anyVore, | |||
| anyVore, | |||
| 250 | |||
| ) | |||
| class Paw extends DefaultContainer{ | |||
| constructor(owner : Creature,capacityFactor : number) { | |||
| super( | |||
| new Noun("paw", "paws"), | |||
| owner, | |||
| new Set<VoreType>([VoreType.Paw]), | |||
| capacityFactor, | |||
| new Set<ContainerCapability>([ | |||
| ContainerCapability.Consume, | |||
| ContainerCapability.Release | |||
| ]) | |||
| ) | |||
| } | |||
| } | |||
| const paw = new Paw( | |||
| this, | |||
| 25 | |||
| ) | |||
| const throat = new Throat( | |||
| this, | |||
| 25 | |||
| ) | |||
| const stomach = new Stomach( | |||
| this, | |||
| 50, | |||
| new StatDamageFormula([ | |||
| { fraction: 1, stat: Stat.Toughness, type: DamageType.Acid, target: Vigor.Health } | |||
| ]) | |||
| ) | |||
| stomach.effects.push(new class extends StatusEffect { | |||
| constructor () { | |||
| super( | |||
| "Pinned", | |||
| "Prey sometimes can't move.", | |||
| "fas fa-sun" | |||
| ) | |||
| } | |||
| onApply (creature: Creature): LogEntry { | |||
| return new LogLine( | |||
| `${stomach.owner.name.capital.possessive} ${stomach.name} is incredibly tight, gripping ${creature.name.objective} like a vice!` | |||
| ) | |||
| } | |||
| preAction (creature: Creature): { prevented: boolean; log: LogEntry } { | |||
| if (Math.random() < 0.5) { | |||
| return { | |||
| prevented: true, | |||
| log: new LogLine(`${creature.name.capital} can't move!`) | |||
| } | |||
| } else { | |||
| return { | |||
| prevented: false, | |||
| log: nilLog | |||
| } | |||
| } | |||
| } | |||
| }()) | |||
| paw.effects.push(new class extends StatusEffect { | |||
| constructor () { | |||
| super( | |||
| "Pinned", | |||
| "Prey sometimes can't move.", | |||
| "fas fa-sun" | |||
| ) | |||
| } | |||
| onApply (creature: Creature): LogEntry { | |||
| return new LogLine( | |||
| `${stomach.owner.name.capital.possessive} ${stomach.name} is incredibly tight, gripping ${creature.name.objective} like a vice!` | |||
| ) | |||
| } | |||
| preAction (creature: Creature): { prevented: boolean; log: LogEntry } { | |||
| if (Math.random() < 0.5) { | |||
| return { | |||
| prevented: true, | |||
| log: new LogLine(`${creature.name.capital} can't move!`) | |||
| } | |||
| } else { | |||
| return { | |||
| prevented: false, | |||
| log: nilLog | |||
| } | |||
| } | |||
| } | |||
| }()) | |||
| this.addContainer(throat) | |||
| this.addContainer(stomach) | |||
| this.addContainer(paw) | |||
| throat.connect({ | |||
| destination: stomach, | |||
| direction: ConnectionDirection.Deeper, | |||
| description: transferDescription(Words.Swallow, new Preposition("down")) | |||
| }) | |||
| stomach.connect({ | |||
| destination: throat, | |||
| direction: ConnectionDirection.Shallower, | |||
| description: transferDescription(new Verb("hork"), new Preposition("up")) | |||
| }) | |||
| stomach.voreRelay.subscribe("onDigested", (sender, args) => { | |||
| return Onomatopoeia.makeOnomatopoeia(Onomatopoeia.Burp) | |||
| }) | |||
| this.side = Side.Monsters | |||
| this.ai = new VoreAI(this) | |||
| } | |||
| } | |||
| @@ -23,7 +23,7 @@ export default class Player extends Creature { | |||
| const stomach = new Stomach(this, 2, new ConstantDamageFormula(new Damage({ amount: 20, type: DamageType.Acid, target: Vigor.Health }, { amount: 10, type: DamageType.Crush, target: Vigor.Health }))) | |||
| this.addContainer(stomach) | |||
| this.perspective = POV.First | |||
| this.perspective = POV.Second | |||
| this.ai = new VoreAI(this) | |||
| @@ -37,7 +37,6 @@ export abstract class Item implements Actionable { | |||
| abstract kind: ItemKind | |||
| constructor (public name: Word, public desc: TextLike) { | |||
| } | |||
| } | |||
| @@ -169,7 +168,11 @@ export class ItemAction extends Action { | |||
| } | |||
| execute (user: Creature, target: Creature): LogEntry { | |||
| if (this.item.consumed) { | |||
| return new LogLine(`You have already consumed this `.concat(this.item.name.toString())) // Keeps the [[Consumable]] from being used twice, even if the element still exists | |||
| } | |||
| this.item.consumed = true | |||
| delete user.items[user.items.indexOf(this.item)] // Removes [[Consumable]], but does not remove the element till the next gui draw (Not keen in html) | |||
| return this.action.execute(user, target) | |||
| } | |||
| @@ -195,7 +198,7 @@ export class Consumable extends Item { | |||
| export abstract class Potion extends Consumable { | |||
| constructor (name: ImproperNoun, desc: string, consequences: Array<Consequence>) { | |||
| super( | |||
| new ImproperNoun("health potion"), | |||
| name, | |||
| desc, | |||
| new CompositionAction( | |||
| "Drink " + name, | |||
| @@ -0,0 +1,533 @@ | |||
| import { Place, Choice, Direction, World } from '@/game/world' | |||
| import { ProperNoun, ImproperNoun, MalePronouns, FemalePronouns, TheyPronouns } from '@/game/language' | |||
| import { Encounter, Stat, Damage, DamageType, Vigor, Side } from '@/game/combat' | |||
| import * as Items from '@/game/items' | |||
| import { LogLine, nilLog, LogLines } from '@/game/interface' | |||
| import { Creature } from '@/game/creature' | |||
| import { DevourAction } from '@/game/combat/actions' | |||
| import { InstantDigestionEffect, SurrenderEffect } from '@/game/combat/effects' | |||
| import moment from 'moment' | |||
| import { VoreAI } from '@/game/ai' | |||
| import { DeliciousPerk } from '@/game/combat/perks' | |||
| import Samuel from '../creatures/characters/Samuel' | |||
| import Human from '../creatures/human' | |||
| import Werewolf from '../creatures/monsters/werewolf' | |||
| import SheenTheGryph from '../creatures/characters/SheenTheGryph' | |||
| function makeParty (): Creature[] { | |||
| const fighter = new Human(new ProperNoun("Redgar"), MalePronouns, { | |||
| stats: { | |||
| Toughness: 20, | |||
| Power: 20, | |||
| Reflexes: 15, | |||
| Agility: 15, | |||
| Willpower: 15, | |||
| Charm: 10 | |||
| } | |||
| }) | |||
| fighter.title = "Lv. 6 Fighter" | |||
| fighter.equip(new Items.Sword(), Items.EquipmentSlot.MainHand) | |||
| const rogue = new Human(new ProperNoun('Lidda'), FemalePronouns, { | |||
| stats: { | |||
| Toughness: 10, | |||
| Power: 15, | |||
| Reflexes: 20, | |||
| Agility: 20, | |||
| Willpower: 15, | |||
| Charm: 20 | |||
| } | |||
| }) | |||
| rogue.title = "Lv. 5 Rogue" | |||
| rogue.equip(new Items.Dagger(), Items.EquipmentSlot.MainHand) | |||
| const wizard = new Human(new ProperNoun('Mialee'), FemalePronouns, { | |||
| stats: { | |||
| Toughness: 10, | |||
| Power: 10, | |||
| Reflexes: 15, | |||
| Agility: 15, | |||
| Willpower: 20, | |||
| Charm: 25 | |||
| } | |||
| }) | |||
| wizard.title = "Lv. 6 Wizard" | |||
| wizard.equip(new Items.Wand(), Items.EquipmentSlot.MainHand) | |||
| const cleric = new Human(new ProperNoun('Jozan'), MalePronouns, { | |||
| stats: { | |||
| Toughness: 15, | |||
| Power: 15, | |||
| Reflexes: 10, | |||
| Agility: 10, | |||
| Willpower: 20, | |||
| Charm: 15 | |||
| } | |||
| }) | |||
| cleric.title = "Lv. 5 Cleric" | |||
| cleric.equip(new Items.Mace(), Items.EquipmentSlot.MainHand) | |||
| return [fighter, cleric, rogue, wizard] | |||
| } | |||
| export const Newtown = (): Place => { | |||
| const home = new Place( | |||
| new ProperNoun("Home"), | |||
| "A place you can rest after long adventures" | |||
| ) | |||
| const debug = new Place( | |||
| new ProperNoun("Debug Room"), | |||
| "Where weird stuff happens" | |||
| ) | |||
| const southTownStreet = new Place( | |||
| new ProperNoun("South Town Street"), | |||
| "Town street south of the Town square" | |||
| ) | |||
| const northTownStreet = new Place( | |||
| new ProperNoun("North Town Street"), | |||
| "Town street north of the Town square" | |||
| ) | |||
| const northTownShop = new Place( | |||
| new ProperNoun("North Town Shop"), | |||
| "A shop for your impulsive buying needs" | |||
| ) | |||
| const eastTownStreet = new Place( | |||
| new ProperNoun("East Town Street"), | |||
| "Town street east of the Town square" | |||
| ) | |||
| const westTownStreet = new Place( | |||
| new ProperNoun("West Town Street"), | |||
| "Town street west of the Town square" | |||
| ) | |||
| const townSquare = new Place( | |||
| new ProperNoun("Town Square"), | |||
| "The central-most part of town, and a hub of bustling activity" | |||
| ) | |||
| const eastGate = new Place( | |||
| new ProperNoun("East Gate"), | |||
| "The towns gate, leading out into the wilderness" | |||
| ) | |||
| const woods = new Place( | |||
| new ProperNoun("The Woods"), | |||
| "A scary part of the forest where monsters hide" | |||
| ) | |||
| const deepwoods = new Place( | |||
| new ProperNoun("Deep Woods"), | |||
| "Extra scary" | |||
| ) | |||
| southTownStreet.choices.push( | |||
| new Choice( | |||
| "Fight Sheen", | |||
| "Go fight Sheen!", | |||
| (world, executor) => { | |||
| const enemy = new SheenTheGryph() | |||
| const encounter = new Encounter( | |||
| { | |||
| name: "Fight some tough nerd", | |||
| intro: () => new LogLine(`Sheen Approaches!`) | |||
| }, | |||
| [world.player, enemy].concat(world.party) | |||
| ) | |||
| world.encounter = encounter | |||
| return nilLog | |||
| } | |||
| ) | |||
| ) | |||
| deepwoods.choices.push( | |||
| new Choice( | |||
| "Fight Werewolf", | |||
| "Go fight Werewolf!", | |||
| (world, executor) => { | |||
| const enemy = new Werewolf() | |||
| const encounter = new Encounter( | |||
| { | |||
| name: "Fight some tough nerd", | |||
| intro: () => new LogLine(`Werewolf Approaches!`) | |||
| }, | |||
| [world.player, enemy].concat(world.party) | |||
| ) | |||
| world.encounter = encounter | |||
| return nilLog | |||
| } | |||
| ) | |||
| ) | |||
| const bossEncounters = [ | |||
| new Encounter( | |||
| { name: "Inazuma", intro: () => nilLog }, | |||
| makeParty().concat([new Werewolf()]) | |||
| ) | |||
| ] | |||
| home.choices.push( | |||
| new Choice( | |||
| "Nap", | |||
| "Zzzzzz", | |||
| (world) => { | |||
| return new LogLines( | |||
| `You lie down for a nice nap...`, | |||
| world.advance(moment.duration(1, "hour")) | |||
| ) | |||
| } | |||
| ) | |||
| ) | |||
| home.choices.push( | |||
| new Choice( | |||
| "Heal", | |||
| "Become not dead and/or eaten", | |||
| (world, executor) => { | |||
| Object.keys(Vigor).forEach(vigor => { | |||
| executor.vigors[vigor as Vigor] = executor.maxVigors[vigor as Vigor] | |||
| }) | |||
| if (executor.containedIn !== null) { | |||
| executor.containedIn.release(executor) | |||
| } | |||
| executor.statusEffects.forEach(effect => { | |||
| executor.removeEffect(effect) | |||
| }) | |||
| executor.destroyed = false | |||
| return new LogLine(`You're healthy again`) | |||
| } | |||
| ) | |||
| ) | |||
| home.choices.push( | |||
| new Choice( | |||
| "Heal your party", | |||
| "Revive your party, and ensure they are not dead and/or eaten", | |||
| (world, executor) => { | |||
| world.party.forEach((partyMember) => { | |||
| Object.keys(Vigor).forEach(vigor => { | |||
| partyMember.vigors[vigor as Vigor] = partyMember.maxVigors[vigor as Vigor] | |||
| }) | |||
| if (partyMember.containedIn !== null) { | |||
| partyMember.containedIn.release(partyMember) | |||
| } | |||
| partyMember.statusEffects.forEach(effect => { | |||
| partyMember.removeEffect(effect) | |||
| }) | |||
| partyMember.destroyed = false | |||
| }) | |||
| Object.keys(Vigor).forEach(vigor => { | |||
| executor.vigors[vigor as Vigor] = executor.maxVigors[vigor as Vigor] | |||
| }) | |||
| if (executor.containedIn !== null) { | |||
| executor.containedIn.release(executor) | |||
| } | |||
| executor.statusEffects.forEach(effect => { | |||
| executor.removeEffect(effect) | |||
| }) | |||
| executor.destroyed = false | |||
| return new LogLine(`Your party is healthy again`) | |||
| } | |||
| ) | |||
| ) | |||
| home.choices.push( | |||
| new Choice( | |||
| "Grab potions", | |||
| "Grab some potions", | |||
| (world, executor) => { | |||
| executor.items.push(new Items.HealthPotion()) | |||
| executor.items.push(new Items.AcidPotion()) | |||
| executor.items.push(new Items.ShrinkPotion()) | |||
| executor.items.push(new Items.StrengthPotion()) | |||
| return new LogLine("You grab some potions") | |||
| } | |||
| ) | |||
| ) | |||
| townSquare.choices.push( | |||
| new Choice( | |||
| "Eat someone", | |||
| "Slurp", | |||
| (world, executor) => { | |||
| const snack = new Human(new ProperNoun(["Snack", "Treat", "Tasty", "Dinner", "Appetizer"][Math.floor(Math.random() * 5)]), [MalePronouns, FemalePronouns, TheyPronouns][Math.floor(Math.random() * 3)]) | |||
| snack.applyEffect(new SurrenderEffect()) | |||
| const options = executor.validActions(snack).filter(action => action instanceof DevourAction) | |||
| return options[Math.floor(options.length * Math.random())].execute(executor, snack) | |||
| } | |||
| ) | |||
| ) | |||
| townSquare.choices.push( | |||
| new Choice( | |||
| "Fight someone", | |||
| "Ow", | |||
| (world) => { | |||
| const enemy = new Human(new ProperNoun("Nerd"), TheyPronouns) | |||
| enemy.side = Side.Monsters | |||
| enemy.ai = new VoreAI(enemy) | |||
| enemy.equip(new Items.Sword(), Items.EquipmentSlot.MainHand) | |||
| enemy.addPerk(new DeliciousPerk()) | |||
| const encounter = new Encounter( | |||
| { | |||
| name: "Fight some tasty nerd", | |||
| intro: () => new LogLine(`You find some nerd to fight.`) | |||
| }, | |||
| [world.player, enemy].concat(world.party) | |||
| ) | |||
| world.encounter = encounter | |||
| return nilLog | |||
| } | |||
| ) | |||
| ) | |||
| townSquare.choices.push( | |||
| new Choice( | |||
| "Recruit someone", | |||
| "Not ow", | |||
| (world) => { | |||
| const ally = new Human(new ProperNoun("Ally"), TheyPronouns) | |||
| ally.side = Side.Heroes | |||
| ally.ai = new VoreAI(ally) | |||
| ally.equip(new Items.Sword(), Items.EquipmentSlot.MainHand) | |||
| world.party.push(ally) | |||
| return new LogLine(`You recruit a nerd`) | |||
| } | |||
| ) | |||
| ) | |||
| northTownShop.choices.push( | |||
| new Choice( | |||
| "Buy a shiny rock", | |||
| "This rock has no use.", | |||
| (world, executor) => { | |||
| if (executor.wallet.Gold >= 500) { | |||
| executor.wallet.Gold -= 500 | |||
| executor.items.push( | |||
| new Items.KeyItem(new ProperNoun("Shiny Rock"), "Very shiny") | |||
| ) | |||
| return new LogLine(`You buy a shiny rock`) | |||
| } else { | |||
| return new LogLine(`Shiny rocks are 500 gold coins, loser!`) | |||
| } | |||
| } | |||
| ) | |||
| ) | |||
| northTownShop.choices.push( | |||
| new Choice( | |||
| "Buy a health potion", | |||
| "50 Gold", | |||
| (world, executor) => { | |||
| if (executor.wallet.Gold >= 50) { | |||
| executor.wallet.Gold -= 50 | |||
| executor.items.push( | |||
| new Items.HealthPotion() | |||
| ) | |||
| return new LogLine(`You buy a health potion.`) | |||
| } else { | |||
| return new LogLine(`Health potions are 50 gold coins.`) | |||
| } | |||
| } | |||
| ) | |||
| ) | |||
| northTownShop.choices.push( | |||
| new Choice( | |||
| "Buy a strength potion", | |||
| "40 Gold", | |||
| (world, executor) => { | |||
| if (executor.wallet.Gold >= 40) { | |||
| executor.wallet.Gold -= 40 | |||
| executor.items.push( | |||
| new Items.StrengthPotion() | |||
| ) | |||
| return new LogLine(`You buy a strength potion.`) | |||
| } else { | |||
| return new LogLine(`Strength potions are 40 gold coins.`) | |||
| } | |||
| } | |||
| ) | |||
| ) | |||
| northTownShop.choices.push( | |||
| new Choice( | |||
| "Buy an acid potion", | |||
| "25 Gold", | |||
| (world, executor) => { | |||
| if (executor.wallet.Gold >= 25) { | |||
| executor.wallet.Gold -= 25 | |||
| executor.items.push( | |||
| new Items.AcidPotion() | |||
| ) | |||
| return new LogLine(`You buy an acid potion.`) | |||
| } else { | |||
| return new LogLine(`Acid potions are 25 gold coins.`) | |||
| } | |||
| } | |||
| ) | |||
| ) | |||
| northTownShop.choices.push( | |||
| new Choice( | |||
| "Buy a shrink potion", | |||
| "10 Gold", | |||
| (world, executor) => { | |||
| if (executor.wallet.Gold >= 10) { | |||
| executor.wallet.Gold -= 10 | |||
| executor.items.push( | |||
| new Items.ShrinkPotion() | |||
| ) | |||
| return new LogLine(`You buy a shrink potion.`) | |||
| } else { | |||
| return new LogLine(`Shrink potions are 10 gold coins.`) | |||
| } | |||
| } | |||
| ) | |||
| ) | |||
| debug.choices.push( | |||
| new Choice( | |||
| "Cut stats", | |||
| "Make your stats less good-er", | |||
| (world, executor) => { | |||
| Object.keys(Stat).forEach(stat => { | |||
| executor.baseStats[stat as Stat] -= 5 | |||
| executor.takeDamage(new Damage( | |||
| { amount: 5, target: (stat as Stat), type: DamageType.Pure } | |||
| )) | |||
| }) | |||
| return new LogLine(`You're weaker now`) | |||
| } | |||
| ) | |||
| ) | |||
| debug.choices.push( | |||
| new Choice( | |||
| "Boost stats", | |||
| "Make your stats more good-er", | |||
| (world, executor) => { | |||
| Object.keys(Stat).forEach(stat => { | |||
| executor.baseStats[stat as Stat] += 5 | |||
| executor.takeDamage(new Damage( | |||
| { amount: 5, target: (stat as Stat), type: DamageType.Heal } | |||
| )) | |||
| }) | |||
| return new LogLine(`You're stronger now`) | |||
| } | |||
| ) | |||
| ) | |||
| debug.choices.push( | |||
| new Choice( | |||
| "Grow", | |||
| "Make yourself larger", | |||
| (world, executor) => { | |||
| executor.voreStats.Mass *= 1.5 | |||
| return new LogLine(`You're larger now`) | |||
| } | |||
| ) | |||
| ) | |||
| debug.choices.push( | |||
| new Choice( | |||
| "Shrink", | |||
| "Make yourself smaller", | |||
| (world, executor) => { | |||
| executor.voreStats.Mass /= 1.5 | |||
| return new LogLine(`You're smaller now`) | |||
| } | |||
| ) | |||
| ) | |||
| debug.choices.push( | |||
| new Choice( | |||
| "Instant Digestion", | |||
| "Make your stomach REALLY powerful", | |||
| (world, executor) => { | |||
| executor.applyEffect(new InstantDigestionEffect()) | |||
| return new LogLine(`You're really gonna melt people now.`) | |||
| } | |||
| ) | |||
| ) | |||
| debug.choices.push( | |||
| new Choice( | |||
| "Set Name", | |||
| "Set your name", | |||
| (world, executor) => { | |||
| const input = prompt("Enter a name") | |||
| if (input !== null) { | |||
| executor.baseName = new ProperNoun(input) | |||
| return new LogLine(`Your new name is ${executor.baseName}.`) | |||
| } else { | |||
| return new LogLine(`nvm`) | |||
| } | |||
| } | |||
| ) | |||
| ) | |||
| debug.choices.push( | |||
| new Choice( | |||
| "Add money", | |||
| "Get some money", | |||
| (world, executor) => { | |||
| executor.wallet.Gold += 1000 | |||
| return new LogLine(`$$$$$$$$$$$$$$$$$`) | |||
| } | |||
| ) | |||
| ) | |||
| woods.choices.push( | |||
| new Choice( | |||
| "Fight Samuel", | |||
| "Go fight a poor little wolf!", | |||
| (world, executor) => { | |||
| const enemy = new Samuel() | |||
| const encounter = new Encounter( | |||
| { | |||
| name: "Fight some tasty nerd", | |||
| intro: () => new LogLine(`Samuel pokes his head out from the bushes!`) | |||
| }, | |||
| [world.player, enemy].concat(world.party) | |||
| ) | |||
| world.encounter = encounter | |||
| return nilLog | |||
| } | |||
| ) | |||
| ) | |||
| const bosses = new Place( | |||
| new ProperNoun("BOSS ZONE"), | |||
| "Extra scary" | |||
| ) | |||
| bossEncounters.forEach(encounter => { | |||
| bosses.choices.push( | |||
| new Choice( | |||
| encounter.desc.name, | |||
| "Boss fight!", | |||
| (world) => { | |||
| world.encounter = encounter | |||
| return nilLog | |||
| } | |||
| ) | |||
| ) | |||
| }) | |||
| home.biconnect(Direction.South, debug, "Walk", "Enter the debug room", "Leave", "Exit the debug room") | |||
| home.biconnect(Direction.Northeast, townSquare, "Leave", "Go outside", "Enter", "Enter your home") | |||
| townSquare.biconnect(Direction.North, northTownStreet, "Walk", "Go that way", "Walk", "Go that way") | |||
| townSquare.biconnect(Direction.East, eastTownStreet, "Walk", "Go that way", "Walk", "Go that way") | |||
| townSquare.biconnect(Direction.South, southTownStreet, "Walk", "Go that way", "Walk", "Go that way") | |||
| townSquare.biconnect(Direction.West, westTownStreet, "Walk", "Go that way", "Walk", "Go that way") | |||
| northTownShop.biconnect(Direction.West, northTownStreet, "Exit", "Leave the shop", "Enter", "Enter the shop") | |||
| debug.biconnect(Direction.South, bosses, "Enter", "Boss Fight!", "Leave", "Go back to the debug room") | |||
| eastTownStreet.biconnect(Direction.East, eastGate, "Walk", "Go that way", "Walk", "Go that way") | |||
| eastGate.biconnect(Direction.East, woods, "Walk", "Enter the woods", "Approach", "Enter the city gates") | |||
| woods.biconnect(Direction.North, deepwoods, "Walk", "Go deeper", "Walk", "Leave the deep woods") | |||
| return home | |||
| } | |||
| @@ -10,6 +10,7 @@ import moment from 'moment' | |||
| import { VoreAI } from '@/game/ai' | |||
| import { DeliciousPerk } from '@/game/combat/perks' | |||
| import Human from '../creatures/human' | |||
| import Samuel from '../creatures/characters/Samuel' | |||
| import Werewolf from '../creatures/monsters/werewolf' | |||
| function makeParty (): Creature[] { | |||
| @@ -334,6 +335,25 @@ export const Town = (): Place => { | |||
| ) | |||
| ) | |||
| woods.choices.push( | |||
| new Choice( | |||
| "Fight Samuel", | |||
| "Go fight a poor little wolf!", | |||
| (world, executor) => { | |||
| const enemy = new Samuel() | |||
| const encounter = new Encounter( | |||
| { | |||
| name: "Fight some tasty nerd", | |||
| intro: () => new LogLine(`Samuel pokes his head out from the bushes!`) | |||
| }, | |||
| [world.player, enemy].concat(world.party) | |||
| ) | |||
| world.encounter = encounter | |||
| return nilLog | |||
| } | |||
| ) | |||
| ) | |||
| debug.choices.push( | |||
| new Choice( | |||
| "Add money", | |||
| @@ -345,8 +365,8 @@ export const Town = (): Place => { | |||
| ) | |||
| ) | |||
| home.biconnect(Direction.South, debug) | |||
| debug.biconnect(Direction.South, bosses) | |||
| home.biconnect(Direction.South, debug, "Walk", "Enter the debug room", "Leave", "Exit the debug room") | |||
| debug.biconnect(Direction.South, bosses, "Enter", "Boss Fight!", "Leave", "Go back to the debug room") | |||
| home.biconnect(Direction.North, square) | |||
| westRoad.biconnect(Direction.South, woods) | |||
| square.biconnect(Direction.West, westRoad) | |||
| @@ -8,7 +8,8 @@ import { Creature } from '@/game/creature' | |||
| import { VoreRelay } from '@/game/events' | |||
| export enum VoreType { | |||
| Oral = "Oral Vore" | |||
| Oral = "Oral Vore", | |||
| Paw = "Paw Vore" | |||
| } | |||
| export const anyVore = new Set([ | |||
| @@ -29,34 +29,55 @@ export function reverse (dir: Direction): Direction { | |||
| } | |||
| export class Choice { | |||
| isAccessible: boolean | |||
| isVisible: boolean | |||
| constructor (public name: TextLike, public desc: TextLike, public execute: (world: World, executor: Creature) => LogEntry) { | |||
| this.isAccessible = true | |||
| this.isVisible = true | |||
| } | |||
| /** | |||
| * Gets the choice's visiblity. No funtionality at the moment. | |||
| */ | |||
| visible (): boolean { | |||
| return true | |||
| } | |||
| /** | |||
| * Gets the choice's visiblity. No funtionality at the moment. | |||
| */ | |||
| accessible (): boolean { | |||
| return true | |||
| } | |||
| } | |||
| export class Connection { | |||
| isAccessible: boolean | |||
| isVisible: boolean | |||
| constructor (public src: Place, public dst: Place, public name: TextLike = "Travel", public desc: TextLike = "Go there lol") { | |||
| this.isAccessible = true | |||
| this.isVisible = true | |||
| } | |||
| /** | |||
| * Gets the connection's visiblity. No funtionality at the moment. | |||
| */ | |||
| visible (): boolean { | |||
| return true | |||
| return this.isVisible | |||
| } | |||
| /** | |||
| * Gets the connection's accessibility. No funtionality at the moment. | |||
| */ | |||
| accessible (): boolean { | |||
| return true | |||
| return this.isAccessible | |||
| } | |||
| travel (world: World, traveler: Creature): LogEntry { | |||
| const advanceLogs = world.advance(moment.duration(5, "minutes")) | |||
| /** | |||
| * Moves [[traveler]] through this connection to [[dst]] and progresses the time by [[moveTime]]. | |||
| */ | |||
| travel (world: World, traveler: Creature, moveTime: Duration = moment.duration(5, "minutes")): LogEntry { | |||
| const advanceLogs = world.advance(moveTime) | |||
| traveler.location = this.dst | |||
| return new LogLines( | |||
| advanceLogs, | |||
| @@ -73,13 +94,19 @@ export class Place { | |||
| } | |||
| connect (dir: Direction, dst: Place) { | |||
| this.connections[dir] = new Connection(this, dst) | |||
| /** | |||
| * Connects a room one way. | |||
| */ | |||
| connect (dir: Direction, dst: Place, name: TextLike = "Travel", desc: TextLike = "Go there lol") { | |||
| this.connections[dir] = new Connection(this, dst, name, desc) | |||
| } | |||
| biconnect (dir: Direction, dst: Place) { | |||
| this.connect(dir, dst) | |||
| dst.connect(reverse(dir), this) | |||
| /** | |||
| * Connects two rooms and names both hover boxes. | |||
| */ | |||
| biconnect (dir: Direction, dst: Place, name1: TextLike = "Travel", desc1: TextLike = "Go there lol", name2: TextLike = "Travel", desc2: TextLike = "Go there lol") { | |||
| this.connect(dir, dst, name1, desc1) | |||
| dst.connect(reverse(dir), this, name2, desc2) | |||
| } | |||
| } | |||
| @@ -99,6 +126,9 @@ export class World { | |||
| this.creatures.push(player) | |||
| } | |||
| /** | |||
| * Progresses the clock. | |||
| */ | |||
| advance (dt: Duration): LogEntry { | |||
| this.time.add(dt) | |||
| return new LogLines( | |||