| @@ -17,6 +17,7 @@ module.exports = { | |||
| 'no-useless-constructor': 'off', | |||
| '@typescript-eslint/no-unused-vars': 'off', | |||
| 'quotes': 'off', | |||
| 'function-paren-newline': ['error', 'multiline-arguments'] | |||
| 'function-paren-newline': ['error', 'multiline-arguments'], | |||
| '@typescript-eslint/member-ordering': ['warn'] | |||
| } | |||
| } | |||
| @@ -97,17 +97,17 @@ export default class Combat extends Vue { | |||
| actionDescription = '' | |||
| created () { | |||
| this.$data.left = this.encounter.combatants.filter(x => x.side === Side.Heroes)[0] | |||
| this.$data.right = this.encounter.combatants.filter(x => x.side === Side.Monsters)[0] | |||
| this.$data.combatants = this.encounter.combatants | |||
| } | |||
| mounted () { | |||
| const leftStats = this.$el.querySelector("#left-stats") | |||
| @Emit("described") | |||
| described (entry: LogEntry) { | |||
| const actionDesc = document.querySelector("#action-desc") | |||
| if (leftStats !== null) { | |||
| leftStats.scrollTo(leftStats.getBoundingClientRect().width * 2, 0) | |||
| if (actionDesc !== null) { | |||
| const holder = document.createElement("div") | |||
| entry.render().forEach(element => { | |||
| holder.appendChild(element) | |||
| }) | |||
| actionDesc.innerHTML = '' | |||
| actionDesc.appendChild(holder) | |||
| } | |||
| } | |||
| @@ -176,17 +176,17 @@ export default class Combat extends Vue { | |||
| }) | |||
| } | |||
| @Emit("described") | |||
| described (entry: LogEntry) { | |||
| const actionDesc = document.querySelector("#action-desc") | |||
| created () { | |||
| this.$data.left = this.encounter.combatants.filter(x => x.side === Side.Heroes)[0] | |||
| this.$data.right = this.encounter.combatants.filter(x => x.side === Side.Monsters)[0] | |||
| this.$data.combatants = this.encounter.combatants | |||
| } | |||
| if (actionDesc !== null) { | |||
| const holder = document.createElement("div") | |||
| entry.render().forEach(element => { | |||
| holder.appendChild(element) | |||
| }) | |||
| actionDesc.innerHTML = '' | |||
| actionDesc.appendChild(holder) | |||
| mounted () { | |||
| const leftStats = this.$el.querySelector("#left-stats") | |||
| if (leftStats !== null) { | |||
| leftStats.scrollTo(leftStats.getBoundingClientRect().width * 2, 0) | |||
| } | |||
| } | |||
| } | |||
| @@ -7,6 +7,10 @@ import { VoreContainer, Container } from '../vore' | |||
| import { CapableCondition, UserDrainedVigorCondition, TogetherCondition, EnemyCondition, SoloCondition, PairCondition, ContainsCondition, ContainedByCondition } from './conditions' | |||
| export class PassAction extends Action { | |||
| constructor () { | |||
| super("Pass", "Do nothing", [new SoloCondition()]) | |||
| } | |||
| execute (user: Creature, target: Creature): LogEntry { | |||
| return nilLog | |||
| } | |||
| @@ -14,24 +18,11 @@ export class PassAction extends Action { | |||
| describe (user: Creature, target: Creature): LogEntry { | |||
| return new LogLine("Do nothing.") | |||
| } | |||
| constructor () { | |||
| super("Pass", "Do nothing", [new SoloCondition()]) | |||
| } | |||
| } | |||
| export class AttackAction extends Action { | |||
| protected test: StatTest | |||
| protected successLine: PairLineArgs<Creature, { damage: Damage }> = (user, target, args) => new LogLine( | |||
| `${user.name.capital} ${user.name.conjugate(this.verb)} ${target.name.objective} for `, | |||
| target.effectiveDamage(args.damage).renderShort() | |||
| ) | |||
| protected failLine: PairLine<Creature> = (user, target) => new LogLine( | |||
| `${user.name.capital} ${user.name.conjugate(new Verb('miss', 'misses'))} ${target.name.objective}` | |||
| ) | |||
| constructor (protected damage: DamageFormula, protected verb: Verb = new Verb('smack')) { | |||
| super( | |||
| verb.root.capital, | |||
| @@ -60,14 +51,28 @@ export class AttackAction extends Action { | |||
| describe (user: Creature, target: Creature): LogEntry { | |||
| return new LogLine(`Attack ${target.name}. `, this.damage.describe(user, target), '. ', this.test.explain(user, target)) | |||
| } | |||
| protected successLine: PairLineArgs<Creature, { damage: Damage }> = (user, target, args) => new LogLine( | |||
| `${user.name.capital} ${user.name.conjugate(this.verb)} ${target.name.objective} for `, | |||
| target.effectiveDamage(args.damage).renderShort() | |||
| ) | |||
| protected failLine: PairLine<Creature> = (user, target) => new LogLine( | |||
| `${user.name.capital} ${user.name.conjugate(new Verb('miss', 'misses'))} ${target.name.objective}` | |||
| ) | |||
| } | |||
| export class DevourAction extends Action { | |||
| private test: StatVigorTest | |||
| protected failLine: PairLineArgs<Entity, { container: Container }> = (user, target, args) => new LogLine( | |||
| `${user.name.capital} ${user.name.conjugate(new Verb('fail'))} to ${args.container.consumeVerb} ${target.name.objective}.` | |||
| ) | |||
| constructor (protected container: Container) { | |||
| super( | |||
| new DynText(new LiveText(container, x => x.consumeVerb.capital), ' (', new LiveText(container, x => x.name.all), ')'), | |||
| new LiveText(container, x => `Try to ${x.consumeVerb} your foe`), | |||
| [new CapableCondition(), new TogetherCondition()] | |||
| ) | |||
| this.test = new StatVigorTest(Stat.Power) | |||
| } | |||
| allowed (user: Creature, target: Creature): boolean { | |||
| const owner = this.container.owner === user | |||
| @@ -81,13 +86,8 @@ export class DevourAction extends Action { | |||
| } | |||
| } | |||
| constructor (protected container: Container) { | |||
| super( | |||
| new DynText(new LiveText(container, x => x.consumeVerb.capital), ' (', new LiveText(container, x => x.name.all), ')'), | |||
| new LiveText(container, x => `Try to ${x.consumeVerb} your foe`), | |||
| [new CapableCondition(), new TogetherCondition()] | |||
| ) | |||
| this.test = new StatVigorTest(Stat.Power) | |||
| describe (user: Creature, target: Creature): LogEntry { | |||
| return new LogLine(`Try to ${this.container.consumeVerb} your opponent, sending them to your ${this.container.name}. `, this.test.explain(user, target)) | |||
| } | |||
| execute (user: Creature, target: Creature): LogEntry { | |||
| @@ -98,19 +98,20 @@ export class DevourAction extends Action { | |||
| } | |||
| } | |||
| describe (user: Creature, target: Creature): LogEntry { | |||
| return new LogLine(`Try to ${this.container.consumeVerb} your opponent, sending them to your ${this.container.name}. `, this.test.explain(user, target)) | |||
| } | |||
| protected failLine: PairLineArgs<Entity, { container: Container }> = (user, target, args) => new LogLine( | |||
| `${user.name.capital} ${user.name.conjugate(new Verb('fail'))} to ${args.container.consumeVerb} ${target.name.objective}.` | |||
| ) | |||
| } | |||
| export class FeedAction extends Action { | |||
| protected successLine: PairLine<Entity> = (user, target) => new LogLine( | |||
| `${user.name.capital} ${user.name.conjugate(new Verb('feed'))} ${user.pronouns.reflexive} to ${target.name}. ` | |||
| ) | |||
| protected failLine: PairLine<Entity> = (user, target) => new LogLine( | |||
| `${user.name.capital} ${user.name.conjugate(new Verb('fail'))} to feed ${user.pronouns.reflexive} to ${target.name}. ` | |||
| ) | |||
| constructor (protected container: Container) { | |||
| super( | |||
| 'Feed', | |||
| 'Feed yourself to your opponent', | |||
| [new UserDrainedVigorCondition(Vigor.Resolve), new TogetherCondition()] | |||
| ) | |||
| this.name += ` (${container.name})` | |||
| } | |||
| allowed (user: Creature, target: Creature): boolean { | |||
| const owner = this.container.owner === target | |||
| @@ -124,15 +125,6 @@ export class FeedAction extends Action { | |||
| } | |||
| } | |||
| constructor (protected container: Container) { | |||
| super( | |||
| 'Feed', | |||
| 'Feed yourself to your opponent', | |||
| [new UserDrainedVigorCondition(Vigor.Resolve), new TogetherCondition()] | |||
| ) | |||
| this.name += ` (${container.name})` | |||
| } | |||
| execute (user: Creature, target: Creature): LogEntry { | |||
| return new LogLines(this.successLine(user, target), this.container.consume(user)) | |||
| } | |||
| @@ -140,18 +132,18 @@ export class FeedAction extends Action { | |||
| describe (user: Creature, target: Creature): LogEntry { | |||
| return new LogLine(`Your willpower is drained, and you really feel like shoving yourself into ${target.name}'s ${this.container.name}...`) | |||
| } | |||
| } | |||
| export class StruggleAction extends Action { | |||
| private test: StatVigorTest | |||
| protected successLine: PairLineArgs<Entity, { container: Container }> = (prey, pred, args) => new LogLine( | |||
| `${prey.name.capital} ${prey.name.conjugate(new Verb('escape'))} from ${pred.name.possessive} ${args.container.name}.` | |||
| protected successLine: PairLine<Entity> = (user, target) => new LogLine( | |||
| `${user.name.capital} ${user.name.conjugate(new Verb('feed'))} ${user.pronouns.reflexive} to ${target.name}. ` | |||
| ) | |||
| protected failLine: PairLineArgs<Entity, { container: Container }> = (prey, pred, args) => new LogLine( | |||
| `${prey.name.capital} ${prey.name.conjugate(new Verb('fail'))} to escape from ${pred.name.possessive} ${args.container.name}.` | |||
| protected failLine: PairLine<Entity> = (user, target) => new LogLine( | |||
| `${user.name.capital} ${user.name.conjugate(new Verb('fail'))} to feed ${user.pronouns.reflexive} to ${target.name}. ` | |||
| ) | |||
| } | |||
| export class StruggleAction extends Action { | |||
| private test: StatVigorTest | |||
| constructor (public container: Container) { | |||
| super( | |||
| @@ -177,17 +169,17 @@ export class StruggleAction extends Action { | |||
| describe (user: Creature, target: Creature): LogEntry { | |||
| return new LogLine(`Try to escape from ${target.name}'s ${this.container.name}. `, this.test.explain(user, target)) | |||
| } | |||
| protected successLine: PairLineArgs<Entity, { container: Container }> = (prey, pred, args) => new LogLine( | |||
| `${prey.name.capital} ${prey.name.conjugate(new Verb('escape'))} from ${pred.name.possessive} ${args.container.name}.` | |||
| ) | |||
| protected failLine: PairLineArgs<Entity, { container: Container }> = (prey, pred, args) => new LogLine( | |||
| `${prey.name.capital} ${prey.name.conjugate(new Verb('fail'))} to escape from ${pred.name.possessive} ${args.container.name}.` | |||
| ) | |||
| } | |||
| export abstract class EatenAction extends Action { | |||
| allowed (user: Creature, target: Creature) { | |||
| if (target.containedIn === this.container) { | |||
| return super.allowed(user, target) | |||
| } else { | |||
| return false | |||
| } | |||
| } | |||
| constructor (public container: Container, name: TextLike, desc: string) { | |||
| super( | |||
| new DynText(name, ' (', new LiveText(container, x => x.name.all), ')'), | |||
| @@ -195,17 +187,17 @@ export abstract class EatenAction extends Action { | |||
| [new CapableCondition(), new PairCondition()] | |||
| ) | |||
| } | |||
| } | |||
| export class DigestAction extends Action { | |||
| allowed (user: Creature, target: Creature) { | |||
| if (this.container.owner === user && this.container.contents.length > 0) { | |||
| if (target.containedIn === this.container) { | |||
| return super.allowed(user, target) | |||
| } else { | |||
| return false | |||
| } | |||
| } | |||
| } | |||
| export class DigestAction extends Action { | |||
| constructor (protected container: VoreContainer) { | |||
| super( | |||
| new DynText('Digest (', new LiveText(container, container => container.name.all), ')'), | |||
| @@ -214,6 +206,14 @@ export class DigestAction extends Action { | |||
| ) | |||
| } | |||
| allowed (user: Creature, target: Creature) { | |||
| if (this.container.owner === user && this.container.contents.length > 0) { | |||
| return super.allowed(user, target) | |||
| } else { | |||
| return false | |||
| } | |||
| } | |||
| execute (user: Creature, target: Creature): LogEntry { | |||
| const results = this.container.tick(60) | |||
| return new CompositeLog(results) | |||
| @@ -225,14 +225,6 @@ export class DigestAction extends Action { | |||
| } | |||
| export class ReleaseAction extends Action { | |||
| allowed (user: Creature, target: Creature) { | |||
| if (target.containedIn === this.container && this.container.contents.indexOf(target) >= 0) { | |||
| return super.allowed(user, target) | |||
| } else { | |||
| return false | |||
| } | |||
| } | |||
| constructor (protected container: Container) { | |||
| super( | |||
| new DynText('Release (', new LiveText(container, x => x.name.all), ')'), | |||
| @@ -241,6 +233,14 @@ export class ReleaseAction extends Action { | |||
| ) | |||
| } | |||
| allowed (user: Creature, target: Creature) { | |||
| if (target.containedIn === this.container && this.container.contents.indexOf(target) >= 0) { | |||
| return super.allowed(user, target) | |||
| } else { | |||
| return false | |||
| } | |||
| } | |||
| execute (user: Creature, target: Creature): LogEntry { | |||
| return this.container.release(target) | |||
| } | |||
| @@ -253,6 +253,14 @@ export class ReleaseAction extends Action { | |||
| export class TransferAction extends Action { | |||
| verb: Verb = new Verb('send') | |||
| constructor (protected from: Container, protected to: Container, name = 'Transfer') { | |||
| super( | |||
| name, | |||
| `${from.name.all.capital} to ${to.name.all}`, | |||
| [new CapableCondition(), new PairCondition()] | |||
| ) | |||
| } | |||
| line: PairLineArgs<Creature, { from: Container; to: Container }> = (user, target, args) => new LogLine( | |||
| `${user.name.capital} ${user.name.conjugate(this.verb)} ${target.name.objective} from ${user.pronouns.possessive} ${args.from.name} to ${user.pronouns.possessive} ${args.to.name}` | |||
| ) | |||
| @@ -265,14 +273,6 @@ export class TransferAction extends Action { | |||
| } | |||
| } | |||
| constructor (protected from: Container, protected to: Container, name = 'Transfer') { | |||
| super( | |||
| name, | |||
| `${from.name.all.capital} to ${to.name.all}`, | |||
| [new CapableCondition(), new PairCondition()] | |||
| ) | |||
| } | |||
| execute (user: Creature, target: Creature): LogEntry { | |||
| this.from.release(target) | |||
| this.to.consume(target) | |||
| @@ -8,11 +8,6 @@ import { InstantKillEffect } from '../combat/effects' | |||
| import * as Words from '../words' | |||
| class BellyCrushAction extends AttackAction { | |||
| successLine: PairLineArgs<Creature, { damage: Damage }> = (user, target, args) => new LogLines(new LogLine( | |||
| `${user.name.capital} ${user.name.conjugate(new Verb('crush', 'crushes'))} on ${target.name.objective} with ${user.pronouns.possessive} belly for `, | |||
| target.effectiveDamage(args.damage).renderShort() | |||
| ), new ImgElem('./media/cafat/images/belly-crush.webp')) | |||
| constructor (_damage: Damage) { | |||
| super({ | |||
| calc (user) { return _damage.scale(user.voreStats.Bulk / 25) }, | |||
| @@ -26,9 +21,20 @@ class BellyCrushAction extends AttackAction { | |||
| describe (user: Creature, target: Creature): LogEntry { | |||
| return new LogLine(`Crush ${target.name} under your gut. `, this.damage.describe(user, target)) | |||
| } | |||
| successLine: PairLineArgs<Creature, { damage: Damage }> = (user, target, args) => new LogLines(new LogLine( | |||
| `${user.name.capital} ${user.name.conjugate(new Verb('crush', 'crushes'))} on ${target.name.objective} with ${user.pronouns.possessive} belly for `, | |||
| target.effectiveDamage(args.damage).renderShort() | |||
| ), new ImgElem('./media/cafat/images/belly-crush.webp')) | |||
| } | |||
| class BelchAction extends AttackAction { | |||
| constructor (damage: Damage) { | |||
| super(new ConstantDamageFormula(damage)) | |||
| this.name = 'Belch' | |||
| this.desc = 'Drain your foe\'s stats with a solid BELCH' | |||
| } | |||
| successLine: PairLineArgs<Creature, { damage: Damage }> = (user, target, args) => new LogLines( | |||
| new LogLine( | |||
| `${user.name.capital} ${user.name.conjugate(new Verb('belch', 'belches'))} on ${target.name.objective} for `, | |||
| @@ -36,32 +42,26 @@ class BelchAction extends AttackAction { | |||
| ), | |||
| new ImgElem('./media/cafat/images/belch.webp') | |||
| ) | |||
| constructor (damage: Damage) { | |||
| super(new ConstantDamageFormula(damage)) | |||
| this.name = 'Belch' | |||
| this.desc = 'Drain your foe\'s stats with a solid BELCH' | |||
| } | |||
| } | |||
| class CrushAction extends EatenAction { | |||
| line: PairLineArgs<Creature, { container: VoreContainer }> = (user, target, args) => new LogLine( | |||
| `${user.name.capital.possessive} ${args.container.name} ${Words.Brutally} ${user.name.conjugate(new Verb('crush', 'crushes'))} ${target.name.objective}; ${user.pronouns.subjective} ${user.pronouns.conjugate(new Verb('belch', 'belches'))} as ${user.pronouns.possessive} gut lets out a fatal CRUNCH `, | |||
| new ImgElem('./media/cafat/images/crunch.webp') | |||
| ) | |||
| constructor (private _container: VoreContainer) { | |||
| super(_container, "Crush", "Crush 'em!") | |||
| this.desc = "Crush somebody in your gut" | |||
| } | |||
| describe (user: Creature, target: Creature): LogEntry { | |||
| return new LogLine(`Crush ${target.name} in your ${this.container.name} for massive, unavoidable damage.`) | |||
| } | |||
| execute (user: Creature, target: Creature): LogEntry { | |||
| return new LogLines(this.line(user, target, { container: this._container }), target.applyEffect(new InstantKillEffect())) | |||
| } | |||
| describe (user: Creature, target: Creature): LogEntry { | |||
| return new LogLine(`Crush ${target.name} in your ${this.container.name} for massive, unavoidable damage.`) | |||
| } | |||
| line: PairLineArgs<Creature, { container: VoreContainer }> = (user, target, args) => new LogLine( | |||
| `${user.name.capital.possessive} ${args.container.name} ${Words.Brutally} ${user.name.conjugate(new Verb('crush', 'crushes'))} ${target.name.objective}; ${user.pronouns.subjective} ${user.pronouns.conjugate(new Verb('belch', 'belches'))} as ${user.pronouns.possessive} gut lets out a fatal CRUNCH `, | |||
| new ImgElem('./media/cafat/images/crunch.webp') | |||
| ) | |||
| } | |||
| export class Cafat extends Creature { | |||
| @@ -26,9 +26,9 @@ export abstract class Entity { | |||
| } | |||
| } | |||
| title: TextLike = "Some thing." | |||
| desc: TextLike = "It's a ting." | |||
| perspective: POV = POV.Third | |||
| title: TextLike = "Some thing." | |||
| constructor (public baseName: Noun, public kind: Noun, public basePronouns: Pronoun) { | |||
| @@ -36,17 +36,23 @@ export abstract class Entity { | |||
| } | |||
| export abstract class Mortal extends Entity { | |||
| abstract destroy (): LogEntry; | |||
| abstract effectiveDamage (damage: Damage): Damage | |||
| resistances: Map<DamageType, number> = new Map() | |||
| stats: Stats; | |||
| vigors: {[key in Vigor]: number} = { | |||
| [Vigor.Health]: 100, | |||
| [Vigor.Stamina]: 100, | |||
| [Vigor.Resolve]: 100 | |||
| } | |||
| constructor (name: Noun, kind: Noun, pronouns: Pronoun, public baseStats: Stats) { | |||
| super(name, kind, pronouns) | |||
| Object.entries(this.maxVigors).forEach(([key, val]) => { | |||
| this.vigors[key as Vigor] = val | |||
| }) | |||
| this.stats = Object.keys(Stat).reduce((base: any, key) => { base[key] = baseStats[key as Stat]; return base }, {}) | |||
| } | |||
| get maxVigors (): Readonly<Vigors> { | |||
| return { | |||
| Health: this.stats.Toughness * 10 + this.stats.Power * 5, | |||
| @@ -96,33 +102,33 @@ export abstract class Mortal extends Entity { | |||
| return this.name.toString() | |||
| } | |||
| constructor (name: Noun, kind: Noun, pronouns: Pronoun, public baseStats: Stats) { | |||
| super(name, kind, pronouns) | |||
| Object.entries(this.maxVigors).forEach(([key, val]) => { | |||
| this.vigors[key as Vigor] = val | |||
| }) | |||
| this.stats = Object.keys(Stat).reduce((base: any, key) => { base[key] = baseStats[key as Stat]; return base }, {}) | |||
| } | |||
| abstract destroy (): LogEntry; | |||
| abstract effectiveDamage (damage: Damage): Damage | |||
| } | |||
| export class Creature extends Vore implements Combatant { | |||
| title = "Lv. 1 Creature" | |||
| actions: Array<Action> = [] | |||
| containedIn: VoreContainer|null = null; | |||
| desc = "Some creature" | |||
| effects: Array<StatusEffect> = [] | |||
| groupActions: Array<GroupAction> = [] | |||
| items: Array<Item> = [] | |||
| otherActions: Array<Action> = [] | |||
| side: Side | |||
| title = "Lv. 1 Creature" | |||
| effects: Array<StatusEffect> = [] | |||
| constructor (name: Noun, kind: Noun, pronouns: Pronoun, stats: Stats, preyPrefs: Set<VoreType>, predPrefs: Set<VoreType>, mass: number) { | |||
| super(name, kind, pronouns, stats, preyPrefs, predPrefs, mass) | |||
| this.actions.push(new PassAction()) | |||
| this.side = Side.Heroes | |||
| } | |||
| applyEffect (effect: StatusEffect): LogEntry { | |||
| this.effects.push(effect) | |||
| return effect.onApply(this) | |||
| } | |||
| removeEffect (effect: StatusEffect): LogEntry { | |||
| this.effects = this.effects.filter(eff => eff !== effect) | |||
| return effect.onRemove(this) | |||
| } | |||
| /** | |||
| * Determines how much damage an attack would do | |||
| */ | |||
| @@ -132,6 +138,21 @@ export class Creature extends Vore implements Combatant { | |||
| }, damage) | |||
| } | |||
| executeAction (action: Action, target: Creature): LogEntry { | |||
| const effectResults = this.effects.map(effect => effect.preAction(this)) | |||
| const blocking = effectResults.filter(result => result.prevented) | |||
| if (blocking.length > 0) { | |||
| return new LogLines(...blocking.map(result => result.log)) | |||
| } else { | |||
| return action.execute(this, target) | |||
| } | |||
| } | |||
| removeEffect (effect: StatusEffect): LogEntry { | |||
| this.effects = this.effects.filter(eff => eff !== effect) | |||
| return effect.onRemove(this) | |||
| } | |||
| get status (): Array<VisibleStatus> { | |||
| const results: Array<VisibleStatus> = [] | |||
| @@ -154,30 +175,6 @@ export class Creature extends Vore implements Combatant { | |||
| return results | |||
| } | |||
| executeAction (action: Action, target: Creature): LogEntry { | |||
| const effectResults = this.effects.map(effect => effect.preAction(this)) | |||
| const blocking = effectResults.filter(result => result.prevented) | |||
| if (blocking.length > 0) { | |||
| return new LogLines(...blocking.map(result => result.log)) | |||
| } else { | |||
| return action.execute(this, target) | |||
| } | |||
| } | |||
| actions: Array<Action> = [] | |||
| groupActions: Array<GroupAction> = [] | |||
| otherActions: Array<Action> = [] | |||
| items: Array<Item> = [] | |||
| containedIn: VoreContainer|null = null; | |||
| constructor (name: Noun, kind: Noun, pronouns: Pronoun, stats: Stats, preyPrefs: Set<VoreType>, predPrefs: Set<VoreType>, mass: number) { | |||
| super(name, kind, pronouns, stats, preyPrefs, predPrefs, mass) | |||
| this.actions.push(new PassAction()) | |||
| this.side = Side.Heroes | |||
| } | |||
| validActions (target: Creature): Array<Action> { | |||
| let choices = this.actions.concat( | |||
| this.containers.flatMap(container => container.actions) | |||
| @@ -13,46 +13,13 @@ export enum VoreType { | |||
| } | |||
| export abstract class Vore extends Mortal { | |||
| destroyed = false; | |||
| voreStats: VoreStats | |||
| containedIn: Container | null = null | |||
| containers: Array<VoreContainer> = [] | |||
| otherContainers: Array<Container> = [] | |||
| destroy (): LogEntry { | |||
| const line: SoloLine<Vore> = (victim) => new LogLine( | |||
| `${victim.name.capital} ${victim.name.conjugate(new Verb('die'))}` | |||
| ) | |||
| const released: Array<Vore> = this.containers.flatMap(container => { | |||
| return container.contents.map(prey => { | |||
| prey.containedIn = this.containedIn | |||
| if (this.containedIn !== null) { | |||
| this.containedIn.contents.push(prey) | |||
| } | |||
| return prey | |||
| }) | |||
| }) | |||
| const names = released.reduce((list: Array<string>, prey: Vore) => list.concat([prey.name.toString()]), []).joinGeneral(", ", " and ").join("") | |||
| containedIn: Container | null = null | |||
| destroyed = false; | |||
| if (released.length > 0) { | |||
| if (this.containedIn === null) { | |||
| return new LogLines( | |||
| line(this), | |||
| new LogLine(names + ` spill out!`) | |||
| ) | |||
| } else { | |||
| return new LogLines( | |||
| line(this), | |||
| new LogLine(names + ` spill out into ${this.containedIn.owner.name}'s ${this.containedIn.name}!`) | |||
| ) | |||
| } | |||
| } else { | |||
| return line(this) | |||
| } | |||
| } | |||
| voreStats: VoreStats | |||
| constructor (name: Noun, kind: Noun, pronouns: Pronoun, baseStats: Stats, public preyPrefs: Set<VoreType>, public predPrefs: Set<VoreType>, public mass: number) { | |||
| super(name, kind, pronouns, baseStats) | |||
| @@ -94,20 +61,57 @@ export abstract class Vore extends Mortal { | |||
| } | |||
| } | |||
| } | |||
| destroy (): LogEntry { | |||
| const line: SoloLine<Vore> = (victim) => new LogLine( | |||
| `${victim.name.capital} ${victim.name.conjugate(new Verb('die'))}` | |||
| ) | |||
| const released: Array<Vore> = this.containers.flatMap(container => { | |||
| return container.contents.map(prey => { | |||
| prey.containedIn = this.containedIn | |||
| if (this.containedIn !== null) { | |||
| this.containedIn.contents.push(prey) | |||
| } | |||
| return prey | |||
| }) | |||
| }) | |||
| const names = released.reduce((list: Array<string>, prey: Vore) => list.concat([prey.name.toString()]), []).joinGeneral(", ", " and ").join("") | |||
| if (released.length > 0) { | |||
| if (this.containedIn === null) { | |||
| return new LogLines( | |||
| line(this), | |||
| new LogLine(names + ` spill out!`) | |||
| ) | |||
| } else { | |||
| return new LogLines( | |||
| line(this), | |||
| new LogLine(names + ` spill out into ${this.containedIn.owner.name}'s ${this.containedIn.name}!`) | |||
| ) | |||
| } | |||
| } else { | |||
| return line(this) | |||
| } | |||
| } | |||
| } | |||
| export interface Container extends Actionable { | |||
| name: Noun; | |||
| owner: Vore; | |||
| voreTypes: Set<VoreType>; | |||
| contents: Array<Vore>; | |||
| capacity: number; | |||
| fullness: number; | |||
| contents: Array<Vore>; | |||
| describe: () => LogEntry; | |||
| canTake: (prey: Vore) => boolean; | |||
| consume: (prey: Vore) => LogEntry; | |||
| release: (prey: Vore) => LogEntry; | |||
| struggle: (prey: Vore) => LogEntry; | |||
| describe: () => LogEntry; | |||
| consumeVerb: Verb; | |||
| releaseVerb: Verb; | |||
| @@ -119,6 +123,17 @@ export abstract class NormalContainer implements Container { | |||
| contents: Array<Vore> = [] | |||
| actions: Array<Action> = [] | |||
| abstract consumeVerb: Verb | |||
| abstract releaseVerb: Verb | |||
| abstract struggleVerb: Verb | |||
| constructor (name: Noun, public owner: Vore, public voreTypes: Set<VoreType>, public capacity: number) { | |||
| this.name = name.all | |||
| this.actions.push(new DevourAction(this)) | |||
| this.actions.push(new ReleaseAction(this)) | |||
| this.actions.push(new StruggleAction(this)) | |||
| } | |||
| consumeLine: PairLineArgs<Vore, { container: Container }> = (user, target, args) => { | |||
| return new LogLine(`${user.name.capital} ${user.name.conjugate(this.consumeVerb)} ${target.name.objective} in ${user.pronouns.possessive} ${args.container.name}.`) | |||
| } | |||
| @@ -131,10 +146,6 @@ export abstract class NormalContainer implements Container { | |||
| return new LogLine(`${user.name.capital} ${user.name.conjugate(this.struggleVerb)} within ${target.name.possessive} ${args.container.name}.`) | |||
| } | |||
| abstract consumeVerb: Verb | |||
| abstract releaseVerb: Verb | |||
| abstract struggleVerb: Verb | |||
| get fullness (): number { | |||
| return Array.from(this.contents.values()).reduce((total: number, prey: Vore) => total + prey.voreStats.Bulk, 0) | |||
| } | |||
| @@ -181,13 +192,6 @@ export abstract class NormalContainer implements Container { | |||
| return new LogLine(...lines) | |||
| } | |||
| constructor (name: Noun, public owner: Vore, public voreTypes: Set<VoreType>, public capacity: number) { | |||
| this.name = name.all | |||
| this.actions.push(new DevourAction(this)) | |||
| this.actions.push(new ReleaseAction(this)) | |||
| this.actions.push(new StruggleAction(this)) | |||
| } | |||
| } | |||
| export interface VoreContainer extends Container { | |||
| @@ -197,8 +201,20 @@ export interface VoreContainer extends Container { | |||
| } | |||
| export abstract class NormalVoreContainer extends NormalContainer implements VoreContainer { | |||
| consumeVerb = new Verb('devour') | |||
| releaseVerb = new Verb('release', 'releases', 'releasing', 'released') | |||
| struggleVerb = new Verb('struggle', 'struggles', 'struggling', 'struggled') | |||
| digested: Array<Vore> = [] | |||
| constructor (name: Noun, owner: Vore, voreTypes: Set<VoreType>, capacity: number, private damage: Damage) { | |||
| super(name, owner, voreTypes, capacity) | |||
| this.name = name | |||
| this.actions.push(new DigestAction(this)) | |||
| } | |||
| consumeLine: PairLineArgs<Vore, { container: Container }> = (user, target, args) => { | |||
| return new LogLine(`${user.name.capital} ${user.name.conjugate(this.consumeVerb)} ${target.name.objective}, forcing ${target.pronouns.objective} into ${user.pronouns.possessive} ${args.container.name}.`) | |||
| } | |||
| @@ -211,10 +227,6 @@ export abstract class NormalVoreContainer extends NormalContainer implements Vor | |||
| return new LogLine(`${user.name.capital.possessive} ${args.container.name} finishes ${Words.Digests.present} ${target.name.objective} down, ${target.pronouns.possessive} ${Words.Struggles.singular} fading away.`) | |||
| } | |||
| consumeVerb = new Verb('devour') | |||
| releaseVerb = new Verb('release', 'releases', 'releasing', 'released') | |||
| struggleVerb = new Verb('struggle', 'struggles', 'struggling', 'struggled') | |||
| tick (dt: number): LogEntry { | |||
| const justDigested: Array<Vore> = [] | |||
| @@ -246,23 +258,9 @@ export abstract class NormalVoreContainer extends NormalContainer implements Vor | |||
| digest (preys: Vore[]): LogEntry { | |||
| return new LogLines(...preys.map(prey => this.digestLine(this.owner, prey, { container: this }))) | |||
| } | |||
| constructor (name: Noun, owner: Vore, voreTypes: Set<VoreType>, capacity: number, private damage: Damage) { | |||
| super(name, owner, voreTypes, capacity) | |||
| this.name = name | |||
| this.actions.push(new DigestAction(this)) | |||
| } | |||
| } | |||
| abstract class InnerContainer extends NormalVoreContainer { | |||
| release (prey: Vore): LogEntry { | |||
| prey.containedIn = this.escape | |||
| this.contents = this.contents.filter(victim => victim !== prey) | |||
| return this.releaseLine(this.owner, prey, { container: this }) | |||
| } | |||
| constructor (name: Noun, owner: Vore, voreTypes: Set<VoreType>, capacity: number, damage: Damage, private escape: VoreContainer) { | |||
| super(name, owner, voreTypes, capacity, damage) | |||
| @@ -271,6 +269,12 @@ abstract class InnerContainer extends NormalVoreContainer { | |||
| this.actions.push(new DigestAction(this)) | |||
| this.actions.push(new StruggleAction(this)) | |||
| } | |||
| release (prey: Vore): LogEntry { | |||
| prey.containedIn = this.escape | |||
| this.contents = this.contents.filter(victim => victim !== prey) | |||
| return this.releaseLine(this.owner, prey, { container: this }) | |||
| } | |||
| } | |||
| export class Stomach extends NormalVoreContainer { | |||