Skip to content

Instantly share code, notes, and snippets.

@CodaBool
Created October 18, 2024 16:37
Show Gist options
  • Save CodaBool/f17aeb0a47f956c1c775cb2fb7029be7 to your computer and use it in GitHub Desktop.
Save CodaBool/f17aeb0a47f956c1c775cb2fb7029be7 to your computer and use it in GitHub Desktop.
throwaway pastebin (foundry V13 first draft)
Hooks.on("renderTileConfig", async (app, html) => {
const doc = app.document
// game.data.release.generation > 12
const styleExists = Object.keys(game.settings.get(ID, "styles")).includes(
doc.getFlag(ID, "style"),
)
const itemPrettyName = game.items.get(doc.getFlag(ID, "keycard")?.split("@")[0])?.name
let macroPrettyName = game.macros.get(doc.getFlag(ID, "macro"))?.name
let actorPrettyName = game.actors.get(doc.getFlag(ID, "observeActor"))?.name
let journalPrettyName = game.journal.get(doc.getFlag(ID, "journal"))?.name
if (doc.getFlag(ID, "journal") && !journalPrettyName)
journalPrettyName = "journal has been deleted!"
if (doc.getFlag(ID, "observeActor") && !actorPrettyName)
actorPrettyName = "actor has been deleted!"
if (doc.getFlag(ID, "macro") && !macroPrettyName)
macroPrettyName = "macro has been deleted!"
if (!styleExists && doc._id) {
const preset = stylePresets.get(game.system.id)
doc.setFlag(ID, "style", preset || "generic-green")
}
const stylesUnsorted = game.settings.get(ID, "styles")
const styles = Object.values(stylesUnsorted).sort((a, b) => {
return (defaultStyles[a.uuid] ? 1 : 0) - (defaultStyles[b.uuid] ? 1 : 0)
})
let template = await renderTemplate(`modules/${ID}/templates/config.hbs`, {
styles,
...doc.flags.terminal,
system: game.system.id,
hasMonk: game.modules.get("monks-active-tiles")?.active,
hasPuzzleLocks: game.modules.get("puzzle-locks")?.active,
journalPrettyName,
actorPrettyName,
macroPrettyName,
itemPrettyName,
styleExists,
doorLength: doc.getFlag(ID, "unlockIds")?.length && doc.getFlag(ID, "unlockIds")?.split(",")?.length || 0,
doorIds: doc.getFlag(ID, "unlockIds")?.length ? doc.getFlag(ID, "unlockIds")?.split(",") : [],
})
if (!doc._id) {
template = `<div class='tab' data-tab='${ID}' data-group='sheet'><p style="color: grey; text-align: center">You must <b>Create Tile</b> before editing Terminal settings</p></div>`
}
// add tab
const a = document.createElement("a")
a.setAttribute("data-action", "tab")
a.setAttribute("data-group", "sheet")
a.setAttribute("data-tab", "terminal")
if (!html.querySelector(".active")) {
a.classList.add("active")
}
a.innerHTML = `<i class="fa-solid fa-computer"></i><label> Terminal</label>`
const tabs = document.getElementById(app.id).querySelector(".tabs a:nth-child(3)")
tabs.insertAdjacentElement("afterend", a)
// add content
const tabContent = html.querySelector('div[data-tab="terminal"]')
if (!tabContent) {
// Tile config newly reopened inject tab content
const newTabContent = document.createElement("div")
if (a.classList.contains("active")) {
newTabContent.classList.add("active")
}
newTabContent.classList.add("tab")
newTabContent.setAttribute("data-tab", "terminal")
newTabContent.setAttribute("data-group", "sheet")
newTabContent.style.gap = ".2em"
newTabContent.innerHTML = template
html.querySelector(".window-content").insertBefore(newTabContent, html.querySelector("footer"))
} else {
// replace the entire previous tab content
tabContent.innerHTML = template
}
html.querySelector("#terminal-style")?.addEventListener("click", () => new Style().render(true));
html.querySelector("#terminal-autostyle")?.addEventListener("click", () => replaceImageHelper(doc, app.id));
html.querySelector("#terminal-check-btn")?.addEventListener("click", () => new Check(doc).render(true));
html.querySelector("#terminal-rename-btn")?.addEventListener("click", () => new Rename(doc).render(true));
html.querySelector("#terminal-feedback-btn")?.addEventListener("click", () => new Feedback().render(true));
html.querySelector("#terminal-regions-btn")?.addEventListener("click", () => new Regions(doc).render(true))
html.querySelectorAll(".terminal-rm-door-btn").forEach(el => el.addEventListener("click", e => {
const ids = doc.getFlag(ID, "unlockIds")?.split(",")?.filter(d =>
d !== e.target.attributes.uuid.value
)
setFlagAndTab(doc, "unlockIds", ids.join(","))
}))
// link buttons
html.querySelectorAll(".terminal-link").forEach(el => el.addEventListener("click", e => {
const type = e.target.attributes.link.value
canvas.tiles.releaseAll()
const name = e.target.attributes.pretty?.value || type.toUpperCase()
ui.notifications.info(`🖱️ Click on a ${name} to connect to this terminal. Close the tile config to cancel`)
Object.values(ui.windows).forEach(app => app.minimize())
foundry.applications.instances.forEach(a => a.minimize())
if (type === "journal" || type === "actors" || type === "items" || type === "macros") {
ui.sidebar.expand()
ui.sidebar.changeTab(type, "primary")
} else { // tiles, walls
canvas[type].activate()
}
game.settings.set(ID, "pointer", doc.uuid)
}))
// delete buttons
html.querySelectorAll(".terminal-delete").forEach(el => el.addEventListener("click", e => {
setFlagAndTab(doc, e.target.id.split(".")[2], "")
}))
// view buttons
html.querySelectorAll(".terminal-view").forEach(el => el.addEventListener("click", async e => {
const doc = await fromUuid(e.target.attributes.uuid.value)
if (!doc) return
if (!doc.parent.isView) {
ui.notifications.info(`Cannot view ${doc.parentCollection} on separate scene "${doc.parent.name}"`)
return
}
canvas.ping(doc.object.center)
canvas[doc.parentCollection].activate()
canvas.animatePan({ ...doc.object.center, duration: 500 })
}))
// make data save instant for checkboxes
html.querySelectorAll("div[data-tab='terminal'] input[type='checkbox']").forEach(el =>
el.addEventListener("click", e => setFlagAndTab(doc, e.target.name.split(".")[2], e.target.checked))
)
// use a dialog, to never leave unsaved changes
html.querySelectorAll(".terminal-use-dialog").forEach(el => el.addEventListener("click", e => {
openDialog(
doc,
e.target.name.split(".")[2],
e.target.attributes.readable.value,
e.target.type,
)
}))
// make data save instant for selects
html.querySelector("div[data-tab='terminal'] select")?.addEventListener("input", e => {
setFlagAndTab(doc, "style", e.target.value)
})
// fix issue of there being a scrollbar
if (html.length)
html[0].style.height = "auto"
})
@CodaBool
Copy link
Author

CodaBool commented Oct 18, 2024

Here is the stable V12 Hook that users install currently:

Hooks.on("renderTileConfig", async (app, html, data) => {
  const styleExists = Object.keys(game.settings.get(ID, "styles")).includes(
    app.object.getFlag(ID, "style"),
  )
  const itemPrettyName = game.items.get(app.object.getFlag(ID, "keycard")?.split("@")[0])?.name
  let macroPrettyName = game.macros.get(app.object.getFlag(ID, "macro"))?.name
  let actorPrettyName = game.actors.get(app.object.getFlag(ID, "observeActor"))?.name
  let journalPrettyName = game.journal.get(app.object.getFlag(ID, "journal"))?.name

  if (app.object.getFlag(ID, "journal") && !journalPrettyName)
    journalPrettyName = "journal has been deleted!"
  if (app.object.getFlag(ID, "observeActor") && !actorPrettyName)
    actorPrettyName = "actor has been deleted!"
  if (app.object.getFlag(ID, "macro") && !macroPrettyName)
    macroPrettyName = "macro has been deleted!"

  if (!styleExists && data.data._id) {
    const preset = stylePresets.get(game.system.id)
    app.object.setFlag(ID, "style", preset || "generic-green")
  }
  const stylesUnsorted = game.settings.get(ID, "styles")
  const styles = Object.values(stylesUnsorted).sort((a, b) => {
    return (defaultStyles[a.uuid] ? 1 : 0) - (defaultStyles[b.uuid] ? 1 : 0)
  })
  let template = await renderTemplate(`modules/${ID}/templates/config.hbs`, {
    styles,
    ...app.object.flags.terminal,
    system: game.system.id,
    hasMonk: game.modules.get("monks-active-tiles")?.active,
    hasPuzzleLocks: game.modules.get("puzzle-locks")?.active,
    journalPrettyName,
    actorPrettyName,
    macroPrettyName,
    itemPrettyName,
    styleExists,
    doorLength: app.object.getFlag(ID, "unlockIds")?.length && app.object.getFlag(ID, "unlockIds")?.split(",")?.length || 0,
    doorIds: app.object.getFlag(ID, "unlockIds")?.length ? app.object.getFlag(ID, "unlockIds")?.split(",") : [],
  })

  if (!data.data._id) {
    template = `<div class='tab' data-tab='${ID}'><p style="color:grey">You must "Create Tile" before editing Terminal settings</p></div>`
  }

  $(`#${app.id}`).find(".tabs .item").eq(2).after(`
    <a class="item" data-tab="${ID}"><i class="fa-solid fa-computer"></i>Terminal</a>
  `)

  $(`#${app.id}`).find(".tab").eq(2).after(template)

  // listeners
  html.find("#terminal-style")
    .on("click", () => new Style().render(true))
  html.find("#terminal-autostyle")
    .on("click", () => replaceImageHelper(data, app.id))
  html.find("#terminal-check-btn")
    .on("click", () => new Check(data).render(true))
  html.find("#terminal-rename-btn")
    .on("click", () => new Rename(data).render(true))
  html.find("#terminal-feedback-btn")
    .on("click", () => new Feedback().render(true))
  html.find("#terminal-regions-btn")
    .on("click", () => new Regions(data).render(true))
  html.find(".terminal-rm-door-btn").on("click", e => {
    const ids = app.object.getFlag(ID, "unlockIds")?.split(",")?.filter(d =>
      d !== e.target.attributes.uuid.value
    )
    setFlagAndTab(app.object, "unlockIds", ids.join(","))
  })

  // link buttons
  html.find("div[data-tab='terminal'] .terminal-link").on("click", e => {
    const type = e.target.attributes.link.value
    canvas.tiles.releaseAll()
    const name = e.target.attributes.pretty?.value || type.toUpperCase()
    ui.notifications.info(`🖱️ Click on a ${name} to connect to this terminal. Close the tile config to cancel`)
    Object.values(ui.windows).forEach(app => app.minimize())
    foundry.applications.instances.forEach(a => a.minimize())
    if (type === "macro") {
      ui.macros.render(true)
    } else if (type === "journal" || type === "actors" || type === "items") {
      ui.sidebar.tabs[type].activate()
    } else { // tiles, walls
      canvas[ui.controls.controls.find(t => t.name == type).layer].activate()
    }
    game.settings.set(ID, "pointer", data.document.uuid)
  })

  // delete buttons
  html.find("div[data-tab='terminal'] .terminal-delete").on("click", e => {
    setFlagAndTab(app.object, e.target.id.split(".")[2], "")
  })

  // view buttons
  html.find("div[data-tab='terminal'] .terminal-view").on("click", async e => {
    const doc = await fromUuid(e.target.attributes.uuid.value)
    if (!doc) return
    if (!doc.parent.isView) {
      ui.notifications.info(`Cannot view ${doc.parentCollection} on separate scene "${doc.parent.name}"`)
      return
    }
    canvas.ping(doc.object.center)
    canvas[ui.controls.controls.find(t => t.name == doc.parentCollection).layer].activate()
    canvas.animatePan({ ...doc.object.center, duration: 500 })
  })

  // make data save instant for checkboxes
  html.find("div[data-tab='terminal'] input[type='checkbox']").on("click", e => {
    setFlagAndTab(app.object, e.target.name.split(".")[2], e.target.checked)
  })

  // use a dialog, to never leave unsaved changes
  html.find(".terminal-use-dialog").on("click", e => {
    openDialog(
      app.object,
      e.target.name.split(".")[2],
      e.target.attributes.readable.value,
      e.target.type,
    )
  })

  // make data save instant for selects
  html.find("div[data-tab='terminal'] select").on("input", e => {
    setFlagAndTab(app.object, "style", e.target.value)
  })

  // fix issue of there being a scrollbar
  if (html.length)
    html[0].style.height = "auto"
})

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment