| @@ -210,7 +210,7 @@ export default class Combat extends Vue { | |||
| } | |||
| }) | |||
| if (!(this.encounter.currentMove.ai instanceof NoAI)) { | |||
| if (!(this.encounter.currentMove.ai === null)) { | |||
| if (this.encounter.currentMove.side === Side.Heroes) { | |||
| this.executedLeft(this.encounter.currentMove.ai.decide(this.encounter.currentMove, this.encounter)) | |||
| } else { | |||
| @@ -60,9 +60,6 @@ | |||
| <button v-if="subject.perspective === POV.Third" @click.stop="subject.perspective = POV.Second">Second-person</button> | |||
| <button v-if="subject.perspective === POV.First" @click.stop="subject.perspective = POV.Third">Third-person</button> | |||
| <button v-if="subject.perspective === POV.Second" @click.stop="subject.perspective = POV.First">First-person</button> | |||
| <select @change="subject.ai = ais[$event.target.selectedIndex]" class="ai-picker"> | |||
| <option v-for="(ai, index) in ais" :key="'ai-' + index">{{ ai.name }}</option> | |||
| </select> | |||
| </div> | |||
| </div> | |||
| </template> | |||
| @@ -71,7 +68,6 @@ | |||
| import { Component, Prop, Vue, Watch, Emit } from 'vue-property-decorator' | |||
| import { Creature } from '@/game/creature' | |||
| import { POV } from '@/game/language' | |||
| import { NoAI, RandomAI, VoreAI, AI } from '@/game/ai' | |||
| import { Stats, Stat, StatIcons, StatDescs, Vigor, VigorIcons, VigorDescs, VoreStatDescs, VoreStatIcons, VisibleStatus } from '@/game/combat' | |||
| import ContainerView from './ContainerView.vue' | |||
| import tippy, { delegate, createSingleton } from 'tippy.js' | |||
| @@ -83,8 +79,7 @@ import 'tippy.js/dist/tippy.css' | |||
| }, | |||
| data () { | |||
| return { | |||
| POV: POV, | |||
| ais: [new NoAI(), new RandomAI(), new VoreAI()] | |||
| POV: POV | |||
| } | |||
| }, | |||
| methods: { | |||
| @@ -156,9 +151,6 @@ export default class Statblock extends Vue { | |||
| createSingleton(tippyInstances, { delay: 500, touch: ["hold", 500] }) | |||
| this.statusChanged([]) | |||
| const picker = this.$el.querySelector(".ai-picker") as HTMLSelectElement | |||
| picker.selectedIndex = this.$data.ais.findIndex((ai: AI) => ai.name === this.subject.ai.name) | |||
| } | |||
| } | |||
| </script> | |||
| @@ -6,18 +6,6 @@ import { NoPassDecider, NoReleaseDecider, ChanceDecider, ConsequenceDecider, Con | |||
| import { SurrenderEffect } from './combat/effects' | |||
| import { StatusConsequence, DamageConsequence } from './combat/consequences' | |||
| export interface AI { | |||
| name: string; | |||
| decide (actor: Creature, encounter: Encounter): LogEntry; | |||
| } | |||
| export class NoAI implements AI { | |||
| name = "No AI" | |||
| decide (actor: Creature, encounter: Encounter): LogEntry { | |||
| throw new Error("This AI cannot be used.") | |||
| } | |||
| } | |||
| /** | |||
| * A Decider determines how favorable an action is to perform. | |||
| */ | |||
| @@ -25,7 +13,7 @@ export interface Decider { | |||
| decide: (encounter: Encounter, user: Creature, target: Creature, action: Action) => number; | |||
| } | |||
| export class DeciderAI implements AI { | |||
| export class AI { | |||
| constructor (public name: string, private deciders: Decider[]) { | |||
| } | |||
| @@ -74,25 +62,10 @@ export class DeciderAI implements AI { | |||
| } | |||
| } | |||
| /** | |||
| * The RandomAI is **COMPLETELY** random. Good luck. | |||
| */ | |||
| export class RandomAI implements AI { | |||
| name = "Random AI" | |||
| decide (actor: Creature, encounter: Encounter): LogEntry { | |||
| const actions = encounter.combatants.flatMap(enemy => actor.validActions(enemy).map(action => ({ | |||
| target: enemy, | |||
| action: action | |||
| }))) | |||
| const chosen = actions[Math.floor(Math.random() * actions.length)] | |||
| return chosen.action.try(actor, chosen.target) | |||
| } | |||
| } | |||
| /** | |||
| * The VoreAI tries to eat opponents, but only if the odds are good enough | |||
| */ | |||
| export class VoreAI extends DeciderAI { | |||
| export class VoreAI extends AI { | |||
| constructor () { | |||
| super( | |||
| "Vore AI", | |||
| @@ -4,7 +4,7 @@ import { LogEntry, LogLines, LogLine } from './interface' | |||
| import { VoreContainer, VoreType, Container } from './vore' | |||
| import { Item, EquipmentSlot, Equipment, ItemKind, Currency } from './items' | |||
| import { PassAction } from './combat/actions' | |||
| import { AI, NoAI } from './ai' | |||
| import { AI } from './ai' | |||
| import { Mortal } from './entity' | |||
| export class Creature extends Mortal { | |||
| @@ -34,7 +34,7 @@ export class Creature extends Mortal { | |||
| side: Side; | |||
| title = "Lv. 1 Creature"; | |||
| equipment: {[key in EquipmentSlot]?: Equipment } = {} | |||
| ai: AI = new NoAI() | |||
| ai: AI|null = null | |||
| constructor (name: Noun, kind: Noun, pronouns: Pronoun, stats: Stats, public preyPrefs: Set<VoreType>, public predPrefs: Set<VoreType>, private baseMass: number) { | |||
| super(name, kind, pronouns, stats) | |||
| @@ -27,6 +27,7 @@ export class Werewolf extends Creature { | |||
| ) | |||
| this.ai = new VoreAI() | |||
| this.side = Side.Monsters | |||
| const stomach = new Stomach(this, 2, new ConstantDamageFormula(new Damage( | |||
| @@ -8,7 +8,7 @@ import { Creature } from '../creature' | |||
| import { DevourAction } from '../combat/actions' | |||
| import { SurrenderEffect } from '../combat/effects' | |||
| import moment from 'moment' | |||
| import { RandomAI, VoreAI } from '../ai' | |||
| import { VoreAI } from '../ai' | |||
| function makeParty (): Creature[] { | |||
| const fighter = new Creatures.Human(new ProperNoun("Redgar"), MalePronouns, { | |||
| @@ -244,7 +244,7 @@ export const Town = (): Place => { | |||
| (world, executor) => { | |||
| const enemy = new Creatures.Human(new ProperNoun("Nerd"), TheyPronouns) | |||
| enemy.side = Side.Monsters | |||
| enemy.ai = new RandomAI() | |||
| enemy.ai = new VoreAI() | |||
| const encounter = new Encounter( | |||
| { | |||
| name: "Fight some nerd", | |||