Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
89d9d71395 | ||
|
|
1e298ed809 | ||
|
|
4ffaebf52b | ||
|
|
de7691c4dc | ||
|
|
c87b25fbaf | ||
|
|
6ce51ed8cf | ||
|
|
df852f17d1 | ||
|
|
8916600bf6 | ||
|
|
0189f62734 |
@@ -19,6 +19,9 @@ foundry
|
|||||||
# Ancien dossier SCSS (archivé)
|
# Ancien dossier SCSS (archivé)
|
||||||
scss_old/
|
scss_old/
|
||||||
|
|
||||||
|
# Outils d'audit local
|
||||||
|
.swival/
|
||||||
|
|
||||||
# Fichiers CSS générés
|
# Fichiers CSS générés
|
||||||
css/vermine2047.*.css
|
css/vermine2047.*.css
|
||||||
!css/vermine2047.css
|
!css/vermine2047.css
|
||||||
|
|||||||
+10
-3
@@ -31,7 +31,7 @@
|
|||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
}
|
}
|
||||||
.char-name-value {
|
.char-name-value {
|
||||||
font-size: 1.5rem;
|
font-size: 1.1rem;
|
||||||
}
|
}
|
||||||
input:not([type="radio"]):not([type="checkbox"]) {
|
input:not([type="radio"]):not([type="checkbox"]) {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
@@ -291,7 +291,7 @@
|
|||||||
|
|
||||||
.chooseTotem {
|
.chooseTotem {
|
||||||
font-family: "DistressBlack", sans-serif;
|
font-family: "DistressBlack", sans-serif;
|
||||||
font-size: 0.9rem;
|
font-size: 0.8rem;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
@@ -377,7 +377,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.char-vermine2047 {
|
.char-vermine2047 {
|
||||||
font-size: 1.5rem;
|
font-size: 1.1rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -409,6 +409,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── Onglet Histoire : doubler la hauteur des zones Notes et Relations ──
|
||||||
|
.tab.stories {
|
||||||
|
prose-mirror.editor {
|
||||||
|
min-height: 150px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
nav.tabs[data-group="sheet"] {
|
nav.tabs[data-group="sheet"] {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
justify-content: space-around;
|
justify-content: space-around;
|
||||||
|
|||||||
+18
-3
@@ -20,16 +20,25 @@
|
|||||||
|
|
||||||
div.minor-totems {
|
div.minor-totems {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
height: 8.5rem;
|
||||||
background-color: rgba(146, 156, 111, 0.5215686275);
|
background-color: rgba(146, 156, 111, 0.5215686275);
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
h5 {
|
h5 {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 2.6rem;
|
||||||
|
margin: 0;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
img {
|
img {
|
||||||
max-width: 2rem;
|
max-width: 2rem;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: -2rem;
|
top: 1.4rem;
|
||||||
|
bottom: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.human, &.adapted {
|
&.human, &.adapted {
|
||||||
@@ -53,6 +62,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.totem-dice {
|
.totem-dice {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 2;
|
||||||
|
|
||||||
.human-dice,
|
.human-dice,
|
||||||
.adapted-dice {
|
.adapted-dice {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -90,7 +105,7 @@
|
|||||||
|
|
||||||
.adapted {
|
.adapted {
|
||||||
right: 0;
|
right: 0;
|
||||||
img { right: 0; }
|
img { right: 0; left: auto; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -230,6 +230,17 @@ export default class VermineBaseActorSheet extends HandlebarsApplicationMixin(fo
|
|||||||
const id = target.closest("[data-item-id]")?.dataset?.itemId
|
const id = target.closest("[data-item-id]")?.dataset?.itemId
|
||||||
if (!id) return
|
if (!id) return
|
||||||
const item = this.document.items.get(id)
|
const item = this.document.items.get(id)
|
||||||
|
if (!item) return
|
||||||
|
if (item.type === "weapon" && this.document.system.abilities && this.document.system.skills) {
|
||||||
|
const { default: RollDialog } = await import("../../system/dialogs/rollDialog.mjs")
|
||||||
|
const dialog = await RollDialog.create({
|
||||||
|
actorId: this.document.id,
|
||||||
|
rolltype: "skill",
|
||||||
|
label: item.system.skill
|
||||||
|
})
|
||||||
|
if (dialog) dialog.render(true)
|
||||||
|
return
|
||||||
|
}
|
||||||
item?.roll()
|
item?.roll()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ export class VermineBaseItemSheet extends HandlebarsApplicationMixin(foundry.app
|
|||||||
// ── Rendu ───────────────────────────────────────────────────────────
|
// ── Rendu ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
_onRender(context, options) {
|
_onRender(context, options) {
|
||||||
|
super._onRender(context, options);
|
||||||
this.#dragDrop.forEach(d => d.bind(this.element))
|
this.#dragDrop.forEach(d => d.bind(this.element))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ export default class VermineItem extends Item {
|
|||||||
*/
|
*/
|
||||||
getRollData() {
|
getRollData() {
|
||||||
// If present, return the actor's roll data.
|
// If present, return the actor's roll data.
|
||||||
if (!this.actor) return null;
|
if (!this.actor) return this.system;
|
||||||
const rollData = this.actor.getRollData();
|
const rollData = this.actor.getRollData();
|
||||||
// Grab the item's system data as well.
|
// Grab the item's system data as well.
|
||||||
rollData.item = foundry.utils.deepClone(this.system);
|
rollData.item = foundry.utils.deepClone(this.system);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export default class VermineWeaponData extends foundry.abstract.TypeDataModel {
|
|||||||
const reqInt = { required: true, nullable: false, integer: true }
|
const reqInt = { required: true, nullable: false, integer: true }
|
||||||
return {
|
return {
|
||||||
...baseItemSchema(),
|
...baseItemSchema(),
|
||||||
|
skill: new fields.StringField({ required: true, nullable: false, initial: "" }),
|
||||||
min_range: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }),
|
min_range: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }),
|
||||||
max_range: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }),
|
max_range: new fields.NumberField({ ...reqInt, initial: 0, min: 0 }),
|
||||||
damage: new fields.SchemaField({
|
damage: new fields.SchemaField({
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api
|
||||||
|
|
||||||
export class TotemPicker extends HandlebarsApplicationMixin(ApplicationV2) {
|
export class TotemPicker extends HandlebarsApplicationMixin(ApplicationV2) {
|
||||||
|
|
||||||
constructor(linkEl, actor) {
|
constructor(linkEl, actor) {
|
||||||
@@ -24,9 +26,9 @@ export class TotemPicker extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||||||
return { config: CONFIG.VERMINE };
|
return { config: CONFIG.VERMINE };
|
||||||
}
|
}
|
||||||
|
|
||||||
activateListeners(html) {
|
_onRender(context, options) {
|
||||||
super.activateListeners(html);
|
super._onRender(context, options);
|
||||||
html.querySelectorAll('.totem').forEach(el => {
|
this.element.querySelectorAll('.totem').forEach(el => {
|
||||||
el.addEventListener('click', event => {
|
el.addEventListener('click', event => {
|
||||||
const link = event.target.closest('a');
|
const link = event.target.closest('a');
|
||||||
if (!link?.dataset.totem) return;
|
if (!link?.dataset.totem) return;
|
||||||
@@ -80,9 +82,9 @@ export class ActorPicker extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
activateListeners(html) {
|
_onRender(context, options) {
|
||||||
super.activateListeners(html);
|
super._onRender(context, options);
|
||||||
html.querySelectorAll('.actor').forEach(el => {
|
this.element.querySelectorAll('.actor').forEach(el => {
|
||||||
el.addEventListener('click', async event => {
|
el.addEventListener('click', async event => {
|
||||||
const div = event.target.closest('div');
|
const div = event.target.closest('div');
|
||||||
const actorId = div?.dataset.actorId;
|
const actorId = div?.dataset.actorId;
|
||||||
@@ -148,15 +150,15 @@ export class TraitSelector extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
activateListeners(html) {
|
_onRender(context, options) {
|
||||||
super.activateListeners(html);
|
super._onRender(context, options);
|
||||||
this._validateTraits(html);
|
this._validateTraits(this.element);
|
||||||
html.querySelectorAll('input').forEach(el => {
|
this.element.querySelectorAll('input').forEach(el => {
|
||||||
el.addEventListener('change', ev => this._onChangeInput(ev));
|
el.addEventListener('change', ev => this._onChangeInput(ev));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async _validateTraits(html) {
|
_validateTraits(html) {
|
||||||
const checks = html.querySelectorAll("input.trait-selector");
|
const checks = html.querySelectorAll("input.trait-selector");
|
||||||
for (const inp of checks) {
|
for (const inp of checks) {
|
||||||
if (this.targetItem.system.traits[inp.dataset.trait]) {
|
if (this.targetItem.system.traits[inp.dataset.trait]) {
|
||||||
@@ -165,7 +167,6 @@ export class TraitSelector extends HandlebarsApplicationMixin(ApplicationV2) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await this.render(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async _onChangeInput(ev) {
|
async _onChangeInput(ev) {
|
||||||
|
|||||||
@@ -121,7 +121,8 @@ export default class RollDialog extends HandlebarsApplicationMixin(foundry.appli
|
|||||||
const specChecked = this.#el.querySelector("#usingSpecialization")?.checked;
|
const specChecked = this.#el.querySelector("#usingSpecialization")?.checked;
|
||||||
const helped = this.#el.querySelector("#helped")?.checked;
|
const helped = this.#el.querySelector("#helped")?.checked;
|
||||||
const tools = this.#el.querySelector("input[name='usingTools']:checked")?.value !== "0";
|
const tools = this.#el.querySelector("input[name='usingTools']:checked")?.value !== "0";
|
||||||
const bonuses = (specChecked ? 1 : 0) + (helped ? 1 : 0) + (tools ? 1 : 0);
|
const group = parseInt(this.#el.querySelector("#group")?.value, 10) || 0;
|
||||||
|
const bonuses = (specChecked ? 1 : 0) + (helped ? 1 : 0) + (tools ? 1 : 0) + group;
|
||||||
return (abilVal + sc + skillPool + bonuses) || 0;
|
return (abilVal + sc + skillPool + bonuses) || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,10 +213,6 @@ export default class RollDialog extends HandlebarsApplicationMixin(foundry.appli
|
|||||||
b += parseInt(this.#getSelfCtrl()?.value, 10) || 0;
|
b += parseInt(this.#getSelfCtrl()?.value, 10) || 0;
|
||||||
const tools = this.#el.querySelector("input[name='usingTools']:checked");
|
const tools = this.#el.querySelector("input[name='usingTools']:checked");
|
||||||
if (tools && tools.value !== "0") b += 1;
|
if (tools && tools.value !== "0") b += 1;
|
||||||
const human = this.#el.querySelector("#human-totem");
|
|
||||||
if (human?.checked) b += parseInt(this.#actor?.system?.adaptation?.totems?.human?.value, 10) || 0;
|
|
||||||
const adapted = this.#el.querySelector("#adapted-totem");
|
|
||||||
if (adapted?.checked) b += parseInt(this.#actor?.system?.adaptation?.totems?.adapted?.value, 10) || 0;
|
|
||||||
if (this.hasSpecialtySelected()) b += 1;
|
if (this.hasSpecialtySelected()) b += 1;
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,6 @@ export function prepareActiveEffectCategories(effects) {
|
|||||||
|
|
||||||
// Iterate over active effects, classifying them into categories
|
// Iterate over active effects, classifying them into categories
|
||||||
for ( let e of effects ) {
|
for ( let e of effects ) {
|
||||||
e._getSourceName(); // Trigger a lookup for the source name
|
|
||||||
if ( e.disabled ) categories.inactive.effects.push(e);
|
if ( e.disabled ) categories.inactive.effects.push(e);
|
||||||
else if ( e.isTemporary ) categories.temporary.effects.push(e);
|
else if ( e.isTemporary ) categories.temporary.effects.push(e);
|
||||||
else categories.passive.effects.push(e);
|
else categories.passive.effects.push(e);
|
||||||
|
|||||||
+62
-13
@@ -184,10 +184,9 @@ export class VermineCombat extends Combat {
|
|||||||
// encounter check
|
// encounter check
|
||||||
}
|
}
|
||||||
|
|
||||||
async rollInitiative(ids, formula = undefined, messageOptions = {}) {
|
async rollInitiative(ids, {formula=null, updateTurn=true, messageMode, messageOptions={}}={}) {
|
||||||
// roll initiative
|
// roll initiative
|
||||||
return super.rollInitiative(ids, formula, messageOptions)
|
return super.rollInitiative(ids, {formula, updateTurn, messageMode, messageOptions});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nextRound() {
|
nextRound() {
|
||||||
@@ -224,24 +223,74 @@ export class VermineCombat extends Combat {
|
|||||||
|
|
||||||
export class VermineCombatTracker extends foundry.applications.sidebar.tabs.CombatTracker {
|
export class VermineCombatTracker extends foundry.applications.sidebar.tabs.CombatTracker {
|
||||||
|
|
||||||
get template() {
|
/** @override */
|
||||||
return "systems/vermine2047/templates/combat-tracker.hbs";
|
static PARTS = {
|
||||||
|
main: {
|
||||||
|
template: "systems/vermine2047/templates/combat-tracker.hbs",
|
||||||
|
root: true
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
async getData(options) {
|
/** @override */
|
||||||
const context = await super.getData(options);
|
async _prepareContext(options) {
|
||||||
|
const context = await super._prepareContext(options);
|
||||||
|
context.user = game.user;
|
||||||
|
|
||||||
if (!context.hasCombat) {
|
const combat = this.viewed;
|
||||||
|
const hasCombat = combat !== null;
|
||||||
|
const combats = this.combats;
|
||||||
|
const currentIdx = combats.indexOf(combat);
|
||||||
|
const isPlayerTurn = combat?.combatant?.players?.includes(game.user);
|
||||||
|
const canControl = combat?.turn && combat.turn.between(1, combat.turns.length - 2)
|
||||||
|
? combat.canUserModify(game.user, "update", { turn: 0 })
|
||||||
|
: combat?.canUserModify(game.user, "update", { round: 0 });
|
||||||
|
|
||||||
|
Object.assign(context, {
|
||||||
|
combat,
|
||||||
|
hasCombat,
|
||||||
|
combatCount: combats.length,
|
||||||
|
currentIndex: currentIdx + 1,
|
||||||
|
previousId: combats[currentIdx - 1]?.id,
|
||||||
|
nextId: combats[currentIdx + 1]?.id,
|
||||||
|
round: combat?.round,
|
||||||
|
control: isPlayerTurn && canControl,
|
||||||
|
linked: combat?.scene !== null,
|
||||||
|
cssClass: combats.length > 7 ? "cycle" : combats.length ? "tabbed" : "",
|
||||||
|
cssId: "combat",
|
||||||
|
tabName: "combat",
|
||||||
|
labels: {
|
||||||
|
scope: game.i18n.localize(`COMBAT.${combat?.scene ? "Linked" : "Unlinked"}`)
|
||||||
|
},
|
||||||
|
turns: []
|
||||||
|
});
|
||||||
|
|
||||||
|
if ( combat ) {
|
||||||
|
for ( const [i, combatant] of combat.turns.entries() ) {
|
||||||
|
if ( !combatant.visible ) continue;
|
||||||
|
context.turns.push(await this._prepareTurnContext(combat, combatant, i));
|
||||||
|
}
|
||||||
|
}
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @override */
|
||||||
return context;
|
async _prepareTurnContext(combat, combatant, index) {
|
||||||
|
const turn = await super._prepareTurnContext(combat, combatant, index);
|
||||||
|
const actor = combatant.actor;
|
||||||
|
turn.attitude = combatant.getFlag("world", "attitude") || "active";
|
||||||
|
turn.isPlayer = actor?.hasPlayerOwner ?? false;
|
||||||
|
turn.isNpc = !!actor && !actor.hasPlayerOwner;
|
||||||
|
turn.owner = combatant.isOwner;
|
||||||
|
turn.defeated = combatant.isDefeated;
|
||||||
|
turn.hasRolled = combatant.initiative !== null;
|
||||||
|
turn.hasResource = combatant.resource !== null && combatant.resource !== undefined;
|
||||||
|
turn.effects = (turn.effects?.icons ?? []).map(e => e.img);
|
||||||
|
return turn;
|
||||||
}
|
}
|
||||||
|
|
||||||
activateListeners(html) {
|
_onRender(context, options) {
|
||||||
super.activateListeners(html);
|
super._onRender(context, options);
|
||||||
html.querySelectorAll("[data-attitude]").forEach(el => {
|
this.element.querySelectorAll("[data-attitude]").forEach(el => {
|
||||||
el.addEventListener("click", this._setStatut.bind(this));
|
el.addEventListener("click", this._setStatut.bind(this));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -388,4 +388,15 @@ export const registerHandlebarsHelpers = function () {
|
|||||||
Handlebars.registerHelper("setVar", function (varName, varValue, options) {
|
Handlebars.registerHelper("setVar", function (varName, varValue, options) {
|
||||||
options.data.root[varName] = varValue;
|
options.data.root[varName] = varValue;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// assign local variables in the current context (used by roll-message.hbs)
|
||||||
|
Handlebars.registerHelper("set", function (options) {
|
||||||
|
const ctx = (this && typeof this === "object") ? this : options.contexts?.[0];
|
||||||
|
if ( ctx && typeof ctx === "object" ) {
|
||||||
|
for ( const [key, value] of Object.entries(options.hash) ) {
|
||||||
|
ctx[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
});
|
||||||
}
|
}
|
||||||
@@ -69,8 +69,8 @@ export const registerHooks = function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// changement de la pause
|
// changement de la pause
|
||||||
Hooks.on("renderPause", async function () {
|
Hooks.on("renderGamePause", async function (app, element) {
|
||||||
const pauseEl = document.getElementById("pause");
|
const pauseEl = element ?? document.getElementById("pause");
|
||||||
if (!pauseEl || pauseEl.className !== "paused") return;
|
if (!pauseEl || pauseEl.className !== "paused") return;
|
||||||
pauseEl.querySelectorAll("img").forEach(img => {
|
pauseEl.querySelectorAll("img").forEach(img => {
|
||||||
img.src = 'systems/vermine2047/assets/images/ui/vermine_pause.webp';
|
img.src = 'systems/vermine2047/assets/images/ui/vermine_pause.webp';
|
||||||
@@ -94,17 +94,17 @@ export const registerHooks = function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Hooks.on('getSceneControlButtons', async (controls) => {
|
Hooks.on('getSceneControlButtons', async (controls) => {
|
||||||
const tokenControls = controls.token;
|
const tokenControls = controls.tokens;
|
||||||
if (tokenControls && tokenControls.tools) {
|
if (tokenControls && tokenControls.tools) {
|
||||||
tokenControls.tools.push({
|
tokenControls.tools['dice-roller'] = {
|
||||||
name: 'Dice Roller',
|
name: 'Dice Roller',
|
||||||
title: game.i18n.localize("VERMINE.RollTool"),
|
title: game.i18n.localize("VERMINE.RollTool"),
|
||||||
icon: 'fas fa-dice-d10',
|
icon: 'fas fa-dice-d10',
|
||||||
button: true,
|
button: true,
|
||||||
onClick() {
|
onChange() {
|
||||||
RollDialog.create().then(d => d.render(true));
|
RollDialog.create().then(d => d.render(true));
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+8
-76
@@ -49,22 +49,13 @@ export class VermineUtils {
|
|||||||
totemBonus = this._calculateTotemDomainBonuses(skillCategory, actor);
|
totemBonus = this._calculateTotemDomainBonuses(skillCategory, actor);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply auto-successes and auto-thresholds
|
// Apply automatic successes from skill mastery
|
||||||
let autoSuccesses = 0;
|
|
||||||
let adjustedDifficulty = difficulty;
|
let adjustedDifficulty = difficulty;
|
||||||
|
|
||||||
if (skillLevel !== null && skillLevel !== undefined) {
|
|
||||||
autoSuccesses = this._calculateAutoSuccesses(skillLevel, hasSpecialty);
|
|
||||||
const autoThreshold = this._getAutoThreshold(skillLevel);
|
|
||||||
if (autoThreshold !== null) {
|
|
||||||
adjustedDifficulty = autoThreshold;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle human totem
|
// Handle human totem
|
||||||
if (totems.human) {
|
if (totems.human) {
|
||||||
NoD--;
|
NoD--;
|
||||||
const humanDifficulty = skillLevel !== null ? Math.max(adjustedDifficulty, difficulty) : adjustedDifficulty;
|
const humanDifficulty = adjustedDifficulty;
|
||||||
const humanFormula = `(1D10cs>=${humanDifficulty}[human_${safeUserName}]*2)`;
|
const humanFormula = `(1D10cs>=${humanDifficulty}[human_${safeUserName}]*2)`;
|
||||||
|
|
||||||
// Apply domain bonus/malus
|
// Apply domain bonus/malus
|
||||||
@@ -78,7 +69,7 @@ export class VermineUtils {
|
|||||||
// Handle adapted totem
|
// Handle adapted totem
|
||||||
if (totems.adapted) {
|
if (totems.adapted) {
|
||||||
NoD--;
|
NoD--;
|
||||||
const adaptedDifficulty = skillLevel !== null ? Math.max(adjustedDifficulty, difficulty) : adjustedDifficulty;
|
const adaptedDifficulty = adjustedDifficulty;
|
||||||
const adaptedFormula = `(1D10cs>=${adaptedDifficulty}[adapted_${safeUserName}]*2)`;
|
const adaptedFormula = `(1D10cs>=${adaptedDifficulty}[adapted_${safeUserName}]*2)`;
|
||||||
|
|
||||||
// Apply domain bonus/malus
|
// Apply domain bonus/malus
|
||||||
@@ -105,8 +96,8 @@ export class VermineUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply handicap (wounds/maluses reduce dice pool)
|
// Ensure the dice pool cannot go below zero
|
||||||
NoD = Math.max(1, NoD - handicap)
|
NoD = Math.max(0, NoD)
|
||||||
|
|
||||||
// Build base formula
|
// Build base formula
|
||||||
const baseFormula = `${NoD}d10cs>=${adjustedDifficulty}[regular_${safeUserName}]`;
|
const baseFormula = `${NoD}d10cs>=${adjustedDifficulty}[regular_${safeUserName}]`;
|
||||||
@@ -126,7 +117,6 @@ export class VermineUtils {
|
|||||||
skillCategory: skillCategory,
|
skillCategory: skillCategory,
|
||||||
skillLevel: skillLevel,
|
skillLevel: skillLevel,
|
||||||
hasSpecialty: hasSpecialty,
|
hasSpecialty: hasSpecialty,
|
||||||
autoSuccesses: autoSuccesses,
|
|
||||||
totemBonuses: { ...totemBonus },
|
totemBonuses: { ...totemBonus },
|
||||||
baseNoD: NoD,
|
baseNoD: NoD,
|
||||||
rerolls: Reroll,
|
rerolls: Reroll,
|
||||||
@@ -152,7 +142,8 @@ export class VermineUtils {
|
|||||||
skillCategory,
|
skillCategory,
|
||||||
keepTotem,
|
keepTotem,
|
||||||
skillLevel,
|
skillLevel,
|
||||||
hasSpecialty
|
hasSpecialty,
|
||||||
|
handicap
|
||||||
});
|
});
|
||||||
|
|
||||||
return roll;
|
return roll;
|
||||||
@@ -205,63 +196,6 @@ export class VermineUtils {
|
|||||||
return bonuses;
|
return bonuses;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates automatic successes based on skill mastery level.
|
|
||||||
* @param {number} skillLevel - Skill level (0-5)
|
|
||||||
* @param {boolean} [hasSpecialty=false] - Whether a specialty is used
|
|
||||||
* @returns {number} Number of automatic successes
|
|
||||||
*/
|
|
||||||
static _calculateAutoSuccesses(skillLevel, hasSpecialty = false) {
|
|
||||||
// According to Vermine2047 rules, automatic successes are based on mastery level:
|
|
||||||
// Level 0 (Incompetent): 0 automatic successes
|
|
||||||
// Level 1 (Beginner): 0 automatic successes
|
|
||||||
// Level 2 (Proficient): 1 automatic success if specialty is used
|
|
||||||
// Level 3 (Expert): 1 automatic success
|
|
||||||
// Level 4 (Master): 1 automatic success + 1 if specialty is used
|
|
||||||
// Level 5 (Legend): 2 automatic successes
|
|
||||||
|
|
||||||
if (!skillLevel) return 0;
|
|
||||||
|
|
||||||
let autoSuccesses = 0;
|
|
||||||
|
|
||||||
switch (skillLevel) {
|
|
||||||
case 2: // Compétent
|
|
||||||
if (hasSpecialty) autoSuccesses = 1;
|
|
||||||
break;
|
|
||||||
case 3: // Expert
|
|
||||||
autoSuccesses = 1;
|
|
||||||
break;
|
|
||||||
case 4: // Maître
|
|
||||||
autoSuccesses = 1;
|
|
||||||
if (hasSpecialty) autoSuccesses += 1;
|
|
||||||
break;
|
|
||||||
case 5: // Légende
|
|
||||||
autoSuccesses = 2;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
autoSuccesses = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return autoSuccesses;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines the automatic threshold if the skill is not mastered.
|
|
||||||
* @param {number} skillLevel - Skill level
|
|
||||||
* @returns {number|null} Automatic threshold or null if skill is mastered
|
|
||||||
*/
|
|
||||||
static _getAutoThreshold(skillLevel) {
|
|
||||||
// If the skill is not mastered (level 0 or 1), use a default threshold
|
|
||||||
// Level 0 (Incompetent): threshold = 9 (very hard)
|
|
||||||
// Level 1 (Beginner): threshold = 7 (hard)
|
|
||||||
// Level >= 2: null (use normal threshold)
|
|
||||||
|
|
||||||
if (skillLevel === 0) return 9; // Very hard
|
|
||||||
if (skillLevel === 1) return 7; // Hard
|
|
||||||
|
|
||||||
return null; // Utiliser le seuil normal
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles reroll events on dice in chat messages.
|
* Handles reroll events on dice in chat messages.
|
||||||
* @param {Object} message - The chat message containing the reroll event
|
* @param {Object} message - The chat message containing the reroll event
|
||||||
@@ -471,11 +405,9 @@ export class VermineUtils {
|
|||||||
const chatData = {
|
const chatData = {
|
||||||
user: game.user?._id,
|
user: game.user?._id,
|
||||||
speaker: ChatMessage.getSpeaker(),
|
speaker: ChatMessage.getSpeaker(),
|
||||||
content: content,
|
content: content
|
||||||
roll: roll
|
|
||||||
};
|
};
|
||||||
const msg = await ChatMessage.create(chatData);
|
const msg = await ChatMessage.create(chatData);
|
||||||
await msg.setFlag('world', 'roll', roll);
|
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ Hooks.once('init', async function () {
|
|||||||
|
|
||||||
// Register sheet application classes (ApplicationV2)
|
// Register sheet application classes (ApplicationV2)
|
||||||
// Unregister core sheets
|
// Unregister core sheets
|
||||||
foundry.applications.sheets.ActorSheetV2?.unregisterSheet?.("core", "Actor", {
|
foundry.documents.collections.Actors.unregisterSheet("core", foundry.applications.sheets.ActorSheetV2, {
|
||||||
types: ["character", "npc", "group", "creature"]
|
types: ["character", "npc", "group", "creature"]
|
||||||
})
|
})
|
||||||
foundry.documents.collections.Items.unregisterSheet("core", foundry.appv1?.sheets?.ItemSheet)
|
foundry.documents.collections.Items.unregisterSheet("core", foundry.appv1?.sheets?.ItemSheet)
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
{{!-- Logo --}}
|
{{!-- Logo --}}
|
||||||
<img class="logo mx-auto" src="systems/vermine2047/assets/images/ui/logo.webp"
|
<img class="logo mx-auto" src="systems/vermine2047/assets/images/ui/logo.webp"
|
||||||
width="200" alt="logo Vermine" />
|
width="150" alt="logo Vermine" />
|
||||||
|
|
||||||
{{!-- Sidebar: Profile Image + Totem Dice --}}
|
{{!-- Sidebar: Profile Image + Totem Dice --}}
|
||||||
<div class="image-wrapper">
|
<div class="image-wrapper">
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
</a>
|
</a>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<a class="combat-button combat-control" data-tooltip="COMBAT.Delete"
|
<a class="combat-button combat-control" data-tooltip="COMBAT.Delete"
|
||||||
data-control="endCombat" {{#unless
|
data-action="endCombat" {{#unless
|
||||||
combatCount}}disabled{{/unless}}>
|
combatCount}}disabled{{/unless}}>
|
||||||
<i class="fas fa-trash"></i>
|
<i class="fas fa-trash"></i>
|
||||||
</a>
|
</a>
|
||||||
@@ -33,12 +33,12 @@
|
|||||||
<div class="encounter-controls flexrow {{#if hasCombat}}combat{{/if}}">
|
<div class="encounter-controls flexrow {{#if hasCombat}}combat{{/if}}">
|
||||||
{{#if user.isGM}}
|
{{#if user.isGM}}
|
||||||
<a class="combat-button combat-control"
|
<a class="combat-button combat-control"
|
||||||
data-tooltip="COMBAT.RollAll" data-control="rollAll" {{#unless
|
data-tooltip="COMBAT.RollAll" data-action="rollAll" {{#unless
|
||||||
turns}}disabled{{/unless}}>
|
turns}}disabled{{/unless}}>
|
||||||
<i class="fas fa-users"></i>
|
<i class="fas fa-users"></i>
|
||||||
</a>
|
</a>
|
||||||
<a class="combat-button combat-control"
|
<a class="combat-button combat-control"
|
||||||
data-tooltip="COMBAT.RollNPC" data-control="rollNPC" {{#unless
|
data-tooltip="COMBAT.RollNPC" data-action="rollNPC" {{#unless
|
||||||
turns}}disabled{{/unless}}>
|
turns}}disabled{{/unless}}>
|
||||||
<i class="fas fa-users-cog"></i>
|
<i class="fas fa-users-cog"></i>
|
||||||
</a>
|
</a>
|
||||||
@@ -58,18 +58,18 @@
|
|||||||
|
|
||||||
{{#if user.isGM}}
|
{{#if user.isGM}}
|
||||||
<a class="combat-button combat-control"
|
<a class="combat-button combat-control"
|
||||||
data-tooltip="COMBAT.InitiativeReset" data-control="resetAll"
|
data-tooltip="COMBAT.InitiativeReset" data-action="resetAll"
|
||||||
{{#unless hasCombat}}disabled{{/unless}}>
|
{{#unless hasCombat}}disabled{{/unless}}>
|
||||||
<i class="fas fa-undo"></i>
|
<i class="fas fa-undo"></i>
|
||||||
</a>
|
</a>
|
||||||
<a class="combat-button combat-control"
|
<a class="combat-button combat-control"
|
||||||
data-tooltip="{{labels.scope}}"
|
data-tooltip="{{labels.scope}}"
|
||||||
data-control="toggleSceneLink" {{#unless
|
data-action="toggleSceneLink" {{#unless
|
||||||
hasCombat}}disabled{{/unless}}>
|
hasCombat}}disabled{{/unless}}>
|
||||||
<i class="fas fa-{{#unless linked}}un{{/unless}}link"></i>
|
<i class="fas fa-{{#unless linked}}un{{/unless}}link"></i>
|
||||||
</a>
|
</a>
|
||||||
<a class="combat-button combat-settings"
|
<a class="combat-button combat-settings"
|
||||||
data-tooltip="COMBAT.Settings" data-control="trackerSettings">
|
data-tooltip="COMBAT.Settings" data-action="trackerSettings">
|
||||||
<i class="fas fa-cog"></i>
|
<i class="fas fa-cog"></i>
|
||||||
</a>
|
</a>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
@@ -90,20 +90,20 @@
|
|||||||
<a
|
<a
|
||||||
class="combatant-control {{#if turn.hidden}}active{{/if}}"
|
class="combatant-control {{#if turn.hidden}}active{{/if}}"
|
||||||
data-tooltip="COMBAT.ToggleVis"
|
data-tooltip="COMBAT.ToggleVis"
|
||||||
data-control="toggleHidden">
|
data-action="toggleHidden">
|
||||||
<i class="fas fa-eye-slash"></i>
|
<i class="fas fa-eye-slash"></i>
|
||||||
</a>
|
</a>
|
||||||
<a
|
<a
|
||||||
class="combatant-control {{#if turn.defeated}}active{{/if}}"
|
class="combatant-control {{#if turn.defeated}}active{{/if}}"
|
||||||
data-tooltip="COMBAT.ToggleDead"
|
data-tooltip="COMBAT.ToggleDead"
|
||||||
data-control="toggleDefeated">
|
data-action="toggleDefeated">
|
||||||
<i class="fas fa-skull"></i>
|
<i class="fas fa-skull"></i>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
{{#if turn.canPing}}
|
{{#if turn.canPing}}
|
||||||
<a class="combatant-control"
|
<a class="combatant-control"
|
||||||
data-tooltip="COMBAT.PingCombatant"
|
data-tooltip="COMBAT.PingCombatant"
|
||||||
data-control="alertPingCombatant">
|
data-action="pingCombatant">
|
||||||
<i class="fa-solid fa-bullseye-arrow"></i>
|
<i class="fa-solid fa-bullseye-arrow"></i>
|
||||||
</a>
|
</a>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
@@ -158,7 +158,7 @@
|
|||||||
{{else if this.owner}}
|
{{else if this.owner}}
|
||||||
<a class="combatant-control roll"
|
<a class="combatant-control roll"
|
||||||
data-tooltip="COMBAT.InitiativeRoll"
|
data-tooltip="COMBAT.InitiativeRoll"
|
||||||
data-control="rollInitiative"></a>
|
data-action="rollInitiative"></a>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
@@ -171,30 +171,30 @@
|
|||||||
{{#if user.isGM}}
|
{{#if user.isGM}}
|
||||||
{{#if round}}
|
{{#if round}}
|
||||||
<a class="combat-control" data-tooltip="COMBAT.RoundPrev"
|
<a class="combat-control" data-tooltip="COMBAT.RoundPrev"
|
||||||
data-control="previousRound"><i
|
data-action="previousRound"><i
|
||||||
class="fas fa-step-backward"></i></a>
|
class="fas fa-step-backward"></i></a>
|
||||||
<a class="combat-control" data-tooltip="COMBAT.TurnPrev"
|
<a class="combat-control" data-tooltip="COMBAT.TurnPrev"
|
||||||
data-control="previousTurn"><i
|
data-action="previousTurn"><i
|
||||||
class="fas fa-arrow-left"></i></a>
|
class="fas fa-arrow-left"></i></a>
|
||||||
<a class="combat-control center" data-control="endCombat">{{localize
|
<a class="combat-control center" data-action="endCombat">{{localize
|
||||||
'COMBAT.End'}}</a>
|
'COMBAT.End'}}</a>
|
||||||
<a class="combat-control" data-tooltip="COMBAT.TurnNext"
|
<a class="combat-control" data-tooltip="COMBAT.TurnNext"
|
||||||
data-control="nextTurn"><i class="fas fa-arrow-right"></i></a>
|
data-action="nextTurn"><i class="fas fa-arrow-right"></i></a>
|
||||||
<a class="combat-control" data-tooltip="COMBAT.RoundNext"
|
<a class="combat-control" data-tooltip="COMBAT.RoundNext"
|
||||||
data-control="nextRound"><i class="fas fa-step-forward"></i></a>
|
data-action="nextRound"><i class="fas fa-step-forward"></i></a>
|
||||||
{{else}}
|
{{else}}
|
||||||
<a class="combat-control center"
|
<a class="combat-control center"
|
||||||
data-control="startCombat">{{localize
|
data-action="startCombat">{{localize
|
||||||
'COMBAT.Begin'}}</a>
|
'COMBAT.Begin'}}</a>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{else if control}}
|
{{else if control}}
|
||||||
<a class="combat-control" data-tooltip="COMBAT.TurnPrev"
|
<a class="combat-control" data-tooltip="COMBAT.TurnPrev"
|
||||||
data-control="previousTurn"><i
|
data-action="previousTurn"><i
|
||||||
class="fas fa-arrow-left"></i></a>
|
class="fas fa-arrow-left"></i></a>
|
||||||
<a class="combat-control center" data-control="nextTurn">{{localize
|
<a class="combat-control center" data-action="nextTurn">{{localize
|
||||||
'COMBAT.TurnEnd'}}</a>
|
'COMBAT.TurnEnd'}}</a>
|
||||||
<a class="combat-control" data-tooltip="COMBAT.TurnNext"
|
<a class="combat-control" data-tooltip="COMBAT.TurnNext"
|
||||||
data-control="nextTurn"><i class="fas fa-arrow-right"></i></a>
|
data-action="nextTurn"><i class="fas fa-arrow-right"></i></a>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</nav>
|
</nav>
|
||||||
|
|||||||
@@ -280,7 +280,7 @@
|
|||||||
class="totem-checkbox"
|
class="totem-checkbox"
|
||||||
/>
|
/>
|
||||||
<span class="totem-label">{{localize 'TOTEMS.human.name'}}</span>
|
<span class="totem-label">{{localize 'TOTEMS.human.name'}}</span>
|
||||||
<small>(+{{@root.actor.system.adaptation.totems.human.value}}D)</small>
|
<small>({{@root.actor.system.adaptation.totems.human.value}}D)</small>
|
||||||
</label>
|
</label>
|
||||||
<label class="totem-option">
|
<label class="totem-option">
|
||||||
<input
|
<input
|
||||||
@@ -292,7 +292,7 @@
|
|||||||
class="totem-checkbox"
|
class="totem-checkbox"
|
||||||
/>
|
/>
|
||||||
<span class="totem-label">{{localize 'TOTEMS.adapted.name'}}</span>
|
<span class="totem-label">{{localize 'TOTEMS.adapted.name'}}</span>
|
||||||
<small>(+{{@root.actor.system.adaptation.totems.adapted.value}}D)</small>
|
<small>({{@root.actor.system.adaptation.totems.adapted.value}}D)</small>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<small class="totem-hint">{{localize 'VERMINE.totem_hint'}}</small>
|
<small class="totem-hint">{{localize 'VERMINE.totem_hint'}}</small>
|
||||||
@@ -309,7 +309,7 @@
|
|||||||
value="1"
|
value="1"
|
||||||
/>
|
/>
|
||||||
<span class="totem-label">{{localize 'TOTEMS.human.name'}}</span>
|
<span class="totem-label">{{localize 'TOTEMS.human.name'}}</span>
|
||||||
<small>(+{{@root.actor.system.adaptation.totems.human.value}}D)</small>
|
<small>({{@root.actor.system.adaptation.totems.human.value}}D)</small>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
{{/ifgt}}
|
{{/ifgt}}
|
||||||
@@ -326,7 +326,7 @@
|
|||||||
value="1"
|
value="1"
|
||||||
/>
|
/>
|
||||||
<span class="totem-label">{{localize 'TOTEMS.adapted.name'}}</span>
|
<span class="totem-label">{{localize 'TOTEMS.adapted.name'}}</span>
|
||||||
<small>(+{{@root.actor.system.adaptation.totems.adapted.value}}D)</small>
|
<small>({{@root.actor.system.adaptation.totems.adapted.value}}D)</small>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
{{/ifgt}}
|
{{/ifgt}}
|
||||||
|
|||||||
@@ -3,6 +3,29 @@
|
|||||||
|
|
||||||
|
|
||||||
<section class="sheet-body">
|
<section class="sheet-body">
|
||||||
|
<div class="flexrow">
|
||||||
|
<label class="resource-label" for="system.skill">{{ localize 'VERMINE.skill_title'}}</label>
|
||||||
|
<select
|
||||||
|
name="system.skill"
|
||||||
|
class="skill-select"
|
||||||
|
>
|
||||||
|
{{#each @root.config.skillCategories as |skillCategory sckey|}}
|
||||||
|
<optgroup label="{{ smarttlk 'SKILLS_CATEGORIES' sckey 'name' }}">
|
||||||
|
{{#each @root.config.skills as |skill key|}}
|
||||||
|
{{#ife skill.category sckey}}
|
||||||
|
<option
|
||||||
|
value="{{key}}"
|
||||||
|
{{#ife key @root.system.skill}}
|
||||||
|
selected
|
||||||
|
{{/ife}}
|
||||||
|
>{{ smarttlk 'SKILLS' key 'name' }}</option>
|
||||||
|
{{/ife}}
|
||||||
|
{{/each}}
|
||||||
|
</optgroup>
|
||||||
|
{{/each}}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
{{> "systems/vermine2047/templates/item/partials/traits.hbs"}}
|
{{> "systems/vermine2047/templates/item/partials/traits.hbs"}}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="flexcol">
|
<div class="flexcol">
|
||||||
<h4>{{localize "VERMINE.success_required"}}:</h4>
|
<h4>{{localize "VERMINE.success_required"}}:</h4>
|
||||||
<span id="required">{{param.handicap}}</span>
|
<span id="required">{{math_add 1 param.handicap}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
Reference in New Issue
Block a user