Skip to content

Commit

Permalink
Updated Icons
Browse files Browse the repository at this point in the history
Check for Roll20 JSON and give a meaningful error
Import into Folders
Duplicate Checking
Configuration Options
  • Loading branch information
draconas1 committed Oct 16, 2021
1 parent f24797d commit 9365edf
Show file tree
Hide file tree
Showing 12 changed files with 202 additions and 61 deletions.
27 changes: 15 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,33 @@
## Importer
* **Author**: Draconas#9000
* **Foundry VTT Compatibility**: 0.8.9
* **DnD4E system compatibility**: 0.2.38
* **DnD4E system compatibility**: 0.2.43

### Description
An importer for Monster Data from Masterplan. You will need https://github.com/draconas1/masterplan-json-export to export the data, this module provides the importer

## Installation
* Open the Foundry application and click **"Install System"** in the **"Addon Modules"** tab.
* Paste the following link: https://github.com/draconas1/foundry-4e-tools/blob/main/module.json
* Open the Foundry application and click **"Install Module"** in the **"Addon Modules"** tab.
* Paste the following link: https://raw.githubusercontent.com/draconas1/foundry-4e-tools/main/module.json
* Click "Install"
* Enable it on your game.

# NOTE IT IS CURRENTLY IN DEV!

## Use
* The GM will get button in the actors tab for "import monsters"
* Paste the JSON from the Masterplan export, make sure the export was for Foundry JSON not Roll20 and click go

It is currently extremely rough and ready.
### Options
* **Import Into Folders**: The importer will find / create a folder based on the encounter name and put all the imports there
* **Duplicate Checking**: Either no checking, or check (and don't import) if a creature exists with a matching name, or check for matching name, but only in the folder you are importing into.

An import button will appear on the actors/items/compendium tab of the chat. It only imports NPC's regardless of where you click it.

When the popup box appears paste the json from the masterplan in there and click import.
### Configuration
The folder import and duplicate checking have configuration options that let you specify their default values.

## Notes
There are probably a lot of bugs, here are some I know about:
1. It still has legacy code and UI for the "todo list" tutorial all over it.
2. No checking if the actor already exists
3. Power range calculation is still a bit iffy based on the quality of the Masterplan file
4. You need version 38 of dnd4e to get power range of "melee", which isn't released yet
1. It still has legacy code and UI for the "todo list" tutorial all over it.
2. Empty folders can be created for empty encounters.

## Credit
Dan Houston and Dave Barnett both kindly created the SVG art of the 4E power icons for me when I posted a request out to my friends :)
4 changes: 2 additions & 2 deletions icons/area-burst.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions icons/close-blast.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions icons/melee-basic.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions icons/melee.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions icons/ranged-basic.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions icons/ranged.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 17 additions & 1 deletion languages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@
"title" : "Import JSON from Masterplan",
"label" : "Paste the Foundry JSON you exported from Masterplan below and click import",
"placeholder" : "Foundry format JSON",
"import-button" : "Import creatures into Foundry"
"import-button" : "Import creatures into Foundry",
"create-in-encounter-folders" : "Import creatures into folders based on encounter",
"no-duplicate-checking" : "Do not check if creatures already exist",
"do-not-import-duplicates" : "Do not import creatures that already exist (globally)",
"do-not-import-duplicates-in-folder" : "Do not import creatures that already exist in the same folder"
},
"button-title": "Open To-Do list",
"add-todo": "Add To-Do",
Expand All @@ -19,6 +23,18 @@
"inject-button": {
"Name": "Display To-Do List button?",
"Hint": "Controls whether or not the To-Do List can be opened from the player list."
},
"create-in-encounter-folders": {
"Name": "Default: Import into Folders?",
"Hint": "Default setting for when importing, should monsters be grouped together into a folder named for their encounter?"
},
"do-not-import-duplicates": {
"Name": "Default: Don't import monsters that exist?",
"Hint": "Default setting for if a monster already exists (in any folder), no not reimport it."
},
"do-not-import-duplicates-in-folder": {
"Name": "Default: Only check duplicates in same folder?",
"Hint": "Default setting for only checking within the encounter folder to see if a monster already exists."
}
},
"confirms": {
Expand Down
4 changes: 2 additions & 2 deletions module.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
"title": "Dracs 4E tools for Foundry VTT",
"description": "Imports Creatures from Masterplan into Foundry",
"author": "Drac",
"version": "0.0.2",
"version": "0.0.3",
"minimumCoreVersion": "0.8.0",
"compatibleCoreVersion": "0.8.9",
"url": "https://github.com/draconas1/foundry-4e-tools",
"manifest": "https://raw.githubusercontent.com/draconas1/foundry-4e-tools/main/module.json",
"download": "https://github.com/draconas1/foundry-4e-tools/releases/download/0.0.2/foundry-4e-tools.zip",
"download": "https://github.com/draconas1/foundry-4e-tools/releases/download/0.0.3/foundry-4e-tools.zip",
"esmodules": [
"module/4e-tools.js"
],
Expand Down
40 changes: 35 additions & 5 deletions module/4e-tools.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ export default class DnD4eTools {
}

static SETTINGS = {
INJECT_BUTTON: 'inject-button'
INJECT_BUTTON: 'inject-button',
CREATE_IN_ENCOUNTER_FOLDERS: 'create-in-encounter-folders',
DO_NOT_DUPLICATE: 'do-not-import-duplicates',
DO_NOT_DUPLICATE_IN_FOLDER: 'do-not-import-duplicates-in-folder',
}

static TEMPLATES = {
Expand All @@ -49,14 +52,41 @@ export default class DnD4eTools {
static initialize() {
//this.toDoListConfig = new ToDoListConfig();
console.log(this.NAME + " | Initialising 4E Tools and Masterplan Importer")
game.settings.register(this.ID, this.SETTINGS.INJECT_BUTTON, {
name: `TOOLS4E.settings.${this.SETTINGS.INJECT_BUTTON}.Name`,
// game.settings.register(this.ID, this.SETTINGS.INJECT_BUTTON, {
// name: `TOOLS4E.settings.${this.SETTINGS.INJECT_BUTTON}.Name`,
// default: true,
// type: Boolean,
// scope: 'client',
// config: true,
// hint: `TOOLS4E.settings.${this.SETTINGS.INJECT_BUTTON}.Hint`,
// onChange: () => ui.players.render()
// });

game.settings.register(this.ID, this.SETTINGS.CREATE_IN_ENCOUNTER_FOLDERS, {
name: `TOOLS4E.settings.${this.SETTINGS.CREATE_IN_ENCOUNTER_FOLDERS}.Name`,
default: true,
type: Boolean,
scope: 'client',
config: true,
hint: `TOOLS4E.settings.${this.SETTINGS.CREATE_IN_ENCOUNTER_FOLDERS}.Hint`
});

game.settings.register(this.ID, this.SETTINGS.DO_NOT_DUPLICATE, {
name: `TOOLS4E.settings.${this.SETTINGS.DO_NOT_DUPLICATE}.Name`,
default: true,
type: Boolean,
scope: 'client',
config: true,
hint: `TOOLS4E.settings.${this.SETTINGS.DO_NOT_DUPLICATE}.Hint`
});

game.settings.register(this.ID, this.SETTINGS.DO_NOT_DUPLICATE_IN_FOLDER, {
name: `TOOLS4E.settings.${this.SETTINGS.DO_NOT_DUPLICATE_IN_FOLDER}.Name`,
default: true,
type: Boolean,
scope: 'client',
config: true,
hint: `TOOLS4E.settings.${this.SETTINGS.INJECT_BUTTON}.Hint`,
onChange: () => ui.players.render()
hint: `TOOLS4E.settings.${this.SETTINGS.DO_NOT_DUPLICATE_IN_FOLDER}.Hint`
});
}
}
Expand Down
100 changes: 73 additions & 27 deletions module/screens/creature-import.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,51 +8,97 @@ export default class CreatureImporterScreen extends FormApplication {
height: 'auto',
id: 'creature-import',
template: DnD4eTools.TEMPLATES.IMPORTER_INPUT,
title: title,
userId: game.userId
title: title
};

const mergedOptions = foundry.utils.mergeObject(defaults, overrides);
return mergedOptions;
}

getData(options) {
const data = {}
data[DnD4eTools.SETTINGS.CREATE_IN_ENCOUNTER_FOLDERS] = game.settings.get(DnD4eTools.ID, DnD4eTools.SETTINGS.CREATE_IN_ENCOUNTER_FOLDERS)
data[DnD4eTools.SETTINGS.DO_NOT_DUPLICATE] = game.settings.get(DnD4eTools.ID, DnD4eTools.SETTINGS.DO_NOT_DUPLICATE)
data[DnD4eTools.SETTINGS.DO_NOT_DUPLICATE_IN_FOLDER] = game.settings.get(DnD4eTools.ID, DnD4eTools.SETTINGS.DO_NOT_DUPLICATE_IN_FOLDER)
DnD4eTools.log(false, "Default Input Form Config: " + JSON.stringify(data))
return data;
}

async _updateObject(event, formData) {
let obj = "";

try{
obj = JSON.parse(('Pasted content: ', formData.masterPlanInput))
} catch(err) {
DnD4eTools.log(false, "Invalid JSON Input")
ui.notifications.error("Invalid Input, JSON formatting did not validate");
DnD4eTools.log(false, "Invalid JSON Input " + err)
ui.notifications.error("Invalid Input, JSON formatting did not validate: " + err);
return;
}

for (const encounter of obj) {
for (const creature of encounter.Creatures) {
DnD4eTools.log(false, "Importing " + creature.Name)
let actor = await Actor.create(
{
"name" : creature.Name,
"type" : "NPC",
"data" : creature.Data
try {
for (const encounter of obj) {
let folder = undefined
let folderId = undefined
if (formData[DnD4eTools.SETTINGS.CREATE_IN_ENCOUNTER_FOLDERS] === true) {
folder = game.folders.find(x => x.name === encounter.Name)
if (!folder) {
folder = await Folder.create({
name: encounter.Name,
type: "Actor",
parent: null
});
}
)
for (const power of creature.Powers) {
//assign obj ID if one was not made
if(!power._id) { power._id = randomID(16); }
await actor.createOwnedItem(power)
}
for (const trait of creature.Traits) {
//assign obj ID if one was not made
if(!trait._id) { trait._id = randomID(16); }
await actor.createOwnedItem(trait)
folderId = folder.id
}

// work around because advancedCals does not want to be set on import.
actor.update({
"data.advancedCals" : true
})
for (const creature of encounter.Creatures) {
DnD4eTools.log(false, "Importing " + creature.Name)
if (creature.Size) {
throw "Invalid JSON: Received JSON for Roll20, you must set the exporter to export Foundry JSON"
}

// duplicate check
if (formData['handle-duplicates'] === DnD4eTools.SETTINGS.DO_NOT_DUPLICATE_IN_FOLDER) {
if (folder && folder.contents.find(x => x.name === creature.Name)) {
ui.notifications.info(creature.Name + " already existed in folder " + folder.name)
continue;
}
}

if (formData['handle-duplicates'] === DnD4eTools.SETTINGS.DO_NOT_DUPLICATE) {
if (game.actors.find(x => x.name === creature.Name)) {
ui.notifications.info(creature.Name + " already existed")
continue;
}
}

let actor = await Actor.create(
{
"name" : creature.Name,
"type" : "NPC",
"data" : creature.Data,
"folder" : folderId
}
)
for (const power of creature.Powers) {
//assign obj ID if one was not made
if(!power._id) { power._id = randomID(16); }
await actor.createOwnedItem(power)
}
for (const trait of creature.Traits) {
//assign obj ID if one was not made
if(!trait._id) { trait._id = randomID(16); }
await actor.createOwnedItem(trait)
}

// work around because advancedCals does not want to be set on import.
actor.update({
"data.advancedCals" : true
})
}
}
} catch(err) {
DnD4eTools.log(false, err)
ui.notifications.error(err);
}
}
}
50 changes: 48 additions & 2 deletions templates/importer-input.hbs
Original file line number Diff line number Diff line change
@@ -1,10 +1,56 @@
<form>
<label for="masterPlanInput">{{localize "TOOLS4E.import-json-screen.label"}}</label>
<textarea type="text" id="masterPlanInput" name="masterPlanInput" data-dtype="String" onclick="this.select();" rows="10" cols="80" placeholder="{{localize "TOOLS4E.import-json-screen.placeholder"}}"></textarea>
<button type="submit"><i class="fas fa-plus"></i>{{localize "TOOLS4E.import-json-screen.import-button"}}</button>
<textarea type="text" id="masterPlanInput" name="masterPlanInput" data-dtype="String" onclick="this.select();" rows="10" cols="80"
placeholder="{{localize "TOOLS4E.import-json-screen.placeholder"}}"></textarea>
<div>
<input type="checkbox" id="create-in-encounter-folders" name="create-in-encounter-folders" onchange="handleImportInFoldersChange(this);">
<label for="create-in-encounter-folders">{{localize "TOOLS4E.import-json-screen.create-in-encounter-folders"}}</label>
</div>
<div>
<input type="radio" id="no-duplicate-checking" name="handle-duplicates" checked value="no-duplicate-checking">
<label for="do-not-import-duplicates">{{localize "TOOLS4E.import-json-screen.no-duplicate-checking"}}</label>
</div>
<div>
<input type="radio" id="do-not-import-duplicates" name="handle-duplicates" value="do-not-import-duplicates">
<label for="do-not-import-duplicates">{{localize "TOOLS4E.import-json-screen.do-not-import-duplicates"}}</label>
</div>
<div>
<input type="radio" id="do-not-import-duplicates-in-folder" name="handle-duplicates" value="do-not-import-duplicates-in-folder">
<label for="do-not-import-duplicates-in-folder">{{localize "TOOLS4E.import-json-screen.do-not-import-duplicates-in-folder"}}</label>
</div>
<input type="hidden" id="create-in-encounter-folders-default" value="{{create-in-encounter-folders}}"/>
<input type="hidden" id="do-not-import-duplicates-default" value="{{do-not-import-duplicates}}"/>
<input type="hidden" id="do-not-import-duplicates-in-folder-default" value="{{do-not-import-duplicates-in-folder}}"/>
<button type="submit"><i class="fas fa-plus"></i>{{localize "TOOLS4E.import-json-screen.import-button"}}</button>
</form>

<script>
document.getElementById('masterPlanInput').focus();
document.getElementById('masterPlanInput').select();
if (truthy(document.getElementById('create-in-encounter-folders-default').value)) {
document.getElementById('create-in-encounter-folders').setAttribute("checked", "true")
}
else {
document.getElementById('do-not-import-duplicates-in-folder').setAttribute("disabled", "true")
}
if (truthy(document.getElementById('do-not-import-duplicates-default').value)) {
document.getElementById('do-not-import-duplicates').setAttribute("checked", "true")
}
if (truthy((document.getElementById('do-not-import-duplicates-in-folder-default').value)) && (truthy(document.getElementById('create-in-encounter-folders-default').value))) {
document.getElementById('do-not-import-duplicates-in-folder').setAttribute("checked", "true")
}
function handleImportInFoldersChange(cb) {
if (cb.checked) {
document.getElementById('do-not-import-duplicates-in-folder').removeAttribute("disabled")
}
else {
document.getElementById('do-not-import-duplicates-in-folder').setAttribute("disabled", "true")
}
}
function truthy(val) {
return val && val == "true"
}
</script>

0 comments on commit 9365edf

Please sign in to comment.