diff --git a/src/game/ai.ts b/src/game/ai.ts index cc921a4..7012491 100644 --- a/src/game/ai.ts +++ b/src/game/ai.ts @@ -1,8 +1,10 @@ import { Creature } from './creature' -import { Encounter, Action } from './combat' +import { Encounter, Action, Damage, StatusEffect } from './combat' import { LogEntry, nilLog } from './interface' import { ReleaseAction, TransferAction, PassAction } from './combat/actions' -import { NoPassDecider, NoReleaseDecider, ChanceDecider } from './ai/deciders' +import { NoPassDecider, NoReleaseDecider, ChanceDecider, ConsequenceDecider, ConsequenceFunctionDecider, NoSurrenderDecider } from './ai/deciders' +import { SurrenderEffect } from './combat/effects' +import { StatusConsequence, DamageConsequence } from './combat/consequences' export interface AI { name: string; @@ -20,7 +22,7 @@ export class NoAI implements AI { * A Decider determines how favorable an action is to perform. */ 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 { @@ -28,7 +30,7 @@ export class DeciderAI implements AI { } - decide(actor: Creature, encounter: Encounter): LogEntry { + decide (actor: Creature, encounter: Encounter): LogEntry { const options = encounter.combatants.flatMap(enemy => actor.validActions(enemy).map(action => ({ target: enemy, action: action, @@ -39,7 +41,7 @@ export class DeciderAI implements AI { this.deciders.forEach( decider => options.forEach( - option => option.weight *= decider.decide(encounter, actor, option.target, option.action) + option => { option.weight *= decider.decide(encounter, actor, option.target, option.action) } ) ) @@ -51,7 +53,7 @@ export class DeciderAI implements AI { total *= Math.random() console.log(total) - const chosen = options.find ( + const chosen = options.find( option => { if (total < option.weight) { return true @@ -92,12 +94,12 @@ export class RandomAI implements AI { */ export class VoreAI extends DeciderAI { constructor () { - super ( + super( "Vore AI", [ - new NoPassDecider(), new NoReleaseDecider(), - new ChanceDecider() + new ChanceDecider(), + new NoSurrenderDecider() ] ) } diff --git a/src/game/ai/deciders.ts b/src/game/ai/deciders.ts index 02553fc..74415e6 100644 --- a/src/game/ai/deciders.ts +++ b/src/game/ai/deciders.ts @@ -1,7 +1,9 @@ import { Decider } from '../ai' -import { Encounter, Action } from '../combat' +import { Encounter, Action, Consequence, CompositionAction } from '../combat' import { Creature } from '../creature' import { PassAction, ReleaseAction } from '../combat/actions' +import { StatusConsequence } from '../combat/consequences' +import { SurrenderEffect } from '../combat/effects' /** * Specifically avoids using a [[PassAction]] @@ -37,3 +39,62 @@ export class ChanceDecider implements Decider { return action.odds(user, target) } } + +/** + * Adjusts the weights for [[CompositionAction]]s that contain the specified consequence + */ +export class ConsequenceDecider implements Decider { + constructor (private consequenceType: new (...args: any) => T, private weight: number) { + + } + + decide (encounter: Encounter, user: Creature, target: Creature, action: Action): number { + if (action instanceof CompositionAction) { + if (action.consequences.some( + consequence => consequence instanceof this.consequenceType + )) { + return this.weight + } else { + return 1 + } + } else { + return 1 + } + } +} + +/** + * Adjusts the weights for [[CompositionAction]]s, using the provided function to make the choice. + */ +export class ConsequenceFunctionDecider implements Decider { + constructor (private func: (encounter: Encounter, user: Creature, target: Creature, action: CompositionAction) => number) { + + } + + decide (encounter: Encounter, user: Creature, target: Creature, action: Action): number { + if (action instanceof CompositionAction) { + return this.func(encounter, user, target, action) + } else { + return 1 + } + } +} + +/** + * A [[ConsequenceFunctionDecider]] that filters out status effects + */ +export class NoSurrenderDecider extends ConsequenceFunctionDecider { + constructor () { + super( + (encounter, user, target, action) => { + return action.consequences.some( + consequence => { + if (consequence instanceof StatusConsequence) { + return (consequence.statusMaker(user, target) instanceof SurrenderEffect) + } + } + ) ? 0 : 1 + } + ) + } +} diff --git a/src/game/combat.ts b/src/game/combat.ts index 33539c4..b698628 100644 --- a/src/game/combat.ts +++ b/src/game/combat.ts @@ -389,7 +389,7 @@ export abstract class Action { } export class CompositionAction extends Action { - private consequences: Array; + public consequences: Array; constructor ( name: TextLike, diff --git a/src/game/combat/consequences.ts b/src/game/combat/consequences.ts index b25f968..b50cac9 100644 --- a/src/game/combat/consequences.ts +++ b/src/game/combat/consequences.ts @@ -85,17 +85,17 @@ export class HealingConsequence extends Consequence { * Applies a status effect */ export class StatusConsequence extends Consequence { - constructor (private statusMaker: () => StatusEffect, conditions: Condition[] = []) { + constructor (public statusMaker: (user: Creature, target: Creature) => StatusEffect, conditions: Condition[] = []) { super(conditions) } apply (user: Creature, target: Creature): LogEntry { - return target.applyEffect(this.statusMaker()) + return target.applyEffect(this.statusMaker(user, target)) } describePair (user: Creature, target: Creature): LogEntry { return new LogLine( - `Applies a ${this.statusMaker().name} effect.` + `Applies a ${this.statusMaker(user, target)} effect.` ) } } diff --git a/src/game/creatures/human.ts b/src/game/creatures/human.ts index 7cde43a..d2c71da 100644 --- a/src/game/creatures/human.ts +++ b/src/game/creatures/human.ts @@ -32,7 +32,7 @@ export class Human extends Creature { { consequences: [ new StatusConsequence( - () => new SurrenderEffect() + (user, target) => new SurrenderEffect() ) ], conditions: [