Feast 2.0!
25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

702 lines
15 KiB

  1. import { LogEntry } from "@/game/interface"
  2. import { parseTwoDigitYear } from "moment"
  3. export enum POV {
  4. First,
  5. Second,
  6. Third
  7. }
  8. export type SoloLine<T> = (user: T) => LogEntry
  9. export type SoloLineArgs<T, V> = (user: T, args: V) => LogEntry
  10. export type PairLine<T> = (user: T, target: T) => LogEntry
  11. export type PairLineArgs<T, V> = (user: T, target: T, args: V) => LogEntry
  12. export type GroupLine<T> = (user: T, targets: Array<T>) => LogEntry
  13. enum NounKind {
  14. Specific,
  15. Nonspecific,
  16. All
  17. }
  18. enum VowelSound {
  19. Default,
  20. Vowel,
  21. NonVowel
  22. }
  23. enum VerbKind {
  24. Root,
  25. Singular,
  26. Present,
  27. Past,
  28. PastParticiple
  29. }
  30. export interface Pluralizable {
  31. isPlural: boolean;
  32. }
  33. interface WordOptions {
  34. plural: boolean;
  35. capital: boolean;
  36. allCaps: boolean;
  37. proper: boolean;
  38. nounKind: NounKind;
  39. verbKind: VerbKind;
  40. vowel: VowelSound;
  41. count: boolean;
  42. possessive: boolean;
  43. objective: boolean;
  44. perspective: POV;
  45. }
  46. const emptyConfig: WordOptions = {
  47. allCaps: false,
  48. capital: false,
  49. count: false,
  50. nounKind: NounKind.Specific,
  51. verbKind: VerbKind.Root,
  52. plural: false,
  53. proper: false,
  54. vowel: VowelSound.Default,
  55. possessive: false,
  56. objective: false,
  57. perspective: POV.Third
  58. }
  59. export type TextLike = { toString: () => string }
  60. // updates as needed
  61. export class LiveText<T> {
  62. constructor (private contents: T, private run: (thing: T) => TextLike) {}
  63. toString (): string {
  64. return this.run(this.contents).toString()
  65. }
  66. }
  67. export class DynText {
  68. private parts: Array<TextLike>
  69. constructor (...parts: TextLike[]) {
  70. this.parts = parts
  71. }
  72. toString (): string {
  73. return this.parts.map(part => part.toString()).join("")
  74. }
  75. }
  76. export abstract class Word {
  77. constructor (public opt: WordOptions = emptyConfig) {}
  78. abstract configure(opts: WordOptions): Word
  79. abstract toString(): string
  80. // These functions are pure; they don't mutate the original object.
  81. // This is necessary to avoid causing chaos.
  82. get allCaps (): this {
  83. const opts: WordOptions = Object.assign({}, this.opt)
  84. opts.allCaps = true
  85. return this.configure(opts) as this
  86. }
  87. get capital (): this {
  88. const opts: WordOptions = Object.assign({}, this.opt)
  89. opts.capital = true
  90. return this.configure(opts) as this
  91. }
  92. get plural (): this {
  93. const opts: WordOptions = Object.assign({}, this.opt)
  94. opts.plural = true
  95. return this.configure(opts) as this
  96. }
  97. get proper (): this {
  98. const opts: WordOptions = Object.assign({}, this.opt)
  99. opts.proper = true
  100. return this.configure(opts) as this
  101. }
  102. get improper (): this {
  103. const opts: WordOptions = Object.assign({}, this.opt)
  104. opts.proper = false
  105. return this.configure(opts) as this
  106. }
  107. get specific (): this {
  108. const opts: WordOptions = Object.assign({}, this.opt)
  109. opts.nounKind = NounKind.Specific
  110. return this.configure(opts) as this
  111. }
  112. get nonspecific (): this {
  113. const opts: WordOptions = Object.assign({}, this.opt)
  114. opts.nounKind = NounKind.Nonspecific
  115. return this.configure(opts) as this
  116. }
  117. get all (): this {
  118. const opts: WordOptions = Object.assign({}, this.opt)
  119. opts.nounKind = NounKind.All
  120. return this.configure(opts) as this
  121. }
  122. get uncountable (): this {
  123. const opts: WordOptions = Object.assign({}, this.opt)
  124. opts.count = false
  125. return this.configure(opts) as this
  126. }
  127. get root (): this {
  128. const opts: WordOptions = Object.assign({}, this.opt)
  129. opts.verbKind = VerbKind.Root
  130. return this.configure(opts) as this
  131. }
  132. get singular (): this {
  133. const opts: WordOptions = Object.assign({}, this.opt)
  134. opts.verbKind = VerbKind.Singular
  135. return this.configure(opts) as this
  136. }
  137. get present (): this {
  138. const opts: WordOptions = Object.assign({}, this.opt)
  139. opts.verbKind = VerbKind.Present
  140. return this.configure(opts) as this
  141. }
  142. get past (): this {
  143. const opts: WordOptions = Object.assign({}, this.opt)
  144. opts.verbKind = VerbKind.Past
  145. return this.configure(opts) as this
  146. }
  147. get pastParticiple (): this {
  148. const opts: WordOptions = Object.assign({}, this.opt)
  149. opts.verbKind = VerbKind.PastParticiple
  150. return this.configure(opts) as this
  151. }
  152. get possessive (): this {
  153. const opts: WordOptions = Object.assign({}, this.opt)
  154. opts.possessive = true
  155. return this.configure(opts) as this
  156. }
  157. get objective (): this {
  158. const opts: WordOptions = Object.assign({}, this.opt)
  159. opts.objective = true
  160. return this.configure(opts) as this
  161. }
  162. get pov1 (): this {
  163. const opts: WordOptions = Object.assign({}, this.opt)
  164. opts.perspective = POV.First
  165. return this.configure(opts) as this
  166. }
  167. get pov2 (): this {
  168. const opts: WordOptions = Object.assign({}, this.opt)
  169. opts.perspective = POV.Second
  170. return this.configure(opts) as this
  171. }
  172. get pov3 (): this {
  173. const opts: WordOptions = Object.assign({}, this.opt)
  174. opts.perspective = POV.Third
  175. return this.configure(opts) as this
  176. }
  177. }
  178. export class OptionalWord extends Word {
  179. constructor (public word: Word, private chance = 0.5, private suffix = " ") {
  180. super(word.opt)
  181. }
  182. configure (opts: WordOptions): Word {
  183. return new OptionalWord(this.word.configure(opts), this.chance, this.suffix)
  184. }
  185. toString (): string {
  186. if (Math.random() < this.chance) {
  187. return this.word + this.suffix
  188. } else {
  189. return ""
  190. }
  191. }
  192. }
  193. export class RandomWord extends Word {
  194. private history: { last: number }
  195. constructor (
  196. public choices: Array<Word>,
  197. opt: WordOptions = emptyConfig,
  198. history: { last: number } = { last: -1 }
  199. ) {
  200. super(opt)
  201. this.history = history
  202. }
  203. configure (opts: WordOptions): Word {
  204. return new RandomWord(this.choices, opts, this.history)
  205. }
  206. toString (): string {
  207. let choice
  208. do {
  209. choice = Math.floor(Math.random() * this.choices.length)
  210. } while (choice === this.history.last && this.choices.length > 1)
  211. this.history.last = choice
  212. return this.choices[choice].configure(this.opt).toString()
  213. }
  214. }
  215. export class Noun extends Word {
  216. constructor (
  217. protected singularNoun: string,
  218. protected pluralNoun: string | null = null,
  219. protected possessiveNoun: string | null = null,
  220. protected options: WordOptions = emptyConfig
  221. ) {
  222. super(options)
  223. }
  224. configure (opts: WordOptions): Word {
  225. return new Noun(
  226. this.singularNoun,
  227. this.pluralNoun,
  228. this.possessiveNoun,
  229. opts
  230. )
  231. }
  232. toString (): string {
  233. let result: string
  234. // TODO: plural possessive nouns?
  235. if (this.options.possessive) {
  236. if (this.possessiveNoun === null) {
  237. result = this.singularNoun + "'s"
  238. } else {
  239. result = this.possessiveNoun
  240. }
  241. } else if (this.options.plural) {
  242. if (this.pluralNoun === null) {
  243. result = this.singularNoun
  244. } else {
  245. result = this.pluralNoun as string
  246. }
  247. } else {
  248. result = this.singularNoun
  249. }
  250. if (!this.options.proper) {
  251. if (
  252. this.options.nounKind === NounKind.Nonspecific &&
  253. this.options.count
  254. ) {
  255. if (this.options.plural) {
  256. result = "some " + result
  257. } else {
  258. if (this.options.vowel === VowelSound.Default) {
  259. if ("aeiouAEIOU".indexOf(result.slice(0, 1)) >= 0) {
  260. result = "an " + result
  261. } else {
  262. result = "a " + result
  263. }
  264. } else if (this.options.vowel === VowelSound.Vowel) {
  265. result = "an " + result
  266. } else if (this.options.vowel === VowelSound.NonVowel) {
  267. result = "a " + result
  268. }
  269. }
  270. } else if (this.options.nounKind === NounKind.Specific) {
  271. result = "the " + result
  272. }
  273. }
  274. if (this.options.allCaps) {
  275. result = result.toUpperCase()
  276. } else if (this.options.capital) {
  277. result = result.slice(0, 1).toUpperCase() + result.slice(1)
  278. }
  279. return result
  280. }
  281. conjugate (verb: Word): Word {
  282. if (this.opt.plural) {
  283. return verb.root
  284. } else {
  285. return verb.singular
  286. }
  287. }
  288. }
  289. export class ImproperNoun extends Noun {
  290. constructor (singularNoun: string, pluralNoun: string = singularNoun) {
  291. super(singularNoun, pluralNoun, null, {
  292. plural: false,
  293. allCaps: false,
  294. capital: false,
  295. proper: false,
  296. nounKind: NounKind.Specific,
  297. verbKind: VerbKind.Root,
  298. vowel: VowelSound.Default,
  299. count: true,
  300. possessive: false,
  301. objective: false,
  302. perspective: POV.Third
  303. })
  304. }
  305. }
  306. export class ProperNoun extends Noun {
  307. constructor (singularNoun: string) {
  308. super(singularNoun, null, null, {
  309. plural: false,
  310. allCaps: false,
  311. capital: false,
  312. proper: true,
  313. nounKind: NounKind.Specific,
  314. verbKind: VerbKind.Root,
  315. vowel: VowelSound.Default,
  316. count: true,
  317. possessive: false,
  318. objective: false,
  319. perspective: POV.Third
  320. })
  321. }
  322. }
  323. export class Adjective extends Word {
  324. constructor (private adjective: string, opt: WordOptions = emptyConfig) {
  325. super(opt)
  326. }
  327. configure (opts: WordOptions): Word {
  328. return new Adjective(this.adjective, opts)
  329. }
  330. toString (): string {
  331. let word = this.adjective
  332. if (this.opt.allCaps) {
  333. word = word.toUpperCase()
  334. } else if (this.opt.capital) {
  335. word = word.slice(0, 1).toUpperCase() + word.slice(1)
  336. }
  337. return word
  338. }
  339. }
  340. export class Adverb extends Word {
  341. constructor (private adverb: string, opt: WordOptions = emptyConfig) {
  342. super(opt)
  343. }
  344. configure (opt: WordOptions): Word {
  345. return new Adverb(this.adverb, opt)
  346. }
  347. toString (): string {
  348. return this.adverb
  349. }
  350. }
  351. /**
  352. * root: break
  353. * singular: breaks
  354. * present: breaking
  355. * past: broken
  356. */
  357. export class Verb extends Word {
  358. constructor (
  359. private _root: string,
  360. private _singular: string = _root + "s",
  361. private _present: string = _root + "ing",
  362. private _past: string = _root + "ed",
  363. private _pastParticiple: string = _past,
  364. public opt: WordOptions = emptyConfig
  365. ) {
  366. super(opt)
  367. }
  368. configure (opts: WordOptions): Word {
  369. return new Verb(
  370. this._root,
  371. this._singular,
  372. this._present,
  373. this._past,
  374. this._pastParticiple,
  375. opts
  376. )
  377. }
  378. toString (): string {
  379. let choice: string
  380. switch (this.opt.verbKind) {
  381. case VerbKind.Root:
  382. choice = this._root
  383. break
  384. case VerbKind.Singular:
  385. choice = this._singular
  386. break
  387. case VerbKind.Present:
  388. choice = this._present
  389. break
  390. case VerbKind.Past:
  391. choice = this._past
  392. break
  393. case VerbKind.PastParticiple:
  394. choice = this._pastParticiple
  395. break
  396. }
  397. if (this.opt.allCaps) {
  398. choice = choice.toUpperCase()
  399. } else if (this.opt.capital) {
  400. choice = choice.slice(0, 1).toUpperCase() + choice.slice(1)
  401. }
  402. return choice
  403. }
  404. }
  405. export class Preposition extends Word {
  406. constructor (private word: string, public opt: WordOptions = emptyConfig) {
  407. super(opt)
  408. }
  409. configure (opts: WordOptions): Word {
  410. return new Preposition(this.word, opts)
  411. }
  412. toString (): string {
  413. if (this.opt.capital) {
  414. return this.word.slice(0, 1).toUpperCase() + this.word.slice(1)
  415. } else {
  416. return this.word
  417. }
  418. }
  419. }
  420. // this one is obnoxious
  421. export class ToBe extends Word {
  422. constructor (protected opts: WordOptions = emptyConfig) {
  423. super(opts)
  424. }
  425. configure (opts: WordOptions): Word {
  426. return new ToBe(opts)
  427. }
  428. toString (): string {
  429. let choice
  430. if (this.opts.plural) {
  431. choice = "are"
  432. }
  433. switch (this.opts.perspective) {
  434. case POV.First:
  435. choice = "am"
  436. break
  437. case POV.Second:
  438. choice = "are"
  439. break
  440. case POV.Third:
  441. choice = "is"
  442. break
  443. }
  444. if (this.opt.allCaps) {
  445. choice = choice.toUpperCase()
  446. } else if (this.opt.capital) {
  447. choice = choice.slice(0, 1).toUpperCase() + choice.slice(1)
  448. }
  449. return choice
  450. }
  451. }
  452. interface PronounDict {
  453. subjective: string;
  454. objective: string;
  455. possessive: string;
  456. reflexive: string;
  457. }
  458. export class Pronoun implements Pluralizable {
  459. constructor (
  460. private pronouns: PronounDict,
  461. private capitalize: boolean = false,
  462. public isPlural: boolean = false
  463. ) {}
  464. get capital (): Pronoun {
  465. return new Pronoun(this.pronouns, true)
  466. }
  467. get subjective (): string {
  468. return this.caps(this.pronouns.subjective)
  469. }
  470. get objective (): string {
  471. return this.caps(this.pronouns.objective)
  472. }
  473. get possessive (): string {
  474. return this.caps(this.pronouns.possessive)
  475. }
  476. get reflexive (): string {
  477. return this.caps(this.pronouns.reflexive)
  478. }
  479. conjugate (verb: Word): Word {
  480. if (this.isPlural) {
  481. return verb.root
  482. } else {
  483. return verb.singular
  484. }
  485. }
  486. private caps (input: string): string {
  487. if (this.capitalize) {
  488. return input.slice(0, 1).toUpperCase() + input.slice(1)
  489. } else {
  490. return input
  491. }
  492. }
  493. }
  494. export type OnomatopoeiaPart = [string, number, number]
  495. export class Onomatopoeia extends Word {
  496. constructor (protected parts: Array<OnomatopoeiaPart>, public opts: WordOptions = emptyConfig) {
  497. super(opts)
  498. }
  499. configure (opts: WordOptions): Word {
  500. return new Onomatopoeia(this.parts, opts)
  501. }
  502. toString (): string {
  503. const built = this.parts.reduce((result, next) => {
  504. const [piece, min, max] = next
  505. const count = Math.floor(Math.random() * (max - min)) + min
  506. for (let i = 0; i < count; i++) {
  507. result += piece
  508. }
  509. return result
  510. }, "")
  511. return built
  512. }
  513. }
  514. export const MalePronouns = new Pronoun({
  515. subjective: "he",
  516. objective: "him",
  517. possessive: "his",
  518. reflexive: "himself"
  519. })
  520. export const FemalePronouns = new Pronoun({
  521. subjective: "she",
  522. objective: "her",
  523. possessive: "her",
  524. reflexive: "herself"
  525. })
  526. export const TheyPronouns = new Pronoun(
  527. {
  528. subjective: "they",
  529. objective: "them",
  530. possessive: "their",
  531. reflexive: "themself"
  532. },
  533. false,
  534. true
  535. )
  536. export const TheyPluralPronouns = new Pronoun(
  537. {
  538. subjective: "they",
  539. objective: "them",
  540. possessive: "their",
  541. reflexive: "themselves"
  542. },
  543. false,
  544. true
  545. )
  546. export const ObjectPronouns = new Pronoun({
  547. subjective: "it",
  548. objective: "it",
  549. possessive: "its",
  550. reflexive: "itself"
  551. })
  552. export const SecondPersonPronouns = new Pronoun(
  553. {
  554. subjective: "you",
  555. objective: "you",
  556. possessive: "your",
  557. reflexive: "yourself"
  558. },
  559. false,
  560. true
  561. )
  562. export const FirstPersonPronouns = new Pronoun(
  563. {
  564. subjective: "I",
  565. objective: "me",
  566. possessive: "my",
  567. reflexive: "myself"
  568. },
  569. false,
  570. true
  571. )
  572. export class PronounAsNoun extends Noun {
  573. constructor (private pronouns: Pronoun, opt: WordOptions = emptyConfig) {
  574. super(pronouns.subjective, pronouns.subjective, pronouns.possessive, opt)
  575. this.options.nounKind = NounKind.All
  576. this.options.plural = true
  577. }
  578. configure (opts: WordOptions): Word {
  579. return new PronounAsNoun(this.pronouns, opts)
  580. }
  581. toString (): string {
  582. if (this.options.objective) {
  583. return new Noun(
  584. this.pronouns.objective,
  585. this.pronouns.objective,
  586. this.pronouns.possessive,
  587. this.options
  588. ).toString()
  589. } else {
  590. return super.toString()
  591. }
  592. }
  593. conjugate (verb: Word): Word {
  594. if (this.pronouns === FirstPersonPronouns) {
  595. return super.conjugate(verb.pov1)
  596. } else if (this.pronouns === SecondPersonPronouns) {
  597. return super.conjugate(verb.pov2)
  598. } else {
  599. return super.conjugate(verb.pov3)
  600. }
  601. }
  602. }