- 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 { 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 { span.navbar-brand {
position: absolute; // position: absolute;
right: 110px; // 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; // 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 { div.container {
--aug-all-width: "42px"; position: relative;
} }
input:invalid { #combatants-table {
border: 2px solid red; 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 { .ko-or-dead {
@ -22,61 +193,93 @@ input:invalid {
background-color: darkslategray; background-color: darkslategray;
} }
.table-primary { .max-ini {
font-weight: bold; font-weight: bold;
} }
.combatant-name::after { .zero-ini td {
content: attr(data-combatant-name); color: lightgray !important;
}
.combatant-dice::after {
content: attr(data-combatant-dice);
}
.combatant-rea::after {
content: attr(data-combatant-rea);
} }
.badge.bg-warning { .badge.bg-warning {
background: radial-gradient(circle at center, cyan, darkcyan);
left: 12px; left: 12px;
bottom: -4px; bottom: -4px;
width: 20px; width: 20px;
} }
.badge.bg-danger { .badge.bg-danger {
background: radial-gradient(circle at center, deeppink, maroon);
left: 12px; left: 12px;
top: 12px; top: 12px;
width: 20px; width: 20px;
} }
.damage-dropdown {
display: inline-block;
}
.damage-monitor { .damage-monitor {
position: absolute; position: absolute;
z-index: 20; z-index: 200;
top: 100%;
right: -8px;
width: 60px;
th { top: calc(100% - 2px);
width: 28px; right: 10px;
height: 28px;
padding: 3px;
}
td { @include aug;
width: 30px;
height: 24px; --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 { button {
font-size: smaller; font-size: smaller;
width: 30px; width: 24px;
height: 24px; height: 24px;
padding: 2px;
border: none;
margin: 0px 2px;
border-radius: none;
} }
button.active { .damage-stun {
filter: brightness(88%); 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 { 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 { footer {
z-index: -10 !important; z-index: -10 !important;
@ -98,3 +328,83 @@ footer {
margin: .5rem 0; 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 --> <!-- 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://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="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 --> <!-- javascript files -->
<script type="module" src="https://cdn.jsdelivr.net/npm/bootstrap@latest/dist/js/bootstrap.min.js"></script> <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="https://code.jquery.com/jquery-latest.min.js"></script>
<script type="module" src="js/sr2ini.js" type="text/javascript"></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> </head>
<body> <body>
<div class="container"> <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> <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="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="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="new-round-button" data-bs-toggle="modal" data-bs-target="#confirm-modal" title="Start new round"><img src="img/newround.png"></button>
</nav> </nav>
</header> </header>
<div class="table-responsive overflow-visible"> <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> <thead>
<tr> <tr data-augmented-ui="tl-2-clip-y r-clip-y">
<th class="col-4 text-start" title="Name">Name</th> <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" title="Initiative">Ini</th> <th class="col-2 text-center th-ini" data-augmented-ui="both" 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-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-4 text-center" title="Actions">Actions</th> <th class="col-3 text-center th-actions" data-augmented-ui="r-clip-y both" title="Actions">Actions</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -74,15 +79,15 @@
<div class="modal-body"> <div class="modal-body">
<form id="combatant-form" name="combatant-modal-form" class="was-validated" onsubmit="return false;"> <form id="combatant-form" name="combatant-modal-form" class="was-validated" onsubmit="return false;">
<div class="my-2"> <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>
<div class="input-group input-group-sm my-2"> <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> <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>
<div class="my-2"> <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> <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> </div>
</form> </form>
@ -98,9 +103,9 @@
<div class="container"> <div class="container">
<footer class="footer fixed-bottom bg-light"> <footer class="footer fixed-bottom bg-light">
<hr /> <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> <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> 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> </footer>
</div> </div>

View File

@ -4,46 +4,34 @@
const DAMAGE_PENALTY = [0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4]; const DAMAGE_PENALTY = [0, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4];
const DAMAGE_NIVEAU = ["", "L", "M", "S", "D"]; const DAMAGE_NIVEAU = ["", "L", "M", "S", "D"];
const COMBATANT_TABLE_ROW = [ const COMBATANT_TABLE_ROW = [
'<tr class="combatant-row align-middle" data-true-ini="">\n', //TODO: add data-damage-* attributes with initial damage levels '<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"></td>\n', '<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 text-center" title="Effective initiative (w/ wound penalties)" data-bs-toggle="modal" data-bs-target="#combatant-modal"></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"><span class="combatant-dice"></span>D+<span class="combatant-rea"></span></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="text-end">\n', '<td class="combatant-actions text-end" data-augmented-ui="tr-clip-y br-scoop both">\n',
'<div class="btn-group">\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="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="act-button" title="Act and reduce ini by 10"><img src="002-act.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',
'<div class="damage-dropdown">\n', '<button type="button" class="damage-button" title="Take damage" ><img src="003-damage.png"/></button>\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',
'</div>\n', '</div>\n',
'</td>\n', '</td>\n',
'</tr>'].join(""); '</tr>'].join("");
const DAMAGE_MONITOR_HTML = [ const DAMAGE_MONITOR_HTML = [
'<table class="bg-light damage-monitor text-center align-middle d-none">\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>\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',
'<td><button class="btn btn-sm btn-warning damage-stun active" title="+/-0">0</button></td>\n', '<tr><td><button class="damage-stun active"></button></td><td><button class="damage-physical active"></button></td></tr>\n',
'<td><button class="btn btn-sm btn-danger damage-physical active" title="+/-0">0</button></td>\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>\n', '<tr><td><button class="damage-stun active"></button></td><td><button class="damage-physical active"></button></td></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="damage-stun active"></button></td><td><button class="damage-physical active"></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="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="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="damage-stun active"></button></td><td><button class="damage-physical active"></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="damage-stun active"></button></td><td><button class="damage-physical active"></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="damage-stun active"></button></td><td><button class="damage-physical active"></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="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><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><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',
'<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>'].join(""); '</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 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 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 * helper functions
*/ */
@ -55,8 +43,8 @@ function rollForInitiative(dice, rea) {
// figure out whose action comes first out of two combatants a and b // figure out whose action comes first out of two combatants a and b
function whoGoesFirst(a, b) { function whoGoesFirst(a, b) {
// check for K.O./death // check for K.O./death
let tmpA = $(a).hasClass(CONTEXTUAL_CLASSES["KO_OR_DEAD"]) ? -1 : parseInt($(a).find(".combatant-ini").text()) || 0; let tmpA = $(a).hasClass("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 tmpB = $(b).hasClass("ko-or-dead") ? -1 : parseInt($(b).find(".combatant-ini").text()) || 0;
// compare ini // compare ini
let compIni = tmpB - tmpA; let compIni = tmpB - tmpA;
if (compIni != 0) { if (compIni != 0) {
@ -74,7 +62,7 @@ function whoGoesFirst(a, b) {
// returns a combatant's effective ini value (modified by wound penalties) // returns a combatant's effective ini value (modified by wound penalties)
function getEffectiveIni(tr) { function getEffectiveIni(tr) {
// return -1 if combatant is K.O. or dead // 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; return -1;
} }
// otherwise compute effective ini (true ini minus wound penalties) // 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-add-ok-button").addClass("d-none");
$("#combatant-modal-edit-ok-button").removeClass("d-none"); $("#combatant-modal-edit-ok-button").removeClass("d-none");
// populate modal with values from row // 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-dice").val($tr.find(".combatant-dice").attr("data-combatant-dice"));
$("#combatant-modal-rea").val($tr.find(".combatant-rea").attr("data-combatant-rea")); $("#combatant-modal-rea").val($tr.find(".combatant-rea").attr("data-combatant-rea"));
$("#combatant-modal-ini").val($tr.attr("data-true-ini")); $("#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)); $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.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-dice").attr("data-combatant-dice", $("#combatant-modal-dice").val().trim());
$tr.find(".combatant-rea").attr("data-combatant-rea", $("#combatant-modal-rea").val().trim()); $tr.find(".combatant-rea").attr("data-combatant-rea", $("#combatant-modal-rea").val().trim());
//TODO: retrieve initial damage levels //TODO: retrieve initial damage levels
@ -213,16 +201,22 @@ function applyDamage(e) {
let $btn = $(e.target).is("button") ? $(e.target) : $(e.target).parent(); let $btn = $(e.target).is("button") ? $(e.target) : $(e.target).parent();
// retrieve new damage level and type from button position and "damage-[type]" class // retrieve new damage level and type from button position and "damage-[type]" class
let damageLevel = $btn.parent().parent().index(); let damageLevel = $btn.parent().parent().index();
if ( $btn.hasClass("active") ) {
damageLevel += 1;
}
let damageType = $btn.attr("class").split(" ").filter(function (cls) { let damageType = $btn.attr("class").split(" ").filter(function (cls) {
return cls.substr(0, 7) == "damage-" ? cls : false; return cls.substr(0, 7) == "damage-" ? cls : false;
}).toString().substr(7); }).toString().substr(7);
// add damage level to table row as as data attribute // add damage level to table row as as data attribute
$btn.parents("tr.combatant-row").attr("data-damage-" + damageType, damageLevel); $btn.parents("tr.combatant-row").attr("data-damage-" + damageType, damageLevel);
// select/unselect damage buttons above/below // select/unselect damage buttons above/below
$btn.addClass("active"); $btn.toggleClass("active");
$btn.parent().parent().nextAll().find("button.damage-" + damageType).removeClass("active"); $btn.parent().parent().prevAll().find("button.damage-" + damageType).removeClass("active");
$btn.parent().parent().prevAll().find("button.damage-" + damageType).addClass("active"); $btn.parent().parent().nextAll().find("button.damage-" + damageType + ":not(.active)").addClass("active");
// resort // resort
/* setTimeout(function(){
sortTable();
},250);*/
sortTable(); sortTable();
} }
// edit combatant // edit combatant
@ -244,7 +238,7 @@ function editCombatant(e) {
$tr = $("tr.combatant-row").eq(index); $tr = $("tr.combatant-row").eq(index);
// set new values // set new values
$tr.attr("data-true-ini", ini); $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-dice").attr("data-combatant-dice", dice);
$tr.find(".combatant-rea").attr("data-combatant-rea", rea); $tr.find(".combatant-rea").attr("data-combatant-rea", rea);
// sort table // sort table
@ -283,19 +277,19 @@ function startNewRound(e) {
// add contextual classes and sort combatants by ini value // add contextual classes and sort combatants by ini value
function sortTable() { function sortTable() {
// do some clean up: remove previous classes from rows, disable act buttons, remove effective ini and damage badges // 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-row").find(".act-button").prop("disabled", true).attr("aria-disabled", "true");
$(".combatant-ini").empty(); $(".combatant-ini").empty();
// mark KO or death with class // mark KO or death with class
$(".combatant-row").each(function () { $(".combatant-row").each(function () {
if (parseInt($(this).attr("data-damage-stun")) == 10 || parseInt($(this).attr("data-damage-physical")) == 10) { 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 // compute highest effective ini
let iniMax = Math.max.apply(null, $.map($(".combatant-row"), function (tr, i) { let iniMax = Math.max.apply(null, $.map($(".combatant-row"), function (tr, i) {
// write current effective ini to table row // 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(); return $(tr).find(".combatant-ini").text();
})); }));
// add damage badges and contextual classes // 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")]]); $(this).find(".physical-badge").append(DAMAGE_NIVEAU[DAMAGE_PENALTY[$(this).attr("data-damage-physical")]]);
} }
// K.O./dead -> don't add anything // K.O./dead -> don't add anything
if ($(this).hasClass(CONTEXTUAL_CLASSES["KO_OR_DEAD"])) { if ($(this).hasClass("ko-or-dead")) {
return true; return true;
} }
// ini = zero // ini = zero
if (parseInt($(this).find(".combatant-ini").text()) == 0) { if (parseInt($(this).find(".combatant-ini").text()) == 0) {
$(this).addClass(CONTEXTUAL_CLASSES["ZERO_INI"]); $(this).addClass("zero-ini");
return true; return true;
} }
// ini = max and non-zero // ini = max and non-zero
if (parseInt($(this).find(".combatant-ini").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"); $(this).addClass("max-ini").find(".act-button").prop("disabled", false).removeAttr("aria-disabled");
return true; return true;
} }
// everything else /* // everything else
$(this).addClass(CONTEXTUAL_CLASSES["REGULAR_INI"]); $(this).addClass("REGULAR_INI");*/
}) })
// sort rows and append them in new order // sort rows and append them in new order
let $rows = $(".combatant-row").toArray().sort(whoGoesFirst); let $rows = $(".combatant-row").toArray().sort(whoGoesFirst);
for (let i = 0; i < $rows.length; i++) { for (let i = 0; i < $rows.length; i++) {
$("#combatants-table").append($rows[i]); $("#combatants-table").append($rows[i]);
$($rows[i]).css("z-index", 50-i).css("position", "relative");
} }
return; return;
} }