| @@ -110,6 +110,9 @@ export default class Statblock extends Vue { | |||
| @Prop() | |||
| initiative!: number | |||
| firstperson: POV = POV.First | |||
| thirdperson: POV = POV.Third | |||
| private vigorIcons = VigorIcons | |||
| private statIcons = StatIcons | |||
| private voreStatIcons = VoreStatIcons | |||
| @@ -118,24 +121,6 @@ export default class Statblock extends Vue { | |||
| private voreStatDescs = VoreStatDescs | |||
| private vigor = Vigor | |||
| firstperson: POV = POV.First | |||
| thirdperson: POV = POV.Third | |||
| mounted () { | |||
| const statEntries = Array.from(this.$el.querySelectorAll(".stat-entry")) | |||
| const name = Array.from(this.$el.querySelectorAll(".name")) | |||
| const tippyInstances = statEntries.concat(name).map(elem => { | |||
| const tooltip = elem.querySelector(".tooltip-template") as HTMLElement | |||
| return tippy(elem, { | |||
| content: tooltip | |||
| }) | |||
| }) | |||
| createSingleton(tippyInstances, { delay: 500 }) | |||
| this.statusChanged([]) | |||
| } | |||
| @Watch('subject.status') | |||
| private statusChanged (a: Array<VisibleStatus>) { | |||
| this.$nextTick(() => { | |||
| @@ -151,6 +136,21 @@ export default class Statblock extends Vue { | |||
| }) | |||
| }) | |||
| } | |||
| mounted () { | |||
| const statEntries = Array.from(this.$el.querySelectorAll(".stat-entry")) | |||
| const name = Array.from(this.$el.querySelectorAll(".name")) | |||
| const tippyInstances = statEntries.concat(name).map(elem => { | |||
| const tooltip = elem.querySelector(".tooltip-template") as HTMLElement | |||
| return tippy(elem, { | |||
| content: tooltip | |||
| }) | |||
| }) | |||
| createSingleton(tippyInstances, { delay: 500 }) | |||
| this.statusChanged([]) | |||
| } | |||
| } | |||
| </script> | |||
| @@ -167,14 +167,14 @@ export interface DamageFormula { | |||
| * Simply returns the damage it was given. | |||
| */ | |||
| export class ConstantDamageFormula implements DamageFormula { | |||
| calc (user: Creature, target: Creature): Damage { | |||
| return this.damage | |||
| } | |||
| constructor (private damage: Damage) { | |||
| } | |||
| calc (user: Creature, target: Creature): Damage { | |||
| return this.damage | |||
| } | |||
| describe (user: Creature, target: Creature): LogEntry { | |||
| return this.explain(user) | |||
| } | |||
| @@ -188,14 +188,14 @@ export class ConstantDamageFormula implements DamageFormula { | |||
| * Randomly scales the damage it was given with a factor of (1-x) to (1+x) | |||
| */ | |||
| export class UniformRandomDamageFormula implements DamageFormula { | |||
| calc (user: Creature, target: Creature): Damage { | |||
| return this.damage.scale(Math.random() * this.variance * 2 - this.variance + 1) | |||
| } | |||
| constructor (private damage: Damage, private variance: number) { | |||
| } | |||
| calc (user: Creature, target: Creature): Damage { | |||
| return this.damage.scale(Math.random() * this.variance * 2 - this.variance + 1) | |||
| } | |||
| describe (user: Creature, target: Creature): LogEntry { | |||
| return this.explain(user) | |||
| } | |||
| @@ -206,6 +206,10 @@ export class UniformRandomDamageFormula implements DamageFormula { | |||
| } | |||
| export class StatDamageFormula implements DamageFormula { | |||
| constructor (private factors: Array<{ stat: Stat|VoreStat; fraction: number; type: DamageType; target: Vigor|Stat }>) { | |||
| } | |||
| calc (user: Creature, target: Creature): Damage { | |||
| const instances: Array<DamageInstance> = this.factors.map(factor => { | |||
| if (factor.stat in Stat) { | |||
| @@ -252,10 +256,6 @@ export class StatDamageFormula implements DamageFormula { | |||
| )).joinGeneral(new LogLine(`, `), new LogLine(` and `)) | |||
| ) | |||
| } | |||
| constructor (private factors: Array<{ stat: Stat|VoreStat; fraction: number; type: DamageType; target: Vigor|Stat }>) { | |||
| } | |||
| } | |||
| export enum Side { | |||
| @@ -276,21 +276,20 @@ export interface Combatant { | |||
| * An Action is anything that can be done by a [[Creature]] to a [[Creature]]. | |||
| */ | |||
| export abstract class Action { | |||
| allowed (user: Creature, target: Creature): boolean { | |||
| return this.conditions.every(cond => cond.allowed(user, target)) | |||
| } | |||
| abstract execute (user: Creature, target: Creature): LogEntry | |||
| abstract describe (user: Creature, target: Creature): LogEntry | |||
| constructor (public name: TextLike, public desc: TextLike, private conditions: Array<Condition> = []) { | |||
| } | |||
| allowed (user: Creature, target: Creature): boolean { | |||
| return this.conditions.every(cond => cond.allowed(user, target)) | |||
| } | |||
| toString (): string { | |||
| return this.name.toString() | |||
| } | |||
| abstract execute (user: Creature, target: Creature): LogEntry | |||
| abstract describe (user: Creature, target: Creature): LogEntry | |||
| } | |||
| /** | |||
| @@ -305,6 +304,10 @@ export interface Actionable { | |||
| } | |||
| export abstract class GroupAction extends Action { | |||
| constructor (name: TextLike, desc: TextLike, conditions: Array<Condition>) { | |||
| super(name, desc, conditions) | |||
| } | |||
| allowedGroup (user: Creature, targets: Array<Creature>): Array<Creature> { | |||
| return targets.filter(target => this.allowed(user, target)) | |||
| } | |||
| @@ -314,10 +317,6 @@ export abstract class GroupAction extends Action { | |||
| } | |||
| abstract describeGroup (user: Creature, targets: Array<Creature>): LogEntry | |||
| constructor (name: TextLike, desc: TextLike, conditions: Array<Condition>) { | |||
| super(name, desc, conditions) | |||
| } | |||
| } | |||
| /** | |||
| @@ -364,12 +363,12 @@ export interface VisibleStatus { | |||
| * a status indicating that it is dead, but entities cannot be "given" the dead effect | |||
| */ | |||
| export class ImplicitStatus implements VisibleStatus { | |||
| topLeft = '' | |||
| bottomRight = '' | |||
| constructor (public name: TextLike, public desc: TextLike, public icon: string) { | |||
| } | |||
| topLeft = '' | |||
| bottomRight = '' | |||
| } | |||
| /** | |||
| @@ -388,7 +387,7 @@ export abstract class StatusEffect extends Effective implements VisibleStatus { | |||
| * An Encounter describes a fight: who is in it and whose turn it is | |||
| */ | |||
| export class Encounter { | |||
| private initiatives: Map<Creature, number> | |||
| initiatives: Map<Creature, number> | |||
| currentMove: Creature | |||
| turnTime = 100 | |||
| @@ -3,13 +3,13 @@ import { Creature } from "../entity" | |||
| import { Container } from '../vore' | |||
| export class InverseCondition implements Condition { | |||
| allowed (user: Creature, target: Creature): boolean { | |||
| return !this.condition.allowed(user, target) | |||
| } | |||
| constructor (private condition: Condition) { | |||
| } | |||
| allowed (user: Creature, target: Creature): boolean { | |||
| return !this.condition.allowed(user, target) | |||
| } | |||
| } | |||
| export class CapableCondition implements Condition { | |||
| @@ -19,23 +19,23 @@ export class CapableCondition implements Condition { | |||
| } | |||
| export class UserDrainedVigorCondition implements Condition { | |||
| constructor (private vigor: Vigor) { | |||
| } | |||
| allowed (user: Creature, target: Creature): boolean { | |||
| return user.vigors[this.vigor] <= 0 | |||
| } | |||
| } | |||
| export class TargetDrainedVigorCondition implements Condition { | |||
| constructor (private vigor: Vigor) { | |||
| } | |||
| } | |||
| export class TargetDrainedVigorCondition implements Condition { | |||
| allowed (user: Creature, target: Creature): boolean { | |||
| return target.vigors[this.vigor] <= 0 | |||
| } | |||
| constructor (private vigor: Vigor) { | |||
| } | |||
| } | |||
| export class SoloCondition implements Condition { | |||
| @@ -57,23 +57,23 @@ export class TogetherCondition implements Condition { | |||
| } | |||
| export class ContainedByCondition implements Condition { | |||
| constructor (private container: Container) { | |||
| } | |||
| allowed (user: Creature, target: Creature): boolean { | |||
| return user.containedIn === this.container && this.container.owner === target | |||
| } | |||
| } | |||
| export class ContainsCondition implements Condition { | |||
| constructor (private container: Container) { | |||
| } | |||
| } | |||
| export class ContainsCondition implements Condition { | |||
| allowed (user: Creature, target: Creature): boolean { | |||
| return target.containedIn === this.container | |||
| } | |||
| constructor (private container: Container) { | |||
| } | |||
| } | |||
| export class AllyCondition implements Condition { | |||
| @@ -8,6 +8,14 @@ import { StatTest } from '../combat/tests' | |||
| import { StunEffect, PredatorCounterEffect } from '../combat/effects' | |||
| class StompAttack extends AttackAction { | |||
| constructor (protected damage: DamageFormula, protected verb: Verb = new Verb('smack')) { | |||
| super( | |||
| damage, | |||
| verb | |||
| ) | |||
| this.test = new StatTest(Stat.Power) | |||
| } | |||
| execute (user: Creature, target: Creature): LogEntry { | |||
| if (this.test.test(user, target)) { | |||
| const damage = this.damage.calc(user, target) | |||
| @@ -20,14 +28,6 @@ class StompAttack extends AttackAction { | |||
| return this.failLine(user, target) | |||
| } | |||
| } | |||
| constructor (protected damage: DamageFormula, protected verb: Verb = new Verb('smack')) { | |||
| super( | |||
| damage, | |||
| verb | |||
| ) | |||
| this.test = new StatTest(Stat.Power) | |||
| } | |||
| } | |||
| export class Kenzie extends Creature { | |||
| title = "Large Lycanroc" | |||
| @@ -10,6 +10,17 @@ import * as Words from '../words' | |||
| import { StatVigorTest } from '../combat/tests' | |||
| class LevelDrain extends Action { | |||
| constructor (private container: Container) { | |||
| super( | |||
| 'Level Drain', | |||
| 'Drain energy from your prey', | |||
| [ | |||
| new ContainsCondition(container), | |||
| new CapableCondition() | |||
| ] | |||
| ) | |||
| } | |||
| execute (user: Creature, target: Creature): LogEntry { | |||
| const damage: Damage = new Damage(...Object.keys(Stat).map(stat => { | |||
| return { | |||
| @@ -40,19 +51,20 @@ class LevelDrain extends Action { | |||
| describe (user: Creature, target: Creature): LogEntry { | |||
| return new LogLine(`Drain energy from ${target.name}`) | |||
| } | |||
| constructor (private container: Container) { | |||
| } | |||
| class HypnotizeAction extends Action { | |||
| constructor () { | |||
| super( | |||
| 'Level Drain', | |||
| 'Drain energy from your prey', | |||
| `Hypnotize`, | |||
| `Change their mind!`, | |||
| [ | |||
| new ContainsCondition(container), | |||
| new TogetherCondition(), | |||
| new EnemyCondition(), | |||
| new CapableCondition() | |||
| ] | |||
| ) | |||
| } | |||
| } | |||
| class HypnotizeAction extends Action { | |||
| line: PairLine<Creature> = (user, target) => new LogLine( | |||
| `${user.name.capital.possessive} hypnotic gaze enthralls ${target.name}, putting ${target.pronouns.objective} under ${user.pronouns.possessive} control!` | |||
| ) | |||
| @@ -65,18 +77,6 @@ class HypnotizeAction extends Action { | |||
| describe (user: Creature, target: Creature): LogEntry { | |||
| return new LogLine(`Force your target to fight by your side`) | |||
| } | |||
| constructor () { | |||
| super( | |||
| `Hypnotize`, | |||
| `Change their mind!`, | |||
| [ | |||
| new TogetherCondition(), | |||
| new EnemyCondition(), | |||
| new CapableCondition() | |||
| ] | |||
| ) | |||
| } | |||
| } | |||
| class MawContainer extends NormalContainer { | |||
| consumeVerb = new Verb('grab', 'grabs', 'grabbing', 'grabbed') | |||
| @@ -93,27 +93,27 @@ class MawContainer extends NormalContainer { | |||
| } | |||
| class FlexToesAction extends GroupAction { | |||
| line = (user: Creature, target: Creature, args: { damage: Damage }) => new LogLine(`${user.name.capital.possessive} toes crush ${target.name.objective} for `, args.damage.renderShort(), ` damage!`) | |||
| describeGroup (user: Creature, targets: Creature[]): LogEntry { | |||
| return new LogLine(`Flex your toes. `, this.damage.explain(user)) | |||
| } | |||
| execute (user: Creature, target: Creature): LogEntry { | |||
| const damage = this.damage.calc(user, target) | |||
| return new LogLines(target.takeDamage(damage), this.line(user, target, { damage: damage })) | |||
| } | |||
| describe (user: Creature, target: Creature): LogEntry { | |||
| return new LogLine(`Flex your toes! `, this.damage.describe(user, target)) | |||
| } | |||
| constructor (private damage: DamageFormula, container: Container) { | |||
| super('Flex Toes', 'Flex your toes!', [ | |||
| new ContainsCondition(container), | |||
| new PairCondition() | |||
| ]) | |||
| } | |||
| line = (user: Creature, target: Creature, args: { damage: Damage }) => new LogLine(`${user.name.capital.possessive} toes crush ${target.name.objective} for `, args.damage.renderShort(), ` damage!`) | |||
| describeGroup (user: Creature, targets: Creature[]): LogEntry { | |||
| return new LogLine(`Flex your toes. `, this.damage.explain(user)) | |||
| } | |||
| execute (user: Creature, target: Creature): LogEntry { | |||
| const damage = this.damage.calc(user, target) | |||
| return new LogLines(target.takeDamage(damage), this.line(user, target, { damage: damage })) | |||
| } | |||
| describe (user: Creature, target: Creature): LogEntry { | |||
| return new LogLine(`Flex your toes! `, this.damage.describe(user, target)) | |||
| } | |||
| } | |||
| class BootContainer extends NormalContainer { | |||
| @@ -159,6 +159,12 @@ class BiteAction extends AttackAction { | |||
| } | |||
| class ChewAction extends GroupAction { | |||
| constructor (private damage: DamageFormula, container: Container, private killAction: Action) { | |||
| super('Chew', 'Give them the big chew', [ | |||
| new ContainsCondition(container) | |||
| ]) | |||
| } | |||
| line = (user: Creature, target: Creature, args: { damage: Damage }) => new LogLine(`${user.name.capital} chews on ${target.name.objective} for `, args.damage.renderShort(), `!`) | |||
| describeGroup (user: Creature, targets: Creature[]): LogEntry { | |||
| @@ -185,15 +191,17 @@ class ChewAction extends GroupAction { | |||
| describe (user: Creature, target: Creature): LogEntry { | |||
| return new LogLine('Do the crunch') | |||
| } | |||
| } | |||
| constructor (private damage: DamageFormula, container: Container, private killAction: Action) { | |||
| super('Chew', 'Give them the big chew', [ | |||
| new ContainsCondition(container) | |||
| class StompAction extends GroupAction { | |||
| constructor () { | |||
| super('Stomp', 'STOMP!', [ | |||
| new TogetherCondition(), | |||
| new EnemyCondition(), | |||
| new CapableCondition() | |||
| ]) | |||
| } | |||
| } | |||
| class StompAction extends GroupAction { | |||
| line: PairLine<Creature> = (user, target) => new LogLine( | |||
| `${user.name.capital} ${user.name.conjugate(new Verb('flatten'))} ${target.name.objective} under ${user.pronouns.possessive} ${huge} foot!` | |||
| ) | |||
| @@ -209,17 +217,17 @@ class StompAction extends GroupAction { | |||
| describeGroup (user: Creature, targets: Array<Creature>): LogEntry { | |||
| return new LogLine('Stomp all ', targets.length.toString(), ' of \'em!') | |||
| } | |||
| } | |||
| class StompAllyAction extends Action { | |||
| constructor () { | |||
| super('Stomp', 'STOMP!', [ | |||
| super('Stomp Ally', '-1 ally, +1 buff', [ | |||
| new TogetherCondition(), | |||
| new EnemyCondition(), | |||
| new AllyCondition(), | |||
| new CapableCondition() | |||
| ]) | |||
| } | |||
| } | |||
| class StompAllyAction extends Action { | |||
| line: PairLine<Creature> = (user, target) => new LogLine( | |||
| `${user.name.capital} ${user.name.conjugate(new Verb('flatten'))} ${target.name.objective} under ${user.pronouns.possessive} ${huge} boot!` | |||
| ) | |||
| @@ -251,17 +259,20 @@ class StompAllyAction extends Action { | |||
| describe (user: Creature, target: Creature): LogEntry { | |||
| return new LogLine('Crush an ally to absorb their power') | |||
| } | |||
| } | |||
| constructor () { | |||
| super('Stomp Ally', '-1 ally, +1 buff', [ | |||
| class DevourAllAction extends GroupAction { | |||
| private test: CombatTest | |||
| constructor (private container: VoreContainer) { | |||
| super('Devour All', 'GULP!', [ | |||
| new TogetherCondition(), | |||
| new AllyCondition(), | |||
| new EnemyCondition(), | |||
| new CapableCondition() | |||
| ]) | |||
| this.test = new StatVigorTest(Stat.Power) | |||
| } | |||
| } | |||
| class DevourAllAction extends GroupAction { | |||
| line = (user: Creature, target: Creature) => new LogLine(`${user.name.capital} ${user.name.conjugate(new Verb('scoop'))} ${target.name} up!`) | |||
| groupLine = (user: Creature, args: { count: number }) => new LogLine(`${Words.SwallowSound.allCaps}! All ${args.count} of ${user.pronouns.possessive} prey pour down ${user.name.possessive} ${Words.Slick} gullet as ${user.pronouns.subjective} ${user.name.conjugate(Words.Swallows)}; they're just ${user.kind.all} chow now`) | |||
| @@ -287,17 +298,6 @@ class DevourAllAction extends GroupAction { | |||
| describeGroup (user: Creature, targets: Array<Creature>): LogEntry { | |||
| return new LogLine('Eat all ', targets.length.toString(), ' of \'em!') | |||
| } | |||
| private test: CombatTest | |||
| constructor (private container: VoreContainer) { | |||
| super('Devour All', 'GULP!', [ | |||
| new TogetherCondition(), | |||
| new EnemyCondition(), | |||
| new CapableCondition() | |||
| ]) | |||
| this.test = new StatVigorTest(Stat.Power) | |||
| } | |||
| } | |||
| export class Withers extends Creature { | |||
| @@ -64,13 +64,13 @@ export type TextLike = { toString: () => string } | |||
| // updates as needed | |||
| export class LiveText<T> { | |||
| toString (): string { | |||
| return this.run(this.contents).toString() | |||
| } | |||
| constructor (private contents: T, private run: (thing: T) => TextLike) { | |||
| } | |||
| toString (): string { | |||
| return this.run(this.contents).toString() | |||
| } | |||
| } | |||
| export class DynText { | |||
| @@ -330,6 +330,10 @@ export class Adjective extends Word { | |||
| } | |||
| export class Verb extends Word { | |||
| constructor (private _root: string, private _singular: string = _root + "s", private _present: string = _root + "ing", private _past: string = _root + "ed", private _pastParticiple: string = _past, public opt: WordOptions = emptyConfig) { | |||
| super(opt) | |||
| } | |||
| configure (opts: WordOptions): Word { | |||
| return new Verb( | |||
| this._root, | |||
| @@ -360,14 +364,14 @@ export class Verb extends Word { | |||
| return choice | |||
| } | |||
| constructor (private _root: string, private _singular: string = _root + "s", private _present: string = _root + "ing", private _past: string = _root + "ed", private _pastParticiple: string = _past, public opt: WordOptions = emptyConfig) { | |||
| super(opt) | |||
| } | |||
| } | |||
| // this one is obnoxious | |||
| export class ToBe extends Word { | |||
| constructor (protected opts: WordOptions = emptyConfig) { | |||
| super(opts) | |||
| } | |||
| configure (opts: WordOptions): Word { | |||
| return new ToBe(opts) | |||
| } | |||
| @@ -392,10 +396,6 @@ export class ToBe extends Word { | |||
| return choice | |||
| } | |||
| constructor (protected opts: WordOptions = emptyConfig) { | |||
| super(opts) | |||
| } | |||
| } | |||
| interface PronounDict { | |||