import { Stat, Vigor, StatDescs, VigorDescs, StatIcons, VigorIcons, VoreStat, VoreStatIcons, VoreStatDescs } from './combat' import tippy from 'tippy.js' /** * A LogEntry is something that can produce zero or more HTMLElements */ export interface LogEntry { render: () => HTMLElement[]; } /** * Takes zero or more strings or [[LogEntry]] objects * * Produces a list of divs containing each string/object */ export class LogLines implements LogEntry { private parts: Array constructor (...parts: Array) { this.parts = parts } render (): HTMLElement[] { const div = document.createElement("div") this.parts.forEach(part => { if (typeof part === "string") { const partDiv = document.createElement("div") partDiv.innerText = part div.appendChild(partDiv) } else { (part as LogEntry).render().forEach(logPart => { const partDiv = document.createElement("div") partDiv.appendChild(logPart) div.appendChild(partDiv) }) } }) return [div] } } export enum FormatOpt { Damage = "log-damage", DamageInst = "damage-instance" } /** * Wraps its LogEntry up in a span with the specified class */ export class FormatEntry implements LogEntry { constructor (private entry: LogEntry, private opt: FormatOpt) { } render (): HTMLElement[] { const span = document.createElement("span") this.entry.render().forEach(elem => { span.appendChild(elem) }) span.classList.add(this.opt) return [span] } } /** * Wraps a string up in a span with the specified class * * This will probably be folded into FormatEntry soon */ export class FormatText implements LogEntry { constructor (private opt: FormatOpt, private line: string) { } render (): HTMLElement[] { const span = document.createElement("span") span.innerText = this.line span.classList.add(this.opt) return [span] } } /** * Like [[LogLines]], but with spans instead of divs */ export class LogLine implements LogEntry { private parts: Array constructor (...parts: Array) { this.parts = parts } render (): HTMLElement[] { const div = document.createElement("span") this.parts.forEach(part => { if (typeof part === "string") { const partSpan = document.createElement("span") partSpan.innerText = part div.appendChild(partSpan) } else { (part as LogEntry).render().forEach(logPart => { div.appendChild(logPart) }) } }) return [div] } } export class Newline implements LogEntry { render (): HTMLElement[] { return [document.createElement("br")] } } /** * Produces a FontAwesome icon */ export class FAElem implements LogEntry { constructor (private name: string) { } render (): HTMLElement[] { const i = document.createElement("i") this.name.split(" ").map(cls => i.classList.add(cls)) return [i] } } /** * Produces a representation of a creature's property, such as health or power * * Can be just the icon, or include a number as well * * A tooltip is attached to the symbol */ export class PropElem implements LogEntry { constructor (private prop: Stat | Vigor | VoreStat, private value: number|null = null) { } render (): HTMLElement[] { let cls: string if (this.prop in Stat) { cls = StatIcons[this.prop as Stat] } else if (this.prop in Vigor) { cls = VigorIcons[this.prop as Vigor] } else if (this.prop in Vigor) { cls = VoreStatIcons[this.prop as VoreStat] } else { // this shouldn't be possible, given the typing... cls = "fas fa-exclamation-triangle" } const span = document.createElement("span") span.classList.add("stat-entry") const tooltipTemplate = document.createElement("div") const tooltipTitle = document.createElement("div") tooltipTitle.classList.add("tooltip-title") const tooltipBody = document.createElement("div") tooltipBody.classList.add("tooltip-body") tooltipTemplate.appendChild(tooltipTitle) tooltipTemplate.appendChild(tooltipBody) tooltipTitle.textContent = this.prop if (this.prop in Stat) { tooltipBody.textContent = StatDescs[this.prop as Stat] } else if (this.prop in Vigor) { tooltipBody.textContent = VigorDescs[this.prop as Vigor] } else if (this.prop in VoreStat) { tooltipBody.textContent = VoreStatDescs[this.prop as VoreStat] } if (this.value !== null) { const numText = Math.round(this.value).toFixed(0) === this.value.toFixed(0) ? this.value.toFixed(0) : this.value.toFixed(1) span.textContent = numText + ' ' } const icon = new FAElem(cls).render()[0] span.appendChild(icon) tippy(icon, { content: tooltipTemplate }) return [span] } } /** * Produces an */ export class ImgElem implements LogEntry { constructor (private url: string) { } render (): HTMLElement[] { const div = document.createElement("div") const img = document.createElement("img") img.src = this.url div.appendChild(img) return [div] } } /** * Directly concatenates zero or more [[LogEntry]] objects, without wrapping them in anything */ export class CompositeLog implements LogEntry { entries: LogEntry[] constructor (...entries: LogEntry[]) { this.entries = entries } render (): HTMLElement[] { return this.entries.flatMap(e => e.render()) } }