mirror of
https://github.com/Nighthawk42/roll20-character-sheets.git
synced 2026-08-30 03:40:32 +00:00
344 lines
12 KiB
JavaScript
344 lines
12 KiB
JavaScript
// ==============================
|
|
// === TABLES XP ===============
|
|
// ==============================
|
|
const xpSkillTable = [0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18];
|
|
const xpBaseCosts = [0, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 90, 100];
|
|
|
|
// ==============================
|
|
// === 1. COMPÉTENCES COMMUNES =
|
|
// ==============================
|
|
const commonSkills = {
|
|
armessimples: "arm", camouflage: "nin", cac: "tai", esquive: "tai",
|
|
gensou: "gen", henge: "nin", kawarimi: "gen", mental: "esp",
|
|
parade: "arm", physique: "cor", survie: "nin", vigilance: "nin"
|
|
};
|
|
|
|
for (const skill in commonSkills) {
|
|
const baseAttr = commonSkills[skill];
|
|
on(`change:scmp_${skill} change:${baseAttr} sheet:opened`, () => {
|
|
getAttrs([`scmp_${skill}`, baseAttr], values => {
|
|
const base = parseInt(values[baseAttr]) || 0;
|
|
const mod = parseInt(values[`scmp_${skill}`]) || 0;
|
|
setAttrs({ [`stcmp_${skill}`]: base + mod });
|
|
});
|
|
});
|
|
}
|
|
|
|
// =========================================
|
|
// === 2. XP DES CARACTÉRISTIQUES (base) ===
|
|
// =========================================
|
|
const baseAttrs = ['cor', 'esp', 'arm', 'tai', 'nin', 'gen', 'lign'];
|
|
|
|
baseAttrs.forEach(attr => {
|
|
on(`change:${attr}`, () => {
|
|
getAttrs([attr], values => {
|
|
let score = parseInt(values[attr]) || 1;
|
|
score = Math.min(score, xpBaseCosts.length - 1);
|
|
const cost = xpBaseCosts.slice(1, score).reduce((a, b) => a + b, 0);
|
|
setAttrs({ [`xp_${attr}`]: cost });
|
|
});
|
|
});
|
|
});
|
|
|
|
// ===========================================
|
|
// === 3. CALCUL DU CHAKRA TOTAL & RESTANT ===
|
|
// ===========================================
|
|
on("change:cor change:esp change:chakra_colossal change:chakra_endurci change:chakra_imperieux sheet:opened", () => {
|
|
getAttrs(["cor", "esp", "chakra_colossal", "chakra_endurci", "chakra_imperieux", "chakra_restant"], values => {
|
|
const toInt = v => parseInt(v) || 0;
|
|
|
|
const totalChakra =
|
|
(toInt(values.cor) * 50) +
|
|
(toInt(values.esp) * 50) +
|
|
(toInt(values.chakra_colossal) * 100) +
|
|
(toInt(values.chakra_endurci) * 50) +
|
|
(toInt(values.chakra_imperieux) * 50);
|
|
|
|
const actuel = parseInt(values.chakra_restant);
|
|
const restant = isNaN(actuel) || actuel === 0 ? totalChakra : actuel;
|
|
|
|
setAttrs({
|
|
chakra: totalChakra,
|
|
chakra_restant: restant
|
|
});
|
|
});
|
|
});
|
|
|
|
// ===============================
|
|
// === 4. STATS SECONDAIRES =====
|
|
// ===============================
|
|
on("change:cor change:esp change:arm change:tai change:nin change:gen change:chakra change:chakra_endurci change:chakra_imperieux change:chakra_fulgurant change:stcmp_physique sheet:opened", () => {
|
|
getAttrs([
|
|
"cor", "esp", "arm", "tai", "nin", "gen", "chakra", "chakra_endurci",
|
|
"chakra_imperieux", "chakra_fulgurant", "stcmp_physique"
|
|
], values => {
|
|
const toInt = v => parseInt(values[v]) || 0;
|
|
|
|
const cor = toInt("cor");
|
|
const esp = toInt("esp");
|
|
const arm = toInt("arm");
|
|
const tai = toInt("tai");
|
|
|
|
const controle = cor + esp;
|
|
let spe = 1;
|
|
if (controle >= 5 && controle <= 9) spe = 2;
|
|
else if (controle >= 10 && controle <= 13) spe = 4;
|
|
else if (controle >= 14 && controle <= 19) spe = 6;
|
|
else if (controle >= 20) spe = 9;
|
|
|
|
const initiative = toInt("stcmp_physique") + (toInt("chakra_fulgurant") * 2);
|
|
const vigueur = cor + 2 + toInt("chakra_endurci");
|
|
const caractere = esp + 2 + toInt("chakra_imperieux");
|
|
const regen = Math.floor(toInt("chakra") * (spe * 0.01));
|
|
const interceptions_arm = Math.floor(arm / 2);
|
|
const interceptions_tai = Math.floor(tai / 2);
|
|
|
|
setAttrs({
|
|
controle,
|
|
specialisation_chakra: spe,
|
|
initiative,
|
|
vigueur,
|
|
caractere,
|
|
regen,
|
|
interceptions_arm,
|
|
interceptions_tai
|
|
});
|
|
});
|
|
});
|
|
on("change:cor change:esp change:chakra_colossal change:chakra_endurci change:chakra_imperieux change:bonus_chakra sheet:opened", () => {
|
|
getAttrs(["cor", "esp", "chakra_colossal", "chakra_endurci", "chakra_imperieux", "chakra_restant", "bonus_chakra"], values => {
|
|
const toInt = v => parseInt(v) || 0;
|
|
|
|
const totalChakra =
|
|
(toInt(values.cor) * 50) +
|
|
(toInt(values.esp) * 50) +
|
|
(toInt(values.chakra_colossal) * 100) +
|
|
(toInt(values.chakra_endurci) * 50) +
|
|
(toInt(values.chakra_imperieux) * 50) +
|
|
toInt(values.bonus_chakra); // Bonus manuel
|
|
|
|
const actuel = parseInt(values.chakra_restant);
|
|
const restant = isNaN(actuel) || actuel === 0 ? totalChakra : actuel;
|
|
|
|
setAttrs({
|
|
chakra: totalChakra,
|
|
chakra_restant: restant
|
|
});
|
|
});
|
|
});
|
|
|
|
on("change:cor change:esp change:chakra_colossal change:chakra_endurci change:chakra_imperieux change:bonus_chakra sheet:opened", () => {
|
|
getAttrs(["cor", "esp", "chakra_colossal", "chakra_endurci", "chakra_imperieux", "chakra_restant", "bonus_chakra"], values => {
|
|
const toInt = v => parseInt(v) || 0;
|
|
|
|
const totalChakra =
|
|
(toInt(values.cor) * 50) +
|
|
(toInt(values.esp) * 50) +
|
|
(toInt(values.chakra_colossal) * 100) +
|
|
(toInt(values.chakra_endurci) * 50) +
|
|
(toInt(values.chakra_imperieux) * 50) +
|
|
toInt(values.bonus_chakra); // Bonus manuel
|
|
|
|
const actuel = parseInt(values.chakra_restant);
|
|
const restant = isNaN(actuel) || actuel === 0 ? totalChakra : actuel;
|
|
|
|
setAttrs({
|
|
chakra: totalChakra,
|
|
chakra_restant: restant
|
|
});
|
|
});
|
|
});
|
|
|
|
on("change:cor change:esp change:arm change:tai change:nin change:gen change:chakra change:chakra_endurci change:chakra_imperieux change:chakra_fulgurant change:stcmp_physique change:bonus_vigueur change:bonus_caractere sheet:opened", () => {
|
|
getAttrs([
|
|
"cor", "esp", "arm", "tai", "nin", "gen", "chakra", "chakra_endurci",
|
|
"chakra_imperieux", "chakra_fulgurant", "stcmp_physique",
|
|
"bonus_vigueur", "bonus_caractere"
|
|
], values => {
|
|
const toInt = v => parseInt(values[v]) || 0;
|
|
|
|
const cor = toInt("cor");
|
|
const esp = toInt("esp");
|
|
const arm = toInt("arm");
|
|
const tai = toInt("tai");
|
|
|
|
const controle = cor + esp;
|
|
let spe = 1;
|
|
if (controle >= 5 && controle <= 9) spe = 2;
|
|
else if (controle >= 10 && controle <= 13) spe = 4;
|
|
else if (controle >= 14 && controle <= 19) spe = 6;
|
|
else if (controle >= 20) spe = 9;
|
|
|
|
const initiative = toInt("stcmp_physique") + (toInt("chakra_fulgurant") * 2);
|
|
const vigueur = cor + 2 + toInt("chakra_endurci") + toInt("bonus_vigueur");
|
|
const caractere = esp + 2 + toInt("chakra_imperieux") + toInt("bonus_caractere");
|
|
const regen = Math.floor(toInt("chakra") * (spe * 0.01));
|
|
const interceptions_arm = Math.floor(arm / 2);
|
|
const interceptions_tai = Math.floor(tai / 2);
|
|
|
|
setAttrs({
|
|
controle,
|
|
specialisation_chakra: spe,
|
|
initiative,
|
|
vigueur,
|
|
caractere,
|
|
regen,
|
|
interceptions_arm,
|
|
interceptions_tai
|
|
});
|
|
});
|
|
});
|
|
|
|
// ===============================
|
|
// === 5. DÉGÂTS ================
|
|
// ===============================
|
|
on("change:cor change:arm change:tai change:gen change:nin change:chakra_acere change:chakra_explosif change:bonus_degats_gen change:bonus_degats_nin change:degats_tai_bonus change:degats_arm_bonus sheet:opened", () => {
|
|
getAttrs([
|
|
"cor", "arm", "tai", "gen", "nin",
|
|
"chakra_acere", "chakra_explosif",
|
|
"bonus_degats_gen", "bonus_degats_nin",
|
|
"degats_tai_bonus", "degats_arm_bonus"
|
|
], values => {
|
|
const toInt = v => parseInt(values[v]) || 0;
|
|
|
|
const baseArm = toInt("cor") + toInt("arm") + (toInt("chakra_acere") * 2);
|
|
const baseTai = toInt("cor") + toInt("tai") + (toInt("chakra_explosif") * 2);
|
|
const baseGen = toInt("gen") + 1;
|
|
const baseNin = toInt("nin") + 1;
|
|
|
|
setAttrs({
|
|
degats_arm_base: baseArm,
|
|
degats_tai_base: baseTai,
|
|
degats_gen_base: baseGen,
|
|
degats_nin_base: baseNin,
|
|
degats_arm: baseArm + toInt("degats_arm_bonus"),
|
|
degats_tai: baseTai + toInt("degats_tai_bonus"),
|
|
degats_gen: baseGen + toInt("bonus_degats_gen"),
|
|
degats_nin: baseNin + toInt("bonus_degats_nin")
|
|
});
|
|
});
|
|
});
|
|
|
|
// ============================================
|
|
// === 6. XP COMPÉTENCES COMMUNES ============
|
|
// ============================================
|
|
Object.keys(commonSkills).forEach(skill => {
|
|
on(`change:scmp_${skill}`, () => {
|
|
getAttrs([`scmp_${skill}`], values => {
|
|
const score = Math.min(parseInt(values[`scmp_${skill}`]) || 1, xpSkillTable.length);
|
|
const xp = xpSkillTable.slice(1, score).reduce((a, b) => a + b, 0);
|
|
setAttrs({ [`xp_scmp_${skill}`]: xp });
|
|
});
|
|
});
|
|
});
|
|
|
|
// ==============================================
|
|
// === 7. XP DES COMPÉTENCES SPÉCIFIQUES (REP) ===
|
|
// ==============================================
|
|
on("change:repeating_cmpspe:scmps change:repeating_cmpspe:nom_cmps", function (eventInfo) {
|
|
const rowId = eventInfo.sourceAttribute.split("_")[2];
|
|
getAttrs([`repeating_cmpspe_${rowId}_nom_cmps`, `repeating_cmpspe_${rowId}_scmps`], function (values) {
|
|
const attrBase = values[`repeating_cmpspe_${rowId}_nom_cmps`];
|
|
const mod = parseInt(values[`repeating_cmpspe_${rowId}_scmps`]) || 0;
|
|
|
|
getAttrs([attrBase], function (baseValues) {
|
|
const base = parseInt(baseValues[attrBase]) || 0;
|
|
const total = base + mod;
|
|
const xp = mod > 1 ? xpSkillTable.slice(1, mod).reduce((a, b) => a + b, 0) : 0;
|
|
|
|
|
|
setAttrs({
|
|
[`repeating_cmpspe_${rowId}_stcmps`]: total,
|
|
[`repeating_cmpspe_${rowId}_xp_cmps`]: xp
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
// =========================================
|
|
// === 8. XP TOTALE UTILISÉE & RESTANTE ====
|
|
// =========================================
|
|
function calculateTotalXP() {
|
|
getSectionIDs("repeating_cmpspe", ids => {
|
|
const attrs = ["xp_total", ...baseAttrs.map(a => `xp_${a}`), ...Object.keys(commonSkills).map(s => `xp_scmp_${s}`)];
|
|
ids.forEach(id => attrs.push(`repeating_cmpspe_${id}_xp_cmps`));
|
|
|
|
getAttrs(attrs, values => {
|
|
let totalXP = 0;
|
|
|
|
baseAttrs.forEach(attr => totalXP += parseInt(values[`xp_${attr}`]) || 0);
|
|
Object.keys(commonSkills).forEach(skill => totalXP += parseInt(values[`xp_scmp_${skill}`]) || 0);
|
|
ids.forEach(id => totalXP += parseInt(values[`repeating_cmpspe_${id}_xp_cmps`]) || 0);
|
|
|
|
const total = parseInt(values.xp_total) || 0;
|
|
setAttrs({
|
|
xp_utilisee: totalXP,
|
|
xp_restante: total - totalXP
|
|
});
|
|
});
|
|
});
|
|
}
|
|
on("change:xp_total sheet:opened", calculateTotalXP);
|
|
baseAttrs.forEach(attr => on(`change:xp_${attr}`, calculateTotalXP));
|
|
Object.keys(commonSkills).forEach(skill => on(`change:xp_scmp_${skill}`, calculateTotalXP));
|
|
on("change:repeating_cmpspe:xp_cmps", calculateTotalXP);
|
|
|
|
// ===============================
|
|
// === LOGO DU VILLAGE ======
|
|
// ===============================
|
|
|
|
on("change:village sheet:opened", () => {
|
|
getAttrs(["village"], values => {
|
|
const logoURL = values.village || "";
|
|
setAttrs({ village_logo: logoURL });
|
|
});
|
|
});
|
|
|
|
|
|
|
|
// =====================================
|
|
// === 10. VALIDATION SPÉ CHAKRA =======
|
|
// =====================================
|
|
on("change:chakra_acere change:chakra_colossal change:chakra_endurci change:chakra_explosif change:chakra_fulgurant change:chakra_hereditaire change:chakra_imperieux change:chakra_inepuisable change:chakra_puissant change:chakra_remanent change:specialisation_chakra", () => {
|
|
const chakraAttrs = [
|
|
"chakra_acere", "chakra_colossal", "chakra_endurci", "chakra_explosif",
|
|
"chakra_fulgurant", "chakra_hereditaire", "chakra_imperieux", "chakra_inepuisable",
|
|
"chakra_puissant", "chakra_remanent", "specialisation_chakra"
|
|
];
|
|
|
|
getAttrs(chakraAttrs, values => {
|
|
const toInt = v => parseInt(values[v]) || 0;
|
|
|
|
const total =
|
|
toInt("chakra_acere") + toInt("chakra_colossal") + toInt("chakra_endurci") +
|
|
toInt("chakra_explosif") + toInt("chakra_fulgurant") + toInt("chakra_hereditaire") +
|
|
toInt("chakra_imperieux") + toInt("chakra_inepuisable") +
|
|
toInt("chakra_puissant") + toInt("chakra_remanent");
|
|
|
|
const max = toInt("specialisation_chakra");
|
|
|
|
setAttrs({ chakra_specialisation_total: total });
|
|
|
|
if (total > max) {
|
|
setTimeout(() => {
|
|
alert(`⚠️ Vous avez dépassé la limite de spécialisations (${max}). Veuillez ajuster vos points.`);
|
|
}, 50);
|
|
}
|
|
|
|
const fields = chakraAttrs.slice(0, -1); // sans specialisation_chakra
|
|
fields.forEach(name => {
|
|
const input = document.querySelector(`input[name="attr_${name}"]`);
|
|
if (input) {
|
|
input.classList.toggle("over-limit", total > max);
|
|
}
|
|
});
|
|
});
|
|
});
|
|
|
|
on("change:prenom", () => {
|
|
getAttrs(["prenom"], values => {
|
|
const nom = values.prenom || "";
|
|
setAttrs({ character_name: nom });
|
|
});
|
|
});
|