MediaWiki:ClassMasteryCalculator.js: Difference between revisions
From EQOA Sandstorm Wiki
Jump to navigationJump to search
RenodorDev (talk | contribs) No edit summary |
RenodorDev (talk | contribs) No edit summary |
||
| Line 28: | Line 28: | ||
var data = window.EQOA_CM_DATA; | var data = window.EQOA_CM_DATA; | ||
var selectedMasteries = {}; | var selectedMasteries = {}; | ||
var activeCmTab = "General"; | |||
app.innerHTML = ` | app.innerHTML = ` | ||
| Line 60: | Line 61: | ||
<div class="character-builder-section-title" style="margin-top:18px;">Class Masteries</div> | <div class="character-builder-section-title" style="margin-top:18px;">Class Masteries</div> | ||
<div class="cm-tabs"> | |||
<button type="button" class="cm-tab-button cm-tab-active" data-tab="General">General</button> | |||
<button type="button" class="cm-tab-button" data-tab="Racial">Racial</button> | |||
<button type="button" class="cm-tab-button" data-tab="Archetype">Archetype</button> | |||
<button type="button" class="cm-tab-button" data-tab="Class">Class</button> | |||
</div> | |||
<div id="cm-mastery-panel" class="character-builder-warning-box"> | |||
Select a race and class to begin. | |||
</div> | |||
</div> | </div> | ||
`; | `; | ||
| Line 174: | Line 183: | ||
}); | }); | ||
return raceOk && classOk && levelOk && spentOk && reqOk; | var tabOk = cm.classification === activeCmTab; | ||
return raceOk && classOk && levelOk && spentOk && reqOk && tabOk; | |||
}); | }); | ||
| Line 181: | Line 192: | ||
return; | return; | ||
} | } | ||
var html = ''; | var html = ''; | ||
filtered.forEach(function (cm) { | |||
html += | |||
'<div class="cm-card">' + | |||
'<div class="cm-card-title">' + cm.name + '</div>' + | |||
'<div class="cm-card-desc">' + cm.description + '</div>' + | |||
'<div class="cm-card-meta">' + | |||
'Level: ' + cm.level + | |||
' | Cost: ' + cm.points + | |||
(cm.cmSpentReq ? ' | CM Spent Req: ' + cm.cmSpentReq : '') + | |||
'</div>' + | |||
'</div>'; | |||
}); | |||
masteryPanel.innerHTML = html; | |||
} | } | ||
| Line 220: | Line 215: | ||
levelInput.addEventListener('input', updateSummary); | levelInput.addEventListener('input', updateSummary); | ||
Array.prototype.forEach.call( | |||
document.querySelectorAll('.cm-tab-button'), | |||
function (button) { | |||
button.addEventListener('click', function () { | |||
activeCmTab = button.getAttribute('data-tab'); | |||
Array.prototype.forEach.call( | |||
document.querySelectorAll('.cm-tab-button'), | |||
function (btn) { | |||
btn.classList.remove('cm-tab-active'); | |||
} | |||
); | |||
button.classList.add('cm-tab-active'); | |||
updateSummary(); | |||
}); | |||
} | |||
); | |||
loadRaces(); | loadRaces(); | ||
} | } | ||
}); | }); | ||
Revision as of 18:28, 14 May 2026
mw.hook('wikipage.content').add(function () {
function waitForCMData(attemptsLeft) {
if (window.EQOA_CM_DATA) {
initClassMasteryCalculator();
return;
}
if (attemptsLeft <= 0) {
var app = document.getElementById('cm-calculator-app');
if (app) {
app.innerHTML = '<div style="color:red;">EQOA_CM_DATA failed to load.</div>';
}
return;
}
setTimeout(function () {
waitForCMData(attemptsLeft - 1);
}, 100);
}
waitForCMData(30);
function initClassMasteryCalculator() {
var app = document.getElementById('cm-calculator-app');
if (!app) return;
var data = window.EQOA_CM_DATA;
var selectedMasteries = {};
var activeCmTab = "General";
app.innerHTML = `
<div class="cm-calculator-box">
<div class="character-builder-title">Class Mastery Calculator</div>
<div class="character-builder-controls">
<label>Race</label>
<select id="cm-race"></select>
<label>Class</label>
<select id="cm-class"></select>
<label>Character Level</label>
<input type="number" id="cm-level" min="1" max="60" value="60" style="width:70px;">
</div>
<div class="character-builder-section-title" style="margin-top:15px;">Point Summary</div>
<table class="character-builder-stat-table">
<tr>
<th>Available</th>
<th>Spent</th>
<th>Remaining</th>
</tr>
<tr>
<td id="cm-available">1500</td>
<td id="cm-spent">0</td>
<td id="cm-remaining">1500</td>
</tr>
</table>
<div class="character-builder-section-title" style="margin-top:18px;">Class Masteries</div>
<div class="cm-tabs">
<button type="button" class="cm-tab-button cm-tab-active" data-tab="General">General</button>
<button type="button" class="cm-tab-button" data-tab="Racial">Racial</button>
<button type="button" class="cm-tab-button" data-tab="Archetype">Archetype</button>
<button type="button" class="cm-tab-button" data-tab="Class">Class</button>
</div>
<div id="cm-mastery-panel" class="character-builder-warning-box">
Select a race and class to begin.
</div>
</div>
`;
var raceSelect = document.getElementById('cm-race');
var classSelect = document.getElementById('cm-class');
var levelInput = document.getElementById('cm-level');
function addOption(select, value, label) {
var option = document.createElement('option');
option.value = value;
option.textContent = label;
select.appendChild(option);
}
function loadRaces() {
raceSelect.innerHTML = '';
Object.keys(data.raceClasses).forEach(function (race) {
addOption(raceSelect, race, race);
});
loadClasses();
}
function loadClasses() {
classSelect.innerHTML = '';
var race = raceSelect.value;
var classList = data.raceClasses[race] || [];
classList.forEach(function (classCode) {
addOption(
classSelect,
classCode,
classCode + ' - ' + (data.classes[classCode] || classCode)
);
});
updateSummary();
}
function getAvailablePoints() {
var level = parseInt(levelInput.value, 10);
if (isNaN(level) || level < 1) level = 1;
if (level > 60) level = 60;
levelInput.value = level;
return level * 25;
}
function getSpentPoints() {
var total = 0;
Object.keys(selectedMasteries).forEach(function (name) {
if (selectedMasteries[name]) {
total += selectedMasteries[name].points || 0;
}
});
return total;
}
function updateSummary() {
var available = getAvailablePoints();
var spent = getSpentPoints();
var remaining = available - spent;
document.getElementById('cm-available').textContent = available;
document.getElementById('cm-spent').textContent = spent;
document.getElementById('cm-remaining').textContent = remaining;
renderMasteryList();
}
function renderMasteryList() {
var masteryPanel = document.getElementById('cm-mastery-panel');
if (!masteryPanel) return;
var selectedRace = raceSelect.value;
var selectedRaceCode = data.raceCodes[selectedRace];
var selectedClass = classSelect.value;
var level = parseInt(levelInput.value, 10) || 1;
var spent = getSpentPoints();
var filtered = (data.masteries || []).filter(function (cm) {
var raceOk =
!cm.races.length ||
cm.races.indexOf(selectedRaceCode) !== -1;
var classOk =
!cm.classes.length ||
cm.classes.indexOf(selectedClass) !== -1;
var levelOk =
!cm.level ||
cm.level <= level;
var spentOk =
!cm.cmSpentReq ||
spent >= cm.cmSpentReq;
var reqs = cm.requirements || [];
var reqOk = reqs.every(function (reqName) {
return selectedMasteries[reqName];
});
var tabOk = cm.classification === activeCmTab;
return raceOk && classOk && levelOk && spentOk && reqOk && tabOk;
});
if (!filtered.length) {
masteryPanel.innerHTML = 'No Class Masteries found for this selection.';
return;
}
var html = '';
filtered.forEach(function (cm) {
html +=
'<div class="cm-card">' +
'<div class="cm-card-title">' + cm.name + '</div>' +
'<div class="cm-card-desc">' + cm.description + '</div>' +
'<div class="cm-card-meta">' +
'Level: ' + cm.level +
' | Cost: ' + cm.points +
(cm.cmSpentReq ? ' | CM Spent Req: ' + cm.cmSpentReq : '') +
'</div>' +
'</div>';
});
masteryPanel.innerHTML = html;
}
raceSelect.addEventListener('change', loadClasses);
classSelect.addEventListener('change', updateSummary);
levelInput.addEventListener('input', updateSummary);
Array.prototype.forEach.call(
document.querySelectorAll('.cm-tab-button'),
function (button) {
button.addEventListener('click', function () {
activeCmTab = button.getAttribute('data-tab');
Array.prototype.forEach.call(
document.querySelectorAll('.cm-tab-button'),
function (btn) {
btn.classList.remove('cm-tab-active');
}
);
button.classList.add('cm-tab-active');
updateSummary();
});
}
);
loadRaces();
}
});