index : btroops | |
Virtual board game-aid for BattleTroops, an infantry combat simulator wargame published by FASA in 1989. |
aboutsummaryrefslogtreecommitdiff |
diff options
author | Catalin Mititiuc <webdevcat@proton.me> | 2024-08-05 15:29:45 -0700 |
---|---|---|
committer | Catalin Mititiuc <webdevcat@proton.me> | 2024-08-05 15:29:58 -0700 |
commit | f35d716bd38d03285fa511f6794af5d93e88f4dc (patch) | |
tree | c9b10a7ff8749a2cdb51a58ba8731e965986e18c | |
parent | 34ed6e2a9c0ef9d4354454394c52e412d8c34d51 (diff) |
WIP: smoothe squad view
-rw-r--r-- | public/assets/css/style.css | 45 | ||||
-rw-r--r-- | public/assets/images/map4.svg | 88 | ||||
-rw-r--r-- | public/index.html | 58 | ||||
-rw-r--r-- | src/index.js | 35 | ||||
-rw-r--r-- | src/modules/gameboard.js | 16 | ||||
-rw-r--r-- | src/modules/record_sheet.js | 95 |
6 files changed, 232 insertions, 105 deletions
diff --git a/public/assets/css/style.css b/public/assets/css/style.css index f3dbc4d..bd90a4e 100644 --- a/public/assets/css/style.css +++ b/public/assets/css/style.css @@ -271,26 +271,35 @@ div#content { overflow-x: hidden; } -.soldier-record { - display: none; -} - -#record-sheet [data-view-squad-number="1"].records .soldier-record[data-squad="1"] { - display: block; -} - -#record-sheet [data-view-squad-number="2"].records .soldier-record[data-squad="2"] { - display: block; +#record-sheet .records [class^="squad-"] { + transition: transform 0.125s; } - -#record-sheet [data-view-squad-number="3"].records .soldier-record[data-squad="3"] { - display: block; -} - -#record-sheet [data-view-squad-number="4"].records .soldier-record[data-squad="4"] { - display: block; -} +#record-sheet .records > :not(:first-child) { + display: none; + transform: translateX(100%); +} + +/*.soldier-record {*/ +/* display: none;*/ +/*}*/ + +/*#record-sheet [data-view-squad-number="1"].records .soldier-record[data-squad="1"] {*/ +/* display: block;*/ +/*}*/ +/**/ +/*#record-sheet [data-view-squad-number="2"].records .soldier-record[data-squad="2"] {*/ +/* display: block;*/ +/*}*/ +/**/ +/**/ +/*#record-sheet [data-view-squad-number="3"].records .soldier-record[data-squad="3"] {*/ +/* display: block;*/ +/*}*/ +/**/ +/*#record-sheet [data-view-squad-number="4"].records .soldier-record[data-squad="4"] {*/ +/* display: block;*/ +/*}*/ #record-sheet .name { text-transform: capitalize; diff --git a/public/assets/images/map4.svg b/public/assets/images/map4.svg index 2603ed5..0113404 100644 --- a/public/assets/images/map4.svg +++ b/public/assets/images/map4.svg @@ -4,7 +4,6 @@ <g id="building1" class="building"> <g class="footprint"> <g data-q="0" data-r="0" data-s="0" data-left="1" data-top="3" data-right="2" data-bottom="4" data-offset="left" /> - <g data-q="0" data-r="0" data-s="0" data-left="1" data-top="3" data-right="4" data-bottom="4" data-offset="left" /> </g> <g class="structure"> @@ -25,7 +24,7 @@ <rect id="cabinet" x="-2.5" y="-4.75" width="5" height="9.5" /> <g id="mapsheet" class="mapsheet"> - <g class="building1" data-q="1" data-r="-2" data-s="1"> + <g class="building1" data-q="3" data-r="-5" data-s="2"> <use href="#building1"/> <g class="furniture"> <use href="#couch" transform="rotate(90,10.34,-27.66)" /> @@ -44,7 +43,6 @@ </g> <use href="counters.svg#mech-template"/> <use href="counters.svg#fallen-mech-template"/> - <use href="counters.svg#standing-mech-template"/> <use href="counters.svg#vehicle-template"/> </defs> @@ -57,22 +55,82 @@ <g class="start-locations" data-attacker-name="liao" data-defender-name="davion"> <g data-edge="north" style="--i: -2"> - <g data-x="13"> - <g class="counter" data-allegiance="attacker" data-number="1"> - <use class="primary-weapon" href="counters.svg#blazer"/> - <use class="troop-number" href="counters.svg#number-1"/> - <use class="squad-number" href="counters.svg#number-1"/> - </g> + <g class="counter" data-allegiance="attacker" data-number="1" data-squad="1"> + <use class="primary-weapon" href="counters.svg#blazer"/> + <use class="troop-number" href="counters.svg#number-1"/> + <use class="squad-number" href="counters.svg#number-1"/> + </g> + <g class="counter" data-allegiance="attacker" data-number="2" data-squad="1"> + <use class="primary-weapon" href="counters.svg#hsplaser"/> + <use class="troop-number" href="counters.svg#number-2"/> + <use class="squad-number" href="counters.svg#number-1"/> + </g> + + <g class="counter" data-allegiance="attacker" data-number="1" data-squad="2"> + <use class="primary-weapon" href="counters.svg#smg"/> + <use class="troop-number" href="counters.svg#number-1"/> + <use class="squad-number" href="counters.svg#number-2"/> + </g> + <g class="counter" data-allegiance="attacker" data-number="2" data-squad="2"> + <use class="primary-weapon" href="counters.svg#smg"/> + <use class="troop-number" href="counters.svg#number-2"/> + <use class="squad-number" href="counters.svg#number-2"/> + </g> + <g class="counter" data-allegiance="attacker" data-number="3" data-squad="2"> + <use class="primary-weapon" href="counters.svg#smg"/> + <use class="troop-number" href="counters.svg#number-3"/> + <use class="squad-number" href="counters.svg#number-2"/> + </g> + + <g class="counter" data-allegiance="attacker" data-number="1" data-squad="3"> + <use class="primary-weapon" href="counters.svg#rifle"/> + <use class="troop-number" href="counters.svg#number-1"/> + <use class="squad-number" href="counters.svg#number-3"/> + </g> + + <g class="counter" data-allegiance="attacker" data-number="1" data-squad="4"> + <use class="primary-weapon" href="counters.svg#rifle"/> + <use class="troop-number" href="counters.svg#number-1"/> + <use class="squad-number" href="counters.svg#number-4"/> + </g> + <g class="counter" data-allegiance="attacker" data-number="2" data-squad="4"> + <use class="primary-weapon" href="counters.svg#rifle"/> + <use class="troop-number" href="counters.svg#number-2"/> + <use class="squad-number" href="counters.svg#number-4"/> + </g> + + <g class="counter" data-allegiance="attacker" data-number="1" data-squad="5"> + <use class="primary-weapon" href="counters.svg#rifle"/> + <use class="troop-number" href="counters.svg#number-1"/> + <use class="squad-number" href="counters.svg#number-5"/> + </g> + <g class="counter" data-allegiance="attacker" data-number="2" data-squad="5"> + <use class="primary-weapon" href="counters.svg#rifle"/> + <use class="troop-number" href="counters.svg#number-2"/> + <use class="squad-number" href="counters.svg#number-5"/> </g> </g> <g data-edge="south" style="--i: 53"> - <g data-x="13"> - <g class="counter" data-allegiance="defender" data-number="1"> - <use class="primary-weapon" href="counters.svg#blazer"/> - <use class="troop-number" href="counters.svg#number-1"/> - <use class="squad-number" href="counters.svg#number-1"/> - </g> + <g class="counter" data-allegiance="defender" data-number="1" data-squad="1"> + <use class="primary-weapon" href="counters.svg#blazer"/> + <use class="troop-number" href="counters.svg#number-1"/> + <use class="squad-number" href="counters.svg#number-1"/> + </g> + <!--<g class="counter" data-allegiance="defender" data-number="2" data-squad="1">--> + <!-- <use class="primary-weapon" href="counters.svg#hsplaser"/>--> + <!-- <use class="troop-number" href="counters.svg#number-2"/>--> + <!-- <use class="squad-number" href="counters.svg#number-1"/>--> + <!--</g>--> + <g class="counter" data-allegiance="defender" data-number="1" data-squad="2"> + <use class="primary-weapon" href="counters.svg#smg"/> + <use class="troop-number" href="counters.svg#number-1"/> + <use class="squad-number" href="counters.svg#number-2"/> + </g> + <g class="counter" data-allegiance="defender" data-number="2" data-squad="2"> + <use class="primary-weapon" href="counters.svg#smg"/> + <use class="troop-number" href="counters.svg#number-2"/> + <use class="squad-number" href="counters.svg#number-2"/> </g> </g> </g> diff --git a/public/index.html b/public/index.html index 51c4fcf..c3a1c75 100644 --- a/public/index.html +++ b/public/index.html @@ -263,7 +263,7 @@ <div id="record-sheet"> <div id="attacker-record" data-allegiance="attacker"> <!-- <img class="logo" src="logo-davion.png" /> --> - <div> + <div class="records-header"> <div><strong class="name">Attacker</strong></div> <div><button type="button" class="end-move" data-allegiance="attacker"> End Movement @@ -271,30 +271,14 @@ <div><button type="button" class="end-turn" data-allegiance="defender"> End Turn </button></div> - <div class="squad-number selected" data-number="1"> + <div><button type="button" class="view-squad" value="previous"><</button></div> + <div class="squad-number"> <svg viewBox="-5 -5 10 10" xmlns="http://www.w3.org/2000/svg"> <circle id="counter-base" r="5" cx="0" cy="0"></circle> <text>1</text> </svg> </div> - <div class="squad-number" data-number="2"> - <svg viewBox="-5 -5 10 10" xmlns="http://www.w3.org/2000/svg"> - <circle id="counter-base" r="5" cx="0" cy="0"></circle> - <text>2</text> - </svg> - </div> - <div class="squad-number" data-number="3"> - <svg viewBox="-5 -5 10 10" xmlns="http://www.w3.org/2000/svg"> - <circle id="counter-base" r="5" cx="0" cy="0"></circle> - <text>3</text> - </svg> - </div> - <div class="squad-number" data-number="4"> - <svg viewBox="-5 -5 10 10" xmlns="http://www.w3.org/2000/svg"> - <circle id="counter-base" r="5" cx="0" cy="0"></circle> - <text>4</text> - </svg> - </div> + <div><button type="button" class="view-squad" value="next">></button></div> <!-- 1st Squad, 3rd Platoon, Bravo Company, 2nd Battalion<br> 17th Kestral Mechanized Infantry --> </div> @@ -302,7 +286,7 @@ </div> <div id="defender-record" data-allegiance="defender"> <!-- <img class="logo" src="logo-liao.png" /> --> - <div> + <div class="records-header"> <div><strong class="name">Defender</strong></div> <div><button type="button" class="end-move" data-allegiance="defender"> End Movement @@ -310,30 +294,16 @@ <div><button type="button" class="end-turn" data-allegiance="attacker"> End Turn </button></div> - <div class="squad-number selected" data-number="1"> - <svg viewBox="-5 -5 10 10" xmlns="http://www.w3.org/2000/svg"> - <circle id="counter-base" r="5" cx="0" cy="0"></circle> - <text>1</text> - </svg> - </div> - <div class="squad-number" data-number="2"> - <svg viewBox="-5 -5 10 10" xmlns="http://www.w3.org/2000/svg"> - <circle id="counter-base" r="5" cx="0" cy="0"></circle> - <text>2</text> - </svg> - </div> - <div class="squad-number" data-number="3"> - <svg viewBox="-5 -5 10 10" xmlns="http://www.w3.org/2000/svg"> - <circle id="counter-base" r="5" cx="0" cy="0"></circle> - <text>3</text> - </svg> - </div> - <div class="squad-number" data-number="4"> - <svg viewBox="-5 -5 10 10" xmlns="http://www.w3.org/2000/svg"> - <circle id="counter-base" r="5" cx="0" cy="0"></circle> - <text>4</text> - </svg> + <div><button type="button" class="view-squad" value="previous"><</button></div> + <div> + <div class="squad-number"> + <svg viewBox="-5 -5 10 10" xmlns="http://www.w3.org/2000/svg"> + <circle id="counter-base" r="5" cx="0" cy="0"></circle> + <text>1</text> + </svg> + </div> </div> + <div><button type="button" class="view-squad" value="next">></button></div> <!-- 2nd Squad, 1st Platoon, 3rd Company, 2nd Battalion<br> Aldebaran Home Guard --> </div> diff --git a/src/index.js b/src/index.js index e6a8802..62c1128 100644 --- a/src/index.js +++ b/src/index.js @@ -279,6 +279,41 @@ document.querySelectorAll('#record-sheet [data-allegiance]').forEach(el => { ); }); +document.querySelectorAll('.view-squad').forEach(b => b.addEventListener('click', e => { + const currentSquad = b.closest('.records-header').querySelector('.squad-number text'); + const currentSquadContainer = b.closest('[id$="-record"]').querySelector(`.records > .squad-${currentSquad.textContent}`); + + if (b.value === 'next') { + //const toSquad = currentSquadContainer.nextElementSibling || b.closest('[id$="-record"]').querySelector('.records > :first-child'); + const toSquad = currentSquadContainer.nextElementSibling; + if (!toSquad) return; + currentSquad.textContent = +toSquad.className.match(/\d+/); + + currentSquadContainer.addEventListener('transitionend', e => { + console.log('transitionend', 'current', currentSquadContainer, 'next', toSquad); + currentSquadContainer.style.display = 'none'; + toSquad.style.display = 'block'; + b.closest('[id$="-record"]').querySelector('.records').scrollTo(0, 0); + toSquad.style.transform = 'translateX(0)'; + }, { once: true }); + + currentSquadContainer.style.transform = 'translateX(-100%)'; + } else { + const toSquad = currentSquadContainer.previousElementSibling; + if (!toSquad) return; + currentSquad.textContent = +toSquad.className.match(/\d+/); + + currentSquadContainer.addEventListener('transitionend', e => { + currentSquadContainer.style.display = 'none'; + toSquad.style.display = 'block'; + b.closest('[id$="-record"]').querySelector('.records').scrollTo(0, 0); + toSquad.style.transform = 'translateX(0)'; + }, { once: true }); + + currentSquadContainer.style.transform = 'translateX(100%)'; + } +})); + contentVisToggleEl.addEventListener('input', toggleContentVis); contentVisToggleEl.checked = (localStorage.getItem('content-visibility') !== 'false'); toggleContentVis(); diff --git a/src/modules/gameboard.js b/src/modules/gameboard.js index 74e5582..8cb6af1 100644 --- a/src/modules/gameboard.js +++ b/src/modules/gameboard.js @@ -378,10 +378,18 @@ export function start(el) { //frontmost.append(trooper2); //cell2.classList.add('hover'); // - //const cell = getCell(0, 0, 0, 0); - //const attacker = { dataset: { allegiance: 'attacker', number: 1, squad: 1 }}; - //const trooper = soldier.createCounter(attacker, 'blazer'); - //soldier.place(svg, trooper2, cell2); + + soldier.place( + svg, + soldier.createCounter({ dataset: { allegiance: 'attacker', number: 2, squad: 1 }}, 'hsplaser'), + getCell(-2, 3, -1, 0) + ); + + soldier.place( + svg, + soldier.createCounter({ dataset: { allegiance: 'attacker', number: 2, squad: 5 }}, 'rifle'), + getCell(-3, 3, 0, 0) + ); // Add some counters in an unoccupied cell //const countersCell = getCell(-1, 1, 0, 0); diff --git a/src/modules/record_sheet.js b/src/modules/record_sheet.js index 01a9051..824668e 100644 --- a/src/modules/record_sheet.js +++ b/src/modules/record_sheet.js @@ -38,12 +38,6 @@ const weapons = { shortRange: '1-44', longRange: '45-108' }, - lmg: { - name: 'Light MG', - damage: '5L', - shortRange: '1-30', - longRange: '31-84' - }, gl: { name: 'SMG w/Grenade Launcher', damage: '4/2/1 L', @@ -52,8 +46,6 @@ const weapons = { } } -const cacheBuster = Array(20).fill(null).map(() => getRandomIntInclusive(0, 9)).join(''); - function createIcon(number) { const [icon, use, text] = ['svg', 'use', 'text'].map(t => document.createElementNS(svgns, t)); @@ -172,16 +164,18 @@ function createRecord(unit) { } function createRecords(units) { - const grouped = Array.from(units).reduce((acc, unit) => { - acc[unit.dataset.allegiance]?.push(unit) || (acc[unit.dataset.allegiance] = [unit]); - return acc; - }, {}); + return Array.from(units).reduce((acc, unit) => { + const record = createRecord(unit), + { allegiance, squad } = unit.dataset; - for (const al in grouped) { - grouped[al] = grouped[al].map(createRecord); - } + if (acc[allegiance]) { + acc[allegiance][squad]?.push(record) || (acc[allegiance][squad] = [record]) + } else { + acc[allegiance] = { [squad]: [record] } + } - return grouped; + return acc; + }, {}); } function getRecord({ dataset: { allegiance: al, number: n, squad: s }}) { @@ -199,7 +193,12 @@ function deselect() { } function clear() { - document.querySelectorAll('#record-sheet .soldier-record').forEach(el => el.remove()); + document.querySelectorAll('#record-sheet > *').forEach(el => { + //el.querySelectorAll('.squad-number').forEach(sn => sn.remove()); + const records = el.querySelector('.records'); + records.dataset.viewSquadNumber = 1; + [...records.children].forEach(c => c.remove()); + }); //document.querySelector('#attacker-record .name').textContent = 'attacker'; //document.querySelector('#defender-record .name').textContent = 'defender'; } @@ -212,8 +211,52 @@ function select(data) { if (isSelected || !data) return; - record.classList.add('selected'); - record.scrollIntoView({ behavior: 'smooth' }); + const currentSquadView = document.querySelector(`#record-sheet #${record.dataset.allegiance}-record .records-header .squad-number text`); + const records = document.querySelector(`#record-sheet #${record.dataset.allegiance}-record .records`); + const target = records.querySelector(`.squad-${record.dataset.squad}`); + const currentSquad = records.querySelector(`.squad-${currentSquadView.textContent}`); + + let direction; + let next = prev = currentSquad; + + while (!direction && (next || prev)) { + next = next?.nextElementSibling; + prev = prev?.previousElementSibling; + if (next === target) direction = 'next'; + else if (prev === target) direction = 'previous'; + } + + function showSquad(current, target, direction) { + current.addEventListener('transitionend', e => { + const toSquad = current[`${direction}ElementSibling`]; + currentSquadView.textContent = +toSquad.className.match(/\d+/); + current.style.display = 'none'; + + // There needs to be a delay between making it visible and the + // transformation. ScrollTo seems to create enough delay. + toSquad.style.display = 'block'; + records.scrollTo(0, 0); + if (toSquad[`${direction}ElementSibling`] && toSquad !== target) { + showSquad(toSquad, target, direction); + } else { + toSquad.style.transform = 'translateX(0)'; + + toSquad.addEventListener('transitionend', e => { + record.classList.add('selected'); + record.scrollIntoView({ behavior: 'smooth' }); + }, { once: true }); + } + }, { once: true }); + + current.style.transform = `translateX(${direction === 'next' ? '-' : ''}100%)`; + } + + if (currentSquad !== target) + showSquad(currentSquad, target, direction); + else { + record.classList.add('selected'); + record.scrollIntoView({ behavior: 'smooth' }); + } } function endMove() { @@ -254,12 +297,16 @@ export function start(startLoc, units) { for (const affiliation in forces) { const container = document.querySelector(`#${affiliation}-record`); const records = container.querySelector('.records'); - // const name = startLoc?.dataset[`${affiliation}Name`]; + const viewSquadIndicator = container.querySelector('.squad-number svg text'); + + for (const squadNumber in forces[affiliation]) { + const squadContainer = document.createElement('div'); + squadContainer.classList.add(`squad-${squadNumber}`); + forces[affiliation][squadNumber].forEach(r => squadContainer.append(r)); + records.append(squadContainer); + } - // if (name) { - // container.querySelector('.name').textContent = name; - // } - forces[affiliation].forEach(r => records.appendChild(r)); + viewSquadIndicator.textContent = Object.keys(forces[affiliation])[0]; } document.querySelectorAll('.soldier-record').forEach(el => |