Outils pour utilisateurs

Outils du site


simulateur_saison3

Différences

Ci-dessous, les différences entre deux révisions de la page.

Lien vers cette vue comparative

Prochaine révision
Révision précédente
simulateur_saison3 [2026/09/20 23:54] – créée nanakisimulateur_saison3 [2026/09/21 00:22] (Version actuelle) nanaki
Ligne 1: Ligne 1:
-Voici un simulateur en version beta, n'hésitez pas à faire vos remarques sur le discord ou le forum. 
- 
- 
 <html> <html>
 <head> <head>
Ligne 9: Ligne 6:
 <style> <style>
 body { body {
-  font-family: Arial; +    font-family: Arial; 
-  background:#0b1a13; +    background:#0b1a13; 
-  color:#7dd3fc; +    color:#7dd3fc; 
-  padding:10px;+    padding:10px
 +
 + 
 +h1,h2 { 
 +    color:gold; 
 +
 + 
 +.box { 
 +    border:1px solid gold; 
 +    padding:10px; 
 +    margin-bottom:10px; 
 +
 + 
 +select { 
 +    margin:3px; 
 +    max-width:180px; 
 +
 + 
 +input { 
 +    margin:3px; 
 +    width:60px; 
 +
 + 
 +.statLine { 
 +    display:flex; 
 +    justify-content:space-between; 
 +    max-width:260px; 
 +
 + 
 +.result { 
 +    background:#000; 
 +    color:#00ffcc; 
 +    padding:10px; 
 +    white-space:pre-wrap; 
 +
 + 
 +.small { 
 +    font-size:12px; 
 +    color:#aaa; 
 +
 + 
 +.equipStats { 
 +    margin-left:5px; 
 +
 + 
 +.bonus { 
 +    color:#00ff00; 
 +
 + 
 +.malus { 
 +    color:#ff3333; 
 +
 + 
 +.special { 
 +    color:#00ff00;
 } }
-h1,h2 { color:gold;} 
-.box { border:1px solid gold; padding:10px; margin-bottom:10px; } 
-select { margin:3px; max-width:150px; } 
-input { margin:3px; width:60px; } 
-.statLine { display:flex; justify-content:space-between; max-width:260px; } 
-.result { background:#000; color:#00ffcc; padding:10px; white-space:pre-wrap; } 
-.small { font-size:12px; color:#aaa; } 
 </style> </style>
 </head> </head>
Ligne 29: Ligne 73:
  
 <div class="box"> <div class="box">
-Race : +    Race : 
-<select id="race"></select> +    <select id="race"></select> 
-<h3>Stats de base</h3> + 
-<pre id="baseStats"></pre>+    <h3>Stats de base</h3> 
 +    <pre id="baseStats"></pre>
 </div> </div>
  
 <div class="box"> <div class="box">
-<h3>Améliorations</h3> +    <h3>Améliorations</h3> 
-<div id="stats"></div> +    <div id="stats"></div> 
-<div id="cost"></div>+    <div id="cost"></div>
 </div> </div>
  
 <div class="box"> <div class="box">
-<h3>Équipement (3 max + 1 anneau)</h3> +    <h3>Équipement (3 max + 1 anneau)</h3> 
-Tête <select id="head"></select> <span id="headStats"></span><br> + 
-Cou <select id="neck"></select> <span id="neckStats"></span><br> +    Tête 
-Dos <select id="cape"></select> <span id="capeStats"></span><br> +    <select id="head"></select> 
-Main droite <select id="rightHand"></select> <span id="rightStats"></span><br> +    <span id="headStats" class="equipStats"></span> 
-Main gauche <select id="leftHand"></select> <span id="leftStats"></span><br> +    <br> 
-Corps <select id="body"></select> <span id="bodyStats"></span><br> + 
-Anneau <select id="ring"></select> <span id="ringStats"></span><br> +    Dos 
-Pieds <select id="feet"></select> <span id="feetStats"></span> +    <select id="cape"></select> 
-<div id="limitWarn" style="color:red;"></div>+    <span id="capeStats" class="equipStats"></span> 
 +    <br> 
 + 
 +    Main droite 
 +    <select id="rightHand"></select> 
 +    <span id="rightStats" class="equipStats"></span> 
 +    <br> 
 + 
 +    Main gauche 
 +    <select id="leftHand"></select> 
 +    <span id="leftStats" class="equipStats"></span> 
 +    <br> 
 + 
 +    Corps 
 +    <select id="body"></select> 
 +    <span id="bodyStats" class="equipStats"></span> 
 +    <br> 
 + 
 +    Anneau 
 +    <select id="ring"></select> 
 +    <span id="ringStats" class="equipStats"></span> 
 +    <br> 
 + 
 +    Pieds 
 +    <select id="feet"></select> 
 +    <span id="feetStats" class="equipStats"></span> 
 + 
 +    <div id="limitWarn" style="color:red;"></div>
 </div> </div>
  
 <div class="box"> <div class="box">
-<h3>Passifs</h3> +    <h3>Passifs</h3> 
-<div id="skills"></div>+    <div id="skills"></div>
 </div> </div>
  
 <div class="box"> <div class="box">
-<h3>Sorts actifs</h3> +    <h3>Sorts actifs</h3> 
-<div id="spells"></div> +    <div id="spells"></div> 
-<div id="spellBonus" class="small"></div>+    <div id="spellBonus" class="small"></div>
 </div> </div>
  
 +<div class="box">
 +    <h3>Simulateur de poussée</h3>
  
 +    Force assaillant
 +    <input id="pushF" type="number" value="10">
  
-<!-- ================= POUSSEE ================= --+    Renforcement 
-<div class="box"> +    <select id="pushBuff"
-<h3>Simulateur de poussée</h3>+        <option value="0">0</option
 +        <option value="6">+6</option> 
 +    </select>
  
-Force assaillant <input id="pushF" type="number" value="10"> +    <br>
-Renforcement <select id="pushBuff"><option value="0">0</option><option value="6">+6</option></select><br>+
  
-Endurance <input id="pushE" type="number" value="5"+    Endurance 
-Agilité <input id="pushAgi" type="number" value="5"><br>+    <input id="pushE" type="number" value="5">
  
-PV actuels <input id="pushPV" type="number" value="50"><br>+    Agilité 
 +    <input id="pushAgi" type="number" value="5">
  
-Instabilité <select id="pushDebuff"><option value="0">0</option><option value="-6">-6</option></select> +    <br>
-Stabilité <select id="pushStab"><option value="0">0</option><option value="6">+6</option><option value="12">+12</option><option value="18">+18</option></select>+
  
-<pre id="pushResult" class="result"></pre>+    PV actuels 
 +    <input id="pushPV" type="number" value="50"> 
 + 
 +    <br> 
 + 
 +    Instabilité 
 +    <select id="pushDebuff"> 
 +        <option value="0">0</option> 
 +        <option value="-6">-6</option> 
 +    </select> 
 + 
 +    Stabilité 
 +    <select id="pushStab"> 
 +        <option value="0">0</option> 
 +        <option value="6">+6</option> 
 +        <option value="12">+12</option> 
 +        <option value="18">+18</option> 
 +    </select> 
 + 
 +    <pre id="pushResult" class="result"></pre>
 </div> </div>
  
 <button onclick="resetAll()">Reset</button> <button onclick="resetAll()">Reset</button>
- 
 <button onclick="exportBuild()">📤 Export</button> <button onclick="exportBuild()">📤 Export</button>
 <button onclick="importBuild()">📥 Import</button> <button onclick="importBuild()">📥 Import</button>
Ligne 92: Ligne 187:
  
 <pre id="result" class="result"></pre> <pre id="result" class="result"></pre>
 +
  
 <script> <script>
  
-/===== RACES =====+/========================================================= 
 +   RACES 
 +========================================================= */ 
 const races = { const races = {
-Elfe:{a:2,cc:9,ct:10,f:9,e:4,agi:9,p:5,mvt:5,pv:55,pm:30,r:4,rm:6,fm:8,m:6}, + 
-Nain:{a:2,cc:11,ct:8,f:11,e:6,agi:6,p:4,mvt:4,pv:50,pm:15,r:5,rm:4,fm:10,m:3}, +    Elfe:{ 
-Géant:{a:2,cc:9,ct:10,f:12,e:4,agi:7,p:4,mvt:5,pv:65,pm:20,r:5,rm:5,fm:6,m:5}, +        a:2, 
-Olympien:{a:2,cc:10,ct:9,f:10,e:5,agi:8,p:4,mvt:5,pv:60,pm:30,r:5,rm:6,fm:7,m:5}, +        cc:9, 
-HS:{a:2,cc:8,ct:9,f:7,e:3,agi:11,p:5,mvt:6,pv:45,pm:40,r:3,rm:7,fm:9,m:5}+        ct:10, 
 +        f:9, 
 +        e:4, 
 +        agi:9, 
 +        p:5, 
 +        mvt:5, 
 +        pv:55, 
 +        pm:30, 
 +        r:4, 
 +        rm:6, 
 +        fm:8, 
 +        pui:6
 +        res:5 
 +    }, 
 + 
 +    Nain:{ 
 +        a:2, 
 +        cc:11, 
 +        ct:8, 
 +        f:11, 
 +        e:6, 
 +        agi:7, 
 +        p:4, 
 +        mvt:4, 
 +        pv:60, 
 +        pm:25, 
 +        r:7, 
 +        rm:5, 
 +        fm:10, 
 +        pui:4, 
 +        res:3 
 +    }, 
 + 
 +    Géant:{ 
 +        a:2, 
 +        cc:9, 
 +        ct:10, 
 +        f:12, 
 +        e:4, 
 +        agi:8, 
 +        p:4, 
 +        mvt:5, 
 +        pv:70, 
 +        pm:30, 
 +        r:6, 
 +        rm:6, 
 +        fm:6, 
 +        pui:5
 +        res:4 
 +    }, 
 + 
 +    Olympien:{ 
 +        a:2, 
 +        cc:10, 
 +        ct:9, 
 +        f:10, 
 +        e:5, 
 +        agi:9, 
 +        p:4, 
 +        mvt:5, 
 +        pv:60, 
 +        pm:30, 
 +        r:5, 
 +        rm:6, 
 +        fm:7, 
 +        pui:5
 +        res:5 
 +    }, 
 + 
 +    HS:{ 
 +        a:2, 
 +        cc:8, 
 +        ct:9, 
 +        f:8, 
 +        e:3, 
 +        agi:11, 
 +        p:5, 
 +        mvt:6, 
 +        pv:50, 
 +        pm:40, 
 +        r:3, 
 +        rm:6, 
 +        fm:9, 
 +        pui:5
 +        res:6 
 +    }
 }; };
  
-/===== COST =====+ 
 +/========================================================= 
 +   COUT DES AMELIORATIONS 
 +========================================================= */ 
 const costTable = { const costTable = {
-a:[800,200,100], 
-cc:[100,50,30], ct:[110,50,30], 
-f:[120,55,30], e:[120,55,30], 
-agi:[95,45,25], 
-m:[110,55,35], 
-fm:[100,50,30], 
-p:[110,85,78], 
-pv:[4,2,1], 
-pm:[5,3,1], 
-mvt:[100,50,30], 
-r:[40,30,15], 
-rm:[50,40,20] 
-}; 
  
-// ===== PASSIFS ===== +    a:[800,200,100],
-const skills = [ +
-{name:"Fulgurance",desc:"+1 toucher au cac + 1 esquive tous les 5 mouvements"}, +
-{name:"Couverture",desc:"85% CC / 15% AGI"}, +
-{name:"Réflexes fulgurants",desc:"85% AGI / 15% CC"}, +
-{name:"Berserker",desc:"+1 toucher au cac tous les 10 pv perdus"}, +
-{name:"Griffes",desc:"+3 dégâts au poings"}, +
-{name:"Anguille",desc:"Avantage esquive"+
-];+
  
-// ===== SORTS ===== +    cc:[100,50,30]
-const spells = [ +    ct:[110,50,30],
-{name:"Peau de granit",dex:0,prot:2}+
-{name:"Coup précis",dex:2,prot:0},+
  
-{name:"Maladresse",dex:-2,prot:0}+    f:[120,55,30]
-{name:"Vulnérabilité",dex:0,prot:-2},+    e:[120,55,30],
  
-{name:"Puissance de la nature",dex:2,prot:2},+    agi:[95,45,25],
  
-{name:"Aide",dex:4,prot:0}+    pui:[110,55,35]
-{name:"Malchance",dex:-4,prot:0},+    res:[110,55,35],
  
-{name:"Fatigue",dex:0,prot:-4}, +    fm:[100,50,30],
-{name:"Réflexes accrus",dex:0,prot:4},+
  
-{name:"Bénédiction",dex:4,prot:4},+    p:[110,85,78],
  
-{name:"Puissance du lutin capricieux",dex:-4,prot:-4},+    pv:[4,2,1], 
 +    pm:[5,3,1],
  
-{name:"Exténuation",dex:0,prot:-8}, +    mvt:[100,50,30],
-{name:"Sauvegarde",dex:0,prot:8}, +
-{name:"Guigne",dex:-8,prot:0}, +
-{name:"Virtuose",dex:8,prot:0} +
-];+
  
 +    r:[40,30,15],
 +    rm:[50,40,20]
 +};
  
  
 +/* =========================================================
 +   EQUIPEMENTS
 +=========================================================
 +
 +Règle :
 +- équipement normal = toutes les races
 +- équipement exo = uniquement la race indiquée dans "race"
 +
 +Rappel :
 +R   = r
 +Rés = res
 +*/
  
 const equipments = [ const equipments = [
  
-/* ========================================================= +/* ========================= 
-   COMMUNS +   COMMUN 
-   ========================================================= */+========================= */
  
-{name:"Bâton de marche",race:"all",slot:"weapon",stats:{mvt:1}}+{ 
-{name:"Gladius",race:"all",slot:"weapon",stats:{cc:1}}+    name:"Bâton de marche", 
-{name:"Sceptre",race:"all",slot:"weapon",stats:{fm:1}}+    race:"all", 
-{name:"Fustibale",race:"all",slot:"weapon",stats:{ct:1,cc:-2}}, +    slot:"rightHand", 
-{name:"Arbalète de poing",race:"all",slot:"weapon",stats:{f:12,ct:1}}, +    stats:{mvt:1} 
-{name:"Torche",race:"all",slot:"weapon",stats:{cc:-2,f:-2}}, +},
-{name:"Lance barbelée",race:"all",slot:"weapon",stats:{ct:1,f:1,cc:-1}}, +
-{name:"Lance",race:"all",slot:"weapon",stats:{ct:1,cc:-1}}, +
-{name:"Rapière",race:"all",slot:"weapon",stats:{cc:2,f:-1}}, +
-{name:"Arc",race:"all",slot:"weapon2h",stats:{ct:1,cc:-1}}, +
-{name:"Arc long",race:"all",slot:"weapon2h",stats:{ct:2,f:-1,cc:-2}}, +
-{name:"Bélier",race:"all",slot:"weapon2h",stats:{cc:-2}}, +
-{name:"Pioche",race:"all",slot:"weapon2h",stats:{}}, +
-{name:"Catalyseur magique",race:"all",slot:"weapon2h",stats:{cc:-3}}, +
-{name:"Targe",race:"all",slot:"offhand",stats:{cc:1,agi:-1}}, +
-{name:"Bouclier Parma",race:"all",slot:"offhand",stats:{e:1,agi:-1}}, +
-{name:"Écu brillant",race:"all",slot:"offhand",stats:{r:1,agi:-1}}, +
-{name:"Armure matelassée",race:"all",slot:"body",stats:{e:1,agi:-1}}, +
-{name:"Toge d'apprenti",race:"all",slot:"body",stats:{r:1,agi:-1}}, +
-{name:"Ceinture",race:"all",slot:"body",stats:{r:1}}, +
-{name:"Cape de chasse",race:"all",slot:"cape",stats:{ct:1}}, +
-{name:"Couronne d'adonis",race:"all",slot:"head",stats:{rm:1}}, +
-{name:"Visière",race:"all",slot:"head",stats:{p:1,cc:-1,fm:-1}}, +
-{name:"Collier tressé",race:"all",slot:"head",stats:{pv:5,pm:5}}, +
-{name:"Bottes de marche",race:"all",slot:"feet",stats:{mvt:1}}, +
-{name:"Sceptre de mage",race:"all",slot:"weapon",stats:{m:1,fm:1}}, +
-{name:"Sarbacane",race:"all",slot:"weapon",stats:{ct:1,f:10,cc:-1}}, +
-{name:"Pierre Noire",race:"all",slot:"weapon",stats:{f:1,cc:-1}},+
  
 +{
 +    name:"Gladius",
 +    race:"all",
 +    slot:"rightHand",
 +    stats:{cc:1}
 +},
  
-/* =========================================================+
 +    name:"Sceptre", 
 +    race:"all", 
 +    slot:"rightHand", 
 +    stats:{fm:1} 
 +}, 
 + 
 +
 +    name:"Fustibale", 
 +    race:"all", 
 +    slot:"rightHand", 
 +    stats:{ct:1,cc:-2} 
 +}, 
 + 
 +
 +    name:"Arbalète de poing", 
 +    race:"all", 
 +    slot:"rightHand", 
 +    stats:{f:12,ct:1} 
 +}, 
 + 
 +
 +    name:"Torche", 
 +    race:"all", 
 +    slot:"rightHand", 
 +    stats:{cc:-2,f:-2,feu:true} 
 +}, 
 + 
 +
 +    name:"Lance barbelée", 
 +    race:"all", 
 +    slot:"rightHand", 
 +    stats:{ct:1,f:1,cc:-1} 
 +}, 
 + 
 +
 +    name:"Lance", 
 +    race:"all", 
 +    slot:"rightHand", 
 +    stats:{ct:1,cc:-1} 
 +}, 
 + 
 +
 +    name:"Rapière", 
 +    race:"all", 
 +    slot:"rightHand", 
 +    stats:{cc:2,f:-1} 
 +}, 
 + 
 +
 +    name:"Arc", 
 +    race:"all", 
 +    slot:"rightHand", 
 +    stats:{ct:1,cc:-1} 
 +}, 
 + 
 +
 +    name:"Arc long", 
 +    race:"all", 
 +    slot:"rightHand", 
 +    stats:{ct:2,f:-1,cc:-2} 
 +}, 
 + 
 +
 +    name:"Bélier", 
 +    race:"all", 
 +    slot:"rightHand", 
 +    stats:{demolition:10,cc:-2} 
 +}, 
 + 
 +
 +    name:"Pioche", 
 +    race:"all", 
 +    slot:"rightHand", 
 +    stats:{demolition:5} 
 +}, 
 + 
 +
 +    name:"Catalyseur magique", 
 +    race:"all", 
 +    slot:"rightHand", 
 +    stats:{magique:true,cc:-3,demolition:10} 
 +}, 
 + 
 +
 +    name:"Targe", 
 +    race:"all", 
 +    slot:"leftHand", 
 +    stats:{cc:1,agi:-1} 
 +}, 
 + 
 +
 +    name:"Bouclier Parma", 
 +    race:"all", 
 +    slot:"leftHand", 
 +    stats:{e:1,agi:-1} 
 +}, 
 + 
 +
 +    name:"Écu brillant", 
 +    race:"all", 
 +    slot:"leftHand", 
 +    stats:{res:1,agi:-1} 
 +}, 
 + 
 +
 +    name:"Armure matelassée", 
 +    race:"all", 
 +    slot:"body", 
 +    stats:{e:1,agi:-1} 
 +}, 
 + 
 +
 +    name:"Toge d'apprenti", 
 +    race:"all", 
 +    slot:"body", 
 +    stats:{res:1,agi:-1} 
 +}, 
 + 
 +
 +    name:"Ceinture", 
 +    race:"all", 
 +    slot:"body", 
 +    stats:{r:1} 
 +}, 
 + 
 +
 +    name:"Cape de chasse", 
 +    race:"all", 
 +    slot:"cape", 
 +    stats:{ct:1} 
 +}, 
 + 
 +
 +    name:"Couronne d'adonis", 
 +    race:"all", 
 +    slot:"head", 
 +    stats:{rm:1} 
 +}, 
 + 
 +
 +    name:"Visière", 
 +    race:"all", 
 +    slot:"head", 
 +    stats:{p:1,cc:-1,fm:-1} 
 +}, 
 + 
 +
 +    name:"Collier tressé", 
 +    race:"all", 
 +    slot:"head", 
 +    stats:{pv:5,pm:5} 
 +}, 
 + 
 +
 +    name:"Bottes de marche", 
 +    race:"all", 
 +    slot:"feet", 
 +    stats:{mvt:1} 
 +}, 
 + 
 +
 +    name:"Sceptre de mage", 
 +    race:"all", 
 +    slot:"rightHand", 
 +    stats:{pui:1,fm:1} 
 +}, 
 + 
 +
 +    name:"Sarbacane", 
 +    race:"all", 
 +    slot:"rightHand", 
 +    stats:{ct:1,f:10,cc:-1} 
 +}, 
 + 
 +
 +    name:"Pierre Noire", 
 +    race:"all", 
 +    slot:"rightHand", 
 +    stats:{f:1,cc:-1} 
 +}, 
 + 
 + 
 +/* =========================
    OLYMPIENS    OLYMPIENS
-   ========================================================= */+========================= */
  
-{name:"Spatha",race:"Olympien",slot:"weapon",stats:{cc:1,f:1}}+{ 
-{name:"Lame sainte",race:"Olympien",slot:"weapon",stats:{cc:1,m:1}}+    name:"Spatha", 
-{name:"Pilum",race:"Olympien",slot:"weapon",stats:{f:1,mvt:1,cc:-1}}+    race:"Olympiens", 
-{name:"Sceptre de puissance",race:"Olympien",slot:"weapon",stats:{m:1,pm:8}}, +    slot:"rightHand", 
-{name:"Gastraphète",race:"Olympien",slot:"weapon2h",stats:{f:15,ct:-2,cc:-1}}, +    stats:{cc:1,f:1} 
-{name:"Bouclier Ancile",race:"Olympien",slot:"offhand",stats:{e:1,pv:5}}, +},
-{name:"Armure Hoplitique",race:"Olympien",slot:"body",stats:{e:1,r:1}}, +
-{name:"Lorica (exo)",race:"Olympien",slot:"body",stats:{e:1,r:1}}, +
-{name:"Cape de dueliste",race:"Olympien",slot:"cape",stats:{cc:2,f:-1}}, +
-{name:"Diadème",race:"Olympien",slot:"head",stats:{r:1,rm:2}}, +
-{name:"Tiare d'Oracle",race:"Olympien",slot:"head",stats:{fm:1,p:1}}, +
-{name:"Cnémides",race:"Olympien",slot:"feet",stats:{mvt:1,rm:1,r:1}},+
  
 +{
 +    name:"Lame sainte",
 +    race:"Olympiens",
 +    slot:"rightHand",
 +    stats:{cc:1,pui:1,magique:true}
 +},
  
 +{
 +    name:"Pilum",
 +    race:"Olympiens",
 +    slot:"rightHand",
 +    stats:{f:1,mvt:1,cc:-1}
 +},
  
-/* =========================================================+
 +    name:"Sceptre de puissance", 
 +    race:"Olympiens", 
 +    slot:"rightHand", 
 +    stats:{pui:1,pm:8} 
 +}, 
 + 
 +
 +    name:"Gastraphète", 
 +    race:"Olympiens", 
 +    slot:"rightHand", 
 +    stats:{f:15,ct:-2,cc:-1} 
 +}, 
 + 
 +
 +    name:"Bouclier Ancile", 
 +    race:"Olympiens", 
 +    slot:"leftHand", 
 +    stats:{e:1,pv:5} 
 +}, 
 + 
 +
 +    name:"Armure Hoplitique", 
 +    race:"Olympiens", 
 +    slot:"body", 
 +    stats:{e:1,res:1} 
 +}, 
 + 
 +
 +    name:"Lorica", 
 +    race:"Olympiens", 
 +    exo:true, 
 +    slot:"body", 
 +    stats:{e:1,res:1} 
 +}, 
 + 
 +
 +    name:"Cape de dueliste", 
 +    race:"Olympiens", 
 +    slot:"cape", 
 +    stats:{cc:2,f:-1} 
 +}, 
 + 
 +
 +    name:"Diadème", 
 +    race:"Olympiens", 
 +    slot:"head", 
 +    stats:{r:1,rm:2} 
 +}, 
 + 
 +
 +    name:"Tiare d'Oracle", 
 +    race:"Olympiens", 
 +    slot:"head", 
 +    stats:{fm:1,p:1} 
 +}, 
 + 
 +
 +    name:"Cnémides", 
 +    race:"Olympiens", 
 +    slot:"feet", 
 +    stats:{mvt:1,rm:1,r:1} 
 +}, 
 + 
 + 
 +/* =========================
    ELFES    ELFES
-   ========================================================= */+========================= */
  
-{name:"Lance de garde Sylvestre",race:"Elfe",slot:"weapon",stats:{cc:1,f:1}}+{ 
-{name:"Baton du Pelerin",race:"Elfe",slot:"weapon",stats:{mvt:1,fm:1}}+    name:"Lance de garde Sylvestre", 
-{name:"Charybde",race:"Elfe",slot:"weapon2h",stats:{m:2,e:-1}}+    race:"Elfes", 
-{name:"Arc Ensorcelé",race:"Elfe",slot:"weapon2h",stats:{ct:1,rm:2,cc:-1}}, +    slot:"rightHand", 
-{name:"Arc Elfique (exo)",race:"Elfe",slot:"weapon2h",stats:{ct:1,f:1,mvt:1,cc:-2}}, +    stats:{cc:1,f:1} 
-{name:"Targe renforcée",race:"Elfe",slot:"offhand",stats:{e:1,cc:1}}, +},
-{name:"Cuirasse",race:"Elfe",slot:"body",stats:{r:1,r:2}}, +
-{name:"Armure de garde Sylvestre",race:"Elfe",slot:"body",stats:{r:2,pm:-10}}, +
-{name:"Cape de feuillages",race:"Elfe",slot:"cape",stats:{fm:1,pv:5}}, +
-{name:"Marque Forestière",race:"Elfe",slot:"head",stats:{pm:10,r:1}}, +
-{name:"Capuche du chasseur",race:"Elfe",slot:"head",stats:{ct:1,r:1,rm:1}}, +
-{name:"Bottes Tâlroval",race:"Elfe",slot:"feet",stats:{mvt:2}},+
  
 +{
 +    name:"Baton du Pelerin",
 +    race:"Elfes",
 +    slot:"rightHand",
 +    stats:{mvt:1,fm:1}
 +},
  
 +{
 +    name:"Charybde",
 +    race:"Elfes",
 +    slot:"rightHand",
 +    stats:{pui:2,e:-1}
 +},
  
-/* =========================================================+
 +    name:"Arc Ensorcelé", 
 +    race:"Elfes", 
 +    slot:"rightHand", 
 +    stats:{ct:1,rm:2,cc:-1,magique:true} 
 +}, 
 + 
 +
 +    name:"Arc Elfique", 
 +    race:"Elfes", 
 +    exo:true, 
 +    slot:"rightHand", 
 +    stats:{ct:1,f:1,mvt:1,cc:-2} 
 +}, 
 + 
 +
 +    name:"Targe renforcée", 
 +    race:"Elfes", 
 +    slot:"leftHand", 
 +    stats:{e:1,cc:1} 
 +}, 
 + 
 +
 +    name:"Cuirasse", 
 +    race:"Elfes", 
 +    slot:"body", 
 +    stats:{res:1,r:2} 
 +}, 
 + 
 +
 +    name:"Armure de garde Sylvestre", 
 +    race:"Elfes", 
 +    exo:true, 
 +    slot:"body", 
 +    stats:{res:2,pm:-10} 
 +}, 
 + 
 +
 +    name:"Cape de feuillages", 
 +    race:"Elfes", 
 +    slot:"cape", 
 +    stats:{fm:1,pv:5} 
 +}, 
 + 
 +
 +    name:"Marque Forestière", 
 +    race:"Elfes", 
 +    slot:"head", 
 +    stats:{pm:10,r:1} 
 +}, 
 + 
 +
 +    name:"Capuche du chasseur", 
 +    race:"Elfes", 
 +    slot:"head", 
 +    stats:{ct:1,r:1,rm:1} 
 +}, 
 + 
 +
 +    name:"Bottes Tâlroval", 
 +    race:"Elfes", 
 +    slot:"feet", 
 +    stats:{mvt:2} 
 +}, 
 + 
 + 
 +/* =========================
    NAINS    NAINS
-   ========================================================= */+========================= */
  
-{name:"Labrys",race:"Nain",slot:"weapon",stats:{cc:1,f:1}}+{ 
-{name:"Hache runique",race:"Nain",slot:"weapon",stats:{cc:1,fm:1}}+    name:"Labrys", 
-{name:"Marteau runique",race:"Nain",slot:"weapon2h",stats:{m:2}}+    race:"Nains", 
-{name:"Baton d'archimage",race:"Nain",slot:"weapon",stats:{fm:1,pm:10}}, +    slot:"rightHand", 
-{name:"Marteau de guerre",race:"Nain",slot:"weapon2h",stats:{f:2,cc:-1}}, +    stats:{cc:1,f:1} 
-{name:"Bouclier Miroir",race:"Nain",slot:"offhand",stats:{r:1,pv:5}}, +},
-{name:"Armure runique",race:"Nain",slot:"body",stats:{e:1,pm:8}}, +
-{name:"Cotte de mailles",race:"Nain",slot:"body",stats:{e:2}}, +
-{name:"Cape dorée (exo)",race:"Nain",slot:"cape",stats:{r:1,mvt:1}}, +
-{name:"Casque grossissant",race:"Nain",slot:"head",stats:{ct:2,p:-1}}, +
-{name:"Casque de guerre",race:"Nain",slot:"head",stats:{pv:5,r:1,pm:5,rm:1}}, +
-{name:"Solerets",race:"Nain",slot:"feet",stats:{mvt:1,pm:5,pv:5}},+
  
 +{
 +    name:"Hache runique",
 +    race:"Nains",
 +    slot:"rightHand",
 +    stats:{cc:1,fm:1}
 +},
  
 +{
 +    name:"Marteau runique",
 +    race:"Nains",
 +    slot:"rightHand",
 +    stats:{magique:true,pui:2}
 +},
  
-/* ========================================================= +{ 
-   GÉANTS +    name:"Baton d'archimage", 
-   ========================================================= */+    race:"Nains", 
 +    slot:"rightHand", 
 +    stats:{fm:1,pm:10} 
 +},
  
-{name:"Masse d'arme",race:"Géant",slot:"weapon",stats:{cc:1,f:1}}, +
-{name:"Hallebarde de pierre",race:"Géant",slot:"weapon",stats:{f:1,mvt:1}}+    name:"Marteau de guerre", 
-{name:"Javelot lourd",race:"Géant",slot:"weapon2h",stats:{f:2,ct:-1,cc:-1}}, +    race:"Nains", 
-{name:"Pierre en fusion",race:"Géant",slot:"weapon",stats:{ct:-2}}, +    slot:"rightHand", 
-{name:"Baton de shaman (exo)",race:"Géant",slot:"weapon2h",stats:{fm:2}}, +    stats:{f:2,cc:-1} 
-{name:"Bouclier Clipeus",race:"Géant",slot:"offhand",stats:{e:1,pv:10}}, +},
-{name:"Peau de granit manifiée",race:"Géant",slot:"body",stats:{r:1,rm:2}}, +
-{name:"Broigne",race:"Géant",slot:"body",stats:{r:2,fm:-1}}, +
-{name:"Cape de l'arpenteur",race:"Géant",slot:"cape",stats:{cc:1,ct:1}}, +
-{name:"Fétiche de shaman",race:"Géant",slot:"head",stats:{r:2,pm:5}}, +
-{name:"Coiffe de rage",race:"Géant",slot:"head",stats:{cc:1,pv:5,r:1}}, +
-{name:"Bottes incandescentes",race:"Géant",slot:"feet",stats:{mvt:1,r:1,pv:5}},+
  
 +{
 +    name:"Bouclier Miroir",
 +    race:"Nains",
 +    slot:"leftHand",
 +    stats:{res:1,pv:5}
 +},
  
 +{
 +    name:"Armure runique",
 +    race:"Nains",
 +    slot:"body",
 +    stats:{e:1,pm:8}
 +},
  
-/* =========================================================+
 +    name:"Cotte de mailles", 
 +    race:"Nains", 
 +    exo:true, 
 +    slot:"body", 
 +    stats:{e:2,esquive:-4} 
 +}, 
 + 
 +
 +    name:"Cape dorée", 
 +    race:"Nains", 
 +    exo:true, 
 +    slot:"cape", 
 +    stats:{res:1,mvt:1} 
 +}, 
 + 
 +
 +    name:"Casque grossissant", 
 +    race:"Nains", 
 +    slot:"head", 
 +    stats:{ct:2,p:-1} 
 +}, 
 + 
 +
 +    name:"Casque de guerre", 
 +    race:"Nains", 
 +    slot:"head", 
 +    stats:{pv:5,r:1,pm:5,rm:1} 
 +}, 
 + 
 +
 +    name:"Solerets", 
 +    race:"Nains", 
 +    slot:"feet", 
 +    stats:{mvt:1,pm:5,pv:5} 
 +}, 
 + 
 + 
 +/* ========================= 
 +   GEANTS 
 +========================= */ 
 + 
 +
 +    name:"Masse d'arme", 
 +    race:"Géants", 
 +    slot:"rightHand", 
 +    stats:{cc:1,f:1} 
 +}, 
 + 
 +
 +    name:"Hallebarde de pierre", 
 +    race:"Géants", 
 +    slot:"rightHand", 
 +    stats:{f:1,mvt:1} 
 +}, 
 + 
 +
 +    name:"Javelot lourd", 
 +    race:"Géants", 
 +    slot:"rightHand", 
 +    stats:{f:2,ct:-1,cc:-1} 
 +}, 
 + 
 +
 +    name:"Pierre en fusion", 
 +    race:"Géants", 
 +    slot:"rightHand", 
 +    stats:{magique:true,ct:-2,feu:true} 
 +}, 
 + 
 +
 +    name:"Baton de shaman", 
 +    race:"Géants", 
 +    exo:true, 
 +    slot:"rightHand", 
 +    stats:{fm:2} 
 +}, 
 + 
 +
 +    name:"Bouclier Clipeus", 
 +    race:"Géants", 
 +    slot:"leftHand", 
 +    stats:{e:1,pv:10} 
 +}, 
 + 
 +
 +    name:"Peau de granit manifiée", 
 +    race:"Géants", 
 +    slot:"body", 
 +    stats:{res:1,rm:2,esquive:-2} 
 +}, 
 + 
 +
 +    name:"Broigne", 
 +    race:"Géants", 
 +    exo:true, 
 +    slot:"body", 
 +    stats:{res:2,fm:-1} 
 +}, 
 + 
 +
 +    name:"Cape de l'arpenteur", 
 +    race:"Géants", 
 +    slot:"cape", 
 +    stats:{cc:1,ct:1} 
 +}, 
 + 
 +
 +    name:"Fétiche de shaman", 
 +    race:"Géants", 
 +    slot:"head", 
 +    stats:{r:2,pm:5} 
 +}, 
 + 
 +
 +    name:"Coiffe de rage", 
 +    race:"Géants", 
 +    slot:"head", 
 +    stats:{cc:1,pv:5,r:1} 
 +}, 
 + 
 +
 +    name:"Bottes incandescentes", 
 +    race:"Géants", 
 +    slot:"feet", 
 +    stats:{mvt:1,r:1,pv:5} 
 +}, 
 + 
 + 
 +/* =========================
    HS    HS
-   ========================================================= */+========================= */
  
-{name:"Ceste",race:"HS",slot:"weapon",stats:{f:1,cc:1}}+{ 
-{name:"Mains de mana",race:"HS",slot:"weapon2h",stats:{cc:2}}+    name:"Ceste", 
-{name:"Baton de Sage",race:"HS",slot:"weapon",stats:{m:1,agi:1}}+    race:"HS", 
-{name:"Orbe de mana",race:"HS",slot:"weapon2h",stats:{m:2,fm:-1}}, +    slot:"rightHand", 
-{name:"Bouclier en lianes",race:"HS",slot:"offhand",stats:{r:1,agi:1}}, +    stats:{f:1,cc:1} 
-{name:"Manteau de feuillage",race:"HS",slot:"body",stats:{e:1,agi:1}}, +},
-{name:"Armure de fourrure",race:"HS",slot:"body",stats:{e:2,rm:-3}}, +
-{name:"Cape de mage",race:"HS",slot:"cape",stats:{fm:1,rm:1}}, +
-{name:"Collier d'apaisement",race:"HS",slot:"head",stats:{pv:10,pm:5}}, +
-{name:"Coiffe de rapace",race:"HS",slot:"head",stats:{ct:1,p:1}}, +
-{name:"Bottes griffues (exo)",race:"HS",slot:"feet",stats:{cc:1,f:1,pv:5}}, +
-{name:"Bottes de voleur",race:"HS",slot:"feet",stats:{mvt:1,agi:1}},+
  
 +{
 +    name:"Mains de mana",
 +    race:"HS",
 +    slot:"rightHand",
 +    stats:{magique:true,cc:2}
 +},
  
 +{
 +    name:"Baton de Sage",
 +    race:"HS",
 +    slot:"rightHand",
 +    stats:{pui:1,agi:1}
 +},
  
-/* =========================================================+
 +    name:"Orbe de mana", 
 +    race:"HS", 
 +    slot:"rightHand", 
 +    stats:{pui:2,fm:-1} 
 +}, 
 + 
 +
 +    name:"Bouclier en lianes", 
 +    race:"HS", 
 +    slot:"leftHand", 
 +    stats:{res:1,agi:1} 
 +}, 
 + 
 +
 +    name:"Manteau de feuillage", 
 +    race:"HS", 
 +    slot:"body", 
 +    stats:{e:1,agi:1} 
 +}, 
 + 
 +
 +    name:"Armure de fourrure", 
 +    race:"HS", 
 +    exo:true, 
 +    slot:"body", 
 +    stats:{e:2,rm:-3} 
 +}, 
 + 
 +
 +    name:"Cape de mage", 
 +    race:"HS", 
 +    slot:"cape", 
 +    stats:{fm:1,rm:1} 
 +}, 
 + 
 +
 +    name:"Collier d'apaisement", 
 +    race:"HS", 
 +    slot:"head", 
 +    stats:{pv:10,pm:5} 
 +}, 
 + 
 +
 +    name:"Coiffe de rapace", 
 +    race:"HS", 
 +    slot:"head", 
 +    stats:{ct:1,p:1} 
 +}, 
 + 
 +
 +    name:"Bottes griffues", 
 +    race:"HS", 
 +    exo:true, 
 +    slot:"feet", 
 +    stats:{cc:1,f:1,pv:5} 
 +}, 
 + 
 +
 +    name:"Bottes de voleur", 
 +    race:"HS", 
 +    slot:"feet", 
 +    stats:{mvt:1,agi:1} 
 +}, 
 + 
 + 
 +/* =========================
    LUTINS    LUTINS
-   ========================================================= */+========================= */
  
-{name:"Anneau d'Horizon",race:"Lutin",slot:"ring",stats:{p:1}}, +{ 
-{name:"Anneau de Caprice",race:"Lutin",slot:"ring",stats:{fm:1}}, +    name:"Anneau d'Horizon", 
-{name:"Anneau de Puissance",race:"Lutin",slot:"ring",stats:{m:1}}, +    race:"Lutin", 
-{name:"Anneau de Férocité",race:"Lutin",slot:"ring",stats:{f:1}}, +    slot:"ring", 
-{name:"Anneau de Résistance",race:"Lutin",slot:"ring",stats:{r:1}}, +    stats:{p:1} 
-{name:"Anneau d'Équilibre",race:"Lutin",slot:"ring",stats:{pv:5,pm:5}}, +}, 
-{name:"Anneau de Finesse",race:"Lutin",slot:"ring",stats:{cc:1}}, + 
-{name:"Anneau de Prétention",race:"Lutin",slot:"ring",stats:{ct:1}}, +{ 
-{name:"Anneau de Tenacité",race:"Lutin",slot:"ring",stats:{e:1}}, +    name:"Anneau de Caprice", 
-{name:"Anneau de Souplesse",race:"Lutin",slot:"ring",stats:{agi:1}}, +    race:"Lutin", 
-{name:"Bottes de 7 lieux (exo)",race:"Lutin",slot:"feet",stats:{mvt:3}}+    slot:"ring", 
 +    stats:{fm:1} 
 +}, 
 + 
 +{ 
 +    name:"Anneau de Puissance", 
 +    race:"Lutin", 
 +    slot:"ring", 
 +    stats:{pui:1} 
 +}, 
 + 
 +{ 
 +    name:"Anneau de Férocité", 
 +    race:"Lutin", 
 +    slot:"ring", 
 +    stats:{f:1} 
 +}, 
 + 
 +{ 
 +    name:"Anneau de Résistance", 
 +    race:"Lutin", 
 +    slot:"ring", 
 +    stats:{res:1} 
 +}, 
 + 
 +{ 
 +    name:"Anneau d'Équilibre", 
 +    race:"Lutin", 
 +    slot:"ring", 
 +    stats:{pv:5,pm:5} 
 +}, 
 + 
 +{ 
 +    name:"Anneau de Finesse", 
 +    race:"Lutin", 
 +    slot:"ring", 
 +    stats:{cc:1} 
 +}, 
 + 
 +{ 
 +    name:"Anneau de Prétention", 
 +    race:"Lutin", 
 +    slot:"ring", 
 +    stats:{ct:1} 
 +}, 
 + 
 +{ 
 +    name:"Anneau de Tenacité", 
 +    race:"Lutin", 
 +    slot:"ring", 
 +    stats:{e:1} 
 +}, 
 + 
 +{ 
 +    name:"Anneau de Souplesse", 
 +    race:"Lutin", 
 +    slot:"ring", 
 +    stats:{agi:1} 
 +}, 
 + 
 +{ 
 +    name:"Bottes de 7 lieux", 
 +    race:"Lutin", 
 +    exo:true, 
 +    slot:"feet", 
 +    stats:{mvt:3} 
 +}
  
 ]; ];
  
  
-// ===== INIT =====+/* ========================================================= 
 +   PASSIFS 
 +========================================================= */ 
 + 
 +const skills 
 + 
 +    { 
 +        name:"Fulgurance", 
 +        desc:"+1 toucher au cac + 1 esquive tous les 5 mouvements" 
 +    }, 
 + 
 +    { 
 +        name:"Couverture", 
 +        desc:"85% CC / 15% AGI" 
 +    }, 
 + 
 +    { 
 +        name:"Réflexes fulgurants", 
 +        desc:"85% AGI / 15% CC" 
 +    }, 
 + 
 +    { 
 +        name:"Berserker", 
 +        desc:"+1 toucher au cac tous les 10 pv perdus" 
 +    }, 
 + 
 +    { 
 +        name:"Griffes", 
 +        desc:"+3 dégâts au poings" 
 +    }, 
 + 
 +    { 
 +        name:"Anguille", 
 +        desc:"Avantage esquive" 
 +    } 
 + 
 +]; 
 + 
 + 
 +/* ========================================================= 
 +   SORTS 
 +========================================================= */ 
 + 
 +const spells = [ 
 + 
 +    {name:"Peau de granit",dex:0,prot:2}, 
 +    {name:"Coup précis",dex:2,prot:0}, 
 +    {name:"Maladresse",dex:-2,prot:0}, 
 +    {name:"Vulnérabilité",dex:0,prot:-2}, 
 +    {name:"Puissance de la nature",dex:2,prot:2}, 
 +    {name:"Aide",dex:4,prot:0}, 
 +    {name:"Malchance",dex:-4,prot:0}, 
 +    {name:"Fatigue",dex:0,prot:-4}, 
 +    {name:"Réflexes accrus",dex:0,prot:4}, 
 +    {name:"Bénédiction",dex:4,prot:4}, 
 +    {name:"Puissance du lutin capricieux",dex:-4,prot:-4}, 
 +    {name:"Exténuation",dex:0,prot:-8}, 
 +    {name:"Sauvegarde",dex:0,prot:8}, 
 +    {name:"Guigne",dex:-8,prot:0}, 
 +    {name:"Virtuose",dex:8,prot:0} 
 + 
 +]; 
 + 
 + 
 +/* ========================================================= 
 +   INITIALISATION 
 +========================================================= */ 
 const raceSelect = document.getElementById("race"); const raceSelect = document.getElementById("race");
-const statsDiv = document.getElementById("stats"); 
-const baseDiv = document.getElementById("baseStats"); 
  
-for(let r in races){ +for(const r in races){ 
-raceSelect.innerHTML +`<option>${r}</option>`;+    const o=document.createElement("option"); 
 +    o.value=r
 +    o.textContent=r; 
 +    raceSelect.appendChild(o);
 } }
  
-// stats UI + 
-for(let s in costTable){ +/* ========================================================= 
-statsDiv.innerHTML += ` +   STATS AMELIORATIONS 
-<div class="statLine"> +========================================================= */ 
-<span>${s}</span> + 
-<input type="number" id="${s}" value="0min="0"> +const statsDiv=document.getElementById("stats"); 
-</div>`;+ 
 +for(const stat in costTable){ 
 + 
 +    const line=document.createElement("div"); 
 +    line.className="statLine"; 
 + 
 +    const label=document.createElement("label"); 
 +    label.textContent=stat.toUpperCase(); 
 + 
 +    const input=document.createElement("input"); 
 +    input.type="number"
 +    input.id="up_"+stat; 
 +    input.value=0
 +    input.min=0
 + 
 +    input.addEventListener("input",calculate); 
 + 
 +    line.appendChild(label); 
 +    line.appendChild(input); 
 + 
 +    statsDiv.appendChild(line);
 } }
  
-// passifs + 
-skills.forEach(s=>+/* ========================================================= 
-document.getElementById("skills").innerHTML += ` +   PASSIFS 
-<label> +========================================================= */ 
-<input type="checkbox" class="skilldata-name="${s.name}"> + 
-<b>${s.name}</b><br> +const skillsDiv=document.getElementById("skills"); 
-<span class="small">${s.desc}</span> + 
-</label><br>`;+skills.forEach((s,i)=>{ 
 + 
 +    const label=document.createElement("label"); 
 + 
 +    const cb=document.createElement("input"); 
 +    cb.type="checkbox"
 +    cb.id="skill_"+i; 
 + 
 +    cb.addEventListener("change",calculate); 
 + 
 +    label.appendChild(cb); 
 +    label.appendChild( 
 +        document.createTextNode(" "+s.name+— "+s.desc) 
 +    ); 
 + 
 +    skillsDiv.appendChild(label); 
 +    skillsDiv.appendChild(document.createElement("br"));
 }); });
  
  
-// affichage sorts +/* ========================================================= 
-spells.forEach(s=>+   SORTS 
-document.getElementById("spells").innerHTML += ` +========================================================= */ 
-<label> + 
-<input type="checkbox" class="spelldata-dex="${s.dex}data-prot="${s.prot}"> +const spellsDiv=document.getElementById("spells"); 
-<b>${s.name}</b>  + 
-<span class="small">(Dex ${s.dex>=0?"+":""}${s.dex} | Prot ${s.prot>=0?"+":""}${s.prot})</span> +spells.forEach((s,i)=>{ 
-</label><br>`;+ 
 +    const label=document.createElement("label"); 
 + 
 +    const cb=document.createElement("input"); 
 +    cb.type="checkbox"
 +    cb.id="spell_"+i; 
 + 
 +    cb.addEventListener("change",calculate); 
 + 
 +    label.appendChild(cb); 
 +    label.appendChild( 
 +        document.createTextNode( 
 +            " "+s.name+ 
 +            " (Dex "+s.dex+", Prot "+s.prot+")" 
 +        
 +    ); 
 + 
 +    spellsDiv.appendChild(label); 
 +    spellsDiv.appendChild(document.createElement("br"));
 }); });
  
-// base stats 
-function displayBase(){ 
-baseDiv.textContent = JSON.stringify(races[raceSelect.value],null,2); 
-} 
-raceSelect.addEventListener("change", displayBase); 
-displayBase(); 
  
-/===== COST ===== +/* ========================================================= 
-function calcCost(b,m1,m2,n){ +   EQUIPEMENTS 
-let total=0,last=b; +========================================================= */
-for(let i=1;i<=n;i++){ +
-if(i===1) last=b; +
-else if(i<=3) last+=m1; +
-else last+=m2; +
-total+=last; +
-} +
-return total; +
-}+
  
-function updateCost()+const head=document.getElementById("head"); 
-let total=0+const cape=document.getElementById("cape"); 
-for(let s in costTable){ +const rightHand=document.getElementById("rightHand"); 
-let v=+document.getElementById(s).value||0+const leftHand=document.getElementById("leftHand")
-let [b,m1,m2]=costTable[s]+const body=document.getElementById("body"); 
-total+=calcCost(b,m1,m2,v); +const ring=document.getElementById("ring")
-+const feet=document.getElementById("feet");
-document.getElementById("cost").innerHTML="💰 PI : "+total; +
-}+
  
-// ===== SLOTS ===== 
 const slots = { const slots = {
-head:head,neck:neck,cape:cape,rightHand:rightHand, + 
-leftHand:leftHand,body:body,ring:ring,feet:feet+    head:head, 
 +    cape:cape, 
 +    rightHand:rightHand, 
 +    leftHand:leftHand, 
 +    body:body, 
 +    ring:ring, 
 +    feet:feet 
 }; };
  
-function resetSlots(){ + 
-Object.values(slots).forEach(s=>s.innerHTML="<option value=''>--</option>");+/* ========================================================= 
 +   REGLE D'EQUIPEMENT 
 +========================================================= 
 + 
 +Normal : 
 +    toutes les races 
 + 
 +Exo : 
 +    uniquement la race propriétaire 
 +*/ 
 + 
 +function canEquip(e,race){ 
 + 
 +    if(e.exo === true)
 +        return e.race === race; 
 +    } 
 + 
 +    return true;
 } }
 +
 +
 +/* =========================================================
 +   REMPLISSAGE DES EQUIPEMENTS
 +========================================================= */
  
 function fillEquip(){ function fillEquip(){
-let r=raceSelect.value; 
  
-equipments.forEach((e,i)=>+    const race=raceSelect.value;
-if(e.race!=="all"&&e.race!==r) return;+
  
-if(e.slot==="weapon"||e.slot==="weapon2h"rightHand.innerHTML+=`<option value="${i}">${e.name}</option>`; +    for(const key in slots){
-if(e.slot==="offhand") leftHand.innerHTML+=`<option value="${i}">${e.name}</option>`; +
-if(e.slot==="body") body.innerHTML+=`<option value="${i}">${e.name}</option>`; +
-if(e.slot==="head") head.innerHTML+=`<option value="${i}">${e.name}</option>`; +
-if(e.slot==="neck") neck.innerHTML+=`<option value="${i}">${e.name}</option>`; +
-if(e.slot==="cape") cape.innerHTML+=`<option value="${i}">${e.name}</option>`; +
-if(e.slot==="feet") feet.innerHTML+=`<option value="${i}">${e.name}</option>`; +
-if(e.slot==="ring") ring.innerHTML+=`<option value="${i}">${e.name}</option>`; +
-}); +
-}+
  
-raceSelect.addEventListener("change",()=>{ +        const select=slots[key];
-resetSlots(); +
-fillEquip(); +
-autoCalculate(); +
-});+
  
-resetSlots(); +        select.innerHTML="";
-fillEquip();+
  
-// ===== BONUS ===== +        const empty=document.createElement("option"); 
-function displayStats(select,id){ +        empty.value=""
-let v=select.value; +        empty.textContent="— Aucun —"; 
-let el=document.getElementById(id); +        select.appendChild(empty); 
-if(v===""){el.innerHTML="";return;} + 
-let e=equipments[v]; +        equipments 
-let txt=""; +            .filter(=> e.slot===key) 
-for(let s in e.stats){ +            .filter(e => canEquip(e,race)) 
-let val=e.stats[s]+            .forEach(e=>{ 
-txt+=val>0?`+${val} ${s} `:`${val${s`+ 
-+                const o=document.createElement("option")
-el.innerHTML=txt;+ 
 +                o.value=equipments.indexOf(e); 
 + 
 +                o.textContent= 
 +                    e.name + 
 +                    (e.exo ? " (exo)" : "")
 + 
 +                select.appendChild(o); 
 +            }); 
 + 
 + select.onchange=()=>{ 
 + 
 + const statIds = { 
 + head:"headStats", 
 + cape:"capeStats", 
 + rightHand:"rightStats", 
 + leftHand:"leftStats", 
 + body:"bodyStats", 
 + ring:"ringStats", 
 + feet:"feetStats" 
 + }
 + 
 + displayStats(select,statIds[key]); 
 + 
 + checkLimit(key); 
 + calculate(); 
 + }; 
 +    
 + 
 +    displayAllEquipStats(); 
 +    checkLimit(); 
 +    calculate();
 } }
  
-const slotToStatId = { + 
-head:"headStats", +/* ========================================================= 
-neck:"neckStats", +   AFFICHAGE DES CARACTERISTIQUES 
-cape:"capeStats", +========================================================= */ 
-rightHand:"rightStats", + 
-leftHand:"leftStats", +const statNames={ 
-body:"bodyStats", + 
-ring:"ringStats", +    f:"F", 
-feet:"feetStats"+    e:"E", 
 + 
 +    agi:"Agi", 
 + 
 +    cc:"CC", 
 +    ct:"CT", 
 +    fm:"FM", 
 + 
 +    mvt:"Mvt", 
 + 
 +    pv:"PV", 
 +    pm:"PM", 
 + 
 +    r:"R", 
 +    res:"Rés", 
 +    rm:"RM", 
 + 
 +    p:"P", 
 +    pui:"Pui", 
 + 
 +    esquive:"Esquive", 
 + 
 +    demolition:"Démolition", 
 + 
 +    magique:"Magique", 
 +    feu:"Feu"
 }; };
  
-Object.entries(slots).forEach(([k,s])=>{ 
-s.addEventListener("change",()=>{ 
-displayStats(s, slotToStatId[k]); 
-checkLimit(); 
-autoCalculate(); 
-}); 
-}); 
  
-// ===== LIMIT ===== +function displayStats(select,id){
-function checkLimit(){ +
-let count=0; +
-Object.entries(slots).forEach(([k,s])=>{ +
-if(k!=="ring"&&s.value!=="") count++; +
-});+
  
-if(count>=3){ +    const el=document.getElementById(id); 
-limitWarn.innerText="Limite atteinte"; + 
-Object.entries(slots).forEach(([k,s])=>+    if(!el){ 
-if(k!=="ring"&&s.value==="") s.disabled=true+        return; 
-}); +    } 
-}else{ + 
-limitWarn.innerText=""; +    const index=select.value; 
-Object.values(slots).forEach(s=>s.disabled=false);+ 
 +    if(index===""){ 
 +        el.innerHTML=""; 
 +        return; 
 +    } 
 + 
 +    const e=equipments[Number(index)]; 
 + 
 +    if(!e){ 
 +        el.innerHTML=""; 
 +        return; 
 +    } 
 + 
 +    let html=""; 
 + 
 +    for(const [stat,valueof Object.entries(e.stats || {})){ 
 + 
 +        const name=statNames[stat] || stat; 
 + 
 +        /* Booléens : Magique / Feu */ 
 +        if(value===true){ 
 + 
 +            html += 
 +                `<span class="special">${name}</span> `; 
 + 
 +            continue; 
 +        } 
 + 
 +        if(value===false || value===0){ 
 +            continue
 +        } 
 + 
 +        if(value>0)
 + 
 +            html += 
 +                `<span class="bonus">+${value} ${name}</span> `
 + 
 +        }else{ 
 + 
 +            html += 
 +                `<span class="malus">${value} ${name}</span> `
 + 
 +        } 
 +    } 
 + 
 +    el.innerHTML=html;
 } }
 +
 +
 +function displayAllEquipStats(){
 +
 +    displayStats(head,"headStats");
 +    displayStats(cape,"capeStats");
 +    displayStats(rightHand,"rightStats");
 +    displayStats(leftHand,"leftStats");
 +    displayStats(body,"bodyStats");
 +    displayStats(ring,"ringStats");
 +    displayStats(feet,"feetStats");
 } }
  
-// ===== CALCUL ===== 
  
-function calculate(){+/* ========================================================= 
 +   LIMITE 3 EQUIPEMENTS + 1 ANNEAU 
 +========================================================= */
  
-let activeSpells = [...document.querySelectorAll(".spell:checked")];+function checkLimit(changedSlot = null){
  
-let bestDex = 0; +    let count = 0;
-let bestProt = 0;+
  
-activeSpells.forEach(s=>{ +    for(const key in slots){
-let d = +s.dataset.dex; +
-let p = +s.dataset.prot;+
  
-if(Math.abs(d> Math.abs(bestDex)) bestDex = d; +        if(key === "ring"){ 
-if(Math.abs(p) > Math.abs(bestProt)) bestProt = p+            continue
-});+        }
  
-let char={...races[raceSelect.value]};+        if(slots[key].value !== ""){ 
 +            count++; 
 +        } 
 +    }
  
-// stats investies +    const warn = document.getElementById("limitWarn"); 
-for(let s in costTable){ + 
-let v=+document.getElementById(s).value||0+    if(count > 3){ 
-char[s]=(char[s]||0)+v;+ 
 +        // Si un équipement vient d'être ajouté, 
 +        // on le retire immédiatement 
 +        if(changedSlot && changedSlot !== "ring"){ 
 +            slots[changedSlot].value ""; 
 +            displayStats( 
 +                slots[changedSlot]
 +                changedSlot "Stats" 
 +            ); 
 +        } 
 + 
 +        count--; 
 + 
 +        warn.textContent = 
 +            "⚠️ Maximum 3 équipements hors anneau."; 
 + 
 +    }else{ 
 + 
 +        warn.textContent = ""; 
 +    }
 } }
  
-// equip + 
-Object.values(slots).forEach(sel=>+/* ========================================================= 
-if(sel.value==="") return+   RESET DES EQUIPEMENTS 
-let e=equipments[sel.value]; +========================================================= *
-for(let s in e.stats){ + 
-char[s]=(char[s]||0)+e.stats[s];+function resetSlots(){ 
 + 
 +    for(const key in slots){ 
 + 
 +        slots[key].value=""; 
 +    } 
 + 
 +    displayAllEquipStats(); 
 +    checkLimit();
 } }
-}); 
  
-// ===== DÉS ===== 
-let ccDice = char.cc * 2; 
-let ctDice = char.ct * 2; 
-let fmDice = char.fm * 2; 
-let agiDice = char.agi * 2; 
  
-/===== VALEURS FINALES ===== +/* ========================================================= 
-let finalCC ccDice + bestDex; +   STATS DE BASE 
-let finalCT ctDice + bestDex; +========================================================= */ 
-let finalFM fmDice + bestDex;+ 
 +function displayBase(){
  
 +    const r=races[raceSelect.value];
  
-// ===== CT DISTANCE ===== +    document.getElementById("baseStats").textContent 
-function getCTDistance(baseCT, distance)+        JSON.stringify(r,null,2);
-if(distance <2) return baseCT; +
-return baseCT - (distance - 2) * 3;+
 } }
  
-let ct1 = getCTDistance(finalCT,1); 
-let ct2 = getCTDistance(finalCT,2); 
-let ct3 = getCTDistance(finalCT,3); 
-let ct4 = getCTDistance(finalCT,4); 
  
 +/* =========================================================
 +   CALCUL DU COUT
 +========================================================= */
  
-// ===== ESQUIVE =====+function calcCost(){
  
-let checked [...document.querySelectorAll(".skill:checked")].map(e=>e.dataset.name);+    let total=0;
  
-// ===== ESQUIVE CAC ===== +    for(const stat in costTable){
-// règle : meilleur entre CC et AGI (dés), puis conversion jet + protection+
  
-let esquiveCAC Math.max(ccDice, agiDice);+        const input=document.getElementById("up_"+stat);
  
-// conversion en jet + protection +        if(!input){ 
-esquiveCAC = esquiveCAC + bestProt;+            continue; 
 +        }
  
 +        let level=parseInt(input.value)||0;
  
-// ===== ESQUIVE TIR ===== +        const costs=costTable[stat];
-// règle : +
-// max entre : +
-// (3/4 CC + 1/4 AGI) +
-// (1/4 CC + 3/4 AGI) +
-// arrondi inférieur → dés → ×2 → + protection+
  
-let esquiveA Math.floor(char.cc * 0.75 + char.agi * 0.25); +        for(let i=0;i<level;i++){
-let esquiveB = Math.floor(char.cc * 0.25 char.agi * 0.75);+
  
-// passifs qui remplacent le calcul +            total += 
-let esquiveStat;+                costs[Math.min(i,costs.length-1)]; 
 +        } 
 +    }
  
-if(checked.includes("Réflexes fulgurants")){ +    return total;
-    esquiveStat = Math.floor(char.agi * 0.85 + char.cc * 0.15);+
 } }
-else if(checked.includes("Couverture")){ + 
-    esquiveStat = Math.floor(char.cc * 0.85 char.agi * 0.15); + 
-+function updateCost(){ 
-else{ + 
-    esquiveStat = Math.max(esquiveA, esquiveB);+    document.getElementById("cost").textContent = 
 +        "Coût total : "+calcCost();
 } }
  
-// conversion en jet 
-let esquiveTir = esquiveStat * 2; 
  
-// protection (UNE seule fois) +/* ========================================================= 
-esquiveTir +bestProt;+   CALCUL 
 +========================================================= */
  
-// bonus passifs +function calculate(){
-if(checked.includes("Fulgurance")) +
-    esquiveTir += 1;+
  
 +    const race=races[raceSelect.value];
  
-// ===== ESQUIVE MAGIQUE ===== +    if(!race){ 
-// règle : FM en jet + protection+        return; 
 +    }
  
-let esquiveFM = fmDice + bestProt;+    updateCost(); 
 +    displayBase();
  
-// ===== MAGIE ===== +    /* 
-let jetFM = finalFM;+       Copie des stats raciales 
 +    */
  
-let sorts ""; +    const char={};
-for(let lvl=1; lvl<=5; lvl++){ +
-    let seuil = 6 + 6 * lvl; +
-    let reussite = jetFM >= seuil ? "✅" : "❌";+
  
-    sorts += `Niveau ${lvl} → Seuil ${seuil} | Jet ${jetFM} ${reussite}\n`; +    for(const stat in race){
-}+
  
-// ===== AFFICHAGE ====+        char[stat]=race[stat]; 
-document.getElementById("spellBonus").innerText = +    }
-"Bonus actifs → Dex: "+bestDex+" | Protection: "+bestProt;+
  
-result.textContent = 
  
-"===== PERSONNAGE =====\n"+ +    /* 
-"Race : "+raceSelect.value++       Ajout des améliorations 
 +    */
  
-"\n\n===== STATS =====\n"+    for(const stat in costTable){
-JSON.stringify(char,null,2)++
  
-"\n\n===== JET OFFENSIF =====\n"+        const input=document.getElementById("up_"+stat);
-"\n⚔ CC : jet moyen "+ccDice+" ("+finalCC+")"+ +
-"\n🎯 CT : jet moyen "+ctDice+" ("+finalCT+")"+ +
-"\n✨ FM : jet moyen "+fmDice+" ("+finalFM+")"++
  
-"\n\n===== JET DEFENSIF =====\n"+        if(input){
-"\n🛡 Esquive CAC : "+esquiveCAC+ +
-"\n🏹 Esquive tir : "+esquiveTir+ +
-"\n✨ Esquive FM : "+esquiveFM++
  
-"\n\n===== JET DISTANCE =====\n"+ +            char[stat] 
-"\n🎯 CT distances :"+                (char[stat]||0) 
-"\n1 case : "+ct1+ +                (parseInt(input.value)||0); 
-"\n2 cases : "+ct2+ +        } 
-"\n3 cases : "+ct3+ +    }
-"\n4 cases : "+ct4++
  
-"\n\n===== MAGIE =====\n"+ 
-"Jet FM : "+jetFM+"\n\n"+ 
-sorts; 
-} 
  
 +    /*
 +       Ajout équipement
 +    */
  
-// ===== AUTO ===== +    for(const key in slots){
-function autoCalculate(){ +
-updateCost(); +
-calculate(); +
-}+
  
-// events stats +        const index=slots[key].value;
-document.querySelectorAll("#stats input").forEach(i=>{ +
-i.addEventListener("input", autoCalculate); +
-});+
  
-document.querySelectorAll(".spell").forEach(s=>+        if(index===""){ 
-s.addEventListener("change", autoCalculate)+            continue
-});+        }
  
 +        const e=equipments[Number(index)];
  
-// passifs +        if(!e){ 
-document.querySelectorAll(".skill").forEach(s=>+            continue
-s.addEventListener("change", autoCalculate)+        }
-});+
  
 +        for(const [stat,value] of Object.entries(e.stats||{})){
  
 +            if(typeof value==="number"){
  
-// ===== RESET ===== +                char[stat]=(char[stat]||0)+value; 
-function resetAll(){+            } 
 +        } 
 +    }
  
-document.querySelectorAll("#stats input").forEach(i=>i.value=0); 
-document.querySelectorAll(".spell").forEach(s=>s.checked=false); 
  
-Object.values(slots).forEach(s=>{ +    /* 
-s.value=""; +       Sorts actifs 
-s.disabled=false; +    */
-});+
  
-document.querySelectorAll(".skill").forEach(s=>s.checked=false);+    let bestDex=0; 
 +    let bestProt=0;
  
-resetSlots(); +    let spellDetails=[];
-fillEquip(); +
-displayBase(); +
-checkLimit(); +
-autoCalculate(); +
-}+
  
-// init +    spells.forEach((s,i)=>{
-autoCalculate();+
  
 +        const cb=document.getElementById("spell_"+i);
  
 +        if(cb && cb.checked){
  
-// ===== POUSSEE ===== +            bestDex=Math.max(bestDex,s.dex); 
-function calculatePush(){+            bestProt=Math.max(bestProt,s.prot);
  
-let F=+pushF.value+            spellDetails.push(s.name)
-let buff=+pushBuff.value;+        } 
 +    });
  
-let E=+pushE.value; 
-let agi=+pushAgi.value; 
-let pv=+pushPV.value; 
  
-let instability=+pushDebuff.value; +    document.getElementById("spellBonus").textContent = 
-let stability=+pushStab.value;+        spellDetails.length 
 +        ? 
 +        "Sorts actifs : "+spellDetails.join(", ")+ 
 +        " | Bonus Dex : "+bestDex+ 
 +        " | Protection : "+bestProt 
 +        : 
 +        "";
  
-let attaque=F+buff; 
-let defense=Math.max(E+4,agi)+(pv/10)+instability+stability; 
  
-pushResult.textContent= +    /* 
-"Attaque: "+attaque+ +       Dés de caractéristiques 
-"\nDéfense: "+defense.toFixed(1)+ +    */
-"\n"+(attaque>=defense?"✅ PUSH":"❌ FAIL"); +
-}+
  
-// ===== EVENTS ===== +    const ccDice=char.cc*2; 
-document.querySelectorAll("input").forEach(i=>i.addEventListener("input",()=>+    const ctDice=char.ct*2; 
-updateCost(); calculate(); calculatePush()+    const fmDice=char.fm*2
-}));+    const agiDice=char.agi*2;
  
-document.querySelectorAll("select").forEach(i=>i.addEventListener("change",()=>{ 
-updateCost(); calculate(); calculatePush(); 
-})); 
  
 +    /*
 +       Résultats principaux
 +    */
  
-// ===== INIT ===== +    let result="";
-updateCost(); +
-calculate(); +
-calculatePush();+
  
  
-// ====================================+    result +
-// ENCODE / DECODE +        "=== PERSONNAGE ===\n\n";
-// =====================================+
  
-function encodeBuild(data){ +    result += 
-    return encodeURIComponent(JSON.stringify(data)); +        "Force : "+char.f+"\n";
-}+
  
-function decodeBuild(code){ +    result += 
-    return JSON.parse(decodeURIComponent(code)); +        "Endurance : "+char.e+"\n";
-}+
  
-// ====================================+    result +
-// BUILD DATA +        "Agilité : "+char.agi+"\n";
-// =====================================+
  
-function getBuildData(){+    result += 
 +        "CC : "+char.cc+"\n";
  
-    let data { +    result +
-        raceraceSelect.value, +        "CT "+char.ct+"\n";
-        stats: {}, +
-        equip: {}, +
-        skills: [], +
-        spells: [] +
-    };+
  
-    // stats +    result += 
-    for(let s in costTable){ +        "FM : "+char.fm+"\n"; 
-        data.stats[s] = +document.getElementById(s).value || 0;+ 
 +    result += 
 +        "PV : "+char.pv+"\n"; 
 + 
 +    result += 
 +        "PM : "+char.pm+"\n"; 
 + 
 +    result += 
 +        "Mvt : "+char.mvt+"\n"; 
 + 
 +    result += 
 +        "R : "+char.r+"\n"; 
 + 
 +    result += 
 +        "Rés : "+char.res+"\n"; 
 + 
 +    result += 
 +        "RM : "+char.rm+"\n"; 
 + 
 +    result += 
 +        "P : "+char.p+"\n"; 
 + 
 +    result += 
 +        "Pui : "+char.pui+"\n"; 
 + 
 + 
 +    result += 
 +        "\n=== COMBAT ===\n\n"; 
 + 
 + 
 +    /* 
 +       CC CT FM 
 +    */ 
 + 
 +    const finalCC=ccDice+bestDex; 
 +    const finalCT=ctDice+bestDex; 
 +    const finalFM=fmDice+bestDex; 
 + 
 + 
 +    result += 
 +        "Toucher CAC : "+finalCC+"\n"; 
 + 
 +    result += 
 +        "Toucher tir : "+finalCT+"\n"; 
 + 
 +    result += 
 +        "FM : "+finalFM+"\n"; 
 + 
 + 
 +    /* 
 +       Tir selon distance 
 +    */ 
 + 
 +    result += 
 +        "\n=== TIR ===\n\n"; 
 + 
 +    for(let cases=1;cases<=4;cases++){ 
 + 
 +        let ctDistance=finalCT; 
 + 
 +        if(cases>2){ 
 + 
 +            ctDistance -= 
 +                (cases-2)*3; 
 +        } 
 + 
 +        result +
 +            cases+" case(s) : CT "+ctDistance+"\n";
     }     }
  
-    // équipements 
-    Object.entries(slots).forEach(([k,s])=>{ 
-        data.equip[k] = s.value; 
-    }); 
  
-    // passifs +    /* 
-    document.querySelectorAll(".skill:checked").forEach(s=>+       Esquive CAC 
-        data.skills.push(s.dataset.name); +    */
-    });+
  
-    // sorts +    let meleeDodge= 
-    document.querySelectorAll(".spell:checked").forEach(s=>+        Math.max(ccDice,agiDice)+bestProt; 
-        data.spells.push({ + 
-            dex: +s.dataset.dex, + 
-            prot: +s.dataset.prot +    /
-        });+       Fulgurance 
 +    */ 
 + 
 +    const fulg=document.getElementById("skill_0"); 
 + 
 +    if(fulg && fulg.checked)
 + 
 +        meleeDodge += 1; 
 +    } 
 + 
 + 
 +    result += 
 +        "\n=== ESQUIVE CAC ===\n\n"; 
 + 
 +    result += 
 +        "Esquive : "+meleeDodge+"\n"; 
 + 
 + 
 +    /* 
 +       Esquive tir 
 +    */ 
 + 
 +    let rangedDodge; 
 + 
 +    const couverture= 
 +        document.getElementById("skill_1"); 
 + 
 +    const reflexes= 
 +        document.getElementById("skill_2"); 
 + 
 +    if(couverture && couverture.checked){ 
 + 
 +        rangedDodge= 
 +            Math.floor( 
 +                (ccDice*0.85)+(agiDice*0.15) 
 +            ); 
 + 
 +    }else if(reflexes && reflexes.checked){ 
 + 
 +        rangedDodge= 
 +            Math.floor( 
 +                (agiDice*0.85)+(ccDice*0.15) 
 +            ); 
 + 
 +    }else{ 
 + 
 +        rangedDodge= 
 +            Math.floor( 
 +                (ccDice+agiDice)/2 
 +            ); 
 +    } 
 + 
 +    rangedDodge += bestProt; 
 + 
 +    result += 
 +        "\n=== ESQUIVE TIR ===\n\n"; 
 + 
 +    result += 
 +        "Esquive "+rangedDodge+"\n"; 
 + 
 + 
 +    /* 
 +       Esquive magie 
 +    */ 
 + 
 +    const magicDodge= 
 +        fmDice+bestProt; 
 + 
 +    result += 
 +        "\n=== ESQUIVE MAGIQUE ===\n\n"; 
 + 
 +    result += 
 +        "FM défensive : "+magicDodge+"\n"; 
 + 
 + 
 +    /* 
 +       Seuils magie 
 +    */ 
 + 
 +    const magicLevels=[ 
 +        12, 
 +        18, 
 +        24, 
 +        30, 
 +        36 
 +    ]; 
 + 
 +    result += 
 +        "\n=== SEUILS MAGIE ===\n\n"; 
 + 
 +    magicLevels.forEach((threshold,i)=>
 + 
 +        result += 
 +            "Niveau "+(i+1)
 +            " : "+threshold+ 
 +            " → "+ 
 +            (finalFM>=threshold ? "OK" : "NON")+ 
 +            "\n";
     });     });
  
-    return data;+ 
 +    document.getElementById("result").textContent=result; 
 + 
 +    calculatePush();
 } }
  
-// ===================================== 
-// APPLY BUILD 
-// ===================================== 
  
-function applyBuild(data){+/* ========================================================= 
 +   POUSSEE 
 +========================================================= */ 
 + 
 +function calculatePush(){ 
 + 
 +    const F= 
 +        parseInt( 
 +            document.getElementById("pushF").value 
 +        )||0; 
 + 
 +    const buff= 
 +        parseInt( 
 +            document.getElementById("pushBuff").value 
 +        )||0; 
 + 
 +    const E= 
 +        parseInt( 
 +            document.getElementById("pushE").value 
 +        )||0; 
 + 
 +    const agi= 
 +        parseInt( 
 +            document.getElementById("pushAgi").value 
 +        )||0; 
 + 
 +    const pv= 
 +        parseInt( 
 +            document.getElementById("pushPV").value 
 +        )||0; 
 + 
 +    const instability= 
 +        parseInt( 
 +            document.getElementById("pushDebuff").value 
 +        )||0; 
 + 
 +    const stability= 
 +        parseInt( 
 +            document.getElementById("pushStab").value 
 +        )||0; 
 + 
 + 
 +    const attaque= 
 +        F+buff; 
 + 
 +    const defense= 
 +        Math.max(E+4,agi) 
 +        +(pv/10) 
 +        +instability 
 +        +stability; 
 + 
 + 
 +    const result= 
 +        "Attaque : "+attaque+ 
 +        "\nDéfense : "+defense; 
 + 
 + 
 +    document.getElementById("pushResult").textContent=result; 
 +
 + 
 + 
 +/* ========================================================= 
 +   EVENEMENTS POUSSEE 
 +========================================================= */ 
 + 
 +
 +    "pushF", 
 +    "pushBuff", 
 +    "pushE", 
 +    "pushAgi", 
 +    "pushPV", 
 +    "pushDebuff", 
 +    "pushStab" 
 +].forEach(id=>
 + 
 +    document.getElementById(id) 
 +        .addEventListener("input",calculatePush); 
 + 
 +    document.getElementById(id) 
 +        .addEventListener("change",calculatePush); 
 +}); 
 + 
 + 
 +/* ========================================================= 
 +   RESET COMPLET 
 +========================================================= */ 
 + 
 +function resetAll(){ 
 + 
 +    raceSelect.selectedIndex=0; 
 + 
 +    for(const stat in costTable){ 
 + 
 +        const input=document.getElementById("up_"+stat); 
 + 
 +        if(input){ 
 +            input.value=0; 
 +        } 
 +    } 
 + 
 + 
 +    document 
 +        .querySelectorAll( 
 +            '#skills input[type="checkbox"], #spells input[type="checkbox"]' 
 +        ) 
 +        .forEach(cb=>
 +            cb.checked=false; 
 +        });
  
-    // race 
-    raceSelect.value = data.race || "Elfe"; 
  
-    // recharge équipements race 
     resetSlots();     resetSlots();
 +
     fillEquip();     fillEquip();
  
-    // stats +    calculate(); 
-    for(let s in costTable){ +
-        document.getElementById(s).value = + 
-            data.stats?.[s] || 0;+ 
 +/* ========================================================= 
 +   EXPORT 
 +========================================================= */ 
 + 
 +function exportBuild(){ 
 + 
 +    const build={}; 
 + 
 +    build.race=raceSelect.value; 
 + 
 +    build.stats={}; 
 + 
 +    for(const stat in costTable){ 
 + 
 +        build.stats[stat]= 
 +            parseInt( 
 +                document.getElementById("up_"+stat).value 
 +            )||0;
     }     }
-     
-    // équipements 
-    Object.entries(slots).forEach(([k,s])=>{ 
-        s.value = data.equip?.[k] || ""; 
-    }); 
  
-    // refresh affichage bonus équipements 
-    Object.entries(slots).forEach(([k,s])=>{ 
-        displayStats(s, slotToStatId[k]); 
-    }); 
-     
  
-    // passifs +    build.equipment={};
-    document.querySelectorAll(".skill").forEach(s=>{ +
-        s.checked = +
-            data.skills?.includes(s.dataset.name); +
-    });+
  
-    // sorts +    for(const key in slots){
-    document.querySelectorAll(".spell").forEach(s=>{+
  
-        s.checked data.spells?.some(sp => +        build.equipment[key]
-            sp.dex == +s.dataset.dex && +            slots[key].value; 
-            sp.prot == +s.dataset.prot +    }
-        );+
  
 +
 +    build.skills=[];
 +
 +    skills.forEach((s,i)=>{
 +
 +        const cb=document.getElementById("skill_"+i);
 +
 +        if(cb.checked){
 +            build.skills.push(s.name);
 +        }
     });     });
  
-    displayBase(); 
-    checkLimit(); 
-    autoCalculate(); 
-} 
  
-// ===================================== +    build.spells=[];
-// EXPORT +
-// =====================================+
  
-function exportBuild(){+    spells.forEach((s,i)=>{
  
-    let data getBuildData();+        const cb=document.getElementById("spell_"+i);
  
-    let code = encodeBuild(data);+        if(cb.checked){
  
-    prompt("Copie ton build :", code);+            build.spells.push({ 
 +                dex:s.dex, 
 +                prot:s.prot 
 +            }); 
 +        } 
 +    }); 
 + 
 + 
 +    const text= 
 +        JSON.stringify(build,null,2); 
 + 
 + 
 +    prompt( 
 +        "Copie ton build :", 
 +        text 
 +    );
 } }
  
-// ===================================== 
-// IMPORT 
-// ===================================== 
  
-function importBuild(codeInput){+/* ========================================================= 
 +   IMPORT 
 +========================================================= */
  
-    let code = codeInput;+function importBuild(){
  
-    if(!code){ +    const text= 
-        code = prompt("Colle le code du build :");+        prompt("Colle ton build :")
 + 
 +    if(!text){ 
 +        return;
     }     }
  
-    if(!code) return; 
  
     try{     try{
  
-        code code.trim();+        const build= 
 +            JSON.parse(text);
  
-        let data = decodeBuild(code); 
  
-        applyBuild(data);+        /* 
 +           Race 
 +        */
  
-        alert("✅ Build importé");+        if(build.race && races[build.race]){
  
-    }catch(e){+            raceSelect.value=build.race; 
 +        }
  
-        console.log(e); 
  
-        alert("❌ Code invalide");+        /* 
 +           Stats 
 +        */ 
 + 
 +        if(build.stats){ 
 + 
 +            for(const stat in build.stats){ 
 + 
 +                const input= 
 +                    document.getElementById("up_"+stat); 
 + 
 +                if(input){ 
 + 
 +                    input.value= 
 +                        build.stats[stat]; 
 +                } 
 +            } 
 +        } 
 + 
 + 
 +        /* 
 +           Recrée les listes d'équipements 
 +        */ 
 + 
 +        fillEquip(); 
 + 
 + 
 +        /* 
 +           Equipements 
 + 
 +           On essaye d'abord de retrouver 
 +           l'équipement par son index. 
 +        */ 
 + 
 +        if(build.equipment){ 
 + 
 +            for(const key in build.equipment){ 
 + 
 +                if(!slots[key]){ 
 +                    continue; 
 +                } 
 + 
 +                const wanted= 
 +                    build.equipment[key]; 
 + 
 +                if(wanted==="" || wanted===null){ 
 +                    continue; 
 +                } 
 + 
 +                /* 
 +                   Ancien format : index 
 +                */ 
 + 
 +                if( 
 +                    equipments[wanted] && 
 +                    canEquip( 
 +                        equipments[wanted], 
 +                        raceSelect.value 
 +                    ) 
 +                ){ 
 + 
 +                    slots[key].value=wanted; 
 +                    continue; 
 +                } 
 + 
 + 
 +                /* 
 +                   Nouveau format éventuel : 
 +                   nom de l'objet 
 +                */ 
 + 
 +                const found= 
 +                    equipments.findIndex(e => 
 +                        e.name===wanted && 
 +                        e.slot===key && 
 +                        canEquip( 
 +                            e, 
 +                            raceSelect.value 
 +                        ) 
 +                    ); 
 + 
 +                if(found>=0){ 
 + 
 +                    slots[key].value=found; 
 +                } 
 +            } 
 +        } 
 + 
 + 
 +        /* 
 +           Passifs 
 +        */ 
 + 
 +        document 
 +            .querySelectorAll( 
 +                '#skills input[type="checkbox"]' 
 +            ) 
 +            .forEach(cb=>
 +                cb.checked=false; 
 +            }); 
 + 
 + 
 +        if(Array.isArray(build.skills)){ 
 + 
 +            build.skills.forEach(name=>
 + 
 +                const index= 
 +                    skills.findIndex( 
 +                        s=>s.name===name 
 +                    ); 
 + 
 +                if(index>=0){ 
 + 
 +                    document.getElementById( 
 +                        "skill_"+index 
 +                    ).checked=true; 
 +                } 
 +            }); 
 +        } 
 + 
 + 
 +        /* 
 +           Sorts 
 +        */ 
 + 
 +        document 
 +            .querySelectorAll( 
 +                '#spells input[type="checkbox"]' 
 +            ) 
 +            .forEach(cb=>
 +                cb.checked=false; 
 +            }); 
 + 
 + 
 +        if(Array.isArray(build.spells)){ 
 + 
 +            build.spells.forEach(spell=>
 + 
 +                const index= 
 +                    spells.findIndex( 
 +                        s => 
 +                            s.dex===spell.dex && 
 +                            s.prot===spell.prot 
 +                    ); 
 + 
 +                if(index>=0){ 
 + 
 +                    document.getElementById( 
 +                        "spell_"+index 
 +                    ).checked=true; 
 +                } 
 +            }); 
 +        } 
 + 
 + 
 +        displayAllEquipStats(); 
 +        checkLimit(); 
 +        calculate(); 
 + 
 + 
 +    }catch(err){ 
 + 
 +        alert( 
 +            "Erreur lors de l'import : "
 +            err.message 
 +        );
     }     }
 } }
  
-/===================================== + 
-// LIEN PARTAGE +/* ========================================================= 
-// =====================================+   LIEN 
 +========================================================= */
  
 function copyLink(){ function copyLink(){
  
-    let data getBuildData();+    const build={};
  
-    let code encodeBuild(data);+    build.race=raceSelect.value;
  
-    let url = +    build.stats={};
-        window.location.origin + +
-        window.location.pathname + +
-        "?id=simulateur&build=" + code;+
  
-    navigator.clipboard.writeText(url).then(()=>{+    for(const stat in costTable){
  
-        alert("🔗 Lien copié !");+        build.stats[stat]= 
 +            parseInt( 
 +                document.getElementById("up_"+stat).value 
 +            )||0; 
 +    }
  
-    }).catch(()=>{ 
  
-        prompt("Copie ce lien :", url);+    build.equipment={};
  
 +    for(const key in slots){
 +
 +        const index=slots[key].value;
 +
 +        if(index===""){
 +
 +            build.equipment[key]="";
 +
 +        }else{
 +
 +            /*
 +               On sauvegarde le nom plutôt que l'index.
 +               Cela évite que les liens cassent si
 +               l'ordre des équipements change.
 +            */
 +
 +            build.equipment[key]=
 +                equipments[Number(index)]
 +                ? equipments[Number(index)].name
 +                : "";
 +        }
 +    }
 +
 +
 +    build.skills=[];
 +
 +    skills.forEach((s,i)=>{
 +
 +        if(
 +            document.getElementById("skill_"+i).checked
 +        ){
 +
 +            build.skills.push(s.name);
 +        }
     });     });
 +
 +
 +    build.spells=[];
 +
 +    spells.forEach((s,i)=>{
 +
 +        if(
 +            document.getElementById("spell_"+i).checked
 +        ){
 +
 +            build.spells.push({
 +                dex:s.dex,
 +                prot:s.prot
 +            });
 +        }
 +    });
 +
 +
 +    const encoded=
 +        btoa(
 +            unescape(
 +                encodeURIComponent(
 +                    JSON.stringify(build)
 +                )
 +            )
 +        );
 +
 +
 +    const url=
 +        location.origin+
 +        location.pathname+
 +        "?build="+
 +        encodeURIComponent(encoded);
 +
 +
 +    navigator.clipboard.writeText(url)
 +        .then(()=>{
 +
 +            alert(
 +                "Lien copié dans le presse-papiers."
 +            );
 +
 +        })
 +        .catch(()=>{
 +
 +            prompt(
 +                "Copie ce lien :",
 +                url
 +            );
 +        });
 } }
  
-// ===================================== 
-// AUTO LOAD URL 
-// ===================================== 
  
-(function(){+/* ========================================================= 
 +   CHARGEMENT D'UN BUILD DEPUIS L'URL 
 +========================================================= */ 
 + 
 +function loadBuildFromUrl(){ 
 + 
 +    const params= 
 +        new URLSearchParams( 
 +            location.search 
 +        ); 
 + 
 +    const encoded= 
 +        params.get("build"); 
 + 
 +    if(!encoded){ 
 +        return; 
 +    } 
  
     try{     try{
  
-        let params +        const json
-            new URLSearchParams(window.location.search);+            decodeURIComponent( 
 +                escape( 
 +                    atob( 
 +                        decodeURIComponent(encoded) 
 +                    ) 
 +                ) 
 +            );
  
-        let code = params.get("build"); 
  
-        if(!codereturn;+        const build= 
 +            JSON.parse(json);
  
-        let data = decodeBuild(code); 
  
-        applyBuild(data);+        if(build.race && races[build.race]){
  
-        console.log("✅ Build URL chargé");+            raceSelect.value= 
 +                build.race; 
 +        }
  
-    }catch(e){ 
  
-        console.log("Erreur chargement URL", e);+        fillEquip();
  
 +
 +        /*
 +           Stats
 +        */
 +
 +        if(build.stats){
 +
 +            for(const stat in build.stats){
 +
 +                const input=
 +                    document.getElementById(
 +                        "up_"+stat
 +                    );
 +
 +                if(input){
 +
 +                    input.value=
 +                        build.stats[stat];
 +                }
 +            }
 +        }
 +
 +
 +        /*
 +           Equipements par nom
 +        */
 +
 +        if(build.equipment){
 +
 +            for(const key in build.equipment){
 +
 +                if(!slots[key]){
 +                    continue;
 +                }
 +
 +                const name=
 +                    build.equipment[key];
 +
 +                if(!name){
 +                    continue;
 +                }
 +
 +                const index=
 +                    equipments.findIndex(e =>
 +                        e.name===name &&
 +                        e.slot===key &&
 +                        canEquip(
 +                            e,
 +                            raceSelect.value
 +                        )
 +                    );
 +
 +                if(index>=0){
 +
 +                    slots[key].value=
 +                        index;
 +                }
 +            }
 +        }
 +
 +
 +        /*
 +           Passifs
 +        */
 +
 +        if(Array.isArray(build.skills)){
 +
 +            build.skills.forEach(name=>{
 +
 +                const index=
 +                    skills.findIndex(
 +                        s=>s.name===name
 +                    );
 +
 +                if(index>=0){
 +
 +                    document.getElementById(
 +                        "skill_"+index
 +                    ).checked=true;
 +                }
 +            });
 +        }
 +
 +
 +        /*
 +           Sorts
 +        */
 +
 +        if(Array.isArray(build.spells)){
 +
 +            build.spells.forEach(spell=>{
 +
 +                const index=
 +                    spells.findIndex(
 +                        s =>
 +                            s.dex===spell.dex &&
 +                            s.prot===spell.prot
 +                    );
 +
 +                if(index>=0){
 +
 +                    document.getElementById(
 +                        "spell_"+index
 +                    ).checked=true;
 +                }
 +            });
 +        }
 +
 +
 +        displayAllEquipStats();
 +        checkLimit();
 +        calculate();
 +
 +
 +    }catch(err){
 +
 +        console.error(
 +            "Impossible de charger le build :",
 +            err
 +        );
     }     }
 +}
  
-})();+ 
 +/* ========================================================= 
 +   CHANGEMENT DE RACE 
 +========================================================= */ 
 + 
 +raceSelect.addEventListener( 
 +    "change", 
 +    ()=>{ 
 +        fillEquip(); 
 +        displayBase(); 
 +        calculate(); 
 +    } 
 +); 
 + 
 + 
 +/* ========================================================= 
 +   INITIALISATION 
 +========================================================= */ 
 + 
 +fillEquip(); 
 +displayBase(); 
 +calculatePush()
 +loadBuildFromUrl();
  
 </script> </script>
 +
 </body> </body>
 </html> </html>
simulateur_saison3.1789941264.txt.gz · Dernière modification : 2026/09/20 23:54 de nanaki