diff --git a/package.json b/package.json index f73599f..46ebf59 100644 --- a/package.json +++ b/package.json @@ -1,18 +1,29 @@ { "name": "sr2ini", "version": "1.0.0", - "description": "Simple Initiative tracker for Shadowrun 2e.", + "description": "Simple Initiative tracker for Shadowrun 2e", + "private": true, + "author": { + "name": "Eclipse729", + "email": "eclipse@unterdemradar.de", + "url": "https://www.unterdemradar.de" + }, + "homepage": "https://www.unterdemradar.de/sr2ini", + "license": "ISC", "source": "src/index.html", "devDependencies": { "@parcel/transformer-sass": "^2.8.3", "parcel": "^2.8.3", "parcel-reporter-static-files-copy": "^1.5.0" }, + "dependencies": { + }, "staticFiles": { "staticPath": "src/img" }, "scripts": { "start": "npx parcel serve src/index.html --public-url / --dist-dir dist", + "prebuild": "rm -rf dist/", "build": "npx parcel build", "test": "echo \"Error: no test specified\" && exit 1" }, @@ -24,11 +35,4 @@ "Shadowrun", "Initiative tracker", "sr2e" - ], - "author": "Eclipse", - "license": "ISC", - "dependencies": { - "bootstrap": "^5.2.3", - "jquery": "^3.6.3" - } -} + ]} \ No newline at end of file diff --git a/src/css/custom.scss b/src/css/custom.scss index 43b5d08..69ea536 100644 --- a/src/css/custom.scss +++ b/src/css/custom.scss @@ -1,7 +1,3 @@ -/* Import all of Bootstrap's CSS */ -@import "../../node_modules/bootstrap/scss/bootstrap.scss"; - - header.navbar { background-image: url("../img/horizon.png"); } @@ -12,6 +8,10 @@ span.navbar-brand { text-shadow: 2px 0 0 #fff, -2px 0 0 #fff, 0 2px 0 #fff, 0 -2px 0 #fff, 1px 1px #fff, -1px -1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff; } +header .btn { + --aug-all-width: "42px"; +} + input:invalid { border: 2px solid red; } @@ -26,6 +26,18 @@ input:invalid { font-weight: bold; } +.combatant-name::after { + content: attr(data-combatant-name); +} + +.combatant-dice::after { + content: attr(data-combatant-dice); +} + +.combatant-rea::after { + content: attr(data-combatant-rea); +} + .badge.bg-warning { left: 12px; bottom: -4px; @@ -41,45 +53,48 @@ input:invalid { .damage-monitor { position: absolute; z-index: 20; - top: 40px; + top: 100%; right: -8px; width: 60px; + + th { + width: 28px; + height: 28px; + padding: 3px; + } + + td { + width: 30px; + height: 24px; + } + + button { + font-size: smaller; + width: 30px; + height: 24px; + padding: 2px; + } + + button.active { + filter: brightness(88%); + } + + td button img { + position: relative; + top: -2px; + } + } -.damage-monitor th { - width: 28px; - height: 28px; - /* background-color: white;*/ - /* border: solid darkgray 1px;*/ - padding: 3px; -} +footer { + z-index: -10 !important; -.damage-monitor td { - width: 30px; - height: 24px; -} + p { + font-size: x-small; + margin-bottom: .5rem; + } -.damage-monitor button { - font-size: smaller; - height: 24px; - width: 30px; - padding: 2px; + hr { + margin: .5rem 0; + } } - -.damage-monitor button.active { - filter: brightness(88%); -} - -.damage-monitor td button img { - position: relative; - top: -2px; -} - -footer p { - font-size: x-small; - margin-bottom: .5rem; -} - -footer hr { - margin: .5rem 0; -} \ No newline at end of file diff --git a/src/index.html b/src/index.html index 6ed6c9b..212b5a9 100644 --- a/src/index.html +++ b/src/index.html @@ -9,9 +9,13 @@ Shadowrun 2e Ini Tracker - - + + + + + + @@ -21,12 +25,12 @@
- +
@@ -41,8 +45,8 @@ - \n', //TODO: add data-damage-* attributes with initial damage levels - '\n', - '\n', - '\n', +'\n', //TODO: add data-damage-* attributes with initial damage levels + '\n', + '\n', + '\n', '\n', '\n', '\n', - '\n', + '\n', '
Name
D+
D+\n', '
\n', - '\n', + '\n', '\n', '
\n', '\n', @@ -41,7 +34,7 @@ const DAMAGE_MONITOR_HTML = [ '
'].join(""); const STUN_BADGE_HTML = ''; const PHYSICAL_BADGE_HTML = ''; @@ -62,8 +55,8 @@ function rollForInitiative(dice, rea) { // figure out whose action comes first out of two combatants a and b function whoGoesFirst(a, b) { // check for K.O./death - let tmpA = $(a).hasClass(CONTEXTUAL_CLASSES["KO_OR_DEAD"]) ? -1 : parseInt($(a).find(".combatantIni").text()) || 0; - let tmpB = $(b).hasClass(CONTEXTUAL_CLASSES["KO_OR_DEAD"]) ? -1 : parseInt($(b).find(".combatantIni").text()) || 0; + let tmpA = $(a).hasClass(CONTEXTUAL_CLASSES["KO_OR_DEAD"]) ? -1 : parseInt($(a).find(".combatant-ini").text()) || 0; + let tmpB = $(b).hasClass(CONTEXTUAL_CLASSES["KO_OR_DEAD"]) ? -1 : parseInt($(b).find(".combatant-ini").text()) || 0; // compare ini let compIni = tmpB - tmpA; if (compIni != 0) { @@ -71,10 +64,10 @@ function whoGoesFirst(a, b) { } // tie; compare reaction else { - if ($(a).find(".combatantRea").text() == "" || $(b).find(".combatantRea").text() == "") { + if ($(a).find(".combatant-rea").text() == "" || $(b).find(".combatant-rea").text() == "") { return 0; } else { - return parseInt($(b).find(".combatantRea").text()) - parseInt($(a).find(".combatantRea").text()); + return parseInt($(b).find(".combatant-rea").text()) - parseInt($(a).find(".combatant-rea").text()); } } } @@ -91,13 +84,13 @@ function getEffectiveIni(tr) { // add test combatant for testing purposes (duh) function addTestCombatant() { // Eclipse - $("#addCombatantButton").click(); - $("#combatantModalName").val("Eclipse"); - $("#combatantModalDice").val(3); - $("#combatantModalRea").val(6); - // $("#combatantModalIni").val(12); + $("#add-combatant-button").click(); + $("#combatant-modal-name").val("Eclipse"); + $("#combatant-modal-dice").val(3); + $("#combatant-modal-rea").val(6); + // $("#combatant-modal-ini").val(12); setTimeout(function () { - $("#combatantModalAddOkButton").click(); + $("#combatant-modal-add-ok-button").click(); }, 500); } /* @@ -106,21 +99,21 @@ function addTestCombatant() { // click handler for act buttons; reduces ini by 10 function handleActButtonClick(e) { // reduce ini by 10 but not lower than 0 - let ini = Math.max(parseInt($(e.target).parents(".combatantRow").attr("data-true-ini")) - 10, 0); + let ini = Math.max(parseInt($(e.target).parents(".combatant-row").attr("data-true-ini")) - 10, 0); // set new ini value - $(e.target).parents(".combatantRow").attr("data-true-ini", ini); + $(e.target).parents(".combatant-row").attr("data-true-ini", ini); // resort table sortTable(); } // click handler for add buttons function handleAddButtonClick(e) { // restyle modal - $("#combatantModal .modal-title").text("Add Combatant"); - $("#combatantModalAddOkButton").removeClass("d-none"); - $("#combatantModalEditOkButton").addClass("d-none"); + $("#combatant-modal .modal-title").text("Add Combatant"); + $("#combatant-modal-add-ok-button").removeClass("d-none"); + $("#combatant-modal-edit-ok-button").addClass("d-none"); // add handler for enter key - $("#combatantModal input[id*='combatantModal']").off("keydown"); - $("#combatantModal input[id*='combatantModal']").on("keydown", function (e) { + $("#combatant-modal input[id*='combatant-modal']").off("keydown"); + $("#combatant-modal input[id*='combatant-modal']").on("keydown", function (e) { if (e.which == 13 || e.which == 10) { addCombatant(e); } @@ -141,24 +134,24 @@ function handleDamageButtonClick(e) { // click handler for edit buttons function handleEditButtonClick(e) { // find current table row - let $tr = $(e.target).parents(".combatantRow"); + let $tr = $(e.target).parents(".combatant-row"); // restyle modal - $("#combatantModal .modal-title").text("Edit Combatant"); - $("#combatantModalAddOkButton").addClass("d-none"); - $("#combatantModalEditOkButton").removeClass("d-none"); + $("#combatant-modal .modal-title").text("Edit Combatant"); + $("#combatant-modal-add-ok-button").addClass("d-none"); + $("#combatant-modal-edit-ok-button").removeClass("d-none"); // 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.attr("data-true-ini")); + $("#combatant-modal-name").val($tr.find(".combatant-name").attr("data-combatant-name")); + $("#combatant-modal-dice").val($tr.find(".combatant-dice").attr("data-combatant-dice")); + $("#combatant-modal-rea").val($tr.find(".combatant-rea").attr("data-combatant-rea")); + $("#combatant-modal-ini").val($tr.attr("data-true-ini")); // show effective ini in modal $("#penalty-stun").text(DAMAGE_PENALTY[parseInt($tr.attr("data-damage-stun")) || 0]); $("#penalty-physical").text(DAMAGE_PENALTY[parseInt($tr.attr("data-damage-physical")) || 0]); // mark which row is being edited - $("#combatantModal").data("row", $(".combatantRow").index($tr)); // here it's okay to use .data() b/c HTML/CSS does not care about this value + $("#combatant-modal").data("row", $(".combatant-row").index($tr)); // here it's okay to use .data() b/c HTML/CSS does not care about this value // add handler for enter key - $("#combatantModal input[id*='combatantModal']").off("keydown"); - $("#combatantModal input[id*='combatantModal']").on("keydown", function (e) { + $("#combatant-modal input[id*='combatant-modal']").off("keydown"); + $("#combatant-modal input[id*='combatant-modal']").on("keydown", function (e) { if (e.which == 13 || e.which == 10) { editCombatant(e); } @@ -167,43 +160,43 @@ function handleEditButtonClick(e) { function handleNewRoundButton(e) { // restyle modal - $("#confirmModal .modal-title").text("Start new Round"); - $("#confirmModalNewRoundOkButton").removeClass("d-none"); - $("#confirmModalRemoveCombatantOkButton").addClass("d-none"); + $("#confirm-modal .modal-title").text("Start new Round"); + $("#confirm-modal-new-round-ok-button").removeClass("d-none"); + $("#confirm-modal-remove-combatant-ok-button").addClass("d-none"); } // click handler for remove buttons function handleRemoveButtonClick(e) { // restyle modal - $("#confirmModal .modal-title").text("Remove Combatant"); - $("#confirmModalRemoveCombatantOkButton").removeClass("d-none"); - $("#confirmModalNewRoundOkButton").addClass("d-none"); + $("#confirm-modal .modal-title").text("Remove Combatant"); + $("#confirm-modal-remove-combatant-ok-button").removeClass("d-none"); + $("#confirm-modal-new-round-ok-button").addClass("d-none"); // mark which row is being removed - $("#confirmModal").data("row", $(".combatantRow").index($(e.target).parents(".combatantRow"))); // here it's okay to use .data() b/c HTML/CSS does not care about this value + $("#confirm-modal").data("row", $(".combatant-row").index($(e.target).parents(".combatant-row"))); // here it's okay to use .data() b/c HTML/CSS does not care about this value } /* * Main functions */ // add new combatant function addCombatant(e) { - e.preventDefault(); + // e.preventDefault(); // validate form if (!validateCombatant()) { return false; } // roll for initiative if necessary - let ini = $("#combatantModalIni").val().trim(); - ini = (ini != "") ? ini : rollForInitiative($("#combatantModalDice").val(), $("#combatantModalRea").val()); + let ini = $("#combatant-modal-ini").val().trim(); + ini = (ini != "") ? ini : rollForInitiative($("#combatant-modal-dice").val(), $("#combatant-modal-rea").val()); // construct jQuery object for table row let $tr = $($.parseHTML(COMBATANT_TABLE_ROW)); $tr.find(".damage-dropdown").append($.parseHTML(DAMAGE_MONITOR_HTML)); - // populate table row with values from modal + // populate table row with values from modal $tr.attr("data-true-ini", ini); - $tr.find(".combatantName").text($("#combatantModalName").val().trim()); - $tr.find(".combatantDice").text($("#combatantModalDice").val().trim()); - $tr.find(".combatantRea").text($("#combatantModalRea").val().trim()); + $tr.find(".combatant-name").attr("data-combatant-name", $("#combatant-modal-name").val().trim()); + $tr.find(".combatant-dice").attr("data-combatant-dice", $("#combatant-modal-dice").val().trim()); + $tr.find(".combatant-rea").attr("data-combatant-rea", $("#combatant-modal-rea").val().trim()); //TODO: retrieve initial damage levels // add handler to table cells (click to edit) - $tr.find(".combatantName, .combatantIni, .combatantDiceAndRea").on("click", handleEditButtonClick); + $tr.find(".combatant-name, .combatant-ini, .combatant-dice-and-rea").on("click", handleEditButtonClick); // add handlers to action buttons $tr.find("button.edit-button").on("click", handleEditButtonClick); $tr.find("button.act-button").on("click", handleActButtonClick); @@ -212,19 +205,19 @@ function addCombatant(e) { // add handler to damage monitor $tr.find(".damage-stun, .damage-physical").on("click", applyDamage); // add row to table and sort - $("#combatantsTable").append($tr); + $("#combatants-table").append($tr); sortTable(); } // event handler for when any damage monitor is clicked function applyDamage(e) { - let $btn = $(e.target).is("button") ? $(e.target) : $(e.target).parents("button")[0]; + let $btn = $(e.target).is("button") ? $(e.target) : $(e.target).parent(); // retrieve new damage level and type from button position and "damage-[type]" class let damageLevel = $btn.parent().parent().index(); let damageType = $btn.attr("class").split(" ").filter(function (cls) { return cls.substr(0, 7) == "damage-" ? cls : false; }).toString().substr(7); // add damage level to table row as as data attribute - $btn.parents("tr.combatantRow").attr("data-damage-" + damageType, damageLevel); + $btn.parents("tr.combatant-row").attr("data-damage-" + damageType, damageLevel); // select/unselect damage buttons above/below $btn.addClass("active"); $btn.parent().parent().nextAll().find("button.damage-" + damageType).removeClass("active"); @@ -234,55 +227,54 @@ function applyDamage(e) { } // edit combatant function editCombatant(e) { - e.preventDefault(); + // e.preventDefault(); // validate form if (!validateCombatant()) { return false; } // get values - let name = $("#combatantModalName").val().trim(); - let ini = $("#combatantModalIni").val().trim(); - let dice = $("#combatantModalDice").val().trim(); - let rea = $("#combatantModalRea").val().trim(); + let name = $("#combatant-modal-name").val().trim(); + let ini = $("#combatant-modal-ini").val().trim(); + let dice = $("#combatant-modal-dice").val().trim(); + let rea = $("#combatant-modal-rea").val().trim(); // roll for initiative if ini is empty ini = (ini != "") ? ini : rollForInitiative(dice, rea); // get correct row - let index = parseInt($("#combatantModal").data("row")); - $tr = $("tr.combatantRow").eq(index); + let index = parseInt($("#combatant-modal").data("row")); + $tr = $("tr.combatant-row").eq(index); // set new values - $tr.find(".combatantName").text(name); - $tr.find(".combatantDice").text(dice); - $tr.find(".combatantRea").text(rea); $tr.attr("data-true-ini", ini); + $tr.find(".combatant-name").attr("data-combatant-name", name); + $tr.find(".combatant-dice").attr("data-combatant-dice", dice); + $tr.find(".combatant-rea").attr("data-combatant-rea", rea); // sort table sortTable(); // clean up - $("#combatantModal").removeAttr("data-row"); + $("#combatant-modal").data("row", ""); } // remove combatant function removeCombatant(e) { e.preventDefault(); // remove correct row - let index = parseInt($("#confirmModal").data("row")); - $(".combatantRow").eq(index).remove(); + let index = parseInt($("#confirm-modal").data("row")); + $(".combatant-row").eq(index).remove(); sortTable(); // clean up - $("#confirmModal").removeAttr("data-row"); + $("#confirm-modal").data("row", ""); } // start a new combat round function startNewRound(e) { e.preventDefault(); - // hide modal // are there rows at all? - if ($(".combatantRow").length == 0) { + if ($(".combatant-row").length == 0) { return; } // reset ini values - $(".combatantRow").each(function () { - if ($(this).find(".combatantDice").text() == "") { + $(".combatant-row").each(function () { + if ($(this).find(".combatant-dice").attr("data-combatant-dice") == "") { $(this).attr("data-true-ini", 1); } else { - $(this).attr("data-true-ini", rollForInitiative(parseInt($(this).find(".combatantDice").text()), parseInt($(this).find(".combatantRea").text()))); + $(this).attr("data-true-ini", rollForInitiative(parseInt($(this).find(".combatant-dice").attr("data-combatant-dice")), parseInt($(this).find(".combatant-rea").attr("data-combatant-rea")))); } }); // resort table @@ -291,30 +283,30 @@ function startNewRound(e) { // add contextual classes and sort combatants by ini value function sortTable() { // do some clean up: remove previous classes from rows, disable act buttons, remove effective ini and damage badges - $(".combatantRow").removeClass(Object.values(CONTEXTUAL_CLASSES).join(" ")); - $(".combatantRow").find(".act-button").prop("disabled", true).attr("aria-disabled", "true"); - $(".combatantIni").empty(); + $(".combatant-row").removeClass(Object.values(CONTEXTUAL_CLASSES).join(" ")); + $(".combatant-row").find(".act-button").prop("disabled", true).attr("aria-disabled", "true"); + $(".combatant-ini").empty(); // mark KO or death with class - $(".combatantRow").each(function () { + $(".combatant-row").each(function () { if (parseInt($(this).attr("data-damage-stun")) == 10 || parseInt($(this).attr("data-damage-physical")) == 10) { $(this).addClass(CONTEXTUAL_CLASSES["KO_OR_DEAD"]); } }); // compute highest effective ini - let iniMax = Math.max.apply(null, $.map($(".combatantRow"), function (tr, i) { + let iniMax = Math.max.apply(null, $.map($(".combatant-row"), function (tr, i) { // write current effective ini to table row - $(tr).find(".combatantIni").text($(tr).hasClass(CONTEXTUAL_CLASSES["KO_OR_DEAD"]) ? 0 : getEffectiveIni($(tr))); - return $(tr).find(".combatantIni").text(); + $(tr).find(".combatant-ini").text($(tr).hasClass(CONTEXTUAL_CLASSES["KO_OR_DEAD"]) ? 0 : getEffectiveIni($(tr))); + return $(tr).find(".combatant-ini").text(); })); // add damage badges and contextual classes - $(".combatantRow").each(function () { + $(".combatant-row").each(function () { // damage badges if ($(this).attr("data-damage-stun") && $(this).attr("data-damage-stun") != "0") { - $(this).find(".combatantIni").append($.parseHTML(STUN_BADGE_HTML)); + $(this).find(".combatant-ini").append($.parseHTML(STUN_BADGE_HTML)); $(this).find(".stun-badge").append(DAMAGE_NIVEAU[DAMAGE_PENALTY[$(this).attr("data-damage-stun")]]); } if ($(this).attr("data-damage-physical") && $(this).attr("data-damage-physical") != "0") { - $(this).find(".combatantIni").append($.parseHTML(PHYSICAL_BADGE_HTML)); + $(this).find(".combatant-ini").append($.parseHTML(PHYSICAL_BADGE_HTML)); $(this).find(".physical-badge").append(DAMAGE_NIVEAU[DAMAGE_PENALTY[$(this).attr("data-damage-physical")]]); } // K.O./dead -> don't add anything @@ -322,12 +314,12 @@ function sortTable() { return true; } // ini = zero - if (parseInt($(this).find(".combatantIni").text()) == 0) { + if (parseInt($(this).find(".combatant-ini").text()) == 0) { $(this).addClass(CONTEXTUAL_CLASSES["ZERO_INI"]); return true; } // ini = max and non-zero - if (parseInt($(this).find(".combatantIni").text()) == iniMax && iniMax > 0) { + if (parseInt($(this).find(".combatant-ini").text()) == iniMax && iniMax > 0) { $(this).addClass(CONTEXTUAL_CLASSES["MAX_INI"]).find(".act-button").prop("disabled", false).removeAttr("aria-disabled"); return true; } @@ -335,9 +327,9 @@ function sortTable() { $(this).addClass(CONTEXTUAL_CLASSES["REGULAR_INI"]); }) // sort rows and append them in new order - let $rows = $(".combatantRow").toArray().sort(whoGoesFirst); + let $rows = $(".combatant-row").toArray().sort(whoGoesFirst); for (let i = 0; i < $rows.length; i++) { - $("#combatantsTable").append($rows[i]); + $("#combatants-table").append($rows[i]); } return; } @@ -345,10 +337,10 @@ function sortTable() { function validateCombatant() { // get input elements let inputElements = { - name: $("#combatantModalName").get(0), - ini: $("#combatantModalIni").get(0), - dice: $("#combatantModalDice").get(0), - rea: $("#combatantModalRea").get(0) + name: $("#combatant-modal-name").get(0), + ini: $("#combatant-modal-ini").get(0), + dice: $("#combatant-modal-dice").get(0), + rea: $("#combatant-modal-rea").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) @@ -387,20 +379,20 @@ function validateCombatant() { */ $(document).ready(function () { // add event handlers to navbar buttons - $("#addCombatantButton").on("click", handleAddButtonClick); - $("#newRoundButton").on("click", handleNewRoundButton); + $("#add-combatant-button").on("click", handleAddButtonClick); + $("#new-round-button").on("click", handleNewRoundButton); // add event handlers to modal buttons - $("#combatantModalAddOkButton").on("click", addCombatant); - $("#combatantModalEditOkButton").on("click", editCombatant); - $("#confirmModalNewRoundOkButton").on("click", startNewRound); - $("#confirmModalRemoveCombatantOkButton").on("click", removeCombatant); + $("#combatant-modal-add-ok-button").on("click", addCombatant); + $("#combatant-modal-edit-ok-button").on("click", editCombatant); + $("#confirm-modal-new-round-ok-button").on("click", startNewRound); + $("#confirm-modal-remove-combatant-ok-button").on("click", removeCombatant); // always focus name input field when combatant modal appears - $('#combatantModal').on('shown.bs.modal', function () { - $('#combatantModalName').focus(); + $('#combatant-modal').on('shown.bs.modal', function () { + $('#combatant-modal-name').focus(); }); // always empty input fields when combatant modal disappears - $("#combatantModal").on('hidden.bs.modal', function (e) { - $("#combatantModal input[id*='combatantModal']").val(""); + $("#combatant-modal").on('hidden.bs.modal', function (e) { + $("#combatant-modal input[id*='combatant-modal']").val(""); }); // Hide damage monitors after click somewhere else $("html").on("click", function (e) { @@ -409,5 +401,4 @@ $(document).ready(function () { } }); addTestCombatant(); - console.log("trololol"); }); \ No newline at end of file