Control is now determined by whether or not something is viewed in third person.master
| @@ -4,6 +4,7 @@ | |||
| <div id="main-area"> | |||
| <transition name="component-fade" mode='out-in'> | |||
| <component | |||
| @control="control" | |||
| @profile="profile" | |||
| @exit="$data.profileSubject = null" | |||
| @give-in="gameOver()" | |||
| @@ -23,7 +24,6 @@ import Header from '@/components/Header.vue' | |||
| import Combat from '@/components/Combat.vue' | |||
| import Explore from '@/components/Explore.vue' | |||
| import Profile from '@/components/Profile.vue' | |||
| import * as Creatures from '@/game/creatures' | |||
| import * as Items from '@/game/items' | |||
| import { Creature } from '@/game/creature' | |||
| import { ProperNoun, TheyPronouns, FemalePronouns, MalePronouns, ImproperNoun, POV } from '@/game/language' | |||
| @@ -33,6 +33,7 @@ import { LogLine, nilLog } from '@/game/interface' | |||
| import { InstantKillEffect } from '@/game/combat/effects' | |||
| import moment from 'moment' | |||
| import { Town } from '@/game/maps/town' | |||
| import Player from './game/creatures/player' | |||
| @Component({ | |||
| components: { | |||
| @@ -74,7 +75,7 @@ export default class App extends Vue { | |||
| } | |||
| created () { | |||
| const player = new Creatures.Player() | |||
| const player = new Player() | |||
| player.perspective = POV.Second | |||
| player.side = Side.Heroes | |||
| player.equipment[Items.EquipmentSlot.MainHand] = new Items.Sword() | |||
| @@ -95,6 +96,12 @@ export default class App extends Vue { | |||
| profile (subject: Creature) { | |||
| this.$data.profileSubject = subject | |||
| } | |||
| control (subject: Creature) { | |||
| this.$data.world.player.perspective = POV.Third | |||
| this.$data.world.player = subject | |||
| subject.perspective = POV.Second | |||
| } | |||
| } | |||
| </script> | |||
| @@ -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 { | |||
| @@ -57,9 +57,7 @@ | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <button @click.stop="subject.perspective = POV.First">1st</button> | |||
| <button @click.stop="subject.perspective = POV.Second">2nd</button> | |||
| <button @click.stop="subject.perspective = POV.Third">3rd</button> | |||
| <button @click="$emit('control', subject)">Control</button> | |||
| <button class="show-profile" @click="$emit('profile', subject)">Show profile</button> | |||
| </div> | |||
| </div> | |||
| @@ -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 | |||
| */ | |||
| @@ -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<VoreType>, public predPrefs: Set<VoreType>, 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( | |||
| @@ -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 } | |||
| @@ -92,7 +92,6 @@ export default class Inazuma extends Creature { | |||
| )) | |||
| this.addVoreContainer(stomach) | |||
| this.ai = null | |||
| this.ai = new VoreAI(this) | |||
| } | |||
| } | |||
| @@ -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) | |||
| } | |||
| } | |||
| @@ -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) | |||
| } | |||
| } | |||
| @@ -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 | |||
| } | |||
| @@ -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") | |||