rolling
This commit is contained in:
+2
-6
@@ -1,10 +1,6 @@
|
|||||||
{
|
{
|
||||||
"TOTEM.AbilityStrAbbr": "str",
|
"TOTEM.WorldSettings.GameMode.Name":"Choix du mode de jeu",
|
||||||
"TOTEM.AbilityConAbbr": "con",
|
"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.AbilityDexAbbr": "dex",
|
|
||||||
"TOTEM.AbilityIntAbbr": "int",
|
|
||||||
"TOTEM.AbilityWisAbbr": "wis",
|
|
||||||
"TOTEM.AbilityChaAbbr": "cha",
|
|
||||||
|
|
||||||
"TOTEM.level": "Niveau",
|
"TOTEM.level": "Niveau",
|
||||||
"TOTEM.pool": "Réserve",
|
"TOTEM.pool": "Réserve",
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ export class TotemActorSheet extends ActorSheet {
|
|||||||
// Prepare character data and items.
|
// Prepare character data and items.
|
||||||
if (actorData.type == 'character') {
|
if (actorData.type == 'character') {
|
||||||
this._prepareItems(context);
|
this._prepareItems(context);
|
||||||
this._prepareCharacterData(context);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare NPC data and items.
|
// Prepare NPC data and items.
|
||||||
@@ -60,171 +59,5 @@ export class TotemActorSheet extends ActorSheet {
|
|||||||
return context;
|
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export class TotemCharacterSheet extends TotemActorSheet {
|
|||||||
|
|
||||||
/** @override */
|
/** @override */
|
||||||
get template() {
|
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();
|
event.preventDefault();
|
||||||
const element = event.currentTarget;
|
const element = event.currentTarget;
|
||||||
const dataset = element.dataset;
|
const dataset = element.dataset;
|
||||||
|
console.log("Ceci est un jet d'un personnage joueur");
|
||||||
// Handle item rolls.
|
// Handle item rolls.
|
||||||
if (dataset.rollType) {
|
if (dataset.rollType) {
|
||||||
if (dataset.rollType == 'item') {
|
if (dataset.rollType == 'item') {
|
||||||
@@ -217,14 +217,10 @@ export class TotemCharacterSheet extends TotemActorSheet {
|
|||||||
|
|
||||||
// Handle rolls that supply the formula directly.
|
// Handle rolls that supply the formula directly.
|
||||||
if (dataset.roll) {
|
if (dataset.roll) {
|
||||||
let label = dataset.label ? `[ability] ${dataset.label}` : '';
|
const label = game.i18n.localize(dataset.label) ? `[ability] ${game.i18n.localize(dataset.label)}` : '';
|
||||||
let roll = new Roll(dataset.roll, this.actor.getRollData());
|
console.log($(element).attr('for'), this.actor.system.skills[$(element).attr('for').split('.')[2]].value);
|
||||||
roll.toMessage({
|
const NoD = this.actor.system.skills[$(element).attr('for').split('.')[2]]?.value || 0
|
||||||
speaker: ChatMessage.getSpeaker({ actor: this.actor }),
|
return game.totem.TotemRoll.roll(this.actor.id, label, NoD, 0, {});
|
||||||
flavor: label,
|
|
||||||
rollMode: game.settings.get('core', 'rollMode'),
|
|
||||||
});
|
|
||||||
return roll;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+15
-4
@@ -1,4 +1,3 @@
|
|||||||
import { TotemFight } from './fight.mjs';
|
|
||||||
|
|
||||||
export const registerHooks = function () {
|
export const registerHooks = function () {
|
||||||
/**
|
/**
|
||||||
@@ -34,7 +33,7 @@ export const registerHooks = function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Hooks.on('getSceneControlButtons', (controls) => {
|
Hooks.on('getSceneControlButtons', (controls) => {
|
||||||
controls.find((c) => c.name === 'token').tools.push({
|
/*controls.find((c) => c.name === 'token').tools.push({
|
||||||
name: 'Dice Roller',
|
name: 'Dice Roller',
|
||||||
title: game.i18n.localize("TOTEM.RollTool"),
|
title: game.i18n.localize("TOTEM.RollTool"),
|
||||||
icon: 'fas fa-dice-d6',
|
icon: 'fas fa-dice-d6',
|
||||||
@@ -42,7 +41,7 @@ export const registerHooks = function () {
|
|||||||
onClick() {
|
onClick() {
|
||||||
TotemRoll.ui();
|
TotemRoll.ui();
|
||||||
}
|
}
|
||||||
});
|
});*/
|
||||||
});
|
});
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
@@ -50,7 +49,7 @@ export const registerHooks = function () {
|
|||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
|
||||||
Hooks.on("preCreateActor", function (actor) {
|
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") {
|
if (actor.img == "icons/svg/mystery-man.svg") {
|
||||||
// actor.updateSource({"img": `systems/totem/icons/actors/${actor.type}.webp`});
|
// actor.updateSource({"img": `systems/totem/icons/actors/${actor.type}.webp`});
|
||||||
// item.updateSource({"img": `systems/totem/icons/competence.webp`});
|
// item.updateSource({"img": `systems/totem/icons/competence.webp`});
|
||||||
@@ -91,5 +90,17 @@ 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"
|
||||||
|
}));
|
||||||
|
});*/
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 += '<br />'+ 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 += '<li class="roll die d6 max">' + result + '</li>';
|
|
||||||
}
|
|
||||||
else if (result <= 5) {
|
|
||||||
diceString += '<li class="roll die d6">' + result + '</li>';
|
|
||||||
}
|
|
||||||
else if (result >= 1) {
|
|
||||||
diceString += '<li class="roll die d6 min">' + result + '</li>';
|
|
||||||
}
|
|
||||||
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 = `
|
|
||||||
<div class="totem roll attribute">
|
|
||||||
<div class="dice-roll">
|
|
||||||
<div class="dice-result">
|
|
||||||
<div class="dice-formula">
|
|
||||||
` + dicePool + `d6
|
|
||||||
</div>
|
|
||||||
<div class="dice-tooltip expanded">
|
|
||||||
<section class="tooltip-part flexrow">
|
|
||||||
<div class="" style="flex:60%;">
|
|
||||||
<div class="parameters">
|
|
||||||
` + targetText + `
|
|
||||||
</div>
|
|
||||||
<div class="dice">
|
|
||||||
<ol class="dice-rolls">` + diceString + `</ol>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="align-center">
|
|
||||||
Résultat
|
|
||||||
<p style="font-weight:bold; font-size:2em;">` + (total + skill + _trait + _usingSpecialization + _used).toString() + `</p>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>` +
|
|
||||||
`<h4 class="dice-total">` + successText + `</h4>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
// 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"
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,29 +1,18 @@
|
|||||||
export const registerSettings = function () {
|
export const registerSettings = function () {
|
||||||
game.settings.register("totem", "game-level", {
|
game.settings.register("totem", "game-mode", {
|
||||||
name: game.i18n.localize("TOTEM.WorldSettings.GameLevel.Name"),
|
name: game.i18n.localize("TOTEM.WorldSettings.GameMode.Name"),
|
||||||
hint: game.i18n.localize("TOTEM.WorldSettings.GameLevel.Hint"),
|
hint: game.i18n.localize("TOTEM.WorldSettings.GameMode.Hint"),
|
||||||
scope: "system",
|
scope: "system",
|
||||||
config: true,
|
config: true,
|
||||||
type: String,
|
type: String,
|
||||||
choices: {
|
choices: {
|
||||||
"e": "Totem",
|
"1": "Survie",
|
||||||
"c": "Céphale",
|
"2": "Cauchemar",
|
||||||
"b": "Bohème",
|
"3": "Apocalypse"
|
||||||
"a": "Amertume"
|
|
||||||
},
|
},
|
||||||
default: 'e',
|
default: 'e',
|
||||||
onChange: value => {
|
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
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
}
|
||||||
+5
-89
@@ -14,6 +14,9 @@ import { TotemCreatureSheet } from "./sheets/creature-sheet.mjs";
|
|||||||
import { TotemItem } from "./documents/item.mjs";
|
import { TotemItem } from "./documents/item.mjs";
|
||||||
import { TotemItemSheet } from "./sheets/item-sheet.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 helper/utility classes and constants.
|
||||||
import { preloadHandlebarsTemplates, registerHandlebarsHelpers } from "./system/handlebars-manager.mjs";
|
import { preloadHandlebarsTemplates, registerHandlebarsHelpers } from "./system/handlebars-manager.mjs";
|
||||||
import { TOTEM } from "./system/config.mjs";
|
import { TOTEM } from "./system/config.mjs";
|
||||||
@@ -31,7 +34,8 @@ Hooks.once('init', async function() {
|
|||||||
TotemNpc,
|
TotemNpc,
|
||||||
TotemCreature,
|
TotemCreature,
|
||||||
TotemItem,
|
TotemItem,
|
||||||
rollItemMacro
|
TotemRoll,
|
||||||
|
TotemCombat
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add custom constants for configuration.
|
// Add custom constants for configuration.
|
||||||
@@ -76,91 +80,3 @@ Hooks.once('init', async function() {
|
|||||||
// Preload Handlebars templates.
|
// Preload Handlebars templates.
|
||||||
return preloadHandlebarsTemplates();
|
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();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
<form class="{{cssClass}} {{actor.type}} flexcol" autocomplete="off">
|
||||||
|
|
||||||
|
{{!-- Sheet Header --}}
|
||||||
|
<header class="sheet-header">
|
||||||
|
<img class="profile-img" src="{{actor.img}}" data-edit="img" title="{{actor.name}}" height="100" width="100"/>
|
||||||
|
<div class="header-fields">
|
||||||
|
<h1 class="charname"><input name="name" type="text" value="{{actor.name}}" placeholder="Name"/></h1>
|
||||||
|
{{!-- 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. --}}
|
||||||
|
<div class="resources grid grid-3col">
|
||||||
|
|
||||||
|
{{!-- "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. --}}
|
||||||
|
<div class="resource flex-group-center">
|
||||||
|
<label for="system.health.value" class="resource-label">Health</label>
|
||||||
|
<div class="resource-content flexrow flex-center flex-between">
|
||||||
|
<input type="text" name="system.health.value" value="{{system.health.value}}" data-dtype="Number"/>
|
||||||
|
<span> / </span>
|
||||||
|
<input type="text" name="system.health.max" value="{{system.health.max}}" data-dtype="Number"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="resource flex-group-center">
|
||||||
|
<label for="system.power.value" class="resource-label">Power</label>
|
||||||
|
<div class="resource-content flexrow flex-center flex-between">
|
||||||
|
<input type="text" name="system.power.value" value="{{system.power.value}}" data-dtype="Number"/>
|
||||||
|
<span> / </span>
|
||||||
|
<input type="text" name="system.power.max" value="{{system.power.max}}" data-dtype="Number"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="resource flex-group-center">
|
||||||
|
<label for="system.cr" class="resource-label">CR / XP</label>
|
||||||
|
<div class="resource-content flexrow flex-center flex-between">
|
||||||
|
<input type="text" name="system.cr" value="{{system.cr}}" data-dtype="Number"/>
|
||||||
|
<span> / </span>
|
||||||
|
<input type="text" disabled="true" name="system.xp" value="{{system.xp}}" data-dtype="Number"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
{{!-- Sheet Tab Navigation --}}
|
||||||
|
<nav class="sheet-tabs tabs" data-group="primary">
|
||||||
|
{{!-- Default tab is specified in actor-sheet.mjs --}}
|
||||||
|
<a class="item" data-tab="description">Description</a>
|
||||||
|
<a class="item" data-tab="items">Items</a>
|
||||||
|
<a class="item" data-tab="effects">Effects</a>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
{{!-- Sheet Body --}}
|
||||||
|
<section class="sheet-body">
|
||||||
|
|
||||||
|
{{!-- Biography Tab --}}
|
||||||
|
<div class="tab biography" data-group="primary" data-tab="description">
|
||||||
|
{{!-- 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}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{!-- Owned Items Tab --}}
|
||||||
|
<div class="tab items" data-group="primary" data-tab="items">
|
||||||
|
{{> "systems/totem/templates/actor/parts/actor-items.html"}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{!-- Active Effects Tab --}}
|
||||||
|
<div class="tab effects flexcol" data-group="primary" data-tab="effects">
|
||||||
|
{{> "systems/totem/templates/actor/parts/actor-effects.html"}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
</form>
|
||||||
|
|
||||||
Reference in New Issue
Block a user