Working on initiative, and now TN is global with GM tool

This commit is contained in:
Vlyan
2021-01-14 18:44:56 +01:00
parent 30950a0d63
commit 58a4e71b32
15 changed files with 561 additions and 186 deletions

View File

@@ -175,7 +175,6 @@ export class BaseSheetL5r5e extends ActorSheet {
new game.l5r5e.DicePickerDialog({
skillId: li.data("skill") || null,
skillCatId: li.data("skillcat") || null,
difficulty: li.data("diff") || 2,
actor: this.actor,
}).render(true);
});

View File

@@ -1,61 +1,103 @@
/**
* Roll initiative for one or multiple Combatants within the Combat entity
* @param {string|string[]} ids A Combatant id or Array of ids for which to roll
* @param {string|null} [formula] A non-default initiative formula to roll. Otherwise the system default is used.
* @param {boolean} [updateTurn] Update the Combat turn after adding new initiative scores to keep the turn on
* the same Combatant.
* @param {object} [messageOptions] Additional options with which to customize created Chat Messages
* @return {Promise<Combat>} A promise which resolves to the updated Combat entity once updates are complete.
* Extends the actor to process special things from L5R.
*/
export async function rollInitiative(ids, { formula = null, updateTurn = true, messageOptions = {} } = {}) {
if (!Array.isArray(ids)) {
ids = [ids];
}
const updatedCombatants = [];
ids.forEach((combatantId) => {
const combatant = game.combat.combatants.find((c) => c._id === combatantId);
if (!combatant || !combatant.actor) {
return;
}
const data = combatant.actor.data.data;
const formula = [`${data.rings[data.stance]}dr`];
const skillValue =
combatant.actor.data.type === "npc" ? data.skills["martial"] : data.skills["martial"]["tactics"];
if (skillValue > 0) {
formula.push(`${skillValue}ds`);
export class CombatL5r5e extends Combat {
// game.combat.settings.resource = "fatigue.value"; // nope :/
// constructor(...args) {
// super(...args);
// console.log(args);
// }
/**
* Roll initiative for one or multiple Combatants within the Combat entity
* @param {string|string[]} ids A Combatant id or Array of ids for which to roll
* @param {string|null} [formula] A non-default initiative formula to roll. Otherwise the system default is used.
* @param {boolean} [updateTurn] Update the Combat turn after adding new initiative scores to keep the turn on
* the same Combatant.
* @param {object} [messageOptions] Additional options with which to customize created Chat Messages
* @return {Promise<Combat>} A promise which resolves to the updated Combat entity once updates are complete.
*/
async rollInitiative(ids, { formula = null, updateTurn = true, messageOptions = {} } = {}) {
if (!Array.isArray(ids)) {
ids = [ids];
}
const roll = new game.l5r5e.RollL5r5e(formula.join("+"));
roll.actor = combatant.actor;
roll.l5r5e.stance = data.stance;
roll.l5r5e.skillId = "tactics";
roll.l5r5e.summary.difficulty = 1;
roll.roll();
roll.toMessage({ flavor: game.i18n.localize("l5r5e.chatdices.initiative_roll") });
updatedCombatants.push({
_id: combatant._id,
initiative: roll.l5r5e.summary.success,
// Make combatants array
const combatants = [];
ids.forEach((combatantId) => {
const combatant = game.combat.combatants.find((c) => c._id === combatantId);
if (combatant && combatant.actor) {
combatants.push(combatant);
}
});
});
// Update all combatants at once
await this.updateEmbeddedEntity("Combatant", updatedCombatants);
return this;
}
// Get modifiers
const difficulty = game.settings.get("l5r5e", "initiative.difficulty.value");
const difficultyHidden = game.settings.get("l5r5e", "initiative.difficulty.hidden");
const skillId = CONFIG.l5r5e.initiativeSkills[game.settings.get("l5r5e", "initiative.encounter")];
const skillCat = CONFIG.l5r5e.skills.get(skillId);
/**
* Define how the array of Combatants is sorted in the displayed list of the tracker.
* This method can be overridden by a system or module which needs to display combatants in an alternative order.
* By default sort by initiative, falling back to name
* @private
*/
export function _sortCombatants(a, b) {
// if tie, sort by honor, less honorable first
if (a.initiative === b.initiative) {
return a.actor.data.data.social.honor - b.actor.data.data.social.honor;
// Get score for each combatant
const updatedCombatants = [];
combatants.forEach((combatant) => {
const data = combatant.actor.data.data;
// A characters initiative value is based on their state of preparedness when the conflict began.
// If the character was ready for the conflict, their base initiative value is their focus attribute.
// If the character was unprepared (such as when surprised), their base initiative value is their vigilance attribute.
const isPrepared = true; // TODO in actor ? (pc and npc)
let initiative = isPrepared ? data.focus : data.vigilance;
// PC and Adversary
// (minion NPCs can generate initiative value without a check, using their focus or vigilance attribute)
if (combatant.actor.data.type !== "npc" || combatant.actor.data.data.type === "minion") {
const formula = [`${data.rings[data.stance]}dr`];
const skillValue =
combatant.actor.data.type === "npc" ? data.skills[skillCat] : data.skills[skillCat][skillId];
if (skillValue > 0) {
formula.push(`${skillValue}ds`);
}
const roll = new game.l5r5e.RollL5r5e(formula.join("+"));
roll.actor = combatant.actor;
roll.l5r5e.stance = data.stance;
roll.l5r5e.skillId = skillId;
roll.l5r5e.summary.difficulty = difficulty;
roll.l5r5e.summary.difficultyHidden = difficultyHidden;
roll.roll();
roll.toMessage({ flavor: game.i18n.localize("l5r5e.chatdices.initiative_roll") });
// if the character succeeded on their Initiative check, they add 1 to their base initiative value,
// plus an additional amount equal to their bonus successes.
if (roll.l5r5e.summary.success >= difficulty) {
initiative = initiative + 1 + Math.max(roll.l5r5e.summary.success - difficulty, 0);
}
}
updatedCombatants.push({
_id: combatant._id,
initiative: initiative,
});
});
// Update all combatants at once
await this.updateEmbeddedEntity("Combatant", updatedCombatants);
return this;
}
/**
* Define how the array of Combatants is sorted in the displayed list of the tracker.
* This method can be overridden by a system or module which needs to display combatants in an alternative order.
* By default sort by initiative, falling back to name
* @private
*/
_sortCombatants(a, b) {
// if tie, sort by honor, less honorable first
if (a.initiative === b.initiative) {
return a.actor.data.data.social.honor - b.actor.data.data.social.honor;
}
return b.initiative - a.initiative;
}
return b.initiative - a.initiative;
}

View File

@@ -5,16 +5,24 @@ L5R5E.paths = {
templates: `systems/l5r5e/templates/`,
};
L5R5E.money = [50, 10];
L5R5E.stances = ["earth", "air", "water", "fire", "void"];
L5R5E.techniques = ["kata", "kiho", "invocation", "ritual", "shuji", "maho", "ninjutsu"];
L5R5E.techniques_school = ["school_ability", "mastery_ability"];
L5R5E.xp = {
costPerRank: [0, 20, 24, 32, 44, 60],
ringCostMultiplier: 3,
skillCostMultiplier: 2,
techniqueCost: 3,
};
L5R5E.money = [50, 10];
L5R5E.initiativeSkills = {
intrigue: "sentiment",
duel: "meditation",
skirmish: "tactics",
mass_battle: "command",
};
// Map SkillId - CategoryId
L5R5E.skills = new Map();

View File

@@ -121,14 +121,27 @@ export class DicePickerDialog extends FormApplication {
// Difficulty
if (options?.difficulty) {
this.difficulty = options.difficulty;
} else {
this.difficulty = game.settings.get("l5r5e", "initiative.difficulty.value");
}
// difficultyHidden
if (options?.difficultyHidden) {
this.difficultyHidden = options.difficultyHidden;
} else {
this.difficultyHidden = game.settings.get("l5r5e", "initiative.difficulty.hidden");
}
}
/**
* Refresh data (used from socket)
*/
async refresh() {
this.difficulty = game.settings.get("l5r5e", "initiative.difficulty.value");
this.difficultyHidden = game.settings.get("l5r5e", "initiative.difficulty.hidden");
this.render(false);
}
/**
* Set actor
* @param actor

View File

@@ -0,0 +1,138 @@
/**
* L5R Initiative Roll dialog
* @extends {FormApplication}
*/
export class GmToolsDialog extends FormApplication {
/**
* Settings
*/
object = {};
/**
* Assign the default options
* @override
*/
static get defaultOptions() {
const x = $(window).width();
const y = $(window).height();
return mergeObject(super.defaultOptions, {
id: "l5r5e-gm-tools-dialog",
classes: ["l5r5e", "gm-tools-dialog"],
template: CONFIG.l5r5e.paths.templates + "dice/gm-tools-dialog.html",
title: game.i18n.localize("l5r5e.dicepicker.difficulty_title"),
width: 200, // ignored under 200px
height: 130, // ignored under 50px
scale: 0.5, // so scale /2 :D
left: x - 470,
top: y - 94,
closeOnSubmit: false,
submitOnClose: false,
submitOnChange: true,
});
}
/**
* Constructor
* @param {ApplicationOptions} options
*/
constructor(options = {}) {
super(options);
this.object = {
difficulty: game.settings.get("l5r5e", "initiative.difficulty.value"),
difficultyHidden: game.settings.get("l5r5e", "initiative.difficulty.hidden"),
};
}
/**
* Prevent non GM to render this windows
* @override
*/
render(force = false, options = {}) {
if (!game.user.isGM) {
return false;
}
return super.render(force, options);
}
/**
* Remove the close button
* @override
*/
_getHeaderButtons() {
return [];
}
/**
* Construct and return the data object used to render the HTML template for this form application.
* @param options
* @return {Object}
* @override
*/
getData(options = null) {
return {
...super.getData(options),
data: this.object,
};
}
/**
* Listen to html elements
* @override
*/
activateListeners(html) {
super.activateListeners(html);
if (!game.user.isGM) {
return;
}
// Modify difficulty hidden
html.find(`.difficulty_hidden`).on("click", (event) => {
this.object.difficultyHidden = !this.object.difficultyHidden;
game.settings
.set("l5r5e", "initiative.difficulty.hidden", this.object.difficultyHidden)
.then(() => this.submit());
});
// Modify difficulty (TN)
html.find(`.difficulty`).on("mousedown", (event) => {
event.preventDefault();
event.stopPropagation();
switch (event.which) {
case 1:
// left clic - add 1
this.object.difficulty = Math.min(9, this.object.difficulty + 1);
break;
case 2:
// middle clic - reset to 2
this.object.difficulty = 2;
break;
case 3:
// right clic - minus 1
this.object.difficulty = Math.max(1, this.object.difficulty - 1);
break;
}
game.settings.set("l5r5e", "initiative.difficulty.value", this.object.difficulty).then(() => this.submit());
});
}
/**
* This method is called upon form submission after form data is validated
* @param event The initial triggering submission event
* @param formData The object of validated form data with which to update the object
* @returns A Promise which resolves once the update operation has completed
* @override
*/
async _updateObject(event, formData) {
// Notify the change to other players if they already have opened the DicePicker
game.l5r5e.sockets.refreshAppId("l5r5e-dice-picker-dialog");
// If the current GM also have the DP open
const app = Object.values(ui.windows).find((e) => e.id === "l5r5e-dice-picker-dialog");
if (app && typeof app.refresh === "function") {
app.refresh();
}
this.render(false);
}
}

191
system/scripts/hooks.js Normal file
View File

@@ -0,0 +1,191 @@
/* eslint-disable no-undef */
export default class HooksL5r5e {
/**
* Do anything after initialization but before ready
*/
static setup() {
// Embed Babele compendiums
if (
typeof Babele !== "undefined" &&
Babele.get().modules.every((module) => module.lang !== "fr" || module.module !== "l5r5e-dev")
) {
Babele.get().register({
module: "../systems/l5r5e", // babele only accept modules, so... well :D
lang: "fr",
dir: "babele/fr-fr",
});
}
}
/**
* Do anything once the system is ready
*/
static ready() {
// Settings TN and EncounterType
if (game.user.isGM) {
new game.l5r5e.GmToolsDialog().render(true);
}
// ***** UI *****
// Add title on button dice icon
$(".chat-control-icon")[0].title = game.i18n.localize("l5r5e.chatdices.dicepicker");
// Open Help dialog on clic on logo
$("#logo")
.on("click", () => new game.l5r5e.HelpDialog().render(true))
.prop("title", game.i18n.localize("l5r5e.logo.alt"));
}
/**
* SidebarTab
*/
static renderSidebarTab(app, html, data) {
// Add button on dice icon
html.find(".chat-control-icon").click(async () => {
new game.l5r5e.DicePickerDialog().render();
});
}
/**
* Chat Message
*/
static renderChatMessage(message, html, data) {
// Add a extra CSS class to roll
if (message.isRoll) {
html.addClass("roll");
html.on("click", ".chat-dice-rnk", game.l5r5e.RollnKeepDialog.onChatAction.bind(this));
}
}
/**
* Combat tracker
*/
static async renderCombatTracker(app, html, data) {
// TODO do this in partial
let bar = "";
// *** Encounter Type ***
const encounterIcons = {
intrigue: "i_courtier",
duel: "fas fa-tint", // fa-tint / fa-blind
skirmish: "i_bushi",
mass_battle: "fa fa-users",
};
const encounterType = game.settings.get("l5r5e", "initiative.encounter");
Object.entries(CONFIG.l5r5e.initiativeSkills).forEach(([id, skill]) => {
bar =
bar +
`<a class="encounter encounter-control" data-id="${id}">` +
`<i class="${encounterIcons[id]}${id === encounterType ? " active" : ""}" title="${game.i18n.localize(
"l5r5e.conflict.initiative." + id
)}"></i>` +
`</a>`;
});
// *** Prepared ***
// TODO
// const encounterType = game.settings.get("l5r5e", "initiative.prepared");
bar =
bar +
`<a class="encounter prepared-control" data-id="tmp">` +
`<i class="fa fa-low-vision" title="npc prepared or not (WIP)"></i>` +
`</a>`;
const elmt = html.find("#l5r5e_encounter");
if (elmt.length > 0) {
elmt.html(bar);
} else {
html.find("#combat-round").append(`<nav class="encounters flexrow" id="l5r5e_encounter">${bar}</nav>`);
}
// Buttons Listener
html.find(".encounter-control").on("click", (event) => {
event.preventDefault();
event.stopPropagation();
const encounter = $(event.currentTarget).data("id");
game.settings
.set("l5r5e", "initiative.encounter", encounter)
.then(() => HooksL5r5e.renderCombatTracker(app, html, data));
});
// html.find(".prepared-control").on("click", (event) => {
// event.preventDefault();
// event.stopPropagation();
// let prepared = $(event.currentTarget).data('id');
// // if same, unset it
// if (prepared === encounterType) {
// prepared = "";
// }
// game.settings.set("l5r5e", "initiative.prepared", prepared).then(() => HooksL5r5e.renderCombatTracker(app, html, data));
// });
}
/**
* Compendium display
*/
static async renderCompendium(app, html, data) {
// Add Rank & Ring in the compendium
if (app.entity === "Item") {
const content = await app.getContent();
content.forEach((item) => {
const tags = [];
if (item.data.data.rank) {
tags.push("<i>" + game.i18n.localize("l5r5e.rank") + " " + item.data.data.rank + "</i>");
}
if (item.data.data.ring) {
tags.push(`<i class="i_${item.data.data.ring}"></i>`);
}
if (tags.length > 0) {
html.find(`[data-entry-id='${item._id}']`).append(tags.join(" "));
}
});
return false;
}
}
/**
* DiceSoNice Hook
*/
static diceSoNiceReady(dice3d) {
const texturePath = `${CONFIG.l5r5e.paths.assets}dices/default/3d/`;
// dice3d.addSystem({
// id: "l5r5e",
// name: "Legend of the Five Rings 5E"
// }, "force");
// Rings
dice3d.addDicePreset(
{
name: "L5R Ring Dice",
type: "ddr", // don't known why the "dd" prefix is required, term is "r"
labels: Object.keys(game.l5r5e.RingDie.FACES).map(
(e) => `${texturePath}${game.l5r5e.RingDie.FACES[e].image.replace("ring_", "")}.png`
),
bumpMaps: Object.keys(game.l5r5e.RingDie.FACES).map(
(e) => `${texturePath}${game.l5r5e.RingDie.FACES[e].image.replace("ring_", "")}_bm.png`
),
colorset: "black",
system: "standard",
},
"d6"
);
// Skills
dice3d.addDicePreset(
{
name: "L5R Skill Dice",
type: "dds",
labels: Object.keys(game.l5r5e.AbilityDie.FACES).map(
(e) => `${texturePath}${game.l5r5e.AbilityDie.FACES[e].image.replace("skill_", "")}.png`
),
bumpMaps: Object.keys(game.l5r5e.AbilityDie.FACES).map(
(e) => `${texturePath}${game.l5r5e.AbilityDie.FACES[e].image.replace("skill_", "")}_bm.png`
),
colorset: "white",
system: "standard",
},
"d12"
);
}
}

View File

@@ -1,4 +1,3 @@
import { L5R5E } from "../config.js";
import { ItemSheetL5r5e } from "./item-sheet.js";
/**
@@ -20,7 +19,7 @@ export class WeaponSheetL5r5e extends ItemSheetL5r5e {
const sheetData = await super.getData();
// Martial skills only
sheetData.data.skills = Array.from(L5R5E.skills)
sheetData.data.skills = Array.from(CONFIG.l5r5e.skills)
.filter(([id, cat]) => cat === "martial")
.map(([id, cat]) => id);

View File

@@ -5,6 +5,7 @@ import { SocketHandlerL5r5e } from "./socket-handler.js";
import { RegisterSettings } from "./settings.js";
import { PreloadTemplates } from "./preloadTemplates.js";
import { HelpDialog } from "./help/help-dialog.js";
import HooksL5r5e from "./hooks.js";
// Actors
import { ActorL5r5e } from "./actor.js";
import { CharacterSheetL5r5e } from "./actors/character-sheet.js";
@@ -15,7 +16,8 @@ import { RingDie } from "./dice/dietype/ring-die.js";
import { RollL5r5e } from "./dice/roll.js";
import { DicePickerDialog } from "./dice/dice-picker-dialog.js";
import { RollnKeepDialog } from "./dice/roll-n-keep-dialog.js";
import { _sortCombatants, rollInitiative } from "./combat.js";
import { CombatL5r5e } from "./combat.js";
import { GmToolsDialog } from "./dice/gm-tools-dialog.js";
// Items
import { ItemL5r5e } from "./item.js";
import { ItemSheetL5r5e } from "./items/item-sheet.js";
@@ -48,6 +50,7 @@ Hooks.once("init", async function () {
CONFIG.l5r5e = L5R5E;
// Assign custom classes and constants here
CONFIG.Combat.entityClass = CombatL5r5e;
CONFIG.Actor.entityClass = ActorL5r5e;
CONFIG.Actor.sheetClasses = CharacterSheetL5r5e;
CONFIG.Item.entityClass = ItemL5r5e;
@@ -64,10 +67,13 @@ Hooks.once("init", async function () {
// Add some classes in game
game.l5r5e = {
RingDie,
AbilityDie,
HelpersL5r5e,
RollL5r5e,
DicePickerDialog,
RollnKeepDialog,
GmToolsDialog,
HelpDialog,
sockets: new SocketHandlerL5r5e(),
};
@@ -78,11 +84,6 @@ Hooks.once("init", async function () {
// Preload Handlebars templates
await PreloadTemplates();
// ***** Combat *****
Combat.prototype.rollInitiative = rollInitiative;
Combat.prototype._sortCombatants = _sortCombatants;
// game.combat.settings.resource = "fatigue.value"; // nope :/
// ***** Register custom sheets *****
// Actors
Actors.unregisterSheet("core", ActorSheet);
@@ -188,100 +189,16 @@ Hooks.once("init", async function () {
});
/* ------------------------------------ */
/* Setup system */
/* Hooks Once */
/* ------------------------------------ */
Hooks.once("setup", function () {
// Do anything after initialization but before ready
// Embed Babele compendiums
/* eslint-disable no-undef */
if (
typeof Babele !== "undefined" &&
Babele.get().modules.every((module) => module.lang !== "fr" || module.module !== "l5r5e-dev")
) {
Babele.get().register({
module: "../systems/l5r5e", // babele only accept modules, so... well :D
lang: "fr",
dir: "babele/fr-fr",
});
}
});
Hooks.once("setup", HooksL5r5e.setup);
Hooks.once("ready", HooksL5r5e.ready);
Hooks.once("diceSoNiceReady", (dice3d) => HooksL5r5e.diceSoNiceReady(dice3d));
/* ------------------------------------ */
/* Do anything once the system is ready */
/* Hooks On */
/* ------------------------------------ */
Hooks.once("ready", function () {
// Add title on button dice icon
$(".chat-control-icon")[0].title = game.i18n.localize("l5r5e.chatdices.dicepicker");
// Open Help dialog on clic on logo
$("#logo")
.on("click", () => new game.l5r5e.HelpDialog().render(true))
.prop("title", game.i18n.localize("l5r5e.logo.alt"));
});
/* ------------------------------------ */
/* SidebarTab */
/* ------------------------------------ */
Hooks.on("renderSidebarTab", (app, html, data) => {
// Add button on dice icon
html.find(".chat-control-icon").click(async () => {
new game.l5r5e.DicePickerDialog().render();
});
});
/* ------------------------------------ */
/* Chat Message */
/* ------------------------------------ */
Hooks.on("renderChatMessage", (message, html, data) => {
// Add a extra CSS class to roll
if (message.isRoll) {
html.addClass("roll");
html.on("click", ".chat-dice-rnk", RollnKeepDialog.onChatAction.bind(this));
}
});
/* ------------------------------------ */
/* DiceSoNice Hook */
/* ------------------------------------ */
Hooks.once("diceSoNiceReady", (dice3d) => {
const texturePath = `${CONFIG.l5r5e.paths.assets}dices/default/3d/`;
// dice3d.addSystem({
// id: "l5r5e",
// name: "Legend of the Five Rings 5E"
// }, "force");
// Rings
dice3d.addDicePreset(
{
name: "L5R Ring Dice",
type: "ddr", // don't known why the "dd" prefix is required, term is "r"
labels: Object.keys(RingDie.FACES).map(
(e) => `${texturePath}${RingDie.FACES[e].image.replace("ring_", "")}.png`
),
bumpMaps: Object.keys(RingDie.FACES).map(
(e) => `${texturePath}${RingDie.FACES[e].image.replace("ring_", "")}_bm.png`
),
colorset: "black",
system: "standard",
},
"d6"
);
// Skills
dice3d.addDicePreset(
{
name: "L5R Skill Dice",
type: "dds",
labels: Object.keys(AbilityDie.FACES).map(
(e) => `${texturePath}${AbilityDie.FACES[e].image.replace("skill_", "")}.png`
),
bumpMaps: Object.keys(AbilityDie.FACES).map(
(e) => `${texturePath}${AbilityDie.FACES[e].image.replace("skill_", "")}_bm.png`
),
colorset: "white",
system: "standard",
},
"d12"
);
});
Hooks.on("renderSidebarTab", (app, html, data) => HooksL5r5e.renderSidebarTab(app, html, data));
Hooks.on("renderChatMessage", (message, html, data) => HooksL5r5e.renderChatMessage(message, html, data));
Hooks.on("renderCombatTracker", (app, html, data) => HooksL5r5e.renderCombatTracker(app, html, data));
Hooks.on("renderCompendium", async (app, html, data) => HooksL5r5e.renderCompendium(app, html, data));

View File

@@ -1,4 +1,36 @@
export const RegisterSettings = function() {
// Register any custom system settings here
}
/**
* Custom system settings register
*/
export const RegisterSettings = function () {
/**
* Settings set by Initiative Roll Dialog (GM only)
*/
game.settings.register("l5r5e", "initiative.difficulty.hidden", {
name: "Initiative difficulty is hidden",
scope: "world",
config: false,
type: Boolean,
default: false,
});
game.settings.register("l5r5e", "initiative.difficulty.value", {
name: "Initiative difficulty value",
scope: "world",
config: false,
type: Number,
default: 2,
});
game.settings.register("l5r5e", "initiative.encounter", {
name: "Initiative encounter type",
scope: "world",
config: false,
type: String,
default: "skirmish",
});
game.settings.register("l5r5e", "initiative.prepared", {
name: "Initiative NPC prepared or not",
scope: "world",
config: false,
type: Boolean,
default: true,
});
};

View File

@@ -48,7 +48,7 @@ export class SocketHandlerL5r5e {
}
/**
* Refresh a app by it's id, not windowsId (ex "l5r5e-twenty-questions-dialog-kZHczAFghMNYFRWe", not "65")
* Refresh a app by it's htmlId, not windowsId (ex "l5r5e-twenty-questions-dialog-kZHczAFghMNYFRWe", not "65")
* usage : game.l5r5e.sockets.refreshAppId(appId);
* @param appId
*/
@@ -60,10 +60,7 @@ export class SocketHandlerL5r5e {
}
_onRefreshAppId(data) {
const app = Object.values(ui.windows).find((e) => e.id === data.appId);
if (!app) {
return;
}
if (typeof app.refresh !== "function") {
if (!app || typeof app.refresh !== "function") {
return;
}
app.refresh();

File diff suppressed because one or more lines are too long

View File

@@ -184,3 +184,39 @@
border: 3px solid green;
}
}
.gm-tools-dialog {
bottom: 10px;
right: 5px;
display: flex;
.window-content {
text-align: center;
vertical-align: middle;
.gm-tools-container {
flex: 1;
display: flex;
flex-flow: wrap;
div {
flex: 1;
cursor: url("../assets/cursors/pointer.webp"), pointer;
}
.difficulty {
font-size: 60px;
}
i {
font-size: 60px;
vertical-align: middle;
}
}
}
// hide "search anywhere" draggable icon
.window-draggable-handle {
display: none;
}
}

View File

@@ -568,30 +568,22 @@ button {
h3 {
font-size: 0.85rem;
}
}
}
}
// Dialog
.dialo {
.logo-dialog {
height: 125px;
.dialog-buttons {
button {
line-height: 1rem;
}
}
.dialog-content {
height: 1rem;
p {
margin: 0;
.encounter {
i {
font-size: 23px;
vertical-align: middle;
}
.active {
box-shadow: 0 1px 5px $l5r5e-red;
}
}
}
}
}
// Pause
#pause {
img {
content: url("../assets/icons/pause.svg");

View File

@@ -1,9 +1,9 @@
<fieldset class="initiative initiative-wrapper">
<legend class="section-header">{{ localize 'l5r5e.conflict.initiative.title' }}</legend>
<button class="initiative dice-picker" data-skill="sentiment" data-diff="1">{{ localize 'l5r5e.conflict.initiative.intrigue'}}</button>
<button class="initiative dice-picker" data-skill="meditation" data-diff="1">{{ localize 'l5r5e.conflict.initiative.duel'}}</button>
<button class="initiative dice-picker" data-skill="tactics" data-diff="1">{{ localize 'l5r5e.conflict.initiative.skirmish'}}</button>
<button class="initiative dice-picker" data-skill="command" data-diff="1">{{ localize 'l5r5e.conflict.initiative.mass_battle'}}</button>
<button class="initiative dice-picker" data-skill="sentiment">{{ localize 'l5r5e.conflict.initiative.intrigue'}}</button>
<button class="initiative dice-picker" data-skill="meditation">{{ localize 'l5r5e.conflict.initiative.duel'}}</button>
<button class="initiative dice-picker" data-skill="tactics">{{ localize 'l5r5e.conflict.initiative.skirmish'}}</button>
<button class="initiative dice-picker" data-skill="command">{{ localize 'l5r5e.conflict.initiative.mass_battle'}}</button>
</fieldset>
<fieldset class="stances-content flexrow">
<legend class="section-header">{{ localize 'l5r5e.conflict.stance' }}</legend>

View File

@@ -0,0 +1,11 @@
<form class="l5r5e gm-tools-dialog" autocomplete="off">
<div class="gm-tools-container">
<div class="difficulty_hidden">
<i class="fa fa-eye{{#if data.difficultyHidden}}-slash{{/if}}"></i>
</div>
<div class="difficulty">
{{data.difficulty}}
</div>
</div>
</form>