diff --git a/lang/en.json b/lang/en.json index 8930796..6f41842 100644 --- a/lang/en.json +++ b/lang/en.json @@ -1,10 +1,6 @@ { - "TOTEM.AbilityStrAbbr": "str", - "TOTEM.AbilityConAbbr": "con", - "TOTEM.AbilityDexAbbr": "dex", - "TOTEM.AbilityIntAbbr": "int", - "TOTEM.AbilityWisAbbr": "wis", - "TOTEM.AbilityChaAbbr": "cha", + "TOTEM.WorldSettings.GameMode.Name":"Choix du mode de jeu", + "TOTEM.WorldSettings.GameMode.Hint":"À l’image de certains jeux vidéo proposant différents, Vermine 2047 permet aux joueurs de choisir leur Mode de jeu et de fixer eux-mêmes le degré de réalisme, de surnaturel et de dangerosité de l’univers.", "TOTEM.level": "Niveau", "TOTEM.pool": "Réserve", diff --git a/module/sheets/actor-sheet.mjs b/module/sheets/actor-sheet.mjs index 53147b9..7278632 100644 --- a/module/sheets/actor-sheet.mjs +++ b/module/sheets/actor-sheet.mjs @@ -43,7 +43,6 @@ export class TotemActorSheet extends ActorSheet { // Prepare character data and items. if (actorData.type == 'character') { this._prepareItems(context); - this._prepareCharacterData(context); } // Prepare NPC data and items. @@ -60,171 +59,5 @@ export class TotemActorSheet extends ActorSheet { 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/sheets/character-sheet.mjs b/module/sheets/character-sheet.mjs index b3d6006..dac8392 100644 --- a/module/sheets/character-sheet.mjs +++ b/module/sheets/character-sheet.mjs @@ -20,7 +20,7 @@ export class TotemCharacterSheet extends TotemActorSheet { /** @override */ get template() { - return `systems/totem/templates/actor/actor-${this.actor.type}-sheet.html`; + return `systems/totem/templates/actor/actor-character-sheet.html`; } /* -------------------------------------------- */ @@ -205,7 +205,7 @@ export class TotemCharacterSheet extends TotemActorSheet { event.preventDefault(); const element = event.currentTarget; const dataset = element.dataset; - + console.log("Ceci est un jet d'un personnage joueur"); // Handle item rolls. if (dataset.rollType) { if (dataset.rollType == 'item') { @@ -217,14 +217,10 @@ export class TotemCharacterSheet extends TotemActorSheet { // 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; + const label = game.i18n.localize(dataset.label) ? `[ability] ${game.i18n.localize(dataset.label)}` : ''; + console.log($(element).attr('for'), this.actor.system.skills[$(element).attr('for').split('.')[2]].value); + const NoD = this.actor.system.skills[$(element).attr('for').split('.')[2]]?.value || 0 + return game.totem.TotemRoll.roll(this.actor.id, label, NoD, 0, {}); } } diff --git a/module/system/hooks.mjs b/module/system/hooks.mjs index 3894d34..735cd6e 100644 --- a/module/system/hooks.mjs +++ b/module/system/hooks.mjs @@ -1,4 +1,3 @@ -import { TotemFight } from './fight.mjs'; export const registerHooks = function () { /** @@ -34,7 +33,7 @@ export const registerHooks = function () { }); Hooks.on('getSceneControlButtons', (controls) => { - controls.find((c) => c.name === 'token').tools.push({ + /*controls.find((c) => c.name === 'token').tools.push({ name: 'Dice Roller', title: game.i18n.localize("TOTEM.RollTool"), icon: 'fas fa-dice-d6', @@ -42,7 +41,7 @@ export const registerHooks = function () { onClick() { TotemRoll.ui(); } - }); + });*/ }); /* -------------------------------------------- */ @@ -50,7 +49,7 @@ export const registerHooks = function () { /* -------------------------------------------- */ Hooks.on("preCreateActor", function (actor) { - console.log('pre create actor', actor); + // console.log('pre create actor', actor); if (actor.img == "icons/svg/mystery-man.svg") { // actor.updateSource({"img": `systems/totem/icons/actors/${actor.type}.webp`}); // item.updateSource({"img": `systems/totem/icons/competence.webp`}); @@ -90,6 +89,18 @@ export const registerHooks = function () { }*/ } }); + + /* Hooks.on("chatCommandsReady", function (chatCommands) { + chatCommands.registerCommand(chatCommands.createCommandFromData({ + commandKey: "/dr", + invokeOnCommand: (chatlog, messageText, chatdata) => { + Roll.get().parse(messageText); + }, + shouldDisplayToChat: false, + iconClass: "fa-dice-d6", + description: "Roll Totem check" + })); + });*/ } diff --git a/module/system/roll.js b/module/system/roll.js deleted file mode 100644 index cd8e706..0000000 --- a/module/system/roll.js +++ /dev/null @@ -1,284 +0,0 @@ -import { getActorSkillScore, updateActorSkillScore } from "./functions.mjs"; - -export class TotemRoll { - async performTest(dicePool, target, trait, usingSpecialization, difficulty, skill, params, actor) { - const r = new Roll(dicePool + 'd6'); - r.roll(); - let _trait = trait || 0; - let _usingSpecialization = usingSpecialization || 0; - let _skillLabel = (params.skill != undefined) ? game.i18n.format(params.skill) : ""; - let _used = (params.usure != undefined) ? params.usure : 0; - let diceString = ''; - let total = 0; - - // affichage des valeurs - let targetText = _skillLabel + ' : ' + skill + ' (+'+ _used +')'; - if (trait) - targetText += ', '+ game.i18n.format('TOTEM.Traits') + ' : ' + _trait; - if (_usingSpecialization != 0) - targetText += ', '+ game.i18n.format('TOTEM.UsingSpecialization'); - if (difficulty) - targetText += '
'+ game.i18n.format('TOTEM.Against') +': ' + Math.abs(difficulty); - - // affichage des jets - for (let i = 0; i < dicePool; i++) { - let result = r.terms[0].results[i].result; - if (result == 6) { - diceString += '
  • ' + result + '
  • '; - } - else if (result <= 5) { - diceString += '
  • ' + result + '
  • '; - } - else if (result >= 1) { - diceString += '
  • ' + result + '
  • '; - } - total += result; - } - - // Here we want to check if the success was exactly one (as "1 Successes" doesn't make grammatical sense). - // We create a string for the Successes. - let successText = ''; - let successMargin = 0; - - successMargin = total + skill + _trait + _usingSpecialization + _used + difficulty; - if (params.usure != undefined){ - successMargin += parseInt(params.usure,10); - } - - // console.log(total, skill, _trait, _usingSpecialization, difficulty, successMargin); - // règle de la MR qui ne peut pas dépasser la compétence - if (successMargin > skill){ - successMargin = skill; - } - - if (successMargin > target + 1) { - successText = game.i18n.localize('TOTEM.RollSuccess') + ' (' + game.i18n.localize('TOTEM.SM') + ' ' + successMargin.toString() +')'; - } else if (successMargin == target + 1) { - successText = game.i18n.localize('TOTEM.RollSuccess'); - } else if (successMargin == target) { - successText = game.i18n.localize('TOTEM.PartialSuccess'); - } else { - successText = game.i18n.localize('TOTEM.Failure'); - } - - - // Build a dynamic html using the variables from above. - const html = ` -
    -
    -
    -
    - ` + dicePool + `d6 -
    -
    -
    -
    -
    - ` + targetText + ` -
    -
    -
      ` + diceString + `
    -
    -
    -
    - Résultat -

    ` + (total + skill + _trait + _usingSpecialization + _used).toString() + `

    -
    -
    -
    ` + - `

    ` + successText + `

    -
    -
    -
    - `; - - // Check if the dice3d module exists (Dice So Nice). If it does, post a roll in that and then - // send to chat after the roll has finished. If not just send to chat. - if (game.dice3d) { - game.dice3d.showForRoll(r).then((displayed) => { - this.sendToChat(html, r, actor); - }); - } else { - this.sendToChat(html, r, actor); - }; - } - - async sendToChat(content, roll, actor) { - let conf = { - user: game.user._id, - content: content, - roll: roll, - sound: 'sounds/dice.wav' - }; - if (actor) - conf.speaker = ChatMessage.getSpeaker({ actor: actor }); - // Send's Chat Message to foundry, if items are missing they will appear as false or undefined and this not be rendered. - ChatMessage.create(conf).then((msg) => { - return msg; - }); - } - - static instance = null; - - static get() { - if (!TotemRoll.instance) - TotemRoll.instance = new TotemRoll(); - return TotemRoll.instance; - } - - // Parse XdYtZfAc || XdYsZfAc - // {size of dice pool}d{target number}(t|s)[{skill level - for trait}f][{complication range}c][D] - async parse(cmd, usingSpecialization) { - let actor = game.user.character; - if (canvas.tokens.controlled.length > 0) - actor = canvas.tokens.controlled[0].actor; - let r = cmd.match(/([2-5])d([01]?[0-9])[ts](([4-8])f)?((20|[1][5-9])c)?(D)?/); - if (r) { - //console.log(r); - let dicePool = +r[1]; - let target = +r[2]; - let trait = +r[4]; - if (!!r[7]) usingSpecialization = true; - let difficulty = +r[6]; - this.performTest(dicePool, target, trait, usingSpecialization, difficulty, actor); - } else - ui.notifications.error("Unparsable command: " + cmd); - } - - // data injected to char data - static previousValues = { - dicePool: 2 - }; - - static rollerTemplate = 'systems/totem/templates/roll.html'; - - /** - * main class function - * @returns - */ - static async ui(externalData = {}) { - let charData = (externalData) => { - return Object.assign({ _template: TotemRoll.rollerTemplate }, {...TotemRoll.previousValues, ...externalData}); - }; - - // get the actor - let actor = null; - - try { - let actor = game.user.character; - - if (canvas.tokens.controlled.length > 0) - actor = canvas.tokens.controlled[0].actor; - } catch (e) { - console.log(e); - } - - if (actor == null && externalData.speakerId != undefined && externalData.speakerId != null){ - // on récupère le speakerId, et de là l'objet actor - actor = game.actors.get(externalData.speakerId); - } - - // get the data - let data = charData(externalData); - console.log('npc2', data); - - if (actor.type != undefined){ - data.actor_type = actor.type; - if (actor.type == 'character'){ - data.skillMaxScore = getActorSkillScore(actor, data.skill); - data.skillScore = data.skillMaxScore - getActorSkillScore(actor, data.skill, 'spent'); - data.skillSpent = getActorSkillScore(actor, data.skill, 'spent'); - } else if(actor.type == 'npc'){ - - if (data.specialization == 1){ - //data.skillMaxScore = getActorSkillScore(actor, data.skill); - // data.skillScore = data.skillMaxScore; - } else { - // compétence, il faut récupérer le score du skill type - data.skillScore = data.value; - } - } - } - - // render template - let html = await renderTemplate(data._template, data); - - let ui = new Dialog({ - title: game.i18n.localize("TOTEM.RollTool"), - content: html, - buttons: { - roll: { - label: game.i18n.localize('TOTEM.RollDice'), - callback: (html) => { - let form = html.find('#dice-pool-form'); - if (!form[0].checkValidity()) { - throw "Invalid Data"; - } - let target = 0, trait, usingSpecialization, difficulty, skill = 0, params = {}; - form.serializeArray().forEach(e => { - switch (e.name) { - case "difficulty": - if (e.value != "") - difficulty = -e.value; - break; - case "skillLabel": - params.skill = e.value; - break; - case "usure": - params.usure = +e.value; - break; - case "skill": - skill = +e.value; - break; - case "trait": - trait = +e.value; - break; - case "usingSpecialization": - if (e.value && +e.value > 1) - usingSpecialization = +e.value; - break; - } - - // prise en compte de l'usure sur la feuille de perso - if (params.usure != undefined){ - updateActorSkillScore(actor, data.skill, 'spent', data.skillSpent + parseInt(params.usure,10)); - } - }); - return TotemRoll.get().performTest(data.dicePool, target, trait, usingSpecialization, difficulty, skill, params, actor); - } - }, - close: { - label: game.i18n.localize('Close'), - callback: () => { } - } - }, - render: function (h) { - h.find("#skills-radio input").change(function () { - let s = $(this).attr("data-skill"); - h.find(".trait-list .hidden").removeClass("show"); - let f = h.find(".trait-list ." + s); - f.addClass("show"); - if (f.length == 0) { - h.find(".use-trait input").attr("disabled", "disabled").prop("checked", false); - } else - h.find(".use-trait input").attr("disabled", null); - }); - } - }); - ui.render(true); - return ui; - } -} - - -Hooks.on("chatCommandsReady", function (chatCommands) { - chatCommands.registerCommand(chatCommands.createCommandFromData({ - commandKey: "/dr", - invokeOnCommand: (chatlog, messageText, chatdata) => { - TotemRoll.get().parse(messageText); - }, - shouldDisplayToChat: false, - iconClass: "fa-dice-d6", - description: "Roll Totem check" - })); -}); diff --git a/module/system/roll.mjs b/module/system/roll.mjs new file mode 100644 index 0000000..598f0ea --- /dev/null +++ b/module/system/roll.mjs @@ -0,0 +1,18 @@ +export class TotemRoll { + + + static roll(actorId, label, NoD, Mod, params = {}){ + const actor = game.actors.get(actorId); + let roll = new Roll(NoD + "d10+"+ Mod, actor.getRollData()); + roll.toMessage({ + speaker: ChatMessage.getSpeaker({ actor: actor }), + flavor: label, + rollMode: game.settings.get('core', 'rollMode'), + }); + return roll; + } + +} + + + diff --git a/module/system/settings.mjs b/module/system/settings.mjs index ca9dc36..61f4020 100644 --- a/module/system/settings.mjs +++ b/module/system/settings.mjs @@ -1,29 +1,18 @@ export const registerSettings = function () { - game.settings.register("totem", "game-level", { - name: game.i18n.localize("TOTEM.WorldSettings.GameLevel.Name"), - hint: game.i18n.localize("TOTEM.WorldSettings.GameLevel.Hint"), + game.settings.register("totem", "game-mode", { + name: game.i18n.localize("TOTEM.WorldSettings.GameMode.Name"), + hint: game.i18n.localize("TOTEM.WorldSettings.GameMode.Hint"), scope: "system", config: true, type: String, choices: { - "e": "Totem", - "c": "Céphale", - "b": "Bohème", - "a": "Amertume" + "1": "Survie", + "2": "Cauchemar", + "3": "Apocalypse" }, default: 'e', onChange: value => { - console.log(value); + // console.log(value); } }); - - game.settings.register("totem", "granting_cephalie", { - name: game.i18n.localize("TOTEM.WorldSettings.GrantingCephales.Label"), - hint: game.i18n.localize("TOTEM.WorldSettings.GrantingCephales.Description"), - scope: "system", - config: true, - type: Boolean, - default: !1 - }) - } \ No newline at end of file diff --git a/module/totem.mjs b/module/totem.mjs index 96163de..a22e478 100644 --- a/module/totem.mjs +++ b/module/totem.mjs @@ -14,6 +14,9 @@ import { TotemCreatureSheet } from "./sheets/creature-sheet.mjs"; import { TotemItem } from "./documents/item.mjs"; import { TotemItemSheet } from "./sheets/item-sheet.mjs"; +import { TotemRoll } from "./system/roll.mjs"; +import { TotemCombat } from "./system/fight.mjs"; + // Import helper/utility classes and constants. import { preloadHandlebarsTemplates, registerHandlebarsHelpers } from "./system/handlebars-manager.mjs"; import { TOTEM } from "./system/config.mjs"; @@ -31,7 +34,8 @@ Hooks.once('init', async function() { TotemNpc, TotemCreature, TotemItem, - rollItemMacro + TotemRoll, + TotemCombat }; // Add custom constants for configuration. @@ -76,91 +80,3 @@ Hooks.once('init', async function() { // Preload Handlebars templates. return preloadHandlebarsTemplates(); }); - -/* -------------------------------------------- */ -/* Handlebars Helpers */ -/* -------------------------------------------- */ - -// If you need to add Handlebars helpers, here are a few useful examples: -Handlebars.registerHelper('concat', function() { - var outStr = ''; - for (var arg in arguments) { - if (typeof arguments[arg] != 'object') { - outStr += arguments[arg]; - } - } - return outStr; -}); - -Handlebars.registerHelper('toLowerCase', function(str) { - return str.toLowerCase(); -}); - -/* -------------------------------------------- */ -/* Ready Hook */ -/* -------------------------------------------- */ - -Hooks.once("ready", async function() { - // Wait to register hotbar drop hook on ready so that modules could register earlier if they want to - Hooks.on("hotbarDrop", (bar, data, slot) => createItemMacro(data, slot)); -}); - -/* -------------------------------------------- */ -/* Hotbar Macros */ -/* -------------------------------------------- */ - -/** - * Create a Macro from an Item drop. - * Get an existing item macro if one exists, otherwise create a new one. - * @param {Object} data The dropped data - * @param {number} slot The hotbar slot to use - * @returns {Promise} - */ -async function createItemMacro(data, slot) { - // First, determine if this is a valid owned item. - if (data.type !== "Item") return; - if (!data.uuid.includes('Actor.') && !data.uuid.includes('Token.')) { - return ui.notifications.warn("You can only create macro buttons for owned Items"); - } - // If it is, retrieve it based on the uuid. - const item = await Item.fromDropData(data); - - // Create the macro command using the uuid. - const command = `game.totem.rollItemMacro("${data.uuid}");`; - let macro = game.macros.find(m => (m.name === item.name) && (m.command === command)); - if (!macro) { - macro = await Macro.create({ - name: item.name, - type: "script", - img: item.img, - command: command, - flags: { "totem.itemMacro": true } - }); - } - game.user.assignHotbarMacro(macro, slot); - return false; -} - -/** - * Create a Macro from an Item drop. - * Get an existing item macro if one exists, otherwise create a new one. - * @param {string} itemUuid - */ -function rollItemMacro(itemUuid) { - // Reconstruct the drop data so that we can load the item. - const dropData = { - type: 'Item', - uuid: itemUuid - }; - // Load the item from the uuid. - Item.fromDropData(dropData).then(item => { - // Determine if the item loaded and if it's an owned item. - if (!item || !item.parent) { - const itemName = item?.name ?? itemUuid; - return ui.notifications.warn(`Could not find item ${itemName}. You may need to delete and recreate this macro.`); - } - - // Trigger the item roll - item.roll(); - }); -} \ No newline at end of file diff --git a/templates/actor/actor-creature-sheet.html b/templates/actor/actor-creature-sheet.html new file mode 100644 index 0000000..fd2fb28 --- /dev/null +++ b/templates/actor/actor-creature-sheet.html @@ -0,0 +1,76 @@ +
    + + {{!-- 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 --}} +
    + {{!-- If you want TinyMCE editors to output inline rolls when rendered, you need to pass the actor's roll data to the rollData property. --}} + {{editor system.biography target="system.biography" rollData=rollData button=true owner=owner editable=editable}} +
    + + {{!-- Owned Items Tab --}} +
    + {{> "systems/totem/templates/actor/parts/actor-items.html"}} +
    + + {{!-- Active Effects Tab --}} +
    + {{> "systems/totem/templates/actor/parts/actor-effects.html"}} +
    + +
    +
    +