New developer and need some basic help

After much tinkering and following multiple tutorials I still can't get my system to show a custom sheet. All the tutorials I have come across are out of date or incomplete, so I'm sure I'm missing something basic. If anyone knows a good tutorial that shows how to build a system from scratch that would be great. Short of that, just looking for what is required to get a custom sheet to show up in a new system. Just the bare minimum will help at this stage since I'm just trying to figure out how things are linked and talk to each other and it seems that has changed over the versions.
41 Replies
CussaMitre
CussaMitre•5mo ago
Did you checked the boilerplate system and the tutorial that it goes with that one? https://github.com/hodpub/boilerplate/tree/v13 This is a PR branch for the v13. And it has this tutorial, that although it is a little bit outdated, it is still a very good point of start: https://foundryvtt.wiki/en/development/guides/SD-tutorial
GitHub
GitHub - hodpub/boilerplate at v13
Boilerplate system for FoundryVTT to use as a starting point for your own system's development. Follow along with the [accompanying tutorial](https://foundryvtt.wiki/en/development/guides/S...
Demonic Leprechaun
Demonic LeprechaunOP•5mo ago
That is one that I've been picking through. Since it isn't from scratch it doesn't seem to explain certain things (or I could be missing it). Part of wanting something that is actually from scratch is so I can see how everything is connect. Though, I am rereading through it now to see if I can see what I'm missing in it. Also, looking through the dnd5e system files, though that only really helps when I have at least an idea of what I'm looking for.
CussaMitre
CussaMitre•5mo ago
So, I don't think any system is going from really the scratch. Most systems are using the Boilerplate or a similar version of it to make things connected. Of course you could basically create the only required bits (the JS file that is the entry point of the system) and start from there. But I don't think that doing that would bring you a lot of value. The most common approach is to use something and build upon that, and looking to other system whenever you want to do something that is beyond the tutorial.
Demonic Leprechaun
Demonic LeprechaunOP•5mo ago
Different styles. I'll keep trying to figure it out.
CussaMitre
CussaMitre•5mo ago
If you have specfic questions, I am sure that most people would be happy to share some knowledge 🙂
Demonic Leprechaun
Demonic LeprechaunOP•5mo ago
As I put in the original post, after following several tutorials I can't get a custom sheet (actor or item) to show up in place of the default. So I'm trying to figure out what I missed.
CussaMitre
CussaMitre•5mo ago
Did you follow the process to unregister the default sheet and register the one from your system?
// Register sheet application classes
Actors.unregisterSheet('core', ActorSheet);
Actors.registerSheet('boilerplate', BoilerplateActorSheet, {
makeDefault: true,
label: 'BOILERPLATE.SheetLabels.Actor',
});
Items.unregisterSheet('core', ItemSheet);
Items.registerSheet('boilerplate', BoilerplateItemSheet, {
makeDefault: true,
label: 'BOILERPLATE.SheetLabels.Item',
});
// Register sheet application classes
Actors.unregisterSheet('core', ActorSheet);
Actors.registerSheet('boilerplate', BoilerplateActorSheet, {
makeDefault: true,
label: 'BOILERPLATE.SheetLabels.Actor',
});
Items.unregisterSheet('core', ItemSheet);
Items.registerSheet('boilerplate', BoilerplateItemSheet, {
makeDefault: true,
label: 'BOILERPLATE.SheetLabels.Item',
});
(getting the example from Boilerplate, but should be easily associated with yours)
Demonic Leprechaun
Demonic LeprechaunOP•5mo ago
Yep. I'd post the code, but I'm in the middle of rewriting it. Since I was pulling information from multiple locations that where all on different versions when they made the tutorial I'm making sure the issue was a mix of new and old code.
Demonic Leprechaun
Demonic LeprechaunOP•5mo ago
Still not seeing my sheets when I load up the system. I do see character as an option under actor and all the options for items, but the sheet is just the default picture and name one. sending system.json, the main javascript file, and one of the item sheets. Let me know if you need anything else.
Dangermouse
Dangermouse•4mo ago
I'm not an expert -I have built my stuff from Boilerplate and gone from there so I'm guessing a bit Do you have something like this in your main.mjs Hooks.on('renderActorSheet', AoVActorSheet.renderSheet); Hooks.on('renderItemSheet', AoVItemSheet.renderSheet); They are telling Foundry to render your Actor and Item Sheets rather than the core Foundry ones I found these videos really helpful - they are a bit out of date but do build from scratch - but they don't cover Data Models or V2 Application https://www.youtube.com/@oatveal
Demonic Leprechaun
Demonic LeprechaunOP•4mo ago
I did not. I will add that. Yeah I watched some of his stuff, but wasn't sure how much of it was still relevant since a lot of stuff seems to have changed in between when those videos are made and now, but I like their style.
Dangermouse
Dangermouse•4mo ago
Let me know how it goes.
Demonic Leprechaun
Demonic LeprechaunOP•4mo ago
Adding that line didn't do anything. Just to make sure I did it right, does it need to be inside another method or should it be on its own? Should the "AoVItemSheet" be the class name of the sheet? My system.json
{
"id": "dnd0e",
"title": "Dungeons & Dragons - Original",
"description": "Dungeons & Dragons White Box ruleset",
"version": "0.0.1",
"compatibility":
{
"minimum": 12,
"verified": 12.331
},
"authors": [{ "name": "Demonic Leprechaun" }],
"esmodules": ["dnd0e.mjs"],
"styles": ["dnd0e.css"],
"packs": [],
"languages":
[{
"lang": "en",
"name": "English",
"path": "lang/en.json"
}],
"documentTypes":
{
"Item":
{
"item":
{
"name": "",
"description": "",
"cost": 0,
"weight": 0
}
}
},
"initiative": "1d6",
"grid": {
"distance": 5,
"units": "ft"
}
}
{
"id": "dnd0e",
"title": "Dungeons & Dragons - Original",
"description": "Dungeons & Dragons White Box ruleset",
"version": "0.0.1",
"compatibility":
{
"minimum": 12,
"verified": 12.331
},
"authors": [{ "name": "Demonic Leprechaun" }],
"esmodules": ["dnd0e.mjs"],
"styles": ["dnd0e.css"],
"packs": [],
"languages":
[{
"lang": "en",
"name": "English",
"path": "lang/en.json"
}],
"documentTypes":
{
"Item":
{
"item":
{
"name": "",
"description": "",
"cost": 0,
"weight": 0
}
}
},
"initiative": "1d6",
"grid": {
"distance": 5,
"units": "ft"
}
}
My system.mjs
class ItemDataModel extends foundry.abstract.TypeDataModel
{
static defineSchema()
{
const fields = foundry.data.fields;

return {
name: new fields.StringField(),
description: new fields.HTMLField({required: false, blank: true, initial: ""}),
cost: new fields.NumberField(),
weight: new fields.NumberField()
};
}

prepareDerivedData()
{
super.prepareDerivedData();
}
}

class dnd0eItem extends Item
{
prepareData() {}
}

class dnd0eItemSheet
{
static get defaultOptions()
{
return foundry.utils.mergeObject(super.defaultOptions,
{
width: 600,
height: 800
}
);
}

get template()
{
//const path = 'systems/dnd0e/templates/sheets';

//return `${path}/${this.item.type}_sheet.html`;
return 'systems/dnd0e/templates/sheets/item_sheet.html';
}

getData()
{
const context = super.getData();

const itemData = context.data;

context.system = itemData.system;
context.flags = itemData.flags;

return context;
}
}

Hooks.on("init", () =>
{
CONFIG.Item.dataModels = {
item: ItemDataModel
}

CONFIG.Item.documentClass = dnd0eItem;

DocumentSheetConfig.unregisterSheet(Item, "core", ItemSheet);
DocumentSheetConfig.registerSheet(Item, "dnd0e", dnd0eItemSheet, {makeDefault: true});
});
class ItemDataModel extends foundry.abstract.TypeDataModel
{
static defineSchema()
{
const fields = foundry.data.fields;

return {
name: new fields.StringField(),
description: new fields.HTMLField({required: false, blank: true, initial: ""}),
cost: new fields.NumberField(),
weight: new fields.NumberField()
};
}

prepareDerivedData()
{
super.prepareDerivedData();
}
}

class dnd0eItem extends Item
{
prepareData() {}
}

class dnd0eItemSheet
{
static get defaultOptions()
{
return foundry.utils.mergeObject(super.defaultOptions,
{
width: 600,
height: 800
}
);
}

get template()
{
//const path = 'systems/dnd0e/templates/sheets';

//return `${path}/${this.item.type}_sheet.html`;
return 'systems/dnd0e/templates/sheets/item_sheet.html';
}

getData()
{
const context = super.getData();

const itemData = context.data;

context.system = itemData.system;
context.flags = itemData.flags;

return context;
}
}

Hooks.on("init", () =>
{
CONFIG.Item.dataModels = {
item: ItemDataModel
}

CONFIG.Item.documentClass = dnd0eItem;

DocumentSheetConfig.unregisterSheet(Item, "core", ItemSheet);
DocumentSheetConfig.registerSheet(Item, "dnd0e", dnd0eItemSheet, {makeDefault: true});
});
My item_sheet.html
form>
<header>
<h1>Basic Item</h1>
<img src="{{item.img}}" data-edit="img" title="{{item.name}}" height="64" width="64"/>
<h1><input name="name" type="text" value="{{item.name}}" placeholder="Name"/></h1>
</header>
</form>
form>
<header>
<h1>Basic Item</h1>
<img src="{{item.img}}" data-edit="img" title="{{item.name}}" height="64" width="64"/>
<h1><input name="name" type="text" value="{{item.name}}" placeholder="Name"/></h1>
</header>
</form>
I trimmed down things to see what was actually needed, and made sure there were no errors in the console, but now nothing (not even the default sheet shows).
Dangermouse
Dangermouse•4mo ago
@Demonic Leprechaun
A question I should have asked - are you using V1 or V2 application for your sheets? If it's V2 then you'll want Hooks.on('renderActorSheetV2', AoVActorSheet.renderSheet); THis is roughly what I have set up - your setup may differ...... (and now I hope I don't miss anything) - this is for V2 application in Foundry v13 My aov.mjs file is just
import { AoVActorSheet } from "./system/actor/sheets/actor-sheet.mjs";
import { AoVItemSheet } from "./system/item/sheets/item-sheet.mjs";
import Init from './system/hooks/init.mjs';
import Ready from './system/hooks/ready.mjs';

Hooks.once('init', Init);
Hooks.once('ready', Ready);
Hooks.on('renderActorSheetV2', AoVActorSheet.renderSheet);
Hooks.on('renderItemSheetV2', AoVItemSheet.renderSheet);
import { AoVActorSheet } from "./system/actor/sheets/actor-sheet.mjs";
import { AoVItemSheet } from "./system/item/sheets/item-sheet.mjs";
import Init from './system/hooks/init.mjs';
import Ready from './system/hooks/ready.mjs';

Hooks.once('init', Init);
Hooks.once('ready', Ready);
Hooks.on('renderActorSheetV2', AoVActorSheet.renderSheet);
Hooks.on('renderItemSheetV2', AoVItemSheet.renderSheet);
My init.mjs file is
import { AOV } from "../setup/config.mjs";
import { AOVActor } from "../actor/actor.mjs";
import { AOVItem } from "../item/item.mjs";
import { CID } from '../cid/cid.mjs'
import { handlebarsHelper } from '../setup/handlebar-helpers.mjs';
import { registerSettings } from '../settings/register-settings.mjs'
import { registerSheets } from '../setup/register-sheets.mjs'
import * as models from '../data/_module.mjs';

export default function Init() {
//Add classes to global game object
game.aov = {
AOVActor,
AOVItem
}
//Add Custom Configuration
CONFIG.AOV = AOV;

//Register Settings and Handlebar Helpers
registerSettings();
handlebarsHelper();

// Define custom Document classes
CONFIG.Item.documentClass = AOVItem;
CONFIG.Actor.documentClass = AOVActor;


//Declare Data Models
CONFIG.Actor.dataModels.character = models.AOVCharacterModel
CONFIG.Actor.dataModels.farm = models.AOVFarmModel
CONFIG.Item.dataModels.devotion = models.AOVDevotionModel
CONFIG.Item.dataModels.family = models.AOVFamilyModel
CONFIG.Item.dataModels.gear = models.AOVGearModel
CONFIG.Item.dataModels.hitloc = models.AOVHitLocModel
CONFIG.Item.dataModels.passion = models.AOVPassionModel
CONFIG.Item.dataModels.thrall = models.AOVThrallModel
CONFIG.Item.dataModels.skill = models.AOVSkillModel
CONFIG.Item.dataModels.weapon = models.AOVWeaponModel
CONFIG.Item.dataModels.weaponCat = models.AOVWeaponCatModel
CONFIG.Item.dataModels.wound = models.AOVWoundModel

// Active Effects are never copied to the Actor,
// but will still apply to the Actor from within the Item
// if the transfer property on the Active Effect is true.
CONFIG.ActiveEffect.legacyTransferral = false;

CID.init()
registerSheets()
}
import { AOV } from "../setup/config.mjs";
import { AOVActor } from "../actor/actor.mjs";
import { AOVItem } from "../item/item.mjs";
import { CID } from '../cid/cid.mjs'
import { handlebarsHelper } from '../setup/handlebar-helpers.mjs';
import { registerSettings } from '../settings/register-settings.mjs'
import { registerSheets } from '../setup/register-sheets.mjs'
import * as models from '../data/_module.mjs';

export default function Init() {
//Add classes to global game object
game.aov = {
AOVActor,
AOVItem
}
//Add Custom Configuration
CONFIG.AOV = AOV;

//Register Settings and Handlebar Helpers
registerSettings();
handlebarsHelper();

// Define custom Document classes
CONFIG.Item.documentClass = AOVItem;
CONFIG.Actor.documentClass = AOVActor;


//Declare Data Models
CONFIG.Actor.dataModels.character = models.AOVCharacterModel
CONFIG.Actor.dataModels.farm = models.AOVFarmModel
CONFIG.Item.dataModels.devotion = models.AOVDevotionModel
CONFIG.Item.dataModels.family = models.AOVFamilyModel
CONFIG.Item.dataModels.gear = models.AOVGearModel
CONFIG.Item.dataModels.hitloc = models.AOVHitLocModel
CONFIG.Item.dataModels.passion = models.AOVPassionModel
CONFIG.Item.dataModels.thrall = models.AOVThrallModel
CONFIG.Item.dataModels.skill = models.AOVSkillModel
CONFIG.Item.dataModels.weapon = models.AOVWeaponModel
CONFIG.Item.dataModels.weaponCat = models.AOVWeaponCatModel
CONFIG.Item.dataModels.wound = models.AOVWoundModel

// Active Effects are never copied to the Actor,
// but will still apply to the Actor from within the Item
// if the transfer property on the Active Effect is true.
CONFIG.ActiveEffect.legacyTransferral = false;

CID.init()
registerSheets()
}
Then in register-sheets.mjs there's a lot of
export function registerSheets() {


const { sheets } = foundry.applications;
let { collections } = foundry.documents;

/* // FoundryVTT V12 */
if (typeof collections === 'undefined') {
collections = {
Actors: Actors,
Items: Items,
}
}

collections.Actors.unregisterSheet("core", sheets.ActorSheetV2);
collections.Actors.registerSheet('aov', AoVCharacterSheet, {
types: ['character'],
makeDefault: true
});
collections.Actors.registerSheet('aov', AoVFarmSheet, {
types: ['farm'],
makeDefault: true
});
export function registerSheets() {


const { sheets } = foundry.applications;
let { collections } = foundry.documents;

/* // FoundryVTT V12 */
if (typeof collections === 'undefined') {
collections = {
Actors: Actors,
Items: Items,
}
}

collections.Actors.unregisterSheet("core", sheets.ActorSheetV2);
collections.Actors.registerSheet('aov', AoVCharacterSheet, {
types: ['character'],
makeDefault: true
});
collections.Actors.registerSheet('aov', AoVFarmSheet, {
types: ['farm'],
makeDefault: true
});
Demonic Leprechaun
Demonic LeprechaunOP•4mo ago
Not sure the difference between V1 and V2, so I guess I'll just learn V2. Ah, your importing a bunch of things. Are those things that were setup by boilerplate or you?
Dangermouse
Dangermouse•4mo ago
Does your item sheet.mjs file (or equivalent) have
export class AoVItemSheet extends api.HandlebarsApplicationMixin(sheets.ItemSheetV2) {
export class AoVItemSheet extends api.HandlebarsApplicationMixin(sheets.ItemSheetV2) {
If so that's V2 They're sort of both. In Boilerplate alot of them come in different files (for example the register-sheets are built in to boilerplate.mjs) - I';ve moved them to separate files to make each file easier to read (well easier for me)
Demonic Leprechaun
Demonic LeprechaunOP•4mo ago
Right now I have everything in one file to avoid importing and exporting. I was simplifying things down to the barebones to find what was actually needed.
Dangermouse
Dangermouse•4mo ago
So everything's in your dnd0e.mjs file?
Demonic Leprechaun
Demonic LeprechaunOP•4mo ago
Yep. Well all the javascript. Obviously the sheets and json files are their own thing.
Dangermouse
Dangermouse•4mo ago
OK - so I think you're using V1
class dnd0eActorSheet extends ActorSheet
class dnd0eActorSheet extends ActorSheet
Demonic Leprechaun
Demonic LeprechaunOP•4mo ago
Okay. I was using whatever I could find. Some of it was on Foundry's site, but that was incomplete. And some of it was from boilerplate tutorials, but those don't mention things that boilerplate is doing for them (at least the ones I found).
Dangermouse
Dangermouse•4mo ago
Is your code on a Github or something - I'll download if so and have a play if that's ok
Demonic Leprechaun
Demonic LeprechaunOP•4mo ago
Not at the moment. I can put it up somewhere in a bit, but most of it is in the messages above. It really is just the bare minimum to try and get an item sheet to render.
Dangermouse
Dangermouse•4mo ago
Let me have a play then and see what I can do
Demonic Leprechaun
Demonic LeprechaunOP•4mo ago
In this:
/* // FoundryVTT V12 */
if (typeof collections === 'undefined') {
collections = {
Actors: Actors,
Items: Items,
}
}
/* // FoundryVTT V12 */
if (typeof collections === 'undefined') {
collections = {
Actors: Actors,
Items: Items,
}
}
Are the "Actors" and "Items" after the colon datamodels, just the class or something else? Thanks.
Leo The League Lion
Leo The League Lion•4mo ago
@Demonic Leprechaun gave :vote: LeaguePoints™ to @Dangermouse (#274 • 2)
Dangermouse
Dangermouse•4mo ago
Are you getting any error messages in the console when you start your world?
Demonic Leprechaun
Demonic LeprechaunOP•4mo ago
No I get one when I try to open the item sheet.
Dangermouse
Dangermouse•4mo ago
Then I think I may need the rest of your files
Demonic Leprechaun
Demonic LeprechaunOP•4mo ago
No description
Demonic Leprechaun
Demonic LeprechaunOP•4mo ago
Okay. Give me a moment. Ah, the system is referencing some stuff not being used. That's probably the error you are getting. Let me trim it up and test it.
Demonic Leprechaun
Demonic LeprechaunOP•4mo ago
That should work but error on the render. Looks like there is probably a Hook somewhere that I need to add. Oh the dnd0eItemSheet class needs its get template() fixed
get template()
{
return 'systems/ItemTest/item_sheet.html';
}
get template()
{
return 'systems/ItemTest/item_sheet.html';
}
Dangermouse
Dangermouse•4mo ago
In your dnd0e.mjs file change
class dnd0eItemSheet
class dnd0eItemSheet
to
class dnd0eItemSheet extends ItemSheet
class dnd0eItemSheet extends ItemSheet
row 24 I'm now getting your item sheet opening
Demonic Leprechaun
Demonic LeprechaunOP•4mo ago
Cool. Same.
Dangermouse
Dangermouse•4mo ago
So in your original dnd0e.mjs file you need to change row 135
Demonic Leprechaun
Demonic LeprechaunOP•4mo ago
Thanks again. I had it extending in an earlier version. Probably had a different error and that got missed when I was stripping things down. Yeah Thanks again.
Dangermouse
Dangermouse•4mo ago
No worries. Happy to help where I can (my knowledge in this space is basic)
Demonic Leprechaun
Demonic LeprechaunOP•4mo ago
It helps a lot. I just picked up foundry a couple weeks ago, so I'm trying to learn its structure.
Demonic Leprechaun
Demonic LeprechaunOP•4mo ago
Update: Made a minor change to the code so that you can have an editable description field as an example.
Dangermouse
Dangermouse•4mo ago
@Demonic Leprechaun Have dropped you a DM

Did you find this page helpful?