diff --git a/css/vermine2047.css b/css/vermine2047.css index 79aa8d5..577c924 100644 --- a/css/vermine2047.css +++ b/css/vermine2047.css @@ -143,6 +143,8 @@ ul.unstyled li { .actor.sheet .form .characteristics h4 { font-size:1.25rem; } + +.actor.sheet .form .tab.totem h4, .actor.sheet .form .tab.equipment h4, .actor.sheet .form .tab.stories h4 { margin-top:0.875rem; diff --git a/lang/en.json b/lang/en.json index b9731b8..619cf5b 100644 --- a/lang/en.json +++ b/lang/en.json @@ -68,7 +68,12 @@ "traits": "Traits", "clew": "Indice", "combat": "Combat", - "stories": "Histoires" + "stories": "Histoires", + "gear": "Matériel", + "information": "Informations", + "boost": "boost", + "group_abilities": "Capacités de groupe", + "totem_abilities": "Capacités de totem" }, "ITEMS": { "defense": "Protection", @@ -83,7 +88,8 @@ "abilities": "Capacités", "specialties": "Spécialités", "evolution": "Adaptation", - "evolutions": "Adaptations" + "evolutions": "Adaptations", + "vehicles": "Véhicules" }, "ABILITIES": { "vigor": { "name": "Vigueur"}, diff --git a/module/sheets/actor-sheet.mjs b/module/sheets/actor-sheet.mjs index 849cd14..7d4d32b 100644 --- a/module/sheets/actor-sheet.mjs +++ b/module/sheets/actor-sheet.mjs @@ -48,7 +48,17 @@ export class VermineActorSheet extends ActorSheet { // Prepare NPC data and items. if (actorData.type == 'npc') { - this._prepareItems(context); + this._prepareNpcItems(context); + } + + // Prepare Group data and items. + if (actorData.type == 'group') { + this._prepareGroupItems(context); + } + + // Prepare Creature data and items. + if (actorData.type == 'npc') { + this._prepareCreatureItems(context); } // Add roll data for TinyMCE editors. @@ -180,6 +190,116 @@ export class VermineActorSheet extends ActorSheet { } + /** + * Organize and classify Items for Npc sheets. + * + * @param {Object} actorData The actor to prepare. + * + * @return {undefined} + */ + _prepareNpcItems(context) { + // Initialize containers. + const gear = []; + const traits = []; + + + // Iterate through items, allocating to containers + for (let i of context.items) { + i.img = i.img || DEFAULT_TOKEN; + // Append to gear. + if (i.type === 'item') { + gear.push(i); + } + else if (i.type === 'trait') { + traits.push(i); + } + + + } + + // Assign and return + context.gear = gear; + + } + + + /** + * Organize and classify Items for Group sheets. + * + * @param {Object} actorData The actor to prepare. + * + * @return {undefined} + */ + _prepareGroupItems(context) { + // Initialize containers. + const gear = []; + const defenses = []; + const abilities = []; + const weapons = []; + const vehicles = []; + + // Iterate through items, allocating to containers + for (let i of context.items) { + i.img = i.img || DEFAULT_TOKEN; + // Append to gear. + if (i.type === 'item') { + gear.push(i); + } + else if (i.type === 'defense') { + defenses.push(i); + } + else if (i.type === 'weapon') { + weapons.push(i); + } + else if (i.type === 'ability') { + abilities.push(i); + } + else if (i.type === 'vehicle') { + vehicles.push(i); + } + + + } + + // Assign and return + context.gear = gear; + context.weapons = weapons; + context.defenses = defenses; + context.abilities = abilities; + context.vehicles = vehicles; + } + + /** + * Organize and classify Items for Creature sheets. + * + * @param {Object} actorData The actor to prepare. + * + * @return {undefined} + */ + _prepareCreatureItems(context) { + // Initialize containers. + const gear = []; + const traits = []; + + + // Iterate through items, allocating to containers + for (let i of context.items) { + i.img = i.img || DEFAULT_TOKEN; + // Append to gear. + if (i.type === 'item') { + gear.push(i); + } + else if (i.type === 'trait') { + traits.push(i); + } + + + } + + // Assign and return + context.gear = gear; + + } async _onItemCreate(event) { event.preventDefault(); const header = event.currentTarget; diff --git a/module/sheets/npc-group.mjs b/module/sheets/npc-group.mjs new file mode 100644 index 0000000..921669a --- /dev/null +++ b/module/sheets/npc-group.mjs @@ -0,0 +1,231 @@ +import {onManageActiveEffect, prepareActiveEffectCategories} from "../system/effects.mjs"; +import { VermineActorSheet } from "./actor-sheet.mjs"; + +/** + * Extend the basic ActorSheet with some very simple modifications + * @extends {VermineActorSheet} + */ +export class VermineGroupSheet extends VermineActorSheet { + + /** @override */ + static get defaultOptions() { + return mergeObject(super.defaultOptions, { + classes: ["vermine2047", "sheet", "actor"], + template: "systems/vermine2047/templates/actor/actor-sheet.html", + width: 600, + height: 600, + tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "features" }] + }); + } + + /** @override */ + get template() { + return `systems/vermine2047/templates/actor/actor-${this.actor.type}-sheet.html`; + } + + /* -------------------------------------------- */ + + /** @override */ + getData() { + // Retrieve the data structure from the base sheet. You can inspect or log + // the context variable to see the structure, but some key properties for + // sheets are the actor object, the data object, whether or not it's + // editable, the items array, and the effects array. + const context = super.getData(); + + // Use a safe clone of the actor data for further operations. + const actorData = this.actor.toObject(false); + + // Add the actor's data to context.data for easier access, as well as flags. + context.system = actorData.system; + context.flags = actorData.flags; + context.config = CONFIG.VERMINE; + + // Prepare character data and items. + if (actorData.type == 'character') { + this._prepareItems(context); + this._prepareCharacterData(context); + } + + // Prepare NPC data and items. + if (actorData.type == 'npc') { + this._prepareItems(context); + } + + // Add roll data for TinyMCE editors. + context.rollData = context.actor.getRollData(); + + // Prepare active effects + context.effects = prepareActiveEffectCategories(this.actor.effects); + + return context; + } + + /** + * Organize and classify Items for Character sheets. + * + * @param {Object} actorData The actor to prepare. + * + * @return {undefined} + */ + _prepareCharacterData(context) { + // Handle ability scores. + for (let [k, v] of Object.entries(context.system.abilities)) { + v.label = game.i18n.localize(context.system.abilities[k].label) ?? k; + } + } + + /** + * Organize and classify Items for Character sheets. + * + * @param {Object} actorData The actor to prepare. + * + * @return {undefined} + */ + _prepareItems(context) { + // Initialize containers. + const gear = []; + const features = []; + const spells = { + 0: [], + 1: [], + 2: [], + 3: [], + 4: [], + 5: [], + 6: [], + 7: [], + 8: [], + 9: [] + }; + + // Iterate through items, allocating to containers + for (let i of context.items) { + i.img = i.img || DEFAULT_TOKEN; + // Append to gear. + if (i.type === 'item') { + gear.push(i); + } + // Append to features. + else if (i.type === 'feature') { + features.push(i); + } + // Append to spells. + else if (i.type === 'spell') { + if (i.system.spellLevel != undefined) { + spells[i.system.spellLevel].push(i); + } + } + } + + // Assign and return + context.gear = gear; + context.features = features; + context.spells = spells; + } + + /* -------------------------------------------- */ + + /** @override */ + activateListeners(html) { + super.activateListeners(html); + + // Render the item sheet for viewing/editing prior to the editable check. + html.find('.item-edit').click(ev => { + const li = $(ev.currentTarget).parents(".item"); + const item = this.actor.items.get(li.data("itemId")); + item.sheet.render(true); + }); + + // ------------------------------------------------------------- + // Everything below here is only needed if the sheet is editable + if (!this.isEditable) return; + + // Add Inventory Item + html.find('.item-create').click(this._onItemCreate.bind(this)); + + // Delete Inventory Item + html.find('.item-delete').click(ev => { + const li = $(ev.currentTarget).parents(".item"); + const item = this.actor.items.get(li.data("itemId")); + item.delete(); + li.slideUp(200, () => this.render(false)); + }); + + // Active Effect management + html.find(".effect-control").click(ev => onManageActiveEffect(ev, this.actor)); + + // Rollable abilities. + html.find('.rollable').click(this._onRoll.bind(this)); + + // Drag events for macros. + if (this.actor.isOwner) { + let handler = ev => this._onDragStart(ev); + html.find('li.item').each((i, li) => { + if (li.classList.contains("inventory-header")) return; + li.setAttribute("draggable", true); + li.addEventListener("dragstart", handler, false); + }); + } + } + + /** + * Handle creating a new Owned Item for the actor using initial data defined in the HTML dataset + * @param {Event} event The originating click event + * @private + */ + async _onItemCreate(event) { + event.preventDefault(); + const header = event.currentTarget; + // Get the type of item to create. + const type = header.dataset.type; + // Grab any data associated with this control. + const data = duplicate(header.dataset); + // Initialize a default name. + const name = `New ${type.capitalize()}`; + // Prepare the item object. + const itemData = { + name: name, + type: type, + system: data + }; + // Remove the type from the dataset since it's in the itemData.type prop. + delete itemData.system["type"]; + + // Finally, create the item! + return await Item.create(itemData, {parent: this.actor}); + } + + /** + * Handle clickable rolls. + * @param {Event} event The originating click event + * @private + */ + _onRoll(event) { + event.preventDefault(); + const element = event.currentTarget; + const dataset = element.dataset; + + // Handle item rolls. + if (dataset.rollType) { + if (dataset.rollType == 'item') { + const itemId = element.closest('.item').dataset.itemId; + const item = this.actor.items.get(itemId); + if (item) return item.roll(); + } + } + + // Handle rolls that supply the formula directly. + if (dataset.roll) { + let label = dataset.label ? `[ability] ${dataset.label}` : ''; + let roll = new Roll(dataset.roll, this.actor.getRollData()); + roll.toMessage({ + speaker: ChatMessage.getSpeaker({ actor: this.actor }), + flavor: label, + rollMode: game.settings.get('core', 'rollMode'), + }); + return roll; + } + } + +} diff --git a/module/system/handlebars-manager.mjs b/module/system/handlebars-manager.mjs index 0c9ada6..8221f16 100644 --- a/module/system/handlebars-manager.mjs +++ b/module/system/handlebars-manager.mjs @@ -21,6 +21,12 @@ "systems/vermine2047/templates/actor/parts/actor-stories.hbs", "systems/vermine2047/templates/actor/parts/actor-effects.html", + // Group partials + "systems/vermine2047/templates/actor/parts/group-vehicles.hbs", + "systems/vermine2047/templates/actor/parts/group-info.hbs", + "systems/vermine2047/templates/actor/parts/group-items.hbs", + "systems/vermine2047/templates/actor/parts/group-experience.hbs", + // additional templates "systems/vermine2047/templates/roll.hbs", ]); diff --git a/module/system/hooks.mjs b/module/system/hooks.mjs index 22e6c89..28442e7 100644 --- a/module/system/hooks.mjs +++ b/module/system/hooks.mjs @@ -9,12 +9,12 @@ export const registerHooks = function () { }); // changement de la pause - /*Hooks.on("renderPause", async function () { + Hooks.on("renderPause", async function () { if ($("#pause").attr("class") !== "paused") return; $(".paused img").attr("src", 'systems/vermine2047/assets/images/ui/vermine_pause.webp'); $(".paused img").css({ "opacity": 1}); $("#pause.paused figcaption").text("Communauté endormie..."); - });*/ + }); // Hooks.on('renderChatLog', (log, html, data) => VermineFight.chatListeners(html)); // Hooks.on('renderChatMessage', (message, html, data) => VermineFight.chatMessageHandler(message, html, data)); diff --git a/module/vermine2047.mjs b/module/vermine2047.mjs index e994aea..ac713ef 100644 --- a/module/vermine2047.mjs +++ b/module/vermine2047.mjs @@ -6,6 +6,7 @@ import { VermineActor } from "./documents/actor.mjs"; import { VermineCharacterSheet } from "./sheets/character-sheet.mjs"; import { VermineNpcSheet } from "./sheets/npc-sheet.mjs"; +import { VermineGroupSheet } from "./sheets/npc-group.mjs"; import { VermineCreatureSheet } from "./sheets/creature-sheet.mjs"; import { VermineItem } from "./documents/item.mjs"; @@ -65,6 +66,11 @@ Hooks.once('init', async function() { types: ['creature'], makeDefault: true, }); // Register vehicle Sheet + + Actors.registerSheet('vermine2047', VermineGroupSheet, { + types: ['group'], + makeDefault: true, + }); // Register vehicle Sheet Items.unregisterSheet("core", ItemSheet); Items.registerSheet("vermine2047", VermineItemSheet, { makeDefault: true }); diff --git a/system.json b/system.json index ae98657..6f0f31f 100644 --- a/system.json +++ b/system.json @@ -2,7 +2,7 @@ "id": "vermine2047", "title": "Vermine 2047", "description": "The Vermine 2047 system for FoundryVTT!", - "version": "0.0.13", + "version": "0.0.14", "compatibility": { "minimum": 10, "verified": "10.287", diff --git a/template.json b/template.json index ddabcd6..f00a81a 100644 --- a/template.json +++ b/template.json @@ -25,7 +25,7 @@ "identity": { "height": 0, "weight": 0, - "vermine2047": "", + "totem": "", "age": 15, "profile": "", "origin": "", @@ -346,6 +346,29 @@ "max": 4 } }, + "group": { + "templates": ["base"], + "identity": { + "totem": "", + "profile": "", + "origin": "", + "theme": "", + "instincts": "", + "prohibits": "", + "major_objectives": "", + "minor_objectives": "", + "notes": "" + }, + "equipment": { + "description": "" + }, + "level": { + "value": 1, + "min": 1, + "max": 3 + }, + "members": [] + }, "creature": { "templates": ["base"], "age": 15, @@ -367,14 +390,15 @@ } }, "Item": { - "types": ["item", "weapon", "defense", "ability", "specialty", "trait", "background", "trauma", "evolution", "rumor"], + "types": ["item", "weapon", "defense", "vehicle", "ability", "specialty", "trait", "background", "trauma", "evolution", "rumor"], "templates": { "base": { "description": "", "rarity":3, "reliability":3, "quantity": 1, - "weight": 0 + "weight": 0, + "traits": [] }, "list": { "description": "" @@ -396,7 +420,14 @@ "mobility":3 }, "ability": { - "templates": ["list"] + "templates": ["list"], + "type": "character", + "level": { + "value": 1, + "min": 1, + "max": 3 + }, + "effects": [] }, "specialty": { "templates": ["list"] @@ -405,6 +436,9 @@ "templates": ["list"], "level":0 }, + "vehicle": { + "templates": ["base"] + }, "background": { "templates": ["list"] }, diff --git a/templates/actor/actor-group-sheet.html b/templates/actor/actor-group-sheet.html new file mode 100644 index 0000000..31ce37d --- /dev/null +++ b/templates/actor/actor-group-sheet.html @@ -0,0 +1,78 @@ +
+ + {{!-- Sheet Header --}} +
+ +
+

+ {{!-- The grid classes are defined in scss/global/_grid.scss. To use, + use both the "grid" and "grid-Ncol" class where "N" can be any number + from 1 to 12 and will create that number of columns. --}} +
+ + {{!-- "flex-group-center" is also defined in the _grid.scss file + and it will add a small amount of padding, a border, and will + center all of its child elements content and text. --}} +
+ +
+ + / + +
+
+ +
+ +
+ + / + +
+
+ +
+ +
+ + / + +
+
+ +
+
+
+ + {{!-- Sheet Tab Navigation --}} + + + {{!-- Sheet Body --}} +
+ + {{!-- Biography Tab --}} +
+

{{ localize 'VERMINE.information'}}

+ {{> "systems/vermine2047/templates/actor/parts/group-info.hbs"}} +
+ + {{!-- Owned Items Tab --}} +
+

{{ localize 'VERMINE.gear'}}

+ {{> "systems/vermine2047/templates/actor/parts/group-items.hbs"}} +
+ + {{!-- Active Effects Tab --}} +
+

{{ localize 'VERMINE.experience'}}

+ {{> "systems/vermine2047/templates/actor/parts/group-experience.hbs"}} +
+ +
+
+ diff --git a/templates/actor/parts/group-experience.hbs b/templates/actor/parts/group-experience.hbs new file mode 100644 index 0000000..3adfae4 --- /dev/null +++ b/templates/actor/parts/group-experience.hbs @@ -0,0 +1,53 @@ +
+
+

{{ localize 'VERMINE.boost'}}

+
    + {{#each abilities as |item id|}} +
  1. + +
    + + +
    +
  2. + {{/each}} +
+
+
+

{{ localize 'VERMINE.group_abilities'}}

+
    + {{#each specialties as |item id|}} +
  1. + +
    + + +
    +
  2. + {{/each}} +
+
+
+

{{ localize 'VERMINE.totem_abilities'}}

+
    + {{#each backgrounds as |item id|}} +
  1. + +
    + + +
    +
  2. + {{/each}} +
+
+
+

+ {{ localize 'VERMINE.pool' }} +

diff --git a/templates/actor/parts/group-info.hbs b/templates/actor/parts/group-info.hbs new file mode 100644 index 0000000..fb0c832 --- /dev/null +++ b/templates/actor/parts/group-info.hbs @@ -0,0 +1,83 @@ +
+
+

{{ localize 'ITEMS.abilities'}}

+
    + {{#each abilities as |item id|}} +
  1. + +
    + + +
    +
  2. + {{/each}} +
+
+
+

{{ localize 'ITEMS.specialties'}}

+
    + {{#each specialties as |item id|}} +
  1. + +
    + + +
    +
  2. + {{/each}} +
+
+
+

{{ localize 'ITEMS.backgrounds'}}

+
    + {{#each backgrounds as |item id|}} +
  1. + +
    + + +
    +
  2. + {{/each}} +
+
+
+

{{ localize 'ITEMS.traumas'}}

+
    + {{#each traumas as |item id|}} +
  1. + +
    + + +
    +
  2. + {{/each}} +
+
+
+

{{ localize 'ITEMS.evolutions'}}

+
    + {{#each evolutions as |item id|}} +
  1. + +
    + + +
    +
  2. + {{/each}} +
+
+
+ \ No newline at end of file diff --git a/templates/actor/parts/group-items.hbs b/templates/actor/parts/group-items.hbs new file mode 100644 index 0000000..ee40e9f --- /dev/null +++ b/templates/actor/parts/group-items.hbs @@ -0,0 +1,39 @@ +
+
+
    +
  1. +
    {{ localize 'IDENTITY.name'}}
    +
    {{ localize 'VERMINE.qty'}}
    +
    {{ localize 'VERMINE.weight'}}
    +
    + +
    +
  2. + {{#each gear as |item id|}} +
  3. +
    +
    + +
    + {{item.name}} +
    +

    {{item.system.quantity}}

    +

    {{item.system.weight}}

    +
    + +
    +
  4. + {{/each}} +
+
+
+

{{ localize 'IDENTITY.notes'}}

+ {{editor system.equipment.description target="system.equipment.description" button=true owner=owner editable=editable}} +
+
+

{{localize 'ITEMS.weapons'}}

+{{> "systems/vermine2047/templates/actor/parts/actor-weapons.hbs"}} +

{{localize 'ITEMS.defenses'}}

+{{> "systems/vermine2047/templates/actor/parts/actor-defenses.hbs"}} +

{{localize 'ITEMS.vehicles'}}

+{{> "systems/vermine2047/templates/actor/parts/group-vehicles.hbs"}} \ No newline at end of file diff --git a/templates/actor/parts/group-vehicles.hbs b/templates/actor/parts/group-vehicles.hbs new file mode 100644 index 0000000..c25dfcc --- /dev/null +++ b/templates/actor/parts/group-vehicles.hbs @@ -0,0 +1,27 @@ +
    +
  1. +
    {{ localize 'IDENTITY.name'}}
    +
    {{ localize 'VERMINE.mobility'}}
    +
    {{ localize 'VERMINE.rarity'}}
    +
    {{ localize 'VERMINE.reliability'}}
    +
    + +
    +
  2. + {{#each vehicles as |item id|}} +
  3. +
    +
    + +
    + {{item.name}} +
    +

    {{item.system.mobility}}

    +

    {{item.system.rarity}}

    +

    {{item.system.reliability}}

    +
    + +
    +
  4. + {{/each}} +
\ No newline at end of file