import { Creature, POV, Entity } from './entity' import { POVPair, POVPairArgs, TextLike, DynText, LiveText } from './language' import { Container } from './vore' import { LogEntry, LogLines, CompositeLog, FAElem, LogLine, FormatEntry, FormatOpt, PropElem } from './interface' import { StatTest, StatVigorTest } from './combat/tests' export enum DamageType { Pierce = "Pierce", Slash = "Slash", Crush = "Crush", Acid = "Acid", Seduction = "Seduction", Dominance = "Dominance" } export interface DamageInstance { type: DamageType; amount: number; target: Vigor; } export enum Vigor { Health = "Health", Stamina = "Stamina", Resolve = "Resolve" } export const VigorIcons: {[key in Vigor]: string} = { Health: "fas fa-heart", Stamina: "fas fa-bolt", Resolve: "fas fa-brain" } export const VigorDescs: {[key in Vigor]: string} = { Health: "How much damage you can take", Stamina: "How much energy you have", Resolve: "How much dominance you can resist" } export type Vigors = {[key in Vigor]: number} export enum Stat { Toughness = "Toughness", Power = "Power", Speed = "Speed", Willpower = "Willpower", Charm = "Charm" } export type Stats = {[key in Stat]: number} export const StatIcons: {[key in Stat]: string} = { Toughness: 'fas fa-heartbeat', Power: 'fas fa-fist-raised', Speed: 'fas fa-feather', Willpower: 'fas fa-book', Charm: 'fas fa-comments' } export const StatDescs: {[key in Stat]: string} = { Toughness: 'Your physical resistance', Power: 'Your physical power', Speed: 'How quickly you can act', Willpower: 'Your mental resistance', Charm: 'Your mental power' } export enum VoreStat { Mass = "Mass", Bulk = "Bulk", PreyCount = "Prey Count" } export type VoreStats = {[key in VoreStat]: number} export const VoreStatIcons: {[key in VoreStat]: string} = { [VoreStat.Mass]: "fas fa-weight", [VoreStat.Bulk]: "fas fa-weight-hanging", [VoreStat.PreyCount]: "fas fa-utensils" } export const VoreStatDescs: {[key in VoreStat]: string} = { [VoreStat.Mass]: "How much you weigh", [VoreStat.Bulk]: "Your weight, plus the weigh of your prey", [VoreStat.PreyCount]: "How many creatures you've got inside of you" } export interface CombatTest { test: (user: Creature, target: Creature) => boolean; odds: (user: Creature, target: Creature) => number; explain: (user: Creature, target: Creature) => LogEntry; } export interface Effect { name: string; desc: string; apply: (target: Creature) => LogEntry; } /** * An instance of damage. Contains zero or more [[DamageInstance]] objects */ export class Damage { readonly damages: DamageInstance[] constructor (...damages: DamageInstance[]) { this.damages = damages } scale (factor: number): Damage { const results: Array = [] this.damages.forEach(damage => { results.push({ type: damage.type, amount: damage.amount * factor, target: damage.target }) }) return new Damage(...results) } toString (): string { return this.damages.map(damage => damage.amount + " " + damage.type).join("/") } render (): LogEntry { return new LogLine(...this.damages.flatMap(instance => { return [instance.amount.toString(), new FAElem(VigorIcons[instance.target]), " " + instance.type] })) } renderShort (): LogEntry { const totals: Vigors = Object.keys(Vigor).reduce((total: any, key) => { total[key] = 0; return total }, {}) this.damages.forEach(instance => { totals[instance.target] += instance.amount }) return new FormatEntry(new LogLine(...Object.keys(Vigor).flatMap(key => totals[key as Vigor] === 0 ? [] : [new PropElem(key as Vigor, totals[key as Vigor]), ' '])), FormatOpt.DamageInst) } } /** * Computes damage given the source and target of the damage. */ export interface DamageFormula { calc (user: Creature, target: Creature): Damage; describe (user: Creature, target: Creature): LogEntry; } /** * 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) { } describe (user: Creature, target: Creature): LogEntry { return new LogLine('Deal ', this.damage.renderShort(), ' damage') } } /** * 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) { } describe (user: Creature, target: Creature): LogEntry { return new LogLine('Deal between ', this.damage.scale(1 - this.variance).renderShort(), ' and ', this.damage.scale(1 + this.variance).renderShort(), ' damage.') } } /** * A Combatant has a list of possible actions to take. * * This may be merged with [[Actionable]] */ export interface Combatant { actions: Array; } /** * 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 = []) { } toString (): string { return this.name.toString() } } /** * A Condition describes whether or not something is permissible between two [[Creature]]s */ export interface Condition { allowed: (user: Creature, target: Creature) => boolean; } export interface Actionable { actions: Array; } export abstract class SelfAction extends Action { allowed (user: Creature, target: Creature) { if (user === target) { return super.allowed(user, target) } else { return false } } } export abstract class PairAction extends Action { allowed (user: Creature, target: Creature) { if (user !== target) { return super.allowed(user, target) } else { return false } } } export abstract class TogetherAction extends PairAction { allowed (user: Creature, target: Creature) { if (user.containedIn === target.containedIn) { return super.allowed(user, target) } else { return false } } }