Feast 2.0!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

528 lines
13 KiB

  1. import { Entity } from './entity'
  2. import { LogEntry, LogLine } from './interface'
  3. export enum POV {First, Second, Third}
  4. export type SoloLine<T> = (user: T) => LogEntry
  5. export type SoloLineArgs<T, V> = (user: T, args: V) => LogEntry
  6. export type PairLine<T> = (user: T, target: T) => LogEntry
  7. export type PairLineArgs<T, V> = (user: T, target: T, args: V) => LogEntry
  8. enum NounKind {
  9. Specific,
  10. Nonspecific,
  11. All
  12. }
  13. enum VowelSound {
  14. Default,
  15. Vowel,
  16. NonVowel
  17. }
  18. enum VerbKind {
  19. Root,
  20. Singular,
  21. Present,
  22. Past,
  23. PastParticiple
  24. }
  25. export interface Pluralizable {
  26. isPlural: boolean;
  27. }
  28. interface WordOptions {
  29. plural: boolean;
  30. capital: boolean;
  31. allCaps: boolean;
  32. proper: boolean;
  33. nounKind: NounKind;
  34. verbKind: VerbKind;
  35. vowel: VowelSound;
  36. count: boolean;
  37. possessive: boolean;
  38. objective: boolean;
  39. perspective: POV;
  40. }
  41. const emptyConfig: WordOptions = {
  42. allCaps: false,
  43. capital: false,
  44. count: false,
  45. nounKind: NounKind.Specific,
  46. verbKind: VerbKind.Root,
  47. plural: false,
  48. proper: false,
  49. vowel: VowelSound.Default,
  50. possessive: false,
  51. objective: false,
  52. perspective: POV.Third
  53. }
  54. export type TextLike = { toString: () => string }
  55. // updates as needed
  56. export class LiveText<T> {
  57. constructor (private contents: T, private run: (thing: T) => TextLike) {
  58. }
  59. toString (): string {
  60. return this.run(this.contents).toString()
  61. }
  62. }
  63. export class DynText {
  64. private parts: Array<TextLike>
  65. constructor (...parts: TextLike[]) {
  66. this.parts = parts
  67. }
  68. toString (): string {
  69. return (this.parts.map(part => part.toString())).join('')
  70. }
  71. }
  72. export abstract class Word {
  73. constructor (public opt: WordOptions = emptyConfig) {
  74. }
  75. abstract configure (opts: WordOptions): Word;
  76. abstract toString (): string;
  77. // These functions are pure; they don't mutate the original object.
  78. // This is necessary to avoid causing chaos.
  79. get allCaps (): this {
  80. const opts: WordOptions = Object.assign({}, this.opt)
  81. opts.allCaps = true
  82. return this.configure(opts) as this
  83. }
  84. get capital (): this {
  85. const opts: WordOptions = Object.assign({}, this.opt)
  86. opts.capital = true
  87. return this.configure(opts) as this
  88. }
  89. get plural (): this {
  90. const opts: WordOptions = Object.assign({}, this.opt)
  91. opts.plural = true
  92. return this.configure(opts) as this
  93. }
  94. get proper (): this {
  95. const opts: WordOptions = Object.assign({}, this.opt)
  96. opts.proper = true
  97. return this.configure(opts) as this
  98. }
  99. get improper (): this {
  100. const opts: WordOptions = Object.assign({}, this.opt)
  101. opts.proper = false
  102. return this.configure(opts) as this
  103. }
  104. get specific (): this {
  105. const opts: WordOptions = Object.assign({}, this.opt)
  106. opts.nounKind = NounKind.Specific
  107. return this.configure(opts) as this
  108. }
  109. get nonspecific (): this {
  110. const opts: WordOptions = Object.assign({}, this.opt)
  111. opts.nounKind = NounKind.Nonspecific
  112. return this.configure(opts) as this
  113. }
  114. get all (): this {
  115. const opts: WordOptions = Object.assign({}, this.opt)
  116. opts.nounKind = NounKind.All
  117. return this.configure(opts) as this
  118. }
  119. get uncountable (): this {
  120. const opts: WordOptions = Object.assign({}, this.opt)
  121. opts.count = false
  122. return this.configure(opts) as this
  123. }
  124. get root (): this {
  125. const opts: WordOptions = Object.assign({}, this.opt)
  126. opts.verbKind = VerbKind.Root
  127. return this.configure(opts) as this
  128. }
  129. get singular (): this {
  130. const opts: WordOptions = Object.assign({}, this.opt)
  131. opts.verbKind = VerbKind.Singular
  132. return this.configure(opts) as this
  133. }
  134. get present (): this {
  135. const opts: WordOptions = Object.assign({}, this.opt)
  136. opts.verbKind = VerbKind.Present
  137. return this.configure(opts) as this
  138. }
  139. get past (): this {
  140. const opts: WordOptions = Object.assign({}, this.opt)
  141. opts.verbKind = VerbKind.Past
  142. return this.configure(opts) as this
  143. }
  144. get pastParticiple (): this {
  145. const opts: WordOptions = Object.assign({}, this.opt)
  146. opts.verbKind = VerbKind.PastParticiple
  147. return this.configure(opts) as this
  148. }
  149. get possessive (): this {
  150. const opts: WordOptions = Object.assign({}, this.opt)
  151. opts.possessive = true
  152. return this.configure(opts) as this
  153. }
  154. get objective (): this {
  155. const opts: WordOptions = Object.assign({}, this.opt)
  156. opts.objective = true
  157. return this.configure(opts) as this
  158. }
  159. get pov1 (): this {
  160. const opts: WordOptions = Object.assign({}, this.opt)
  161. opts.perspective = POV.First
  162. return this.configure(opts) as this
  163. }
  164. get pov2 (): this {
  165. const opts: WordOptions = Object.assign({}, this.opt)
  166. opts.perspective = POV.Second
  167. return this.configure(opts) as this
  168. }
  169. get pov3 (): this {
  170. const opts: WordOptions = Object.assign({}, this.opt)
  171. opts.perspective = POV.Third
  172. return this.configure(opts) as this
  173. }
  174. }
  175. export class RandomWord extends Word {
  176. private history: { last: number }
  177. constructor (public choices: Array<Word>, opt: WordOptions = emptyConfig, history: { last: number } = { last: -1 }) {
  178. super(opt)
  179. this.history = history
  180. }
  181. configure (opts: WordOptions): Word {
  182. return new RandomWord(this.choices, opts, this.history)
  183. }
  184. toString (): string {
  185. let choice
  186. do {
  187. choice = Math.floor(Math.random() * this.choices.length)
  188. } while (choice === this.history.last)
  189. this.history.last = choice
  190. return this.choices[choice].configure(this.opt).toString()
  191. }
  192. }
  193. export class Noun extends Word {
  194. constructor (protected singularNoun: string, protected pluralNoun: string|null = null, protected possessiveNoun: string|null = null, protected options: WordOptions = emptyConfig) {
  195. super(options)
  196. }
  197. configure (opts: WordOptions): Word {
  198. return new Noun(this.singularNoun, this.pluralNoun, this.possessiveNoun, opts)
  199. }
  200. toString (): string {
  201. let result: string
  202. // TODO: plural possessive nouns?
  203. if (this.options.possessive) {
  204. if (this.possessiveNoun === null) {
  205. result = this.singularNoun + "'s"
  206. } else {
  207. result = this.possessiveNoun
  208. }
  209. } else if (this.options.plural) {
  210. if (this.pluralNoun === null) {
  211. result = this.singularNoun
  212. } else {
  213. result = (this.pluralNoun as string)
  214. }
  215. } else {
  216. result = this.singularNoun
  217. }
  218. if (!this.options.proper) {
  219. if (this.options.nounKind === NounKind.Nonspecific && this.options.count) {
  220. if (this.options.plural) {
  221. result = 'some ' + result
  222. } else {
  223. if (this.options.vowel === VowelSound.Default) {
  224. if ('aeiouAEIOU'.indexOf(result.slice(0, 1)) >= 0) {
  225. result = 'an ' + result
  226. } else {
  227. result = 'a ' + result
  228. }
  229. } else if (this.options.vowel === VowelSound.Vowel) {
  230. result = 'an ' + result
  231. } else if (this.options.vowel === VowelSound.NonVowel) {
  232. result = 'a ' + result
  233. }
  234. }
  235. } else if (this.options.nounKind === NounKind.Specific) {
  236. result = 'the ' + result
  237. }
  238. }
  239. if (this.options.allCaps) {
  240. result = result.toUpperCase()
  241. } else if (this.options.capital) {
  242. result = result.slice(0, 1).toUpperCase() + result.slice(1)
  243. }
  244. return result
  245. }
  246. conjugate (verb: Word): Word {
  247. if (this.opt.plural) {
  248. return verb.root
  249. } else {
  250. return verb.singular
  251. }
  252. }
  253. }
  254. export class ImproperNoun extends Noun {
  255. constructor (singularNoun: string, pluralNoun: string = singularNoun, possessiveNoun: string = singularNoun + "'s") {
  256. 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 })
  257. }
  258. }
  259. export class ProperNoun extends Noun {
  260. constructor (singularNoun: string) {
  261. 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 })
  262. }
  263. }
  264. export class Adjective extends Word {
  265. constructor (private adjective: string, opt: WordOptions = emptyConfig) {
  266. super(opt)
  267. }
  268. configure (opts: WordOptions): Word {
  269. return new Adjective(this.adjective, opts)
  270. }
  271. // TODO caps et al.
  272. toString (): string {
  273. return this.adjective
  274. }
  275. }
  276. export class Verb extends Word {
  277. 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) {
  278. super(opt)
  279. }
  280. configure (opts: WordOptions): Word {
  281. return new Verb(
  282. this._root,
  283. this._singular,
  284. this._present,
  285. this._past,
  286. this._pastParticiple,
  287. opts
  288. )
  289. }
  290. toString (): string {
  291. let choice: string
  292. switch (this.opt.verbKind) {
  293. case VerbKind.Root: choice = this._root; break
  294. case VerbKind.Singular: choice = this._singular; break
  295. case VerbKind.Present: choice = this._present; break
  296. case VerbKind.Past: choice = this._past; break
  297. case VerbKind.PastParticiple: choice = this._pastParticiple; break
  298. }
  299. if (this.opt.allCaps) {
  300. choice = choice.toUpperCase()
  301. } else if (this.opt.capital) {
  302. choice = choice.slice(0, 1).toUpperCase() + choice.slice(1)
  303. }
  304. return choice
  305. }
  306. }
  307. // this one is obnoxious
  308. export class ToBe extends Word {
  309. constructor (protected opts: WordOptions = emptyConfig) {
  310. super(opts)
  311. }
  312. configure (opts: WordOptions): Word {
  313. return new ToBe(opts)
  314. }
  315. toString (): string {
  316. let choice
  317. if (this.opts.plural) {
  318. choice = 'are'
  319. }
  320. switch (this.opts.perspective) {
  321. case POV.First: choice = 'am'; break
  322. case POV.Second: choice = 'are'; break
  323. case POV.Third: choice = 'is'; break
  324. }
  325. if (this.opt.allCaps) {
  326. choice = choice.toUpperCase()
  327. } else if (this.opt.capital) {
  328. choice = choice.slice(0, 1).toUpperCase() + choice.slice(1)
  329. }
  330. return choice
  331. }
  332. }
  333. interface PronounDict {
  334. subjective: string;
  335. objective: string;
  336. possessive: string;
  337. reflexive: string;
  338. }
  339. export class Pronoun implements Pluralizable {
  340. constructor (private pronouns: PronounDict, private capitalize: boolean = false, public isPlural: boolean = false) {
  341. }
  342. get capital (): Pronoun {
  343. return new Pronoun(this.pronouns, true)
  344. }
  345. get subjective (): string {
  346. return this.caps(this.pronouns.subjective)
  347. }
  348. get objective (): string {
  349. return this.caps(this.pronouns.objective)
  350. }
  351. get possessive (): string {
  352. return this.caps(this.pronouns.possessive)
  353. }
  354. get reflexive (): string {
  355. return this.caps(this.pronouns.reflexive)
  356. }
  357. conjugate (verb: Word): Word {
  358. if (this.isPlural) {
  359. return verb.root
  360. } else {
  361. return verb.singular
  362. }
  363. }
  364. private caps (input: string): string {
  365. if (this.capitalize) {
  366. return input.slice(0, 1).toUpperCase() + input.slice(1)
  367. } else {
  368. return input
  369. }
  370. }
  371. }
  372. export const MalePronouns = new Pronoun({
  373. subjective: 'he',
  374. objective: 'him',
  375. possessive: 'his',
  376. reflexive: 'himself'
  377. })
  378. export const FemalePronouns = new Pronoun({
  379. subjective: 'she',
  380. objective: 'her',
  381. possessive: 'her',
  382. reflexive: 'herself'
  383. })
  384. export const TheyPronouns = new Pronoun({
  385. subjective: 'they',
  386. objective: 'them',
  387. possessive: 'their',
  388. reflexive: 'themself'
  389. }, false, true)
  390. export const TheyPluralPronouns = new Pronoun({
  391. subjective: 'they',
  392. objective: 'them',
  393. possessive: 'their',
  394. reflexive: 'themselves'
  395. }, false, true)
  396. export const ObjectPronouns = new Pronoun({
  397. subjective: 'it',
  398. objective: 'it',
  399. possessive: 'its',
  400. reflexive: 'itself'
  401. })
  402. export const SecondPersonPronouns = new Pronoun({
  403. subjective: 'you',
  404. objective: 'you',
  405. possessive: 'your',
  406. reflexive: 'yourself'
  407. })
  408. export const FirstPersonPronouns = new Pronoun({
  409. subjective: 'I',
  410. objective: 'me',
  411. possessive: 'my',
  412. reflexive: 'myself'
  413. })
  414. export class PronounAsNoun extends Noun {
  415. constructor (private pronouns: Pronoun, opt: WordOptions = emptyConfig) {
  416. super(pronouns.subjective, pronouns.subjective, pronouns.possessive, opt)
  417. this.options.nounKind = NounKind.All
  418. this.options.plural = true
  419. }
  420. configure (opts: WordOptions): Word {
  421. return new PronounAsNoun(this.pronouns, opts)
  422. }
  423. toString (): string {
  424. if (this.options.objective) {
  425. return new Noun(this.pronouns.objective, this.pronouns.objective, this.pronouns.possessive, this.options).toString()
  426. } else {
  427. return super.toString()
  428. }
  429. }
  430. conjugate (verb: Word): Word {
  431. if (this.pronouns === FirstPersonPronouns) {
  432. return super.conjugate(verb.pov1)
  433. } else if (this.pronouns === SecondPersonPronouns) {
  434. return super.conjugate(verb.pov2)
  435. } else {
  436. return super.conjugate(verb.pov3)
  437. }
  438. }
  439. }