2021-09-16 14:05:40 -04:00
|
|
|
/*
|
|
|
|
* TTSDeck.js
|
|
|
|
*
|
|
|
|
* Creates a deck image and corresponding "Saved Object" JSON for use
|
|
|
|
* in Tabletop Simulator
|
|
|
|
*/
|
|
|
|
|
|
|
|
useLibrary('project');
|
|
|
|
useLibrary('imageutils');
|
|
|
|
useLibrary('uilayout');
|
2021-09-18 01:26:15 -04:00
|
|
|
importClass(arkham.project.CopiesList);
|
2021-09-16 14:05:40 -04:00
|
|
|
|
|
|
|
// The resolution (in pixels per inch) of the exported images
|
|
|
|
const RESOLUTION = 200;
|
|
|
|
// The extension of the image file format to use, e.g., png, jpg
|
|
|
|
const FORMAT = ImageUtils.FORMAT_JPEG;
|
|
|
|
|
|
|
|
|
|
|
|
function getName() {
|
|
|
|
return 'TTSDeck';
|
|
|
|
}
|
|
|
|
function getDescription() {
|
|
|
|
return 'Generates a TTS deck image and JSON file';
|
|
|
|
}
|
|
|
|
function getVersion() {
|
|
|
|
return 1.0;
|
|
|
|
}
|
|
|
|
function getPluginType() {
|
|
|
|
return arkham.plugins.Plugin.INJECTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
function unload() {
|
|
|
|
unregisterAll();
|
|
|
|
}
|
|
|
|
|
2021-09-17 17:04:17 -04:00
|
|
|
// Creates a test button during development that calls unload() to clean up.
|
|
|
|
testProjectScript();
|
|
|
|
|
2021-09-16 16:46:15 -04:00
|
|
|
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,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
function makeDeckJSON(face_url, back_url, num_width, num_height, cards, nickname, description) {
|
|
|
|
const deck_ids = cards.map(function (card) {
|
|
|
|
return card.CardID;
|
|
|
|
});
|
|
|
|
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: deck_ids,
|
|
|
|
CustomDeck: {
|
|
|
|
"1": {
|
|
|
|
FaceURL: String(face_url),
|
|
|
|
BackURL: String(back_url),
|
|
|
|
NumWidth: num_width,
|
|
|
|
NumHeight: num_height,
|
|
|
|
}
|
|
|
|
},
|
|
|
|
ContainedObjects: cards,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
function makeSavedObjectJSON(objects, save_name) {
|
|
|
|
return {
|
|
|
|
SaveName: String(save_name || ""),
|
|
|
|
GameMode: "",
|
|
|
|
Date: "",
|
|
|
|
Table: "",
|
|
|
|
Sky: "",
|
|
|
|
Note: "",
|
|
|
|
Rules: "",
|
|
|
|
PlayerTurn: "",
|
|
|
|
ObjectStates: objects,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2021-09-18 01:26:15 -04:00
|
|
|
// Hack to override the default return value of 1
|
|
|
|
function copyCount(copies_list, name) {
|
|
|
|
const entries = copies_list.getListEntries().map(function (x) {
|
|
|
|
return String(x);
|
|
|
|
});
|
|
|
|
if (entries.indexOf(String(name)) == -1) {
|
|
|
|
return 2;
|
|
|
|
} else {
|
|
|
|
return copies_list.getCopyCount(name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-16 14:05:40 -04:00
|
|
|
function run() {
|
|
|
|
const ttsDeckAction = JavaAdapter(TaskAction, {
|
|
|
|
getLabel: function getLabel() {
|
|
|
|
return 'Generate TTS Deck';
|
|
|
|
},
|
|
|
|
getActionName: function getActionName() {
|
|
|
|
return '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) {
|
|
|
|
member = ProjectUtilities.simplify(project, task, member);
|
|
|
|
Eons.setWaitCursor(true);
|
|
|
|
try {
|
|
|
|
this.performImpl(member);
|
|
|
|
} catch (ex) {
|
|
|
|
Error.handleUncaught(ex);
|
|
|
|
} finally {
|
|
|
|
Eons.setWaitCursor(false);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
performImpl: function performImpl(member) {
|
2021-09-18 01:26:15 -04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2021-09-16 14:05:40 -04:00
|
|
|
const children = member.getChildren();
|
|
|
|
const cards = children.filter(function (child) {
|
|
|
|
if (ProjectUtilities.matchExtension(child, 'eon')) {
|
|
|
|
let component = ResourceKit.getGameComponentFromFile(child.file);
|
|
|
|
return component.isDeckLayoutSupported();
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const columns = Math.ceil(Math.sqrt(cards.length));
|
|
|
|
const rows = Math.ceil(cards.length / columns);
|
|
|
|
let deck_image;
|
2021-09-17 23:18:27 -04:00
|
|
|
let deck_graphics;
|
2021-09-16 16:46:15 -04:00
|
|
|
let card_jsons = [];
|
2021-09-16 14:05:40 -04:00
|
|
|
|
|
|
|
for (let row = 0; row < rows; row++) {
|
|
|
|
for (let col = 0; col < columns && row * columns + col < cards.length; col++) {
|
2021-09-16 16:46:15 -04:00
|
|
|
let index = row * columns + col;
|
|
|
|
let card = cards[index];
|
2021-09-16 14:05:40 -04:00
|
|
|
println("Processing Card ", card);
|
|
|
|
|
|
|
|
try {
|
|
|
|
let component = ResourceKit.getGameComponentFromFile(card.file);
|
|
|
|
let sheets = component.createDefaultSheets();
|
2021-09-18 01:26:15 -04:00
|
|
|
let copies = copyCount(copies_list, card.baseName);
|
2021-09-16 16:46:15 -04:00
|
|
|
|
2021-09-18 01:26:15 -04:00
|
|
|
for (let ii = 0; ii < copies; ii++) {
|
|
|
|
card_jsons.push(makeCardJSON(100 + index, component.getName()));
|
|
|
|
}
|
2021-09-16 16:46:15 -04:00
|
|
|
|
2021-09-16 14:05:40 -04:00
|
|
|
// export front face
|
|
|
|
// TODO: handle two-sided cards
|
|
|
|
let card_image = sheets[0].paint(arkham.sheet.RenderTarget.EXPORT, RESOLUTION);
|
|
|
|
|
2021-09-17 23:18:27 -04:00
|
|
|
if (!deck_image) {
|
|
|
|
deck_image = ImageUtils.create(
|
|
|
|
card_image.width * columns, card_image.height * rows, false);
|
|
|
|
deck_graphics = deck_image.createGraphics();
|
2021-09-16 14:05:40 -04:00
|
|
|
}
|
2021-09-17 23:18:27 -04:00
|
|
|
|
|
|
|
deck_graphics.drawImage(card_image, col * card_image.width, row * card_image.height, null);
|
2021-09-16 14:05:40 -04:00
|
|
|
} catch (ex) {
|
2021-09-18 00:33:22 -04:00
|
|
|
alert('Error while processing ' + card + ': ' + ex, true);
|
2021-09-16 14:05:40 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
println("End of Row ", row);
|
|
|
|
}
|
|
|
|
|
2021-09-16 16:46:15 -04:00
|
|
|
const deck_json = makeDeckJSON('TODO', 'TODO', columns, rows, card_jsons);
|
|
|
|
const saved_object = makeSavedObjectJSON([deck_json], member.getName());
|
|
|
|
|
|
|
|
println("Writing output files");
|
|
|
|
const json_file = new File(member.file, member.getName() + '.json');
|
|
|
|
ProjectUtilities.writeTextFile(json_file, JSON.stringify(saved_object, null, 4));
|
|
|
|
|
2021-09-18 01:24:53 -04:00
|
|
|
const image_file = new File(member.file, member.getName() + '.' + FORMAT);
|
2021-09-16 16:46:15 -04:00
|
|
|
ImageUtils.write(deck_image, image_file, FORMAT, -1, false, RESOLUTION);
|
2021-09-16 14:05:40 -04:00
|
|
|
|
|
|
|
member.synchronize();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
ActionRegistry.register(ttsDeckAction, Actions.PRIORITY_IMPORT_EXPORT);
|
|
|
|
}
|