diff --git a/.styles/root.css b/.styles/root.css index d74992a..9272be7 100644 --- a/.styles/root.css +++ b/.styles/root.css @@ -1,15 +1,48 @@ @import url("https://fonts.googleapis.com/css2?family=Pixelify+Sans&display=swap"); +.dotdungeon > .window-content ::-webkit-scrollbar { + width: 10px; +} +.dotdungeon > .window-content ::-webkit-scrollbar-thumb { + border-radius: 5px; +} .dotdungeon > .window-content h2, .dotdungeon > .window-content h3, .dotdungeon > .window-content h4, .dotdungeon > .window-content h5, .dotdungeon > .window-content h6 { all: initial; display: block; + box-sizing: border-box; + display: block; font-family: "Pixelify Sans", sans-serif; margin: 0; } +.dotdungeon > .window-content label { + display: block; +} .dotdungeon > .window-content button, .dotdungeon > .window-content button:hover { all: initial; + display: block; + box-sizing: border-box; font-family: inherit; cursor: pointer; } +.dotdungeon > .window-content input[type=text], +.dotdungeon > .window-content input[type=number], +.dotdungeon > .window-content textarea { + all: initial; + display: block; + box-sizing: border-box; + padding: 5px 7px; + border-width: 2px; + border-radius: 4px; + border-style: solid; + border-color: rgba(0, 0, 0, 0.4); + background-color: rgba(0, 0, 0, 0.1); +} +.dotdungeon > .window-content input[type=text]:focus, .dotdungeon > .window-content input[type=text]:active, +.dotdungeon > .window-content input[type=number]:focus, +.dotdungeon > .window-content input[type=number]:active, +.dotdungeon > .window-content textarea:focus, +.dotdungeon > .window-content textarea:active { + border-color: rgb(0, 0, 0); +} .dotdungeon > .window-content select, .dotdungeon > .window-content select:hover { cursor: pointer; } @@ -158,5 +191,19 @@ visibility: hidden; } } +.dotdungeon .item--aspect { + padding: 4px; +} +.dotdungeon .item--aspect input[type=text] { + font-family: sans-serif; + font-size: 1.5em; + height: 1.5em; + width: 100%; +} +.dotdungeon .item--aspect textarea { + font-family: sans-serif; + width: 100%; + resize: vertical; +} -/*# sourceMappingURL=data:application/json;charset=utf-8,%7B%22version%22:3,%22sourceRoot%22:%22%22,%22sources%22:%5B%22../styles/root.scss%22,%22../styles/mixins/_foundry.scss%22,%22../styles/vars.scss%22,%22../styles/sheets/partials/stat.scss%22,%22../styles/sheets/partials/skill.scss%22,%22../styles/sheets/actor/mvp.scss%22,%22../styles/mixins/_breakpoints.scss%22%5D,%22names%22:%5B%5D,%22mappings%22:%22AAGQ;AAIP;ECNA;EDQC;EACA,aEVW;EFWX;;AAGD;ECbA;EDeC;EACA;;AAGD;EACC;;;AAKF;EACC;;AAEA;EACC;EACA,YE9BW;;;ACDb;EACC;EACA;EACA;;ACHD;EACC;EACA;EACA;EACA;;AAEA;EACC;EACA;EACA;EACA;EACA;EACA;;AAEA;EACC;EACA;EACA;EACA;;;ACdF;EACC;EACA,qBACC;EASD;EACA;EACA;EACA;;AAGC;EACC;;AAED;EACC;;AACA;EACC;EACA;EACA;EACA;EACA;;AAGF;EACC;;AAEA;EACC;EACA;EACA;;AAGF;EACC;;AAED;EACC;;AAED;EACC;;AAED;EACC;;AAED;EACC;;AAED;EACC;;AAED;EACC;;AAED;EACC;;AAED;EACC;;AAKH;EACC;EACA;;AAEA;EACC;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACC;EACA;EACA;EACA;EACA,aH5FS;EG6FT;;AAGD;EAEC,QADO;EAEP,OAFO;EAGP;;;ACnGH;ED4GC;IACC;IACA;IACA,qBACC;;EAiBF;IACC;;EAGC;IACC;;;AChIJ;EDyIC;IACC;IACA;IACA,qBACC;;EAaF;IACC;;EAGC;IACC;IACA%22,%22file%22:%22root.css%22%7D */ +/*# sourceMappingURL=data:application/json;charset=utf-8,%7B%22version%22:3,%22sourceRoot%22:%22%22,%22sources%22:%5B%22../styles/root.scss%22,%22../styles/mixins/_foundry.scss%22,%22../styles/_vars.scss%22,%22../styles/mixins/_partials.scss%22,%22../styles/sheets/partials/stat.scss%22,%22../styles/sheets/partials/skill.scss%22,%22../styles/sheets/actor/mvp.scss%22,%22../styles/mixins/_breakpoints.scss%22,%22../styles/sheets/items/aspect.scss%22%5D,%22names%22:%5B%5D,%22mappings%22:%22AAIQ;AAKP;EACC;;AAED;EACC;;AAGD;ECfA;EACA;EACA;EDeC;EACA,aEnBW;EFoBX;;AAGD;EACC;;AAGD;EC1BA;EACA;EACA;ED0BC;EACA;;AAGD;AAAA;AAAA;EChCA;EACA;EACA;EDkCC;EGpCD;EACA;EACA;EACA;EACA;;AAEA;AAAA;AAAA;AAAA;AAAA;EAEC;;AHgCD;EACC;;;AAKF;EACC;;AAEA;EACC;EACA,YElDW;;;AEFb;EACC;EACA;EACA;;ACHD;EACC;EACA;EACA;EACA;;AAEA;EACC;EACA;EACA;EACA;EACA;EACA;;AAEA;EACC;EACA;EACA;EACA;;;ACdF;EACC;EACA,qBACC;EASD;EACA;EACA;EACA;;AAGC;EACC;;AAED;EACC;;AACA;EACC;EACA;EACA;EACA;EACA;;AAGF;EACC;;AAEA;EACC;EACA;EACA;;AAGF;EACC;;AAED;EACC;;AAED;EACC;;AAED;EACC;;AAED;EACC;;AAED;EACC;;AAED;EACC;;AAED;EACC;;AAED;EACC;;AAKH;EACC;EACA;;AAEA;EACC;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACC;EACA;EACA;EACA;EACA,aJ5FS;EI6FT;;AAGD;EAEC,QADO;EAEP,OAFO;EAGP;;;ACnGH;ED4GC;IACC;IACA;IACA,qBACC;;EAiBF;IACC;;EAGC;IACC;;;AChIJ;EDyIC;IACC;IACA;IACA,qBACC;;EAaF;IACC;;EAGC;IACC;IACA;;;AErKL;EACC;;AAEA;EACC,aNLU;EMMV;EACA;EACA;;AAGD;EACC,aNZU;EMaV;EACA%22,%22file%22:%22root.css%22%7D */ diff --git a/.styles/sheets/actor/mvp.css b/.styles/sheets/actor/mvp.css index 51b1163..4139566 100644 --- a/.styles/sheets/actor/mvp.css +++ b/.styles/sheets/actor/mvp.css @@ -109,4 +109,4 @@ } } -/*# sourceMappingURL=data:application/json;charset=utf-8,%7B%22version%22:3,%22sourceRoot%22:%22%22,%22sources%22:%5B%22../../../styles/sheets/actor/mvp.scss%22,%22../../../styles/vars.scss%22,%22../../../styles/mixins/_breakpoints.scss%22%5D,%22names%22:%5B%5D,%22mappings%22:%22AAIC;EACC;EACA,qBACC;EASD;EACA;EACA;EACA;;AAGC;EACC;;AAED;EACC;;AACA;EACC;EACA;EACA;EACA;EACA;;AAGF;EACC;;AAEA;EACC;EACA;EACA;;AAGF;EACC;;AAED;EACC;;AAED;EACC;;AAED;EACC;;AAED;EACC;;AAED;EACC;;AAED;EACC;;AAED;EACC;;AAED;EACC;;AAKH;EACC;EACA;;AAEA;EACC;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACC;EACA;EACA;EACA;EACA,aC5FS;ED6FT;;AAGD;EAEC,QADO;EAEP,OAFO;EAGP;;;AEnGH;EF4GC;IACC;IACA;IACA,qBACC;;EAiBF;IACC;;EAGC;IACC;;;AEhIJ;EFyIC;IACC;IACA;IACA,qBACC;;EAaF;IACC;;EAGC;IACC;IACA%22,%22file%22:%22mvp.css%22%7D */ +/*# sourceMappingURL=data:application/json;charset=utf-8,%7B%22version%22:3,%22sourceRoot%22:%22%22,%22sources%22:%5B%22../../../styles/sheets/actor/mvp.scss%22,%22../../../styles/_vars.scss%22,%22../../../styles/mixins/_breakpoints.scss%22%5D,%22names%22:%5B%5D,%22mappings%22:%22AAIC;EACC;EACA,qBACC;EASD;EACA;EACA;EACA;;AAGC;EACC;;AAED;EACC;;AACA;EACC;EACA;EACA;EACA;EACA;;AAGF;EACC;;AAEA;EACC;EACA;EACA;;AAGF;EACC;;AAED;EACC;;AAED;EACC;;AAED;EACC;;AAED;EACC;;AAED;EACC;;AAED;EACC;;AAED;EACC;;AAED;EACC;;AAKH;EACC;EACA;;AAEA;EACC;EACA;EACA;EACA;EACA;EACA;EACA;;AAEA;EACC;EACA;EACA;EACA;EACA,aC5FS;ED6FT;;AAGD;EAEC,QADO;EAEP,OAFO;EAGP;;;AEnGH;EF4GC;IACC;IACA;IACA,qBACC;;EAiBF;IACC;;EAGC;IACC;;;AEhIJ;EFyIC;IACC;IACA;IACA,qBACC;;EAaF;IACC;;EAGC;IACC;IACA%22,%22file%22:%22mvp.css%22%7D */ diff --git a/.styles/sheets/items/aspect.css b/.styles/sheets/items/aspect.css new file mode 100644 index 0000000..2b39c6f --- /dev/null +++ b/.styles/sheets/items/aspect.css @@ -0,0 +1,16 @@ +.dotdungeon .item--aspect { + padding: 4px; +} +.dotdungeon .item--aspect input[type=text] { + font-family: sans-serif; + font-size: 1.5em; + height: 1.5em; + width: 100%; +} +.dotdungeon .item--aspect textarea { + font-family: sans-serif; + width: 100%; + resize: vertical; +} + +/*# sourceMappingURL=data:application/json;charset=utf-8,%7B%22version%22:3,%22sourceRoot%22:%22%22,%22sources%22:%5B%22../../../styles/sheets/items/aspect.scss%22,%22../../../styles/_vars.scss%22%5D,%22names%22:%5B%5D,%22mappings%22:%22AAEA;EACC;;AAEA;EACC,aCLU;EDMV;EACA;EACA;;AAGD;EACC,aCZU;EDaV;EACA%22,%22file%22:%22aspect.css%22%7D */ diff --git a/.styles/vars.css b/.styles/vars.css deleted file mode 100644 index 57ca8ec..0000000 --- a/.styles/vars.css +++ /dev/null @@ -1,3 +0,0 @@ - - -/*# sourceMappingURL=data:application/json;charset=utf-8,%7B%22version%22:3,%22sourceRoot%22:%22%22,%22sources%22:%5B%5D,%22names%22:%5B%5D,%22mappings%22:%22%22,%22file%22:%22vars.css%22%7D */ diff --git a/langs/en-ca.json b/langs/en-ca.json index a6b8a3d..cf8ca57 100644 --- a/langs/en-ca.json +++ b/langs/en-ca.json @@ -2,54 +2,60 @@ "dotdungeon.sheet.PlayerSheet": "PC Sheet", "dotdungeon.sheet.AspectSheet": "Aspect Sheet", - "dotdungeon.panel.avatar": "Avatar", - "dotdungeon.panel.statistics": "Statistics", - "dotdungeon.panel.skills": "Skills", - "dotdungeon.panel.backpack": "Backpack", - "dotdungeon.panel.sync": "Sync", - "dotdungeon.panel.aspect": "Aspect", - "dotdungeon.panel.weapons": "Weapons", - "dotdungeon.panel.roles": "Roles", - "dotdungeon.panel.spells": "Spells", - "dotdungeon.panel.mounts": "Mounts", - "dotdungeon.panel.summons": "Summons", - "dotdungeon.panel.storage": "Storage", + "dotdungeon.actor.pc.panel.avatar": "Avatar", + "dotdungeon.actor.pc.panel.statistics": "Stats", + "dotdungeon.actor.pc.panel.skills": "Skills", + "dotdungeon.actor.pc.panel.backpack": "Backpack", + "dotdungeon.actor.pc.panel.sync": "Sync", + "dotdungeon.actor.pc.panel.aspect": "Aspect", + "dotdungeon.actor.pc.panel.weapons": "Weapons", + "dotdungeon.actor.pc.panel.roles": "Roles", + "dotdungeon.actor.pc.panel.spells": "Spells", + "dotdungeon.actor.pc.panel.mounts": "Mounts", + "dotdungeon.actor.pc.panel.summons": "Summons", + "dotdungeon.actor.pc.panel.storage": "Storage", - "dotdungeon.stat.build": "Build", - "dotdungeon.stat.meta": "Meta", - "dotdungeon.stat.presence": "Presence", - "dotdungeon.stat.hands": "Hands", - "dotdungeon.stat.tilt": "Tilt", - "dotdungeon.stat.rng": "RNG", + "dotdungeon.actor.pc.stat.build": "Build", + "dotdungeon.actor.pc.stat.meta": "Meta", + "dotdungeon.actor.pc.stat.presence": "Presence", + "dotdungeon.actor.pc.stat.hands": "Hands", + "dotdungeon.actor.pc.stat.tilt": "Tilt", + "dotdungeon.actor.pc.stat.rng": "RNG", - "dotdungeon.skills.build": "Build", - "dotdungeon.skill.defense": "Defense", - "dotdungeon.skill.magic": "Magic", - "dotdungeon.skill.melee": "Melee", - "dotdungeon.skill.platforming": "Platforming", - "dotdungeon.skill.strength": "Strength", + "dotdungeon.actor.pc.skills.build": "Build", + "dotdungeon.actor.pc.skill.defense": "Defense", + "dotdungeon.actor.pc.skill.magic": "Magic", + "dotdungeon.actor.pc.skill.melee": "Melee", + "dotdungeon.actor.pc.skill.platforming": "Platforming", + "dotdungeon.actor.pc.skill.strength": "Strength", - "dotdungeon.skills.meta": "Meta", - "dotdungeon.skill.alchemy": "Alchemy", - "dotdungeon.skill.arcanum": "Arcanum", - "dotdungeon.skill.dreams": "Dreams", - "dotdungeon.skill.lore": "Lore", - "dotdungeon.skill.navigation": "Navigation", + "dotdungeon.actor.pc.skills.meta": "Meta", + "dotdungeon.actor.pc.skill.alchemy": "Alchemy", + "dotdungeon.actor.pc.skill.arcanum": "Arcanum", + "dotdungeon.actor.pc.skill.dreams": "Dreams", + "dotdungeon.actor.pc.skill.lore": "Lore", + "dotdungeon.actor.pc.skill.navigation": "Navigation", - "dotdungeon.skills.presence": "Presence", - "dotdungeon.skill.animal_handling": "Animal Handling", - "dotdungeon.skill.perception": "Perception", - "dotdungeon.skill.sneak": "Sneak", - "dotdungeon.skill.speech": "Speech", - "dotdungeon.skill.vibes": "Vibes", + "dotdungeon.actor.pc.skills.presence": "Presence", + "dotdungeon.actor.pc.skill.animal_handling": "Animal Handling", + "dotdungeon.actor.pc.skill.perception": "Perception", + "dotdungeon.actor.pc.skill.sneak": "Sneak", + "dotdungeon.actor.pc.skill.speech": "Speech", + "dotdungeon.actor.pc.skill.vibes": "Vibes", - "dotdungeon.skills.hands": "Hands", - "dotdungeon.skill.accuracy": "Accuracy", - "dotdungeon.skill.crafting": "Crafting", - "dotdungeon.skill.engineering": "Engineering", - "dotdungeon.skill.explosives": "Explosives", - "dotdungeon.skill.piloting": "Piloting", + "dotdungeon.actor.pc.skills.hands": "Hands", + "dotdungeon.actor.pc.skill.accuracy": "Accuracy", + "dotdungeon.actor.pc.skill.crafting": "Crafting", + "dotdungeon.actor.pc.skill.engineering": "Engineering", + "dotdungeon.actor.pc.skill.explosives": "Explosives", + "dotdungeon.actor.pc.skill.piloting": "Piloting", - "dotdungeon.aria.skill-dropdown": "Your expertise level in the {skill} skill" + "dotdungeon.item.aspect.name": "Name", + "dotdungeon.item.aspect.duration": "Duration (seconds)", + "dotdungeon.item.aspect.description": "Description", + "dotdungeon.item.aspect.send-to-chat": "Send Aspect to Chat", + + "dotdungeon.aria.skill-dropdown": "Your expertise level in the {skill} skill", + "dotdungeon.aria.aspect-description": "The information on how the aspect works" } \ No newline at end of file diff --git a/module/handlebars.mjs b/module/handlebars.mjs index 82d23d6..5a9f119 100644 --- a/module/handlebars.mjs +++ b/module/handlebars.mjs @@ -9,7 +9,8 @@ export const partials = [ export async function registerHandlebarsHelpers() { Handlebars.registerHelper({ - "dotdungeon-array": createArray + "dotdungeon-array": createArray, + "dotdungeon-toFriendlyDuration": toFriendlyDuration, }); }; @@ -33,4 +34,30 @@ export async function preloadHandlebarsTemplates() { function createArray(...args) { return args.slice(0, -1); -}; \ No newline at end of file +}; + + +const secondsInAMinute = 60; +const secondsInAnHour = 60 * secondsInAMinute; +/** + * Converts a duration into a more human-friendly format + * @param {number} duration The length of time in seconds + * @returns The human-friendly time string + */ +function toFriendlyDuration(duration) { + let friendly = ``; + if (duration >= secondsInAnHour) { + let hours = Math.floor(duration / secondsInAnHour); + friendly += `${hours}h`; + duration -= hours * secondsInAnHour; + }; + if (duration >= secondsInAMinute) { + let minutes = Math.floor(duration / secondsInAMinute); + friendly += `${minutes}m`; + duration -= minutes * secondsInAMinute; + }; + if (duration > 0) { + friendly += `${duration}s`; + }; + return friendly; +} \ No newline at end of file diff --git a/module/models/AspectItemData.mjs b/module/models/AspectItemData.mjs index 4ba41bd..f434ff1 100644 --- a/module/models/AspectItemData.mjs +++ b/module/models/AspectItemData.mjs @@ -2,7 +2,6 @@ export class AspectItemData extends foundry.abstract.DataModel { static defineSchema() { const fields = foundry.data.fields; return { - name: new fields.HTMLField({ nullable: true, blank: false, trim: true }), used: new fields.BooleanField(), /** The number of seconds that the effect of the aspect stays */ deactivateAfter: new fields.NumberField({ nullable: true }), diff --git a/module/sheets/AspectSheet.mjs b/module/sheets/AspectSheet.mjs index 85aa013..521c0b6 100644 --- a/module/sheets/AspectSheet.mjs +++ b/module/sheets/AspectSheet.mjs @@ -12,10 +12,20 @@ export class AspectSheet extends ItemSheet { return opts; }; + activateListeners(html) { + super.activateListeners(html); + + if (this.document.isEmbedded) return; + if (!this.isEditable) return; + console.debug(`.dungeon | Adding event listeners for Item: ${this.id}`); + }; + getData() { - const ctx = super.getData(); + const ctx = {}; const item = this.item.toObject(false); + ctx.name = super.name; + ctx.item = item; ctx.system = item.system; ctx.flags = item.flags; diff --git a/module/sheets/PlayerSheet.mjs b/module/sheets/PlayerSheet.mjs index ea4ee57..845da1a 100644 --- a/module/sheets/PlayerSheet.mjs +++ b/module/sheets/PlayerSheet.mjs @@ -13,6 +13,7 @@ export class PlayerSheet extends ActorSheet { activateListeners(html) { super.activateListeners(html); + if (this.document.isEmbedded) return; if (!this.isEditable) return; console.debug(`.dungeon | Adding event listeners for Actor: ${this.id}`); @@ -33,4 +34,4 @@ export class PlayerSheet extends ActorSheet { console.groupEnd(); return ctx; }; -} \ No newline at end of file +}; \ No newline at end of file diff --git a/references/dnd5e-templates.json b/references/dnd5e-templates.json new file mode 100644 index 0000000..7dd30de --- /dev/null +++ b/references/dnd5e-templates.json @@ -0,0 +1,829 @@ +{ + "Actor": { + "types": [ + "character", + "npc", + "vehicle", + "group" + ], + "templates": { + "common": { + "abilities": { + "str": { + "value": 10, + "proficient": 0, + "max": null, + "bonuses": { + "check": "", + "save": "" + } + }, + "dex": { + "value": 10, + "proficient": 0, + "max": null, + "bonuses": { + "check": "", + "save": "" + } + }, + "con": { + "value": 10, + "proficient": 0, + "max": null, + "bonuses": { + "check": "", + "save": "" + } + }, + "int": { + "value": 10, + "proficient": 0, + "max": null, + "bonuses": { + "check": "", + "save": "" + } + }, + "wis": { + "value": 10, + "proficient": 0, + "max": null, + "bonuses": { + "check": "", + "save": "" + } + }, + "cha": { + "value": 10, + "proficient": 0, + "max": null, + "bonuses": { + "check": "", + "save": "" + } + } + }, + "attributes": { + "ac": { + "flat": null, + "calc": "default", + "formula": "" + }, + "hp": { + "value": 10, + "max": null, + "temp": 0, + "tempmax": 0 + }, + "init": { + "ability": "", + "bonus": "" + }, + "movement": { + "burrow": null, + "climb": null, + "fly": null, + "swim": null, + "walk": null, + "units": null, + "hover": false + } + }, + "details": { + "biography": { + "value": "", + "public": "" + } + }, + "traits": { + "size": "med", + "di": { + "value": [], + "bypasses": [], + "custom": "" + }, + "dr": { + "value": [], + "bypasses": [], + "custom": "" + }, + "dv": { + "value": [], + "bypasses": [], + "custom": "" + }, + "ci": { + "value": [], + "custom": "" + } + }, + "currency": { + "pp": 0, + "gp": 0, + "ep": 0, + "sp": 0, + "cp": 0 + } + }, + "creature": { + "attributes": { + "attunement": { + "max": 3 + }, + "senses": { + "darkvision": null, + "blindsight": null, + "tremorsense": null, + "truesight": null, + "units": null, + "special": "" + }, + "spellcasting": "int" + }, + "details": { + "alignment": "", + "race": "" + }, + "skills": { + "acr": { + "value": 0, + "ability": "dex", + "bonuses": { + "check": "", + "passive": "" + } + }, + "ani": { + "value": 0, + "ability": "wis", + "bonuses": { + "check": "", + "passive": "" + } + }, + "arc": { + "value": 0, + "ability": "int", + "bonuses": { + "check": "", + "passive": "" + } + }, + "ath": { + "value": 0, + "ability": "str", + "bonuses": { + "check": "", + "passive": "" + } + }, + "dec": { + "value": 0, + "ability": "cha", + "bonuses": { + "check": "", + "passive": "" + } + }, + "his": { + "value": 0, + "ability": "int", + "bonuses": { + "check": "", + "passive": "" + } + }, + "ins": { + "value": 0, + "ability": "wis", + "bonuses": { + "check": "", + "passive": "" + } + }, + "itm": { + "value": 0, + "ability": "cha", + "bonuses": { + "check": "", + "passive": "" + } + }, + "inv": { + "value": 0, + "ability": "int", + "bonuses": { + "check": "", + "passive": "" + } + }, + "med": { + "value": 0, + "ability": "wis", + "bonuses": { + "check": "", + "passive": "" + } + }, + "nat": { + "value": 0, + "ability": "int", + "bonuses": { + "check": "", + "passive": "" + } + }, + "prc": { + "value": 0, + "ability": "wis", + "bonuses": { + "check": "", + "passive": "" + } + }, + "prf": { + "value": 0, + "ability": "cha", + "bonuses": { + "check": "", + "passive": "" + } + }, + "per": { + "value": 0, + "ability": "cha", + "bonuses": { + "check": "", + "passive": "" + } + }, + "rel": { + "value": 0, + "ability": "int", + "bonuses": { + "check": "", + "passive": "" + } + }, + "slt": { + "value": 0, + "ability": "dex", + "bonuses": { + "check": "", + "passive": "" + } + }, + "ste": { + "value": 0, + "ability": "dex", + "bonuses": { + "check": "", + "passive": "" + } + }, + "sur": { + "value": 0, + "ability": "wis", + "bonuses": { + "check": "", + "passive": "" + } + } + }, + "tools": {}, + "traits": { + "languages": { + "value": [], + "custom": "" + } + }, + "spells": {}, + "bonuses": { + "mwak": { + "attack": "", + "damage": "" + }, + "rwak": { + "attack": "", + "damage": "" + }, + "msak": { + "attack": "", + "damage": "" + }, + "rsak": { + "attack": "", + "damage": "" + }, + "abilities": { + "check": "", + "save": "", + "skill": "" + }, + "spell": { + "dc": "" + } + } + } + }, + "htmlFields": [ + "details.biography.value", + "details.biography.public", + "description.full", + "description.summary" + ], + "character": { + "templates": [ + "common", + "creature" + ], + "attributes": { + "hp": { + "value": 0, + "bonuses": { + "level": "", + "overall": "" + } + }, + "death": { + "success": 0, + "failure": 0 + }, + "exhaustion": 0, + "inspiration": false + }, + "details": { + "background": "", + "originalClass": "", + "xp": { + "value": 0 + }, + "appearance": "", + "trait": "", + "ideal": "", + "bond": "", + "flaw": "" + }, + "resources": { + "primary": { + "value": 0, + "max": 0, + "sr": false, + "lr": false, + "label": "" + }, + "secondary": { + "value": 0, + "max": 0, + "sr": false, + "lr": false, + "label": "" + }, + "tertiary": { + "value": 0, + "max": 0, + "sr": false, + "lr": false, + "label": "" + } + }, + "traits": { + "weaponProf": { + "value": [], + "custom": "" + }, + "armorProf": { + "value": [], + "custom": "" + }, + "toolProf": { + "value": [], + "custom": "" + } + } + }, + "npc": { + "templates": [ + "common", + "creature" + ], + "attributes": { + "hp": { + "max": 10, + "formula": "" + } + }, + "details": { + "type": { + "value": "", + "subtype": "", + "swarm": "", + "custom": "" + }, + "environment": "", + "cr": 1, + "spellLevel": 0, + "source": "" + }, + "resources": { + "legact": { + "value": 0, + "max": 0 + }, + "legres": { + "value": 0, + "max": 0 + }, + "lair": { + "value": false, + "initiative": null + } + } + }, + "vehicle": { + "templates": [ + "common" + ], + "vehicleType": "water", + "abilities": { + "int": { + "value": 0 + }, + "wis": { + "value": 0 + }, + "cha": { + "value": 0 + } + }, + "attributes": { + "ac": { + "calc": "flat", + "motionless": "" + }, + "actions": { + "stations": false, + "value": 0, + "thresholds": { + "2": null, + "1": null, + "0": null + } + }, + "hp": { + "value": null, + "dt": null, + "mt": null + }, + "capacity": { + "creature": "", + "cargo": 0 + } + }, + "traits": { + "size": "lg", + "dimensions": "", + "di": { + "value": [ + "poison", + "psychic" + ] + }, + "ci": { + "value": [ + "blinded", + "charmed", + "deafened", + "frightened", + "paralyzed", + "petrified", + "poisoned", + "stunned", + "unconscious" + ] + } + }, + "cargo": { + "crew": [], + "passengers": [] + } + }, + "group": { + "description": { + "full": "", + "summary": "" + }, + "members": [], + "attributes": { + "movement": { + "land": 0, + "water": 0, + "air": 0 + } + }, + "currency": { + "pp": 0, + "gp": 0, + "ep": 0, + "sp": 0, + "cp": 0 + } + } + }, + "Item": { + "types": [ + "weapon", + "equipment", + "consumable", + "tool", + "loot", + "race", + "background", + "class", + "subclass", + "spell", + "feat", + "backpack" + ], + "templates": { + "itemDescription": { + "description": { + "value": "", + "chat": "", + "unidentified": "" + }, + "source": {} + }, + "physicalItem": { + "quantity": 1, + "weight": 0, + "price": { + "value": 0, + "denomination": "gp" + }, + "attunement": 0, + "equipped": false, + "rarity": "", + "identified": true + }, + "activatedEffect": { + "activation": { + "type": "", + "cost": null, + "condition": "" + }, + "duration": { + "value": "", + "units": "" + }, + "cover": null, + "crewed": false, + "target": { + "value": null, + "width": null, + "units": "", + "type": "" + }, + "range": { + "value": null, + "long": null, + "units": "" + }, + "uses": { + "value": null, + "max": "", + "per": null, + "recovery": "" + }, + "consume": { + "type": "", + "target": null, + "amount": null + } + }, + "action": { + "ability": null, + "actionType": null, + "attackBonus": "", + "chatFlavor": "", + "critical": { + "threshold": null, + "damage": "" + }, + "damage": { + "parts": [], + "versatile": "" + }, + "formula": "", + "save": { + "ability": "", + "dc": null, + "scaling": "spell" + } + }, + "mountable": { + "armor": { + "value": null + }, + "hp": { + "value": null, + "max": null, + "dt": null, + "conditions": "" + } + } + }, + "htmlFields": [ + "description.value", + "description.chat", + "description.unidentified" + ], + "background": { + "templates": [ + "itemDescription" + ], + "advancement": [] + }, + "backpack": { + "templates": [ + "itemDescription", + "physicalItem" + ], + "capacity": { + "type": "weight", + "value": null, + "weightless": false + }, + "currency": { + "cp": 0, + "sp": 0, + "ep": 0, + "gp": 0, + "pp": 0 + } + }, + "class": { + "templates": [ + "itemDescription" + ], + "identifier": "", + "levels": 1, + "hitDice": "d6", + "hitDiceUsed": 0, + "advancement": [], + "spellcasting": { + "progression": "none", + "ability": "" + } + }, + "consumable": { + "templates": [ + "itemDescription", + "physicalItem", + "activatedEffect", + "action" + ], + "consumableType": "potion", + "uses": { + "autoDestroy": false + } + }, + "equipment": { + "templates": [ + "itemDescription", + "physicalItem", + "activatedEffect", + "action", + "mountable" + ], + "armor": { + "type": "light", + "value": null, + "dex": null + }, + "baseItem": "", + "speed": { + "value": null, + "conditions": "" + }, + "strength": null, + "stealth": false, + "proficient": null + }, + "feat": { + "templates": [ + "itemDescription", + "activatedEffect", + "action" + ], + "type": { + "value": "", + "subtype": "" + }, + "requirements": "", + "recharge": { + "value": null, + "charged": false + } + }, + "loot": { + "templates": [ + "itemDescription", + "physicalItem" + ] + }, + "tool": { + "templates": [ + "itemDescription", + "physicalItem" + ], + "toolType": "", + "baseItem": "", + "ability": "int", + "chatFlavor": "", + "proficient": null, + "bonus": "" + }, + "race": {}, + "spell": { + "templates": [ + "itemDescription", + "activatedEffect", + "action" + ], + "level": 1, + "school": "", + "components": { + "vocal": false, + "somatic": false, + "material": false, + "ritual": false, + "concentration": false + }, + "materials": { + "value": "", + "consumed": false, + "cost": 0, + "supply": 0 + }, + "preparation": { + "mode": "prepared", + "prepared": false + }, + "scaling": { + "mode": "none", + "formula": null + } + }, + "subclass": { + "templates": [ + "itemDescription" + ], + "identifier": "", + "classIdentifier": "", + "advancement": [], + "spellcasting": { + "progression": "none", + "ability": "" + } + }, + "weapon": { + "templates": [ + "itemDescription", + "physicalItem", + "activatedEffect", + "action", + "mountable" + ], + "weaponType": "simpleM", + "baseItem": "", + "properties": {}, + "proficient": null + } + }, + "JournalEntryPage": { + "types": [ + "class" + ], + "htmlFields": [ + "description.value", + "description.additionalHitPoints", + "description.additionalTraits", + "description.additionalEquipment", + "description.subclass" + ], + "class": { + "item": "", + "description": { + "value": "", + "additionalHitPoints": "", + "additionalTraits": "", + "additionalEquipment": "", + "subclass": "" + }, + "subclassHeader": "", + "subclassItems": [] + } + } +} \ No newline at end of file diff --git a/references/simple.js b/references/simple.js new file mode 100644 index 0000000..671b8ef --- /dev/null +++ b/references/simple.js @@ -0,0 +1,172 @@ +/** + * A simple and flexible system for world-building using an arbitrary collection of character and item attributes + * Author: Atropos + */ + +// Import Modules +import { SimpleActor } from "./actor.js"; +import { SimpleItem } from "./item.js"; +import { SimpleItemSheet } from "./item-sheet.js"; +import { SimpleActorSheet } from "./actor-sheet.js"; +import { preloadHandlebarsTemplates } from "./templates.js"; +import { createWorldbuildingMacro } from "./macro.js"; +import { SimpleToken, SimpleTokenDocument } from "./token.js"; + +/* -------------------------------------------- */ +/* Foundry VTT Initialization */ +/* -------------------------------------------- */ + +/** + * Init hook. + */ +Hooks.once("init", async function() { + console.log(`Initializing Simple Worldbuilding System`); + + /** + * Set an initiative formula for the system. This will be updated later. + * @type {String} + */ + CONFIG.Combat.initiative = { + formula: "1d20", + decimals: 2 + }; + + game.worldbuilding = { + SimpleActor, + createWorldbuildingMacro + }; + + // Define custom Document classes + CONFIG.Actor.documentClass = SimpleActor; + CONFIG.Item.documentClass = SimpleItem; + CONFIG.Token.documentClass = SimpleTokenDocument; + CONFIG.Token.objectClass = SimpleToken; + + // Register sheet application classes + Actors.unregisterSheet("core", ActorSheet); + Actors.registerSheet("worldbuilding", SimpleActorSheet, { makeDefault: true }); + Items.unregisterSheet("core", ItemSheet); + Items.registerSheet("worldbuilding", SimpleItemSheet, { makeDefault: true }); + + // Register system settings + game.settings.register("worldbuilding", "macroShorthand", { + name: "SETTINGS.SimpleMacroShorthandN", + hint: "SETTINGS.SimpleMacroShorthandL", + scope: "world", + type: Boolean, + default: true, + config: true + }); + + // Register initiative setting. + game.settings.register("worldbuilding", "initFormula", { + name: "SETTINGS.SimpleInitFormulaN", + hint: "SETTINGS.SimpleInitFormulaL", + scope: "world", + type: String, + default: "1d20", + config: true, + onChange: formula => _simpleUpdateInit(formula, true) + }); + + // Retrieve and assign the initiative formula setting. + const initFormula = game.settings.get("worldbuilding", "initFormula"); + _simpleUpdateInit(initFormula); + + /** + * Update the initiative formula. + * @param {string} formula - Dice formula to evaluate. + * @param {boolean} notify - Whether or not to post nofications. + */ + function _simpleUpdateInit(formula, notify = false) { + const isValid = Roll.validate(formula); + if ( !isValid ) { + if ( notify ) ui.notifications.error(`${game.i18n.localize("SIMPLE.NotifyInitFormulaInvalid")}: ${formula}`); + return; + } + CONFIG.Combat.initiative.formula = formula; + } + + /** + * Slugify a string. + */ + Handlebars.registerHelper('slugify', function(value) { + return value.slugify({strict: true}); + }); + + // Preload template partials + await preloadHandlebarsTemplates(); +}); + +/** + * Macrobar hook. + */ +Hooks.on("hotbarDrop", (bar, data, slot) => createWorldbuildingMacro(data, slot)); + +/** + * Adds the actor template context menu. + */ +Hooks.on("getActorDirectoryEntryContext", (html, options) => { + + // Define an actor as a template. + options.push({ + name: game.i18n.localize("SIMPLE.DefineTemplate"), + icon: '', + condition: li => { + const actor = game.actors.get(li.data("documentId")); + return !actor.isTemplate; + }, + callback: li => { + const actor = game.actors.get(li.data("documentId")); + actor.setFlag("worldbuilding", "isTemplate", true); + } + }); + + // Undefine an actor as a template. + options.push({ + name: game.i18n.localize("SIMPLE.UnsetTemplate"), + icon: '', + condition: li => { + const actor = game.actors.get(li.data("documentId")); + return actor.isTemplate; + }, + callback: li => { + const actor = game.actors.get(li.data("documentId")); + actor.setFlag("worldbuilding", "isTemplate", false); + } + }); +}); + +/** + * Adds the item template context menu. + */ +Hooks.on("getItemDirectoryEntryContext", (html, options) => { + + // Define an item as a template. + options.push({ + name: game.i18n.localize("SIMPLE.DefineTemplate"), + icon: '', + condition: li => { + const item = game.items.get(li.data("documentId")); + return !item.isTemplate; + }, + callback: li => { + const item = game.items.get(li.data("documentId")); + item.setFlag("worldbuilding", "isTemplate", true); + } + }); + + // Undefine an item as a template. + options.push({ + name: game.i18n.localize("SIMPLE.UnsetTemplate"), + icon: '', + condition: li => { + const item = game.items.get(li.data("documentId")); + return item.isTemplate; + }, + callback: li => { + const item = game.items.get(li.data("documentId")); + item.setFlag("worldbuilding", "isTemplate", false); + } + }); +}); \ No newline at end of file diff --git a/styles/vars.scss b/styles/_vars.scss similarity index 72% rename from styles/vars.scss rename to styles/_vars.scss index d1541ff..18930d7 100644 --- a/styles/vars.scss +++ b/styles/_vars.scss @@ -1,2 +1,3 @@ $title-font: 'Pixelify Sans', sans-serif; +$body-font: sans-serif; $background: #f2f2f2; \ No newline at end of file diff --git a/styles/mixins/_breakpoints.scss b/styles/mixins/_breakpoints.scss index 2e88c28..4041288 100644 --- a/styles/mixins/_breakpoints.scss +++ b/styles/mixins/_breakpoints.scss @@ -1,16 +1,10 @@ @mixin bp-m { - @container (max-width: 700px) { + @container (max-width: 620px) { @content }; }; @mixin bp-s { - @container (max-width: 550px) { - @content - }; -}; - -@mixin bp-xs { @container (max-width: 400px) { @content }; diff --git a/styles/mixins/_foundry.scss b/styles/mixins/_foundry.scss index 20a6f9a..314c95f 100644 --- a/styles/mixins/_foundry.scss +++ b/styles/mixins/_foundry.scss @@ -1,3 +1,5 @@ @mixin fvtt_reset { all: initial; + display: block; + box-sizing: border-box; } \ No newline at end of file diff --git a/styles/mixins/_partials.scss b/styles/mixins/_partials.scss new file mode 100644 index 0000000..b9763a3 --- /dev/null +++ b/styles/mixins/_partials.scss @@ -0,0 +1,12 @@ +@mixin input-generic { + border-width: 2px; + border-radius: 4px; + border-style: solid; + border-color: rgba(0,0,0, 0.4); + background-color: rgba(0,0,0, 0.1); + + &:focus, + &:active { + border-color: rgba(0,0,0, 1); + } +} \ No newline at end of file diff --git a/styles/root.scss b/styles/root.scss index 8b7421c..756948a 100644 --- a/styles/root.scss +++ b/styles/root.scss @@ -1,3 +1,4 @@ +@use "./mixins/partials" as *; @use "./mixins/foundry" as *; @use "./vars.scss" as *; @@ -5,6 +6,14 @@ // Reset the parts of Foundry's styling which gets in the way of what I want .dotdungeon > .window-content { + + ::-webkit-scrollbar { + width: 10px; + } + ::-webkit-scrollbar-thumb { + border-radius: 5px; + } + h2, h3, h4, h5, h6 { @include fvtt_reset; display: block; @@ -12,12 +21,24 @@ margin: 0; } + label { + display: block; + } + button, button:hover { @include fvtt_reset; font-family: inherit; cursor: pointer; } + input[type="text"], + input[type="number"], + textarea { + @include fvtt_reset; + padding: 5px 7px; + @include input-generic; + } + select, select:hover { cursor: pointer; } @@ -36,4 +57,5 @@ @import "./sheets/partials/stat.scss"; @import "./sheets/partials/skill.scss"; -@import "./sheets/actor/mvp.scss"; \ No newline at end of file +@import "./sheets/actor/mvp.scss"; +@import "./sheets/items/aspect.scss"; \ No newline at end of file diff --git a/styles/sheets/actor/mvp.scss b/styles/sheets/actor/mvp.scss index 9fd0fc3..9492312 100644 --- a/styles/sheets/actor/mvp.scss +++ b/styles/sheets/actor/mvp.scss @@ -105,7 +105,7 @@ } -@include bp-s { +@include bp-m { .dotdungeon { .actor--pc { grid-template-columns: repeat(2, minmax(0, 1fr)); @@ -140,16 +140,34 @@ } } -@include bp-xs { +@include bp-s { .dotdungeon { .actor--pc { grid-template-columns: 1fr; + grid-template-rows: repeat(12, min-content); grid-template-areas: + "avatar" "stats" - "skills"; + "sync" + "skills" + "aspect" + "roles" + "backpack" + "weapons" + "spells" + "mounts" + "summons" + "storage"; } .panel { background: blueviolet; + + &__header { + .icon { + display: none; + visibility: hidden; + } + } } } } \ No newline at end of file diff --git a/styles/sheets/items/aspect.scss b/styles/sheets/items/aspect.scss new file mode 100644 index 0000000..72f8421 --- /dev/null +++ b/styles/sheets/items/aspect.scss @@ -0,0 +1,18 @@ +@use "../../vars" as *; + +.dotdungeon .item--aspect { + padding: 4px; + + input[type=text] { + font-family: $body-font; + font-size: 1.5em; + height: 1.5em; + width: 100%; + } + + textarea { + font-family: $body-font; + width: 100%; + resize: vertical; + } +} \ No newline at end of file diff --git a/templates/actors/char-sheet-mvp/partials/panels/skills.hbs b/templates/actors/char-sheet-mvp/partials/panels/skills.hbs index 6d09cc8..81f23b6 100644 --- a/templates/actors/char-sheet-mvp/partials/panels/skills.hbs +++ b/templates/actors/char-sheet-mvp/partials/panels/skills.hbs @@ -1,23 +1,23 @@