| @@ -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) { | if (this.encounter.currentMove.side === Side.Heroes) { | ||||
| this.executedLeft(this.encounter.currentMove.ai.decide(this.encounter.currentMove, this.encounter)) | this.executedLeft(this.encounter.currentMove.ai.decide(this.encounter.currentMove, this.encounter)) | ||||
| } else { | } 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.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.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> | <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> | ||||
| </div> | </div> | ||||
| </template> | </template> | ||||
| @@ -71,7 +68,6 @@ | |||||
| import { Component, Prop, Vue, Watch, Emit } from 'vue-property-decorator' | import { Component, Prop, Vue, Watch, Emit } from 'vue-property-decorator' | ||||
| import { Creature } from '@/game/creature' | import { Creature } from '@/game/creature' | ||||
| import { POV } from '@/game/language' | 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 { Stats, Stat, StatIcons, StatDescs, Vigor, VigorIcons, VigorDescs, VoreStatDescs, VoreStatIcons, VisibleStatus } from '@/game/combat' | ||||
| import ContainerView from './ContainerView.vue' | import ContainerView from './ContainerView.vue' | ||||
| import tippy, { delegate, createSingleton } from 'tippy.js' | import tippy, { delegate, createSingleton } from 'tippy.js' | ||||
| @@ -83,8 +79,7 @@ import 'tippy.js/dist/tippy.css' | |||||
| }, | }, | ||||
| data () { | data () { | ||||
| return { | return { | ||||
| POV: POV, | |||||
| ais: [new NoAI(), new RandomAI(), new VoreAI()] | |||||
| POV: POV | |||||
| } | } | ||||
| }, | }, | ||||
| methods: { | methods: { | ||||
| @@ -156,9 +151,6 @@ export default class Statblock extends Vue { | |||||
| createSingleton(tippyInstances, { delay: 500, touch: ["hold", 500] }) | createSingleton(tippyInstances, { delay: 500, touch: ["hold", 500] }) | ||||
| this.statusChanged([]) | 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> | </script> | ||||
| @@ -6,18 +6,6 @@ import { NoPassDecider, NoReleaseDecider, ChanceDecider, ConsequenceDecider, Con | |||||
| import { SurrenderEffect } from './combat/effects' | import { SurrenderEffect } from './combat/effects' | ||||
| import { StatusConsequence, DamageConsequence } from './combat/consequences' | 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. | * 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; | 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[]) { | 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 | * 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 () { | constructor () { | ||||
| super( | super( | ||||
| "Vore AI", | "Vore AI", | ||||
| @@ -4,7 +4,7 @@ import { LogEntry, LogLines, LogLine } from './interface' | |||||
| import { VoreContainer, VoreType, Container } from './vore' | import { VoreContainer, VoreType, Container } from './vore' | ||||
| import { Item, EquipmentSlot, Equipment, ItemKind, Currency } from './items' | import { Item, EquipmentSlot, Equipment, ItemKind, Currency } from './items' | ||||
| import { PassAction } from './combat/actions' | import { PassAction } from './combat/actions' | ||||
| import { AI, NoAI } from './ai' | |||||
| import { AI } from './ai' | |||||
| import { Mortal } from './entity' | import { Mortal } from './entity' | ||||
| export class Creature extends Mortal { | export class Creature extends Mortal { | ||||
| @@ -34,7 +34,7 @@ export class Creature extends Mortal { | |||||
| side: Side; | side: Side; | ||||
| title = "Lv. 1 Creature"; | title = "Lv. 1 Creature"; | ||||
| equipment: {[key in EquipmentSlot]?: Equipment } = {} | 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) { | 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) | super(name, kind, pronouns, stats) | ||||
| @@ -27,6 +27,7 @@ export class Werewolf extends Creature { | |||||
| ) | ) | ||||
| this.ai = new VoreAI() | this.ai = new VoreAI() | ||||
| this.side = Side.Monsters | this.side = Side.Monsters | ||||
| const stomach = new Stomach(this, 2, new ConstantDamageFormula(new Damage( | const stomach = new Stomach(this, 2, new ConstantDamageFormula(new Damage( | ||||
| @@ -8,7 +8,7 @@ import { Creature } from '../creature' | |||||
| import { DevourAction } from '../combat/actions' | import { DevourAction } from '../combat/actions' | ||||
| import { SurrenderEffect } from '../combat/effects' | import { SurrenderEffect } from '../combat/effects' | ||||
| import moment from 'moment' | import moment from 'moment' | ||||
| import { RandomAI, VoreAI } from '../ai' | |||||
| import { VoreAI } from '../ai' | |||||
| function makeParty (): Creature[] { | function makeParty (): Creature[] { | ||||
| const fighter = new Creatures.Human(new ProperNoun("Redgar"), MalePronouns, { | const fighter = new Creatures.Human(new ProperNoun("Redgar"), MalePronouns, { | ||||
| @@ -244,7 +244,7 @@ export const Town = (): Place => { | |||||
| (world, executor) => { | (world, executor) => { | ||||
| const enemy = new Creatures.Human(new ProperNoun("Nerd"), TheyPronouns) | const enemy = new Creatures.Human(new ProperNoun("Nerd"), TheyPronouns) | ||||
| enemy.side = Side.Monsters | enemy.side = Side.Monsters | ||||
| enemy.ai = new RandomAI() | |||||
| enemy.ai = new VoreAI() | |||||
| const encounter = new Encounter( | const encounter = new Encounter( | ||||
| { | { | ||||
| name: "Fight some nerd", | name: "Fight some nerd", | ||||