MediaWiki:Script/Calculator.js : Différence entre versions
Ligne 6 : | Ligne 6 : | ||
function hideElement(element) { | function hideElement(element) { | ||
element.classList.add("tabber-noactive"); | element.classList.add("tabber-noactive"); | ||
+ | } | ||
+ | |||
+ | |||
+ | function removeAccent(str) { | ||
+ | return str.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase() | ||
+ | } | ||
+ | |||
+ | |||
+ | function toNormalForm(str) { | ||
+ | return removeAccent(str).replace(/[^a-zA-Z0-9 ]/g, "");; | ||
} | } | ||
Ligne 51 : | Ligne 61 : | ||
var secondCell = newRow.insertCell(1); | var secondCell = newRow.insertCell(1); | ||
secondCell.textContent = numberFormat(value2 * 100, 3) + " %"; | secondCell.textContent = numberFormat(value2 * 100, 3) + " %"; | ||
+ | } | ||
+ | |||
+ | |||
+ | function getMinDamages(tableResult) { | ||
+ | return tableResult.rows[2].cells[0].textContent; | ||
} | } | ||
Ligne 207 : | Ligne 222 : | ||
} | } | ||
return {}; | return {}; | ||
+ | } | ||
+ | |||
+ | |||
+ | function getSavedMonsters() { | ||
+ | var savedMonsters = localStorage.getItem("savedMonstersCalculator"); | ||
+ | |||
+ | if (savedMonsters) { | ||
+ | return JSON.parse(savedMonsters); | ||
+ | } | ||
+ | return []; | ||
} | } | ||
Ligne 242 : | Ligne 267 : | ||
function updateSavedCharacters(savedCharacters) { | function updateSavedCharacters(savedCharacters) { | ||
localStorage.setItem("savedCharactersCalculator", JSON.stringify(savedCharacters)); | localStorage.setItem("savedCharactersCalculator", JSON.stringify(savedCharacters)); | ||
+ | } | ||
+ | |||
+ | |||
+ | function updateSavedMonsters(savedMonsters) { | ||
+ | localStorage.setItem("savedMonstersCalculator", JSON.stringify(savedMonsters)); | ||
} | } | ||
Ligne 373 : | Ligne 403 : | ||
hideElement(characters.characterCreation); | hideElement(characters.characterCreation); | ||
} | } | ||
+ | } | ||
+ | |||
+ | |||
+ | function deleteMonster(characters, monsterName, element, battle) { | ||
+ | |||
+ | characters.savedMonsters.splice(characters.savedMonsters.indexOf(monsterName), 1); | ||
+ | |||
+ | element.remove(); | ||
+ | |||
+ | updateSavedMonsters(characters.savedMonsters); | ||
+ | removeBattleChoice(battle, monsterName); | ||
} | } | ||
Ligne 409 : | Ligne 450 : | ||
− | function handleClickOnCharacter(target, characters, battle, edition) { | + | function handleClickOnCharacter(spanInput, target, characters, characterElement, battle, edition) { |
var displayedPseudo = characters.characterCreation.name.value; | var displayedPseudo = characters.characterCreation.name.value; | ||
− | + | var pseudo = spanInput.dataset.name; | |
+ | |||
if (edition) { | if (edition) { | ||
− | |||
− | |||
if (!characters.unsavedChanges) { | if (!characters.unsavedChanges) { | ||
− | updateForm(characters.savedCharacters[pseudo], characters.characterCreation, characters, | + | updateForm(characters.savedCharacters[pseudo], characters.characterCreation, characters, characterElement); |
} else if (displayedPseudo === pseudo) { | } else if (displayedPseudo === pseudo) { | ||
Ligne 427 : | Ligne 467 : | ||
if (result) { | if (result) { | ||
− | updateForm(characters.savedCharacters[pseudo], characters.characterCreation, characters, | + | updateForm(characters.savedCharacters[pseudo], characters.characterCreation, characters, characterElement); |
characters.unsavedChanges = false; | characters.unsavedChanges = false; | ||
} | } | ||
Ligne 436 : | Ligne 476 : | ||
target = target.parentElement; | target = target.parentElement; | ||
} | } | ||
− | |||
− | |||
switch (target.dataset.icon) { | switch (target.dataset.icon) { | ||
Ligne 464 : | Ligne 502 : | ||
var result = confirm("Voulez-vous vraiment supprimer définitivement le personnage " + pseudo + " ?"); | var result = confirm("Voulez-vous vraiment supprimer définitivement le personnage " + pseudo + " ?"); | ||
if (result) { | if (result) { | ||
− | deleteCharacter(characters, pseudo, displayedPseudo, | + | deleteCharacter(characters, pseudo, displayedPseudo, characterElement, battle); |
} | } | ||
break; | break; | ||
Ligne 482 : | Ligne 520 : | ||
if (pseudo) { | if (pseudo) { | ||
spanInput.textContent = pseudo; | spanInput.textContent = pseudo; | ||
− | + | spanInput.setAttribute("data-name", pseudo); | |
} | } | ||
newCharacterTemplate.addEventListener("click", function(event) { | newCharacterTemplate.addEventListener("click", function(event) { | ||
− | |||
var target = event.target; | var target = event.target; | ||
if (target.tagName === "path" || target.tagName === "svg") { | if (target.tagName === "path" || target.tagName === "svg") { | ||
− | handleClickOnCharacter(target, characters, battle); | + | handleClickOnCharacter(spanInput, target, characters, newCharacterTemplate, battle); |
} else { | } else { | ||
− | handleClickOnCharacter( | + | handleClickOnCharacter(spanInput, null, characters, newCharacterTemplate, battle, true); |
} | } | ||
}); | }); | ||
Ligne 498 : | Ligne 535 : | ||
newCharacterTemplate.addEventListener("keydown", function(event) { | newCharacterTemplate.addEventListener("keydown", function(event) { | ||
if (event.keyCode === 13) { | if (event.keyCode === 13) { | ||
− | + | event.target.click(); | |
} | } | ||
}); | }); | ||
Ligne 549 : | Ligne 586 : | ||
[characterDataObject, characterPseudo] = addUniquePseudo(characterDataObject, Object.keys(characters.savedCharacters)); | [characterDataObject, characterPseudo] = addUniquePseudo(characterDataObject, Object.keys(characters.savedCharacters)); | ||
− | |||
selection.removeAllRanges(); | selection.removeAllRanges(); | ||
spanInput.contentEditable = false; | spanInput.contentEditable = false; | ||
spanInput.textContent = characterPseudo; | spanInput.textContent = characterPseudo; | ||
+ | spanInput.setAttribute("data-name", characterPseudo); | ||
updateForm(characterDataObject, characters.characterCreation, characters, selectedCharacter); | updateForm(characterDataObject, characters.characterCreation, characters, selectedCharacter); | ||
Ligne 640 : | Ligne 677 : | ||
event.returnValue = ''; | event.returnValue = ''; | ||
return ''; | return ''; | ||
+ | } | ||
+ | }); | ||
+ | } | ||
+ | |||
+ | |||
+ | function handleNewMonster(characters, monsterTemplate, monstersContainer, battle, monsterName, monsterList) { | ||
+ | var newMonsterTemplate = monsterTemplate.cloneNode(true); | ||
+ | var spanInput = newMonsterTemplate.querySelector("span.input"); | ||
+ | var deleteSvg = newMonsterTemplate.querySelector("svg"); | ||
+ | |||
+ | spanInput.textContent = monsterName; | ||
+ | monstersContainer.appendChild(newMonsterTemplate); | ||
+ | |||
+ | newMonsterTemplate.setAttribute("tabindex", "0"); | ||
+ | newMonsterTemplate.setAttribute("data-name", monsterName); | ||
+ | monstersContainer.appendChild(newMonsterTemplate); | ||
+ | |||
+ | deleteSvg.addEventListener("click", function(event) { | ||
+ | deleteMonster(characters, monsterName, newMonsterTemplate, battle); | ||
+ | var inputMonster = monsterList.querySelector("input[name='" + monsterName + "']"); | ||
+ | inputMonster.checked = false; | ||
+ | }); | ||
+ | } | ||
+ | |||
+ | |||
+ | function monsterManagement(characters, battle) { | ||
+ | |||
+ | function handleDropdown(searchMonster, monsterList) { | ||
+ | |||
+ | searchMonster.addEventListener("focus", function(event) { | ||
+ | showElement(monsterList); | ||
+ | }); | ||
+ | |||
+ | document.addEventListener("mousedown", function(event) { | ||
+ | var target = event.target; | ||
+ | if (!monsterList.contains(target) && !searchMonster.contains(target)) { | ||
+ | hideElement(monsterList); | ||
+ | } | ||
+ | }); | ||
+ | } | ||
+ | |||
+ | function addMonsterNames(monsterList) { | ||
+ | |||
+ | var monsterIndex = 0; | ||
+ | |||
+ | for (var monsterName in monsterData) { | ||
+ | |||
+ | var li = document.createElement("li"); | ||
+ | var label = document.createElement("label"); | ||
+ | var input = document.createElement("input"); | ||
+ | var textNode = document.createTextNode(monsterName); | ||
+ | |||
+ | label.htmlFor = "monster" + monsterIndex; | ||
+ | input.id = "monster" + monsterIndex; | ||
+ | input.type = "checkbox"; | ||
+ | |||
+ | input.name = monsterName; | ||
+ | |||
+ | label.appendChild(input); | ||
+ | label.appendChild(textNode); | ||
+ | li.appendChild(label); | ||
+ | monsterList.appendChild(li); | ||
+ | |||
+ | monsterIndex++; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | function filterNames(searchMonster, monsterList) { | ||
+ | |||
+ | var debounceTimer; | ||
+ | |||
+ | searchMonster.addEventListener("input", function(event) { | ||
+ | clearTimeout(debounceTimer); | ||
+ | debounceTimer = setTimeout(function() { | ||
+ | var value = toNormalForm(event.target.value); | ||
+ | for (var element of monsterList.children) { | ||
+ | if (!isValueInArray(value, toNormalForm(element.textContent))) { | ||
+ | hideElement(element); | ||
+ | } else { | ||
+ | showElement(element); | ||
+ | } | ||
+ | } | ||
+ | }, 500); | ||
+ | }); | ||
+ | } | ||
+ | |||
+ | var monsterTemplate = characters.newMonsterTemplate; | ||
+ | var monstersContainer = characters.monstersContainer; | ||
+ | var monsterList = characters.monsterList; | ||
+ | var searchMonster = characters.searchMonster; | ||
+ | var monsterListForm = characters.monsterListForm; | ||
+ | |||
+ | handleDropdown(searchMonster, monsterList); | ||
+ | addMonsterNames(monsterList, characters.monsterListTemplate); | ||
+ | filterNames(searchMonster, monsterList); | ||
+ | |||
+ | characters.savedMonsters.forEach(function(monsterName) { | ||
+ | handleNewMonster(characters, monsterTemplate, monstersContainer, battle, monsterName, monsterList); | ||
+ | var inputMonster = monsterList.querySelector("input[name='" + monsterName + "']"); | ||
+ | |||
+ | if (inputMonster) { | ||
+ | inputMonster.checked = true; | ||
+ | } else { | ||
+ | deleteMonster(characters, monsterName, newMonsterTemplate, battle); | ||
+ | } | ||
+ | }); | ||
+ | |||
+ | monsterListForm.addEventListener("submit", function(event) { | ||
+ | event.preventDefault(); | ||
+ | }) | ||
+ | |||
+ | monsterListForm.addEventListener("change", function(event) { | ||
+ | |||
+ | var target = event.target; | ||
+ | var monsterName = target.name; | ||
+ | |||
+ | if (monsterName === "search-monster") { | ||
+ | return; | ||
+ | } | ||
+ | |||
+ | if (target.checked) { | ||
+ | |||
+ | handleNewMonster(characters, monsterTemplate, monstersContainer, battle, monsterName, monsterList); | ||
+ | |||
+ | characters.savedMonsters.push(monsterName); | ||
+ | updateSavedMonsters(characters.savedMonsters); | ||
+ | addBattleChoice(battle, monsterName, true); | ||
+ | |||
+ | } else { | ||
+ | var currentMonsterTemplate = monstersContainer.querySelector("[data-name='" + monsterName + "']"); | ||
+ | deleteMonster(characters, monsterName, currentMonsterTemplate, battle); | ||
} | } | ||
}); | }); | ||
Ligne 660 : | Ligne 828 : | ||
− | function addBattleChoice(battle, name) { | + | function addBattleChoice(battle, name, isMonster = false) { |
function createOption(text) { | function createOption(text) { | ||
Ligne 666 : | Ligne 834 : | ||
option.textContent = text; | option.textContent = text; | ||
option.value = text; | option.value = text; | ||
+ | |||
+ | if (!isMonster) { | ||
+ | option.classList.add("notranslate"); | ||
+ | } | ||
+ | |||
return option; | return option; | ||
} | } | ||
Ligne 674 : | Ligne 847 : | ||
− | function updateBattleChoice( | + | function updateBattleChoice(characters, battle) { |
− | |||
− | |||
− | Object.keys(savedCharacters).forEach(function(pseudo) { | + | Object.keys(characters.savedCharacters).forEach(function(pseudo) { |
addBattleChoice(battle, pseudo); | addBattleChoice(battle, pseudo); | ||
+ | }); | ||
+ | |||
+ | characters.savedMonsters.forEach(function(monsterName) { | ||
+ | addBattleChoice(battle, monsterName, true); | ||
}); | }); | ||
} | } | ||
Ligne 685 : | Ligne 860 : | ||
function isPC(character) { | function isPC(character) { | ||
− | if (character.race === | + | if (character.race === 0 || character.race === 1) { |
return false; | return false; | ||
} | } | ||
Ligne 731 : | Ligne 906 : | ||
switch (character.race) { | switch (character.race) { | ||
− | case | + | case "warrior": |
− | case | + | case "sura": |
return 2 * character.str | return 2 * character.str | ||
− | case | + | case "ninja": |
return parseInt(1 / 4 * (character.str + 7 * character.dex)) | return parseInt(1 / 4 * (character.str + 7 * character.dex)) | ||
− | case | + | case "shaman": |
return parseInt(1 / 3 * (5 * character.int + character.dex)) | return parseInt(1 / 3 * (5 * character.int + character.dex)) | ||
− | case | + | case "lycan": |
return character.vit + 2 * character.dex; | return character.vit + 2 * character.dex; | ||
default: | default: | ||
Ligne 806 : | Ligne 981 : | ||
damages = parseInt(damages * battleValues.attackValueCoeff); | damages = parseInt(damages * battleValues.attackValueCoeff); | ||
+ | damages = parseInt(damages * battleValues.typeBonusCoeff); | ||
+ | damages = parseInt(damages * battleValues.monsterBonusCoeff); | ||
damages = parseInt(damages * battleValues.damageMultiplier); | damages = parseInt(damages * battleValues.damageMultiplier); | ||
Ligne 839 : | Ligne 1 016 : | ||
var attackValuePercent = 0; | var attackValuePercent = 0; | ||
var attackMeleeMagic = 0; | var attackMeleeMagic = 0; | ||
+ | var typeBonus = 0; | ||
+ | var monsterBonus = 0; | ||
var damageMultiplier = 1; | var damageMultiplier = 1; | ||
var weaponDefense = 0; | var weaponDefense = 0; | ||
Ligne 857 : | Ligne 1 036 : | ||
weaponType = weaponData[attacker.weapon][1]; | weaponType = weaponData[attacker.weapon][1]; | ||
− | var weaponDefenseName = mapping. | + | var weaponDefenseName = mapping.defenseWeapon[weaponType]; |
− | var weaponDefenseBreakName = mapping. | + | var weaponDefenseBreakName = mapping.breakWeapon[weaponType] |
if (victim.hasOwnProperty(weaponDefenseName)) { | if (victim.hasOwnProperty(weaponDefenseName)) { | ||
Ligne 864 : | Ligne 1 043 : | ||
} | } | ||
− | if (isPC(victim) | + | if (isPC(victim)) { |
− | + | ||
+ | typeBonus = attacker.humanBonus; | ||
+ | |||
+ | if (attacker.hasOwnProperty(weaponDefenseBreakName)) { | ||
+ | weaponDefense -= attacker[weaponDefenseBreakName]; | ||
+ | } | ||
+ | } else { | ||
+ | typeBonus = attacker[mapping.typeFlag[victim.type]]; | ||
+ | monsterBonus = attacker.monsterBonus; | ||
} | } | ||
Ligne 891 : | Ligne 1 078 : | ||
var battleValues = { | var battleValues = { | ||
attackValueCoeff: 1 + (attackValuePercent + Math.min(100, attackMeleeMagic)) / 100, | attackValueCoeff: 1 + (attackValuePercent + Math.min(100, attackMeleeMagic)) / 100, | ||
+ | typeBonusCoeff: 1 + typeBonus / 100, | ||
+ | monsterBonusCoeff: 1 + monsterBonus / 100, | ||
damageMultiplier: damageMultiplier, | damageMultiplier: damageMultiplier, | ||
defense: victim.defense, | defense: victim.defense, | ||
Ligne 911 : | Ligne 1 100 : | ||
return battleValues; | return battleValues; | ||
+ | } | ||
+ | |||
+ | |||
+ | function handleFinalDamages(battleValues, damages, extraDamages, minDamages, maxDamages, damagesType, tableResult, probability) { | ||
+ | |||
+ | var finalDamages = calcDamageWithSecondaryBonuses(damages, battleValues, damagesType, extraDamages); | ||
+ | addToTableResult(tableResult, finalDamages, probability); | ||
+ | |||
+ | if (finalDamages < minDamages) { | ||
+ | minDamages = finalDamages; | ||
+ | } | ||
+ | |||
+ | if (finalDamages > maxDamages) { | ||
+ | maxDamages = finalDamages; | ||
+ | } | ||
+ | |||
+ | return [finalDamages, minDamages, maxDamages]; | ||
} | } | ||
Ligne 923 : | Ligne 1 129 : | ||
var sumDamages = 0; | var sumDamages = 0; | ||
var minDamages = 2**53 - 1; | var minDamages = 2**53 - 1; | ||
− | var | + | var saveDamages = 0; |
clearTableResult(tableResult); | clearTableResult(tableResult); | ||
Ligne 949 : | Ligne 1 155 : | ||
var weight; | var weight; | ||
− | |||
if (attackValue > lastWeightsLimit) { | if (attackValue > lastWeightsLimit) { | ||
Ligne 966 : | Ligne 1 171 : | ||
if (damagesWithPrimaryBonuses <= 2) { | if (damagesWithPrimaryBonuses <= 2) { | ||
− | + | ||
for (var damages = 1; damages <= 5; damages++) { | for (var damages = 1; damages <= 5; damages++) { | ||
− | + | ||
− | finalDamages = calcDamageWithSecondaryBonuses(damages, battleValues, damagesType, damagesWithPrimaryBonuses); | + | var finalDamages = calcDamageWithSecondaryBonuses(damages, battleValues, damagesType, damagesWithPrimaryBonuses); |
− | weight = weight / 5; | + | addToTableResult(tableResult, finalDamages, weight * damagesType.weight / (5 * totalCardinal)); |
+ | |||
+ | if (damages === 5) { | ||
+ | saveDamages = finalDamages; | ||
+ | } | ||
+ | |||
+ | sumDamages += finalDamages * weight * damagesType.weight / 5; | ||
} | } | ||
− | + | ||
} else { | } else { | ||
+ | |||
+ | var finalDamages = calcDamageWithSecondaryBonuses(damagesWithPrimaryBonuses, battleValues, damagesType, 0); | ||
+ | addToTableResult(tableResult, finalDamages, weight * damagesType.weight / totalCardinal); | ||
− | + | sumDamages += finalDamages * weight * damagesType.weight; | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
} | } | ||
− | |||
− | |||
} | } | ||
} | } | ||
− | return [sumDamages / totalCardinal, | + | return [sumDamages / totalCardinal, getMinDamages(tableResult), Math.max(saveDamages, finalDamages)]; |
} | } | ||
Ligne 1 002 : | Ligne 1 204 : | ||
var monster = { | var monster = { | ||
"name": name, | "name": name, | ||
− | " | + | "race": data[0], |
− | " | + | "level": data[1], |
− | " | + | "type": data[2], |
− | " | + | "str": data[3], |
− | " | + | "dex": data[4], |
− | " | + | "vit": data[5], |
− | "randomAttackValueMin": data[ | + | "int": data[6], |
− | "randomAttackValueMax": data[ | + | "randomAttackValueMin": data[7], |
− | "defense": data[ | + | "randomAttackValueMax": data[8], |
− | "criticalHit": data[ | + | "defense": data[9] + data[1] + data[5], |
− | "piercingHit": data[ | + | "criticalHit": data[10], |
− | "fistDefense": data[ | + | "piercingHit": data[11], |
− | "swordDefense": data[ | + | "fistDefense": data[12], |
− | "twoHandedDefense": data[ | + | "swordDefense": data[13], |
− | "daggerDefense": data[ | + | "twoHandedDefense": data[14], |
− | "bellDefense": data[ | + | "daggerDefense": data[15], |
− | "fanDefense": data[ | + | "bellDefense": data[16], |
− | "bowDefense": data[ | + | "fanDefense": data[17], |
− | "clawDefense": data[ | + | "bowDefense": data[18], |
− | "damageMultiplier": data[ | + | "clawDefense": data[19], |
+ | "damageMultiplier": data[33] | ||
} | } | ||
Ligne 1 038 : | Ligne 1 241 : | ||
var victimName = battleInfo.get("victim"); | var victimName = battleInfo.get("victim"); | ||
− | if (attackerName | + | if (characters.savedCharacters.hasOwnProperty(attackerName)) { |
− | var attacker = | + | var attacker = characters.savedCharacters[attackerName]; |
− | |||
} else { | } else { | ||
− | var attacker = | + | var attacker = createMonster(attackerName); |
} | } | ||
− | if (victimName | + | if (characters.savedCharacters.hasOwnProperty(victimName)) { |
− | var victim = | + | var victim = characters.savedCharacters[victimName]; |
− | |||
} else { | } else { | ||
− | var victim = | + | var victim = createMonster(victimName); |
} | } | ||
var [meanDamages, minDamages, maxDamages] = calcBattleDamages(attacker, victim, battle.tableResult, battle.mapping); | var [meanDamages, minDamages, maxDamages] = calcBattleDamages(attacker, victim, battle.tableResult, battle.mapping); | ||
− | + | ||
battle.damageResult.textContent = attacker.name + " inflige " + numberFormat(meanDamages, 1) + " dégâts en moyenne à " + victim.name + " (minimum : " + minDamages + ", maximum : " + maxDamages + ")."; | battle.damageResult.textContent = attacker.name + " inflige " + numberFormat(meanDamages, 1) + " dégâts en moyenne à " + victim.name + " (minimum : " + minDamages + ", maximum : " + maxDamages + ")."; | ||
Ligne 1 073 : | Ligne 1 274 : | ||
newCharacterTemplate: document.getElementById("new-character-template").children[0], | newCharacterTemplate: document.getElementById("new-character-template").children[0], | ||
charactersContainer: document.getElementById("characters-container"), | charactersContainer: document.getElementById("characters-container"), | ||
+ | newMonsterTemplate: document.getElementById("new-monster-template").children[0], | ||
+ | monstersContainer: document.getElementById("monsters-container"), | ||
+ | monsterListForm: document.getElementById("monster-list-form"), | ||
+ | searchMonster: document.getElementById("search-monster"), | ||
+ | monsterList: document.getElementById("monster-list"), | ||
saveButton: document.getElementById("save-button"), | saveButton: document.getElementById("save-button"), | ||
saveBar: document.getElementById("save-bar"), | saveBar: document.getElementById("save-bar"), | ||
Ligne 1 088 : | Ligne 1 294 : | ||
var savedCharacters = getSavedCharacters(); | var savedCharacters = getSavedCharacters(); | ||
+ | var savedMonsters = getSavedMonsters(); | ||
for (var [pseudo, character] of Object.entries(savedCharacters)) { | for (var [pseudo, character] of Object.entries(savedCharacters)) { | ||
Ligne 1 093 : | Ligne 1 300 : | ||
} | } | ||
+ | characters.savedMonsters = savedMonsters; | ||
+ | |||
var mapping = { | var mapping = { | ||
− | + | typeFlag: { | |
+ | 0: "animalBonus", | ||
+ | 1: "humanBonus", | ||
+ | 2: "orcBonus", | ||
+ | 3: "mysticBonus", | ||
+ | 4: "undeadBonus", | ||
+ | 5: "insectBonus", | ||
+ | 6: "desertBonus", | ||
+ | 7: "devilBonus" | ||
+ | }, | ||
+ | defenseWeapon: { | ||
0: "swordDefense", | 0: "swordDefense", | ||
1: "daggerDefense", | 1: "daggerDefense", | ||
Ligne 1 105 : | Ligne 1 324 : | ||
8: "fistDefense" | 8: "fistDefense" | ||
}, | }, | ||
− | + | breakWeapon: { | |
0: "breakSwordDefense", | 0: "breakSwordDefense", | ||
1: "breakDaggerDefense", | 1: "breakDaggerDefense", | ||
Ligne 1 147 : | Ligne 1 366 : | ||
script.onload = onComplete; | script.onload = onComplete; | ||
script.onerror = onComplete; | script.onerror = onComplete; | ||
+ | } | ||
+ | |||
+ | |||
+ | function loadStyle(src) { | ||
+ | |||
+ | var link = document.createElement("link"); | ||
+ | link.href = src; | ||
+ | link.rel = "stylesheet"; | ||
+ | |||
+ | document.head.appendChild(link); | ||
} | } | ||
Ligne 1 152 : | Ligne 1 381 : | ||
(function(){ | (function(){ | ||
− | var | + | var javascriptSource = "/index.php?title=Utilisateur:Ankhseram/Calculator.js&action=raw&ctype=text/javascript"; |
+ | var cssSource = "/index.php?title=Utilisateur:Ankhseram/Style.css&action=raw&ctype=text/css"; | ||
+ | |||
+ | loadStyle(cssSource); | ||
function main() { | function main() { | ||
Ligne 1 159 : | Ligne 1 391 : | ||
characterManagement(characters, battle); | characterManagement(characters, battle); | ||
+ | monsterManagement(characters, battle); | ||
− | updateBattleChoice(characters | + | updateBattleChoice(characters, battle); |
createBattle(characters, battle); | createBattle(characters, battle); | ||
} | } | ||
− | loadScript( | + | loadScript(javascriptSource, main); |
})(); | })(); |
Version du 21 octobre 2023 à 16:53
function showElement(element) {
element.classList.remove("tabber-noactive");
}
function hideElement(element) {
element.classList.add("tabber-noactive");
}
function removeAccent(str) {
return str.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase()
}
function toNormalForm(str) {
return removeAccent(str).replace(/[^a-zA-Z0-9 ]/g, "");;
}
function isValueInArray(value, array) {
return array.indexOf(value) !== -1;
}
function copyObject(object) {
var copy = {};
for (var key in object) {
copy[key] = object[key];
}
return copy;
}
function numberFormat(number, precision) {
return Math.round((number) * 10**precision) / 10**precision;
}
function addRowToTableResult(tableResult, value) {
var newRow = tableResult.insertRow(-1);
var firstCell = newRow.insertCell(0);
firstCell.textContent = value;
firstCell.colSpan = 2;
newRow.style.backgroundColor = "#1f0e02";
newRow.style.color = "#f0d9a2";
newRow.style.fontWeight = "bold";
}
function addToTableResult(tableResult, value1, value2) {
var newRow = tableResult.insertRow(-1);
var firstCell = newRow.insertCell(0);
firstCell.textContent = value1;
var secondCell = newRow.insertCell(1);
secondCell.textContent = numberFormat(value2 * 100, 3) + " %";
}
function getMinDamages(tableResult) {
return tableResult.rows[2].cells[0].textContent;
}
function clearTableResult(tableResult) {
var tableHeaderRowCount = 1;
var rowCount = tableResult.rows.length;
for (var rowIndex = tableHeaderRowCount; rowIndex < rowCount; rowIndex++) {
tableResult.deleteRow(tableHeaderRowCount);
}
}
function addWeapon(weaponChoice) {
for (var weapon in weaponData) {
var option = document.createElement("option");
option.textContent = weaponData[weapon][0];
option.value = weapon;
var weaponType = weaponData[weapon][1];
if (weaponType !== 0 && weaponType !== 3) {
hideElement(option);
}
weaponChoice.appendChild(option);
}
}
function filterClass(raceChoice, classChoice, selectValueIsChanged = false) {
var selectedRace = raceChoice.value;
if (selectedRace == "lycan") {
hideElement(classChoice.parentElement);
} else {
showElement(classChoice.parentElement);
for (var option of classChoice.options) {
if (option.getAttribute("data-race") === selectedRace) {
if (!selectValueIsChanged) {
classChoice.value = option.value;
selectValueIsChanged = true;
}
showElement(option);
} else {
hideElement(option);
}
}
}
}
function filterWeapon(raceChoice, weaponChoice, selectValueIsChanged = false) {
var allowedWeaponsPerRace = {
'warrior': [0, 3, 8],
'ninja': [0, 1, 2, 8],
'sura': [0, 7, 8],
'shaman': [4, 6, 8],
'lycan': [5, 8]
}
var allowedWeapons = allowedWeaponsPerRace[raceChoice.value]
if (!selectValueIsChanged) {
var weaponType = weaponData[weaponChoice.value][1];
if (!isValueInArray(weaponType, allowedWeapons)) {
weaponChoice.value = "Fist";
}
}
for (var option of weaponChoice.options) {
var weaponType = weaponData[option.value][1];
if (isValueInArray(weaponType, allowedWeapons)) {
showElement(option);
} else {
hideElement(option);
}
}
}
function filterUpgrade(weaponUpgrade, weaponChoice, randomAttackValue, currentUpgrade) {
var weaponName = weaponChoice.value;
if (isValueInArray("serpent", weaponName.toLowerCase())) {
showElement(randomAttackValue);
} else {
hideElement(randomAttackValue);
}
var upgradeNumber = weaponData[weaponName][3].length;
if (!upgradeNumber) {
hideElement(weaponUpgrade.parentElement);
} else {
showElement(weaponUpgrade.parentElement);
weaponUpgrade.innerHTML = "";
for (var upgrade = 0; upgrade < upgradeNumber; upgrade++) {
var option = document.createElement("option");
option.value = upgrade;
option.textContent = "+" + upgrade;
weaponUpgrade.appendChild(option);
}
if (currentUpgrade === undefined) {
option.selected = true;
} else {
weaponUpgrade.value = currentUpgrade;
currentUpgrade = undefined;
}
}
}
function filterForm(characters) {
addWeapon(characters.weaponChoice);
characters.raceChoice.addEventListener("change", function(event) {
filterClass(characters.raceChoice, characters.classChoice);
filterWeapon(characters.raceChoice, characters.weaponChoice);
});
characters.weaponChoice.addEventListener("change", function(event) {
filterUpgrade(characters.weaponUpgrade, characters.weaponChoice, characters.randomAttackValue);
});
}
function getSavedCharacters() {
var savedCharacters = localStorage.getItem("savedCharactersCalculator");
if (savedCharacters) {
return JSON.parse(savedCharacters);
}
return {};
}
function getSavedMonsters() {
var savedMonsters = localStorage.getItem("savedMonstersCalculator");
if (savedMonsters) {
return JSON.parse(savedMonsters);
}
return [];
}
function addUniquePseudo(characterDataObject, savedCharactersPseudo) {
var characterPseudo = characterDataObject.name;
var originalPseudo = characterPseudo;
var count = 0;
var regex = /(.*)(\d)$/;
var match = characterPseudo.match(regex);
if (match) {
originalPseudo = match[1];
count = match[2];
}
while (isValueInArray(characterPseudo, savedCharactersPseudo)) {
characterPseudo = originalPseudo + count;
count++;
}
characterDataObject.name = characterPseudo;
return [characterDataObject, characterPseudo]
}
function convertToNumber(value) {
var valueNumber = Number(value);
return isNaN(valueNumber) ? value : valueNumber;
}
function updateSavedCharacters(savedCharacters) {
localStorage.setItem("savedCharactersCalculator", JSON.stringify(savedCharacters));
}
function updateSavedMonsters(savedMonsters) {
localStorage.setItem("savedMonstersCalculator", JSON.stringify(savedMonsters));
}
function saveCharacter(savedCharacters, characterCreation, battle, newCharacter, characterDataObject) {
if (!characterDataObject) {
var characterData = new FormData(characterCreation);
var characterDataObject = {};
characterData.forEach(function(value, key) {
characterDataObject[key] = convertToNumber(value);
});
}
savedCharacters[characterDataObject.name] = characterDataObject;
updateSavedCharacters(savedCharacters);
if (newCharacter) {
addBattleChoice(battle, characterDataObject.name);
}
}
function saveButtonGreen(characters, animation) {
if (animation) {
characters.saveBar.style.animation = characters.saveButton.dataset.animation;
} else {
characters.saveBar.style.animation = "";
characters.saveBar.style.backgroundColor = "lightgreen";
}
}
function saveButtonOrange(characters) {
characters.saveBar.style.animation = "";
characters.saveBar.style.backgroundColor = "#ffdd40";
}
function characterCreationListener(characters, battle) {
characters.characterCreation.addEventListener("submit", function(event) {
event.preventDefault();
if (characters.unsavedChanges) {
saveCharacter(characters.savedCharacters, characters.characterCreation, battle);
saveButtonGreen(characters, true);
characters.unsavedChanges = false;
}
});
}
function downloadCharacter(character) {
var content = JSON.stringify(character);
var link = document.createElement("a");
var blob = new Blob([content], {type: "text/plain"});
var blobURL = URL.createObjectURL(blob);
link.href = blobURL;
link.download = character.name + ".txt";
link.click();
URL.revokeObjectURL(blobURL);
}
function uploadCharacter(characters, characterTemplate, charactersContainer, battle) {
var fileInput = document.createElement("input");
fileInput.type = "file";
fileInput.accept = ".txt";
fileInput.multiple = true;
fileInput.click();
fileInput.addEventListener("change", function(event) {
var selectedFiles = event.target.files;
var selectFilesLength = selectedFiles.length;
hideElement(characters.characterCreation);
for (var fileIndex = 0; fileIndex < selectFilesLength; fileIndex++) {
var selectedFile = selectedFiles[fileIndex];
if (selectedFile.type === "text/plain") {
var reader = new FileReader();
reader.onload = function(e) {
var fileContent = e.target.result;
try {
var characterDataObject = JSON.parse(fileContent);
var characterPseudo = characterDataObject.name;
if (characterPseudo) {
characterPseudo = validPseudo(characterPseudo);
[characterDataObject, characterPseudo] = addUniquePseudo(characterDataObject, Object.keys(characters.savedCharacters));
var selectedCharacter = handleNewCharacter(characters, characterTemplate, charactersContainer, battle, characterPseudo)[0];
if (selectFilesLength === 1) {
updateForm(characterDataObject, characters.characterCreation, characters, selectedCharacter);
}
saveCharacter(characters.savedCharacters, characters.characterCreation, battle, true, characterDataObject);
}
} catch (error) {
if (error.name === "TypeError") {
// delete the character
}
}
};
reader.readAsText(selectedFile);
}
}
});
}
function deleteCharacter(characters, pseudo, displayedPseudo, element, battle) {
delete characters.savedCharacters[pseudo];
element.remove();
updateSavedCharacters(characters.savedCharacters);
removeBattleChoice(battle, pseudo);
if (!Object.keys(characters.savedCharacters).length || characters.characterCreation.name.value === pseudo) {
saveButtonGreen(characters);
characters.unsavedChanges = false;
hideElement(characters.characterCreation);
}
}
function deleteMonster(characters, monsterName, element, battle) {
characters.savedMonsters.splice(characters.savedMonsters.indexOf(monsterName), 1);
element.remove();
updateSavedMonsters(characters.savedMonsters);
removeBattleChoice(battle, monsterName);
}
function handleStyle(currentCharacter, selectedElement) {
var currentCharacterElement = currentCharacter.element;
if (currentCharacterElement) {
currentCharacterElement.setAttribute("style", currentCharacter.normal);
}
selectedElement.setAttribute("style", currentCharacter.onClick);
currentCharacter.element = selectedElement;
}
function updateForm(formData, characterCreation, characters, selectedElement) {
saveButtonGreen(characters);
showElement(characterCreation);
handleStyle(characters.currentCharacter, selectedElement);
characterCreation.reset();
for (var [name, value] of Object.entries(formData)) {
var formElement = characterCreation[name];
if (formElement) {
formElement.value = value;
}
}
filterClass(characters.raceChoice, characters.classChoice, true);
filterWeapon(characters.raceChoice, characters.weaponChoice, true);
filterUpgrade(characters.weaponUpgrade, characters.weaponChoice, characters.randomAttackValue, formData.upgrade);
}
function handleClickOnCharacter(spanInput, target, characters, characterElement, battle, edition) {
var displayedPseudo = characters.characterCreation.name.value;
var pseudo = spanInput.dataset.name;
if (edition) {
if (!characters.unsavedChanges) {
updateForm(characters.savedCharacters[pseudo], characters.characterCreation, characters, characterElement);
} else if (displayedPseudo === pseudo) {
// pass
} else {
var result = confirm("Voulez-vous continuer ? Les dernières modifications ne seront pas sauvegardées.");
if (result) {
updateForm(characters.savedCharacters[pseudo], characters.characterCreation, characters, characterElement);
characters.unsavedChanges = false;
}
}
} else {
if (target.tagName === "path") {
target = target.parentElement;
}
switch (target.dataset.icon) {
case 'duplicate':
if (!characters.unsavedChanges) {
addNewCharacter(characters, characters.newCharacterTemplate, characters.charactersContainer, battle, pseudo);
} else {
var result = confirm("Voulez-vous continuer ? Les dernières modifications ne seront pas sauvegardées.");
if (result) {
addNewCharacter(characters, characters.newCharacterTemplate, characters.charactersContainer, battle, pseudo);
saveButtonGreen(characters);
characters.unsavedChanges = false;
}
}
break;
case 'download':
downloadCharacter(characters.savedCharacters[pseudo]);
break;
case 'delete':
var result = confirm("Voulez-vous vraiment supprimer définitivement le personnage " + pseudo + " ?");
if (result) {
deleteCharacter(characters, pseudo, displayedPseudo, characterElement, battle);
}
break;
}
}
}
function handleNewCharacter(characters, characterTemplate, charactersContainer, battle, pseudo) {
var newCharacterTemplate = characterTemplate.cloneNode(true);
var spanInput = newCharacterTemplate.querySelector("span.input");
var svgContainer = newCharacterTemplate.querySelector("div.svg-container");
newCharacterTemplate.setAttribute("tabindex", "0");
charactersContainer.appendChild(newCharacterTemplate);
if (pseudo) {
spanInput.textContent = pseudo;
spanInput.setAttribute("data-name", pseudo);
}
newCharacterTemplate.addEventListener("click", function(event) {
var target = event.target;
if (target.tagName === "path" || target.tagName === "svg") {
handleClickOnCharacter(spanInput, target, characters, newCharacterTemplate, battle);
} else {
handleClickOnCharacter(spanInput, null, characters, newCharacterTemplate, battle, true);
}
});
newCharacterTemplate.addEventListener("keydown", function(event) {
if (event.keyCode === 13) {
event.target.click();
}
});
return [newCharacterTemplate, spanInput, svgContainer];
}
function validPseudo(pseudo) {
var newPseudo = pseudo.replace(/[^A-Za-z0-9]+/g, "");
if (!newPseudo) {
return "Pseudo"
}
return newPseudo;
}
function addNewCharacter(characters, characterTemplate, charactersContainer, battle, pseudoToDuplicate) {
function editAndSetCharacterPseudoInput(selectedCharacter, spanInput, svgContainer) {
var maxPseudoLength = 15;
var selection = window.getSelection();
var range = document.createRange();
if (pseudoToDuplicate) {
spanInput.textContent = pseudoToDuplicate;
}
spanInput.contentEditable = true;
spanInput.focus();
range.selectNodeContents(spanInput);
selection.removeAllRanges();
selection.addRange(range);
function pseudoValidation() {
var characterPseudo = validPseudo(spanInput.textContent);
var characterDataObject = {name: characterPseudo};
if (pseudoToDuplicate) {
characterDataObject = copyObject(characters.savedCharacters[pseudoToDuplicate]);
characterDataObject.name = characterPseudo;
}
[characterDataObject, characterPseudo] = addUniquePseudo(characterDataObject, Object.keys(characters.savedCharacters));
selection.removeAllRanges();
spanInput.contentEditable = false;
spanInput.textContent = characterPseudo;
spanInput.setAttribute("data-name", characterPseudo);
updateForm(characterDataObject, characters.characterCreation, characters, selectedCharacter);
saveCharacter(characters.savedCharacters, characters.characterCreation, battle, true);
}
function handleMaxLength(event) {
if (spanInput.textContent.length > maxPseudoLength) {
spanInput.textContent = spanInput.textContent.slice(0, maxPseudoLength);
range.setStart(spanInput.childNodes[0], maxPseudoLength);
selection.removeAllRanges();
selection.addRange(range);
}
}
function handleBlur() {
spanInput.removeEventListener("blur", handleBlur);
spanInput.removeEventListener("input", handleMaxLength);
pseudoValidation();
}
function handleKeyDown(event) {
if (event.key === "Enter") {
event.preventDefault();
spanInput.removeEventListener("keydown", handleKeyDown);
spanInput.removeEventListener("blur", handleBlur);
spanInput.removeEventListener("input", handleMaxLength);
pseudoValidation();
}
}
spanInput.addEventListener("input", handleMaxLength);
spanInput.addEventListener("keydown", handleKeyDown);
spanInput.addEventListener("blur", handleBlur);
}
hideElement(characters.characterCreation);
var [selectedCharacter, spanInput, svgContainer] = handleNewCharacter(characters, characterTemplate, charactersContainer, battle);
editAndSetCharacterPseudoInput(selectedCharacter, spanInput, svgContainer);
}
function characterManagement(characters, battle) {
var characterTemplate = characters.newCharacterTemplate;
var charactersContainer = characters.charactersContainer;
Object.keys(characters.savedCharacters).forEach(function(pseudo) {
handleNewCharacter(characters, characterTemplate, charactersContainer, battle, pseudo);
});
characters.addNewCharacterButton.addEventListener("click", function(event) {
if (!characters.unsavedChanges) {
addNewCharacter(characters, characterTemplate, charactersContainer, battle);
} else {
var result = confirm("Voulez-vous continuer ? Les dernières modifications ne seront pas sauvegardées.");
if (result) {
addNewCharacter(characters, characterTemplate, charactersContainer, battle);
saveButtonGreen(characters);
characters.unsavedChanges = false;
}
}
});
characters.uploadCharacter.addEventListener("click", function(event) {
uploadCharacter(characters, characterTemplate, charactersContainer, battle);
});
characters.characterCreation.addEventListener("change", function() {
saveButtonOrange(characters)
characters.unsavedChanges = true;
})
filterForm(characters);
characterCreationListener(characters, battle);
window.addEventListener('beforeunload', function(event) {
if (characters.unsavedChanges) {
event.preventDefault();
event.returnValue = '';
return '';
}
});
}
function handleNewMonster(characters, monsterTemplate, monstersContainer, battle, monsterName, monsterList) {
var newMonsterTemplate = monsterTemplate.cloneNode(true);
var spanInput = newMonsterTemplate.querySelector("span.input");
var deleteSvg = newMonsterTemplate.querySelector("svg");
spanInput.textContent = monsterName;
monstersContainer.appendChild(newMonsterTemplate);
newMonsterTemplate.setAttribute("tabindex", "0");
newMonsterTemplate.setAttribute("data-name", monsterName);
monstersContainer.appendChild(newMonsterTemplate);
deleteSvg.addEventListener("click", function(event) {
deleteMonster(characters, monsterName, newMonsterTemplate, battle);
var inputMonster = monsterList.querySelector("input[name='" + monsterName + "']");
inputMonster.checked = false;
});
}
function monsterManagement(characters, battle) {
function handleDropdown(searchMonster, monsterList) {
searchMonster.addEventListener("focus", function(event) {
showElement(monsterList);
});
document.addEventListener("mousedown", function(event) {
var target = event.target;
if (!monsterList.contains(target) && !searchMonster.contains(target)) {
hideElement(monsterList);
}
});
}
function addMonsterNames(monsterList) {
var monsterIndex = 0;
for (var monsterName in monsterData) {
var li = document.createElement("li");
var label = document.createElement("label");
var input = document.createElement("input");
var textNode = document.createTextNode(monsterName);
label.htmlFor = "monster" + monsterIndex;
input.id = "monster" + monsterIndex;
input.type = "checkbox";
input.name = monsterName;
label.appendChild(input);
label.appendChild(textNode);
li.appendChild(label);
monsterList.appendChild(li);
monsterIndex++;
}
}
function filterNames(searchMonster, monsterList) {
var debounceTimer;
searchMonster.addEventListener("input", function(event) {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(function() {
var value = toNormalForm(event.target.value);
for (var element of monsterList.children) {
if (!isValueInArray(value, toNormalForm(element.textContent))) {
hideElement(element);
} else {
showElement(element);
}
}
}, 500);
});
}
var monsterTemplate = characters.newMonsterTemplate;
var monstersContainer = characters.monstersContainer;
var monsterList = characters.monsterList;
var searchMonster = characters.searchMonster;
var monsterListForm = characters.monsterListForm;
handleDropdown(searchMonster, monsterList);
addMonsterNames(monsterList, characters.monsterListTemplate);
filterNames(searchMonster, monsterList);
characters.savedMonsters.forEach(function(monsterName) {
handleNewMonster(characters, monsterTemplate, monstersContainer, battle, monsterName, monsterList);
var inputMonster = monsterList.querySelector("input[name='" + monsterName + "']");
if (inputMonster) {
inputMonster.checked = true;
} else {
deleteMonster(characters, monsterName, newMonsterTemplate, battle);
}
});
monsterListForm.addEventListener("submit", function(event) {
event.preventDefault();
})
monsterListForm.addEventListener("change", function(event) {
var target = event.target;
var monsterName = target.name;
if (monsterName === "search-monster") {
return;
}
if (target.checked) {
handleNewMonster(characters, monsterTemplate, monstersContainer, battle, monsterName, monsterList);
characters.savedMonsters.push(monsterName);
updateSavedMonsters(characters.savedMonsters);
addBattleChoice(battle, monsterName, true);
} else {
var currentMonsterTemplate = monstersContainer.querySelector("[data-name='" + monsterName + "']");
deleteMonster(characters, monsterName, currentMonsterTemplate, battle);
}
});
}
function removeBattleChoice(battle, name) {
var battleSelects = [battle.attackerSelection, battle.victimSelection];
battleSelects.forEach(function(battleSelect) {
for (var optionIndex = 0; optionIndex < battleSelect.options.length; optionIndex++) {
if (battleSelect.options[optionIndex].value === name) {
battleSelect.remove(optionIndex);
break;
}
}
});
}
function addBattleChoice(battle, name, isMonster = false) {
function createOption(text) {
var option = document.createElement("option");
option.textContent = text;
option.value = text;
if (!isMonster) {
option.classList.add("notranslate");
}
return option;
}
battle.attackerSelection.appendChild(createOption(name));
battle.victimSelection.appendChild(createOption(name));
}
function updateBattleChoice(characters, battle) {
Object.keys(characters.savedCharacters).forEach(function(pseudo) {
addBattleChoice(battle, pseudo);
});
characters.savedMonsters.forEach(function(monsterName) {
addBattleChoice(battle, monsterName, true);
});
}
function isPC(character) {
if (character.race === 0 || character.race === 1) {
return false;
}
return true;
}
function calcAttackFactor(attacker, victim) {
function calcCoeffK(dex, level) {
return Math.min(90, Math.floor((2 * dex + level) / 3))
}
var K1 = calcCoeffK(attacker.dex, attacker.level);
var K2 = calcCoeffK(victim.dex, attacker.level);
var AR = (K1 + 210) / 300;
var ER = (2 * K2 + 5) / (K2 + 95) * 3 / 10;
return AR - ER
}
function calcMainAttackValue(attacker, attackerWeapon) {
var leadership = 0;
var rawWeaponAttackValue = 0;
if (isPC(attacker)) {
var rawWeaponAttackValue = attackerWeapon[3][attacker.upgrade];
if (!rawWeaponAttackValue) {
rawWeaponAttackValue = 0;
}
leadership = attacker.leadership;
}
return 2 * (attacker.level + rawWeaponAttackValue) + leadership;
}
function calcStatAttackValue(character) {
switch (character.race) {
case "warrior":
case "sura":
return 2 * character.str
case "ninja":
return parseInt(1 / 4 * (character.str + 7 * character.dex))
case "shaman":
return parseInt(1 / 3 * (5 * character.int + character.dex))
case "lycan":
return character.vit + 2 * character.dex;
default:
return 2 * character.str
}
}
function calcSecondaryAttackValue(attacker, attackerWeapon) {
var attackValues = [];
var weights = [];
var attackValueOther = 0;
var minAttackValue = 0;
var maxAttackValue = 0;
var minAttackValueSlash = 0;
var maxAttackValueSlash = 0;
if (isPC(attacker)) {
if (!isValueInArray("serpent", attacker.weapon.toLowerCase())) {
minAttackValue += attackerWeapon[2][2];
maxAttackValue += attackerWeapon[2][3];
} else {
var rawAttackValue = attackerWeapon[3][attacker.upgrade];
minAttackValue += attacker.randomAttackValueMin - rawAttackValue;
maxAttackValue += attacker.randomAttackValueMax - rawAttackValue;
minAttackValue = Math.max(0, minAttackValue);
maxAttackValue = Math.max(minAttackValue, maxAttackValue);
}
minAttackValueSlash += Math.min(attacker.slashMin, attacker.slashMax);
maxAttackValueSlash += Math.max(attacker.slashMin, attacker.slashMax);
attackValueOther += attacker.attackValue;
} else {
minAttackValue += attacker.randomAttackValueMin;
maxAttackValue += attacker.randomAttackValueMax;
}
attackValueOther += calcStatAttackValue(attacker);
var weaponRange = maxAttackValue - minAttackValue;
var slashRange = maxAttackValueSlash - minAttackValueSlash;
var totalCardinal = (weaponRange + 1) * (slashRange + 1) * 10000;
var minRange = Math.min(weaponRange, slashRange) + 1;
minAttackValue += minAttackValueSlash;
maxAttackValue += maxAttackValueSlash;
return [minAttackValue, maxAttackValue, attackValueOther, minRange, totalCardinal];
}
function calcDamageWithPrimaryBonuses(damages, battleValues) {
damages = parseInt(damages * battleValues.attackValueCoeff);
damages = parseInt(damages * battleValues.typeBonusCoeff);
damages = parseInt(damages * battleValues.monsterBonusCoeff);
damages = parseInt(damages * battleValues.damageMultiplier);
damages -= battleValues.defense;
return damages;
}
function calcDamageWithSecondaryBonuses(damages, battleValues, damagesType, extraDamages) {
damages = parseInt(damages * battleValues.weaponDefenseCoeff);
if (damagesType.criticalHit) {
damages *= 2;
}
if (damagesType.piercingHit) {
damages += battleValues.defense - Math.min(0, extraDamages);
damages = parseInt(damages * battleValues.extraPiercingHitCoeff);
}
damages = parseInt(damages * battleValues.averageDamageCoeff);
damages = parseInt(damages * battleValues.averageDamageResistanceCoeff);
damages += Math.floor(battleValues.defensePercent);
return damages;
}
function createBattleValues(attacker, victim, mapping) {
var attackValuePercent = 0;
var attackMeleeMagic = 0;
var typeBonus = 0;
var monsterBonus = 0;
var damageMultiplier = 1;
var weaponDefense = 0;
var criticalHitPercentage = attacker.criticalHit;
var piercingHitPercentage = attacker.piercingHit;
var averageDamage = 0;
var averageDamageResistance = 0;
var defensePercent = 0;
if (isPC(attacker)) {
attackValuePercent = attacker.attackValuePercent;
attackMeleeMagic = attacker.attackMeleeMagic;
var weaponType = 8;
if (weaponData.hasOwnProperty(attacker.weapon))
weaponType = weaponData[attacker.weapon][1];
var weaponDefenseName = mapping.defenseWeapon[weaponType];
var weaponDefenseBreakName = mapping.breakWeapon[weaponType]
if (victim.hasOwnProperty(weaponDefenseName)) {
weaponDefense = victim[weaponDefenseName];
}
if (isPC(victim)) {
typeBonus = attacker.humanBonus;
if (attacker.hasOwnProperty(weaponDefenseBreakName)) {
weaponDefense -= attacker[weaponDefenseBreakName];
}
} else {
typeBonus = attacker[mapping.typeFlag[victim.type]];
monsterBonus = attacker.monsterBonus;
}
averageDamage = attacker.averageDamage;
} else {
damageMultiplier = attacker.damageMultiplier;
}
if (isPC(victim)) {
criticalHitPercentage -= victim.criticalHitResistance;
piercingHitPercentage -= victim.piercingHitResistance;
averageDamageResistance = victim.averageDamageResistance;
if (victim.race === "shaman" || victim.class === "black_magic") {
defensePercent = -2 * victim.magicDefense * victim.defensePercent / 100;
} else {
defensePercent = -2 * victim.defense * victim.defensePercent / 100;
}
}
var battleValues = {
attackValueCoeff: 1 + (attackValuePercent + Math.min(100, attackMeleeMagic)) / 100,
typeBonusCoeff: 1 + typeBonus / 100,
monsterBonusCoeff: 1 + monsterBonus / 100,
damageMultiplier: damageMultiplier,
defense: victim.defense,
weaponDefenseCoeff: 1 - weaponDefense / 100,
extraPiercingHitCoeff: 1 + Math.max(0, piercingHitPercentage - 100) / 200,
averageDamageCoeff: 1 + averageDamage / 100,
averageDamageResistanceCoeff: 1 - Math.min(99, averageDamageResistance) / 100,
defensePercent: defensePercent
}
criticalHitPercentage = Math.min(criticalHitPercentage, 100);
piercingHitPercentage = Math.min(piercingHitPercentage, 100);
battleValues.damagesTypeCombinaison = [
{criticalHit: false, piercingHit: false, weight: (100 - criticalHitPercentage) * (100 - piercingHitPercentage), name: "Coup classique"},
{criticalHit: true, piercingHit: false, weight: criticalHitPercentage * (100 - piercingHitPercentage), name: "Coup critique"},
{criticalHit: false, piercingHit: true, weight: (100 - criticalHitPercentage) * piercingHitPercentage, name: "Coup perçant"},
{criticalHit: true, piercingHit: true, weight: criticalHitPercentage * piercingHitPercentage, name: "Coup critique + coup perçant"}
]
return battleValues;
}
function handleFinalDamages(battleValues, damages, extraDamages, minDamages, maxDamages, damagesType, tableResult, probability) {
var finalDamages = calcDamageWithSecondaryBonuses(damages, battleValues, damagesType, extraDamages);
addToTableResult(tableResult, finalDamages, probability);
if (finalDamages < minDamages) {
minDamages = finalDamages;
}
if (finalDamages > maxDamages) {
maxDamages = finalDamages;
}
return [finalDamages, minDamages, maxDamages];
}
function calcBattleDamages(attacker, victim, tableResult, mapping) {
var primaryDamages = [];
var weights = [];
var attackerWeapon = null;
var battleValues = createBattleValues(attacker, victim, mapping);
var sumDamages = 0;
var minDamages = 2**53 - 1;
var saveDamages = 0;
clearTableResult(tableResult);
if (isPC(attacker)) {
attackerWeapon = weaponData[attacker.weapon];
}
var attackFactor = calcAttackFactor(attacker, victim);
var mainAttackValue = calcMainAttackValue(attacker, attackerWeapon);
var [minAttackValue, maxAttackValue, attackValueOther, minRange, totalCardinal] = calcSecondaryAttackValue(attacker, attackerWeapon);
var lastWeightsLimit = maxAttackValue - minRange + 1;
var firstWeightLimit = minAttackValue + minRange - 1;
for (var damagesType of battleValues.damagesTypeCombinaison) {
if (!damagesType.weight) {
continue;
}
addRowToTableResult(tableResult, damagesType.name);
for (var attackValue = minAttackValue; attackValue <= maxAttackValue; attackValue++) {
var weight;
if (attackValue > lastWeightsLimit) {
weight = maxAttackValue - attackValue + 1;
} else if (attackValue < firstWeightLimit) {
weight = attackValue - minAttackValue + 1;
} else {
weight = minRange;
}
var secondaryAttackValue = 2 * attackValue + attackValueOther;
var rawDamages = mainAttackValue + parseInt(attackFactor * secondaryAttackValue);
var damagesWithPrimaryBonuses = calcDamageWithPrimaryBonuses(rawDamages, battleValues);
if (damagesWithPrimaryBonuses <= 2) {
for (var damages = 1; damages <= 5; damages++) {
var finalDamages = calcDamageWithSecondaryBonuses(damages, battleValues, damagesType, damagesWithPrimaryBonuses);
addToTableResult(tableResult, finalDamages, weight * damagesType.weight / (5 * totalCardinal));
if (damages === 5) {
saveDamages = finalDamages;
}
sumDamages += finalDamages * weight * damagesType.weight / 5;
}
} else {
var finalDamages = calcDamageWithSecondaryBonuses(damagesWithPrimaryBonuses, battleValues, damagesType, 0);
addToTableResult(tableResult, finalDamages, weight * damagesType.weight / totalCardinal);
sumDamages += finalDamages * weight * damagesType.weight;
}
}
}
return [sumDamages / totalCardinal, getMinDamages(tableResult), Math.max(saveDamages, finalDamages)];
}
function createMonster(name) {
var data = monsterData[name];
var monster = {
"name": name,
"race": data[0],
"level": data[1],
"type": data[2],
"str": data[3],
"dex": data[4],
"vit": data[5],
"int": data[6],
"randomAttackValueMin": data[7],
"randomAttackValueMax": data[8],
"defense": data[9] + data[1] + data[5],
"criticalHit": data[10],
"piercingHit": data[11],
"fistDefense": data[12],
"swordDefense": data[13],
"twoHandedDefense": data[14],
"daggerDefense": data[15],
"bellDefense": data[16],
"fanDefense": data[17],
"bowDefense": data[18],
"clawDefense": data[19],
"damageMultiplier": data[33]
}
return monster;
}
function createBattle(characters, battle) {
battle.battleForm.addEventListener("submit", function(event) {
event.preventDefault();
var battleInfo = new FormData(event.target)
var attackerName = battleInfo.get("attacker");
var victimName = battleInfo.get("victim");
if (characters.savedCharacters.hasOwnProperty(attackerName)) {
var attacker = characters.savedCharacters[attackerName];
} else {
var attacker = createMonster(attackerName);
}
if (characters.savedCharacters.hasOwnProperty(victimName)) {
var victim = characters.savedCharacters[victimName];
} else {
var victim = createMonster(victimName);
}
var [meanDamages, minDamages, maxDamages] = calcBattleDamages(attacker, victim, battle.tableResult, battle.mapping);
battle.damageResult.textContent = attacker.name + " inflige " + numberFormat(meanDamages, 1) + " dégâts en moyenne à " + victim.name + " (minimum : " + minDamages + ", maximum : " + maxDamages + ").";
showElement(battle.tableContainer);
});
}
function createDamageCalculatorInformation() {
var characters = {
unsavedChanges: false,
savedCharacters: {},
currentCharacter: {element: null},
characterCreation: document.getElementById("character-creation"),
addNewCharacterButton: document.getElementById("add-new-character"),
uploadCharacter: document.getElementById("upload-character"),
newCharacterTemplate: document.getElementById("new-character-template").children[0],
charactersContainer: document.getElementById("characters-container"),
newMonsterTemplate: document.getElementById("new-monster-template").children[0],
monstersContainer: document.getElementById("monsters-container"),
monsterListForm: document.getElementById("monster-list-form"),
searchMonster: document.getElementById("search-monster"),
monsterList: document.getElementById("monster-list"),
saveButton: document.getElementById("save-button"),
saveBar: document.getElementById("save-bar"),
raceChoice: document.getElementById("race-choice"),
classChoice: document.getElementById("class-choice"),
weaponChoice: document.getElementById("weapon-choice"),
weaponUpgrade: document.getElementById("upgrade-choice"),
randomAttackValue: document.getElementById("random-attack-value")
}
characters.currentCharacter.normal = characters.newCharacterTemplate.getAttribute("style");
characters.currentCharacter.onClick = characters.currentCharacter.normal + ' ' + characters.newCharacterTemplate.dataset.click;
delete characters.newCharacterTemplate.dataset.click;
var savedCharacters = getSavedCharacters();
var savedMonsters = getSavedMonsters();
for (var [pseudo, character] of Object.entries(savedCharacters)) {
characters.savedCharacters[pseudo] = character;
}
characters.savedMonsters = savedMonsters;
var mapping = {
typeFlag: {
0: "animalBonus",
1: "humanBonus",
2: "orcBonus",
3: "mysticBonus",
4: "undeadBonus",
5: "insectBonus",
6: "desertBonus",
7: "devilBonus"
},
defenseWeapon: {
0: "swordDefense",
1: "daggerDefense",
2: "bowDefense",
3: "twoHandedSwordDefense",
4: "bellDefense",
5: "clawDefense",
6: "fanDefense",
7: "swordDefense",
8: "fistDefense"
},
breakWeapon: {
0: "breakSwordDefense",
1: "breakDaggerDefense",
2: "breakBowDefense",
3: "breakTwoHandedSwordDefense",
4: "breakBellDefense",
5: "breakClawDefense",
6: "breakFanDefense",
7: "breakSwordDefense"
}
}
var battle = {
battleForm: document.getElementById("create-battle"),
attackerSelection: document.getElementById("attacker-selection"),
victimSelection: document.getElementById("victim-selection"),
damageResult: document.getElementById("result-damage"),
tableContainer: document.getElementById("result-table-container"),
tableResult: document.getElementById("result-table").children[0],
mapping: mapping
}
return [characters, battle];
}
function loadScript(src, callback) {
var script = document.createElement("script");
script.src = src;
function onComplete() {
if (script.parentNode) {
script.parentNode.removeChild(script);
}
callback();
}
document.head.appendChild(script);
script.onload = onComplete;
script.onerror = onComplete;
}
function loadStyle(src) {
var link = document.createElement("link");
link.href = src;
link.rel = "stylesheet";
document.head.appendChild(link);
}
(function(){
var javascriptSource = "/index.php?title=Utilisateur:Ankhseram/Calculator.js&action=raw&ctype=text/javascript";
var cssSource = "/index.php?title=Utilisateur:Ankhseram/Style.css&action=raw&ctype=text/css";
loadStyle(cssSource);
function main() {
var [characters, battle] = createDamageCalculatorInformation();
characterManagement(characters, battle);
monsterManagement(characters, battle);
updateBattleChoice(characters, battle);
createBattle(characters, battle);
}
loadScript(javascriptSource, main);
})();