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.
58 lines
2.0 KiB
58 lines
2.0 KiB
import { shuffleWeightedList, WeightedList } from './weightedList'
|
|
import { PRNG } from 'seedrandom'
|
|
export type RenderableText = string | WeightedList<string>
|
|
|
|
export interface TextSource {
|
|
[key: string]: RenderableText
|
|
}
|
|
|
|
export function renderText(text: RenderableText, data: TextSource, rng: PRNG): string {
|
|
const result = shuffleWeightedList(text, '', rng)
|
|
if (needsSubstitution(result)) {
|
|
return substituteText(result, data, rng)
|
|
} else {
|
|
return result
|
|
}
|
|
}
|
|
|
|
const textReplacementRE = /\\(.)|\{\{\s*([A-Za-z0-9_]+)\s*}}/g
|
|
|
|
function needsSubstitution(text: string): boolean {
|
|
return textReplacementRE.test(text)
|
|
}
|
|
|
|
function substituteText(text: string, data: TextSource, random: PRNG): string {
|
|
return text.replaceAll(
|
|
textReplacementRE,
|
|
(substring, escapedCharacter: string | undefined, fieldName: string | undefined): string => {
|
|
let result: undefined | RenderableText = undefined
|
|
let errType = 'substitution'
|
|
|
|
if (typeof escapedCharacter === 'string') {
|
|
if (escapedCharacter === '\\' || escapedCharacter === '{') {
|
|
result = escapedCharacter
|
|
} else {
|
|
errType = 'escape'
|
|
}
|
|
}
|
|
if (typeof fieldName === 'string') {
|
|
errType = 'variable or commonText'
|
|
result = data[fieldName]
|
|
if (typeof result === 'string') {
|
|
if (needsSubstitution(result)) {
|
|
return substituteText(result, data, random)
|
|
}
|
|
} else if (typeof result === 'undefined') {
|
|
errType = 'variable or commonText'
|
|
} else {
|
|
return renderText(result, data, random)
|
|
}
|
|
}
|
|
|
|
if (typeof result === 'undefined') {
|
|
throw Error(`unknown ${errType} ${substring}`)
|
|
}
|
|
return result
|
|
},
|
|
)
|
|
}
|
|
|