| @@ -53,6 +53,7 @@ export default class App extends Vue { | |||||
| this.$data.encounters.push(new Encounter({ name: 'Dragon' }, this.makeParty().concat([new Creatures.Dragon()]))) | this.$data.encounters.push(new Encounter({ name: 'Dragon' }, this.makeParty().concat([new Creatures.Dragon()]))) | ||||
| this.$data.encounters.push(new Encounter({ name: 'Wolves' }, this.makeParty().concat([new Creatures.Wolf(), new Creatures.Wolf(), new Creatures.Wolf(), new Creatures.Wolf()]))) | this.$data.encounters.push(new Encounter({ name: 'Wolves' }, this.makeParty().concat([new Creatures.Wolf(), new Creatures.Wolf(), new Creatures.Wolf(), new Creatures.Wolf()]))) | ||||
| this.$data.encounters.push(new Encounter({ name: 'Large Wah' }, this.makeParty().concat([new Creatures.Shingo()]))) | this.$data.encounters.push(new Encounter({ name: 'Large Wah' }, this.makeParty().concat([new Creatures.Shingo()]))) | ||||
| this.$data.encounters.push(new Encounter({ name: 'Goldeneye' }, this.makeParty().concat([new Creatures.Goldeneye()]))) | |||||
| this.$data.encounter = this.$data.encounters[0] | this.$data.encounter = this.$data.encounters[0] | ||||
| @@ -91,33 +91,24 @@ export default class Combat extends Vue { | |||||
| @Emit("executedLeft") | @Emit("executedLeft") | ||||
| executedLeft (entry: LogEntry) { | executedLeft (entry: LogEntry) { | ||||
| const log = this.$el.querySelector(".log") | |||||
| if (log !== null) { | |||||
| const before = log.querySelector("div.log-entry") | |||||
| const holder = document.createElement("div") | |||||
| holder.classList.add("log-entry") | |||||
| this.writeLog(entry, "left-move") | |||||
| entry.render().forEach(element => { | |||||
| holder.appendChild(element) | |||||
| }) | |||||
| this.writeLog(this.encounter.nextMove(), "left-move") | |||||
| this.pickNext() | |||||
| } | |||||
| holder.classList.add("left-move") | |||||
| const hline = document.createElement("div") | |||||
| hline.classList.add("log-separator") | |||||
| log.insertBefore(hline, before) | |||||
| log.insertBefore(holder, hline) | |||||
| // TODO these need to render on the correct side | |||||
| log.scrollTo({ top: 0, left: 0 }) | |||||
| } | |||||
| @Emit("executedRight") | |||||
| executedRight (entry: LogEntry) { | |||||
| this.writeLog(entry, "right-move") | |||||
| this.encounter.nextMove() | |||||
| this.writeLog(this.encounter.nextMove(), "right-move") | |||||
| this.pickNext() | this.pickNext() | ||||
| } | } | ||||
| @Emit("executedRight") | |||||
| executedRight (entry: LogEntry) { | |||||
| writeLog (entry: LogEntry, cls: string) { | |||||
| const log = this.$el.querySelector(".log") | const log = this.$el.querySelector(".log") | ||||
| if (log !== null) { | if (log !== null) { | ||||
| const before = log.querySelector("div.log-entry") | const before = log.querySelector("div.log-entry") | ||||
| const holder = document.createElement("div") | const holder = document.createElement("div") | ||||
| @@ -127,7 +118,7 @@ export default class Combat extends Vue { | |||||
| holder.appendChild(element) | holder.appendChild(element) | ||||
| }) | }) | ||||
| holder.classList.add("right-move") | |||||
| holder.classList.add(cls) | |||||
| const hline = document.createElement("div") | const hline = document.createElement("div") | ||||
| hline.classList.add("log-separator") | hline.classList.add("log-separator") | ||||
| log.insertBefore(hline, before) | log.insertBefore(hline, before) | ||||
| @@ -135,9 +126,6 @@ export default class Combat extends Vue { | |||||
| log.scrollTo({ top: 0, left: 0 }) | log.scrollTo({ top: 0, left: 0 }) | ||||
| } | } | ||||
| this.encounter.nextMove() | |||||
| this.pickNext() | |||||
| } | } | ||||
| pickNext () { | pickNext () { | ||||
| @@ -1,5 +1,5 @@ | |||||
| import { Creature } from "./creature" | import { Creature } from "./creature" | ||||
| import { TextLike, DynText, ToBe, LiveText } from './language' | |||||
| import { TextLike, DynText, ToBe, LiveText, PairLineArgs, PairLine } from './language' | |||||
| import { LogEntry, LogLines, FAElem, LogLine, FormatEntry, FormatOpt, PropElem, nilLog } from './interface' | import { LogEntry, LogLines, FAElem, LogLine, FormatEntry, FormatOpt, PropElem, nilLog } from './interface' | ||||
| export enum DamageType { | export enum DamageType { | ||||
| @@ -277,7 +277,7 @@ export class FractionDamageFormula implements DamageFormula { | |||||
| } | } | ||||
| } else if (factor.target in Vigor) { | } else if (factor.target in Vigor) { | ||||
| return { | return { | ||||
| amount: Math.max(factor.fraction * user.vigors[factor.target as Vigor]), | |||||
| amount: Math.max(factor.fraction * target.vigors[factor.target as Vigor]), | |||||
| target: factor.target, | target: factor.target, | ||||
| type: factor.type | type: factor.type | ||||
| } | } | ||||
| @@ -343,6 +343,32 @@ export abstract class Action { | |||||
| abstract describe (user: Creature, target: Creature): LogEntry | abstract describe (user: Creature, target: Creature): LogEntry | ||||
| } | } | ||||
| export type TestBundle = { | |||||
| test: CombatTest; | |||||
| fail: PairLine<Creature>; | |||||
| } | |||||
| export class CompositionAction extends Action { | |||||
| private consequences: Array<Consequence>; | |||||
| private tests: Array<TestBundle>; | |||||
| constructor (name: TextLike, desc: TextLike, properties: { conditions?: Array<Condition>; consequences?: Array<Consequence>; tests?: Array<TestBundle> }) { | |||||
| super(name, desc, properties.conditions ?? []) | |||||
| this.consequences = properties.consequences ?? [] | |||||
| this.tests = properties.tests ?? [] | |||||
| } | |||||
| execute (user: Creature, target: Creature): LogEntry { | |||||
| return new LogLines( | |||||
| ...this.consequences.filter(consequence => consequence.applicable(user, target)).map(consequence => consequence.apply(user, target)) | |||||
| ) | |||||
| } | |||||
| describe (user: Creature, target: Creature): LogEntry { | |||||
| return new LogLine(`No descriptions yet...`) | |||||
| } | |||||
| } | |||||
| /** | /** | ||||
| * A Condition describes whether or not something is permissible between two [[Creature]]s | * A Condition describes whether or not something is permissible between two [[Creature]]s | ||||
| */ | */ | ||||
| @@ -376,10 +402,19 @@ export abstract class GroupAction extends Action { | |||||
| * Some hooks return results along with a log entry. | * Some hooks return results along with a log entry. | ||||
| */ | */ | ||||
| export class Effective { | export class Effective { | ||||
| /** | |||||
| * Executes when the effect is initially applied | |||||
| */ | |||||
| onApply (creature: Creature): LogEntry { return nilLog } | onApply (creature: Creature): LogEntry { return nilLog } | ||||
| /** | |||||
| * Executes when the effect is removed | |||||
| */ | |||||
| onRemove (creature: Creature): LogEntry { return nilLog } | onRemove (creature: Creature): LogEntry { return nilLog } | ||||
| /** | |||||
| * Executes before the creature tries to perform an action | |||||
| */ | |||||
| preAction (creature: Creature): { prevented: boolean; log: LogEntry } { | preAction (creature: Creature): { prevented: boolean; log: LogEntry } { | ||||
| return { | return { | ||||
| prevented: false, | prevented: false, | ||||
| @@ -387,16 +422,42 @@ export class Effective { | |||||
| } | } | ||||
| } | } | ||||
| /** | |||||
| * Executes before another creature tries to perform an action that targets this creature | |||||
| */ | |||||
| preReceiveAction (creature: Creature, attacker: Creature): { prevented: boolean; log: LogEntry } { | |||||
| return { | |||||
| prevented: false, | |||||
| log: nilLog | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Executes before the creature receives damage (or healing) | |||||
| */ | |||||
| preDamage (creature: Creature, damage: Damage): Damage { | preDamage (creature: Creature, damage: Damage): Damage { | ||||
| return damage | return damage | ||||
| } | } | ||||
| /** | |||||
| * Executes before the creature is attacked | |||||
| */ | |||||
| preAttack (creature: Creature, attacker: Creature): { prevented: boolean; log: LogEntry } { | preAttack (creature: Creature, attacker: Creature): { prevented: boolean; log: LogEntry } { | ||||
| return { | return { | ||||
| prevented: false, | prevented: false, | ||||
| log: nilLog | log: nilLog | ||||
| } | } | ||||
| } | } | ||||
| /** | |||||
| * Executes when a creature's turn starts | |||||
| */ | |||||
| preTurn (creature: Creature): { prevented: boolean; log: LogEntry } { | |||||
| return { | |||||
| prevented: false, | |||||
| log: nilLog | |||||
| } | |||||
| } | |||||
| } | } | ||||
| /** | /** | ||||
| * A displayable status effect | * A displayable status effect | ||||
| @@ -454,14 +515,14 @@ export class Encounter { | |||||
| this.nextMove() | this.nextMove() | ||||
| } | } | ||||
| nextMove (): void { | |||||
| nextMove (): LogEntry { | |||||
| this.initiatives.set(this.currentMove, 0) | this.initiatives.set(this.currentMove, 0) | ||||
| const times = new Map<Creature, number>() | const times = new Map<Creature, number>() | ||||
| this.combatants.forEach(combatant => { | this.combatants.forEach(combatant => { | ||||
| // this should never be undefined | // this should never be undefined | ||||
| const currentProgress = this.initiatives.get(combatant) ?? 0 | const currentProgress = this.initiatives.get(combatant) ?? 0 | ||||
| const remaining = (this.turnTime - currentProgress) / Math.max(combatant.stats.Speed, 1) | |||||
| const remaining = (this.turnTime - currentProgress) / Math.sqrt(Math.max(combatant.stats.Speed, 1)) | |||||
| times.set(combatant, remaining) | times.set(combatant, remaining) | ||||
| }) | }) | ||||
| @@ -471,18 +532,40 @@ export class Encounter { | |||||
| return closestTime <= nextTime ? closest : next | return closestTime <= nextTime ? closest : next | ||||
| }, this.combatants[0]) | }, this.combatants[0]) | ||||
| const closestRemaining = (this.turnTime - (this.initiatives.get(this.currentMove) ?? 0)) / Math.max(this.currentMove.stats.Speed, 1) | |||||
| const closestRemaining = (this.turnTime - (this.initiatives.get(this.currentMove) ?? 0)) / Math.sqrt(Math.max(this.currentMove.stats.Speed, 1)) | |||||
| this.combatants.forEach(combatant => { | this.combatants.forEach(combatant => { | ||||
| // still not undefined... | // still not undefined... | ||||
| const currentProgress = this.initiatives.get(combatant) ?? 0 | const currentProgress = this.initiatives.get(combatant) ?? 0 | ||||
| this.initiatives.set(combatant, currentProgress + closestRemaining * Math.max(combatant.stats.Speed, 1)) | |||||
| this.initiatives.set(combatant, currentProgress + closestRemaining * Math.sqrt(Math.max(combatant.stats.Speed, 1))) | |||||
| }) | }) | ||||
| // TODO: still let the creature use drained-vigor moves | // TODO: still let the creature use drained-vigor moves | ||||
| if (this.currentMove.disabled) { | if (this.currentMove.disabled) { | ||||
| this.nextMove() | |||||
| return this.nextMove() | |||||
| } else { | |||||
| const effectResults = this.currentMove.effects.map(effect => effect.preTurn(this.currentMove)).filter(effect => effect.prevented) | |||||
| if (effectResults.some(result => result.prevented)) { | |||||
| return new LogLines( | |||||
| ...effectResults.map(result => result.log).concat([this.nextMove()]) | |||||
| ) | |||||
| } | |||||
| } | } | ||||
| return nilLog | |||||
| } | } | ||||
| } | } | ||||
| export abstract class Consequence { | |||||
| constructor (public conditions: Condition[]) { | |||||
| } | |||||
| applicable (user: Creature, target: Creature): boolean { | |||||
| return this.conditions.every(cond => cond.allowed(user, target)) | |||||
| } | |||||
| abstract apply (user: Creature, target: Creature): LogEntry | |||||
| } | |||||
| @@ -87,3 +87,13 @@ export class EnemyCondition implements Condition { | |||||
| return user.side !== target.side | return user.side !== target.side | ||||
| } | } | ||||
| } | } | ||||
| export class ContainerFullCondition implements Condition { | |||||
| constructor (private container: Container) { | |||||
| } | |||||
| allowed (user: Creature, target: Creature): boolean { | |||||
| return this.container.contents.length > 0 | |||||
| } | |||||
| } | |||||
| @@ -0,0 +1,57 @@ | |||||
| import { Consequence, DamageFormula, Condition, StatusEffect } from '../combat' | |||||
| import { Creature } from '../creature' | |||||
| import { LogEntry, LogLines, LogLine } from '../interface' | |||||
| import { Verb, PairLine } from '../language' | |||||
| /** | |||||
| * Takes a function, and thus can do anything. | |||||
| */ | |||||
| export class ArbitraryConsequence extends Consequence { | |||||
| constructor (public apply: (user: Creature, target: Creature) => LogEntry, conditions: Condition[] = []) { | |||||
| super(conditions) | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Renders some text. | |||||
| */ | |||||
| export class LogConsequence extends Consequence { | |||||
| constructor (private line: PairLine<Creature>, conditions: Condition[] = []) { | |||||
| super(conditions) | |||||
| } | |||||
| apply (user: Creature, target: Creature): LogEntry { | |||||
| return this.line(user, target) | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Deals damage. | |||||
| */ | |||||
| export class DamageConsequence extends Consequence { | |||||
| constructor (private damageFormula: DamageFormula, conditions: Condition[] = []) { | |||||
| super(conditions) | |||||
| } | |||||
| apply (user: Creature, target: Creature): LogEntry { | |||||
| const damage = this.damageFormula.calc(user, target) | |||||
| return new LogLines( | |||||
| new LogLine(`${target.name.capital} ${target.name.conjugate(new Verb('take'))} `, damage.renderShort(), ` damage!`), | |||||
| target.takeDamage(damage) | |||||
| ) | |||||
| } | |||||
| } | |||||
| /** | |||||
| * Applies a status effect | |||||
| */ | |||||
| export class StatusConsequence extends Consequence { | |||||
| constructor (private statusMaker: () => StatusEffect, conditions: Condition[] = []) { | |||||
| super(conditions) | |||||
| } | |||||
| apply (user: Creature, target: Creature): LogEntry { | |||||
| return target.applyEffect(this.statusMaker()) | |||||
| } | |||||
| } | |||||
| @@ -1,4 +1,4 @@ | |||||
| import { StatusEffect, Damage, DamageType, Action } from '../combat' | |||||
| import { StatusEffect, Damage, DamageType, Action, Condition } from '../combat' | |||||
| import { DynText, LiveText, ToBe, Verb } from '../language' | import { DynText, LiveText, ToBe, Verb } from '../language' | ||||
| import { Creature } from "../creature" | import { Creature } from "../creature" | ||||
| import { LogLine, LogEntry, LogLines, FAElem, nilLog } from '../interface' | import { LogLine, LogEntry, LogLines, FAElem, nilLog } from '../interface' | ||||
| @@ -38,7 +38,7 @@ export class StunEffect extends StatusEffect { | |||||
| return new LogLine(`${creature.name.capital} ${creature.name.conjugate(new ToBe())} no longer stunned.`) | return new LogLine(`${creature.name.capital} ${creature.name.conjugate(new ToBe())} no longer stunned.`) | ||||
| } | } | ||||
| preAction (creature: Creature): { prevented: boolean; log: LogEntry } { | |||||
| preTurn (creature: Creature): { prevented: boolean; log: LogEntry } { | |||||
| if (--this.duration <= 0) { | if (--this.duration <= 0) { | ||||
| return { | return { | ||||
| prevented: true, | prevented: true, | ||||
| @@ -96,3 +96,37 @@ export class PredatorCounterEffect extends StatusEffect { | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| export class UntouchableEffect extends StatusEffect { | |||||
| constructor () { | |||||
| super('Untouchable', 'Cannot be attacked', 'fas fa-times') | |||||
| } | |||||
| preAttack (creature: Creature, attacker: Creature) { | |||||
| return { | |||||
| prevented: true, | |||||
| log: new LogLine(`${creature.name.capital} cannot be attacked.`) | |||||
| } | |||||
| } | |||||
| } | |||||
| export class DazzlingEffect extends StatusEffect { | |||||
| constructor (private conditions: Condition[]) { | |||||
| super('Dazzling', 'Stuns enemies who try to affect this creature', 'fas fa-spinner') | |||||
| } | |||||
| preReceiveAction (creature: Creature, attacker: Creature) { | |||||
| if (this.conditions.every(cond => cond.allowed(creature, attacker))) { | |||||
| attacker.applyEffect(new StunEffect(1)) | |||||
| return { | |||||
| prevented: true, | |||||
| log: new LogLine(`${attacker.name.capital} can't act against ${creature.name.objective}!`) | |||||
| } | |||||
| } else { | |||||
| return { | |||||
| prevented: false, | |||||
| log: nilLog | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -38,8 +38,10 @@ export class Creature extends Vore implements Combatant { | |||||
| } | } | ||||
| executeAction (action: Action, target: Creature): LogEntry { | executeAction (action: Action, target: Creature): LogEntry { | ||||
| const effectResults = this.effects.map(effect => effect.preAction(this)) | |||||
| const blocking = effectResults.filter(result => result.prevented) | |||||
| const preActionResults = this.effects.map(effect => effect.preAction(this)) | |||||
| const preReceiveActionResults = target.effects.map(effect => effect.preReceiveAction(target, this)) | |||||
| const blocking = preActionResults.concat(preReceiveActionResults).filter(result => result.prevented) | |||||
| if (blocking.length > 0) { | if (blocking.length > 0) { | ||||
| return new LogLines(...blocking.map(result => result.log)) | return new LogLines(...blocking.map(result => result.log)) | ||||
| } else { | } else { | ||||
| @@ -6,4 +6,6 @@ import { Withers } from './creatures/withers' | |||||
| import { Kenzie } from './creatures/kenzie' | import { Kenzie } from './creatures/kenzie' | ||||
| import { Dragon } from './creatures/dragon' | import { Dragon } from './creatures/dragon' | ||||
| import { Shingo } from './creatures/shingo' | import { Shingo } from './creatures/shingo' | ||||
| export { Wolf, Player, Cafat, Human, Withers, Kenzie, Dragon, Shingo } | |||||
| import { Goldeneye } from './creatures/goldeneye' | |||||
| export { Wolf, Player, Cafat, Human, Withers, Kenzie, Dragon, Shingo, Goldeneye } | |||||
| @@ -0,0 +1,190 @@ | |||||
| import { Creature } from "../creature" | |||||
| import { Damage, DamageType, ConstantDamageFormula, Vigor, Side, GroupAction, FractionDamageFormula, DamageFormula, UniformRandomDamageFormula, CompositionAction, StatusEffect } from '../combat' | |||||
| import { MalePronouns, ImproperNoun, Verb, ProperNoun, ToBe, SoloLineArgs } from '../language' | |||||
| import { VoreType, NormalContainer, Vore, InnerVoreContainer, Container } from '../vore' | |||||
| import { TransferAction } from '../combat/actions' | |||||
| import { LogEntry, LogLine, LogLines } from '../interface' | |||||
| import { ContainerFullCondition, CapableCondition, EnemyCondition, TogetherCondition } from '../combat/conditions' | |||||
| import { UntouchableEffect, DazzlingEffect, StunEffect } from '../combat/effects' | |||||
| import { DamageConsequence, StatusConsequence, LogConsequence } from '../combat/consequences' | |||||
| class GoldeneyeCrop extends NormalContainer { | |||||
| consumeVerb: Verb = new Verb('swallow') | |||||
| releaseVerb: Verb = new Verb('free') | |||||
| struggleVerb: Verb = new Verb('struggle', 'struggles', 'struggling', 'struggled') | |||||
| constructor (owner: Vore) { | |||||
| super( | |||||
| new ImproperNoun('crop').all, | |||||
| owner, | |||||
| new Set([VoreType.Oral]), | |||||
| 300 | |||||
| ) | |||||
| } | |||||
| } | |||||
| class Taunt extends GroupAction { | |||||
| damage: DamageFormula = new UniformRandomDamageFormula( | |||||
| new Damage( | |||||
| { amount: 50, target: Vigor.Resolve, type: DamageType.Dominance } | |||||
| ), | |||||
| 0.5 | |||||
| ) | |||||
| constructor () { | |||||
| super( | |||||
| "Taunt", | |||||
| "Demoralize your enemies", | |||||
| [ | |||||
| new EnemyCondition(), | |||||
| new CapableCondition() | |||||
| ] | |||||
| ) | |||||
| } | |||||
| describeGroup (user: Creature, targets: Creature[]): LogEntry { | |||||
| return new LogLine(`Demoralize your foes`) | |||||
| } | |||||
| execute (user: Creature, target: Creature): LogEntry { | |||||
| return target.takeDamage(this.damage.calc(user, target)) | |||||
| } | |||||
| describe (user: Creature, target: Creature): LogEntry { | |||||
| return new LogLine(`Demoralize your foes`) | |||||
| } | |||||
| } | |||||
| class Flaunt extends GroupAction { | |||||
| constructor (public container: Container) { | |||||
| super( | |||||
| "Flaunt " + container.name, | |||||
| "Show off your " + container.name, | |||||
| [new ContainerFullCondition(container), new CapableCondition(), new EnemyCondition(), new TogetherCondition()] | |||||
| ) | |||||
| } | |||||
| groupLine: SoloLineArgs<Creature, { container: Container }> = (user, args) => new LogLine( | |||||
| `${user.name.capital} ${user.name.conjugate(new Verb('show'))} off ${user.pronouns.possessive} squirming ${args.container.name}, ${user.pronouns.possessive} doomed prey writhing beneath ${user.pronouns.possessive} pelt.` | |||||
| ) | |||||
| describeGroup (user: Creature, targets: Creature[]): LogEntry { | |||||
| return new LogLine(`Flaunt your bulging ${this.container.name} for all your foes to see`) | |||||
| } | |||||
| execute (user: Creature, target: Creature): LogEntry { | |||||
| const fracDamage = new FractionDamageFormula([ | |||||
| { fraction: 0.25, target: Vigor.Resolve, type: DamageType.Dominance } | |||||
| ]) | |||||
| const flatDamage = new ConstantDamageFormula( | |||||
| new Damage( | |||||
| { amount: 50, target: Vigor.Resolve, type: DamageType.Dominance } | |||||
| ) | |||||
| ) | |||||
| const damage = fracDamage.calc(user, target).combine(flatDamage.calc(user, target)) | |||||
| return new LogLines( | |||||
| new LogLine(`${target.name.capital} ${target.name.conjugate(new ToBe())} shaken for `, damage.renderShort(), '.'), | |||||
| target.takeDamage(damage) | |||||
| ) | |||||
| } | |||||
| executeGroup (user: Creature, targets: Array<Creature>): LogEntry { | |||||
| return new LogLines(...[this.groupLine(user, { container: this.container })].concat(targets.map(target => this.execute(user, target)))) | |||||
| } | |||||
| describe (user: Creature, target: Creature): LogEntry { | |||||
| return new LogLine(`Flaunt your bulging gut for all your foes to see`) | |||||
| } | |||||
| } | |||||
| class GoldeneyeStomach extends InnerVoreContainer { | |||||
| consumeVerb: Verb = new Verb('swallow') | |||||
| releaseVerb: Verb = new Verb('free') | |||||
| struggleVerb: Verb = new Verb('struggle', 'struggles', 'struggling', 'struggled') | |||||
| constructor (owner: Vore, crop: GoldeneyeCrop) { | |||||
| super( | |||||
| new ImproperNoun('stomach').all, | |||||
| owner, | |||||
| new Set([VoreType.Oral]), | |||||
| 900, | |||||
| new Damage( | |||||
| { amount: 1000, target: Vigor.Health, type: DamageType.Acid } | |||||
| ), | |||||
| crop | |||||
| ) | |||||
| } | |||||
| } | |||||
| export class Goldeneye extends Creature { | |||||
| constructor () { | |||||
| super( | |||||
| new ProperNoun("Goldeneye"), | |||||
| new ImproperNoun('gryphon', 'gryphons'), | |||||
| MalePronouns, | |||||
| { Toughness: 200, Power: 200, Speed: 200, Willpower: 200, Charm: 200 }, | |||||
| new Set(), | |||||
| new Set([VoreType.Oral]), | |||||
| 2000 | |||||
| ) | |||||
| this.title = "Not really a gryphon" | |||||
| this.desc = "Not really survivable, either." | |||||
| this.side = Side.Monsters | |||||
| this.applyEffect(new DazzlingEffect([ | |||||
| new EnemyCondition(), | |||||
| new TogetherCondition() | |||||
| ])) | |||||
| const crop = new GoldeneyeCrop(this) | |||||
| const stomach = new GoldeneyeStomach(this, crop) | |||||
| this.containers.push(stomach) | |||||
| this.otherContainers.push(crop) | |||||
| this.actions.push( | |||||
| new TransferAction( | |||||
| crop, | |||||
| stomach | |||||
| ) | |||||
| ) | |||||
| this.groupActions.push(new Flaunt(stomach)) | |||||
| this.groupActions.push(new Taunt()) | |||||
| this.actions.push(new CompositionAction( | |||||
| "Stomp", | |||||
| "Big step", | |||||
| { | |||||
| conditions: [ | |||||
| new TogetherCondition(), | |||||
| new EnemyCondition() | |||||
| ], | |||||
| consequences: [ | |||||
| new LogConsequence( | |||||
| (user, target) => new LogLine( | |||||
| `${user.name.capital} ${user.name.conjugate(new Verb('stomp'))} on ${target.name.objective} with crushing force!` | |||||
| ) | |||||
| ), | |||||
| new DamageConsequence( | |||||
| new FractionDamageFormula([ | |||||
| { fraction: 0.75, target: Vigor.Health, type: DamageType.Pure } | |||||
| ]) | |||||
| ), | |||||
| new DamageConsequence( | |||||
| new ConstantDamageFormula( | |||||
| new Damage( | |||||
| { amount: 50, target: Vigor.Health, type: DamageType.Crush } | |||||
| ) | |||||
| ) | |||||
| ), | |||||
| new StatusConsequence( | |||||
| () => new StunEffect(3) | |||||
| ) | |||||
| ] | |||||
| } | |||||
| )) | |||||
| } | |||||
| } | |||||
| @@ -195,7 +195,7 @@ export abstract class NormalContainer implements Container { | |||||
| } | } | ||||
| export abstract class InnerContainer extends NormalContainer { | export abstract class InnerContainer extends NormalContainer { | ||||
| constructor (name: Noun, owner: Vore, voreTypes: Set<VoreType>, capacity: number, private escape: VoreContainer) { | |||||
| constructor (name: Noun, owner: Vore, voreTypes: Set<VoreType>, capacity: number, private escape: Container) { | |||||
| super(name, owner, voreTypes, capacity) | super(name, owner, voreTypes, capacity) | ||||
| this.actions = [] | this.actions = [] | ||||
| @@ -276,8 +276,8 @@ export abstract class NormalVoreContainer extends NormalContainer implements Vor | |||||
| } | } | ||||
| } | } | ||||
| abstract class InnerVoreContainer extends NormalVoreContainer { | |||||
| constructor (name: Noun, owner: Vore, voreTypes: Set<VoreType>, capacity: number, damage: Damage, private escape: VoreContainer) { | |||||
| export abstract class InnerVoreContainer extends NormalVoreContainer { | |||||
| constructor (name: Noun, owner: Vore, voreTypes: Set<VoreType>, capacity: number, damage: Damage, private escape: Container) { | |||||
| super(name, owner, voreTypes, capacity, damage) | super(name, owner, voreTypes, capacity, damage) | ||||
| this.actions = [] | this.actions = [] | ||||