import { LogEntry } from "@/game/interface" import { parseTwoDigitYear } from "moment" export enum POV { First, Second, Third } export type SoloLine = (user: T) => LogEntry export type SoloLineArgs = (user: T, args: V) => LogEntry export type PairLine = (user: T, target: T) => LogEntry export type PairLineArgs = (user: T, target: T, args: V) => LogEntry export type GroupLine = (user: T, targets: Array) => LogEntry enum NounKind { Specific, Nonspecific, All } enum VowelSound { Default, Vowel, NonVowel } enum VerbKind { Root, Singular, Present, Past, PastParticiple } export interface Pluralizable { isPlural: boolean; } interface WordOptions { plural: boolean; capital: boolean; allCaps: boolean; proper: boolean; nounKind: NounKind; verbKind: VerbKind; vowel: VowelSound; count: boolean; possessive: boolean; objective: boolean; perspective: POV; } const emptyConfig: WordOptions = { allCaps: false, capital: false, count: false, nounKind: NounKind.Specific, verbKind: VerbKind.Root, plural: false, proper: false, vowel: VowelSound.Default, possessive: false, objective: false, perspective: POV.Third } export type TextLike = { toString: () => string } // updates as needed export class LiveText { constructor (private contents: T, private run: (thing: T) => TextLike) {} toString (): string { return this.run(this.contents).toString() } } export class DynText { private parts: Array constructor (...parts: TextLike[]) { this.parts = parts } toString (): string { return this.parts.map(part => part.toString()).join("") } } export abstract class Word { constructor (public opt: WordOptions = emptyConfig) {} abstract configure(opts: WordOptions): Word abstract toString(): string // These functions are pure; they don't mutate the original object. // This is necessary to avoid causing chaos. get allCaps (): this { const opts: WordOptions = Object.assign({}, this.opt) opts.allCaps = true return this.configure(opts) as this } get capital (): this { const opts: WordOptions = Object.assign({}, this.opt) opts.capital = true return this.configure(opts) as this } get plural (): this { const opts: WordOptions = Object.assign({}, this.opt) opts.plural = true return this.configure(opts) as this } get proper (): this { const opts: WordOptions = Object.assign({}, this.opt) opts.proper = true return this.configure(opts) as this } get improper (): this { const opts: WordOptions = Object.assign({}, this.opt) opts.proper = false return this.configure(opts) as this } get specific (): this { const opts: WordOptions = Object.assign({}, this.opt) opts.nounKind = NounKind.Specific return this.configure(opts) as this } get nonspecific (): this { const opts: WordOptions = Object.assign({}, this.opt) opts.nounKind = NounKind.Nonspecific return this.configure(opts) as this } get all (): this { const opts: WordOptions = Object.assign({}, this.opt) opts.nounKind = NounKind.All return this.configure(opts) as this } get uncountable (): this { const opts: WordOptions = Object.assign({}, this.opt) opts.count = false return this.configure(opts) as this } get root (): this { const opts: WordOptions = Object.assign({}, this.opt) opts.verbKind = VerbKind.Root return this.configure(opts) as this } get singular (): this { const opts: WordOptions = Object.assign({}, this.opt) opts.verbKind = VerbKind.Singular return this.configure(opts) as this } get present (): this { const opts: WordOptions = Object.assign({}, this.opt) opts.verbKind = VerbKind.Present return this.configure(opts) as this } get past (): this { const opts: WordOptions = Object.assign({}, this.opt) opts.verbKind = VerbKind.Past return this.configure(opts) as this } get pastParticiple (): this { const opts: WordOptions = Object.assign({}, this.opt) opts.verbKind = VerbKind.PastParticiple return this.configure(opts) as this } get possessive (): this { const opts: WordOptions = Object.assign({}, this.opt) opts.possessive = true return this.configure(opts) as this } get objective (): this { const opts: WordOptions = Object.assign({}, this.opt) opts.objective = true return this.configure(opts) as this } get pov1 (): this { const opts: WordOptions = Object.assign({}, this.opt) opts.perspective = POV.First return this.configure(opts) as this } get pov2 (): this { const opts: WordOptions = Object.assign({}, this.opt) opts.perspective = POV.Second return this.configure(opts) as this } get pov3 (): this { const opts: WordOptions = Object.assign({}, this.opt) opts.perspective = POV.Third return this.configure(opts) as this } } export class OptionalWord extends Word { constructor (public word: Word, private chance = 0.5, private suffix = " ") { super(word.opt) } configure (opts: WordOptions): Word { return new OptionalWord(this.word.configure(opts), this.chance, this.suffix) } toString (): string { if (Math.random() < this.chance) { return this.word + this.suffix } else { return "" } } } export class RandomWord extends Word { private history: { last: number } constructor ( public choices: Array, opt: WordOptions = emptyConfig, history: { last: number } = { last: -1 } ) { super(opt) this.history = history } configure (opts: WordOptions): Word { return new RandomWord(this.choices, opts, this.history) } toString (): string { let choice do { choice = Math.floor(Math.random() * this.choices.length) } while (choice === this.history.last && this.choices.length > 1) this.history.last = choice return this.choices[choice].configure(this.opt).toString() } } export class Noun extends Word { constructor ( protected singularNoun: string, protected pluralNoun: string | null = null, protected possessiveNoun: string | null = null, protected options: WordOptions = emptyConfig ) { super(options) } configure (opts: WordOptions): Word { return new Noun( this.singularNoun, this.pluralNoun, this.possessiveNoun, opts ) } toString (): string { let result: string // TODO: plural possessive nouns? if (this.options.possessive) { if (this.possessiveNoun === null) { result = this.singularNoun + "'s" } else { result = this.possessiveNoun } } else if (this.options.plural) { if (this.pluralNoun === null) { result = this.singularNoun } else { result = this.pluralNoun as string } } else { result = this.singularNoun } if (!this.options.proper) { if ( this.options.nounKind === NounKind.Nonspecific && this.options.count ) { if (this.options.plural) { result = "some " + result } else { if (this.options.vowel === VowelSound.Default) { if ("aeiouAEIOU".indexOf(result.slice(0, 1)) >= 0) { result = "an " + result } else { result = "a " + result } } else if (this.options.vowel === VowelSound.Vowel) { result = "an " + result } else if (this.options.vowel === VowelSound.NonVowel) { result = "a " + result } } } else if (this.options.nounKind === NounKind.Specific) { result = "the " + result } } if (this.options.allCaps) { result = result.toUpperCase() } else if (this.options.capital) { result = result.slice(0, 1).toUpperCase() + result.slice(1) } return result } conjugate (verb: Word): Word { if (this.opt.plural) { return verb.root } else { return verb.singular } } } export class ImproperNoun extends Noun { constructor (singularNoun: string, pluralNoun: string = singularNoun) { super(singularNoun, pluralNoun, null, { plural: false, allCaps: false, capital: false, proper: false, nounKind: NounKind.Specific, verbKind: VerbKind.Root, vowel: VowelSound.Default, count: true, possessive: false, objective: false, perspective: POV.Third }) } } export class ProperNoun extends Noun { constructor (singularNoun: string) { super(singularNoun, null, null, { plural: false, allCaps: false, capital: false, proper: true, nounKind: NounKind.Specific, verbKind: VerbKind.Root, vowel: VowelSound.Default, count: true, possessive: false, objective: false, perspective: POV.Third }) } } export class Adjective extends Word { constructor (private adjective: string, opt: WordOptions = emptyConfig) { super(opt) } configure (opts: WordOptions): Word { return new Adjective(this.adjective, opts) } toString (): string { let word = this.adjective if (this.opt.allCaps) { word = word.toUpperCase() } else if (this.opt.capital) { word = word.slice(0, 1).toUpperCase() + word.slice(1) } return word } } export class Adverb extends Word { constructor (private adverb: string, opt: WordOptions = emptyConfig) { super(opt) } configure (opt: WordOptions): Word { return new Adverb(this.adverb, opt) } toString (): string { return this.adverb } } /** * root: break * singular: breaks * present: breaking * past: broken */ 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, this._singular, this._present, this._past, this._pastParticiple, opts ) } toString (): string { let choice: string switch (this.opt.verbKind) { case VerbKind.Root: choice = this._root break case VerbKind.Singular: choice = this._singular break case VerbKind.Present: choice = this._present break case VerbKind.Past: choice = this._past break case VerbKind.PastParticiple: choice = this._pastParticiple break } if (this.opt.allCaps) { choice = choice.toUpperCase() } else if (this.opt.capital) { choice = choice.slice(0, 1).toUpperCase() + choice.slice(1) } return choice } } export class Preposition extends Word { constructor (private word: string, public opt: WordOptions = emptyConfig) { super(opt) } configure (opts: WordOptions): Word { return new Preposition(this.word, opts) } toString (): string { if (this.opt.capital) { return this.word.slice(0, 1).toUpperCase() + this.word.slice(1) } else { return this.word } } } // this one is obnoxious export class ToBe extends Word { constructor (protected opts: WordOptions = emptyConfig) { super(opts) } configure (opts: WordOptions): Word { return new ToBe(opts) } toString (): string { let choice if (this.opts.plural) { choice = "are" } switch (this.opts.perspective) { case POV.First: choice = "am" break case POV.Second: choice = "are" break case POV.Third: choice = "is" break } if (this.opt.allCaps) { choice = choice.toUpperCase() } else if (this.opt.capital) { choice = choice.slice(0, 1).toUpperCase() + choice.slice(1) } return choice } } interface PronounDict { subjective: string; objective: string; possessive: string; reflexive: string; } export class Pronoun implements Pluralizable { constructor ( private pronouns: PronounDict, private capitalize: boolean = false, public isPlural: boolean = false ) {} get capital (): Pronoun { return new Pronoun(this.pronouns, true) } get subjective (): string { return this.caps(this.pronouns.subjective) } get objective (): string { return this.caps(this.pronouns.objective) } get possessive (): string { return this.caps(this.pronouns.possessive) } get reflexive (): string { return this.caps(this.pronouns.reflexive) } conjugate (verb: Word): Word { if (this.isPlural) { return verb.root } else { return verb.singular } } private caps (input: string): string { if (this.capitalize) { return input.slice(0, 1).toUpperCase() + input.slice(1) } else { return input } } } export type OnomatopoeiaPart = [string, number, number] export class Onomatopoeia extends Word { constructor (protected parts: Array, public opts: WordOptions = emptyConfig) { super(opts) } configure (opts: WordOptions): Word { return new Onomatopoeia(this.parts, opts) } toString (): string { const built = this.parts.reduce((result, next) => { const [piece, min, max] = next const count = Math.floor(Math.random() * (max - min)) + min for (let i = 0; i < count; i++) { result += piece } return result }, "") return built } } export const MalePronouns = new Pronoun({ subjective: "he", objective: "him", possessive: "his", reflexive: "himself" }) export const FemalePronouns = new Pronoun({ subjective: "she", objective: "her", possessive: "her", reflexive: "herself" }) export const TheyPronouns = new Pronoun( { subjective: "they", objective: "them", possessive: "their", reflexive: "themself" }, false, true ) export const TheyPluralPronouns = new Pronoun( { subjective: "they", objective: "them", possessive: "their", reflexive: "themselves" }, false, true ) export const ObjectPronouns = new Pronoun({ subjective: "it", objective: "it", possessive: "its", reflexive: "itself" }) export const SecondPersonPronouns = new Pronoun( { subjective: "you", objective: "you", possessive: "your", reflexive: "yourself" }, false, true ) export const FirstPersonPronouns = new Pronoun( { subjective: "I", objective: "me", possessive: "my", reflexive: "myself" }, false, true ) export class PronounAsNoun extends Noun { constructor (private pronouns: Pronoun, opt: WordOptions = emptyConfig) { super(pronouns.subjective, pronouns.subjective, pronouns.possessive, opt) this.options.nounKind = NounKind.All this.options.plural = true } configure (opts: WordOptions): Word { return new PronounAsNoun(this.pronouns, opts) } toString (): string { if (this.options.objective) { return new Noun( this.pronouns.objective, this.pronouns.objective, this.pronouns.possessive, this.options ).toString() } else { return super.toString() } } conjugate (verb: Word): Word { if (this.pronouns === FirstPersonPronouns) { return super.conjugate(verb.pov1) } else if (this.pronouns === SecondPersonPronouns) { return super.conjugate(verb.pov2) } else { return super.conjugate(verb.pov3) } } }