| @@ -1,8 +1,10 @@ | |||||
| import { Creature } from './creature' | import { Creature } from './creature' | ||||
| import { Encounter, Action } from './combat' | |||||
| import { Encounter, Action, Damage, StatusEffect } from './combat' | |||||
| import { LogEntry, nilLog } from './interface' | import { LogEntry, nilLog } from './interface' | ||||
| import { ReleaseAction, TransferAction, PassAction } from './combat/actions' | 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 { | export interface AI { | ||||
| name: string; | name: string; | ||||
| @@ -20,7 +22,7 @@ export class NoAI implements AI { | |||||
| * A Decider determines how favorable an action is to perform. | * A Decider determines how favorable an action is to perform. | ||||
| */ | */ | ||||
| export interface Decider { | 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 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 => ({ | const options = encounter.combatants.flatMap(enemy => actor.validActions(enemy).map(action => ({ | ||||
| target: enemy, | target: enemy, | ||||
| action: action, | action: action, | ||||
| @@ -39,7 +41,7 @@ export class DeciderAI implements AI { | |||||
| this.deciders.forEach( | this.deciders.forEach( | ||||
| decider => options.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() | total *= Math.random() | ||||
| console.log(total) | console.log(total) | ||||
| const chosen = options.find ( | |||||
| const chosen = options.find( | |||||
| option => { | option => { | ||||
| if (total < option.weight) { | if (total < option.weight) { | ||||
| return true | return true | ||||
| @@ -92,12 +94,12 @@ export class RandomAI implements AI { | |||||
| */ | */ | ||||
| export class VoreAI extends DeciderAI { | export class VoreAI extends DeciderAI { | ||||
| constructor () { | constructor () { | ||||
| super ( | |||||
| super( | |||||
| "Vore AI", | "Vore AI", | ||||
| [ | [ | ||||
| new NoPassDecider(), | |||||
| new NoReleaseDecider(), | new NoReleaseDecider(), | ||||
| new ChanceDecider() | |||||
| new ChanceDecider(), | |||||
| new NoSurrenderDecider() | |||||
| ] | ] | ||||
| ) | ) | ||||
| } | } | ||||
| @@ -1,7 +1,9 @@ | |||||
| import { Decider } from '../ai' | import { Decider } from '../ai' | ||||
| import { Encounter, Action } from '../combat' | |||||
| import { Encounter, Action, Consequence, CompositionAction } from '../combat' | |||||
| import { Creature } from '../creature' | import { Creature } from '../creature' | ||||
| import { PassAction, ReleaseAction } from '../combat/actions' | import { PassAction, ReleaseAction } from '../combat/actions' | ||||
| import { StatusConsequence } from '../combat/consequences' | |||||
| import { SurrenderEffect } from '../combat/effects' | |||||
| /** | /** | ||||
| * Specifically avoids using a [[PassAction]] | * Specifically avoids using a [[PassAction]] | ||||
| @@ -37,3 +39,62 @@ export class ChanceDecider implements Decider { | |||||
| return action.odds(user, target) | 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 { | export class CompositionAction extends Action { | ||||
| private consequences: Array<Consequence>; | |||||
| public consequences: Array<Consequence>; | |||||
| constructor ( | constructor ( | ||||
| name: TextLike, | name: TextLike, | ||||
| @@ -85,17 +85,17 @@ export class HealingConsequence extends Consequence { | |||||
| * Applies a status effect | * Applies a status effect | ||||
| */ | */ | ||||
| export class StatusConsequence extends Consequence { | export class StatusConsequence extends Consequence { | ||||
| constructor (private statusMaker: () => StatusEffect, conditions: Condition[] = []) { | |||||
| constructor (public statusMaker: (user: Creature, target: Creature) => StatusEffect, conditions: Condition[] = []) { | |||||
| super(conditions) | super(conditions) | ||||
| } | } | ||||
| apply (user: Creature, target: Creature): LogEntry { | apply (user: Creature, target: Creature): LogEntry { | ||||
| return target.applyEffect(this.statusMaker()) | |||||
| return target.applyEffect(this.statusMaker(user, target)) | |||||
| } | } | ||||
| describePair (user: Creature, target: Creature): LogEntry { | describePair (user: Creature, target: Creature): LogEntry { | ||||
| return new LogLine( | 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: [ | consequences: [ | ||||
| new StatusConsequence( | new StatusConsequence( | ||||
| () => new SurrenderEffect() | |||||
| (user, target) => new SurrenderEffect() | |||||
| ) | ) | ||||
| ], | ], | ||||
| conditions: [ | conditions: [ | ||||