Skip to content

Commit ffd4b5e

Browse files
committed
Add not-so-basic troop selector
This was... painful
1 parent ca4cacf commit ffd4b5e

4 files changed

Lines changed: 234 additions & 4 deletions

File tree

resources/base.css

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,170 @@ input:checked + .slider {
743743
border-color: var(--color-primary-medium);
744744
}
745745

746+
.selector-stats-container {
747+
width: 100%;
748+
display: flex;
749+
flex-flow: row;
750+
direction: rtl;
751+
--local-color-light: var(--color-primary-medium);
752+
--local-color: var(--color-primary-dark);
753+
--local-color-medium: var(--color-primary);
754+
--cutoff: 2em;
755+
}
756+
757+
.selector-stats-container::before {
758+
content: "";
759+
position: absolute;
760+
width: calc(100% + 40px);
761+
height: 15px;
762+
margin-right: -26px;
763+
margin-top: calc(1.5em - 4px);
764+
border-top: 2px solid var(--local-color-light);
765+
border-right: 2px solid var(--local-color-light);
766+
box-shadow: 1px -1px var(--local-color);
767+
background-color: var(--local-color-medium);
768+
}
769+
770+
.selector-stat {
771+
font-size: 0.8em;
772+
position: relative;
773+
color: var(--color-light);
774+
border-color: var(--local-color-medium);
775+
--local-color: var(--local-color);
776+
background-color: var(--local-color-dark);
777+
--border-thickness: 1px;
778+
direction: ltr;
779+
z-index: -1;
780+
}
781+
782+
.selector-stat::before {
783+
content: "";
784+
position: absolute;
785+
height: 0;
786+
width: 240%;
787+
padding-bottom: 240%;
788+
margin-left: -70%;
789+
transform: rotate(45deg);
790+
clip-path: polygon(-1px calc(29.3% - var(--border-thickness) - 6px), -1px calc(29.3% + 1.8em), 5px calc(29.3% + 2em), calc(29.3% + 1.3em) 5px, calc(29.3% + var(--cutoff)) -1px, calc(29.3% - var(--border-thickness) - 6px) -1px);
791+
border-top: var(--border-thickness) solid;
792+
border-left: var(--border-thickness) solid;
793+
border-color: inherit;
794+
box-shadow: -1px -1px var(--local-color);
795+
background: inherit;
796+
left: 0;
797+
z-index: -1;
798+
}
799+
800+
.selector-stat::after {
801+
content: "";
802+
position: absolute;
803+
width: calc(99.9% + var(--border-thickness) - 13px);
804+
margin-left: calc(0.2% + 6px);
805+
margin-top: calc(var(--border-thickness) - 7px);
806+
border-top: var(--border-thickness) solid;
807+
border-color: inherit;
808+
box-shadow: 0 -1px var(--local-color);
809+
left: 0;
810+
z-index: -1;
811+
}
812+
813+
.selector-stat:last-of-type {
814+
font-size: 1.5em;
815+
border-color: var(--color-primary-medium);
816+
--local-color: var(--color-primary-dark);
817+
background-color: var(--color-primary);
818+
--border-thickness: 2px;
819+
--cutoff: 1.1em;
820+
z-index: 0;
821+
}
822+
823+
.selector-stat:not(:last-of-type) {
824+
padding-left: 30px;
825+
padding-right: 5px;
826+
margin-top: 4px;
827+
line-height: 12px;
828+
margin-left: -15px;
829+
}
830+
831+
.selector-stat:nth-last-child(2) {
832+
padding-left: 33px;
833+
}
834+
835+
.selector-stat:first-of-type {
836+
margin-right: auto;
837+
}
838+
839+
.selector-slider {
840+
position: relative;
841+
display: inline-block;
842+
width: 500px;
843+
height: 30px;
844+
margin-left: -30px;
845+
margin-right: -52px;
846+
border-left: 2px solid var(--color-primary-medium);
847+
border-right: 2px solid var(--color-primary-medium);
848+
box-shadow: -1px 0 var(--color-primary-dark), 1px 0 var(--color-primary-dark), 0 0 1px 2px var(--color-primary-dark) inset;
849+
background-color: var(--panel-contrast);
850+
--value: 0.5;
851+
--local-color: var(--color-secondary-dark);
852+
pointer-events: all;
853+
cursor: pointer;
854+
}
855+
856+
.selector-slider::before {
857+
content: "";
858+
position: absolute;
859+
height: calc(100% - 4px);
860+
width: calc(var(--value) * 100% - 3px);
861+
left: 1px;
862+
top: 2px;
863+
background-color: var(--color-secondary-medium);
864+
border: 2px solid var(--color-secondary);
865+
border-right: none;
866+
box-shadow: 2px 0 var(--local-color);
867+
z-index: 1;
868+
}
869+
870+
.selector-slider::after {
871+
content: "";
872+
position: absolute;
873+
width: 16px;
874+
height: 16px;
875+
left: calc(var(--value) * 100% - 9px);
876+
margin-top: -16px;
877+
transform: rotate(45deg);
878+
background-color: var(--local-color);
879+
clip-path: polygon(70% 20%, 100% 50%, 100% 100%, 50% 100%, 20% 70%);
880+
}
881+
882+
.selector-slider:has(input:focus) {
883+
--local-color: var(--color-secondary);
884+
}
885+
886+
.selector-slider:has(input:focus)::before {
887+
background-color: var(--color-secondary-light);
888+
}
889+
890+
.selector-number {
891+
font-size: 0.8em;
892+
position: absolute;
893+
width: 40px;
894+
margin-top: 7px;
895+
margin-left: 5px;
896+
left: calc(var(--value) * 100%);
897+
}
898+
899+
.selector-number-reversed {
900+
margin-left: -46px;
901+
z-index: 1;
902+
}
903+
904+
.selector-slider > input {
905+
opacity: 0;
906+
width: 0;
907+
height: 0;
908+
}
909+
746910
/* Layout elements */
747911

748912
.grid {

src/game/action/MapActionHandler.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,13 @@ import {hasBorderWith, preprocessAttack} from "../attack/AttackActionValidator";
1212
*/
1313
class MapActionHandler implements ClickEventListener {
1414
private action: (tile: number) => void;
15+
private power: number;
1516

1617
/**
1718
* Enables the map action handler.
1819
*/
1920
enable() {
20-
this.setAction(tile => territoryManager.getOwner(tile) !== territoryManager.OWNER_NONE - 1 && (spawnManager.isSelecting ? spawnManager.requestSpawn(tile) : hasBorderWith(clientPlayer, territoryManager.getOwner(tile)) ? preprocessAttack(clientPlayer.id, territoryManager.getOwner(tile), 200) : boatManager.requestBoat(tile, 200)));
21+
this.setAction(tile => territoryManager.getOwner(tile) !== territoryManager.OWNER_NONE - 1 && (spawnManager.isSelecting ? spawnManager.requestSpawn(tile) : hasBorderWith(clientPlayer, territoryManager.getOwner(tile)) ? preprocessAttack(clientPlayer.id, territoryManager.getOwner(tile), this.power) : boatManager.requestBoat(tile, this.power)));
2122
interactionManager.click.register(this, -100);
2223
}
2324

@@ -36,6 +37,14 @@ class MapActionHandler implements ClickEventListener {
3637
this.action = action;
3738
}
3839

40+
/**
41+
* Set the power to attack with
42+
* @param power The power to attack with
43+
*/
44+
setPower(power: number) {
45+
this.power = power;
46+
}
47+
3948
onClick(x: number, y: number): void {
4049
this.action(mapNavigationHandler.getIndex(x, y));
4150
}

src/ui/element/GameHud.ts

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import {gameTicker} from "../../game/GameTicker";
2-
import {formatTime} from "../../util/StringFormatter";
2+
import {formatTime, formatTroops} from "../../util/StringFormatter";
33
import {registerSettingListener} from "../../util/settings/UserSettingManager";
4-
import {registerClickListener} from "../UIEventResolver";
4+
import {registerClickListener, registerDragListener} from "../UIEventResolver";
55
import {loadStaticElement, showUIElement} from "../UIManager";
66
import {interactionManager} from "../../event/InteractionManager";
77
import {resolveElement} from "../UIElement";
8+
import {mapActionHandler} from "../../game/action/MapActionHandler";
9+
import {gameStartRegistry} from "../../game/Game";
10+
import {clientPlayer} from "../../game/player/PlayerManager";
811

912
//@module ui
1013

@@ -17,4 +20,47 @@ const gameClock: HTMLElement = (window.document.getElementById("gameClock") as H
1720
gameTicker.registry.register(() => gameClock.innerHTML = formatTime(gameTicker.getElapsedTime()));
1821
registerSettingListener("hud-clock", show => gameClock.style.display = show ? "inherit" : "none");
1922

20-
interactionManager.draggable.add(resolveElement("GameHudContainer"));
23+
interactionManager.draggable.add(resolveElement("GameHudContainer"));
24+
25+
const slider: HTMLElement = resolveElement("sliderAttackStrength");
26+
interactionManager.draggable.add(slider);
27+
const updateValue = (x: number) => setSliderValue((x - slider.getBoundingClientRect().left) / slider.getBoundingClientRect().width);
28+
29+
registerClickListener(slider, updateValue, true, true);
30+
registerDragListener(slider, updateValue, updateValue, () => {});
31+
32+
const hiddenSlider: HTMLInputElement = resolveElement("sliderAttackHidden") as HTMLInputElement;
33+
hiddenSlider.onchange = () => setSliderValue(parseInt(hiddenSlider.value) / 100);
34+
35+
const numberDisplay: HTMLElement = resolveElement("sliderAttackNumber");
36+
let label = false;
37+
function setSliderValue(value: number) {
38+
if (value < 0) value = 0;
39+
if (value > 1) value = 1;
40+
slider.style.setProperty("--value", value.toString());
41+
if (value > 0.2 && label) {
42+
numberDisplay.classList.add("selector-number-reversed");
43+
label = false;
44+
} else if (value < 0.2 && !label) {
45+
numberDisplay.classList.remove("selector-number-reversed");
46+
label = true;
47+
}
48+
hiddenSlider.value = (value * 100).toString();
49+
50+
const scaled = Math.expm1(2 * Math.LN2 * value) / 3;
51+
numberDisplay.innerHTML = (scaled * 100).toFixed(1) + "%";
52+
mapActionHandler.setPower(scaled * 1000);
53+
}
54+
55+
const troopCountElement = resolveElement("selectorTroopCount");
56+
const densityElement = resolveElement("selectorDensityNumber");
57+
gameTicker.registry.register(() => {
58+
troopCountElement.innerText = formatTroops(clientPlayer.getTroops());
59+
densityElement.innerText = (clientPlayer.getTroops() / clientPlayer.getTerritorySize()).toFixed(2) + "%";
60+
});
61+
62+
gameStartRegistry.register(() => {
63+
setSliderValue(0.5);
64+
troopCountElement.innerText = formatTroops(clientPlayer.getTroops());
65+
densityElement.innerText = (clientPlayer.getTroops() / clientPlayer.getTerritorySize()).toFixed(2) + "%";
66+
});

src/ui/element/static/GameHud.html

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,15 @@
88
<button id="openSettings" class="btn btn-circle btn-primary icon-m icon-settings"></button>
99
<button id="exitGame" class="btn btn-circle btn-danger icon-m icon-quit"></button>
1010
</div>
11+
12+
<div class="anchor-bottom-center ui-passive">
13+
<div id="selectorStats2" class="selector-stats-container">
14+
<span id="selectorDensityNumber" class="selector-stat-success selector-stat"></span>
15+
<span id="selectorTroopCount" class="selector-stat"></span>
16+
</div>
17+
<label id="sliderAttackStrength" class="selector-slider">
18+
<span id="sliderAttackNumber" class="selector-number selector-number-reversed">50.0%</span>
19+
<input id="sliderAttackHidden" type="range" min="0" max="100" step="any">
20+
</label>
21+
</div>
1122
</div>

0 commit comments

Comments
 (0)