diff --git a/src/App.vue b/src/App.vue index 5450fff..1339cb4 100644 --- a/src/App.vue +++ b/src/App.vue @@ -4,6 +4,7 @@
diff --git a/src/components/Combat.vue b/src/components/Combat.vue index 5bb534e..5207262 100644 --- a/src/components/Combat.vue +++ b/src/components/Combat.vue @@ -216,7 +216,7 @@ export default class Combat extends Vue { } }) - if (!(this.encounter.currentMove.ai === null)) { + if (this.encounter.currentMove.perspective === POV.Third) { if (this.encounter.currentMove.side === Side.Heroes) { this.executedLeft(this.encounter.currentMove.ai.decide(this.encounter.currentMove, this.encounter)) } else { diff --git a/src/components/Statblock.vue b/src/components/Statblock.vue index f4a74e5..4fe9c8d 100644 --- a/src/components/Statblock.vue +++ b/src/components/Statblock.vue @@ -57,9 +57,7 @@
- - - + diff --git a/src/game/ai.ts b/src/game/ai.ts index ce17dac..f524d74 100644 --- a/src/game/ai.ts +++ b/src/game/ai.ts @@ -202,6 +202,20 @@ export class FavorEscapedPrey extends Decider { } } +/** + * The RandomAI just does whatever it wants to + */ +export class RandomAI extends AI { + constructor (owner: Creature) { + super( + [ + + ], + owner + ) + } +} + /** * The VoreAI tries to eat opponents, but only if the odds are good enough */ diff --git a/src/game/creature.ts b/src/game/creature.ts index d479ec1..54b4357 100644 --- a/src/game/creature.ts +++ b/src/game/creature.ts @@ -4,7 +4,7 @@ import { LogEntry, LogLines, LogLine } from '@/game/interface' import { VoreContainer, VoreType, Container } from '@/game/vore' import { Item, EquipmentSlot, Equipment, ItemKind, Currency } from '@/game/items' import { PassAction } from '@/game/combat/actions' -import { AI } from '@/game/ai' +import { AI, RandomAI } from '@/game/ai' import { Entity, Resistances } from '@/game/entity' import { Perk } from '@/game/combat/perks' import { VoreRelay } from '@/game/events' @@ -60,7 +60,7 @@ export class Creature extends Entity { side: Side; title = "Lv. 1 Creature"; equipment: {[key in EquipmentSlot]?: Equipment } = {} - ai: AI|null = null + ai: AI constructor (name: Noun, kind: Noun, pronouns: Pronoun, public baseStats: Stats, public preyPrefs: Set, public predPrefs: Set, private baseMass: number) { super(name, kind, pronouns) @@ -76,6 +76,8 @@ export class Creature extends Entity { /* eslint-disable-next-line */ const self = this + this.ai = new RandomAI(this) + this.voreStats = { get [VoreStat.Bulk] () { return self.containers.reduce( diff --git a/src/game/creatures.ts b/src/game/creatures.ts index 71a1372..e69de29 100644 --- a/src/game/creatures.ts +++ b/src/game/creatures.ts @@ -1,4 +0,0 @@ -import Human from '@/game/creatures/human' -import Player from '@/game/creatures/player' -import Inazuma from '@/game/creatures/characters/inazuma' -export { Human, Player, Inazuma } diff --git a/src/game/creatures/characters/inazuma.ts b/src/game/creatures/characters/inazuma.ts index b1acb34..6128c5d 100644 --- a/src/game/creatures/characters/inazuma.ts +++ b/src/game/creatures/characters/inazuma.ts @@ -92,7 +92,6 @@ export default class Inazuma extends Creature { )) this.addVoreContainer(stomach) - - this.ai = null + this.ai = new VoreAI(this) } } diff --git a/src/game/creatures/monsters/slime.ts b/src/game/creatures/monsters/slime.ts new file mode 100644 index 0000000..6d5face --- /dev/null +++ b/src/game/creatures/monsters/slime.ts @@ -0,0 +1,39 @@ +import { VoreAI } from '@/game/ai' +import { DamageType, Side, Stat, StatDamageFormula, Vigor } from '@/game/combat' +import { Creature } from '@/game/creature' +import { ImproperNoun, ObjectPronouns } from '@/game/language' +import { anyVore, Goo } from '@/game/vore' + +export default class Slime extends Creature { + constructor () { + super( + new ImproperNoun("slime", "slimes"), + new ImproperNoun("slime", "slimes"), + ObjectPronouns, + { + Power: 20, + Toughness: 20, + Agility: 5, + Reflexes: 5, + Charm: 5, + Willpower: 5 + }, + anyVore, + anyVore, + 50 + ) + + const gooContainer = new Goo( + this, + 3, + new StatDamageFormula([ + { fraction: 1, stat: Stat.Toughness, type: DamageType.Acid, target: Vigor.Health } + ]) + ) + + this.addVoreContainer(gooContainer) + + this.side = Side.Monsters + this.ai = new VoreAI(this) + } +} diff --git a/src/game/creatures/player.ts b/src/game/creatures/player.ts index 9d4c8e1..e99f334 100644 --- a/src/game/creatures/player.ts +++ b/src/game/creatures/player.ts @@ -4,6 +4,7 @@ import { Damage, DamageType, Vigor, ConstantDamageFormula } from '@/game/combat' import { Stomach, Bowels, anyVore, Cock, Balls, Breasts, InnerBladder, Slit, Womb, biconnectContainers } from '@/game/vore' import { AttackAction } from '@/game/combat/actions' import { RavenousPerk, BellyBulwakPerk, FlauntPerk } from '@/game/combat/perks' +import { VoreAI } from "../ai" export default class Player extends Creature { constructor () { @@ -23,5 +24,7 @@ export default class Player extends Creature { this.addVoreContainer(stomach) this.perspective = POV.Second + + this.ai = new VoreAI(this) } } diff --git a/src/game/maps/town.ts b/src/game/maps/town.ts index 241281d..3ccfecf 100644 --- a/src/game/maps/town.ts +++ b/src/game/maps/town.ts @@ -1,7 +1,6 @@ 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 Creatures from '@/game/creatures' import * as Items from '@/game/items' import { LogLine, nilLog, LogLines } from '@/game/interface' import { Creature } from '@/game/creature' @@ -10,9 +9,12 @@ import { InstantDigestionEffect, SurrenderEffect } from '@/game/combat/effects' import moment from 'moment' import { VoreAI } from '@/game/ai' import { DeliciousPerk } from '@/game/combat/perks' +import Inazuma from '../creatures/characters/inazuma' +import Human from '../creatures/human' +import Slime from '../creatures/monsters/slime' function makeParty (): Creature[] { - const fighter = new Creatures.Human(new ProperNoun("Redgar"), MalePronouns, { + const fighter = new Human(new ProperNoun("Redgar"), MalePronouns, { stats: { Toughness: 20, Power: 20, @@ -24,7 +26,7 @@ function makeParty (): Creature[] { }) fighter.title = "Lv. 6 Fighter" fighter.equip(new Items.Sword(), Items.EquipmentSlot.MainHand) - const rogue = new Creatures.Human(new ProperNoun('Lidda'), FemalePronouns, { + const rogue = new Human(new ProperNoun('Lidda'), FemalePronouns, { stats: { Toughness: 10, Power: 15, @@ -36,7 +38,7 @@ function makeParty (): Creature[] { }) rogue.title = "Lv. 5 Rogue" rogue.equip(new Items.Dagger(), Items.EquipmentSlot.MainHand) - const wizard = new Creatures.Human(new ProperNoun('Mialee'), FemalePronouns, { + const wizard = new Human(new ProperNoun('Mialee'), FemalePronouns, { stats: { Toughness: 10, Power: 10, @@ -48,7 +50,7 @@ function makeParty (): Creature[] { }) wizard.title = "Lv. 6 Wizard" wizard.equip(new Items.Wand(), Items.EquipmentSlot.MainHand) - const cleric = new Creatures.Human(new ProperNoun('Jozan'), MalePronouns, { + const cleric = new Human(new ProperNoun('Jozan'), MalePronouns, { stats: { Toughness: 15, Power: 15, @@ -75,26 +77,6 @@ export const Town = (): Place => { "Where weird stuff happens" ) - const westAve = new Place( - new ImproperNoun('West Avenue'), - "Streets of Sim City" - ) - - const northAve = new Place( - new ImproperNoun('North Avenue'), - "Streets of Sim City" - ) - - const eastAve = new Place( - new ImproperNoun('East Avenue'), - "Streets of Sim City" - ) - - const southAve = new Place( - new ImproperNoun('South Avenue'), - "Streets of Sim City" - ) - const alley = new Place( new ImproperNoun('alley'), "A spooky alley" @@ -123,7 +105,7 @@ export const Town = (): Place => { const bossEncounters = [ new Encounter( { name: "Inazuma", intro: () => nilLog }, - makeParty().concat([new Creatures.Inazuma()]) + makeParty().concat([new Inazuma()]) ) ] @@ -174,12 +156,12 @@ export const Town = (): Place => { ) ) - westAve.choices.push( + square.choices.push( new Choice( "Eat someone", "Slurp", (world, executor) => { - const snack = new Creatures.Human(new ProperNoun(["Snack", "Treat", "Tasty", "Dinner", "Appetizer"][Math.floor(Math.random() * 5)]), [MalePronouns, FemalePronouns, TheyPronouns][Math.floor(Math.random() * 3)]) + 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) @@ -187,12 +169,12 @@ export const Town = (): Place => { ) ) - westAve.choices.push( + square.choices.push( new Choice( "Fight someone", "Ow", (world) => { - const enemy = new Creatures.Human(new ProperNoun("Nerd"), TheyPronouns) + 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) @@ -210,12 +192,12 @@ export const Town = (): Place => { ) ) - westAve.choices.push( + square.choices.push( new Choice( "Recruit someone", "Not ow", (world) => { - const ally = new Creatures.Human(new ProperNoun("Ally"), TheyPronouns) + 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) @@ -339,6 +321,25 @@ export const Town = (): Place => { ) ) + woods.choices.push( + new Choice( + "Fight a slime", + "Go fight a slime", + (world, executor) => { + const enemy = new Slime() + const encounter = new Encounter( + { + name: "Fight some tasty nerd", + intro: () => new LogLine(`A slime draws near!`) + }, + [world.player, enemy].concat(world.party) + ) + world.encounter = encounter + return nilLog + } + ) + ) + debug.choices.push( new Choice( "Add money", @@ -352,14 +353,9 @@ export const Town = (): Place => { home.biconnect(Direction.South, debug) debug.biconnect(Direction.South, bosses) - home.biconnect(Direction.North, westAve) - westAve.biconnect(Direction.West, westRoad) - westAve.biconnect(Direction.North, alley) + home.biconnect(Direction.North, square) westRoad.biconnect(Direction.South, woods) - square.biconnect(Direction.East, eastAve) - square.biconnect(Direction.West, westAve) - square.biconnect(Direction.North, northAve) - square.biconnect(Direction.South, southAve) + square.biconnect(Direction.West, westRoad) return home } diff --git a/src/game/vore.ts b/src/game/vore.ts index c841c7d..f631194 100644 --- a/src/game/vore.ts +++ b/src/game/vore.ts @@ -13,7 +13,8 @@ export enum VoreType { Unbirth = "Unbirthing", Breast = "Breast Vore", Bladder = "Bladder Vore", - Tail = "Tail Vore" + Tail = "Tail Vore", + Goo = "Goo Vore" } export const anyVore = new Set([ @@ -23,7 +24,8 @@ export const anyVore = new Set([ VoreType.Unbirth, VoreType.Breast, VoreType.Bladder, - VoreType.Tail + VoreType.Tail, + VoreType.Goo ]) export interface Container extends Actionable { @@ -408,6 +410,21 @@ export class Tail extends NormalVoreContainer { } } +export class Goo extends NormalVoreContainer { + fluidName = new Noun("goo") + fluidColor = "#66ee66"; + + constructor (owner: Creature, capacity: number, damage: DamageFormula) { + super(new ImproperNoun('goo', 'goo').all, owner, new Set([VoreType.Goo]), capacity, damage) + } + + tickLine (user: Creature, target: Creature, args: { damage: Damage }) { + return new RandomEntry( + new LogLine(`${user.name.capital} ${user.name.conjugate(Words.Clench)} ${target.name.objective} in ${user.pronouns.possessive} ${this.name} for `, args.damage.renderShort(), `.`) + ) + } +} + export class Cock extends NormalVoreContainer { fluidName = new Noun("cum")