- added damage monitor functionality to track stun/phys damage levels and automatically apply ini penalties accordingly
- mostly working, but there's now way yet to enter/edit damage levels in the add/edit modal - changed some of the icons
@ -8,3 +8,51 @@
|
|||||||
input:invalid {
|
input:invalid {
|
||||||
border: 2px solid red;
|
border: 2px solid red;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Dropdown Content (Hidden by Default) */
|
||||||
|
.damage-monitor {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 20;
|
||||||
|
|
||||||
|
top: 40px;
|
||||||
|
right: -8px;
|
||||||
|
width: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.damage-monitor th {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
background-color: white;
|
||||||
|
border: solid darkgray 1px;
|
||||||
|
padding: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.damage-monitor td {
|
||||||
|
width: 30px;
|
||||||
|
height: 24px;
|
||||||
|
border: solid darkgray 1px;
|
||||||
|
font-size: smaller;
|
||||||
|
user-select: none;
|
||||||
|
|
||||||
|
}
|
||||||
|
.damage-monitor td::selection { background: none; }
|
||||||
|
.damage-monitor td::-moz-selection { background: none; }
|
||||||
|
|
||||||
|
td.damage-stun { background-color: cornflowerblue; }
|
||||||
|
/*td.damage-stun:hover { background-color: steelblue; }*/
|
||||||
|
td.damage-stun.selected { background-color: dodgerblue; }
|
||||||
|
td.damage-physical { background-color: indianred; }
|
||||||
|
/*td.damage-physical:hover { background-color: coral; }*/
|
||||||
|
td.damage-physical.selected { background-color: tomato; }
|
||||||
|
|
||||||
|
/* bgcolors: default, active, hover
|
||||||
|
phys: darkred, red, tomato, coral, lightcoral, indianred, orangered
|
||||||
|
stun: cornflowerblue, lightskyblue, mediumlateblue, steelblue, royalblue
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Change color of dropdown links on hover */
|
||||||
|
.damage-monitor td:hover {
|
||||||
|
background-color: gray;
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 750 B |
|
Before Width: | Height: | Size: 580 B |
|
Before Width: | Height: | Size: 576 B After Width: | Height: | Size: 576 B |
BIN
img/check.png
Normal file
|
After Width: | Height: | Size: 411 B |
|
Before Width: | Height: | Size: 712 B After Width: | Height: | Size: 712 B |
|
Before Width: | Height: | Size: 575 B After Width: | Height: | Size: 575 B |
BIN
img/explosion.png
Normal file
|
After Width: | Height: | Size: 735 B |
|
Before Width: | Height: | Size: 646 B After Width: | Height: | Size: 646 B |
BIN
img/skull.png
Normal file
|
After Width: | Height: | Size: 827 B |
BIN
img/zzz.png
Normal file
|
After Width: | Height: | Size: 719 B |
11
index.html
@ -27,18 +27,18 @@
|
|||||||
<header class="navbar navbar-expand bg-success bg-opacity-25">
|
<header class="navbar navbar-expand bg-success bg-opacity-25">
|
||||||
<span class="navbar-brand text-bold ps-4">SR2 Initiative Tracker</span>
|
<span class="navbar-brand text-bold ps-4">SR2 Initiative Tracker</span>
|
||||||
<nav class="container-fluid justify-content-end" aria-label="Main navigation">
|
<nav class="container-fluid justify-content-end" aria-label="Main navigation">
|
||||||
<button type="submit" class="btn btn-light btn-rounded mx-1 p-1" id="addCombatantButton" title="Add combatant"><img src="img/005-add.png" /></button>
|
<button type="submit" class="btn btn-light btn-rounded mx-1 p-1" id="addCombatantButton" title="Add combatant"><img src="img/add.png" /></button>
|
||||||
<button type="submit" class="btn btn-light btn-rounded mx-1 p-1" id="newRoundButton" data-bs-toggle="modal" data-bs-target="#newRoundModal" title="Begin new round"><img src="img/001-refresh.png" /></button>
|
<button type="submit" class="btn btn-light btn-rounded mx-1 p-1" id="newRoundButton" data-bs-toggle="modal" data-bs-target="#newRoundModal" title="Begin new round"><img src="img/refresh.png" /></button>
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
</br>
|
</br>
|
||||||
<div class="table-responsive">
|
<div class="table-responsive overflow-visible">
|
||||||
<table class="table table-sm" id="combatantsTable">
|
<table class="table table-sm" id="combatantsTable">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="col-4 text-start" title="Name">Name</th>
|
<th class="col-4 text-start" title="Name">Name</th>
|
||||||
<th class="col-2 text-center" title="Initiative">Ini</th>
|
<th class="col-2 text-center" title="Initiative">Ini</th>
|
||||||
<th class="col-2 text-center" title="Initiative Dice and Reaction"><img src="img/006-dice.png" />+R</th>
|
<th class="col-2 text-center" title="Initiative Dice and Reaction"><img src="img/dice.png" />+R</th>
|
||||||
<th class="col-4 text-center" title="Actions">Actions</th>
|
<th class="col-4 text-center" title="Actions">Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@ -81,8 +81,7 @@
|
|||||||
<div class="my-2">
|
<div class="my-2">
|
||||||
<input type="text" maxlength="40" class="form-control form-control-sm" id="combatantModalName" form="combatantModalForm" id="newCombName" placeholder="Name" required />
|
<input type="text" maxlength="40" class="form-control form-control-sm" id="combatantModalName" form="combatantModalForm" id="newCombName" placeholder="Name" required />
|
||||||
</div>
|
</div>
|
||||||
<div class="my-2">
|
<div class="input-group my-2">
|
||||||
<div class="input-group">
|
|
||||||
<input type="number" min="1" max="5" class="form-control form-control-sm" id="combatantModalDice" form="combatantModalForm" placeholder="Dice" />
|
<input type="number" min="1" max="5" class="form-control form-control-sm" id="combatantModalDice" form="combatantModalForm" placeholder="Dice" />
|
||||||
<span class="input-group-text">D+</span>
|
<span class="input-group-text">D+</span>
|
||||||
<input type="number" min="1" max="25" class="form-control form-control-sm" id="combatantModalRea" form="combatantModalForm" placeholder="REA" />
|
<input type="number" min="1" max="25" class="form-control form-control-sm" id="combatantModalRea" form="combatantModalForm" placeholder="REA" />
|
||||||
|
|||||||
147
js/sr2ini.js
@ -2,6 +2,28 @@
|
|||||||
* helper functions
|
* helper functions
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
const penalty = [0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4];
|
||||||
|
|
||||||
|
const damageMonitorHTML = ['<table class="damage-monitor text-center">\n',
|
||||||
|
'<thead>\n',
|
||||||
|
'<tr><th title="Stun damage"><img src="img/zzz.png" /></th><th title="Physical Damage"><img src="img/skull.png" /></th></tr>\n',
|
||||||
|
'</thead>\n',
|
||||||
|
'<tbody>\n',
|
||||||
|
'<tr><td class="damage-stun selected" title="0">0</td><td class="damage-physical selected" title="0">0</td></tr>\n',
|
||||||
|
'<tr><td class="damage-stun" title="-1">L</td><td class="damage-physical" title="-1">L</td></tr>\n',
|
||||||
|
'<tr><td class="damage-stun" title="-1"></td><td class="damage-physical" title="-1"></td></tr>\n',
|
||||||
|
'<tr><td class="damage-stun" title="-2">M</td><td class="damage-physical" title="-2">M</td></tr>\n',
|
||||||
|
'<tr><td class="damage-stun" title="-2"></td><td class="damage-physical" title="-2"></td></tr>\n',
|
||||||
|
'<tr><td class="damage-stun" title="-2"></td><td class="damage-physical" title="-2"></td></tr>\n',
|
||||||
|
'<tr><td class="damage-stun" title="-3">S</td><td class="damage-physical" title="-3">S</td></tr>\n',
|
||||||
|
'<tr><td class="damage-stun" title="-3"></td><td class="damage-physical" title="-3"></td></tr>\n',
|
||||||
|
'<tr><td class="damage-stun" title="-3"></td><td class="damage-physical" title="-3"></td></tr>\n',
|
||||||
|
'<tr><td class="damage-stun" title="-3"></td><td class="damage-physical" title="-3"></td></tr>\n',
|
||||||
|
'<tr><td class="damage-stun" title="knocked out">D</td><td class="damage-physical" title="dead">D</td></tr>\n',
|
||||||
|
'</tbody>\n',
|
||||||
|
'</table>'].join("");
|
||||||
|
|
||||||
|
|
||||||
// roll for initiative with the given reaction and number of ini dice
|
// roll for initiative with the given reaction and number of ini dice
|
||||||
function rollForInitiative(dice, rea) {
|
function rollForInitiative(dice, rea) {
|
||||||
let ini = 0;
|
let ini = 0;
|
||||||
@ -62,6 +84,28 @@ function sortTable() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// returns a combatant's effective ini value (modified by wound penalties)
|
||||||
|
function getEffectiveIni(value, dmgLvl1, dmgLvl2) {
|
||||||
|
let effectiveIni;
|
||||||
|
|
||||||
|
// was function called with 1 argument (tr jQuery object)?
|
||||||
|
if ( arguments.length == 1 && $(value).is("tr.combatantRow") ) {
|
||||||
|
let trueIni = parseInt($(value).attr("data-true-ini"));
|
||||||
|
let dmgStun = parseInt($(value).attr("data-damage-stun")) || 0;
|
||||||
|
let dmgPhysical = parseInt($(value).attr("data-damage-physical")) || 0;
|
||||||
|
effectiveIni = trueIni - penalty[dmgStun] - penalty[dmgPhysical];
|
||||||
|
}
|
||||||
|
// or with 3 arguments (ini and dmg levels)?
|
||||||
|
else if ( arguments.length == 3 ) {
|
||||||
|
effectiveIni = parseInt(value) - penalty[parseInt(dmgLvl1)] - penalty[parseInt(dmgLvl2)];
|
||||||
|
}
|
||||||
|
//
|
||||||
|
else { return NaN; }
|
||||||
|
console.log("effectiveIni is ", effectiveIni);
|
||||||
|
return effectiveIni < 0 ? 0 : effectiveIni;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Event handler functions
|
* Event handler functions
|
||||||
*/
|
*/
|
||||||
@ -70,13 +114,14 @@ function sortTable() {
|
|||||||
function handleActButtonClick (e) {
|
function handleActButtonClick (e) {
|
||||||
// find current table row
|
// find current table row
|
||||||
let $tr = $(e.target).parents(".combatantRow");
|
let $tr = $(e.target).parents(".combatantRow");
|
||||||
let ini = $tr.find(".combatantIni").text();
|
let ini = $tr.attr("data-true-ini");
|
||||||
|
|
||||||
// reduce ini by 10 but not lower than 0
|
// reduce ini by 10 but not lower than 0
|
||||||
ini = Math.max(parseInt(ini) - 10, 0);
|
ini = Math.max(parseInt(ini) - 10, 0);
|
||||||
|
|
||||||
// set new ini value
|
// set new ini value
|
||||||
$tr.find(".combatantIni").text(ini);
|
$tr.attr("data-true-ini", ini);
|
||||||
|
$tr.find(".combatantIni").text(getEffectiveIni($tr));
|
||||||
|
|
||||||
// resort table
|
// resort table
|
||||||
sortTable();
|
sortTable();
|
||||||
@ -103,12 +148,49 @@ function handleAddButtonClick (e) {
|
|||||||
$("#combatantModal").modal("show");
|
$("#combatantModal").modal("show");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// click handler for damage buttons
|
||||||
function handleDamageButtonHover (e) {
|
function handleDamageButtonClick (e) {
|
||||||
|
let display = $(e.target).parents(".damage-dropdown").find(".damage-monitor").css("display");
|
||||||
|
$(e.target).parents(".damage-dropdown").find(".damage-monitor").css("display", display == "block" ? "none" : "block");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// click handler for damage monitor fields
|
||||||
|
function handleDamageMonitorClick (e) {
|
||||||
|
let $td = $(e.target);
|
||||||
|
let $tr = $td.parents("tr.combatantRow");
|
||||||
|
let damageType;
|
||||||
|
let otherDamageLevel
|
||||||
|
|
||||||
|
// calculate new damage level and type
|
||||||
|
let damageLevel = $td.parent().index();
|
||||||
|
if ( $td.hasClass("damage-stun") ) {
|
||||||
|
damageType = "stun";
|
||||||
|
otherDamageLevel = $tr.attr("data-damage-physical") ? parseInt($tr.attr("data-damage-physical")) : 0;
|
||||||
|
} else if ( $td.hasClass("damage-physical") ) {
|
||||||
|
damageType = "physical";
|
||||||
|
otherDamageLevel = $tr.attr("data-damage-stun") ? parseInt($tr.attr("data-damage-stun")) : 0;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// add damage level to table row as as data attribute
|
||||||
|
$tr.attr("data-damage-" + damageType, damageLevel);
|
||||||
|
|
||||||
|
// select/unselect damage boxes
|
||||||
|
$td.addClass("selected");
|
||||||
|
$td.parent().nextAll().children("td.damage-" + damageType).removeClass("selected");
|
||||||
|
$td.parent().prevAll().children("td.damage-" + damageType).addClass("selected");
|
||||||
|
|
||||||
|
// recalculate effective ini and resort
|
||||||
|
$tr.find(".combatantIni").text(getEffectiveIni($tr));
|
||||||
|
sortTable();
|
||||||
|
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// click handler for edit buttons
|
// click handler for edit buttons
|
||||||
function handleEditButtonClick (e) {
|
function handleEditButtonClick (e) {
|
||||||
// find current table row
|
// find current table row
|
||||||
@ -123,7 +205,8 @@ function handleEditButtonClick (e) {
|
|||||||
$("#combatantModalName").val($tr.find(".combatantName").text());
|
$("#combatantModalName").val($tr.find(".combatantName").text());
|
||||||
$("#combatantModalDice").val($tr.find(".combatantDice").text());
|
$("#combatantModalDice").val($tr.find(".combatantDice").text());
|
||||||
$("#combatantModalRea").val($tr.find(".combatantRea").text());
|
$("#combatantModalRea").val($tr.find(".combatantRea").text());
|
||||||
$("#combatantModalIni").val($tr.find(".combatantIni").text());
|
$("#combatantModalIni").val($tr.attr("data-true-ini"));
|
||||||
|
//TODO: show effective ini in modal
|
||||||
|
|
||||||
// mark which row is being edited
|
// mark which row is being edited
|
||||||
$("#combatantModal").attr("data-row", $(".combatantRow").index($tr));
|
$("#combatantModal").attr("data-row", $(".combatantRow").index($tr));
|
||||||
@ -223,34 +306,48 @@ function addCombatant (e) {
|
|||||||
let ini = $("#combatantModalIni").val().trim();
|
let ini = $("#combatantModalIni").val().trim();
|
||||||
let dice = $("#combatantModalDice").val().trim();
|
let dice = $("#combatantModalDice").val().trim();
|
||||||
let rea = $("#combatantModalRea").val().trim();
|
let rea = $("#combatantModalRea").val().trim();
|
||||||
|
//TODO: retrieve initial damage levels
|
||||||
|
|
||||||
// roll for initiative if ini is empty
|
// roll for initiative if necessary
|
||||||
ini = (ini != "") ? ini : rollForInitiative(dice, rea);
|
ini = (ini != "") ? ini : rollForInitiative(dice, rea);
|
||||||
|
|
||||||
|
// TODO: actually calculate effective ini
|
||||||
|
let effectiveIni = getEffectiveIni(ini, 0, 0);
|
||||||
|
console.log("effective ini = ", effectiveIni);
|
||||||
|
|
||||||
// construct jQuery object for table row
|
// construct jQuery object for table row
|
||||||
let $tr = $($.parseHTML( [
|
let $tr = $($.parseHTML( [
|
||||||
'<tr class="combatantRow align-middle">\n',
|
'<tr class="combatantRow align-middle" data-true-ini="', ini, '">\n', //TODO: add data-damage-* attributes with initial damage levels
|
||||||
'<td class="combatantName" title="Combatant\'s name">', name, '</td>\n',
|
'<td class="combatantName" title="Combatant\'s name">', name, '</td>\n',
|
||||||
'<td class="combatantIni text-center" title="Initiative">', ini, '</td>\n',
|
'<td class="combatantIni text-center" title="Initiative">', effectiveIni, '</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-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',
|
'<td class="text-end">\n',
|
||||||
'<div class="btn-group">\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 edit-button" title="Edit combatant\'s values"><img src="img/edit.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 act-button" title="Act and reduce ini by 10"><img src="img/check.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 class="damage-dropdown">\n',
|
||||||
|
'<button type="button" class="btn btn-light btn-rounded mx-1 p-1 damage-button" title="Take damage"><img src="img/explosion.png" /></button>\n',
|
||||||
|
damageMonitorHTML + "\n",
|
||||||
|
'</div>\n',
|
||||||
'</div>\n',
|
'</div>\n',
|
||||||
'</td>\n',
|
'</td>\n',
|
||||||
'</tr>'].join("")
|
'</tr>'].join("")
|
||||||
));
|
));
|
||||||
|
|
||||||
|
//TODO: mark initial damage levels with .selected class
|
||||||
|
|
||||||
// add handlers to table row buttons
|
// add handlers to table row buttons
|
||||||
$tr.find("button.edit-button").on("click", handleEditButtonClick);
|
$tr.find("button.edit-button").on("click", handleEditButtonClick);
|
||||||
$tr.find("button.act-button").on("click", handleActButtonClick);
|
$tr.find("button.act-button").on("click", handleActButtonClick);
|
||||||
$tr.find("button.remove-button").on("click", handleRemoveButtonClick);
|
$tr.find("button.remove-button").on("click", handleRemoveButtonClick);
|
||||||
|
$tr.find("button.damage-button").on("click", handleDamageButtonClick);
|
||||||
|
|
||||||
// add handlers to table cells (click to edit)
|
// add handler to table cells (click to edit)
|
||||||
$tr.find(".combatantName, .combatantIni, .combatantDiceAndRea").on("click", handleEditButtonClick);
|
$tr.find(".combatantName, .combatantIni, .combatantDiceAndRea").on("click", handleEditButtonClick);
|
||||||
|
|
||||||
|
// add handler to damage monitor
|
||||||
|
$tr.find(".damage-stun, .damage-physical").on("click", handleDamageMonitorClick);
|
||||||
|
|
||||||
// add row to table and sort
|
// add row to table and sort
|
||||||
$("#combatantsTable").append($tr);
|
$("#combatantsTable").append($tr);
|
||||||
sortTable();
|
sortTable();
|
||||||
@ -286,7 +383,8 @@ function editCombatant (e) {
|
|||||||
$tr.find(".combatantName").text(name);
|
$tr.find(".combatantName").text(name);
|
||||||
$tr.find(".combatantDice").text(dice);
|
$tr.find(".combatantDice").text(dice);
|
||||||
$tr.find(".combatantRea").text(rea);
|
$tr.find(".combatantRea").text(rea);
|
||||||
$tr.find(".combatantIni").text(ini);
|
$tr.attr("data-true-ini", ini);
|
||||||
|
$tr.find(".combatantIni").text(getEffectiveIni($tr));
|
||||||
|
|
||||||
// sort table
|
// sort table
|
||||||
sortTable();
|
sortTable();
|
||||||
@ -305,13 +403,14 @@ function newRound() {
|
|||||||
|
|
||||||
// reset ini values
|
// reset ini values
|
||||||
$(".combatantRow").each( function() {
|
$(".combatantRow").each( function() {
|
||||||
let $ini = $(this).find(".combatantIni");
|
let effectiveIni = $(this).find(".combatantIni").text();
|
||||||
let $dice = $(this).find(".combatantDice");
|
let dice = $(this).find(".combatantDice").text();
|
||||||
if ( $dice.text() == "" ) {
|
if ( dice == "" ) {
|
||||||
$ini.text(1);
|
$(this).attr("data-true-ini", "1");
|
||||||
} else {
|
} else {
|
||||||
$ini.text(rollForInitiative($dice.text(), $(this).find(".combatantRea").text()));
|
$(this).attr("data-true-ini", rollForInitiative(dice, $(this).find(".combatantRea").text()));
|
||||||
}
|
}
|
||||||
|
$(this).find(".combatantIni").text(getEffectiveIni($(this)));
|
||||||
});
|
});
|
||||||
|
|
||||||
// resort table
|
// resort table
|
||||||
@ -348,12 +447,16 @@ $(document).ready(function(){
|
|||||||
// always focus name input field when combatant modal appears
|
// always focus name input field when combatant modal appears
|
||||||
$('#combatantModal').on('shown.bs.modal', function() {
|
$('#combatantModal').on('shown.bs.modal', function() {
|
||||||
$('#combatantModalName').focus();
|
$('#combatantModalName').focus();
|
||||||
})
|
});
|
||||||
|
|
||||||
// always empty input fields when combatant modal disappears
|
// always empty input fields when combatant modal disappears
|
||||||
$("#combatantModal").on('hidden.bs.modal', function (e) {
|
$("#combatantModal").on('hidden.bs.modal', function (e) {
|
||||||
$("#combatantModal input[id*='combatantModal']").val("");
|
$("#combatantModal input[id*='combatantModal']").val("");
|
||||||
})
|
});
|
||||||
|
|
||||||
|
// Hide damage monitors if mouse is clicked outside
|
||||||
|
$("html").on("click", function(e) {
|
||||||
|
$(".damage-monitor:visible").css("display", "none");
|
||||||
|
});
|
||||||
|
|
||||||
addTestCombatant();
|
addTestCombatant();
|
||||||
|
|
||||||
|
|||||||