Refactor to be more object-oriented, move to separate modules
This commit is contained in:
parent
31a367e5fa
commit
25a029e69b
@ -10,4 +10,4 @@ catalog-homepage = https://github.com/ad1217/SE3-TTSDeck
|
|||||||
catalog-description = This plugin for allows you to generate images and \
|
catalog-description = This plugin for allows you to generate images and \
|
||||||
corresponding "Saved Object" JSONs for decks which can be used in Tabletop Simulator.
|
corresponding "Saved Object" JSONs for decks which can be used in Tabletop Simulator.
|
||||||
|
|
||||||
res://ttsdeck/TTSDeck.js
|
res://ttsdeck/plugin.js
|
||||||
|
96
resources/ttsdeck/Card.js
Normal file
96
resources/ttsdeck/Card.js
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
useLibrary("project");
|
||||||
|
|
||||||
|
function Card(member, copies_list) {
|
||||||
|
this.member = member;
|
||||||
|
this.copies_list = copies_list;
|
||||||
|
|
||||||
|
this.component = ResourceKit.getGameComponentFromFile(member.file);
|
||||||
|
}
|
||||||
|
|
||||||
|
Card.getImageFile = function getImageFile(parent, format, page_num) {
|
||||||
|
return new File(
|
||||||
|
parent.file,
|
||||||
|
parent.getName() + "_" + page_num + "." + format
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
Card.prototype.makeImageUncached = function makeImageUncached(
|
||||||
|
resolution,
|
||||||
|
back
|
||||||
|
) {
|
||||||
|
println("Generating image for card ", this.member);
|
||||||
|
const sheets = this.component.createDefaultSheets();
|
||||||
|
const card_image = sheets[back ? 1 : 0].paint(
|
||||||
|
arkham.sheet.RenderTarget.EXPORT,
|
||||||
|
resolution
|
||||||
|
);
|
||||||
|
|
||||||
|
return card_image;
|
||||||
|
};
|
||||||
|
|
||||||
|
// export front face, or retrive it from a cached file
|
||||||
|
// TODO: handle two-sided cards
|
||||||
|
Card.prototype.makeImage = function makeImage(format, resolution) {
|
||||||
|
const cache_dir = new File(this.member.parent.file, ".ttsdeck_cache");
|
||||||
|
const cached_file = new File(cache_dir, this.member.file.name + "." + format);
|
||||||
|
|
||||||
|
if (
|
||||||
|
cached_file.exists() &&
|
||||||
|
cached_file.lastModified() > this.member.file.lastModified()
|
||||||
|
) {
|
||||||
|
println("Got cached image for card", this.member);
|
||||||
|
return ImageUtils.read(cached_file);
|
||||||
|
} else {
|
||||||
|
const card_image = this.makeImageUncached(resolution);
|
||||||
|
|
||||||
|
cache_dir.mkdir();
|
||||||
|
ImageUtils.write(card_image, cached_file, format, -1, false, resolution);
|
||||||
|
|
||||||
|
return card_image;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: allow setting a default copy count
|
||||||
|
// Hack to override the default return value of 1
|
||||||
|
Card.prototype.copyCount = function copyCount() {
|
||||||
|
const entries = this.copies_list.getListEntries().map((x) => String(x));
|
||||||
|
if (entries.indexOf(String(this.member.baseName)) == -1) {
|
||||||
|
return 1;
|
||||||
|
} else {
|
||||||
|
return this.copies_list.getCopyCount(this.member.baseName);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Card.prototype.makeJSON = function makeJSON(card_id, description) {
|
||||||
|
return {
|
||||||
|
Name: "Card",
|
||||||
|
Transform: {
|
||||||
|
posX: 0,
|
||||||
|
posY: 0,
|
||||||
|
posZ: 0,
|
||||||
|
rotX: 0,
|
||||||
|
rotY: 0,
|
||||||
|
rotZ: 0,
|
||||||
|
scaleX: 1.0,
|
||||||
|
scaleY: 1.0,
|
||||||
|
scaleZ: 1.0,
|
||||||
|
},
|
||||||
|
Nickname: String(this.component.getName()),
|
||||||
|
CardID: card_id,
|
||||||
|
Description: String(description || ""),
|
||||||
|
ColorDiffuse: {
|
||||||
|
r: 0.713235259,
|
||||||
|
g: 0.713235259,
|
||||||
|
b: 0.713235259,
|
||||||
|
},
|
||||||
|
Locked: false,
|
||||||
|
Grid: true,
|
||||||
|
Snap: true,
|
||||||
|
Autoraise: true,
|
||||||
|
Sticky: true,
|
||||||
|
Tooltip: true,
|
||||||
|
SidewaysCard: false,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = Card;
|
@ -1,132 +1,61 @@
|
|||||||
/*
|
|
||||||
* TTSDeck.js
|
|
||||||
*
|
|
||||||
* Creates a deck image and corresponding "Saved Object" JSON for use
|
|
||||||
* in Tabletop Simulator
|
|
||||||
*/
|
|
||||||
|
|
||||||
useLibrary("project");
|
|
||||||
useLibrary("imageutils");
|
|
||||||
useLibrary("threads");
|
useLibrary("threads");
|
||||||
useLibrary("uilayout");
|
|
||||||
useLibrary("uicontrols");
|
|
||||||
importClass(arkham.project.CopiesList);
|
|
||||||
|
|
||||||
const TTSJson = require("./TTSJson.js");
|
const Card = require("./Card.js");
|
||||||
|
|
||||||
const TTS_CARDS_PER_IMAGE = 69;
|
const TTS_CARDS_PER_IMAGE = 69;
|
||||||
const TTS_MAX_ROWS = 7;
|
const TTS_MAX_ROWS = 7;
|
||||||
|
|
||||||
const getName = () => "TTSDeck";
|
function TTSDeckPage(image_format, image_resolution, page_num, cards) {
|
||||||
const getDescription = () => "Generates a TTS deck image and JSON file";
|
this.image_format = image_format;
|
||||||
const getVersion = () => 1.0;
|
this.image_resolution = image_resolution;
|
||||||
const getPluginType = () => arkham.plugins.Plugin.INJECTED;
|
this.page_num = page_num;
|
||||||
|
this.cards = cards;
|
||||||
|
|
||||||
function unload() {
|
this.rows = Math.min(Math.ceil(Math.sqrt(cards.length)), TTS_MAX_ROWS);
|
||||||
unregisterAll();
|
this.columns = Math.ceil(cards.length / this.rows);
|
||||||
}
|
this.deck_image = null;
|
||||||
|
this.card_jsons = [];
|
||||||
|
|
||||||
// Creates a test button during development that calls unload() to clean up.
|
this.face_url = String(
|
||||||
testProjectScript();
|
Card.getImageFile(cards[0].member.parent, image_format, this.page_num)
|
||||||
|
.toPath()
|
||||||
// TODO: allow setting a default copy count
|
.toUri()
|
||||||
// Hack to override the default return value of 1
|
);
|
||||||
function copyCount(copies_list, name) {
|
this.back_url = String(
|
||||||
const entries = copies_list.getListEntries().map((x) => String(x));
|
Card.getImageFile(cards[0].member.parent, image_format, "back")
|
||||||
if (entries.indexOf(String(name)) == -1) {
|
.toPath()
|
||||||
return 1;
|
.toUri()
|
||||||
} else {
|
|
||||||
return copies_list.getCopyCount(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getImageFile(parent, format, page_num) {
|
|
||||||
return new File(
|
|
||||||
parent.file,
|
|
||||||
parent.getName() + "_" + page_num + "." + format
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Card(member) {
|
TTSDeckPage.prototype.build = function build(busy_props) {
|
||||||
this.member = member;
|
|
||||||
this.component = ResourceKit.getGameComponentFromFile(member.file);
|
|
||||||
|
|
||||||
this.makeImageUncached = function makeImageUncached(resolution, back) {
|
|
||||||
println("Generating image for card ", this.member);
|
|
||||||
const sheets = this.component.createDefaultSheets();
|
|
||||||
const card_image = sheets[back ? 1 : 0].paint(
|
|
||||||
arkham.sheet.RenderTarget.EXPORT,
|
|
||||||
resolution
|
|
||||||
);
|
|
||||||
|
|
||||||
return card_image;
|
|
||||||
};
|
|
||||||
|
|
||||||
// export front face, or retrive it from a cached file
|
|
||||||
// TODO: handle two-sided cards
|
|
||||||
this.makeImage = function makeImage(format, resolution) {
|
|
||||||
const cache_dir = new File(this.member.parent.file, ".ttsdeck_cache");
|
|
||||||
const cached_file = new File(
|
|
||||||
cache_dir,
|
|
||||||
this.member.file.name + "." + format
|
|
||||||
);
|
|
||||||
|
|
||||||
if (
|
|
||||||
cached_file.exists() &&
|
|
||||||
cached_file.lastModified() > this.member.file.lastModified()
|
|
||||||
) {
|
|
||||||
println("Got cached image for card", this.member);
|
|
||||||
return ImageUtils.read(cached_file);
|
|
||||||
} else {
|
|
||||||
const card_image = this.makeImageUncached(resolution);
|
|
||||||
|
|
||||||
cache_dir.mkdir();
|
|
||||||
ImageUtils.write(card_image, cached_file, format, -1, false, resolution);
|
|
||||||
|
|
||||||
return card_image;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function TTSDeckPage(
|
|
||||||
busy_props,
|
|
||||||
image_format,
|
|
||||||
image_resolution,
|
|
||||||
page_num,
|
|
||||||
page_cards,
|
|
||||||
copies_list
|
|
||||||
) {
|
|
||||||
this.rows = Math.min(Math.ceil(Math.sqrt(page_cards.length)), TTS_MAX_ROWS);
|
|
||||||
this.columns = Math.ceil(page_cards.length / this.rows);
|
|
||||||
this.deck_image = null;
|
|
||||||
let deck_graphics;
|
let deck_graphics;
|
||||||
|
|
||||||
this.card_jsons = [];
|
|
||||||
for (let row = 0; row < this.rows; row++) {
|
for (let row = 0; row < this.rows; row++) {
|
||||||
for (
|
for (
|
||||||
let col = 0;
|
let col = 0;
|
||||||
col < this.columns && row * this.columns + col < page_cards.length;
|
col < this.columns && row * this.columns + col < this.cards.length;
|
||||||
col++
|
col++
|
||||||
) {
|
) {
|
||||||
if (busy_props.cancelled) return;
|
if (busy_props.cancelled) return this;
|
||||||
|
|
||||||
let index = row * this.columns + col;
|
let index = row * this.columns + col;
|
||||||
let card = page_cards[index];
|
let card = this.cards[index];
|
||||||
busy_props.status = "Processing Card " + card.member;
|
busy_props.status = "Processing Card " + card.member;
|
||||||
busy_props.currentProgress = (page_num - 1) * TTS_CARDS_PER_IMAGE + index;
|
busy_props.currentProgress =
|
||||||
|
(this.page_num - 1) * TTS_CARDS_PER_IMAGE + index;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let copies = copyCount(copies_list, card.baseName);
|
let copies = card.copyCount();
|
||||||
|
|
||||||
for (let ii = 0; ii < copies; ii++) {
|
for (let ii = 0; ii < copies; ii++) {
|
||||||
this.card_jsons.push(
|
this.card_jsons.push(card.makeJSON(this.page_num * 100 + index));
|
||||||
TTSJson.makeCardJSON(
|
|
||||||
page_num * 100 + index,
|
|
||||||
card.component.getName()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let card_image = card.makeImage(image_format, image_resolution);
|
let card_image = card.makeImage(
|
||||||
|
this.image_format,
|
||||||
|
this.image_resolution
|
||||||
|
);
|
||||||
|
|
||||||
if (!this.deck_image) {
|
if (!this.deck_image) {
|
||||||
this.deck_image = ImageUtils.create(
|
this.deck_image = ImageUtils.create(
|
||||||
@ -152,225 +81,94 @@ function TTSDeckPage(
|
|||||||
println("End of Row ", row);
|
println("End of Row ", row);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.face_url = String(
|
return this;
|
||||||
getImageFile(page_cards[0].member.parent, image_format, page_num)
|
};
|
||||||
.toPath()
|
|
||||||
.toUri()
|
function TTSDeck(image_format, image_resolution, cards) {
|
||||||
);
|
this.image_format = image_format;
|
||||||
this.back_url = String(
|
this.image_resolution = image_resolution;
|
||||||
getImageFile(page_cards[0].member.parent, image_format, "back")
|
this.cards = cards;
|
||||||
.toPath()
|
|
||||||
.toUri()
|
this.pages = [];
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function makeTTSDeck(
|
TTSDeck.prototype.build = function build(busy_props) {
|
||||||
busy_props,
|
|
||||||
image_format,
|
|
||||||
image_resolution,
|
|
||||||
cards,
|
|
||||||
copies_list
|
|
||||||
) {
|
|
||||||
const pages = [];
|
|
||||||
|
|
||||||
busy_props.title = "Processing Cards";
|
busy_props.title = "Processing Cards";
|
||||||
busy_props.maximumProgress = cards.length;
|
busy_props.maximumProgress = this.cards.length;
|
||||||
|
|
||||||
for (
|
for (
|
||||||
let page_num = 0;
|
let page_num = 0;
|
||||||
page_num * TTS_CARDS_PER_IMAGE < cards.length;
|
page_num * TTS_CARDS_PER_IMAGE < this.cards.length;
|
||||||
page_num++
|
page_num++
|
||||||
) {
|
) {
|
||||||
let page_cards = cards.slice(
|
if (busy_props.cancelled) return this;
|
||||||
|
|
||||||
|
let page_cards = this.cards.slice(
|
||||||
page_num * TTS_CARDS_PER_IMAGE,
|
page_num * TTS_CARDS_PER_IMAGE,
|
||||||
(page_num + 1) * TTS_CARDS_PER_IMAGE
|
(page_num + 1) * TTS_CARDS_PER_IMAGE
|
||||||
);
|
);
|
||||||
printf("Making page %d, with %d cards:\n", page_num + 1, page_cards.length);
|
printf("Making page %d, with %d cards:\n", page_num + 1, page_cards.length);
|
||||||
pages.push(
|
this.pages.push(
|
||||||
new TTSDeckPage(
|
new TTSDeckPage(
|
||||||
busy_props,
|
this.image_format,
|
||||||
image_format,
|
this.image_resolution,
|
||||||
image_resolution,
|
|
||||||
page_num + 1,
|
page_num + 1,
|
||||||
page_cards,
|
page_cards
|
||||||
copies_list
|
).build(busy_props)
|
||||||
)
|
|
||||||
);
|
);
|
||||||
if (busy_props.cancelled) return [,];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const deck_json = TTSJson.makeDeckJSON(pages);
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
return [deck_json, pages.map((page) => page.deck_image)];
|
TTSDeck.prototype.getImages = function getImages() {
|
||||||
}
|
return this.pages.map((page) => page.deck_image);
|
||||||
|
};
|
||||||
|
|
||||||
function settingsDialog(deck_task) {
|
TTSDeck.prototype.makeJSON = function makeJSON(nickname, description) {
|
||||||
const task_settings = deck_task.getSettings();
|
return {
|
||||||
|
Name: "DeckCustom",
|
||||||
const image_format_field = comboBox([
|
Transform: {
|
||||||
ImageUtils.FORMAT_JPEG,
|
posX: 0,
|
||||||
ImageUtils.FORMAT_PNG,
|
posY: 0,
|
||||||
]);
|
posZ: 0,
|
||||||
image_format_field.setSelectedItem(
|
rotX: 0,
|
||||||
task_settings.get("tts_image_format", "jpg")
|
rotY: 0.0,
|
||||||
);
|
rotZ: 0.0,
|
||||||
const resolution_field = textField(
|
scaleX: 1.0,
|
||||||
task_settings.get("tts_image_resolution", "200"),
|
scaleY: 1.0,
|
||||||
15
|
scaleZ: 1.0,
|
||||||
);
|
|
||||||
|
|
||||||
const clear_cache_button = button("Clear Cache", undefined, function (e) {
|
|
||||||
const cache_dir = new File(deck_task.file, ".ttsdeck_cache");
|
|
||||||
cache_dir.listFiles().forEach((file) => file.delete());
|
|
||||||
});
|
|
||||||
|
|
||||||
const panel = new Grid();
|
|
||||||
// prettier-ignore
|
|
||||||
panel.place(
|
|
||||||
"Image Format", "",
|
|
||||||
image_format_field, "grow,span",
|
|
||||||
"Resolution", "",
|
|
||||||
resolution_field, "grow,span",
|
|
||||||
clear_cache_button, "grow,span"
|
|
||||||
);
|
|
||||||
const close_button = panel.createDialog("TTS Export").showDialog();
|
|
||||||
return [
|
|
||||||
close_button,
|
|
||||||
image_format_field.getSelectedItem(),
|
|
||||||
Number(resolution_field.text),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
function run() {
|
|
||||||
const ttsDeckAction = JavaAdapter(TaskAction, {
|
|
||||||
getLabel: () => "Generate TTS Deck",
|
|
||||||
getActionName: () => "ttsdeck",
|
|
||||||
// Applies to Deck Tasks
|
|
||||||
appliesTo: function appliesTo(project, task, member) {
|
|
||||||
if (member != null || task == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const type = task.settings.get(Task.KEY_TYPE);
|
|
||||||
if (NewTaskType.DECK_TYPE.equals(type)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
},
|
||||||
perform: function perform(project, task, member) {
|
Nickname: String(nickname || ""),
|
||||||
let deck_task = ProjectUtilities.simplify(project, task, member);
|
Description: String(description || ""),
|
||||||
const [close_button, image_format, image_resolution] =
|
ColorDiffuse: {
|
||||||
settingsDialog(deck_task);
|
r: 0.713239133,
|
||||||
|
g: 0.713239133,
|
||||||
// User canceled the dialog or closed it without pressing ok
|
b: 0.713239133,
|
||||||
if (close_button != 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// persist settings
|
|
||||||
const task_settings = deck_task.getSettings();
|
|
||||||
task_settings.set("tts_image_format", image_format);
|
|
||||||
task_settings.set("tts_image_resolution", image_resolution);
|
|
||||||
deck_task.writeTaskSettings();
|
|
||||||
|
|
||||||
Eons.setWaitCursor(true);
|
|
||||||
try {
|
|
||||||
Thread.busyWindow(
|
|
||||||
(busy_props) =>
|
|
||||||
this.performImpl(
|
|
||||||
busy_props,
|
|
||||||
image_format,
|
|
||||||
image_resolution,
|
|
||||||
deck_task
|
|
||||||
),
|
|
||||||
"Setting up...",
|
|
||||||
true
|
|
||||||
);
|
|
||||||
} catch (ex) {
|
|
||||||
Error.handleUncaught(ex);
|
|
||||||
} finally {
|
|
||||||
Eons.setWaitCursor(false);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
performImpl: function performImpl(
|
Grid: true,
|
||||||
busy_props,
|
Locked: false,
|
||||||
image_format,
|
SidewaysCard: false,
|
||||||
image_resolution,
|
DeckIDs: this.pages
|
||||||
member
|
.map((page) => page.card_jsons.map((card) => card.CardID))
|
||||||
) {
|
.reduce((acc, val) => acc.concat(val), []),
|
||||||
let copies_list;
|
CustomDeck: this.pages.reduce((acc, page, index) => {
|
||||||
try {
|
acc[String(index + 1)] = {
|
||||||
copies_list = new CopiesList(member);
|
FaceURL: String(page.face_url),
|
||||||
} catch (ex) {
|
BackURL: String(page.back_url),
|
||||||
copies_list = new CopiesList();
|
NumWidth: page.columns,
|
||||||
alert(
|
NumHeight: page.rows,
|
||||||
"unable to read copies list, using card count of 2 for all files",
|
BackIsHidden: true,
|
||||||
true
|
};
|
||||||
);
|
return acc;
|
||||||
}
|
}, {}),
|
||||||
|
ContainedObjects: this.pages
|
||||||
|
.map((page) => page.card_jsons)
|
||||||
|
.reduce((acc, val) => acc.concat(val), []),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const children = member.getChildren();
|
module.exports = {
|
||||||
const cards = children
|
TTSDeckPage: TTSDeckPage,
|
||||||
.map((child) => {
|
TTSDeck: TTSDeck,
|
||||||
if (ProjectUtilities.matchExtension(child, "eon")) {
|
};
|
||||||
let card = new Card(child);
|
|
||||||
if (card.component.isDeckLayoutSupported()) {
|
|
||||||
return card;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
})
|
|
||||||
.filter((card) => card !== undefined);
|
|
||||||
|
|
||||||
const [deck_json, deck_images] = makeTTSDeck(
|
|
||||||
busy_props,
|
|
||||||
image_format,
|
|
||||||
image_resolution,
|
|
||||||
cards,
|
|
||||||
copies_list
|
|
||||||
);
|
|
||||||
if (busy_props.cancelled) return;
|
|
||||||
const saved_object = TTSJson.makeSavedObjectJSON(
|
|
||||||
[deck_json],
|
|
||||||
member.getName()
|
|
||||||
);
|
|
||||||
|
|
||||||
busy_props.status = "";
|
|
||||||
busy_props.maximumProgress = -1;
|
|
||||||
busy_props.title = "Writing JSON";
|
|
||||||
const json_file = new File(member.file, member.getName() + ".json");
|
|
||||||
ProjectUtilities.writeTextFile(
|
|
||||||
json_file,
|
|
||||||
JSON.stringify(saved_object, null, 4)
|
|
||||||
);
|
|
||||||
|
|
||||||
busy_props.title = "Writing Images";
|
|
||||||
busy_props.maximumProgress = deck_images.length;
|
|
||||||
deck_images.forEach((deck_image, index) => {
|
|
||||||
busy_props.currentProgress = index;
|
|
||||||
const image_file = getImageFile(member, image_format, index + 1);
|
|
||||||
ImageUtils.write(
|
|
||||||
deck_image,
|
|
||||||
image_file,
|
|
||||||
image_format,
|
|
||||||
-1,
|
|
||||||
false,
|
|
||||||
image_resolution
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
let back_image = cards[0].makeImageUncached(image_resolution, true);
|
|
||||||
const back_image_file = getImageFile(member, image_format, "back");
|
|
||||||
ImageUtils.write(
|
|
||||||
back_image,
|
|
||||||
back_image_file,
|
|
||||||
image_format,
|
|
||||||
-1,
|
|
||||||
false,
|
|
||||||
image_resolution
|
|
||||||
);
|
|
||||||
|
|
||||||
member.synchronize();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
ActionRegistry.register(ttsDeckAction, Actions.PRIORITY_IMPORT_EXPORT);
|
|
||||||
}
|
|
||||||
|
@ -1,80 +1,5 @@
|
|||||||
// Helper functions for Tabletop Simulator JSON output
|
// Helper functions for Tabletop Simulator JSON output
|
||||||
|
|
||||||
exports.makeCardJSON = function makeCardJSON(card_id, nickname, description) {
|
|
||||||
return {
|
|
||||||
Name: "Card",
|
|
||||||
Transform: {
|
|
||||||
posX: 0,
|
|
||||||
posY: 0,
|
|
||||||
posZ: 0,
|
|
||||||
rotX: 0,
|
|
||||||
rotY: 0,
|
|
||||||
rotZ: 0,
|
|
||||||
scaleX: 1.0,
|
|
||||||
scaleY: 1.0,
|
|
||||||
scaleZ: 1.0,
|
|
||||||
},
|
|
||||||
Nickname: String(nickname),
|
|
||||||
CardID: card_id,
|
|
||||||
Description: String(description || ""),
|
|
||||||
ColorDiffuse: {
|
|
||||||
r: 0.713235259,
|
|
||||||
g: 0.713235259,
|
|
||||||
b: 0.713235259,
|
|
||||||
},
|
|
||||||
Locked: false,
|
|
||||||
Grid: true,
|
|
||||||
Snap: true,
|
|
||||||
Autoraise: true,
|
|
||||||
Sticky: true,
|
|
||||||
Tooltip: true,
|
|
||||||
SidewaysCard: false,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.makeDeckJSON = function makeDeckJSON(pages, nickname, description) {
|
|
||||||
return {
|
|
||||||
Name: "DeckCustom",
|
|
||||||
Transform: {
|
|
||||||
posX: 0,
|
|
||||||
posY: 0,
|
|
||||||
posZ: 0,
|
|
||||||
rotX: 0,
|
|
||||||
rotY: 0.0,
|
|
||||||
rotZ: 0.0,
|
|
||||||
scaleX: 1.0,
|
|
||||||
scaleY: 1.0,
|
|
||||||
scaleZ: 1.0,
|
|
||||||
},
|
|
||||||
Nickname: String(nickname || ""),
|
|
||||||
Description: String(description || ""),
|
|
||||||
ColorDiffuse: {
|
|
||||||
r: 0.713239133,
|
|
||||||
g: 0.713239133,
|
|
||||||
b: 0.713239133,
|
|
||||||
},
|
|
||||||
Grid: true,
|
|
||||||
Locked: false,
|
|
||||||
SidewaysCard: false,
|
|
||||||
DeckIDs: pages
|
|
||||||
.map((page) => page.card_jsons.map((card) => card.CardID))
|
|
||||||
.reduce((acc, val) => acc.concat(val), []),
|
|
||||||
CustomDeck: pages.reduce((acc, page, index) => {
|
|
||||||
acc[String(index + 1)] = {
|
|
||||||
FaceURL: String(page.face_url),
|
|
||||||
BackURL: String(page.back_url),
|
|
||||||
NumWidth: page.columns,
|
|
||||||
NumHeight: page.rows,
|
|
||||||
BackIsHidden: true,
|
|
||||||
};
|
|
||||||
return acc;
|
|
||||||
}, {}),
|
|
||||||
ContainedObjects: pages
|
|
||||||
.map((page) => page.card_jsons)
|
|
||||||
.reduce((acc, val) => acc.concat(val), []),
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.makeSavedObjectJSON = function makeSavedObjectJSON(objects, save_name) {
|
exports.makeSavedObjectJSON = function makeSavedObjectJSON(objects, save_name) {
|
||||||
return {
|
return {
|
||||||
SaveName: String(save_name || ""),
|
SaveName: String(save_name || ""),
|
||||||
|
202
resources/ttsdeck/plugin.js
Normal file
202
resources/ttsdeck/plugin.js
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
/*
|
||||||
|
* TTSDeck.js
|
||||||
|
*
|
||||||
|
* Creates a deck image and corresponding "Saved Object" JSON for use
|
||||||
|
* in Tabletop Simulator
|
||||||
|
*/
|
||||||
|
|
||||||
|
useLibrary("project");
|
||||||
|
useLibrary("imageutils");
|
||||||
|
useLibrary("threads");
|
||||||
|
useLibrary("uilayout");
|
||||||
|
useLibrary("uicontrols");
|
||||||
|
importClass(arkham.project.CopiesList);
|
||||||
|
|
||||||
|
const Card = require("./Card.js");
|
||||||
|
const { TTSDeck } = require("./TTSDeck.js");
|
||||||
|
const TTSJson = require("./TTSJson.js");
|
||||||
|
|
||||||
|
const getName = () => "TTSDeck";
|
||||||
|
const getDescription = () => "Generates a TTS deck image and JSON file";
|
||||||
|
const getVersion = () => 1.0;
|
||||||
|
const getPluginType = () => arkham.plugins.Plugin.INJECTED;
|
||||||
|
|
||||||
|
function unload() {
|
||||||
|
unregisterAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates a test button during development that calls unload() to clean up.
|
||||||
|
testProjectScript();
|
||||||
|
|
||||||
|
function settingsDialog(deck_task) {
|
||||||
|
const task_settings = deck_task.getSettings();
|
||||||
|
|
||||||
|
const image_format_field = comboBox([
|
||||||
|
ImageUtils.FORMAT_JPEG,
|
||||||
|
ImageUtils.FORMAT_PNG,
|
||||||
|
]);
|
||||||
|
image_format_field.setSelectedItem(
|
||||||
|
task_settings.get("tts_image_format", "jpg")
|
||||||
|
);
|
||||||
|
const resolution_field = textField(
|
||||||
|
task_settings.get("tts_image_resolution", "200"),
|
||||||
|
15
|
||||||
|
);
|
||||||
|
|
||||||
|
const clear_cache_button = button("Clear Cache", undefined, function (e) {
|
||||||
|
const cache_dir = new File(deck_task.file, ".ttsdeck_cache");
|
||||||
|
cache_dir.listFiles().forEach((file) => file.delete());
|
||||||
|
});
|
||||||
|
|
||||||
|
const panel = new Grid();
|
||||||
|
// prettier-ignore
|
||||||
|
panel.place(
|
||||||
|
"Image Format", "",
|
||||||
|
image_format_field, "grow,span",
|
||||||
|
"Resolution", "",
|
||||||
|
resolution_field, "grow,span",
|
||||||
|
clear_cache_button, "grow,span"
|
||||||
|
);
|
||||||
|
const close_button = panel.createDialog("TTS Export").showDialog();
|
||||||
|
return [
|
||||||
|
close_button,
|
||||||
|
image_format_field.getSelectedItem(),
|
||||||
|
Number(resolution_field.text),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function run() {
|
||||||
|
const ttsDeckAction = JavaAdapter(TaskAction, {
|
||||||
|
getLabel: () => "Generate TTS Deck",
|
||||||
|
getActionName: () => "ttsdeck",
|
||||||
|
// Applies to Deck Tasks
|
||||||
|
appliesTo: function appliesTo(project, task, member) {
|
||||||
|
if (member != null || task == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const type = task.settings.get(Task.KEY_TYPE);
|
||||||
|
if (NewTaskType.DECK_TYPE.equals(type)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
perform: function perform(project, task, member) {
|
||||||
|
let deck_task = ProjectUtilities.simplify(project, task, member);
|
||||||
|
const [close_button, image_format, image_resolution] =
|
||||||
|
settingsDialog(deck_task);
|
||||||
|
|
||||||
|
// User canceled the dialog or closed it without pressing ok
|
||||||
|
if (close_button != 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// persist settings
|
||||||
|
const task_settings = deck_task.getSettings();
|
||||||
|
task_settings.set("tts_image_format", image_format);
|
||||||
|
task_settings.set("tts_image_resolution", image_resolution);
|
||||||
|
deck_task.writeTaskSettings();
|
||||||
|
|
||||||
|
Eons.setWaitCursor(true);
|
||||||
|
try {
|
||||||
|
Thread.busyWindow(
|
||||||
|
(busy_props) =>
|
||||||
|
this.performImpl(
|
||||||
|
busy_props,
|
||||||
|
image_format,
|
||||||
|
image_resolution,
|
||||||
|
deck_task
|
||||||
|
),
|
||||||
|
"Setting up...",
|
||||||
|
true
|
||||||
|
);
|
||||||
|
} catch (ex) {
|
||||||
|
Error.handleUncaught(ex);
|
||||||
|
} finally {
|
||||||
|
Eons.setWaitCursor(false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
performImpl: function performImpl(
|
||||||
|
busy_props,
|
||||||
|
image_format,
|
||||||
|
image_resolution,
|
||||||
|
member
|
||||||
|
) {
|
||||||
|
let copies_list;
|
||||||
|
try {
|
||||||
|
copies_list = new CopiesList(member);
|
||||||
|
} catch (ex) {
|
||||||
|
copies_list = new CopiesList();
|
||||||
|
alert(
|
||||||
|
"unable to read copies list, using card count of 2 for all files",
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const children = member.getChildren();
|
||||||
|
const cards = children
|
||||||
|
.map((child) => {
|
||||||
|
if (ProjectUtilities.matchExtension(child, "eon")) {
|
||||||
|
let card = new Card(child, copies_list);
|
||||||
|
if (card.component.isDeckLayoutSupported()) {
|
||||||
|
return card;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
})
|
||||||
|
.filter((card) => card !== undefined);
|
||||||
|
|
||||||
|
const deck = new TTSDeck(
|
||||||
|
image_format,
|
||||||
|
image_resolution,
|
||||||
|
cards,
|
||||||
|
copies_list
|
||||||
|
).build(busy_props);
|
||||||
|
|
||||||
|
if (busy_props.cancelled) return;
|
||||||
|
|
||||||
|
const saved_object = TTSJson.makeSavedObjectJSON(
|
||||||
|
[deck.makeJSON()],
|
||||||
|
member.getName()
|
||||||
|
);
|
||||||
|
|
||||||
|
busy_props.status = "";
|
||||||
|
busy_props.maximumProgress = -1;
|
||||||
|
busy_props.title = "Writing JSON";
|
||||||
|
const json_file = new File(member.file, member.getName() + ".json");
|
||||||
|
ProjectUtilities.writeTextFile(
|
||||||
|
json_file,
|
||||||
|
JSON.stringify(saved_object, null, 4)
|
||||||
|
);
|
||||||
|
|
||||||
|
const deck_images = deck.getImages();
|
||||||
|
busy_props.title = "Writing Images";
|
||||||
|
busy_props.maximumProgress = deck_images.length;
|
||||||
|
deck_images.forEach((deck_image, index) => {
|
||||||
|
busy_props.currentProgress = index;
|
||||||
|
const image_file = Card.getImageFile(member, image_format, index + 1);
|
||||||
|
ImageUtils.write(
|
||||||
|
deck_image,
|
||||||
|
image_file,
|
||||||
|
image_format,
|
||||||
|
-1,
|
||||||
|
false,
|
||||||
|
image_resolution
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
let back_image = cards[0].makeImageUncached(image_resolution, true);
|
||||||
|
const back_image_file = Card.getImageFile(member, image_format, "back");
|
||||||
|
ImageUtils.write(
|
||||||
|
back_image,
|
||||||
|
back_image_file,
|
||||||
|
image_format,
|
||||||
|
-1,
|
||||||
|
false,
|
||||||
|
image_resolution
|
||||||
|
);
|
||||||
|
|
||||||
|
member.synchronize();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
ActionRegistry.register(ttsDeckAction, Actions.PRIORITY_IMPORT_EXPORT);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user