diff options
-rw-r--r-- | play.css | 1253 | ||||
-rw-r--r-- | play.html | 182 | ||||
-rw-r--r-- | play.js | 1943 |
3 files changed, 3378 insertions, 0 deletions
diff --git a/play.css b/play.css new file mode 100644 index 0000000..e9dab9f --- /dev/null +++ b/play.css @@ -0,0 +1,1253 @@ +main { background-color: dimgray; } +#roles { background-color: gray; } +header { background-color: silver; } +header.your_turn { background-color: orange; } +#role_Teutons .role_name { background-color: hsl(210,30%,80%); } +#role_Russians .role_name { background-color: hsl(35,40%,80%); } +#turn_info { background-color: gainsboro; } +.role_held { float: right; } + +#log { background-color: whitesmoke; } +#log .h1 { font-weight: bold; padding-top:2px; padding-bottom:2px; text-align: center; } +#log .h2 { padding-top:2px; padding-bottom:2px; text-align: center; } +#log .h3 { text-align: center; } +#log .h4 { text-decoration: underline; } +#log .h5 { text-decoration: underline; } + +#log .h1 { background-color: hsl(0,0%,80%); } +#log .h2.teutonic { background-color: hsl(210,30%,85%); } +#log .h2.russian { background-color: hsl(36,40%,85%); } +#log .h3.teutonic { background-color: hsl(210,30%,90%); } +#log .h3.russian { background-color: hsl(35,40%,90%); } + +#log div { padding-left: 20px; text-indent: -12px; } +#log div.i { padding-left: 32px; text-indent: -12px; } +#log div.ii { padding-left: 44px; text-indent: -12px; } + +#log .card_tip { font-style: italic; } +#log .card_tip:hover { text-decoration: underline; } +#log .lord_tip:hover { cursor: pointer; text-decoration: underline; } +#log .locale_tip:hover { cursor: pointer; text-decoration: underline; } +#log .way_tip:hover { cursor: pointer; text-decoration: underline; } + +.action { + cursor: pointer; +} + +#log { + font-variant-numeric: tabular-nums; +} + +/* PANELS */ + +.panel { + min-width: 1275px; + max-width: 1275px; + margin: 12px auto 36px auto; + background-color: #555; +} + +.panel_header { + background-color: #444; + color: white; + user-select: none; + font-weight: bold; + text-align: center; + padding: 3px 1em; +} + +.panel_body { + display: flex; + justify-content: center; + flex-wrap: wrap; + padding: 18px; + gap: 18px; +} + +.court_panel { + max-width: fit-content; +} + +.court_body { + gap: 24px; +} + +/* ARTS OF WAR */ + +#arts_of_war_list { + min-height: 260px; +} + +/* PLAN */ + +#plan { + min-height: 260px; +} + +#plan_actions { + background-color: #444; + min-height: 130px; +} + +#plan_actions.hide { + display: none +} + +#plan_actions .card { + width: 93px; + height: 130px; + background-size: 93px 130px; +} + +body.Russians #plan_actions .teutonic { display: none } +body.Teutons #plan_actions .russian { display: none } + +/* BATTLE GRID WITH LORD MATS */ + +#battle_grid { + display: grid; + grid-template-columns: auto auto auto; + margin: 0 auto; + justify-content: center; + padding: 18px; +} + +#battle_grid .grid_array { + width: 376px; + height: 506px; + padding: 36px 12px 12px 12px; +} + +#battle_grid #grid_ga { height: auto; } +#battle_grid #grid_sw { height: auto; } + +.grid_array { background-repeat: no-repeat } + +.grid_array.action { + background-image: url(images/battle_array_action.svg); + background-position: top center; +} + +#battle_grid { + background-repeat: no-repeat; + background-position: + 95% 350px, + 85% 175px, + 10% 300px, + 90% 800px, + 10% 850px, + 40% 1000px; + background-image: + url(images/battlefield_buildings.png), + url(images/battlefield_trees1.png), + url(images/battlefield_hill1.png), + url(images/battlefield_hill2.png), + url(images/battlefield_trees2.png), + url(images/battlefield_trees3.png); +} + +#battle_grid.attacker { + background-position: + 10% 900px, + 5% 725px, + 90% 850px, + 10% 250px, + 90% 250px, + 60% 100px; +} + +#grid_rg1:empty:not(.action), #grid_rg2:empty:not(.action), #grid_rg3:empty:not(.action), +#grid_sa1:empty:not(.action), #grid_sa2:empty:not(.action), #grid_sa3:empty:not(.action) { + display: none; +} + +#battle_header { grid-row: 1; grid-column: 1 / 4; } + +.defender .att, +.attacker .def, +.defender .att .unit, +.attacker .def .unit, +.defender .att .asset, +.attacker .def .asset, +.defender .att .service_marker, +.attacker .def .service_marker, +.defender .att .moved_fought, +.attacker .def .moved_fought, +.defender .att .feed_x2, +.attacker .def .feed_x2 +{ + transform: rotate(180deg) +} + +.defender .att .background, +.attacker .def .background +{ + border-color: #b1a05f #f2e19d #f2e19d #b1a05f; + box-shadow: 0 0 0 1px #584800, -1px -2px 4px #0008; +} + +.defender #grid_a1 { grid-row: 1; grid-column: 1; } +.defender #grid_a2 { grid-row: 1; grid-column: 2; } +.defender #grid_a3 { grid-row: 1; grid-column: 3; } +.defender #grid_ga { grid-row: 2; grid-column: 2; } +.defender #grid_d1 { grid-row: 3; grid-column: 1; } +.defender #grid_d2 { grid-row: 3; grid-column: 2; } +.defender #grid_d3 { grid-row: 3; grid-column: 3; } +.defender #grid_rg1 { grid-row: 4; grid-column: 1; } +.defender #grid_rg2 { grid-row: 4; grid-column: 2; } +.defender #grid_rg3 { grid-row: 4; grid-column: 3; } +.defender #grid_sw { grid-row: 5; grid-column: 2; } +.defender #grid_sa1 { grid-row: 6; grid-column: 1; } +.defender #grid_sa2 { grid-row: 6; grid-column: 2; } +.defender #grid_sa3 { grid-row: 6; grid-column: 3; } + +.attacker #grid_a1 { grid-row: 6; grid-column: 3; } +.attacker #grid_a2 { grid-row: 6; grid-column: 2; } +.attacker #grid_a3 { grid-row: 6; grid-column: 1; } +.attacker #grid_ga { grid-row: 5; grid-column: 2; } +.attacker #grid_d1 { grid-row: 4; grid-column: 3; } +.attacker #grid_d2 { grid-row: 4; grid-column: 2; } +.attacker #grid_d3 { grid-row: 4; grid-column: 1; } +.attacker #grid_rg1 { grid-row: 3; grid-column: 3; } +.attacker #grid_rg2 { grid-row: 3; grid-column: 2; } +.attacker #grid_rg3 { grid-row: 3; grid-column: 1; } +.attacker #grid_sw { grid-row: 2; grid-column: 2; } +.attacker #grid_sa1 { grid-row: 1; grid-column: 3; } +.attacker #grid_sa2 { grid-row: 1; grid-column: 2; } +.attacker #grid_sa3 { grid-row: 1; grid-column: 1; } + +.defender #pursuit.rotate, +.attacker #pursuit:not(.rotate) +{ + transform: rotate(180deg); + border-color: #a68c61 #e7cb9e #e7cb9e #a68c61; + box-shadow: 0 0 0 1px #4e370a, -1px -2px 4px #0008; +} + +#pursuit { + margin: 15px auto; +} + +.siegeworks { + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 4px; + margin: 12px; +} + +.siegeworks:empty { display: none } + +#garrison { + background-image: url(images/garrison.svg); + background-repeat: no-repeat; + background-position: center; + display: flex; + flex-wrap: wrap; + justify-content: center; + align-items: center; + width: 376px; + height: 80px; + gap: 2px; + margin: 0 auto; +} + +#garrison.action { + background-image: url(images/garrison_action.svg); +} + +#garrison.hide { display: none } + +#garrison .unit { position: static } + +/* MATS */ + +.mat { + position: relative; + width: 376px; + height: 506px; + z-index: 1; +} + +.mat .background { + position: absolute; + width: 372px; + height: 372px; + border-radius: 12px; + border-width: 2px; + border-style: solid; + background-color: #d1c07e; + background-position: center; + background-size: 360px 360px; + background-repeat: no-repeat; + z-index: 4; +} + +.court_body .mat.besieged .background { + filter: grayscale(50%) +} + +#battle_grid .mat.ambushed .background { + filter: grayscale(50%) +} + +.court_body .mat.hidden .background { + filter: grayscale(100%) +} + +.court_body .mat.besieged.hidden .background { + filter: grayscale(100%) brightness(75%) +} + +.mat.command .background { + box-shadow: 0 0 0 1px #584800, 0 0 0 3px gold; +} + +.mat.selected .background { + box-shadow: 0 0 0 1px #584800, 0 0 0 4px yellow; +} + +.mat .capabilities { + position: absolute; + width: 372px; + height: 260px; + z-index: 2; + top: 246px; + left: 2px; + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 0px; +} + +.mat .events { + position: absolute; + z-index: 3; + width: 372px; + height: 260px; + left: 2px; + top: -36px; + display: flex; + flex-wrap: wrap; + justify-content: center; + gap: 0px; + transition-property: top; + transition-duration: 100ms; +} + +.mat .events:hover { + top: -130px; +} + +.mat .events:empty { + display: none; +} + +.mat .moved_fought, .mat .feed_x2 { + position: absolute; + z-index: 5; +} + +.mat .feed_x2 { left: 8px; top: 164px; } +.mat .moved_fought.one { right: 72px; top: 12px; } +.mat .moved_fought.two { right: 66px; top: 6px; } + +body.shift .capabilities, +body.shift .events, +body.shift #capabilities1, +body.shift #capabilities2 { + z-index: 200; +} + +.mat .forces, .mat .routed, .mat .assets, #veche { + position: absolute; + display: flex; + flex-wrap: wrap; + justify-content: start; + justify-items: center; + align-content: center; + align-items: center; + gap: 4px 6px; + z-index: 5; + //background-color: #f004; +} + +#veche { + gap: 4px; +} + +.mat > .background > div { + //outline: 1px solid red; +} + +.mat .forces, #veche { + justify-content: center; +} + +.mat .forces, .mat .routed { + gap: 2px; +} + +.mat .ready_vassals { + position: absolute; + z-index: 6; +} + +.mat .mustered_vassals { + position: absolute; + z-index: 5; +} + +body.shift .mustered_vassals { + display: block; +} + +.mat .ready_vassals .service_marker, +.mat .mustered_vassals .service_marker { + transition: margin 100ms; + margin-top: -24px; +} + +.mat .ready_vassals:hover .service_marker, +.mat .mustered_vassals:hover .service_marker { + margin-top: 1px; +} + +.mat .mustered_vassals .service_marker { + margin-top: -38px; +} + +.mat .shield { + position: absolute; + background-repeat: no-repeat; +} + +.mat.teutonic .shield { + top: 10px; left: 6px; + width: 75px; height: 85px; + border-radius: 0 0 50% 50%; + filter: drop-shadow(0 0 4px white); +} + +.mat.russian .shield, +.mat.yaroslav .shield { + top: 6px; left: 7px; + width: 63px; height: 125px; + border-radius: 30px 30px 50% 50%; + filter: drop-shadow(0 0 3px white); +} + +.mat.teutonic .shield.action { + background-image: url(images/shield_teutonic.svg); + filter: drop-shadow(0 0 3px white); +} + +.mat.russian .shield.action, .mat.yaroslav .shield.action { + background-image: url(images/shield_russian.svg); + filter: drop-shadow(0 0 3px white); +} + +.mat .forces { top: 96px; left: 66px; width: 240px; height: 96px; } +.mat .routed { top: 200px; left: 18px; width: 236px; height: 48px; } +.mat .assets { top: 255px; left: 24px; width: 230px; height: 96px; } +.mat .ready_vassals { bottom: 22px; right: 22px; width: 94px; } +.mat .mustered_vassals { bottom: 134px; right: 6px; width: 94px; } + +.mat.teutonic.andreas .background { background-image:url(mats.1x/mat_teutonic_andreas-fs8.png) } +.mat.teutonic.heinrich .background { background-image:url(mats.1x/mat_teutonic_heinrich-fs8.png) } +.mat.teutonic.hermann .background { background-image:url(mats.1x/mat_teutonic_hermann-fs8.png) } +.mat.teutonic.knud_and_abel .background { background-image:url(mats.1x/mat_teutonic_knud_and_abel-fs8.png) } +.mat.teutonic.rudolf .background { background-image:url(mats.1x/mat_teutonic_rudolf-fs8.png) } +.mat.teutonic.yaroslav .background { background-image:url(mats.1x/mat_teutonic_yaroslav-fs8.png) } +.mat.russian.aleksandr .background { background-image:url(mats.1x/mat_russian_aleksandr-fs8.png) } +.mat.russian.andrey .background { background-image:url(mats.1x/mat_russian_andrey-fs8.png) } +.mat.russian.domash .background { background-image:url(mats.1x/mat_russian_domash-fs8.png) } +.mat.russian.gavrilo .background { background-image:url(mats.1x/mat_russian_gavrilo-fs8.png) } +.mat.russian.karelians .background { background-image:url(mats.1x/mat_russian_karelians-fs8.png) } +.mat.russian.vladislav .background { background-image:url(mats.1x/mat_russian_vladislav-fs8.png) } + +@media (min-resolution: 97dpi) { +.mat.teutonic.andreas .background { background-image:url(mats.2x/mat_teutonic_andreas-fs8.png) } +.mat.teutonic.heinrich .background { background-image:url(mats.2x/mat_teutonic_heinrich-fs8.png) } +.mat.teutonic.hermann .background { background-image:url(mats.2x/mat_teutonic_hermann-fs8.png) } +.mat.teutonic.knud_and_abel .background { background-image:url(mats.2x/mat_teutonic_knud_and_abel-fs8.png) } +.mat.teutonic.rudolf .background { background-image:url(mats.2x/mat_teutonic_rudolf-fs8.png) } +.mat.teutonic.yaroslav .background { background-image:url(mats.2x/mat_teutonic_yaroslav-fs8.png) } +.mat.russian.aleksandr .background { background-image:url(mats.2x/mat_russian_aleksandr-fs8.png) } +.mat.russian.andrey .background { background-image:url(mats.2x/mat_russian_andrey-fs8.png) } +.mat.russian.domash .background { background-image:url(mats.2x/mat_russian_domash-fs8.png) } +.mat.russian.gavrilo .background { background-image:url(mats.2x/mat_russian_gavrilo-fs8.png) } +.mat.russian.karelians .background { background-image:url(mats.2x/mat_russian_karelians-fs8.png) } +.mat.russian.vladislav .background { background-image:url(mats.2x/mat_russian_vladislav-fs8.png) } +} + +.locale_markers > .conquered + .conquered { margin-left: -44px; margin-bottom: 13px; } +.locale_markers > .conquered + .conquered + .conquered { margin-bottom: 26px; } +.locale_markers > .conquered + .conquered + .conquered + .conquered { margin-bottom: 39px; } + +.locale_markers .siege + .siege { margin-left: -44px; margin-bottom: 13px; } +.locale_markers .siege + .siege + .siege { margin-bottom: 26px; } +.locale_markers .siege + .siege + .siege + .siege { margin-bottom: 39px; } + +/* TUCKED CARDS */ + +.tuck_under_map { + margin: 0 auto 36px auto; + width: 1275px; + min-height: 260px; + margin-top: -130px; + display: grid; + grid-template-columns: auto auto; +} + +#capabilities1, #capabilities2 { + display: flex; + flex-wrap: wrap; + gap: 12px; +} + +#capabilities1 { + margin-left: 24px; + justify-content: start; +} + +#capabilities2 { + margin-right: 24px; + justify-content: end; +} + +/* ASSETS */ + +.asset { + width: 42px; + height: 42px; + background-size: cover; + border: 2px solid transparent; + border-radius: 8px; + box-shadow: 0 0 0 1px #444, 0 1px 4px #0008; +} + +.asset.action { + box-shadow: 0 0 0 1px #444, 0 0 0 4px white !important; +} + +.asset.selected { + box-shadow: 0 0 0 1px #444, 0 0 0 4px yellow !important; +} + +.asset.boat.x1 { background-image: url(images/asset_boat_x1.png); } +.asset.boat.x2 { background-image: url(images/asset_boat_x2.png) } +.asset.boat.x4 { background-image: url(images/asset_boat_x4.png) } +.asset.cart.x1 { background-image: url(images/asset_cart_x1.png); } +.asset.cart.x2 { background-image: url(images/asset_cart_x2.png) } +.asset.cart.x4 { background-image: url(images/asset_cart_x4.png) } +.asset.coin.x1 { background-image: url(images/asset_coin_x1.png); } +.asset.coin.x2 { background-image: url(images/asset_coin_x2.png) } +.asset.coin.x3 { background-image: url(images/asset_coin_x3.png) } +.asset.coin.x4 { background-image: url(images/asset_coin_x4.png) } +.asset.loot.x1 { background-image: url(images/asset_loot_x1.png); } +.asset.loot.x2 { background-image: url(images/asset_loot_x2.png) } +.asset.loot.x3 { background-image: url(images/asset_loot_x3.png) } +.asset.loot.x4 { background-image: url(images/asset_loot_x4.png) } +.asset.prov.x1 { background-image: url(images/asset_prov_x1.png); } +.asset.prov.x2 { background-image: url(images/asset_prov_x2.png) } +.asset.prov.x3 { background-image: url(images/asset_prov_x3.png) } +.asset.prov.x4 { background-image: url(images/asset_prov_x4.png) } +.asset.ship.x1 { background-image: url(images/asset_ship_x1.png); } +.asset.ship.x2 { background-image: url(images/asset_ship_x2.png) } +.asset.ship.x4 { background-image: url(images/asset_ship_x4.png) } +.asset.sled.x1 { background-image: url(images/asset_sled_x1.png); } +.asset.sled.x2 { background-image: url(images/asset_sled_x2.png) } +.asset.sled.x4 { background-image: url(images/asset_sled_x4.png) } + +.marker { + border: 2px solid aqua; + border-radius: 8px; + background-size: cover; + background-position: center; + background-repeat: no-repeat; +} + +.marker.square { + width: 42px; + height: 42px; +} + +.marker.small { + width: 36px; + height: 36px; + border-radius: 6px; +} + +.marker.rectangle { + width: 90px; + height: 42px; +} + +.marker.circle { + width: 51px; + height: 51px; + background-size: 51px 51px; + border-radius: 50%; +} + +.marker.turn.levy { background-image: url(images/marker_levy.png) } +.marker.turn.campaign { background-image: url(images/marker_campaign.png) } +.marker.storm { background-image: url(images/marker_storm.png) } +.marker.battle { background-image: url(images/marker_battle.png) } +.marker.conquered.teutonic { background-image: url(images/marker_conquered_teutonic.png) } +.marker.conquered.russian { background-image: url(images/marker_conquered_russian.png) } +.marker.ravaged.teutonic { background-image: url(images/marker_ravaged_teutonic.png) } +.marker.ravaged.russian { background-image: url(images/marker_ravaged_russian.png) } +.marker.siege.teutonic { background-image: url(images/marker_siege_teutonic.png) } +.marker.siege.russian { background-image: url(images/marker_siege_russian.png) } +.marker.enemy_lords_removed.teutonic { background-image: url(images/marker_enemy_lords_removed_teutonic.png) } +.marker.enemy_lords_removed.russian { background-image: url(images/marker_enemy_lords_removed_russian.png) } +.marker.victory.teutonic { background-image: url(images/marker_victory_teutonic.png) } +.marker.victory.russian { background-image: url(images/marker_victory_russian.png) } +.marker.victory.half.teutonic { background-image: url(images/marker_victory_half_teutonic.png) } +.marker.victory.half.russian { background-image: url(images/marker_victory_half_russian.png) } +.marker.pursuit.teutonic { background-image: url(images/marker_pursuit_teutonic.png) } +.marker.pursuit.russian { background-image: url(images/marker_pursuit_russian.png) } +.marker.castle.russian { background-image: url(images/marker_castle_russian.png) } +.marker.castle.teutonic { background-image: url(images/marker_castle_teutonic.png) } +.marker.walls { background-image: url(images/marker_walls.a.png) } +.marker.moved_fought { background-image: url(images/marker_moved_fought.png) } +.marker.feed_x2 { background-image: url(images/almoravid_marker_feed_x2.png) } + +.marker.number.teutonic.n1 { background-image: url(images/marker_1_teutonic.png) } +.marker.number.teutonic.n2 { background-image: url(images/marker_2_teutonic.png) } +.marker.number.teutonic.n3 { background-image: url(images/marker_3_teutonic.png) } +.marker.number.teutonic.n4 { background-image: url(images/marker_4_teutonic.png) } +.marker.number.teutonic.n5 { background-image: url(images/marker_5_teutonic.png) } +.marker.number.teutonic.n6 { background-image: url(images/marker_6_teutonic.png) } +.marker.number.russian.n1 { background-image: url(images/marker_1_russian.png) } +.marker.number.russian.n2 { background-image: url(images/marker_2_russian.png) } +.marker.number.russian.n3 { background-image: url(images/marker_3_russian.png) } +.marker.number.russian.n4 { background-image: url(images/marker_4_russian.png) } +.marker.number.russian.n5 { background-image: url(images/marker_5_russian.png) } +.marker.number.russian.n6 { background-image: url(images/marker_6_russian.png) } + +/* UNITS */ + +.unit { + background-size: contain; + background-repeat: no-repeat; + filter: drop-shadow(0px 2px 2px #0004); +} + +.unit.action { + filter: + drop-shadow(2px 0px 0px white) + drop-shadow(0px 2px 0px white) + drop-shadow(0px -2px 0px white) + drop-shadow(-2px 0px 0px white); +} + +.unit.knights, .unit.sergeants, .unit.light_horse, .unit.asiatic_horse { + width: 30px; + height: 35px; +} + +.unit.men_at_arms, .unit.militia, .unit.serfs { + width: 43px; + height: 35px; +} + +.unit.knights { background-image: url(images/unit_knights.svg) } +.unit.sergeants { background-image: url(images/unit_sergeants.svg) } +.unit.light_horse { background-image: url(images/unit_light_horse.svg) } +.unit.asiatic_horse { background-image: url(images/unit_asiatic_horse.svg) } +.unit.men_at_arms { background-image: url(images/unit_men_at_arms.svg) } +.unit.militia { background-image: url(images/unit_militia.svg) } +.unit.serfs { background-image: url(images/unit_serfs.svg) } + +/* CARDS */ + +.hand { + margin: 24px auto; + display: flex; + flex-wrap: wrap; + justify-content: center; + align-items: center; + min-height: 260px; + max-width: 1275px; + gap: 16px; +} + +.card { + background-size: 186px 260px; + width: 186px; + height: 260px; + border-radius: 8px; + transition: 100ms; +} + +.card_info { + border-bottom: 1px solid black; +} + +.card_info .card { + margin: 12px auto; +} + +.card.disabled { + filter: brightness(80%); +} + +.card.action.teutonic { background-color: #e1e6e8; box-shadow: 0 0 0 1px #666a6c, 0 0 0 4px white; } +.card.action.russian { background-color: #e1d6c1; box-shadow: 0 0 0 1px #665c4a, 0 0 0 4px white; } + +.card.selected.teutonic { background-color: #e1e6e8; box-shadow: 0 0 0 1px #666a6c, 0 0 0 4px yellow; } +.card.selected.russian { background-color: #e1d6c1; box-shadow: 0 0 0 1px #665c4a, 0 0 0 4px yellow; } + +/* MAP */ + +#mapwrap { + width: 1275px; + height: 1650px; + box-shadow: 0px 1px 10px #0008; + z-index: 3; +} + +#map { + background-repeat: no-repeat; + background-size: cover; + width: 1275px; + height: 1650px; + overflow: clip; +} + +#map { background-image: url(map75.jpg) } +@media (min-resolution: 97dpi) { +#map { background-image: url(map150.jpg) } +} + +.box { + position: absolute; + border: 3px solid transparent; +} + +.veche_border { + position: absolute; +} + +.box.victory { + border-radius: 50%; +} + +.box.turn { + border-radius: 50%; +} + +.box.calendar.end { + background-color: #8886; +} + +.box.calendar.action { + border-color: white; + background-color: #fff6; +} + +.box.way.action { + border-color: dodgerblue; +} + +.box.way.crossroads.action { + border-color: saddlebrown; +} + +.box.way.crossroads { border-radius: 36px; } +.box.way.wirz { border-radius: 36px 36px 75% 75% } +.box.way.peipus-east { border-radius: 75% 36px 75% 36px } +.box.way.peipus-north { border-radius: 36px 36px 36px 75% } + +.locale { + box-sizing: border-box; + position: absolute; + border: 3px solid transparent; +} + +.locale_name { + box-sizing: border-box; + position: absolute; + border: 3px solid transparent; +} + +.locale.region { border-radius: 50% } +.locale.town { border-radius: 80px 80px 36px 36px; } + +.locale.traderoute { border-radius: 60% 40% 0 0 } +.locale.fort { border-radius: 24px 24px 0 0 } +.locale.castle { border-radius: 18px 18px 0 0 } +.locale.city { border-radius: 50% 50% 0 0 } +.locale.bishopric { border-radius: 50% 50% 0 0 } +.locale.novgorod { border-radius: 50% 50% 0 0 } + +.locale.action.region { background-color: #5A02 } +.locale.action.town { background-color: #fc02 } +.locale.action.traderoute { background-color: #0af2 } +.locale.action.fort { background-color: #f552 } +.locale.action.city { background-color: #f552 } +.locale.action.novgorod { background-color: #f552 } +.locale.action.castle { background-color: #0002 } +.locale.action.bishopric { background-color: #0002 } + +.locale.action { border-color: white; box-shadow: 0 0 4px white; } +.locale.action.laden { border-color: white; box-shadow: 0 0 0 3px indianred; background-color: #cd5c5c66 } + +.locale.supply_path { border-color: indianred; background-color: #cd5c5c66; } +.locale.supply_source { border-color: seagreen; background-color: #2e8b5766; } +.locale.supply_source.action { border-color: white; background-color: #2e8b5766; box-shadow: 0 0 0 3px seagreen; } + +.way.tip, .locale.tip, .locale_name.tip { + background-color: #ff08; + box-shadow: 0 0 8px #ff08; +} + +.locale_markers { + position: absolute; + pointer-events: none; + display: flex; + flex-wrap: wrap; + justify-content: center; + align-items: center; + gap: 4px; +} + +.veche_label { + pointer-events: none; + border: 3px solid transparent; +} + +.veche_border { + pointer-events: none; +} + +#veche { left: 1046px; top: 1460px; width: 215px; height: 161px; } +#veche_border_w { left: 1046px; top: 1460px; width: 50px; height: 157px; } +#veche_border_e { left: 1208px; top: 1460px; width: 50px; height: 157px; } +#veche_border_sw { left: 1046px; top: 1618px; width: 59px; height: 0; } +#veche_border_se { left: 1197px; top: 1618px; width: 64px; height: 0; } + +#veche_label_top { left: 1097px; top: 1457px; width: 107px; height: 20px; } +#veche_label_bottom { left: 1104px; top: 1605px; width: 89px; height: 13px; } + +#veche_border_w { border-top: 3px solid transparent; border-left: 3px solid transparent; } +#veche_border_e { border-top: 3px solid transparent; border-right: 3px solid transparent; } +#veche_border_sw { border-bottom: 3px solid transparent; } +#veche_border_se { border-bottom: 3px solid transparent; } + +#veche.action { + box-shadow: 0 0 8px white; +} + +#veche.action ~ .veche_label { + box-shadow: 0 0 8px white; + border-color: white; +} + +#veche.action ~ .veche_border { + border-color: white; +} + +#smerdi { + display: flex; + bottom: 12px; + right: 370px; + gap: 2px; +} + +#vp2.stack:not(.half) ~ #vp1.stack:not(.half) { transform: translate(0px,-36px); } +#vp2.stack.half ~ #vp1.stack.half { transform: translate(0px,-36px); } +#vp2.stack.half ~ #vp1.stack:not(.half) { transform: translate(-12px,-30px); } +#vp2.stack:not(.half) ~ #vp1.stack.half { transform: translate(12px,-30px); } + +.marker.russian.enemy_lords_removed { transform: translate(-12px,18px); } +.marker.teutonic.enemy_lords_removed { transform: translate(12px,18px); } + +.marker.victory, .marker.turn, .marker.enemy_lords_removed { + position: absolute; +} + +.v0 { top: 132px; left: 2px; } + +.v1, .v2, .v3, .v4, .v5, .v6, .v7, .v8, .t1, .t2, .t3, .t4, .t5, .t6, .t7, .t8 { top: 46px } +.v9, .v10, .v11, .v12, .v13, .v14, .v15, .v16, .t9, .t10, .t11, .t12, .t13, .t14, .t15, .t16 { top: 284px } + +.v1, .v9 { left: 44px } +.v2, .v10 { left: 196px } +.v3, .v11 { left: 362px } +.v4, .v12 { left: 514px } +.v5, .v13 { left: 680px } +.v6, .v14 { left: 832px } +.v7, .v15 { left: 999px } +.v8, .v16 { left: 1151px } + +.t1, .t9 { left: 100px } +.t2, .t10 { left: 252px } +.t3, .t11 { left: 418px } +.t4, .t12 { left: 570px } +.t5, .t13 { left: 737px } +.t6, .t14 { left: 888px } +.t7, .t15 { left: 1055px } +.t8, .t16 { left: 1207px } + +/* PIECES */ + +#legate, .cylinder, .marker { + transition-property: top, left; + transition-duration: 700ms; + transition-timing-function: ease; +} + +.service_marker { + transition-property: top, left; + transition-duration: 100ms, 500ms; +} + +#legate { + position: absolute; + width: 32px; + height: 64px; + background-image: url(images/legate.svg); + filter: drop-shadow(0px 2px 4px #0004); +} + +#legate.action { + filter: + drop-shadow(2px 0px 0px white) + drop-shadow(0px 2px 0px white) + drop-shadow(0px -2px 0px white) + drop-shadow(-2px 0px 0px white); +} + +#legate.selected { + filter: + drop-shadow(2px 0px 0px yellow) + drop-shadow(0px 2px 0px yellow) + drop-shadow(0px -2px 0px yellow) + drop-shadow(-2px 0px 0px yellow); +} + +.cylinder { + position: absolute; + width: 44px; + height: 48px; + background-size: 44px 48px; +} + +.cylinder:not(.lieutenant) { + filter: drop-shadow(0px 2px 4px #0004); +} + +#map .cylinder.besieged { + transform: translateY(8px) +} + +.cylinder.command { + filter: + drop-shadow(2px 0px 0px gold) + drop-shadow(0px 2px 0px gold) + drop-shadow(0px -2px 0px gold) + drop-shadow(-2px 0px 0px gold); +} + +.cylinder.action { + filter: + drop-shadow(2px 0px 0px white) + drop-shadow(0px 2px 0px white) + drop-shadow(0px -2px 0px white) + drop-shadow(-2px 0px 0px white); +} + +.cylinder.selected { + filter: + drop-shadow(2px 0px 0px yellow) + drop-shadow(0px 2px 0px yellow) + drop-shadow(0px -2px 0px yellow) + drop-shadow(-2px 0px 0px yellow); +} + +.cylinder.andreas { background-image: url(images/lord_teutonic_1.svg) } +.cylinder.hermann.marshal { background-image: url(images/lord_teutonic_2.svg) } +.cylinder.hermann { background-image: url(images/lord_teutonic_3.svg) } +.cylinder.heinrich { background-image: url(images/lord_teutonic_4.svg) } +.cylinder.rudolf { background-image: url(images/lord_teutonic_5.svg) } +.cylinder.knud_and_abel { background-image: url(images/lord_teutonic_6.svg) } +.cylinder.yaroslav { background-image: url(images/lord_teutonic_7.svg) } + +.cylinder.aleksandr { background-image: url(images/lord_russian_1.svg) } +.cylinder.andrey { background-image: url(images/lord_russian_2.svg) } +.cylinder.domash { background-image: url(images/lord_russian_3.svg) } +.cylinder.gavrilo { background-image: url(images/lord_russian_4.svg) } +.cylinder.vladislav { background-image: url(images/lord_russian_5.svg) } +.cylinder.karelians { background-image: url(images/lord_russian_6.svg) } +.cylinder.andrey.marshal { background-image: url(images/lord_russian_7.svg) } + +.marker.teutonic, +.service_marker.teutonic.lord { + background-color: #444; + border-color: #555 #222 #222 #555; + box-shadow: 0 0 0 1px #111, 0 2px 4px #0008; +} + +.marker.russian, +.marker.teutonic.enemy_lords_removed, +.service_marker.russian.lord { + background-color: #fff; + border-color: #eee #ccc #ccc #eee; + box-shadow: 0 0 0 1px #555, 0 2px 4px #0008; +} + +.service_marker { + background-repeat: no-repeat; + width: 90px; + height: 42px; + border-radius: 8px; + box-shadow: 0 1px 6px #0008; + border-width: 2px; + border-style: solid; +} + +#pieces .service_marker { + position: absolute; +} + +#pieces .castle { + position: absolute; + pointer-events: none; +} + +.service_marker.teutonic.lord.selected { box-shadow: 0 0 0 1px #111, 0 0 0 4px yellow; } +.service_marker.russian.lord.selected { box-shadow: 0 0 0 1px #555, 0 0 0 4px yellow; } +.service_marker.teutonic.lord.action { box-shadow: 0 0 0 1px #111, 0 0 0 4px white; } +.service_marker.russian.lord.action { box-shadow: 0 0 0 1px #555, 0 0 0 4px white; } +.service_marker.teutonic.lord.action.bad { box-shadow: 0 0 0 1px #111, 0 0 0 4px tomato; } +.service_marker.russian.lord.action.bad { box-shadow: 0 0 0 1px #555, 0 0 0 4px tomato; } + +.service_marker.teutonic.vassal.selected { box-shadow: 0 0 0 1px #0f0d0d, 0 0 0 4px yellow; } +.service_marker.russian.vassal.selected { box-shadow: 0 0 0 1px #736e5e, 0 0 0 4px yellow; } +.service_marker.teutonic.vassal.action { box-shadow: 0 0 0 1px #0f0d0d, 0 0 0 4px white; } +.service_marker.russian.vassal.action { box-shadow: 0 0 0 1px #736e5e, 0 0 0 4px white; } + +.service_marker.lord { background-size: 90px 252px } +.service_marker.vassal { background-size: 270px 210px } + +.service_marker.lord.teutonic { background-image:url(images/service_lords_teutonic.png) } +.service_marker.lord.russian { background-image:url(images/service_lords_russian.png) } +.service_marker.vassal.teutonic { background-image:url(images/service_vassals_teutonic.png) } +.service_marker.vassal.russian { background-image:url(images/service_vassals_russian.png) } + +.service_marker.lord.image0{background-position:0 -0px} +.service_marker.lord.image1{background-position:0 -42px} +.service_marker.lord.image2{background-position:0 -84px} +.service_marker.lord.image3{background-position:0 -126px} +.service_marker.lord.image4{background-position:0 -168px} +.service_marker.lord.image5{background-position:0 -210px} + +.service_marker.vassal.image0{background-position:0 -0px} +.service_marker.vassal.image1{background-position:-90px -0px} +.service_marker.vassal.image2{background-position:-180px -0px} +.service_marker.vassal.image3{background-position:0 -42px} +.service_marker.vassal.image4{background-position:-90px -42px} +.service_marker.vassal.image5{background-position:-180px -42px} +.service_marker.vassal.image6{background-position:0 -84px} +.service_marker.vassal.image7{background-position:-90px -84px} +.service_marker.vassal.image8{background-position:-180px -84px} +.service_marker.vassal.image9{background-position:0 -126px} +.service_marker.vassal.image10{background-position:-90px -126px} +.service_marker.vassal.image11{background-position:-180px -126px} +.service_marker.vassal.image12{background-position:0 -168px} +.service_marker.vassal.image13{background-position:-90px -168px} +.service_marker.vassal.image14{background-position:-180px -168px} + +/* BACKGROUND COLORS AND BORDERS */ +/* :r !node tools/colors.mjs */ + +#battle_mat { background-color: #d1c07e; border-color: #f2e19d #b1a05f #b1a05f #f2e19d; box-shadow: 0 0 0 1px #584800, 1px 2px 4px #0008; } +.mat .background { background-color: #d1c07e; border-color: #f2e19d #b1a05f #b1a05f #f2e19d; box-shadow: 0 0 0 1px #584800, 1px 2px 4px #0008; } +.card.teutonic { background-color: #e1e6e8; border-color: #fbffff #c1c5c7 #c1c5c7 #fbffff; box-shadow: 0 0 0 1px #666a6c, 1px 2px 4px #0008; } +.card.russian { background-color: #e1d6c1; border-color: #fff7e1 #c1b6a1 #c1b6a1 #fff7e1; box-shadow: 0 0 0 1px #665c4a, 1px 2px 4px #0008; } +.service_marker.teutonic.vassal { background-color: #777474; border-color: #959292 #5a5858 #5a5858 #959292; box-shadow: 0 0 0 1px #0f0d0d, 1px 2px 4px #0008; } +.service_marker.russian.vassal { background-color: #f0ead8; border-color: #ffffed #cfc9b8 #cfc9b8 #ffffed; box-shadow: 0 0 0 1px #736e5e, 1px 2px 4px #0008; } +.asset.sled { background-color: #e5dcc1; border-color: #fffde2 #c5bca1 #c5bca1 #fffde2; box-shadow: 0 0 0 1px #69614a, 1px 2px 4px #0008; } +.asset.boat { background-color: #adceed; border-color: #cdefff #8eaecc #8eaecc #cdefff; box-shadow: 0 0 0 1px #38556f, 1px 2px 4px #0008; } +.asset.cart.x1 { background-color: #daba8b; border-color: #fbdaaa #ba9b6d #ba9b6d #fbdaaa; box-shadow: 0 0 0 1px #5f4315, 1px 2px 4px #0008; } +.asset.cart.x2 { background-color: #d1a973; border-color: #f2c992 #b18a55 #b18a55 #f2c992; box-shadow: 0 0 0 1px #563400, 1px 2px 4px #0008; } +.asset.cart.x4 { background-color: #c4975b; border-color: #e5b67a #a4793c #a4793c #e5b67a; box-shadow: 0 0 0 1px #4b2500, 1px 2px 4px #0008; } +.asset.coin.x1 { background-color: #d2d5d4; border-color: #f3f6f5 #b2b5b4 #b2b5b4 #f3f6f5; box-shadow: 0 0 0 1px #595c5b, 1px 2px 4px #0008; } +.asset.coin.x2 { background-color: #d2d5d4; border-color: #f3f6f5 #b2b5b4 #b2b5b4 #f3f6f5; box-shadow: 0 0 0 1px #595c5b, 1px 2px 4px #0008; } +.asset.coin.x3 { background-color: #b3b5b4; border-color: #d3d5d4 #949695 #949695 #d3d5d4; box-shadow: 0 0 0 1px #3f4040, 1px 2px 4px #0008; } +.asset.coin.x4 { background-color: #b3b5b4; border-color: #d3d5d4 #949695 #949695 #d3d5d4; box-shadow: 0 0 0 1px #3f4040, 1px 2px 4px #0008; } +.asset.prov.x1 { background-color: #ffe293; border-color: #fffdad #dec173 #dec173 #fffdad; box-shadow: 0 0 0 1px #80650a, 1px 2px 4px #0008; } +.asset.prov.x2 { background-color: #ffe293; border-color: #fffdad #dec173 #dec173 #fffdad; box-shadow: 0 0 0 1px #80650a, 1px 2px 4px #0008; } +.asset.prov.x3 { background-color: #ffcd66; border-color: #ffee88 #dead43 #dead43 #ffee88; box-shadow: 0 0 0 1px #7f5200, 1px 2px 4px #0008; } +.asset.prov.x4 { background-color: #ffcd66; border-color: #ffee88 #dead43 #dead43 #ffee88; box-shadow: 0 0 0 1px #7f5200, 1px 2px 4px #0008; } +.asset.ship.x1 { background-color: #79b7e4; border-color: #98d7ff #5a98c3 #5a98c3 #98d7ff; box-shadow: 0 0 0 1px #004066, 1px 2px 4px #0008; } +.asset.ship.x2 { background-color: #79b7e4; border-color: #98d7ff #5a98c3 #5a98c3 #98d7ff; box-shadow: 0 0 0 1px #004066, 1px 2px 4px #0008; } +.asset.ship.x4 { background-color: #5da9dd; border-color: #7dc9ff #3d8abc #3d8abc #7dc9ff; box-shadow: 0 0 0 1px #00335f, 1px 2px 4px #0008; } +.asset.loot.x1 { background-color: #f0b64f; border-color: #ffd771 #cf9628 #cf9628 #ffd771; box-shadow: 0 0 0 1px #703d00, 1px 2px 4px #0008; } +.asset.loot.x2 { background-color: #eda44c; border-color: #ffc46e #cb8526 #cb8526 #ffc46e; box-shadow: 0 0 0 1px #6c2c00, 1px 2px 4px #0008; } +.asset.loot.x3 { background-color: #eb924a; border-color: #ffb26b #c97326 #c97326 #ffb26b; box-shadow: 0 0 0 1px #681800, 1px 2px 4px #0008; } +.asset.loot.x4 { background-color: #e1884a; border-color: #ffa86a #bf6928 #bf6928 #ffa86a; box-shadow: 0 0 0 1px #5f0c00, 1px 2px 4px #0008; } +.marker.battle { background-color: #d0bf7d; border-color: #f1e09c #b09f5e #b09f5e #f1e09c; box-shadow: 0 0 0 1px #574700, 1px 2px 4px #0008; } +.marker.storm { background-color: #d0bf7d; border-color: #f1e09c #b09f5e #b09f5e #f1e09c; box-shadow: 0 0 0 1px #574700, 1px 2px 4px #0008; } +.marker.pursuit { background-color: #c6ab7f; border-color: #e7cb9e #a68c61 #a68c61 #e7cb9e; box-shadow: 0 0 0 1px #4e370a, 1px 2px 4px #0008; } +.marker.turn.campaign { background-color: #6a8aa8; border-color: #88a9c8 #4d6c89 #4d6c89 #88a9c8; box-shadow: 0 0 0 1px #001c34, 1px 2px 4px #0008; } +.marker.turn.levy { background-color: #967348; border-color: #b59165 #78562b #78562b #b59165; box-shadow: 0 0 0 1px #240800, 1px 2px 4px #0008; } +.marker.teutonic.victory { background-color: #ffd400; border-color: #fff64b #deb300 #deb300 #fff64b; box-shadow: 0 0 0 1px #805600, 1px 2px 4px #0008; } +.marker.teutonic.siege { background-color: #a39382; border-color: #c3b2a0 #857565 #857565 #c3b2a0; box-shadow: 0 0 0 1px #312416, 1px 2px 4px #0008; } +.marker.russian.conquered { background-color: #649655; border-color: #82b573 #477838 #477838 #82b573; box-shadow: 0 0 0 1px #002500, 1px 2px 4px #0008; } +.marker.teutonic.enemy_lords_removed { background-color: #ffd400; border-color: #fff64b #deb300 #deb300 #fff64b; box-shadow: 0 0 0 1px #805600, 1px 2px 4px #0008; } +.marker.russian.victory { background-color: #2d8b47; border-color: #4faa64 #006d2a #006d2a #4faa64; box-shadow: 0 0 0 1px #001a00, 1px 2px 4px #0008; } +.marker.walls { background-color: #e3dedc; border-color: #fffefc #c3bebc #c3bebc #fffefc; box-shadow: 0 0 0 1px #686362, 1px 2px 4px #0008; } +.marker.russian.number { background-color: #c6992f; border-color: #e7b954 #a67a00 #a67a00 #e7b954; box-shadow: 0 0 0 1px #4d2400, 1px 2px 4px #0008; } +.marker.teutonic.number { background-color: #a02532; border-color: #c2474e #7e0017 #7e0017 #c2474e; box-shadow: 0 0 0 1px #1a0000, 1px 2px 4px #0008; } +.marker.moved_fought { background-color: #0072bc; border-color: #3491dd #00549c #00549c #3491dd; box-shadow: 0 0 0 1px #00003f, 1px 2px 4px #0008; } +.marker.feed_x2 { background-color: #0072bc; border-color: #3491dd #00549c #00549c #3491dd; box-shadow: 0 0 0 1px #00003f, 1px 2px 4px #0008; } + +/* CARD IMAGES */ + +.card.teutonic.aow_back{background-image:url(cards.1x/aow_teutonic_back.jpg)} +.card.teutonic.cc_back{background-image:url(cards.1x/cc_teutonic_back.jpg)} +.card.teutonic.cc_pass{background-image:url(cards.1x/cc_teutonic_pass.jpg)} +.card.russian.aow_back{background-image:url(cards.1x/aow_russian_back.jpg)} +.card.russian.cc_back{background-image:url(cards.1x/cc_russian_back.jpg)} +.card.russian.cc_pass{background-image:url(cards.1x/cc_russian_pass.jpg)} +.card.aow_0{background-image:url(cards.1x/aow_teutonic_01.jpg)} +.card.aow_1{background-image:url(cards.1x/aow_teutonic_02.jpg)} +.card.aow_2{background-image:url(cards.1x/aow_teutonic_03.jpg)} +.card.aow_3{background-image:url(cards.1x/aow_teutonic_04.jpg)} +.card.aow_4{background-image:url(cards.1x/aow_teutonic_05.jpg)} +.card.aow_5{background-image:url(cards.1x/aow_teutonic_06.jpg)} +.card.aow_6{background-image:url(cards.1x/aow_teutonic_07.jpg)} +.card.aow_7{background-image:url(cards.1x/aow_teutonic_08.jpg)} +.card.aow_8{background-image:url(cards.1x/aow_teutonic_09.jpg)} +.card.aow_9{background-image:url(cards.1x/aow_teutonic_10.jpg)} +.card.aow_10{background-image:url(cards.1x/aow_teutonic_11.jpg)} +.card.aow_11{background-image:url(cards.1x/aow_teutonic_12.jpg)} +.card.aow_12{background-image:url(cards.1x/aow_teutonic_13.jpg)} +.card.aow_13{background-image:url(cards.1x/aow_teutonic_14.jpg)} +.card.aow_14{background-image:url(cards.1x/aow_teutonic_15.jpg)} +.card.aow_15{background-image:url(cards.1x/aow_teutonic_16.jpg)} +.card.aow_16{background-image:url(cards.1x/aow_teutonic_17.jpg)} +.card.aow_17{background-image:url(cards.1x/aow_teutonic_18.jpg)} +.card.aow_18{background-image:url(cards.1x/aow_teutonic_none.jpg)} +.card.aow_19{background-image:url(cards.1x/aow_teutonic_none.jpg)} +.card.aow_20{background-image:url(cards.1x/aow_teutonic_none.jpg)} +.card.aow_21{background-image:url(cards.1x/aow_russian_01.jpg)} +.card.aow_22{background-image:url(cards.1x/aow_russian_02.jpg)} +.card.aow_23{background-image:url(cards.1x/aow_russian_03.jpg)} +.card.aow_24{background-image:url(cards.1x/aow_russian_04.jpg)} +.card.aow_25{background-image:url(cards.1x/aow_russian_05.jpg)} +.card.aow_26{background-image:url(cards.1x/aow_russian_06.jpg)} +.card.aow_27{background-image:url(cards.1x/aow_russian_07.jpg)} +.card.aow_28{background-image:url(cards.1x/aow_russian_08.jpg)} +.card.aow_29{background-image:url(cards.1x/aow_russian_09.jpg)} +.card.aow_30{background-image:url(cards.1x/aow_russian_10.jpg)} +.card.aow_31{background-image:url(cards.1x/aow_russian_11.jpg)} +.card.aow_32{background-image:url(cards.1x/aow_russian_12.jpg)} +.card.aow_33{background-image:url(cards.1x/aow_russian_13.jpg)} +.card.aow_34{background-image:url(cards.1x/aow_russian_14.jpg)} +.card.aow_35{background-image:url(cards.1x/aow_russian_15.jpg)} +.card.aow_36{background-image:url(cards.1x/aow_russian_16.jpg)} +.card.aow_37{background-image:url(cards.1x/aow_russian_17.jpg)} +.card.aow_38{background-image:url(cards.1x/aow_russian_18.jpg)} +.card.aow_39{background-image:url(cards.1x/aow_russian_none.jpg)} +.card.aow_40{background-image:url(cards.1x/aow_russian_none.jpg)} +.card.aow_41{background-image:url(cards.1x/aow_russian_none.jpg)} +.card.cc_lord_0{background-image:url(cards.1x/cc_teutonic_andreas.jpg)} +.card.cc_lord_1{background-image:url(cards.1x/cc_teutonic_heinrich.jpg)} +.card.cc_lord_2{background-image:url(cards.1x/cc_teutonic_hermann.jpg)} +.card.cc_lord_3{background-image:url(cards.1x/cc_teutonic_knud_and_abel.jpg)} +.card.cc_lord_4{background-image:url(cards.1x/cc_teutonic_rudolf.jpg)} +.card.cc_lord_5{background-image:url(cards.1x/cc_teutonic_yaroslav.jpg)} +.card.cc_lord_6{background-image:url(cards.1x/cc_russian_aleksandr.jpg)} +.card.cc_lord_7{background-image:url(cards.1x/cc_russian_andrey.jpg)} +.card.cc_lord_8{background-image:url(cards.1x/cc_russian_domash.jpg)} +.card.cc_lord_9{background-image:url(cards.1x/cc_russian_gavrilo.jpg)} +.card.cc_lord_10{background-image:url(cards.1x/cc_russian_karelians.jpg)} +.card.cc_lord_11{background-image:url(cards.1x/cc_russian_vladislav.jpg)} + +@media (min-resolution: 97dpi) { +.card.teutonic.aow_back{background-image:url(cards.2x/aow_teutonic_back.jpg)} +.card.teutonic.cc_back{background-image:url(cards.2x/cc_teutonic_back.jpg)} +.card.teutonic.cc_pass{background-image:url(cards.2x/cc_teutonic_pass.jpg)} +.card.russian.aow_back{background-image:url(cards.2x/aow_russian_back.jpg)} +.card.russian.cc_back{background-image:url(cards.2x/cc_russian_back.jpg)} +.card.russian.cc_pass{background-image:url(cards.2x/cc_russian_pass.jpg)} +.card.aow_0{background-image:url(cards.2x/aow_teutonic_01.jpg)} +.card.aow_1{background-image:url(cards.2x/aow_teutonic_02.jpg)} +.card.aow_2{background-image:url(cards.2x/aow_teutonic_03.jpg)} +.card.aow_3{background-image:url(cards.2x/aow_teutonic_04.jpg)} +.card.aow_4{background-image:url(cards.2x/aow_teutonic_05.jpg)} +.card.aow_5{background-image:url(cards.2x/aow_teutonic_06.jpg)} +.card.aow_6{background-image:url(cards.2x/aow_teutonic_07.jpg)} +.card.aow_7{background-image:url(cards.2x/aow_teutonic_08.jpg)} +.card.aow_8{background-image:url(cards.2x/aow_teutonic_09.jpg)} +.card.aow_9{background-image:url(cards.2x/aow_teutonic_10.jpg)} +.card.aow_10{background-image:url(cards.2x/aow_teutonic_11.jpg)} +.card.aow_11{background-image:url(cards.2x/aow_teutonic_12.jpg)} +.card.aow_12{background-image:url(cards.2x/aow_teutonic_13.jpg)} +.card.aow_13{background-image:url(cards.2x/aow_teutonic_14.jpg)} +.card.aow_14{background-image:url(cards.2x/aow_teutonic_15.jpg)} +.card.aow_15{background-image:url(cards.2x/aow_teutonic_16.jpg)} +.card.aow_16{background-image:url(cards.2x/aow_teutonic_17.jpg)} +.card.aow_17{background-image:url(cards.2x/aow_teutonic_18.jpg)} +.card.aow_18{background-image:url(cards.2x/aow_teutonic_none.jpg)} +.card.aow_19{background-image:url(cards.2x/aow_teutonic_none.jpg)} +.card.aow_20{background-image:url(cards.2x/aow_teutonic_none.jpg)} +.card.aow_21{background-image:url(cards.2x/aow_russian_01.jpg)} +.card.aow_22{background-image:url(cards.2x/aow_russian_02.jpg)} +.card.aow_23{background-image:url(cards.2x/aow_russian_03.jpg)} +.card.aow_24{background-image:url(cards.2x/aow_russian_04.jpg)} +.card.aow_25{background-image:url(cards.2x/aow_russian_05.jpg)} +.card.aow_26{background-image:url(cards.2x/aow_russian_06.jpg)} +.card.aow_27{background-image:url(cards.2x/aow_russian_07.jpg)} +.card.aow_28{background-image:url(cards.2x/aow_russian_08.jpg)} +.card.aow_29{background-image:url(cards.2x/aow_russian_09.jpg)} +.card.aow_30{background-image:url(cards.2x/aow_russian_10.jpg)} +.card.aow_31{background-image:url(cards.2x/aow_russian_11.jpg)} +.card.aow_32{background-image:url(cards.2x/aow_russian_12.jpg)} +.card.aow_33{background-image:url(cards.2x/aow_russian_13.jpg)} +.card.aow_34{background-image:url(cards.2x/aow_russian_14.jpg)} +.card.aow_35{background-image:url(cards.2x/aow_russian_15.jpg)} +.card.aow_36{background-image:url(cards.2x/aow_russian_16.jpg)} +.card.aow_37{background-image:url(cards.2x/aow_russian_17.jpg)} +.card.aow_38{background-image:url(cards.2x/aow_russian_18.jpg)} +.card.aow_39{background-image:url(cards.2x/aow_russian_none.jpg)} +.card.aow_40{background-image:url(cards.2x/aow_russian_none.jpg)} +.card.aow_41{background-image:url(cards.2x/aow_russian_none.jpg)} +.card.cc_lord_0{background-image:url(cards.2x/cc_teutonic_andreas.jpg)} +.card.cc_lord_1{background-image:url(cards.2x/cc_teutonic_heinrich.jpg)} +.card.cc_lord_2{background-image:url(cards.2x/cc_teutonic_hermann.jpg)} +.card.cc_lord_3{background-image:url(cards.2x/cc_teutonic_knud_and_abel.jpg)} +.card.cc_lord_4{background-image:url(cards.2x/cc_teutonic_rudolf.jpg)} +.card.cc_lord_5{background-image:url(cards.2x/cc_teutonic_yaroslav.jpg)} +.card.cc_lord_6{background-image:url(cards.2x/cc_russian_aleksandr.jpg)} +.card.cc_lord_7{background-image:url(cards.2x/cc_russian_andrey.jpg)} +.card.cc_lord_8{background-image:url(cards.2x/cc_russian_domash.jpg)} +.card.cc_lord_9{background-image:url(cards.2x/cc_russian_gavrilo.jpg)} +.card.cc_lord_10{background-image:url(cards.2x/cc_russian_karelians.jpg)} +.card.cc_lord_11{background-image:url(cards.2x/cc_russian_vladislav.jpg)} +} + +@media (min-width: 2800px) { + main { display: grid; gap: 0 12px; } + main { grid-template-columns: 0 1275px auto 0; } + #sec_map { grid-row: 1; grid-column: 2 } + #sec_battle { grid-row: 2; grid-column: 2 } + #sec_lords { grid-row: 1 / 3; grid-column: 3 } +} + +@media (min-width: 3200px) { + main { grid-template-columns: 36px 1275px auto 0; } +} + +@media (min-width: 4100px) { + main { grid-template-columns: 0 1275px 1fr 1275px 0; } + #sec_map { grid-row: 1; grid-column: 2 } + #sec_lords { grid-row: 1; grid-column: 3 } + #sec_battle { grid-row: 1; grid-column: 4 } +} diff --git a/play.html b/play.html new file mode 100644 index 0000000..cb1fd2b --- /dev/null +++ b/play.html @@ -0,0 +1,182 @@ +<!DOCTYPE html> +<!-- vim:set nowrap: --> +<html lang="en"> +<head> +<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1"> +<meta charset="utf-8"> +<title>NEVSKY</title> +<link rel="icon" href="favicon.png"> +<link rel="stylesheet" href="/fonts/fonts.css"> +<link rel="stylesheet" href="/common/play.css"> +<link rel="stylesheet" href="play.css"> +<script defer src="/common/play.js"></script> +<script defer src="data.js"></script> +<script defer src="play.js"></script> +</head> +<body> + +<header> + <div id="toolbar"> + <div class="menu"> + <div class="menu_title"><img src="/images/cog.svg"></div> + <div class="menu_popup"> + <a class="menu_item" target="_blanK" href="/nevsky/info/rules.html">Rules of Play</a> + <a class="menu_item" target="_blanK" href="/nevsky/info/playbook.html">Background Book</a> + <a class="menu_item" target="_blanK" href="/nevsky/info/pac.html">Reference Sheets</a> + <a class="menu_item" target="_blanK" href="/nevsky/info/cards.html">Arts of War</a> + <a class="menu_item" target="_blanK" href="/nevsky/info/lords.html">Lord Mats</a> + <div class="resign menu_separator"></div> + <div class="resign menu_item" onclick="confirm_resign()">Resign</div> + </div> + </div> + <div class="icon_button" onclick="toggle_pieces()"><img src="/images/earth-africa-europe.svg"></div> + <div class="icon_button" onclick="toggle_log()"><img src="/images/scroll-quill.svg"></div> + </div> + <div id="prompt"></div> + <div id="actions"></div> +</header> + +<aside> + <div id="roles"> + <div class="role" id="role_Teutons"> + <div class="role_name"> + Teutons + <div class="role_held">0 Held</div> + <div class="role_user">-</div> + </div> + </div> + <div class="role" id="role_Russians"> + <div class="role_name"> + Russians + <div class="role_held">0 Held</div> + <div class="role_user">-</div> + </div> + </div> + <div class="card_info"><div id="command" class="card teutonic aow_back"></div></div> + </div> + <div id="log"></div> +</aside> + +<main> + +<!-- MAP, TUCKED, EVENTS --> +<section id="sec_map"> + +<div id="mapwrap"> +<div id="map"> + +<div id="veche"></div> +<div class="box veche_label" id="veche_label_top"></div> +<div class="box veche_label" id="veche_label_bottom"></div> +<div class="veche_border" id="veche_border_w"></div> +<div class="veche_border" id="veche_border_e"></div> +<div class="veche_border" id="veche_border_sw"></div> +<div class="veche_border" id="veche_border_se"></div> + +<div id="locales"></div> +<div id="boxes"></div> + +<div id="pieces"> + <div id="elr1" class="marker circle enemy_lords_removed teutonic hide"></div> + <div id="elr2" class="marker circle enemy_lords_removed russian hide"></div> + <div id="turn" class="hide marker circle turn levy t1"></div> + <div id="vp2" class="hide marker circle victory russian v0 stack"></div> + <div id="vp1" class="hide marker circle victory teutonic v0 stack"></div> + <div id="legate" class="hide"></div> + <div id="smerdi" class="box"></div> + <div id="castle11" class="hide marker rectangle castle teutonic"/></div> + <div id="castle12" class="hide marker rectangle castle teutonic"/></div> + <div id="castle21" class="hide marker rectangle castle russian"/></div> + <div id="castle22" class="hide marker rectangle castle russian"/></div> +</div> + +</div> +</div> + +<div class="tuck_under_map"> +<div id="capabilities1"></div> +<div id="capabilities2"></div> +</div> + +<div id="events_panel" class="panel hide"> +<div id="events_header" class="panel_header">Events</div> +<div id="events" class="panel_body"></div> +</div> +</div> + +</section> + +<!-- BATTLE / ARTS OF WAR --> +<section id="sec_battle"> + +<div id="arts_of_war_panel" class="panel hide"> +<div id="arts_of_war_header" class="panel_header">Arts of War</div> +<div id="arts_of_war" class="panel_body"></div> +</div> + +<div id="battle_panel" class="panel hide"> +<div id="battle_header" class="panel_header">Battle</div> +<div id="battle_grid"> + <div id="grid_ga"> + <div id="pursuit" class="marker rectangle pursuit russian hide"></div> + <div id="battle_walls1" class="siegeworks"></div> + <div id="battle_walls2" class="siegeworks"></div> + <div id="garrison"></div> + <div id="battle_walls3" class="siegeworks"></div> + <div id="battle_walls4" class="siegeworks"></div> + </div> + <div class="grid_array att" id="grid_a1"></div> + <div class="grid_array att" id="grid_a2"></div> + <div class="grid_array att" id="grid_a3"></div> + <div class="grid_array def" id="grid_d1"></div> + <div class="grid_array def" id="grid_d2"></div> + <div class="grid_array def" id="grid_d3"></div> + <div class="grid_array att" id="grid_rg1"></div> + <div class="grid_array att" id="grid_rg2"></div> + <div class="grid_array att" id="grid_rg3"></div> + <div class="siegeworks" id="grid_sw"></div> + <div class="grid_array def" id="grid_sa1"></div> + <div class="grid_array def" id="grid_sa2"></div> + <div class="grid_array def" id="grid_sa3"></div> +</div> +</div> + +<div id="reserves_panel" class="panel hide"> +<div id="reserves_header" class="panel_header">Reserves</div> +<div id="reserves" class="panel_body"></div> +</div> + +</section> + +<!-- LORD MATS, HAND, PLAN --> +<section id="sec_lords"> + +<div class="panel court_panel"> +<div id="court1_header" class="panel_header">Lords</div> +<div id="court1" class="panel_body court_body"></div> +</div> + +<div id="hand_panel" class="panel hide"> +<div id="hand_header" class="panel_header">Hand</div> +<div id="hand" class="panel_body"></div> +</div> +</div> + +<div id="plan_panel" class="panel hide"> +<div id="plan_header" class="panel_header">Plan</div> +<div id="plan" class="panel_body"></div> +<div id="plan_actions" class="panel_body"></div> +</div> + +<div class="panel court_panel"> +<div id="court2_header" class="panel_header">Lords</div> +<div id="court2" class="panel_body court_body"></div> +</div> + +</section> + +</main> + +<footer id="status"></footer> + +</body> @@ -0,0 +1,1943 @@ +"use strict" + +// TODO: show strikers and targets highlighting on battle mat? + +function toggle_pieces() { + document.getElementById("pieces").classList.toggle("hide") +} + +// === COMMON LIBRARY === + +function map_has(map, key) { + let a = 0 + let b = (map.length >> 1) - 1 + while (a <= b) { + let m = (a + b) >> 1 + let x = map[m<<1] + if (key < x) + b = m - 1 + else if (key > x) + a = m + 1 + else + return true + } + return false +} + +function map_get(map, key, missing) { + let a = 0 + let b = (map.length >> 1) - 1 + while (a <= b) { + let m = (a + b) >> 1 + let x = map[m<<1] + if (key < x) + b = m - 1 + else if (key > x) + a = m + 1 + else + return map[(m<<1)+1] + } + return missing +} + +function set_has(set, item) { + let a = 0 + let b = set.length - 1 + while (a <= b) { + let m = (a + b) >> 1 + let x = set[m] + if (item < x) + b = m - 1 + else if (item > x) + a = m + 1 + else + return true + } + return false +} + +// === CONSTANTS (matching those in rules.js) === + +function find_lord(name) { return data.lords.findIndex((x) => x.name === name) } +function find_card(name) { return data.cards.findIndex((x) => x.name === name) } + +const LORD_ANDREAS = find_lord("Andreas") +const LORD_HERMANN = find_lord("Hermann") +const LORD_ALEKSANDR = find_lord("Aleksandr") +const LORD_ANDREY = find_lord("Andrey") +const LORD_KNUD_ABEL = find_lord("Knud & Abel") +const LORD_RUDOLF = find_lord("Rudolf") + +const first_p1_lord = 0 +const last_p1_lord = 5 +const first_p2_lord = 6 +const last_p2_lord = 11 + +const first_p1_card = 0 +const last_p1_card = 20 +const first_p2_card = 21 +const last_p2_card = 41 + +const first_p1_locale = 0 +const last_p1_locale = 23 +const first_p2_locale = 24 +const last_p2_locale = 52 + +const R1 = find_card("R1") +const R11 = find_card("R11") +const R17 = find_card("R17") +const T4 = find_card("T4") +const T10 = find_card("T10") +const T14 = find_card("T14") +const EVENT_RUSSIAN_BRIDGE = R1 +const EVENT_TEUTONIC_BRIDGE = T4 +const EVENT_TEUTONIC_FIELD_ORGAN = T10 +const AOW_TEUTONIC_TREBUCHETS = T14 +const EVENT_RUSSIAN_VALDEMAR = R11 +const EVENT_RUSSIAN_DIETRICH_VON_GRUNINGEN = R17 + +const A1 = 0, A2 = 1, A3 = 2, D1 = 3, D2 = 4, D3 = 5, SA1 = 6, SA2 = 7, SA3 = 8, RG1 = 9, RG2 = 10, RG3 = 11 + +const KNIGHTS = 0, SERGEANTS = 1, LIGHT_HORSE = 2, ASIATIC_HORSE = 3, MEN_AT_ARMS = 4, MILITIA = 5, SERFS = 6 +const force_type_count = 7 +const force_action_name = [ "knights", "sergeants", "light_horse", "asiatic_horse", "men_at_arms", "militia", "serfs" ] +const routed_force_action_name = [ "routed_knights", "routed_sergeants", "routed_light_horse", "routed_asiatic_horse", "routed_men_at_arms", "routed_militia", "routed_serfs" ] + +const COIN = 1 +const asset_type_count = 7 +const asset_action_name = [ "prov", "coin", "loot", "cart", "sled", "boat", "ship" ] +const asset_type_x3 = [ 1, 1, 1, 0, 0, 0, 0 ] + +const VECHE = 100 +const SUMMER = 0, EARLY_WINTER = 1, LATE_WINTER = 2, RASPUTITSA = 3 +const SEASONS = [ null, + SUMMER, SUMMER, EARLY_WINTER, EARLY_WINTER, LATE_WINTER, LATE_WINTER, RASPUTITSA, RASPUTITSA, + SUMMER, SUMMER, EARLY_WINTER, EARLY_WINTER, LATE_WINTER, LATE_WINTER, RASPUTITSA, RASPUTITSA, + null ] + +const VASSAL_READY = 1 +const VASSAL_MUSTERED = 2 +const NOWHERE = -1 +const CALENDAR = 100 +const LEGATE_INDISPOSED = -2 +const LEGATE_ARRIVED = -1 +const GARRISON = 100 + +// === ACTIONS === + +function is_action(action, arg) { + if (arg === undefined) + return !!(view.actions && view.actions[action] === 1) + return !!(view.actions && view.actions[action] && set_has(view.actions[action], arg)) +} + +function on_action(evt) { + if (evt.button === 0) { + if (evt.target.my_id === undefined) { + send_action(evt.target.my_action) + if (evt.target.my_action_2) + send_action(evt.target.my_action_2) + } else { + send_action(evt.target.my_action, evt.target.my_id) + if (evt.target.my_action_2) + send_action(evt.target.my_action_2, evt.target.my_id) + } + } +} + +function register_action(elt, action, id, action_2) { + elt.my_id = id + elt.my_action = action + elt.my_action_2 = action_2 + elt.onmousedown = on_action +} + +// === TOOLTIPS === + +function register_tooltip(elt, focus, blur) { + if (typeof focus === "function") + elt.onmouseenter = focus + else + elt.onmouseenter = () => on_focus(focus) + if (blur) + elt.onmouseleave = blur + else + elt.onmouseleave = on_blur +} + +function on_focus(text) { + document.getElementById("status").textContent = text +} + +function on_blur() { + document.getElementById("status").textContent = "" +} + +function get_locale_tip(id) { + let loc = data.locales[id] + let tip = loc.name + if (loc.name !== "Novgorod") { + if (loc.type === "traderoute") + tip += " - Trade Route" + else + tip += " - " + loc.type[0].toUpperCase() + loc.type.substring(1) + } + if (data.seaports.includes(id)) + tip += " - Seaport" + let list = [] + if (loc.name === "Adsel" || loc.name === "Fellin" || loc.name === "Leal" || loc.name === "Wenden") + list.push("Commandery") + if (loc.name === "Novgorod") + list.push("Archbishopric") + for (let lord = 0; lord < data.lords.length; ++lord) { + if (data.lords[lord].seats.includes(id)) + list.push(data.lords[lord].name) + } + if (loc.name === "Pskov") + list.push("Yaroslav") + if (list.length > 0) + tip += " - " + list.join(", ") + return tip +} + +function is_event_in_play(c) { + return set_has(view.events, c) +} + +function on_focus_cylinder(evt) { + let lord = evt.target.my_id + let info = data.lords[lord] + let loc = view.pieces.locale[lord] + let tip = info.name + + if (loc >= CALENDAR) { + if (lord !== LORD_ALEKSANDR) + tip += ` - ${info.fealty} Fealty` + tip += ` - ${info.service} Service` + } + + if (lord === LORD_KNUD_ABEL) + if (is_event_in_play(EVENT_RUSSIAN_VALDEMAR)) + tip += ` - No Muster because of Valdemar!` + if (lord === LORD_ANDREAS || lord === LORD_RUDOLF) + if (is_event_in_play(EVENT_RUSSIAN_DIETRICH_VON_GRUNINGEN)) + tip += ` - No Muster because of Dietrich von Grüningen!` + + on_focus(tip) +} + +function on_focus_lord_service_marker(evt) { + let lord = evt.target.my_id + let info = data.lords[lord] + on_focus(`${info.full_name} - ${info.title}`) + if (expand_calendar !== view.pieces.service[lord]) { + expand_calendar = view.pieces.service[lord] + layout_calendar() + } +} + +function on_blur_lord_service_marker(evt) { + let id = evt.target.my_id + on_blur(evt) + if (expand_calendar === view.pieces.service[id]) { + expand_calendar = -1 + layout_calendar() + } +} + +// === GAME STATE === + +function current_season() { + return SEASONS[view.turn >> 1] +} + +function max_plan_length() { + switch (current_season()) { + case SUMMER: return 6 + case EARLY_WINTER: return 4 + case LATE_WINTER: return 4 + case RASPUTITSA: return 5 + } +} + +function is_p1_lord(lord) { + return lord >= first_p1_lord && lord <= last_p1_lord +} + +function is_p2_lord(lord) { + return lord >= first_p2_lord && lord <= last_p2_lord +} + +function is_lord_besieged(lord) { + let besieged = pack1_get(view.pieces.besieged, lord) + // show sallying lords as not besieged + if (view.battle && view.battle.array && view.battle.reserves.includes(lord)) + return false + return besieged +} + +function is_lord_on_left_or_right(lord) { + if (view.battle.array[A1] === lord) return true + if (view.battle.array[A3] === lord) return true + if (view.battle.array[D1] === lord) return true + if (view.battle.array[D3] === lord) return true + if (view.battle.array[SA1] === lord) return true + if (view.battle.array[SA3] === lord) return true + if (view.battle.array[RG1] === lord) return true + if (view.battle.array[RG3] === lord) return true + return false +} + +function is_lord_ambushed(lord) { + if (view.battle) { + // ambush & 2 = attacker played ambush + // ambush & 1 = defender played ambush + if (view.battle.attacker === "Teutons") { + if ((view.battle.ambush & 1) && is_p1_lord(lord)) + return is_lord_on_left_or_right(lord) + if ((view.battle.ambush & 2) && is_p2_lord(lord)) + return is_lord_on_left_or_right(lord) + } else { + if ((view.battle.ambush & 1) && is_p2_lord(lord)) + return is_lord_on_left_or_right(lord) + if ((view.battle.ambush & 2) && is_p1_lord(lord)) + return is_lord_on_left_or_right(lord) + } + } + return false +} + +function get_lord_moved(lord) { + return pack2_get(view.pieces.moved, lord) +} + +function get_lord_forces(lord, n) { + return pack4_get(view.pieces.forces[lord], n) +} + +function count_lord_all_forces(lord) { + return ( + get_lord_forces(lord, KNIGHTS) + + get_lord_forces(lord, SERGEANTS) + + get_lord_forces(lord, LIGHT_HORSE) + + get_lord_forces(lord, ASIATIC_HORSE) + + get_lord_forces(lord, MEN_AT_ARMS) + + get_lord_forces(lord, MILITIA) + + get_lord_forces(lord, SERFS) + ) +} + +function is_p1_locale(loc) { + return loc >= first_p1_locale && loc <= last_p1_locale +} + +function is_p2_locale(loc) { + return loc >= first_p2_locale && loc <= last_p2_locale +} + +function count_vp1() { + let vp = view.pieces.elr1 << 1 + vp += view.pieces.castles1.length << 1 + for (let loc of view.pieces.conquered) + if (is_p2_locale(loc)) + vp += data.locales[loc].vp << 1 + for (let loc of view.pieces.ravaged) + if (is_p2_locale(loc)) + vp += 1 + return vp +} + +function count_vp2() { + let vp = view.pieces.elr2 << 1 + vp += view.pieces.veche_vp << 1 + vp += view.pieces.castles2.length << 1 + for (let loc of view.pieces.conquered) + if (is_p1_locale(loc)) + vp += data.locales[loc].vp << 1 + for (let loc of view.pieces.ravaged) + if (is_p1_locale(loc)) + vp += 1 + return vp +} + +function get_lord_locale(lord) { + return view.pieces.locale[lord] +} + +function is_lord_on_map(lord) { + let loc = get_lord_locale(lord) + return loc !== NOWHERE && loc < CALENDAR +} + +function is_vassal_ready(vassal) { + return view.pieces.vassals[vassal] === VASSAL_READY +} + +function is_vassal_mustered(vassal) { + return view.pieces.vassals[vassal] === VASSAL_MUSTERED +} + +function is_legate_selected() { + return player === "Teutons" && !!view.pieces.legate_selected +} + +function is_levy_phase() { + return (view.turn & 1) === 0 +} + +function is_upper_lord(lord) { + return map_has(view.pieces.lieutenants, lord) +} + +function is_lower_lord(lord) { + for (let i = 1; i < view.pieces.lieutenants.length; i += 2) + if (view.pieces.lieutenants[i] === lord) + return true + return false +} + +function get_lower_lord(upper) { + return map_get(view.pieces.lieutenants, upper, -1) +} + +function is_lord_in_battle(lord) { + if (view.battle && view.battle.array) { + for (let i = 0; i < 12; ++i) + if (view.battle.array[i] === lord) + return true + if (view.battle.reserves.includes(lord)) + return true + } + return false +} + +function is_lord_command(ix) { + return view.command === ix +} + +function is_lord_selected(ix) { + if (view.who >= 0) + return ix === view.who + if (view.group) + return view.group.includes(ix) + return false +} + +function is_town_locale(loc) { + return data.locales[loc].type === "town" +} + +function has_castle_marker(loc) { + return ( + set_has(view.pieces.castles1, loc) || + set_has(view.pieces.castles2, loc) + ) +} + +function is_castle(loc) { + return data.locales[loc].type === "castle" || has_castle_marker(loc) +} + +function is_bishopric(loc) { + return data.locales[loc].type === "bishopric" +} + +function has_walls(loc) { + return set_has(view.pieces.walls, loc) +} + +function lord_has_unrouted_units(lord) { + return view.pieces.forces[lord] !== 0 +} + +function get_lord_capability(lord, n) { + return view.pieces.capabilities[(lord << 1) + n] +} + +function lord_has_capability_card(lord, c) { + let name = data.cards[c].capability + let c1 = get_lord_capability(lord, 0) + if (c1 >= 0 && data.cards[c1].capability === name) + return true + let c2 = get_lord_capability(lord, 1) + if (c2 >= 0 && data.cards[c2].capability === name) + return true + return false +} + +function lord_has_capability(lord, card_or_list) { + if (Array.isArray(card_or_list)) { + for (let card of card_or_list) + if (lord_has_capability_card(lord, card)) + return true + return false + } + return lord_has_capability_card(lord, card_or_list) +} + +function attacker_has_trebuchets() { + if (view.battle.attacker === "Teutons") { + for (let lord = first_p1_lord; lord <= last_p1_lord; ++lord) { + if (get_lord_locale(lord) === view.battle.where && lord_has_unrouted_units(lord)) { + if (lord_has_capability(lord, AOW_TEUTONIC_TREBUCHETS)) + return true + } + } + } + return false +} + +function count_siege_markers(loc) { + return map_get(view.pieces.sieges, loc, 0) +} + +// === BUILD UI === + +const original_boxes = { + "way crossroads": [ 375, 1179, 116, 37 ], + "way wirz": [ 324, 1132, 44, 88 ], + "way peipus-east": [ 558, 1049, 55, 120 ], + "way peipus-north": [ 513, 958, 90, 57 ], + "calendar summer box1": [ 10, 42, 150, 231 ], + "calendar summer box2": [ 163, 42, 150, 231 ], + "calendar winter box3": [ 328, 42, 150, 231 ], + "calendar winter box4": [ 481, 42, 150, 231 ], + "calendar winter box5": [ 647, 42, 150, 231 ], + "calendar winter box6": [ 799, 42, 150, 231 ], + "calendar rasputitsa box7": [ 965, 42, 150, 231 ], + "calendar rasputitsa box8": [ 1118, 42, 150, 231 ], + "calendar summer box9": [ 10, 280, 150, 231 ], + "calendar summer box10": [ 163, 280, 150, 231 ], + "calendar winter box11": [ 328, 280, 150, 231 ], + "calendar winter box12": [ 481, 280, 150, 231 ], + "calendar winter box13": [ 647, 280, 150, 231 ], + "calendar winter box14": [ 799, 280, 150, 231 ], + "calendar rasputitsa box15": [ 965, 280, 150, 231 ], + "calendar rasputitsa box16": [ 1118, 280, 150, 231 ], + "calendar box0": [ 2, 16, 316, 22 ], + "calendar box17": [ 957, 514, 316, 22 ], +} + +const calendar_xy = [ + [ 10, 2 ], + [ 10, 42 ], + [ 162, 42 ], + [ 328, 42 ], + [ 480, 42 ], + [ 646, 42 ], + [ 799, 42 ], + [ 965, 42 ], + [ 1117, 42 ], + [ 10, 280 ], + [ 162, 280 ], + [ 328, 280 ], + [ 480, 280 ], + [ 646, 280 ], + [ 799, 280 ], + [ 965, 280 ], + [ 1117, 280 ], + [ 1115, 517 ], +] + +const locale_xy = [] + +let expand_calendar = -1 + +const ui = { + locale: [], + locale_name: [], + locale_markers: [], + lord_cylinder: [], + lord_service: [], + lord_mat: [], + lord_buttons: [], + vassal_service: [], + forces: [], + routed: [], + assets: [], + ready_vassals: [], + mustered_vassals: [], + lord_capabilities: [], + lord_events: [], + lord_moved1: [], + lord_moved2: [], + lord_feed_x2: [], + cards: [], + boxes: {}, + ways: [], + smerdi: document.getElementById("smerdi"), + legate: document.getElementById("legate"), + veche: document.getElementById("veche"), + + plan_panel: document.getElementById("plan_panel"), + plan: document.getElementById("plan"), + plan_actions: document.getElementById("plan_actions"), + plan_cards: [], + plan_action_cards: [], + + arts_of_war_panel: document.getElementById("arts_of_war_panel"), + arts_of_war: document.getElementById("arts_of_war"), + + reserves_panel: document.getElementById("reserves_panel"), + reserves: document.getElementById("reserves"), + + events_panel: document.getElementById("events_panel"), + events: document.getElementById("events"), + + hand_panel: document.getElementById("hand_panel"), + hand: document.getElementById("hand"), + + held1: document.querySelector("#role_Teutons .role_held"), + held2: document.querySelector("#role_Russians .role_held"), + + capabilities1: document.getElementById("capabilities1"), + capabilities2: document.getElementById("capabilities2"), + command: document.getElementById("command"), + turn: document.getElementById("turn"), + elr1: document.getElementById("elr1"), + elr2: document.getElementById("elr2"), + vp1: document.getElementById("vp1"), + vp2: document.getElementById("vp2"), + court1_header: document.getElementById("court1_header"), + court2_header: document.getElementById("court2_header"), + court1: document.getElementById("court1"), + court2: document.getElementById("court2"), + garrison: document.getElementById("garrison"), + battle_walls: [ + document.getElementById("battle_walls1"), + document.getElementById("battle_walls2"), + document.getElementById("battle_walls3"), + document.getElementById("battle_walls4"), + ], + battle_siegeworks: document.getElementById("grid_sw"), + battle_panel: document.getElementById("battle_panel"), + battle_header: document.getElementById("battle_header"), + pursuit: document.getElementById("pursuit"), + battle_grid: document.getElementById("battle_grid"), + battle_grid_array: [ + document.getElementById("grid_a1"), + document.getElementById("grid_a2"), + document.getElementById("grid_a3"), + document.getElementById("grid_d1"), + document.getElementById("grid_d2"), + document.getElementById("grid_d3"), + document.getElementById("grid_sa1"), + document.getElementById("grid_sa2"), + document.getElementById("grid_sa3"), + document.getElementById("grid_rg1"), + document.getElementById("grid_rg2"), + document.getElementById("grid_rg3"), + ], + castles: [ + document.getElementById("castle11"), + document.getElementById("castle12"), + document.getElementById("castle21"), + document.getElementById("castle22"), + ], +} + +let locale_layout = [] +let calendar_layout_service = [] +let calendar_layout_cylinder = [] + +function clean_name(name) { + return name.toLowerCase().replaceAll("&", "and").replaceAll(" ", "_") +} + +function build_div(parent, className) { + let e = document.createElement("div") + e.className = className + if (parent) + parent.appendChild(e) + return e +} + +function build_lord_mat(lord, ix, side, name) { + let mat = build_div(null, `mat ${side} ${name}`) + let bg = build_div(mat, "background") + ui.forces[ix] = build_div(bg, "forces") + ui.routed[ix] = build_div(bg, "routed") + ui.assets[ix] = build_div(bg, "assets") + ui.ready_vassals[ix] = build_div(bg, "ready_vassals") + ui.mustered_vassals[ix] = build_div(bg, "mustered_vassals") + ui.lord_buttons[ix] = build_div(bg, "shield") + ui.lord_capabilities[ix] = build_div(mat, "capabilities") + ui.lord_events[ix] = build_div(mat, "events") + ui.lord_moved1[ix] = build_div(mat, "marker square moved_fought one hide") + ui.lord_moved2[ix] = build_div(mat, "marker square moved_fought two hide") + ui.lord_feed_x2[ix] = build_div(mat, "marker small feed_x2") + ui.lord_mat[ix] = mat + register_action(ui.lord_buttons[ix], "lord", ix) +} + +function build_card(side, c) { + let card = ui.cards[c] = document.createElement("div") + card.className = `card ${side} aow_${c}` + register_action(card, "card", c) +} + +function build_plan() { + let elt + for (let i = 0; i < 6; ++i) { + elt = document.createElement("div") + elt.className = "hide" + ui.plan_cards.push(elt) + ui.plan.appendChild(elt) + } + for (let lord = 0; lord < 12; ++lord) { + let side = lord < 6 ? "teutonic" : "russian" + elt = document.createElement("div") + elt.className = `card ${side} cc_lord_${lord}` + register_action(elt, "plan", lord) + ui.plan_action_cards.push(elt) + ui.plan_actions.appendChild(elt) + } + + ui.plan_action_pass_p1 = elt = document.createElement("div") + elt.className = `card teutonic cc_pass` + register_action(elt, "plan", -1) + ui.plan_actions.appendChild(elt) + + ui.plan_action_pass_p2 = elt = document.createElement("div") + elt.className = `card russian cc_pass` + register_action(elt, "plan", -1) + ui.plan_actions.appendChild(elt) +} + +function build_way(name, sel) { + let way = data.ways.findIndex(w => w.name === name) + ui.ways[way] = document.querySelector(sel) + register_action(ui.ways[way], "way", way) +} + +const locale_size = { + region: [ 88, 56 ], + town: [ 80, 72 ], + traderoute: [ 90, 54 ], + fort: [ 96, 54 ], + castle: [ 96, 56 ], + city: [ 126, 80 ], + bishopric: [ 106, 72 ], + novgorod: [ 144, 86 ], +} + +function build_map() { + for (let i = 0; i < data.locales.length; ++i) + locale_layout[i] = [] + + data.locales.forEach((locale, ix) => { + let region = clean_name(locale.region) + let { x, y, w, h } = locale.box + let xc = Math.round(x + w / 2) + let yc = Math.round(y + h / 2) + let e + + switch (locale.type) { + case "town": + locale_xy[ix] = [ xc, y - 24 ] + w = locale_size.town[0] + h = locale_size.town[1] + x = xc - w/2 + y = y - h + 16 + break + case "region": + xc += 2 + yc -= 3 + locale_xy[ix] = [ xc, yc - 24 ] + w = locale_size.region[0] + h = locale_size.region[1] + x = xc - w/2 + y = yc - h/2 + break + default: + locale_xy[ix] = [ xc, y - 36 ] + break + } + + // Main Area + e = ui.locale[ix] = document.createElement("div") + e.className = "locale " + locale.type + " " + region + if (locale.type !== "region" && locale.type !== "town") { + let ew = locale_size[locale.type][0] + let eh = locale_size[locale.type][1] + e.style.top = (y - eh) + "px" + e.style.left = (xc - ew/2) + "px" + e.style.width = (ew) + "px" + e.style.height = (eh) + "px" + } else { + e.style.left = x + "px" + e.style.top = y + "px" + e.style.width = w + "px" + e.style.height = h + "px" + } + register_action(e, "locale", ix, "laden_march") + register_tooltip(e, get_locale_tip(ix)) + document.getElementById("locales").appendChild(e) + + // Name Plate + if (locale.type !== 'region' && locale.type !== 'town') { + e = ui.locale_name[ix] = document.createElement("div") + e.className = "locale_name " + locale.type + " " + region + e.style.left = x + "px" + e.style.top = y + "px" + e.style.width = w + "px" + e.style.height = h + "px" + register_action(e, "locale", ix, "laden_march") + register_tooltip(e, get_locale_tip(ix)) + document.getElementById("locales").appendChild(e) + } + + // Locale Markers + e = ui.locale_markers[ix] = document.createElement("div") + e.className = "locale_markers " + locale.type + " " + region + x = locale_xy[ix][0] - 196/2 + y = locale_xy[ix][1] + 36 + e.style.top = y + "px" + e.style.left = x + "px" + e.style.width = 196 + "px" + document.getElementById("pieces").appendChild(e) + }) + + data.lords.forEach((lord, ix) => { + let e = ui.lord_cylinder[ix] = document.createElement("div") + e.className = "cylinder lord " + clean_name(lord.side) + " " + clean_name(lord.name) + " hide" + register_action(e, "lord", ix) + register_tooltip(e, on_focus_cylinder) + document.getElementById("pieces").appendChild(e) + + e = ui.lord_service[ix] = document.createElement("div") + e.className = "service_marker lord image" + lord.image + " " + clean_name(lord.side) + " " + clean_name(lord.name) + " hide" + register_action(e, "service", ix, "service_bad") + register_tooltip(e, on_focus_lord_service_marker, on_blur_lord_service_marker) + document.getElementById("pieces").appendChild(e) + + build_lord_mat(lord, ix, clean_name(lord.side), clean_name(lord.name)) + }) + + data.vassals.forEach((vassal, ix) => { + let lord = data.lords[vassal.lord] + let e = ui.vassal_service[ix] = document.createElement("div") + e.className = "service_marker vassal image" + vassal.image + " " + clean_name(lord.side) + " " + clean_name(vassal.name) + " hide" + register_action(e, "vassal", ix) + register_tooltip(e, data.vassals[ix].name) + document.getElementById("pieces").appendChild(e) + }) + + register_action(ui.legate, "legate") + register_tooltip(ui.legate, "William of Modena") + + register_action(ui.veche, "veche") + + for (let name in original_boxes) { + let x = original_boxes[name][0] + let y = original_boxes[name][1] + let w = original_boxes[name][2] - 8 + let h = original_boxes[name][3] - 8 + let e = ui.boxes[name] = document.createElement("div") + e.className = "box " + name + e.style.left = x + "px" + e.style.top = y + "px" + e.style.width = w + "px" + e.style.height = h + "px" + document.getElementById("boxes").appendChild(e) + } + + ui.calendar = [ + document.querySelector(".calendar.box0"), + document.querySelector(".calendar.box1"), + document.querySelector(".calendar.box2"), + document.querySelector(".calendar.box3"), + document.querySelector(".calendar.box4"), + document.querySelector(".calendar.box5"), + document.querySelector(".calendar.box6"), + document.querySelector(".calendar.box7"), + document.querySelector(".calendar.box8"), + document.querySelector(".calendar.box9"), + document.querySelector(".calendar.box10"), + document.querySelector(".calendar.box11"), + document.querySelector(".calendar.box12"), + document.querySelector(".calendar.box13"), + document.querySelector(".calendar.box14"), + document.querySelector(".calendar.box15"), + document.querySelector(".calendar.box16"), + document.querySelector(".calendar.box17") + ] + + for (let i = 0; i <= 17; ++i) + register_action(ui.calendar[i], "calendar", i) + + build_way("Crossroads", ".way.crossroads") + build_way("Peipus E", ".way.peipus-east") + build_way("Peipus W", ".way.peipus-north") + build_way("Wirz", ".way.wirz") + + build_plan() + + register_action(ui.garrison, "garrison") + for (let i = 0; i < 12; ++i) + register_action(ui.battle_grid_array[i], "array", i) + + for (let c = first_p1_card; c <= last_p1_card; ++c) + build_card("teutonic", c) + for (let c = first_p2_card; c <= last_p2_card; ++c) + build_card("russian", c) +} + +// === UPDATE UI === + +let used_cache = {} +let unused_cache = {} + +function get_cached_element(className, action, id) { + let key = className + if (action !== undefined) + key += "/" + action + "/" + id + if (!(key in unused_cache)) { + unused_cache[key] = [] + used_cache[key] = [] + } + if (unused_cache[key].length > 0) { + let elt = unused_cache[key].pop() + used_cache[key].push(elt) + return elt + } + let elt = document.createElement("div") + elt.className = className + used_cache[key].push(elt) + if (action !== undefined) + register_action(elt, action, id) + return elt +} + +function restart_cache() { + for (let k in used_cache) { + let u = used_cache[k] + let uu = unused_cache[k] + while (u.length > 0) + uu.push(u.pop()) + } +} + +function update_current_card_display() { + if (typeof view.what === "number" && view.what >= 0) { + if (view.what <= first_p1_card) + ui.command.className = `card teutonic aow_${view.what}` + else + ui.command.className = `card russian aow_${view.what}` + } else if ((view.turn & 1) === 0) { + if (player === "Russians") + ui.command.className = `card russian aow_back` + else + ui.command.className = `card teutonic aow_back` + } else if (view.command < 0) { + if (player === "Russians") + ui.command.className = `card russian cc_back` + else + ui.command.className = `card teutonic cc_back` + } else { + if (view.command < 6) + ui.command.className = `card russian cc_lord_${view.command}` + else + ui.command.className = `card teutonic cc_lord_${view.command}` + } +} + +function layout_locale_item(loc, e, is_upper) { + locale_layout[loc].push([e, is_upper]) + e.classList.toggle("lieutenant", is_upper) +} + +function layout_locale_cylinders(loc) { + let [xc, yc] = locale_xy[loc] + + let n = 0 + for (let [e,is_upper] of locale_layout[loc]) + if (!is_upper) + ++n + + let wrap = 3 + switch (data.locales[loc].type) { + case "region": wrap = 2; break + case "town": wrap = 2; break + case "novgorod": wrap = 4; break + } + + let m = Math.floor((n-1) / wrap) + let i = 0 + let k = 0 + for (let [e,is_upper] of locale_layout[loc]) { + let nn = n + if (nn > wrap) + nn = wrap + let x = xc + (i - (nn-1)/2) * 44 + k * 22 + let y = yc + (k * 32) - m * 32 + let z = 1 + if (is_upper) { + y -= 18 + z = 2 + } + if (e === ui.legate) { + y -= 16 + z = 3 + } + e.style.top = (y - 23) + "px" + e.style.left = (x - 23) + "px" + e.style.zIndex = z + if (!is_upper) + ++i + if (i >= wrap) { + i = 0 + ++k + } + } +} + +function layout_calendar() { + for (let loc = 0; loc < 18; ++loc) { + let [cx, cy] = calendar_xy[loc] + let list = calendar_layout_service[loc] + for (let i = 0; i < list.length; ++i) { + let e = list[i] + let x = cx, y = cy, z = 60 - i + let d = 46 - 24 + if (loc === expand_calendar) { + d = 46 + z += 100 + } + if (loc === 0) { + x += -6 + 46 * i + z = 1 + } else if (loc === 17) { + x += 60 - 46 * i + z = 60 - i + } else { + x += (146 - 94 - 2) + y += (227 - 46 - 2) - i * d + } + e.style.top = y + "px" + e.style.left = x + "px" + e.style.zIndex = z + } + + list = calendar_layout_cylinder[loc] + for (let i = 0; i < list.length; ++i) { + let e = list[i] + let x = cx, y = cy, z = 61 + if (loc === 0) { + let k = calendar_layout_service[0].length + if (k > 0) + x += k * 46 + 46 + i * 46 + else + x += 0 + i * 46 + } else if (loc === 17) { + let k = calendar_layout_service[17].length + if (k > 0) + x += 60 - k * 46 - i * 46 + else + x += 60 + i * 46 + } else if (loc === 1) { + x += 46 + (i%2) * 46 + (i/2|0) * 12 + y += 66 + (i/2|0) * 36 + } else { + x += 6 + (i%3) * 46 + (i/3|0) * 24 + y += 66 + (i/3|0) * 36 + } + e.style.top = y + "px" + e.style.left = x + "px" + e.style.zIndex = z + } + } +} + +function add_force(parent, type, lord, routed) { + let elt + if (routed) { + if (is_action(routed_force_action_name[type], lord)) + elt = get_cached_element("action unit " + force_action_name[type], routed_force_action_name[type], lord) + else + elt = get_cached_element("unit " + force_action_name[type], routed_force_action_name[type], lord) + } else { + if (is_action(force_action_name[type], lord)) + elt = get_cached_element("action unit " + force_action_name[type], force_action_name[type], lord) + else + elt = get_cached_element("unit " + force_action_name[type], force_action_name[type], lord) + } + parent.appendChild(elt) +} + +function add_asset(parent, type, n, lord) { + let elt + if (lord === VECHE) { + if (is_action("veche_coin")) + elt = get_cached_element("action asset " + asset_action_name[type] + " x"+n, "veche_coin", undefined) + else + elt = get_cached_element("asset " + asset_action_name[type] + " x"+n) + } else { + if (is_action(asset_action_name[type], lord)) + elt = get_cached_element("action asset " + asset_action_name[type] + " x"+n, asset_action_name[type], lord) + else + elt = get_cached_element("asset " + asset_action_name[type] + " x"+n) + } + parent.appendChild(elt) +} + +function add_veche_vp(parent) { + parent.appendChild(get_cached_element("marker square conquered russian")) +} + +function update_forces(parent, forces, lord_ix, routed) { + parent.replaceChildren() + for (let i = 0; i < force_type_count; ++i) { + let n = pack4_get(forces, i) + for (let k = 0; k < n; ++k) { + add_force(parent, i, lord_ix, routed) + } + } +} + +function update_assets(id, parent, assets) { + parent.replaceChildren() + for (let i = 0; i < asset_type_count; ++i) { + let n = pack4_get(assets, i) + while (n >= 4) { + add_asset(parent, i, 4, id) + n -= 4 + } + if (asset_type_x3[i]) { + while (n >= 3) { + add_asset(parent, i, 3, id) + n -= 3 + } + } + while (n >= 2) { + add_asset(parent, i, 2, id) + n -= 2 + } + while (n >= 1) { + add_asset(parent, i, 1, id) + n -= 1 + } + } +} + +function update_vassals(ready_parent, mustered_parent, lord_ix) { + for (let v of data.lords[lord_ix].vassals) { + let e = ui.vassal_service[v] + if (is_vassal_ready(v)) { + e.classList.remove("hide") + ready_parent.appendChild(e) + } + else if (is_vassal_mustered(v)) { + e.classList.remove("hide") + mustered_parent.appendChild(e) + } + else { + e.classList.add("hide") + } + e.classList.toggle("action", is_action("vassal", v)) + } +} + +function update_lord_mat(ix) { + if (view.reveal & (1 << ix)) { + ui.lord_mat[ix].classList.remove("hidden") + update_assets(ix, ui.assets[ix], view.pieces.assets[ix]) + update_vassals(ui.ready_vassals[ix], ui.mustered_vassals[ix], ix) + update_forces(ui.forces[ix], view.pieces.forces[ix], ix, false) + update_forces(ui.routed[ix], view.pieces.routed[ix], ix, true) + ui.lord_feed_x2[ix].classList.toggle("hide", count_lord_all_forces(ix) <= 6) + } else { + ui.lord_mat[ix].classList.add("hidden") + ui.assets[ix].replaceChildren() + ui.ready_vassals[ix].replaceChildren() + ui.mustered_vassals[ix].replaceChildren() + ui.forces[ix].replaceChildren() + ui.routed[ix].replaceChildren() + ui.lord_moved1[ix].classList.add("hide") + ui.lord_moved2[ix].classList.add("hide") + ui.lord_feed_x2[ix].classList.add("hide") + } + let m = get_lord_moved(ix) + ui.lord_moved1[ix].classList.toggle("hide", is_levy_phase() || (m !== 1 && m !== 2)) + ui.lord_moved2[ix].classList.toggle("hide", is_levy_phase() || (m !== 2)) +} + +function update_lord(ix) { + let locale = view.pieces.locale[ix] + let service = view.pieces.service[ix] + if (locale < 0) { + ui.lord_cylinder[ix].classList.add("hide") + ui.lord_service[ix].classList.add("hide") + ui.lord_mat[ix].classList.remove("action") + return + } + if (locale < 100) { + calendar_layout_service[service].push(ui.lord_service[ix]) + + if (!is_lower_lord(ix)) { + if (is_upper_lord(ix)) { + let lo = get_lower_lord(ix) + if (view.pieces.locale[lo] === locale) { + layout_locale_item(locale, ui.lord_cylinder[ix], 1) + layout_locale_item(locale, ui.lord_cylinder[lo], 0) + } else { + layout_locale_item(locale, ui.lord_cylinder[ix], 0) + } + } else { + layout_locale_item(locale, ui.lord_cylinder[ix], 0) + } + } + + ui.lord_cylinder[ix].classList.remove("hide") + ui.lord_service[ix].classList.remove("hide") + update_lord_mat(ix) + } else { + let t = locale - 100 + if (t > 17) t = 17 + calendar_layout_cylinder[t].push(ui.lord_cylinder[ix]) + ui.lord_cylinder[ix].classList.remove("hide") + ui.lord_service[ix].classList.add("hide") + } + ui.lord_cylinder[ix].classList.toggle("besieged", is_lord_besieged(ix)) + ui.lord_buttons[ix].classList.toggle("action", is_action("lord", ix)) + ui.lord_cylinder[ix].classList.toggle("action", is_action("lord", ix)) + ui.lord_service[ix].classList.toggle("action", is_action("service", ix) || is_action("service_bad", ix)) + ui.lord_service[ix].classList.toggle("bad", is_action("service_bad", ix)) + + if (ix === LORD_HERMANN) + ui.lord_cylinder[ix].classList.toggle("marshal", !is_lord_on_map(LORD_ANDREAS)) + if (ix === LORD_ANDREY) + ui.lord_cylinder[ix].classList.toggle("marshal", !is_lord_on_map(LORD_ALEKSANDR)) + + ui.lord_cylinder[ix].classList.toggle("selected", is_lord_selected(ix)) + ui.lord_service[ix].classList.toggle("selected", is_lord_selected(ix)) + ui.lord_mat[ix].classList.toggle("selected", is_lord_selected(ix)) + + ui.lord_cylinder[ix].classList.toggle("command", is_lord_command(ix)) + ui.lord_mat[ix].classList.toggle("command", is_lord_command(ix)) + + ui.lord_mat[ix].classList.toggle("besieged", is_lord_besieged(ix)) + ui.lord_mat[ix].classList.toggle("ambushed", is_lord_ambushed(ix)) +} + +function update_legate() { + if (view.pieces.legate === LEGATE_INDISPOSED) { + ui.legate.classList.add("hide") + } else { + ui.legate.classList.remove("hide") + ui.legate.classList.toggle("action", is_action("legate")) + ui.legate.classList.toggle("selected", is_legate_selected()) + if (view.pieces.legate === LEGATE_ARRIVED) { + ui.legate.style.top = "1356px" + ui.legate.style.left = "24px" + } else { + layout_locale_item(view.pieces.legate, ui.legate, 0) + } + } +} + +function update_smerdi() { + ui.smerdi.replaceChildren() + for (let i = 0; i < view.pieces.smerdi; ++i) + ui.smerdi.appendChild(get_cached_element("unit serfs")) +} + +function update_veche() { + ui.veche.replaceChildren() + + let n = view.pieces.veche_coin + while (n >= 4) { + add_asset(ui.veche, COIN, 4, VECHE) + n -= 4 + } + while (n >= 3) { + add_asset(ui.veche, COIN, 3, VECHE) + n -= 3 + } + while (n >= 2) { + add_asset(ui.veche, COIN, 2, VECHE) + n -= 2 + } + while (n >= 1) { + add_asset(ui.veche, COIN, 1, VECHE) + n -= 1 + } + + for (let i = 0; i < view.pieces.veche_vp; ++i) + add_veche_vp(ui.veche) +} + +function update_castle(elt, loc) { + if (loc === undefined) { + elt.classList.toggle("hide", true) + } else { + elt.classList.toggle("hide", false) + let [xc, yc] = locale_xy[loc] + if (is_town_locale(loc)) { + elt.style.top = (yc - 26) + "px" + elt.style.left = (xc - 49) + "px" + } else { + elt.style.top = (yc - 15) + "px" + elt.style.left = (xc - 49) + "px" + } + elt.style.zIndex = 0 + } +} + +function is_teutonic_siege_marker(loc) { + if (set_has(view.pieces.castles2, loc)) + return true + if (set_has(view.pieces.castles1, loc)) + return false + if (is_p1_locale(loc)) + return set_has(view.pieces.conquered, loc) + else + return !set_has(view.pieces.conquered, loc) +} + +function update_locale(loc) { + layout_locale_cylinders(loc) + + ui.locale[loc].classList.toggle("action", is_action("locale", loc) || is_action("laden_march", loc)) + ui.locale[loc].classList.toggle("laden", is_action("laden_march", loc)) + ui.locale[loc].classList.toggle("supply_path", !!(view.supply && view.supply[0] === loc)) + ui.locale[loc].classList.toggle("supply_source", !!(view.supply && view.supply[1] === loc)) + if (ui.locale_name[loc]) { + ui.locale_name[loc].classList.toggle("action", is_action("locale", loc) || is_action("laden_march", loc)) + } + + ui.locale_markers[loc].replaceChildren() + + if (view.battle && view.battle.where === loc) + if (view.battle.storm) + ui.locale_markers[loc].appendChild(get_cached_element("marker circle storm")) + else + ui.locale_markers[loc].appendChild(get_cached_element("marker circle battle")) + + if (set_has(view.pieces.ravaged, loc)) { + let cn + if (is_p1_locale(loc)) + cn = "marker small ravaged russian" + else + cn = "marker small ravaged teutonic" + ui.locale_markers[loc].appendChild(get_cached_element(cn)) + } + + if (set_has(view.pieces.conquered, loc)) { + let cn + if (is_p1_locale(loc)) + cn = "marker square conquered russian" + else + cn = "marker square conquered teutonic" + for (let i = 0; i < data.locales[loc].vp; ++i) + ui.locale_markers[loc].appendChild(get_cached_element(cn)) + } + + if (set_has(view.pieces.walls, loc)) + ui.locale_markers[loc].appendChild(get_cached_element("marker square walls")) + + let sieges = map_get(view.pieces.sieges, loc) + if (sieges > 0) { + let cn + if (is_teutonic_siege_marker(loc)) + cn = "marker square siege teutonic" + else + cn = "marker square siege russian" + for (let i = 0; i < sieges; ++i) + ui.locale_markers[loc].appendChild(get_cached_element(cn)) + } +} + +function update_plan() { + if (view.plan) { + let is_planning = view.actions && view.actions.plan + + ui.plan_panel.classList.remove("hide") + for (let i = 0; i < 6; ++i) { + if (i < view.plan.length) { + let lord = view.plan[i] + if (lord < 0) { + if (player === "Teutons") + ui.plan_cards[i].className = "card teutonic cc_pass" + else + ui.plan_cards[i].className = "card russian cc_pass" + } else { + if (lord < 6) + ui.plan_cards[i].className = "card teutonic cc_lord_" + lord + else + ui.plan_cards[i].className = "card russian cc_lord_" + lord + } + } else if (is_planning && i < max_plan_length()) { + if (player === "Teutons") + ui.plan_cards[i].className = "card teutonic cc_back" + else + ui.plan_cards[i].className = "card russian cc_back" + } else { + ui.plan_cards[i].className = "hide" + } + } + + if (is_planning) { + ui.plan_actions.classList.remove("hide") + for (let lord = 0; lord < 12; ++lord) { + if (is_action("plan", lord)) { + ui.plan_action_cards[lord].classList.add("action") + ui.plan_action_cards[lord].classList.remove("disabled") + } else { + ui.plan_action_cards[lord].classList.remove("action") + ui.plan_action_cards[lord].classList.add("disabled") + } + } + if (is_action("plan", -1)) { + ui.plan_action_pass_p1.classList.add("action") + ui.plan_action_pass_p1.classList.remove("disabled") + ui.plan_action_pass_p2.classList.add("action") + ui.plan_action_pass_p2.classList.remove("disabled") + } else { + ui.plan_action_pass_p1.classList.remove("action") + ui.plan_action_pass_p1.classList.add("disabled") + ui.plan_action_pass_p2.classList.remove("action") + ui.plan_action_pass_p2.classList.add("disabled") + } + } else { + ui.plan_actions.classList.add("hide") + } + } else { + ui.plan_panel.classList.add("hide") + } +} + +function update_cards() { + for (let c = 0; c < 42; ++c) { + let elt = ui.cards[c] + elt.classList.toggle("selected", c === view.what) + elt.classList.toggle("action", is_action("card", c)) + } + + if (view.arts_of_war) { + ui.arts_of_war_panel.classList.remove("hide") + ui.arts_of_war.replaceChildren() + for (let c of view.arts_of_war) + ui.arts_of_war.appendChild(ui.cards[c]) + } else { + ui.arts_of_war_panel.classList.add("hide") + } + + if (view.events.length > 0) { + ui.events_panel.classList.remove("hide") + ui.events.replaceChildren() + for (let c of view.events) + ui.events.appendChild(ui.cards[c]) + } else { + ui.events_panel.classList.add("hide") + } + + if (view.hand && view.hand.length > 0) { + ui.hand_panel.classList.remove("hide") + ui.hand.replaceChildren() + if (view.hand) { + for (let c of view.hand) + ui.hand.appendChild(ui.cards[c]) + } + } else { + ui.hand_panel.classList.add("hide") + } + + ui.capabilities1.replaceChildren() + for (let i = first_p1_card; i <= last_p1_card; ++i) + if (view.capabilities.includes(i)) + ui.capabilities1.appendChild(ui.cards[i]) + + ui.capabilities2.replaceChildren() + for (let i = first_p2_card; i <= last_p2_card; ++i) + if (view.capabilities.includes(i)) + ui.capabilities2.appendChild(ui.cards[i]) + + for (let ix = 0; ix < data.lords.length; ++ix) { + ui.lord_capabilities[ix].replaceChildren() + ui.lord_events[ix].replaceChildren() + if (view.reveal & (1 << ix)) { + let c = view.pieces.capabilities[(ix << 1) + 0] + if (c >= 0) + ui.lord_capabilities[ix].appendChild(ui.cards[c]) + c = view.pieces.capabilities[(ix << 1) + 1] + if (c >= 0) + ui.lord_capabilities[ix].appendChild(ui.cards[c]) + if (view.battle && view.battle.field_organ === ix) + ui.lord_events[ix].appendChild(ui.cards[EVENT_TEUTONIC_FIELD_ORGAN]) + if (view.battle && view.battle.bridge && view.battle.bridge.lord1 === ix) + ui.lord_events[ix].appendChild(ui.cards[EVENT_RUSSIAN_BRIDGE]) + if (view.battle && view.battle.bridge && view.battle.bridge.lord2 === ix) + ui.lord_events[ix].appendChild(ui.cards[EVENT_TEUTONIC_BRIDGE]) + } + } +} + +function update_battle() { + let array = view.battle.array + + // Pursuit marker points "up" towards the conceding side + if (view.battle.conceded === "Russians") { + if (view.battle.attacker === "Russians") + ui.pursuit.className = "marker rectangle pursuit teutonic" + else + ui.pursuit.className = "marker rectangle pursuit teutonic rotate" + } else if (view.battle.conceded === "Teutons") { + if (view.battle.attacker === "Teutons") + ui.pursuit.className = "marker rectangle pursuit russian" + else + ui.pursuit.className = "marker rectangle pursuit russian rotate" + } else { + ui.pursuit.className = "hide" + } + + for (let i = 0; i < array.length; ++i) { + let lord = array[i] + ui.battle_grid_array[i].replaceChildren() + if (lord >= 0) + ui.battle_grid_array[i].appendChild(ui.lord_mat[lord]) + ui.battle_grid_array[i].classList.toggle("action", is_action("array", i)) + } + + ui.reserves.replaceChildren() + for (let lord of view.battle.reserves) + ui.reserves.appendChild(ui.lord_mat[lord]) + + ui.garrison.classList.toggle("hide", !view.battle.storm) + ui.garrison.classList.toggle("action", is_action("garrison")) + + ui.garrison.replaceChildren() + if (view.battle.garrison) { + for (let i = 0; i < view.battle.garrison.knights; ++i) + add_force(ui.garrison, KNIGHTS, GARRISON, 0) + for (let i = 0; i < view.battle.garrison.men_at_arms; ++i) + add_force(ui.garrison, MEN_AT_ARMS, GARRISON, 0) + } + + let here = view.battle.where + + let def_prot = 0 + let def_walls = 0 + let att_prot = 0 + let sally_prot = 0 + + if (view.battle.storm) { + if (is_bishopric(here) || is_castle(here)) + def_prot = 4 + else + def_prot = 3 + if (attacker_has_trebuchets()) + def_prot-- + if (has_walls(here)) + def_walls++ + att_prot = count_siege_markers(view.battle.where) + } + + if (view.battle.sally) + def_prot = count_siege_markers(view.battle.where) + else if (view.battle.array[SA2] >= 0) + sally_prot = count_siege_markers(view.battle.where) + + let att_ui, def_ui + if (player === view.battle.attacker) { + att_ui = ui.battle_walls[3] + def_ui = ui.battle_walls[2] + } else { + att_ui = ui.battle_walls[0] + def_ui = ui.battle_walls[1] + } + + for (let i = 0; i < 4; ++i) + ui.battle_walls[i].replaceChildren() + + for (let i = 0; i < def_prot; ++i) + if (view.battle.attacker === "Teutons") + def_ui.appendChild(get_cached_element("marker square russian siege")) + else + def_ui.appendChild(get_cached_element("marker square teutonic siege")) + for (let i = 0; i < def_walls; ++i) + def_ui.appendChild(get_cached_element("marker square walls")) + + ui.battle_siegeworks.replaceChildren() + for (let i = 0; i < sally_prot; ++i) + if (view.battle.attacker === "Teutons") + ui.battle_siegeworks.appendChild(get_cached_element("marker square russian siege")) + else + ui.battle_siegeworks.appendChild(get_cached_element("marker square teutonic siege")) + + for (let i = 0; i < att_prot; ++i) + if (view.battle.attacker === "Teutons") + att_ui.appendChild(get_cached_element("marker square teutonic siege")) + else + att_ui.appendChild(get_cached_element("marker square russian siege")) +} + +function update_court() { + let tcourt_hdr = (player === "Russians") ? ui.court2_header : ui.court1_header + let rcourt_hdr = (player === "Russians") ? ui.court1_header : ui.court2_header + tcourt_hdr.textContent = "Teutonic Lords" + rcourt_hdr.textContent = "Russian Lords" + let tcourt = (player === "Russians") ? ui.court2 : ui.court1 + let rcourt = (player === "Russians") ? ui.court1 : ui.court2 + tcourt.replaceChildren() + rcourt.replaceChildren() + for (let lord = 0; lord < 6; ++lord) + if (!is_lord_in_battle(lord) && is_lord_on_map(lord)) + tcourt.appendChild(ui.lord_mat[lord]) + for (let lord = 6; lord < 12; ++lord) + if (!is_lord_in_battle(lord) && is_lord_on_map(lord)) + rcourt.appendChild(ui.lord_mat[lord]) +} + +function on_update() { + restart_cache() + + for (let i = 0; i < 18; ++i) { + calendar_layout_cylinder[i] = [] + calendar_layout_service[i] = [] + } + + for (let i = 0; i < data.locales.length; ++i) + locale_layout[i].length = 0 + + for (let ix = 0; ix < data.lords.length; ++ix) { + if (view.pieces.locale[ix] < 0) { + ui.lord_cylinder[ix].classList.add("hide") + ui.lord_service[ix].classList.add("hide") + } else { + ui.lord_cylinder[ix].classList.remove("hide") + update_lord(ix) + } + } + + for (let way = 0; way < ui.ways.length; ++way) { + if (is_action("way", way)) + ui.ways[way].classList.add("action") + else + ui.ways[way].classList.remove("action") + } + + layout_calendar() + + update_legate() + update_smerdi() + update_veche() + + for (let loc = 0; loc < data.locales.length; ++loc) + update_locale(loc) + + update_castle(ui.castles[0], view.pieces.castles1[0]) + update_castle(ui.castles[1], view.pieces.castles1[1]) + update_castle(ui.castles[2], view.pieces.castles2[0]) + update_castle(ui.castles[3], view.pieces.castles2[1]) + + update_current_card_display() + + if (view.turn & 1) + ui.turn.className = `marker circle turn campaign t${view.turn>>1}` + else + ui.turn.className = `marker circle turn levy t${view.turn>>1}` + + let vp1 = count_vp1() + let vp2 = count_vp2() + if ((vp1 >> 1) === (vp2 >> 1)) { + if (vp1 & 1) + ui.vp1.className = `marker circle victory teutonic stack v${vp1>>1} half` + else + ui.vp1.className = `marker circle victory teutonic stack v${vp1>>1}` + if (vp2 & 1) + ui.vp2.className = `marker circle victory russian stack v${vp2>>1} half` + else + ui.vp2.className = `marker circle victory russian stack v${vp2>>1}` + } else { + if (vp1 & 1) + ui.vp1.className = `marker circle victory teutonic v${vp1>>1} half` + else + ui.vp1.className = `marker circle victory teutonic v${vp1>>1}` + if (vp2 & 1) + ui.vp2.className = `marker circle victory russian v${vp2>>1} half` + else + ui.vp2.className = `marker circle victory russian v${vp2>>1}` + } + + if (view.pieces.elr1) + ui.elr1.classList = `marker circle enemy_lords_removed teutonic v${view.pieces.elr1}` + else + ui.elr1.classList = `marker circle enemy_lords_removed teutonic hide` + if (view.pieces.elr2) + ui.elr2.classList = `marker circle enemy_lords_removed russian v${view.pieces.elr2}` + else + ui.elr2.classList = `marker circle enemy_lords_removed russian hide` + + ui.held1.textContent = `${view.held1} Held` + ui.held2.textContent = `${view.held2} Held` + + update_plan() + update_cards() + + ui.veche.classList.toggle("action", is_action("veche")) + + if (view.battle && view.battle.array) { + ui.reserves_panel.classList.remove("hide") + ui.battle_panel.classList.remove("hide") + if (view.battle.storm) + ui.battle_header.textContent = "Storm at " + data.locales[view.battle.where].name + else if (view.battle.sally) + ui.battle_header.textContent = "Sally at " + data.locales[view.battle.where].name + else + ui.battle_header.textContent = "Battle at " + data.locales[view.battle.where].name + if (view.battle.attacker === player) { + ui.battle_grid.className = "attacker" + } else { + ui.battle_grid.className = "defender" + } + update_battle() + } else { + ui.battle_panel.classList.add("hide") + } + + if (view.battle && view.battle.array && view.battle.reserves.length > 0) + ui.reserves_panel.classList.remove("hide") + else + ui.reserves_panel.classList.add("hide") + + update_court() + + let first_turn = view.scenario >> 5 + let last_turn = view.scenario & 31 + for (let i = 0; i <= 17; ++i) { + ui.calendar[i].classList.toggle("action", is_action("calendar", i)) + if (i >= 1 && i <= 16) + ui.calendar[i].classList.toggle("end", i < first_turn || i > last_turn) + } + + // Misc + action_button("lordship", "Lordship") + action_button("march", "March") + action_button("avoid", "Avoid Battle") + action_button("withdraw", "Withdraw") + action_button("retreat", "Retreat") + action_button("remove", "Remove") + action_button("surrender", "Surrender") + action_button("siegeworks", "Siegeworks") + action_button("boats_x2", "Boats x2") + + // Use all commands + action_button("use_legate", "Legate") + action_button("stonemasons", "Stonemasons") + action_button("stone_kremlin", "Stone Kremlin") + action_button("tax", "Tax") + action_button("siege", "Siege") + + // Use one command + action_button("smerdi", "Smerdi") + action_button("storm", "Storm") + action_button("sally", "Sally") + action_button("sail", "Sail") + action_button("ravage", "Ravage") + action_button("forage", "Forage") + action_button("supply", "Supply") + + // Muster & Spoils + action_button("take_prov", "Provender") + action_button("take_loot", "Loot") + action_button("take_coin", "Coin") + action_button("take_ship", "Ship") + action_button("take_boat", "Boat") + action_button("take_cart", "Cart") + action_button("take_sled", "Sled") + action_button("capability", "Capability") + + // Events + action_button("decline", "Decline") + action_button("deploy", "Deploy") + action_button("discard", "Discard") + action_button("hold", "Hold") + action_button("play", "Play") + + action_button("approach", "Approach") + action_button("concede", "Concede") + action_button("battle", "Battle") + + action_button("end_array", "End Array") + action_button("end_avoid_battle", "End Avoid Battle") + action_button("end_call_to_arms", "End Call to Arms") + action_button("end_command", "End Command") + action_button("end_disband", "End Disband") + action_button("end_discard", "End Discard") + action_button("end_feed", "End Feed") + action_button("end_growth", "End Growth") + action_button("end_levy", "End Levy") + action_button("end_muster", "End Muster") + action_button("end_pay", "End Pay") + action_button("end_plan", "End Plan") + action_button("end_plow_and_reap", "End Plow and Reap") + action_button("end_ransom", "End Ransom") + action_button("end_remove", "End Remove") + action_button("end_reposition", "End Reposition") + action_button("end_sack", "End Sack") + action_button("end_sally", "End Sally") + action_button("end_setup", "End Setup") + action_button("end_spoils", "End Spoils") + action_button("end_supply", "End Supply") + action_button("end_wastage", "End Wastage") + action_button("end_withdraw", "End Withdraw") + + action_button("pass", "Pass") + action_button("done", "Done") + action_button("undo", "Undo") +} + +// === LOG === + +function on_focus_card_tip(c) { + if (c <= first_p1_card) + ui.command.className = `card teutonic aow_${c}` + else + ui.command.className = `card russian aow_${c}` +} + +function on_blur_card_tip() { + update_current_card_display() +} + +function sub_card_capability(match, p1) { + let x = p1 | 0 + return `<span class="card_tip" onmouseenter="on_focus_card_tip(${x})" onmouseleave="on_blur_card_tip(${x})">${data.cards[x].capability}</span>` +} + +function sub_card_event(match, p1) { + let x = p1 | 0 + return `<span class="card_tip" onmouseenter="on_focus_card_tip(${x})" onmouseleave="on_blur_card_tip(${x})">${data.cards[x].event}</span>` +} + +function on_focus_locale_tip(loc) { + ui.locale[loc].classList.add("tip") + if (ui.locale_name[loc]) + ui.locale_name[loc].classList.add("tip") +} + +function on_blur_locale_tip(loc) { + ui.locale[loc].classList.remove("tip") + if (ui.locale_name[loc]) + ui.locale_name[loc].classList.remove("tip") +} + +function on_click_locale_tip(loc) { + ui.locale[loc].scrollIntoView({ block:"center", inline:"center", behavior:"smooth" }) +} + +function on_focus_way_tip(way) { + ui.ways[way].classList.add("tip") +} + +function on_blur_way_tip(way) { + ui.ways[way].classList.remove("tip") +} + +function on_click_way_tip(way) { + ui.ways[way].scrollIntoView({ block:"center", inline:"center", behavior:"smooth" }) +} + +function on_click_lord_tip(lord) { + ui.lord_mat[lord].scrollIntoView({ block:"center", inline:"center", behavior:"smooth" }) +} + +function sub_locale_name(match, p1) { + let x = p1 | 0 + let n = data.locales[x].name + return `<span class="locale_tip" onmouseenter="on_focus_locale_tip(${x})" onmouseleave="on_blur_locale_tip(${x})" onclick="on_click_locale_tip(${x})">${n}</span>` +} + +function sub_lord_name(match, p1) { + let x = p1 | 0 + let n = data.lords[x].name + return `<span class="lord_tip" onclick="on_click_lord_tip(${x})">${n}</span>` +} + +function sub_way_name(match, p1) { + let x = p1 | 0 + let n = data.ways[x].name + return `<span class="way_tip" onmouseenter="on_focus_way_tip(${x})" onmouseleave="on_blur_way_tip(${x})" onclick="on_click_way_tip(${x})">${n}</span>` +} + +function on_log(text) { + let p = document.createElement("div") + + if (text.match(/^>>/)) { + text = text.substring(2) + p.className = "ii" + } + + if (text.match(/^>/)) { + text = text.substring(1) + p.className = "i" + } + + text = text.replace(/&/g, "&") + text = text.replace(/</g, "<") + text = text.replace(/>/g, ">") + + text = text.replace(/C(\d+)/g, sub_card_capability) + text = text.replace(/E(\d+)/g, sub_card_event) + text = text.replace(/L(\d+)/g, sub_lord_name) + text = text.replace(/%(\d+)/g, sub_locale_name) + text = text.replace(/W(\d+)/g, sub_way_name) + + if (text.match(/^\.h1/)) { + text = text.substring(4) + p.className = "h1" + } + else if (text.match(/^\.h2t/)) { + text = text.substring(5) + p.className = "h2 teutonic" + } + else if (text.match(/^\.h2r/)) { + text = text.substring(5) + p.className = "h2 russian" + } + else if (text.match(/^\.h2/)) { + text = text.substring(4) + p.className = "h2" + } + else if (text.match(/^\.h3t/)) { + text = text.substring(5) + p.className = "h3 teutonic" + } + else if (text.match(/^\.h3r/)) { + text = text.substring(5) + p.className = "h3 russian" + } + else if (text.match(/^\.h3/)) { + text = text.substring(4) + p.className = "h3" + } + else if (text.match(/^\.h4/)) { + text = text.substring(4) + p.className = "h4" + } + else if (text.match(/^\.h5/)) { + text = text.substring(4) + p.className = "h5" + } + + p.innerHTML = text + return p +} + +function pack1_get(word, n) { + return (word >>> n) & 1 +} + +function pack2_get(word, n) { + n = n << 1 + return (word >>> n) & 3 +} + +function pack4_get(word, n) { + n = n << 2 + return (word >>> n) & 15 +} + +build_map() +scroll_with_middle_mouse("main") |