All user data for FoundryVTT. Includes worlds, systems, modules, and any asset in the "foundryuserdata" directory. Does NOT include the FoundryVTT installation itself.
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.

248 lines
7.5 KiB

  1. class BossBar {
  2. constructor() {
  3. this.actor;
  4. this.token;
  5. this.bgPath = game.settings.get("bossbar", "backgroundPath");
  6. this.fgPath = game.settings.get("bossbar", "foregroundPath");
  7. this.tempBarColor = game.settings.get("bossbar", "tempBarColor");
  8. this.textSize = game.settings.get("bossbar", "textSize");
  9. this.position = game.settings.get("bossbar", "position");
  10. }
  11. static async create(token, render = true) {
  12. let instance = new BossBar();
  13. instance.actor = token.actor;
  14. instance.token = token;
  15. let bgFlag = token.document.getFlag("bossbar", "bgTex");
  16. let fgFlag = token.document.getFlag("bossbar", "fgTex");
  17. if (bgFlag) instance.bgPath = bgFlag;
  18. if (fgFlag) instance.fgPath = fgFlag;
  19. this.addBossBar(instance);
  20. if (render) instance.draw(game.settings.get("bossbar", "barHeight"));
  21. if (game.user.isGM) {
  22. let oldBars = canvas.scene.getFlag("bossbar", "bossBarActive");
  23. if (Array.isArray(oldBars)) {
  24. oldBars.push(token.id);
  25. } else {
  26. oldBars = [token.id];
  27. }
  28. await canvas.scene.setFlag("bossbar", "bossBarActive", oldBars);
  29. }
  30. instance.hookId = Hooks.on("updateActor", (actor, updates) => {
  31. if (
  32. actor.id == instance.actor.id &&
  33. Object.byString(updates.system, game.settings.get("bossbar", "currentHpPath")) !== undefined
  34. ) {
  35. instance.update();
  36. }
  37. });
  38. instance.CTHook = Hooks.on("renderCombatTracker", (app, html, data) => {
  39. instance.CCTSetPosition();
  40. });
  41. instance.CCTHook = Hooks.on("combatDock:playIntroAnimation:finished", (app, html, data) => {
  42. instance.CCTSetPosition(0);
  43. });
  44. instance.CCTCloseHook = Hooks.on("closeCombatDock", (app, html, data) => {
  45. instance.CCTSetPosition();
  46. });
  47. return instance;
  48. }
  49. CCTSetPosition(delay = 0) {
  50. setTimeout(() => {
  51. try {
  52. const combatDockElement = document.getElementById("combat-dock");
  53. const spacer = document.querySelector(".bossBarSpacer");
  54. if (!spacer) return;
  55. spacer.style.setProperty("height", "0px");
  56. const spacerDistanceFromTop = spacer.getBoundingClientRect().top;
  57. if (!combatDockElement || combatDockElement.classList.contains("hidden")) {
  58. spacer.style.setProperty("height", "0px");
  59. return;
  60. }
  61. const height = combatDockElement.offsetHeight - spacerDistanceFromTop;
  62. spacer.style.setProperty("height", `${height*0.8}px`);
  63. } catch (error) {
  64. }
  65. }, delay);
  66. }
  67. draw(h) {
  68. if ($("body").find(`div[id="bossBar-${this.id}"]`).length > 0) return; //#navigation
  69. let bossBarContainer = `<div id="bossBarContainer"></div>`;
  70. let bossBarHtml = `<div class="bossBarSpacer" style="transition: height 0.3s ease-in-out; flex-basis: 100%;height: 0px;" id="bossBarSpacer-${
  71. this.id
  72. }"></div><div id="bossBar-${this.id}" class="bossBar">
  73. <a class="bossBarName" style="font-size: ${this.textSize}px;">${
  74. this.name
  75. }</a>
  76. <div id ="bossBarBar-${this.id}" style="z-index: 1000;">
  77. <div id="bossBarMax-${
  78. this.id
  79. }" class="bossBarMax" style="background-image:url('${
  80. this.bgPath
  81. }');height:${h}px;"></div>
  82. <div id="bossBarTemp-${
  83. this.id
  84. }" class="bossBarTemp" style="background-color:${
  85. this.tempBarColor
  86. };height:${h}px;width:${this.hpPercent}%"></div>
  87. <div id="bossBarCurrent-${
  88. this.id
  89. }" class="bossBarCurrent" style="background-image:url('${
  90. this.fgPath
  91. }');height:${h}px;width:${this.hpPercent}%"></div>
  92. </div>
  93. </div>
  94. <div style="flex-basis: 100%;height: ${
  95. this.textSize + 3
  96. }px;" id ="bossBarSpacer-${this.id}"></div>
  97. `;
  98. switch (this.position) {
  99. case 0:
  100. $("#ui-top").append(bossBarHtml);
  101. break;
  102. case 1:
  103. const cameraContainerW = $("#camera-views").width();
  104. if ($("#bossBarContainer").length == 0) {
  105. $("#ui-bottom").find("div").first().prepend(bossBarContainer);
  106. }
  107. $("#bossBarContainer").append(bossBarHtml);
  108. $("#bossBarContainer").css({
  109. position: "fixed",
  110. bottom:
  111. $("#hotbar").outerHeight(true) +
  112. $(".bossBar").outerHeight(true) +
  113. 10,
  114. width: `calc(100% - 330px - ${cameraContainerW}px)`,
  115. left: 15 + cameraContainerW,
  116. });
  117. break;
  118. default:
  119. $("#ui-top").append(bossBarHtml);
  120. }
  121. this.update();
  122. }
  123. update() {
  124. const isBar = document.getElementById(`bossBarCurrent-${this.id}`)
  125. if(!isBar) return;
  126. document
  127. .getElementById(`bossBarCurrent-${this.id}`)
  128. .style.setProperty("width", `${this.hpPercent}%`);
  129. document
  130. .getElementById(`bossBarTemp-${this.id}`)
  131. .style.setProperty("width", `${this.hpPercent}%`);
  132. this.CCTSetPosition();
  133. }
  134. clear() {
  135. $("body").find(`div[id="bossBar-${this.id}"]`).remove();
  136. }
  137. async destroy() {
  138. const flag = canvas.scene.getFlag("bossbar", "bossBarActive");
  139. let newFlag = [];
  140. for (let id of flag) {
  141. if (id == this.token.id) continue;
  142. newFlag.push(id);
  143. }
  144. await canvas.scene.setFlag("bossbar", "bossBarActive", newFlag);
  145. this.unHook();
  146. }
  147. static clearAll() {
  148. if (!canvas.scene._bossBars) return;
  149. for (let bar of Object.entries(canvas.scene._bossBars)) {
  150. $("body").find(`div[id="bossBar-${bar[1].id}"]`).remove();
  151. $("body").find(`div[id="bossBarSpacer-${bar[1].id}"]`).remove();
  152. }
  153. }
  154. static async remove() {
  155. await canvas.scene.unsetFlag("bossbar", "bossBarActive");
  156. canvas.scene._bossBars = {};
  157. this.clearAll();
  158. }
  159. unHook() {
  160. Hooks.off("updateActor", this.hookId);
  161. Hooks.off("renderCombatTracker", this.CTHook)
  162. Hooks.off("renderCombatDock", this.CCTHook)
  163. Hooks.off("closeCombatDock", this.CCTCloseHook)
  164. this.clear();
  165. }
  166. static addBossBar(bossBar) {
  167. if (!canvas.scene._bossBars) {
  168. canvas.scene._bossBars = {};
  169. }
  170. canvas.scene._bossBars[bossBar.id] = bossBar;
  171. }
  172. static cameraPan(tokenId, scale, duration) {
  173. const token = canvas.tokens.get(tokenId);
  174. canvas.animatePan({
  175. x: token.center.x,
  176. y: token.center.y,
  177. scale: scale,
  178. duration: duration,
  179. });
  180. }
  181. static panCamera(token, scale = 1.8, duration = 1000) {
  182. _BossBarSocket.executeForEveryone("cameraPan", token.id, scale, duration);
  183. }
  184. static async renderBossBar() {
  185. if (canvas.scene) {
  186. BossBar.clearAll();
  187. const ids = canvas.scene.getFlag("bossbar", "bossBarActive");
  188. if (!ids) return;
  189. for (let id of ids) {
  190. if (canvas.scene._bossBars && canvas.scene._bossBars[id]) {
  191. canvas.scene._bossBars[id].draw(
  192. game.settings.get("bossbar", "barHeight")
  193. );
  194. } else {
  195. await BossBar.create(canvas.tokens.get(id));
  196. }
  197. }
  198. }
  199. }
  200. get currentHp() {
  201. return Object.byString(
  202. this.actor.system,
  203. game.settings.get("bossbar", "currentHpPath")
  204. );
  205. }
  206. get maxHp() {
  207. return Object.byString(
  208. this.actor.system,
  209. game.settings.get("bossbar", "maxHpPath")
  210. );
  211. }
  212. get hpPercent() {
  213. return Math.max(0, Math.round((100 * this.currentHp) / this.maxHp));
  214. }
  215. get hpPercentAsString() {
  216. return String(this.hpPercent);
  217. }
  218. get name() {
  219. return this.token.document.name;
  220. }
  221. get id() {
  222. return this.token.id;
  223. }
  224. }