Feast 2.0!
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 
 
 

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