diff options
-rw-r--r-- | play.html | 24 | ||||
-rw-r--r-- | play.js | 89 | ||||
-rw-r--r-- | rules.js | 79 |
3 files changed, 147 insertions, 45 deletions
@@ -248,18 +248,18 @@ body.shift .mat .card:hover { z-index: 2; } -#p1_global, #p2_global { +#p1_capabilities, #p2_capabilities { display: flex; flex-wrap: wrap; gap: 12px; } -#p1_global { +#p1_capabilities { margin-left: 24px; justify-content: start; } -#p2_global { +#p2_capabilities { margin-right: 24px; justify-content: end; } @@ -542,6 +542,16 @@ body.shift .mat .card:hover { box-shadow: 0 0 8px #ff06; } +.locale_markers { + position: absolute; + pointer-events: none; + display: flex; + flex-wrap: wrap; + justify-content: center; + align-items: center; + gap: 4px; +} + #veche { position: absolute; } .veche_label { @@ -625,8 +635,8 @@ body.shift .mat .card:hover { width: 32px; height: 64px; background-image: url(images/legate.svg); - top: 1585px; - left: 180px; + top: 1580px; + left: 170px; filter: drop-shadow(0px 2px 4px #0004); } @@ -1021,8 +1031,8 @@ body.shift .marker:hover { transform: scale(2); z-index: 200; } </div> <div class="tuck_under_map"> -<div id="p1_global" class="global"></div> -<div id="p2_global" class="global"></div> +<div id="p1_capabilities"></div> +<div id="p2_capabilities"></div> </div> <div id="plan" class="hide"> @@ -1,5 +1,8 @@ "use strict" +// TODO: put card elements in capability containers, not c1/c2 specials +// TODO: held events, this_turn events + const MAP_DPI = 75 const round = Math.round @@ -49,6 +52,22 @@ function max_plan_length() { } } +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 +} + function pack1_get(word, n) { return (word >>> n) & 1 } @@ -155,7 +174,7 @@ function count_vp2() { } function is_card_in_use(c) { - if (view.global_cards.includes(c)) + if (view.capabilities.includes(c)) return true if (view.lords.cards.includes(c)) return true @@ -242,6 +261,7 @@ const locale_xy = [] const ui = { locale: [], locale_extra: [], + locale_markers: [], lord_cylinder: [], lord_service: [], lord_mat: [], @@ -261,8 +281,8 @@ const ui = { plan_action_cards: [], arts_of_war_dialog: document.getElementById("arts_of_war"), arts_of_war_list: document.getElementById("arts_of_war_list"), - p1_global: document.getElementById("p1_global"), - p2_global: document.getElementById("p2_global"), + p1_capabilities: document.getElementById("p1_capabilities"), + p2_capabilities: document.getElementById("p2_capabilities"), command: document.getElementById("command"), turn: document.getElementById("turn"), vp1: document.getElementById("vp1"), @@ -588,6 +608,47 @@ function update_locale(loc) { ui.locale[loc].classList.toggle("action", is_locale_action(loc)) if (ui.locale_extra[loc]) ui.locale_extra[loc].classList.toggle("action", is_locale_action(loc)) + + ui.locale_markers[loc].replaceChildren() + + if (set_has(view.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.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.castles, loc)) { + let cn + if (is_p1_locale(loc)) + cn = "marker rectangle castle russian" + else + cn = "marker rectangle castle teutonic" + ui.locale_markers[loc].appendChild(get_cached_element(cn)) + } + + if (view.sieges[loc]) { + let cn + // TODO: castle? + if (is_p1_locale(loc)) + cn = "marker square siege russian" + else + cn = "marker square siege teutonic" + for (let i = 0; i < view.sieges[loc]; ++i) + ui.locale_markers[loc].appendChild(get_cached_element(cn)) + } } function update_plan() { @@ -662,16 +723,16 @@ function update_arts_of_war() { } } - ui.p1_global.replaceChildren() + ui.p1_capabilities.replaceChildren() for_each_teutonic_arts_of_war(c => { - if (view.global_cards.includes(c)) - ui.p1_global.appendChild(ui.arts_of_war[c]) + if (view.capabilities.includes(c)) + ui.p1_capabilities.appendChild(ui.arts_of_war[c]) }) - ui.p2_global.replaceChildren() + ui.p2_capabilities.replaceChildren() for_each_russian_arts_of_war(c => { - if (view.global_cards.includes(c)) - ui.p2_global.appendChild(ui.arts_of_war[c]) + if (view.capabilities.includes(c)) + ui.p2_capabilities.appendChild(ui.arts_of_war[c]) }) for (let ix = 0; ix < data.lords.length; ++ix) { @@ -892,6 +953,15 @@ function build_map() { e.addEventListener("mouseleave", on_blur) document.getElementById("locales").appendChild(e) } + + e = ui.locale_markers[ix] = document.createElement("div") + e.className = "locale_markers " + locale.type + " " + region + x = (locale.box.x + locale.box.w / 2) * MAP_DPI / 300 - 196/2 + y = (locale.box.y + locale.box.h * 0) * MAP_DPI / 300 - 8 + e.style.top = y + "px" + e.style.left = x + "px" + e.style.width = 196 + "px" + document.getElementById("pieces").appendChild(e) }) let x = 160 @@ -954,5 +1024,4 @@ function build_map() { build_map() // drag_element_with_mouse("#battle", "#battle_header") drag_element_with_mouse("#arts_of_war", "#arts_of_war_header") -drag_element_with_mouse("#plan", "#plan_header") scroll_with_middle_mouse("main") @@ -9,6 +9,9 @@ const RUSSIANS = "Russians" const P1 = TEUTONS const P2 = RUSSIANS +// NOTE: With Hidden Mats option, the player order of feed/pay may matter. +const FEED_PAY_DISBAND = false // feed, pay, disband in one go + let game = null let view = null let states = {} @@ -351,7 +354,7 @@ function is_levy_phase() { } function is_card_in_use(c) { - if (set_has(game.global_cards, c)) + if (set_has(game.capabilities, c)) return true if (game.lords.cards.includes(c)) return true @@ -482,10 +485,14 @@ exports.setup = function (seed, scenario, options) { stack: [], turn: 0, + p1_hand: [], p2_hand: [], p1_plan: [], p2_plan: [], + events: [], // this levy/this campaign cards + capabilities: [], // global capabilities + lords: { locale: Array(lord_count).fill(NOWHERE), service: Array(lord_count).fill(NEVER), @@ -500,10 +507,11 @@ exports.setup = function (seed, scenario, options) { legate: NOWHERE, veche_vp: 0, veche_coin: 0, + conquered: [], ravaged: [], castles: [], - global_cards: [], + sieges: {}, command: NOBODY, who: NOBODY, @@ -730,14 +738,14 @@ function setup_pleskau_quickstart() { logi("Capability T3") set_lord_capability(LORD_YAROSLAV, 0, find_arts_of_war("T3")) - set_add(game.global_cards, find_arts_of_war("T13")) + set_add(game.capabilities, find_arts_of_war("T13")) game.legate = LOC_DORPAT log_h2("Russians Muster") log_h3("Vladislav") logi("Capability R8") - set_add(game.global_cards, find_arts_of_war("R8")) + set_add(game.capabilities, find_arts_of_war("R8")) logi(`Domash at %${LOC_NOVGOROD}`) muster_lord(LORD_DOMASH, LOC_NOVGOROD) logii("Boat") @@ -1051,7 +1059,7 @@ states.muster_capability = { push_undo() logi(`Capability #${c}`) if (!data.cards[c].lords) { - set_add(game.global_cards, c) + set_add(game.capabilities, c) } else { if (get_lord_capability(game.who, 0) < 0) set_lord_capability(game.who, 0, c) @@ -1123,7 +1131,6 @@ states.campaign_plan = { if (count_cards_in_plan(plan, -1) < 3) gen_action_plan(-1) for (let lord = 0; lord <= last_lord; ++lord) { - console.log("campaign_plan", lord, current) if (lord >= first_p1_lord && lord <= last_p1_lord && current !== P1) continue if (lord >= first_p2_lord && lord <= last_p2_lord && current !== P2) @@ -1237,9 +1244,12 @@ function goto_feed() { end_feed() } +// TODO: feed_self +// TODO: feed_other + states.feed = { prompt() { - view.prompt = "You must Feed your who Moved or Fought." + view.prompt = "You must Feed lords who Moved or Fought." for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) if (get_lord_moved(lord)) gen_action_lord(lord) @@ -1283,11 +1293,15 @@ states.feed_lord = { } function end_feed() { - set_active_enemy() - if (game.active === P2) - goto_feed() - else + if (FEED_PAY_DISBAND) { goto_pay() + } else { + set_active_enemy() + if (game.active === P2) + goto_feed() + else + goto_pay() + } } // === LEVY & CAMPAIGN: PAY === @@ -1345,11 +1359,8 @@ states.pay_lord = { } function end_pay() { - set_active_enemy() - if (game.active === P2) - goto_pay() - else - goto_disband() + // NOTE: We can combine Pay & Disband steps because disband is mandatory only. + goto_disband() } // === LEVY & CAMPAIGN: DISBAND === @@ -1372,7 +1383,10 @@ states.disband = { function end_disband() { set_active_enemy() if (game.active === P2) { - goto_disband() + if (FEED_PAY_DISBAND) + goto_feed() + else + goto_pay() } else { if (is_levy_phase()) goto_levy_muster() @@ -1507,7 +1521,7 @@ exports.view = function(state, current) { legate: game.legate, veche_vp: game.veche_vp, veche_coin: game.veche_coin, - global_cards: game.global_cards, + capabilities: game.capabilities, conquered: game.conquered, ravaged: game.ravaged, castles: game.castles, @@ -1517,6 +1531,8 @@ exports.view = function(state, current) { where: game.where, } + view.sieges = { 0: 3, [LOC_NOVGOROD]: 2, [LOC_PSKOV]: 1 } + if (game.state === 'game_over') { view.prompt = game.victory } else if (current === 'Observer' || (game.active !== current && game.active !== BOTH)) { @@ -1581,7 +1597,8 @@ function pack4_set(word, n, x) { } // Sorted array treated as Set (for JSON) -function set_index(set, item) { + +function set_has(set, item) { let a = 0 let b = set.length - 1 while (a <= b) { @@ -1592,13 +1609,9 @@ function set_index(set, item) { else if (item > x) a = m + 1 else - return m + return true } - return -1 -} - -function set_has(set, item) { - return set_index(set, item) >= 0 + return false } function set_add(set, item) { @@ -1618,9 +1631,19 @@ function set_add(set, item) { } function set_delete(set, item) { - let i = set_index(set, item) - if (i >= 0) - set.splice(i, 1) + 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 array_remove(set, m) + } + return set } function set_clear(set) { |