card-game-generator
Version:
A card game development toolkit
151 lines (126 loc) • 4.49 kB
text/coffeescript
mkdirp = require "mkdirp"
fs = require "fs"
path = require "path"
puppeteer = require "puppeteer"
connect = require "connect"
serve_static = require "serve-static"
portfinder = require "portfinder"
create_save = require "./create-save"
ts_folder = process.env.TABLETOP_SIMULATOR_FOLDER or "#{process.env.USERPROFILE}/Documents/My Games/Tabletop Simulator"
get_css = ({scale})-> """
body {
zoom: #{scale};
text-align: left;
overflow: hidden;
}
body > *:not(.cards),
section > h1,
section > h2,
section > h3,
section > h4,
section > h5,
section > h6,
.cards > h1,
.cards > h2,
.cards > h3,
.cards > h4,
.cards > h5,
.cards > h6 {
display: none;
}
.card {
margin: 0;
}
"""
runTasksInSerial = (tasks)->
for task in tasks
await task()
runTasksInParallel = (tasks)->
Promise.all(task() for task in tasks)
startWebServer = (folder, port)->
new Promise (resolve, reject)->
app = connect()
.use(serve_static(folder))
.listen(port, ->
# console.log "Server running on #{port}..."
stopWebServer = ->
app.close()
resolve(stopWebServer)
)
module.exports =
class CardGameGenerator
constructor: ({@cardSets, @counters})->
@cardSets ?= {}
@counters ?= {}
renderCards: ({page, to, scale, debug}, callback)->
scale ?= 1
css = get_css({scale})
to = path.resolve(to)
page = path.resolve(page)
set_names = ["Back"].concat(Object.keys(@cardSets))
parallel = process.env.PARALLEL_EXPORT in ["on", "ON", "true", "TRUE", "yes", "YES", "1"]
(do ->
port = await portfinder.getPortPromise()
stopWebServer = await startWebServer(path.dirname(page), port)
browser = await puppeteer.launch({devtools: debug})
render_card_set = (set_name)->
n_cols = if set_name is "Back" then 1 else 10
n_rows = if set_name is "Back" then 1 else 7
await mkdirp(to)
pup_page = await browser.newPage()
await pup_page.goto("http://127.0.0.1:#{port}##{set_name}", waitUntil: 'networkidle0')
await pup_page.setViewport({width: 20000, height: 20000})
{cardWidth, cardHeight} = await pup_page.evaluate (css)->
style = document.createElement("style")
style.type = "text/css"
style.appendChild(document.createTextNode(css))
document.head.appendChild(style)
cardEl = document.querySelector(".card")
cardWidth = cardEl.offsetWidth
cardHeight = cardEl.offsetHeight
{cardWidth, cardHeight}
, css
width = cardWidth * n_cols * scale
height = cardHeight * n_rows * scale
# console.log({cardWidth, cardHeight, n_cols, n_rows, scale, width, height})
# TODO: is deviceScaleFactor better than CSS zoom?
await pup_page.setViewport({width, height})
await pup_page.screenshot({path: path.join(to, "#{set_name}.png")})
await pup_page.close()
tasks = set_names.map((set_name)->
-> render_card_set(set_name)
)
# TODO: think about early-failure for task running
runTasks = if parallel then runTasksInParallel else runTasksInSerial
await runTasks(tasks)
await browser.close()
stopWebServer()
).then(
(result)-> callback(null, result)
(error)-> callback(error)
)
exportTabletopSimulatorSave: ({to, saveName, imagesURL, renderedImagesURL}, callback)->
to = path.resolve(to)
mkdirp(to).then(
=>
save = create_save({@cardSets, @counters, imagesURL, renderedImagesURL})
@ts_save_json = JSON.stringify(save, null, 2)
@ts_save_filename = "#{saveName}.json"
fs.writeFile "#{to}/#{@ts_save_filename}", @ts_save_json, "utf8", callback
(err)=> callback(err)
)
exportSaveToTabletopSimulatorChest: ->
unless @ts_save_json
throw new Error "You must call exportTabletopSimulatorSave first"
if not fs.existsSync(ts_folder)
if not process.env.USERPROFILE and not process.env.TABLETOP_SIMULATOR_FOLDER
throw new Error "If you have Tabletop Simulator, please specify the Tabletop Simulator folder with an environment variable TABLETOP_SIMULATOR_FOLDER"
else
throw new Error "The Tabletop Simulator folder doesn't exist at '#{ts_folder}' - specify it with an environment variable TABLETOP_SIMULATOR_FOLDER (if you have Tabletop Simulator)"
chest_folder = "#{ts_folder}/Saves/Chest"
fs.writeFileSync("#{chest_folder}/#{@ts_save_filename}", @ts_save_json, "utf8")
@clearTabletopSimulatorCache()
clearTabletopSimulatorCache: ->
cache_folder = "#{ts_folder}/Mods/Images"
for fname in fs.readdirSync cache_folder
fs.unlinkSync("#{cache_folder}/#{fname}")