- started a complete redesign

- removed zero-row from damage monitor; damage buttons now toggle
- added decreasing z-indexes on sorted combatant-rows to mitigate a weird bug where damage monitors would be displayed under instead of over the rows
This commit is contained in:
Tobias 2023-02-19 00:01:12 +01:00
parent 43a9aef8b1
commit cb36ae4159
9 changed files with 482 additions and 105 deletions

67
TODO.md Normal file
View File

@ -0,0 +1,67 @@
% sr2ini: Bugs and Features Tracker
% Eclipse
%
(Nichts hier ist in "master" gefixt)
# Bugs
- x Bug: KO/dead buttons fkt. nicht
- applyDamage, ersetze erste Zeile: let $btn = $(e.target).is("button") ? $(e.target) : $(e.target).parents("button")[0];
- x Bug: Kann nicht weit genug runterscrollen für remove bzw die unteren schadenskastchen
- Footer hatte viel zu großen z-index
- x Bug: resort after remove -> einfach eingefügt
- x background-image wiederholt sich -> hat sich nach Redesign erledigt
- x Habe alle $("…").modal("hide|show") eliminiert; die Sichtbarkeit der Modals wird jetzt ausschließlich durch data-bs-Attribute gesteuert
- x add-Button leert die inputs vom Modal nicht mehr
- event hidden.bs.modal feuert nicht mehr
- zuerst dachte ich, es liegt daran, dass ich das bootstrap js zu Klasse Modal nicht laden konnte. Mittlerweile habe ich es hingekriegr, doch das event feuert trotzdem nicht
- hab's hingekriegt, indem ich jquery und bootstrap nun doch wieder in der index.html lade und nicht mehr als import.
- x in großer Darstellung bleiben u.U. links und rechts Ränder frei, wo das Hintergrundbild nicht skaliert wird -> background-size: cover
- x bei Benutzung von augmented-ui: der Text von .combatant-name ist nach oben verschoben und links+rechts abgeschnitten
- grundlegendes Problem: augmented-ui nutzt ::before und ::after, um die Styles zu erzeugen; mein eigenes ::before kollidiert damit
- füge den .combatant-name jetzt wieder direkt ein, nicht mehr per Datenattribut
- x damit nirgendwo ein Stück border fehlt, müssen die Ecken von zwei benachbarten combatant tablerows nicht gegeneinander verschoben sein
- d.h. die beiden linken und die beiden rechten Ecken einer Tablerow müssen jeweils die gleiche X-Position haben
- x alles, was vom damage monitor über den unteren Tabellenrand rüberragt, wird abgeschnitten
- musste den clip-path von tr und td auf none setzen
- jetzt kann ich den damage-monitor zwar aufklappen, aber die Buttons der weiter unten liegenden combatant-rows überdecken ihn jedesmal.
- wenn ich einen Damage-Button anklicke, beginnt die Transition des damage-monitor von Neuem
- Output vom HTML Validator:
- img elements müssen alt attributes haben
- The form attribute must refer to a form element. (bei den Input Elementen im combatant-modal) -> muss nachsehen, was ich dann genau da eintragen muss
- wenn die Navbar borders hat, sollte sie nicht direkt am oberen Bildrand anfangen, sondern ein paar Pixel weiter unten
- nach dem Laden passiert es manchmal, dass nach dem Einfügen von testCombatant das add Modal gleich wieder aufgeht
- schien die gleiche Sache zu sein wie mit dem hidden.bs.modal event
- jetzt kommt es aber trotzdem manchmal wieder
- vllt. ein timeout-Problem?
- add modal geht bei enter key nicht zu -> liegt daran, dass ich nicht mehr .modal("hide") verwende
- ich könnt's umstellen, aber will ich das?
- Unterschied zwischen enabled und disabled button nicht gut erkennbar
- wenn ein damage monitor offen ist und ich auf add combatant clicke, springt der Fokus nicht zuverlässig ins erste input feld
- navbar bei größerem viewport
- brand-name nicht mittig
- Bug: rea editieren ändert nicht die ini -> da muss ich die Würfelergebnisse für speichern
# Feature Requests
- x nochmal wg. Daten wie name, dice, rea, true-ini und damage-x:
- Verwalte sie jetzt komplett mit der data-* API; verwende dafür ausschließlich .attr() als Getter/Setter
- Füge die Werte aus dem Attribut per CSS direkt ins Element ein (::after und content).
- x prettify code: alle HTML class names von camelCale zu dash-case komvertieren
- neue Icons: act add cross damage dice edit newround trash zzz clone
- attribution
- noun project: Icons by Febrian Hidayat from <a href="https://thenounproject.com/icons" target="_blank" title="Icons">Noun Project</a>
- iconfinder: CC4.0
- icons8.com:
- Im modal, wenn ich die wound penalties anzeige, die Fälle KO und Tod gesondert behandeln
- im modal soll man die damage levels einstellen/verändern können
- "clone this combatant" button:
- Anzeige, wieviele Aktionen einer hat u.d wieviele davon schon verbraucht sind
- Design cyberpunkig machen
- docstrings
- color converter für CSS filter(): https://isotropic.co/tool/hex-color-to-css-filter/

View File

@ -1,19 +1,190 @@
$sr-border: cyan;
$sr2-btn: darkcyan;
$sr2-fg: deeppink;
@mixin aug() {
--aug-tl: 5px;
--aug-l: 5px;
--aug-bl: 5px;
--aug-tr: 5px;
--aug-r: 5px;
--aug-br: 5px;
}
@mixin border() {
--aug-border-bg: cyan; // "sr2-border doesn't work"
--aug-border-opacity: .5;
--aug-border-left: 2px;
--aug-border-right: 0px;
--aug-border-top: 2px;
--aug-border-bottom: 0px;
}
@mixin inlay() {
--aug-inlay-y: 10%;
--aug-inlay-bg: rgba(0, 0, 0, .5);
}
@mixin button() {
border: 0;
border-radius: none;;
background: radial-gradient(circle at center, gold, goldenrod);
box-shadow: 0 0 3px gold, 0 0 6px goldenrod, 0 0 12px darkgoldenrod;
}
html,
body {
margin: 0;
height: 100%;
// overflow-x: hidden;
}
body {
background-image: linear-gradient(rgba(0, 0, 0, 0.3), rgba(0, 0, 0, 0.3)), url("../img/circuit_board_by_xxaries1970xx_d9ut9nd.jpg");
background-size: cover;
background-repeat: no-repeat;
background-position: center center;
background-color: darkslategray;
font-family: 'Electrolize', sans-serif !important;
}
header.navbar {
background-image: url("../img/horizon.png");
@include border;
--aug-br: 10px;
--aug-tl: 10px;
--aug-inlay-bg: rgba(0, 0, 0, .3);
}
span.navbar-brand {
position: absolute;
right: 110px;
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;
// position: absolute;
// right: 110px;
// color: $sr2-fg;
// 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";
div.container {
position: relative;
}
input:invalid {
border: 2px solid red;
#combatants-table {
margin-top: .5rem;
tr {
@include aug;
@include border;
@include inlay;
}
.combatant-row {
clip-path: none;
}
.th-ini {
min-width: 3rem;
}
.th-dice-and-rea {
min-width: 3.75rem;
}
.th-actions {
min-width: 6.5rem;
}
th,
td {
@include aug;
@include border;
@include inlay;
background: none !important;
// text-shadow: 2px 2px 3px black;
}
th {
color: cyan;
text-transform: uppercase;
// text-shadow: 0 0 3px deeppink, 0 0 6px deeppink, 0 0 12px deeppink, 0 0 24 deeppink;
}
td {
color: $sr2-fg;
clip-path: none;
}
th:first-of-type,
td:first-of-type {
padding-left: 1rem !important;
}
th:last-of-type,
td:last-of-type {
padding-right: .75rem;
--aug-border-right: 2px;
}
td.combatant-actions {
}
tr:last-of-type td {
--aug-border-bottom: 2px;
}
> tbody > tr > td > button,
> tbody > tr > td > div > button, {
padding: 0px;
padding-right: 2px;
padding-left: 2px;
@include button;
img {
width: 20px;
height: 20px;
// filter: invert(34%) sepia(78%) saturate(7014%) hue-rotate(316deg) brightness(75%) contrast(105%) drop-shadow(1 1 1px cyan) drop-shadow(2 2 2px darkcyan);
// animation: glitch 1s ease infinite;
}
/* @keyframes glitch {
// twitch
0%, 33% {
transform: scale(.97, 1.01);
}
18%, 60% {
transform: scaleX(.98),
}
48%, 76% {
transform: scaleY(1.02);
}
68%, 100% {
transform: scale(1, .96);
}
// skew
20%, 75% {
transform: skew(2deg);
}
12%,80%{
transform: skew(1.4deg);
}
}
&:nth-of-type(2) img {
animation-direction: reverse;
}
*/
}
}
.combatant-ini {
text-align: center;
padding-right: 1rem !important;
}
.combatant-dice::before {
content: attr(data-combatant-dice);
}
.combatant-rea::before {
content: attr(data-combatant-rea);
}
.ko-or-dead {
@ -22,61 +193,93 @@ input:invalid {
background-color: darkslategray;
}
.table-primary {
.max-ini {
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);
.zero-ini td {
color: lightgray !important;
}
.badge.bg-warning {
background: radial-gradient(circle at center, cyan, darkcyan);
left: 12px;
bottom: -4px;
width: 20px;
}
.badge.bg-danger {
background: radial-gradient(circle at center, deeppink, maroon);
left: 12px;
top: 12px;
width: 20px;
}
.damage-dropdown {
display: inline-block;
}
.damage-monitor {
position: absolute;
z-index: 20;
top: 100%;
right: -8px;
width: 60px;
z-index: 200;
th {
width: 28px;
height: 28px;
padding: 3px;
}
top: calc(100% - 2px);
right: 10px;
td {
width: 30px;
height: 24px;
}
@include aug;
--aug-inlay-bg: rgba(0, 0, 0, .5);
--aug-border-all: 2px;
--aug-border-bg: cyan; // "sr2-border doesn't work"
--aug-border-opacity: .75;
padding-top: 10px;
padding-bottom: 10px;
button {
font-size: smaller;
width: 30px;
width: 24px;
height: 24px;
padding: 2px;
border: none;
margin: 0px 2px;
border-radius: none;
}
button.active {
filter: brightness(88%);
.damage-stun {
background: radial-gradient(circle at center, cyan, darkcyan);
box-shadow: none;
transition: background .5s, box-shadow .5s;
}
.damage-stun.active {
background: radial-gradient(circle at center, lightcyan, cyan);
box-shadow: 0 0 3px lightcyan, 0 0 6px cyan, 0 0 12px darkcyan;
}
.damage-physical {
border-radius: 50%;;
background: radial-gradient(circle at center, deeppink, maroon);
box-shadow: none;
transition: background .5s, box-shadow .5s;
}
.damage-physical.active {
background: radial-gradient(circle at center, lightpink, deeppink);
box-shadow: 0 0 3px lightpink, 0 0 6px deeppink, 0 0 12px maroon;
}
.remove-button {
width: 56px;
height: 28px;
margin-top: 4px;
@include button;
// background: radial-gradient(circle at center, gold, goldenrod);
// box-shadow: 0 0 3px gold, 0 0 6px goldenrod, 0 0 12px darkgoldenrod;
}
td button img {
@ -86,6 +289,33 @@ input:invalid {
}
/*
.damage-monitor.d-none {
opacity: 0;
animation-name: dmg-mon;
animation-duration: 1s;
animation-direction: reverse;
}
.damage-monitor:not(.d-none) {
opacity: 1;
animation-name: dmg-mon;
animation-duration: 1s;
animation-direction: normal;
}
@keyframes dmg-mon {
from {
opacity: 0;
}
1% {
opacity: 0;
}
100% {
opacity: 1;
}
}
*/
footer {
z-index: -10 !important;
@ -98,3 +328,83 @@ footer {
margin: .5rem 0;
}
}
input:invalid {
border: 2px solid red;
}
/*
th span{
position: absolute;
display: block;
}
th span:nth-child(1){
top: 0;
left: 0;
width: 100%;
height: 2px;
background: linear-gradient(90deg,transparent,darkcyan);
animation: animate1 1s linear infinite;
}
@keyframes animate1{
0%{
left: -100%;
}
50%,100%{
left: 100%;
}
}
th span:nth-child(2){
top: -100%;
right: 0;
width: 2px;
height: 100%;
background: linear-gradient(180deg,transparent,darkcyan);
animation: animate2 1s linear infinite;
animation-delay: 0.25s;
}
@keyframes animate2{
0%{
top: -100%;
}
50%,100%{
top: 100%;
}
}
th span:nth-child(3){
bottom: 0;
right: 0;
width: 100%;
height: 2px;
background: linear-gradient(270deg,transparent,darkcyan);
animation: animate3 1s linear infinite;
animation-delay: 0.50s;
}
@keyframes animate3{
0%{
right: -100%;
}
50%,100%{
right: 100%;
}
}
th span:nth-child(4){
bottom: -100%;
left: 0;
width: 2px;
height: 100%;
background: linear-gradient(360deg,transparent,darkcyan);
animation: animate4 1s linear infinite;
animation-delay: 0.75s;
}
@keyframes animate4{
0%{
bottom: -100%;
}
50%,100%{
bottom: 100%;
}
}*/

BIN
src/img/001-edit.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 396 B

BIN
src/img/002-act.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 628 B

BIN
src/img/003-damage.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

View File

@ -11,32 +11,37 @@
<!-- Style sheets -->
<link type="text/css" rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@latest/dist/css/bootstrap.min.css" >
<link type="text/css" rel="stylesheet" href="https://unpkg.com/augmented-ui@latest/augmented-ui.min.css">
<link type="text/css" rel="stylesheet" href="css/custom.scss" />
<link type="text/css" rel="stylesheet" href="css/custom.scss">
<!-- javascript files -->
<script type="module" src="https://cdn.jsdelivr.net/npm/bootstrap@latest/dist/js/bootstrap.min.js"></script>
<script type="module" src="https://code.jquery.com/jquery-latest.min.js" type="text/javascript"></script>
<script type="module" src="js/sr2ini.js" type="text/javascript"></script>
<script type="module" src="https://code.jquery.com/jquery-latest.min.js"></script>
<script type="module" src="js/sr2ini.js"></script>
<!-- web font -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Electrolize&display=swap" rel="stylesheet">
</head>
<body>
<div class="container">
<header class="navbar navbar-expand">
<header class="navbar navbar-expand" data-augmented-ui="tl-clip-y br-clip-y both">
<span class="navbar-brand text-bold ps-4">SR2 Initiative Tracker</span>
<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="add-combatant-button" title="Add combatant" data-bs-toggle="modal" data-bs-target="#combatant-modal"><img src="img/add.png" /></button>
<button type="submit" class="btn btn-light btn-rounded mx-1 p-1" id="new-round-button" data-bs-toggle="modal" data-bs-target="#confirm-modal" title="Start new round"><img src="img/newround.png" /></button>
<button type="submit" class="" id="add-combatant-button" title="Add combatant" data-bs-toggle="modal" data-bs-target="#combatant-modal"><img src="img/add.png"></button>
<button type="submit" class="" id="new-round-button" data-bs-toggle="modal" data-bs-target="#confirm-modal" title="Start new round"><img src="img/newround.png"></button>
</nav>
</header>
<div class="table-responsive overflow-visible">
<table class="table table-sm" id="combatants-table">
<table class="table table-sm table-borderless" id="combatants-table">
<thead>
<tr>
<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 Dice and Reaction"><img src="img/dice.png" />+R</th>
<th class="col-4 text-center" title="Actions">Actions</th>
<tr data-augmented-ui="tl-2-clip-y r-clip-y">
<th class="col text-start th-name" data-augmented-ui="tl-2-clip-y both" title="Name">Name</th>
<th class="col-2 text-center th-ini" data-augmented-ui="both" title="Initiative">Ini</th>
<th class="col-2 text-center th-dice-and-rea" data-augmented-ui="both" title="Initiative Dice and Reaction"><img src="img/dice.png">+R</th>
<th class="col-3 text-center th-actions" data-augmented-ui="r-clip-y both" title="Actions">Actions</th>
</tr>
</thead>
<tbody>
@ -74,15 +79,15 @@
<div class="modal-body">
<form id="combatant-form" name="combatant-modal-form" class="was-validated" onsubmit="return false;">
<div class="my-2">
<input type="text" maxlength="40" class="form-control form-control-sm" id="combatant-modal-name" form="combatant-modal-form" placeholder="Name" required />
<input type="text" maxlength="40" class="form-control form-control-sm" id="combatant-modal-name" form="#combatant-modal-form" placeholder="Name" required>
</div>
<div class="input-group input-group-sm my-2">
<input type="number" min="1" max="5" class="form-control form-control-sm" id="combatant-modal-dice" form="combatant-modal-form" placeholder="Dice" />
<input type="number" min="1" max="5" class="form-control form-control-sm" id="combatant-modal-dice" form="#combatant-modal-form" placeholder="Dice">
<span class="input-group-text">D+</span>
<input type="number" min="1" max="25" class="form-control form-control-sm" id="combatant-modal-rea" form="combatant-modal-form" placeholder="REA" />
<input type="number" min="1" max="25" class="form-control form-control-sm" id="combatant-modal-rea" form="#combatant-modal-form" placeholder="REA">
</div>
<div class="my-2">
<input type="number" min="0" max="55" class="form-control form-control-sm" id="combatant-modal-ini" form="combatant-modal-form" placeholder="Ini" />
<input type="number" min="0" max="55" class="form-control form-control-sm" id="combatant-modal-ini" form="#combatant-modal-form" placeholder="Ini">
<label for="combatant-modal-ini" class="form-label">Wound penalties: -<span id="penalty-stun">0</span> (stun) and -<span id="penalty-physical">0</span> (physical)</label>
</div>
</form>
@ -98,9 +103,9 @@
<div class="container">
<footer class="footer fixed-bottom bg-light">
<hr />
<p class="text-center text-muted">code & design by <a href="https://tobias-radloff.de/">Eclipse</a> | Shadowrun trademarked by <a href="https://www.topps.com/">Topps</a></br>
icons by <a href="https://www.freepik.com" title="Freepik">Freepik</a> from <a href="https://www.flaticon.com/" title="Flaticon">www.flaticon.com</a> | banner image from <a href="https://www.pinterest.ph/artstation/">Artstation-HQ</a></p>
<hr>
<p class="text-center text-muted">code & design by <a href="https://tobias-radloff.de/">Eclipse</a> | Shadowrun trademarked by <a href="https://www.topps.com/">Topps</a><br>
icons by <a href="https://www.freepik.com" title="Freepik">Freepik</a> from <a href="https://www.flaticon.com/" title="Flaticon">www.flaticon.com</a> | background by <a href="https://www.deviantart.com/xxaries1970xx">xxAries1970xx</a></p>
</footer>
</div>

View File

@ -4,46 +4,34 @@
const DAMAGE_PENALTY = [0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4];
const DAMAGE_NIVEAU = ["", "L", "M", "S", "D"];
const COMBATANT_TABLE_ROW = [
'<tr class="combatant-row align-middle" data-true-ini="">\n', //TODO: add data-damage-* attributes with initial damage levels
'<td class="combatant-name" title="Combatant\'s name" data-bs-toggle="modal" data-bs-target="#combatant-modal"></td>\n',
'<td class="combatant-ini text-center" title="Effective initiative (w/ wound penalties)" data-bs-toggle="modal" data-bs-target="#combatant-modal"></td>\n',
'<td class="text-center combatant-dice-and-rea" title="Iniative dice and reaction" data-bs-toggle="modal" data-bs-target="#combatant-modal"><span class="combatant-dice"></span>D+<span class="combatant-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" data-bs-toggle="modal" data-bs-target="#combatant-modal"><img src="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="act.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="damage.png" /></button>\n',
'</div>\n',
'<tr class="combatant-row align-middle" data-true-ini="" data-augmented-ui="tl-scoop bl-clip-y tr-clip-y br-scoop">\n', //TODO: add data-damage-* attributes with initial damage levels
'<td class="combatant-name" title="Combatant\'s name" data-bs-toggle="modal" data-bs-target="#combatant-modal" data-augmented-ui="tl-scoop bl-clip-y both">Test</td>\n',
'<td class="combatant-ini" title="Effective initiative (w/ wound penalties)" data-bs-toggle="modal" data-bs-target="#combatant-modal" data-augmented-ui="both"></td>\n',
'<td class="text-center combatant-dice-and-rea" title="Iniative dice and reaction" data-bs-toggle="modal" data-bs-target="#combatant-modal" data-augmented-ui="both"><span class="combatant-dice"></span>D+<span class="combatant-rea"></span></td>\n',
'<td class="combatant-actions text-end" data-augmented-ui="tr-clip-y br-scoop both">\n',
'<button type="button" class="edit-button" title="Edit combatant\'s values" data-bs-toggle="modal" data-bs-target="#combatant-modal"><img src="001-edit.png"/></button>\n',
'<button type="button" class="act-button" title="Act and reduce ini by 10"><img src="002-act.png"/></button>\n',
'<div class="damage-dropdown">\n',
'<button type="button" class="damage-button" title="Take damage" ><img src="003-damage.png"/></button>\n',
'</div>\n',
'</td>\n',
'</tr>'].join("");
const DAMAGE_MONITOR_HTML = [
'<table class="bg-light damage-monitor text-center align-middle d-none">\n',
'<tr>\n',
'<td><button class="btn btn-sm btn-warning damage-stun active" title="+/-0">0</button></td>\n',
'<td><button class="btn btn-sm btn-danger damage-physical active" title="+/-0">0</button></td>\n',
'</tr>\n',
'<tr><td><button class="btn btn-sm btn-warning damage-stun" title="Light stun damage">L</button></td><td><button class="btn btn-sm btn-danger damage-physical" title="Light physical damage">L</button></td></tr>\n',
'<tr><td><button class="btn btn-sm btn-warning damage-stun"></button></td><td><button class="btn btn-sm btn-danger damage-physical"></button></td></tr>\n',
'<tr><td><button class="btn btn-sm btn-warning damage-stun" title="Medium stun damage">M</button></td><td><button class="btn btn-sm btn-danger damage-physical" title="Medium physical damage">M</button></td></tr>\n',
'<tr><td><button class="btn btn-sm btn-warning damage-stun"></button></td><td><button class="btn btn-sm btn-danger damage-physical"></button></td></tr>\n',
'<tr><td><button class="btn btn-sm btn-warning damage-stun"></button></td><td><button class="btn btn-sm btn-danger damage-physical"></button></td></tr>\n',
'<tr><td><button class="btn btn-sm btn-warning damage-stun" title="Severe stun damage">S</button></td><td><button class="btn btn-sm btn-danger damage-physical" title="Severe physical damage">S</button></td></tr>\n',
'<tr><td><button class="btn btn-sm btn-warning damage-stun"></button></td><td><button class="btn btn-sm btn-danger damage-physical"></button></td></tr>\n',
'<tr><td><button class="btn btn-sm btn-warning damage-stun"></button></td><td><button class="btn btn-sm btn-danger damage-physical"></button></td></tr>\n',
'<tr><td><button class="btn btn-sm btn-warning damage-stun"></button></td><td><button class="btn btn-sm btn-danger damage-physical"></button></td></tr>\n',
'<tr><td><button class="btn btn-sm btn-warning damage-stun" title="K.O."><img src="zzz.png" height="16" /></button></td><td><button class="btn btn-sm btn-danger damage-physical" title="dead"><img src="cross.png" height="16"/></button></td></tr>\n',
'<tr><th colspan="2"><button type)"submit" class="btn btn-sm btn-light remove-button" title="Remove combatant" data-bs-toggle="modal" data-bs-target="#confirm-modal"><img src="trash.png" /></button></th></tr>\n',
'<table class="damage-monitor text-center align-middle d-none" data-augmented-ui="tl-scoop bl-clip-y tr-clip-y br-scoop both">\n',
'<tr><td><button class="damage-stun active" title="Light stun damage">L</button></td><td><button class="damage-physical active" title="Light physical damage">L</button></td></tr>\n',
'<tr><td><button class="damage-stun active"></button></td><td><button class="damage-physical active"></button></td></tr>\n',
'<tr><td><button class="damage-stun active" title="Medium stun damage">M</button></td><td><button class="damage-physical active" title="Medium physical damage">M</button></td></tr>\n',
'<tr><td><button class="damage-stun active"></button></td><td><button class="damage-physical active"></button></td></tr>\n',
'<tr><td><button class="damage-stun active"></button></td><td><button class="damage-physical active"></button></td></tr>\n',
'<tr><td><button class="damage-stun active" title="Severe stun damage">S</button></td><td><button class="damage-physical active" title="Severe physical damage">S</button></td></tr>\n',
'<tr><td><button class="damage-stun active"></button></td><td><button class="damage-physical active"></button></td></tr>\n',
'<tr><td><button class="damage-stun active"></button></td><td><button class="damage-physical active"></button></td></tr>\n',
'<tr><td><button class="damage-stun active"></button></td><td><button class="damage-physical active"></button></td></tr>\n',
'<tr><td><button class="damage-stun active" title="K.O."><img src="zzz.png" height="16" /></button></td><td><button class="damage-physical active" title="dead"><img src="cross.png" height="16"/></button></td></tr>\n',
'<tr><th colspan="2"><button type)"submit" class="remove-button" title="Remove combatant" data-bs-toggle="modal" data-bs-target="#confirm-modal"><img src="trash.png" /></button></th></tr>\n',
'</table>'].join("");
const STUN_BADGE_HTML = '<sup><span class="badge bg-warning position-absolute translate-middle stun-badge" title="Stun damage niveau"></span></sup>';
const PHYSICAL_BADGE_HTML = '<sub><span class="badge bg-danger position-absolute translate-middle physical-badge" title="Physical damage niveau"></span></sub>';
const CONTEXTUAL_CLASSES = {
MAX_INI: "table-primary",
ZERO_INI: "table-secondary",
REGULAR_INI: "table-success",
KO_OR_DEAD: "ko-or-dead",
};
/*
* helper functions
*/
@ -55,8 +43,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(".combatant-ini").text()) || 0;
let tmpB = $(b).hasClass(CONTEXTUAL_CLASSES["KO_OR_DEAD"]) ? -1 : parseInt($(b).find(".combatant-ini").text()) || 0;
let tmpA = $(a).hasClass("ko-or-dead") ? -1 : parseInt($(a).find(".combatant-ini").text()) || 0;
let tmpB = $(b).hasClass("ko-or-dead") ? -1 : parseInt($(b).find(".combatant-ini").text()) || 0;
// compare ini
let compIni = tmpB - tmpA;
if (compIni != 0) {
@ -74,7 +62,7 @@ function whoGoesFirst(a, b) {
// returns a combatant's effective ini value (modified by wound penalties)
function getEffectiveIni(tr) {
// return -1 if combatant is K.O. or dead
if ($(tr).hasClass(CONTEXTUAL_CLASSES["KO_OR_DEAD"])) {
if ($(tr).hasClass("ko-or-dead")) {
return -1;
}
// otherwise compute effective ini (true ini minus wound penalties)
@ -140,7 +128,7 @@ function handleEditButtonClick(e) {
$("#combatant-modal-add-ok-button").addClass("d-none");
$("#combatant-modal-edit-ok-button").removeClass("d-none");
// populate modal with values from row
$("#combatant-modal-name").val($tr.find(".combatant-name").attr("data-combatant-name"));
$("#combatant-modal-name").val($tr.find(".combatant-name").text());
$("#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"));
@ -191,7 +179,7 @@ function addCombatant(e) {
$tr.find(".damage-dropdown").append($.parseHTML(DAMAGE_MONITOR_HTML));
// populate table row with values from modal
$tr.attr("data-true-ini", ini);
$tr.find(".combatant-name").attr("data-combatant-name", $("#combatant-modal-name").val().trim());
$tr.find(".combatant-name").text($("#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
@ -213,16 +201,22 @@ function applyDamage(e) {
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();
if ( $btn.hasClass("active") ) {
damageLevel += 1;
}
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.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");
$btn.parent().parent().prevAll().find("button.damage-" + damageType).addClass("active");
$btn.toggleClass("active");
$btn.parent().parent().prevAll().find("button.damage-" + damageType).removeClass("active");
$btn.parent().parent().nextAll().find("button.damage-" + damageType + ":not(.active)").addClass("active");
// resort
/* setTimeout(function(){
sortTable();
},250);*/
sortTable();
}
// edit combatant
@ -244,7 +238,7 @@ function editCombatant(e) {
$tr = $("tr.combatant-row").eq(index);
// set new values
$tr.attr("data-true-ini", ini);
$tr.find(".combatant-name").attr("data-combatant-name", name);
$tr.find(".combatant-name").text(name);
$tr.find(".combatant-dice").attr("data-combatant-dice", dice);
$tr.find(".combatant-rea").attr("data-combatant-rea", rea);
// sort table
@ -283,19 +277,19 @@ 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
$(".combatant-row").removeClass(Object.values(CONTEXTUAL_CLASSES).join(" "));
$(".combatant-row").removeClass("ko-or-dead max-ini zero-ini"); //REGULAR_INI
$(".combatant-row").find(".act-button").prop("disabled", true).attr("aria-disabled", "true");
$(".combatant-ini").empty();
// mark KO or death with class
$(".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"]);
$(this).addClass("ko-or-dead");
}
});
// compute highest effective ini
let iniMax = Math.max.apply(null, $.map($(".combatant-row"), function (tr, i) {
// write current effective ini to table row
$(tr).find(".combatant-ini").text($(tr).hasClass(CONTEXTUAL_CLASSES["KO_OR_DEAD"]) ? 0 : getEffectiveIni($(tr)));
$(tr).find(".combatant-ini").text($(tr).hasClass("ko-or-dead") ? 0 : getEffectiveIni($(tr)));
return $(tr).find(".combatant-ini").text();
}));
// add damage badges and contextual classes
@ -310,26 +304,27 @@ function sortTable() {
$(this).find(".physical-badge").append(DAMAGE_NIVEAU[DAMAGE_PENALTY[$(this).attr("data-damage-physical")]]);
}
// K.O./dead -> don't add anything
if ($(this).hasClass(CONTEXTUAL_CLASSES["KO_OR_DEAD"])) {
if ($(this).hasClass("ko-or-dead")) {
return true;
}
// ini = zero
if (parseInt($(this).find(".combatant-ini").text()) == 0) {
$(this).addClass(CONTEXTUAL_CLASSES["ZERO_INI"]);
$(this).addClass("zero-ini");
return true;
}
// ini = max and non-zero
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");
$(this).addClass("max-ini").find(".act-button").prop("disabled", false).removeAttr("aria-disabled");
return true;
}
// everything else
$(this).addClass(CONTEXTUAL_CLASSES["REGULAR_INI"]);
/* // everything else
$(this).addClass("REGULAR_INI");*/
})
// sort rows and append them in new order
let $rows = $(".combatant-row").toArray().sort(whoGoesFirst);
for (let i = 0; i < $rows.length; i++) {
$("#combatants-table").append($rows[i]);
$($rows[i]).css("z-index", 50-i).css("position", "relative");
}
return;
}