| @@ -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() | |||
| ] | |||
| ) | |||
| } | |||
| @@ -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<T extends Consequence> 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 | |||
| } | |||
| ) | |||
| } | |||
| } | |||
| @@ -389,7 +389,7 @@ export abstract class Action { | |||
| } | |||
| export class CompositionAction extends Action { | |||
| private consequences: Array<Consequence>; | |||
| public consequences: Array<Consequence>; | |||
| constructor ( | |||
| name: TextLike, | |||
| @@ -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.` | |||
| ) | |||
| } | |||
| } | |||
| @@ -32,7 +32,7 @@ export class Human extends Creature { | |||
| { | |||
| consequences: [ | |||
| new StatusConsequence( | |||
| () => new SurrenderEffect() | |||
| (user, target) => new SurrenderEffect() | |||
| ) | |||
| ], | |||
| conditions: [ | |||