sr2ini/js/sr2ini.js

361 lines
10 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* helper functions
*/
// roll for initiative with the given reaction and number of ini dice
function rollForInitiative(dice, rea) {
let ini = 0;
for ( let i = 0; i < parseInt(dice); i++ ) {
roll = Math.floor(Math.random() * 6) + 1;
ini += roll;
}
return ini + parseInt(rea);
}
// figure out whose action comes first out of two combatants a and b
function whoGoesFirst(a, b) {
let comparer = parseInt($(b).find(".combatantIni").text()) - parseInt($(a).find(".combatantIni").text());
if (comparer != 0) {
return comparer;
} else {
let reaA = parseInt($(a).find(".combatantRea").text());
let reaB = parseInt($(b).find(".combatantRea").text());
reaA = isNaN(reaA) ? 0 : reaA;
if (isNaN(reaB)) { reaB = 0; }
console.log(reaA, reaB);
return reaB - reaA;
}
}
// sorts the combatants by ini value
function sortTable() {
// sort rows and append them in new order
let $rows = $(".combatantRow").toArray().sort(whoGoesFirst);
for ( var i = 0; i < $rows.length; i++ ) {
$("#combatantsTable").append($rows[i]);
}
// add contectual classes to rows currently for highest ini and ini = 0
// compute highest ini
let iniValues = $.map( $(".combatantIni"), function(td, i) {
return parseInt($(td).text());
});
let iniMax = Math.max.apply(null, iniValues);
// add contextual classes to rows
$(".combatantRow").each( function() {
// always remove previous classes, disable act button
$(this).removeClass("table-primary table-secondary").find(".act-button").prop("disabled", true).attr("aria-disabled", "true");
// add class if ini is zero
if ( parseInt($(this).find(".combatantIni").text()) == 0 ) {
$(this).addClass("table-secondary");
}
// add class, enable act button if ini is max and non-zero
else if ( parseInt($(this).find(".combatantIni").text()) == iniMax && iniMax > 0 ) {
$(this).addClass("table-primary").find(".act-button").prop("disabled", false).removeAttr("aria-disabled");
}
})
return;
}
/*
* Event handler functions
*/
// click handler for act buttons
function handleActButtonClick (e) {
// find current table row
let $tr = $(e.target).parents(".combatantRow");
let ini = $tr.find(".combatantIni").text();
// reduce ini by 10 but not lower than 0
ini = Math.max(parseInt(ini) - 10, 0);
// set new ini value
$tr.find(".combatantIni").text(ini);
// resort table
sortTable();
}
// click handler for add buttons
function handleAddButtonClick (e) {
// restyle modal
$("#combatantModal .modal-title").text("Add Combatant");
$("#combatantModalAddOkButton").show();
$("#combatantModalEditOkButton").hide();
// add handler for enter key
$("#combatantModal input[id*='combatantModal']").off("keydown");
$("#combatantModal input[id*='combatantModal']").on("keydown", function (e) {
if ( e.which == 13 || e.which == 10 ) {
addCombatant(e);
}
});
// show modal
$("#combatantModal").modal("show");
}
function handleDamageButtonHover (e) {
}
// click handler for edit buttons
function handleEditButtonClick (e) {
// find current table row
let $tr = $(e.target).parents(".combatantRow");
// restyle modal
$("#combatantModal .modal-title").text("Edit Combatant");
$("#combatantModalAddOkButton").hide();
$("#combatantModalEditOkButton").show();
// populate modal with values from row
$("#combatantModalName").val($tr.find(".combatantName").text());
$("#combatantModalDice").val($tr.find(".combatantDice").text());
$("#combatantModalRea").val($tr.find(".combatantRea").text());
$("#combatantModalIni").val($tr.find(".combatantIni").text());
// mark which row is being edited
$("#combatantModal").attr("data-row", $(".combatantRow").index($tr));
// add handler for enter key
$("#combatantModal input[id*='combatantModal']").off("keydown");
$("#combatantModal input[id*='combatantModal']").on("keydown", function (e) {
if ( e.which == 13 || e.which == 10 ) {
editCombatant(e);
}
});
// show modal
$("#combatantModal").modal("show");
}
// click handler for remove buttons
function handleRemoveButtonClick (e) {
// remove table row
$(e.target).parents(".combatantRow").remove();
}
/*
* Validation functions
*/
// validate a combatant row form by checking for all conditions, including regular HTML5 validation
function validateCombatant() {
// get input elements
let inputElements = {
name: $("#combatantModalName").get(0),
ini: $("#combatantModalIni").get(0),
dice: $("#combatantModalDice").get(0),
rea: $("#combatantModalRea").get(0)
};
// do standard HTML5 form validation first
// (makes sure that name is not empty and that all other values are numbers within their individual ranges)
let valid = true;
Object.values(inputElements).forEach(function(input) {
if ( ! input.reportValidity() ) {
valid = false;
}
})
if ( ! valid ) {
return false;
}
// now for some custom validation; first we need to get the input values
let ini = inputElements["ini"].value.trim();
let dice = inputElements["dice"].value.trim();
let rea = inputElements["rea"].value.trim();
// invalidate if ini, dice and rea are all empty
if ( ini == "" && ( dice == "" || rea == "" ) ) {
inputElements["ini"].setCustomValidity("Requiring values for ini dice and reaction, or initiative, or all three");
inputElements["ini"].reportValidity();
inputElements["ini"].setCustomValidity("");
return false;
}
// invalidate if dice or rea is empty but not both
if ( ( dice == "" ) != ( rea == "" ) ) {
inputElements["dice"].setCustomValidity("Values required for both dice and reaction, or none (in which case ini is required)");
inputElements["dice"].reportValidity();
inputElements["dice"].setCustomValidity("");
return false;
}
// ok then
return true;
}
/*
* Main functions
*/
// add new combatant
function addCombatant (e) {
e.preventDefault();
// validate form
if ( ! validateCombatant() ) {
return false;
}
// hide modal
$("#combatantModal").modal("hide");
// get values
let name = $("#combatantModalName").val().trim();
let ini = $("#combatantModalIni").val().trim();
let dice = $("#combatantModalDice").val().trim();
let rea = $("#combatantModalRea").val().trim();
// roll for initiative if ini is empty
ini = (ini != "") ? ini : rollForInitiative(dice, rea);
// construct jQuery object for table row
let $tr = $($.parseHTML( [
'<tr class="combatantRow align-middle">\n',
'<td class="combatantName" title="Combatant\'s name">', name, '</td>\n',
'<td class="combatantIni text-center" title="Initiative">', ini, '</td>\n',
'<td class="text-center combatantDiceAndRea" title="Iniative dice and reaction"><span class="combatantDice">', dice, '</span>D+<span class="combatantRea">', rea, '</span></td>\n',
'<td class="text-end">\n',
'<div class="btn-group">\n',
'<button type="button" class="btn btn-light btn-rounded mx-1 p-1 edit-button" title="Edit combatant\'s values"><img src="img/004-edit-button.png" /></button>\n',
'<button type="button" class="btn btn-light btn-rounded mx-1 p-1 act-button" title="Act and reduce ini by 10"><img src="img/003-explosion.png" /></button>\n',
'<button type="button" class="btn btn-light btn-rounded mx-1 p-1 remove-button" title="Remove combatant"><img src="img/002-band-aid.png" /></button>\n',
'</div>\n',
'</td>\n',
'</tr>'].join("")
));
// add handlers to table row buttons
$tr.find("button.edit-button").on("click", handleEditButtonClick);
$tr.find("button.act-button").on("click", handleActButtonClick);
$tr.find("button.remove-button").on("click", handleRemoveButtonClick);
// add handlers to table cells (click to edit)
$tr.find(".combatantName, .combatantIni, .combatantDiceAndRea").on("click", handleEditButtonClick);
// add row to table and sort
$("#combatantsTable").append($tr);
sortTable();
}
// edit combatant values
function editCombatant (e) {
e.preventDefault();
// validate form
if ( ! validateCombatant() ) {
return false;
}
// hide modal
$("#combatantModal").modal("hide");
// get values
let name = $("#combatantModalName").val().trim();
let ini = $("#combatantModalIni").val().trim();
let dice = $("#combatantModalDice").val().trim();
let rea = $("#combatantModalRea").val().trim();
// roll for initiative if ini is empty
ini = (ini != "") ? ini : rollForInitiative(dice, rea);
// get correct row
let index = parseInt($("#combatantModal").attr("data-row"));
$tr = $("tr.combatantRow").eq(index);
// set new values
$tr.find(".combatantName").text(name);
$tr.find(".combatantDice").text(dice);
$tr.find(".combatantRea").text(rea);
$tr.find(".combatantIni").text(ini);
// sort table
sortTable();
// clean up
$("#combatantModal").removeAttr("data-row");
}
// start a new combat round
function newRound() {
// are there rows at all?
if ( $(".combatantRow").length == 0 ) {
return;
}
// reset ini values
$(".combatantRow").each( function() {
let $ini = $(this).find(".combatantIni");
let $dice = $(this).find(".combatantDice");
if ( $dice.text() == "" ) {
$ini.text(1);
} else {
$ini.text(rollForInitiative($dice.text(), $(this).find(".combatantRea").text()));
}
});
// resort table
sortTable();
}
// add test combatant for testing purposes (duh)
function addTestCombatant() {
$("#addCombatantButton").click();
$("#combatantModalName").val("Goon1");
$("#combatantModalDice").val(2);
$("#combatantModalRea").val(6);
$("#combatantModalIni").val(12);
setTimeout(function(){
$("#combatantModalAddOkButton").click();
},500);
}
/*
* Initialize document
*/
$(document).ready(function(){
// add event handlers to navbar buttons
$("#addCombatantButton").on("click", handleAddButtonClick);
$("#newroundModalOkButton").on("click", newRound);
// add event handlers to modal buttons
$("#combatantModalAddOkButton").on("click", addCombatant);
$("#combatantModalEditOkButton").on("click", editCombatant);
// always focus name input field when combatant modal appears
$('#combatantModal').on('shown.bs.modal', function() {
$('#combatantModalName').focus();
})
// always empty input fields when combatant modal disappears
$("#combatantModal").on('hidden.bs.modal', function (e) {
$("#combatantModal input[id*='combatantModal']").val("");
})
addTestCombatant();
});