diff --git a/src/components/Combat.vue b/src/components/Combat.vue index 3878639..230f28e 100644 --- a/src/components/Combat.vue +++ b/src/components/Combat.vue @@ -17,7 +17,7 @@
-
+
@@ -31,7 +31,7 @@
-
+
@@ -46,6 +46,9 @@ +
@@ -66,7 +69,10 @@ import { NoAI } from '../game/ai' return { left: null, right: null, - combatants: null + combatants: null, + won: false, + continuing: false, + totalWon: false } } } @@ -80,6 +86,14 @@ export default class Combat extends Vue { actionDescription = '' + get running () { + if (this.encounter.winner === null || (this.$data.continuing === true && this.encounter.totalWinner === null)) { + return true + } else { + return false + } + } + @Emit("described") described (entry: LogEntry) { const actionDesc = this.$el.querySelector(".action-description") @@ -150,11 +164,22 @@ export default class Combat extends Vue { pickNext () { // Did one side win? + console.log(this.encounter.winner, this.encounter.totalWinner) - if (this.encounter.winner !== null) { + if (this.encounter.totalWinner !== null && !this.$data.totalWon) { + this.$data.totalWon = true + this.$data.won = true this.writeLog( new LogLine( - `game o-vore lmaoooooooo` + `game o-vore for good` + ), + "center" + ) + } else if (this.encounter.winner !== null && !this.$data.won && !this.$data.continuing) { + this.$data.won = true + this.writeLog( + new LogLine( + `game o-vore` ), "center" ) @@ -282,8 +307,8 @@ export default class Combat extends Vue { min-height: 100%; } -.exit-combat { - grid-area: 2 / main-col-start / main-row-start / main-col-end; +.exit-combat, +.continue-combat { width: 100%; padding: 4pt; flex: 0 1; @@ -295,6 +320,14 @@ export default class Combat extends Vue { font-size: 36px; } +.exit-combat { + grid-area: 2 / main-col-start / main-row-start / 3; +} + +.continue-combat { + grid-area: 2 / 3 / main-row-start / main-col-end; +} + .combat-layout { position: relative; display: grid; diff --git a/src/components/Statblock.vue b/src/components/Statblock.vue index 89de514..112140d 100644 --- a/src/components/Statblock.vue +++ b/src/components/Statblock.vue @@ -71,7 +71,7 @@ import { Component, Prop, Vue, Watch, Emit } from 'vue-property-decorator' import { Creature } from '@/game/creature' import { POV } from '@/game/language' -import { NoAI, RandomAI } from '@/game/ai' +import { NoAI, RandomAI, VoreAI } 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' @@ -84,7 +84,7 @@ import 'tippy.js/dist/tippy.css' data () { return { POV: POV, - ais: [new NoAI(), new RandomAI()] + ais: [new NoAI(), new RandomAI(), new VoreAI()] } }, methods: { diff --git a/src/game/ai.ts b/src/game/ai.ts index 74e9acd..ab3740f 100644 --- a/src/game/ai.ts +++ b/src/game/ai.ts @@ -1,6 +1,7 @@ import { Creature } from './creature' import { Encounter } from './combat' import { LogEntry } from './interface' +import { ReleaseAction, TransferAction } from './combat/actions' export interface AI { name: string; @@ -28,3 +29,23 @@ export class RandomAI implements AI { return chosen.action.execute(actor, chosen.target) } } + +/** + * The VoreAI tries to perform moves from its containers + */ +export class VoreAI extends RandomAI { + name = "Vore AI" + decide (actor: Creature, encounter: Encounter): LogEntry { + const actions = encounter.combatants.flatMap(enemy => actor.validActions(enemy).map(action => ({ + target: enemy, + action: action + }))) + const voreActions = actions.filter(action => actor.otherContainers.concat(actor.containers).some(container => container.actions.includes(action.action) || action instanceof TransferAction)) + const aggressiveActions = voreActions.filter(action => !(action.action instanceof ReleaseAction)) + const chosen = aggressiveActions[Math.floor(Math.random() * aggressiveActions.length)] + if (chosen === undefined) { + return super.decide(actor, encounter) + } + return chosen.action.execute(actor, chosen.target) + } +} diff --git a/src/game/combat.ts b/src/game/combat.ts index d9e12c0..122142d 100644 --- a/src/game/combat.ts +++ b/src/game/combat.ts @@ -579,6 +579,9 @@ export class Encounter { return nilLog } + /** + * Combat is won once one side is completely disabled + */ get winner (): null|Side { const remaining: Set = new Set(this.combatants.filter(combatant => !combatant.disabled).map(combatant => combatant.side)) @@ -588,6 +591,19 @@ export class Encounter { return null } } + + /** + * Combat is completely won once one side is completely destroyed + */ + get totalWinner (): null|Side { + const remaining: Set = new Set(this.combatants.filter(combatant => !combatant.destroyed).map(combatant => combatant.side)) + + if (remaining.size === 1) { + return Array.from(remaining)[0] + } else { + return null + } + } } export abstract class Consequence { diff --git a/src/game/entity.ts b/src/game/entity.ts index dd35e69..052e300 100644 --- a/src/game/entity.ts +++ b/src/game/entity.ts @@ -46,6 +46,8 @@ export abstract class Mortal extends Entity { [Vigor.Resolve]: 100 } + destroyed = false; + constructor (name: Noun, kind: Noun, pronouns: Pronoun, public baseStats: Stats) { super(name, kind, pronouns) this.stats = Object.keys(Stat).reduce((base: any, key) => { base[key] = baseStats[key as Stat]; return base }, {}) @@ -109,6 +111,10 @@ export abstract class Mortal extends Entity { } }) + if (this.vigors.Health <= -this.maxVigors.Health) { + this.destroyed = true + } + if (this.vigors.Health <= 0 && startHealth > 0) { return this.destroy() } else { diff --git a/src/game/vore.ts b/src/game/vore.ts index fe170f9..7513288 100644 --- a/src/game/vore.ts +++ b/src/game/vore.ts @@ -24,7 +24,6 @@ export abstract class Vore extends Mortal { otherContainers: Array = [] containedIn: Container | null = null - destroyed = false; voreStats: VoreStats