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.
 
 
 
 
 

233 lines
5.4 KiB

  1. import { Stat, Vigor, StatDescs, VigorDescs, StatIcons, VigorIcons, VoreStat, VoreStatIcons, VoreStatDescs } from './combat'
  2. import tippy from 'tippy.js'
  3. /**
  4. * A LogEntry is something that can produce zero or more HTMLElements
  5. */
  6. export interface LogEntry {
  7. render: () => HTMLElement[];
  8. }
  9. /**
  10. * Takes zero or more strings or [[LogEntry]] objects
  11. *
  12. * Produces a list of divs containing each string/object
  13. */
  14. export class LogLines implements LogEntry {
  15. private parts: Array<string|LogEntry>
  16. constructor (...parts: Array<string|LogEntry>) {
  17. this.parts = parts
  18. }
  19. render (): HTMLElement[] {
  20. const div = document.createElement("div")
  21. this.parts.forEach(part => {
  22. if (typeof part === "string") {
  23. const partDiv = document.createElement("div")
  24. partDiv.innerText = part
  25. div.appendChild(partDiv)
  26. } else {
  27. (part as LogEntry).render().forEach(logPart => {
  28. const partDiv = document.createElement("div")
  29. partDiv.appendChild(logPart)
  30. div.appendChild(partDiv)
  31. })
  32. }
  33. })
  34. return [div]
  35. }
  36. }
  37. export enum FormatOpt {
  38. Damage = "log-damage",
  39. DamageInst = "damage-instance"
  40. }
  41. /**
  42. * Wraps its LogEntry up in a span with the specified class
  43. */
  44. export class FormatEntry implements LogEntry {
  45. constructor (private entry: LogEntry, private opt: FormatOpt) {
  46. }
  47. render (): HTMLElement[] {
  48. const span = document.createElement("span")
  49. this.entry.render().forEach(elem => {
  50. span.appendChild(elem)
  51. })
  52. span.classList.add(this.opt)
  53. return [span]
  54. }
  55. }
  56. /**
  57. * Wraps a string up in a span with the specified class
  58. *
  59. * This will probably be folded into FormatEntry soon
  60. */
  61. export class FormatText implements LogEntry {
  62. constructor (private opt: FormatOpt, private line: string) {
  63. }
  64. render (): HTMLElement[] {
  65. const span = document.createElement("span")
  66. span.innerText = this.line
  67. span.classList.add(this.opt)
  68. return [span]
  69. }
  70. }
  71. /**
  72. * Like [[LogLines]], but with spans instead of divs
  73. */
  74. export class LogLine implements LogEntry {
  75. private parts: Array<string|LogEntry>
  76. constructor (...parts: Array<string|LogEntry>) {
  77. this.parts = parts
  78. }
  79. render (): HTMLElement[] {
  80. const div = document.createElement("span")
  81. this.parts.forEach(part => {
  82. if (typeof part === "string") {
  83. const partSpan = document.createElement("span")
  84. partSpan.innerText = part
  85. div.appendChild(partSpan)
  86. } else {
  87. (part as LogEntry).render().forEach(logPart => {
  88. div.appendChild(logPart)
  89. })
  90. }
  91. })
  92. return [div]
  93. }
  94. }
  95. export class Newline implements LogEntry {
  96. render (): HTMLElement[] {
  97. return [document.createElement("br")]
  98. }
  99. }
  100. /**
  101. * Produces a FontAwesome icon
  102. */
  103. export class FAElem implements LogEntry {
  104. constructor (private name: string) {
  105. }
  106. render (): HTMLElement[] {
  107. const i = document.createElement("i")
  108. this.name.split(" ").map(cls => i.classList.add(cls))
  109. return [i]
  110. }
  111. }
  112. /**
  113. * Produces a representation of a creature's property, such as health or power
  114. *
  115. * Can be just the icon, or include a number as well
  116. *
  117. * A tooltip is attached to the symbol
  118. */
  119. export class PropElem implements LogEntry {
  120. constructor (private prop: Stat | Vigor | VoreStat, private value: number|null = null) {
  121. }
  122. render (): HTMLElement[] {
  123. let cls: string
  124. if (this.prop in Stat) {
  125. cls = StatIcons[this.prop as Stat]
  126. } else if (this.prop in Vigor) {
  127. cls = VigorIcons[this.prop as Vigor]
  128. } else if (this.prop in Vigor) {
  129. cls = VoreStatIcons[this.prop as VoreStat]
  130. } else {
  131. // this shouldn't be possible, given the typing...
  132. cls = "fas fa-exclamation-triangle"
  133. }
  134. const span = document.createElement("span")
  135. span.classList.add("stat-entry")
  136. const tooltipTemplate = document.createElement("div")
  137. const tooltipTitle = document.createElement("div")
  138. tooltipTitle.classList.add("tooltip-title")
  139. const tooltipBody = document.createElement("div")
  140. tooltipBody.classList.add("tooltip-body")
  141. tooltipTemplate.appendChild(tooltipTitle)
  142. tooltipTemplate.appendChild(tooltipBody)
  143. tooltipTitle.textContent = this.prop
  144. if (this.prop in Stat) {
  145. tooltipBody.textContent = StatDescs[this.prop as Stat]
  146. } else if (this.prop in Vigor) {
  147. tooltipBody.textContent = VigorDescs[this.prop as Vigor]
  148. } else if (this.prop in VoreStat) {
  149. tooltipBody.textContent = VoreStatDescs[this.prop as VoreStat]
  150. }
  151. if (this.value !== null) {
  152. const numText = Math.round(this.value).toFixed(0) === this.value.toFixed(0) ? this.value.toFixed(0) : this.value.toFixed(1)
  153. span.textContent = numText + ' '
  154. }
  155. const icon = new FAElem(cls).render()[0]
  156. span.appendChild(icon)
  157. tippy(icon, {
  158. content: tooltipTemplate
  159. })
  160. return [span]
  161. }
  162. }
  163. /**
  164. * Produces an <img>
  165. */
  166. export class ImgElem implements LogEntry {
  167. constructor (private url: string) {
  168. }
  169. render (): HTMLElement[] {
  170. const div = document.createElement("div")
  171. const img = document.createElement("img")
  172. img.src = this.url
  173. div.appendChild(img)
  174. return [div]
  175. }
  176. }
  177. /**
  178. * Directly concatenates zero or more [[LogEntry]] objects, without wrapping them in anything
  179. */
  180. export class CompositeLog implements LogEntry {
  181. entries: LogEntry[]
  182. constructor (...entries: LogEntry[]) {
  183. this.entries = entries
  184. }
  185. render (): HTMLElement[] {
  186. return this.entries.flatMap(e => e.render())
  187. }
  188. }