From 09bec88e3ad8894217f4f426372e9d94b2ad1ba9 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Mon, 26 Jun 2023 11:24:34 +0200 Subject: Highlight current crisis and player on the crisis table. --- play.css | 53 +++++++++++++++++++++- play.html | 2 +- play.js | 24 ++++++---- rules.js | 153 +++++++++++++++++++++++++++++++++++++------------------------- 4 files changed, 161 insertions(+), 71 deletions(-) diff --git a/play.css b/play.css index 9350ba0..5e02eea 100644 --- a/play.css +++ b/play.css @@ -28,10 +28,16 @@ body.Green .your_turn { background-color: hsl(99,36%,65%) } #log .h1.p_yellow { background-color: hsl(47,100%,78%) } #log .h1.p_green { background-color: hsl(99,36%,70%) } +#log img { height: 14px; vertical-align: -6px; } + +#log span.M { color: #e31f26 } +#log span.S { color: #0066b3 } +#log span.P { color: hsl(44, 100%, 35%) } + #log { background-color: whitesmoke; } #log .h1 { background-color: silver; font-weight: bold; padding-top:2px; padding-bottom:2px; margin: 8px 0; text-align: center; } #log .h1 { border-bottom: 1px solid #444; border-top: 1px solid #444; } -#log .h2 { background-color: gainsboro; padding-top:2px; padding-bottom:2px; text-align: center; } +#log .h2 { background-color: gainsboro; text-align: center; } #log .h3 { text-decoration: underline; } #log div { padding-left: 20px; text-indent: -12px; } #log div.i { padding-left: 32px; text-indent: -12px; } @@ -48,6 +54,11 @@ body.Green .your_turn { background-color: hsl(99,36%,65%) } background-repeat: no-repeat; } +#log img { + pointer-events: none; + vertical-align: -3px; +} + #log .white { background-image: url(images/die_black_pips.svg); background-color: #fff; @@ -642,6 +653,46 @@ body.shift #zenobia { background-image: url(images/rival_back.png) } background-image: repeating-linear-gradient(135deg, #555555, #555555 60px, #505050 60px, #505050 120px); } +/* CRISIS TABLE HIGHLIGHT */ + +#crisis_highlight { + transition-property: inset, background-color; + transition-duration: 500ms; + transition-timing-function: ease; + box-sizing: border-box; + position: absolute; + background-color: transparent; + border: 4px solid transparent; + top: 189px; + left: 2194px; + width: 263px; + height: 30px; + box-shadow: 0 0 0 1px #444; +} + +#crisis_highlight.p_red { background-color: hsla(354,85%,67%,20%) } +#crisis_highlight.p_blue { background-color: hsla(207,67%,78%,20%) } +#crisis_highlight.p_yellow { background-color: hsla(47,100%,53%,20%) } +#crisis_highlight.p_green { background-color: hsla(99,36%,60%,20%) } + +#crisis_highlight.p_red { border-color: hsl(354,85%,67%) } +#crisis_highlight.p_blue { border-color: hsl(207,67%,78%) } +#crisis_highlight.p_yellow { border-color: hsl(47,100%,53%) } +#crisis_highlight.p_green { border-color: hsl(99,36%,60%) } + +#crisis_highlight.c0 { display: none } +#crisis_highlight.c2 { top: 216px; } +#crisis_highlight.c3 { top: 243px; } +#crisis_highlight.c4 { top: 270px; } +#crisis_highlight.c5 { top: 297px; } +#crisis_highlight.c6 { top: 324px; } +#crisis_highlight.c7 { top: 351px; } +#crisis_highlight.c8 { top: 378px; } +#crisis_highlight.c9 { top: 405px; } +#crisis_highlight.c10 { top: 432px; } +#crisis_highlight.c11 { top: 459px; } +#crisis_highlight.c12 { top: 486px; } + /* COMBAT MASK */ #combat_mask { diff --git a/play.html b/play.html index 18f4068..28172fd 100644 --- a/play.html +++ b/play.html @@ -25,7 +25,6 @@ Card Gallery -
@@ -48,6 +47,7 @@
+
diff --git a/play.js b/play.js index 90056c6..710610a 100644 --- a/play.js +++ b/play.js @@ -3,7 +3,7 @@ // TODO: battle dialog popup for rolling and assigning hits! // TODO: show killed leaders taken for bonus purchase -const DICE = { +const ICONS = { B0: '', B1: '', B2: '', @@ -691,6 +691,7 @@ let ui = { amphitheater: [], basilica: [], limes: [], + crisis: document.getElementById("crisis_highlight"), dice: [ document.getElementById("crisis_die_1"), document.getElementById("crisis_die_2"), @@ -734,10 +735,6 @@ function hide(elt) { elt.classList.add("hide") } -function toggle_pieces() { - ui.pieces.classList.toggle("hide") -} - function create(t, p, ...c) { let e = document.createElement(t) Object.assign(e, p) @@ -1377,6 +1374,7 @@ function on_update() { ui.body.classList.toggle("populace", view.color === 2) layout_barbarian_dice(ui.dice[2], ui.dice[3], view.crisis[0]) + ui.crisis.className = "p_" + PLAYER_CLASS[view.current] + " c" + (view.crisis[1] + view.crisis[2]) ui.dice[0].className = "dice black d" + view.crisis[1] ui.dice[1].className = "dice white d" + view.crisis[2] ui.dice[2].className = "dice black d" + view.crisis[3] @@ -1477,8 +1475,16 @@ function sub_event_name(match, p1) { return EVENT_NAME[x] } -function sub_dice_image(match) { - return DICE[match] +function sub_icon(match) { + return ICONS[match] +} + +function sub_card_1(match) { + return match +} + +function sub_card_x(match, p1, p2) { + return p1 + "\xa0" + p2 } function on_log(text) { @@ -1495,7 +1501,9 @@ function on_log(text) { text = text.replace(/%(\d+)/g, sub_region_name) text = text.replace(/\bE(\d+)/g, sub_event_name) - text = text.replace(/\b[BW]\d\b/g, sub_dice_image) + text = text.replace(/\b[BW]\d\b/g, sub_icon) + text = text.replace(/\b[MSP][1]\b/g, sub_card_1) + text = text.replace(/\b([MSP][234])\(([^)]*)\)/g, sub_card_x) if (text.match(/^.turn/)) { text = text.substring(6) diff --git a/rules.js b/rules.js index 2c06cc5..756374b 100644 --- a/rules.js +++ b/rules.js @@ -39,7 +39,7 @@ exports.roles = function (scenario, options) { // === CONSTANTS === -const PLAYER_NAMES = [ P1, P2, P3, P4 ] +const PLAYER_NAME = [ P1, P2, P3, P4 ] const PLAYER_INDEX = { [P1]: 0, @@ -350,25 +350,25 @@ const CARD_INFO = [ { name: "M1", type: 0, value: 1, event: "None" }, { name: "S1", type: 1, value: 1, event: "None" }, { name: "P1", type: 2, value: 1, event: "None" }, - { name: "M2 Castra", type: 0, value: 2, event: "Castra" }, - { name: "S2 Tribute", type: 1, value: 2, event: "Tribute" }, - { name: "P2 Quaestor", type: 2, value: 2, event: "Quaestor" }, - { name: "M2 Cavalry", type: 0, value: 2, event: "Cavalry" }, - { name: "S2 Princeps Senatus", type: 1, value: 2, event: "Princeps Senatus" }, - { name: "P2 Ambitus", type: 2, value: 2, event: "Ambitus" }, - { name: "M3 Flanking Maneuver", type: 0, value: 3, event: "Flanking Maneuver" }, - { name: "S3 Foederati", type: 1, value: 3, event: "Foederati" }, - { name: "P3 Mob", type: 2, value: 3, event: "Mob" }, - { name: "M3 Force March", type: 0, value: 3, event: "Force March" }, - { name: "S3 Frumentarii", type: 1, value: 3, event: "Frumentarii" }, - { name: "P3 Mobile Vulgus", type: 2, value: 3, event: "Mobile Vulgus" }, - { name: "M4 Praetorian Guard", type: 0, value: 4, event: "Praetorian Guard" }, - { name: "S4 Damnatio Memoriae", type: 1, value: 4, event: "Damnatio Memoriae" }, - { name: "S4 Damnatio Memoriae", type: 1, value: 4, event: "Damnatio Memoriae (exp)" }, - { name: "P4 Pretender", type: 2, value: 4, event: "Pretender" }, - { name: "M4 Spiculum", type: 0, value: 4, event: "Spiculum" }, - { name: "S4 Triumph", type: 1, value: 4, event: "Triumph" }, - { name: "P4 Demagogue", type: 2, value: 4, event: "Demagogue" }, + { name: "M2(Castra)", type: 0, value: 2, event: "Castra" }, + { name: "S2(Tribute)", type: 1, value: 2, event: "Tribute" }, + { name: "P2(Quaestor)", type: 2, value: 2, event: "Quaestor" }, + { name: "M2(Cavalry)", type: 0, value: 2, event: "Cavalry" }, + { name: "S2(Princeps Senatus)", type: 1, value: 2, event: "Princeps Senatus" }, + { name: "P2(Ambitus)", type: 2, value: 2, event: "Ambitus" }, + { name: "M3(Flanking Maneuver)", type: 0, value: 3, event: "Flanking Maneuver" }, + { name: "S3(Foederati)", type: 1, value: 3, event: "Foederati" }, + { name: "P3(Mob)", type: 2, value: 3, event: "Mob" }, + { name: "M3(Force March)", type: 0, value: 3, event: "Force March" }, + { name: "S3(Frumentarii)", type: 1, value: 3, event: "Frumentarii" }, + { name: "P3(Mobile Vulgus)", type: 2, value: 3, event: "Mobile Vulgus" }, + { name: "M4(Praetorian Guard)", type: 0, value: 4, event: "Praetorian Guard" }, + { name: "S4(Damnatio Memoriae)", type: 1, value: 4, event: "Damnatio Memoriae" }, + { name: "S4(Damnatio Memoriae)", type: 1, value: 4, event: "Damnatio Memoriae (exp)" }, + { name: "P4(Pretender)", type: 2, value: 4, event: "Pretender" }, + { name: "M4(Spiculum)", type: 0, value: 4, event: "Spiculum" }, + { name: "S4(Triumph)", type: 1, value: 4, event: "Triumph" }, + { name: "P4(Demagogue)", type: 2, value: 4, event: "Demagogue" }, ] function card_name(c) { @@ -1129,7 +1129,7 @@ states.setup_province = { capital(where) { push_undo() - log(PLAYER_NAMES[game.current] + " started in %" + where + ".") + log(PLAYER_NAME[game.current] + " in %" + where + ".") set_governor_location(game.current * 6 + 0, where) @@ -1172,7 +1172,7 @@ states.setup_hand = { // === UPKEEP === function goto_start_turn() { - log_h1(PLAYER_NAMES[game.current]) + log_h1(PLAYER_NAME[game.current]) game.killed = 0 game.battled = 0 @@ -1849,7 +1849,6 @@ states.take_actions = { let hand = current_hand() while (hand.length > 0) { let c = hand[0] - log("Played " + card_name(c) + ".") set_delete(hand, c) set_add(game.played, c) add_card_ip(c) @@ -1860,7 +1859,6 @@ states.take_actions = { push_undo() let hand = current_hand() if (set_has(hand, c)) { - log("Played " + card_name(c) + ".") set_delete(hand, c) set_add(game.played, c) add_card_ip(c) @@ -2096,7 +2094,7 @@ function increase_support(where) { } function remove_governor(where) { - log("Removed Governor from %" + where + ".") + log("Removed governor from %" + where + ".") eliminate_militia(where) set_mobs(where, 0) @@ -2918,27 +2916,24 @@ function play_flanking_maneuver() { } function goto_battle_vs_general(where, attacker, target) { - log_h3("Battle " + PLAYER_NAMES[target/6|0] + " in %" + where) goto_battle("general", where, attacker, target) } function goto_battle_vs_barbarian(where, attacker, target) { let tribe = get_barbarian_tribe(target) - log_h3("Battle " + BARBARIAN_NAME[tribe] + " in %" + where) goto_battle("barbarians", where, attacker, tribe) } function goto_battle_vs_rival_emperor(where, attacker, target) { - log_h3("Battle " + RIVAL_EMPEROR_NAME[target] + " in %" + where) goto_battle("rival_emperor", where, attacker, target) } function goto_battle_vs_militia(where, attacker) { - log_h3("Battle militia in %" + where) goto_battle("militia", where, attacker, -1) } function goto_battle(type, where, attacker, target) { + log_h2("Battle in %" + where) spend_military(1) game.where = where game.battle = { type, attacker, target, flanking: 0, killed: 0 } @@ -2983,9 +2978,9 @@ function gen_initiate_battle(where) { function format_battle_target() { switch (game.battle.type) { - case "militia": return PLAYER_NAMES[get_province_player(game.battle.target)] + " militia" + case "militia": return PLAYER_NAME[get_province_player(game.battle.target)] + " militia" case "barbarians": return BARBARIAN_NAME[game.battle.target] - case "general": return PLAYER_NAMES[game.battle.target / 6 | 0] + " army" + case "general": return PLAYER_NAME[game.battle.target / 6 | 0] + " army" case "rival_emperor": return RIVAL_EMPEROR_NAME[game.battle.target] } } @@ -3074,7 +3069,7 @@ function roll_general_dice(general) { let drm = get_roman_drm() - log(GENERAL_NAME[general]) + log(PLAYER_NAME[general/6|0]) if (is_general_inside_capital(general) && has_militia(game.where)) { log("Militia") @@ -3425,6 +3420,8 @@ states.combat_victory = { } function goto_combat_victory() { + log_br() + log("VICTORY") let de = is_defender_eliminated() let ae = is_attacker_eliminated() if (de && ae) @@ -3437,9 +3434,9 @@ function goto_combat_victory() { function award_legacy(p, reason, n) { if (n > 0) - log(PLAYER_NAMES[p] + " +" + n + " Legacy for " + reason + ".") + log(PLAYER_NAME[p] + " +" + n + " Legacy for " + reason + ".") if (n < 0) - log(PLAYER_NAMES[p] + " " + n + " Legacy for " + reason + ".") + log(PLAYER_NAME[p] + " " + n + " Legacy for " + reason + ".") game.legacy[p] += n } @@ -3452,11 +3449,13 @@ function award_legacy_summary(p, reason, n) { } function goto_combat_no_victory() { + log("Nobody") game.battle.killed = 0 end_battle() } function goto_combat_victory_defender() { + log("Defender") game.battle.killed = 0 if (game.battle.type === "general") award_legacy(game.battle.target / 6 | 0, "Victory", 2) @@ -3466,6 +3465,8 @@ function goto_combat_victory_defender() { } function goto_combat_victory_attacker() { + log("Attacker") + if (game.battle.type === "barbarians") { award_legacy(game.current, "Victory", 2 + game.battle.dtaken) @@ -3570,6 +3571,8 @@ function goto_support_check() { } game.count = 0 + if (needs_any_support_check()) + log_h2("Support Check") resume_support_check() } @@ -3583,18 +3586,24 @@ function is_any_rival_emperor_or_pretender() { return false } +function needs_any_support_check() { + return needs_support_check() || needs_support_check_emperor() || needs_support_check_mobs() +} + +function needs_support_check() { + for (let where = 0; where < 12; ++where) + if ((game.count & (1 << where)) === 0) + if (is_own_province(where)) + if (has_active_barbarians(where) || has_rival_emperor(where) || has_enemy_general_in_capital(where)) + return true + return false +} + function resume_support_check() { - for (let where = 0; where < 12; ++where) { - if ((game.count & (1 << where)) === 0) { - if (is_own_province(where)) { - if (has_active_barbarians(where) || has_rival_emperor(where) || has_enemy_general_in_capital(where)) { - game.state = "support_check" - return - } - } - } - } - goto_support_check_emperor() + if (needs_support_check()) + game.state = "support_check" + else + goto_support_check_emperor() } states.support_check = { @@ -3611,17 +3620,21 @@ states.support_check = { region(where) { push_undo() game.count |= (1 << where) + log("Reduced support level in %" + where + ".") reduce_support(where) resume_support_check() }, } +function needs_support_check_emperor() { + return is_emperor_player() && is_any_rival_emperor_or_pretender() +} + function goto_support_check_emperor() { - if (is_emperor_player() && is_any_rival_emperor_or_pretender()) { + if (needs_support_check_emperor()) game.state = "support_check_emperor" - return - } - goto_support_check_mobs() + else + goto_support_check_mobs() } states.support_check_emperor = { @@ -3634,19 +3647,23 @@ states.support_check_emperor = { region(where) { push_undo() game.count |= (1 << where) + log("Reduced support level in %" + where + ".") reduce_support(where) goto_support_check_mobs() }, } +function needs_support_check_mobs() { + for (let where = 0; where < 12; ++where) + if (is_own_province(where) && get_mobs(where) >= get_support(where)) + return true +} + function goto_support_check_mobs() { - for (let where = 0; where < 12; ++where) { - if (is_own_province(where) && get_mobs(where) >= get_support(where)) { - game.state = "support_check_mobs" - return - } - } - goto_expand_pretender_empire() + if (needs_support_check_mobs()) + game.state = "support_check_mobs" + else + goto_expand_pretender_empire() } states.support_check_mobs = { @@ -3660,6 +3677,7 @@ states.support_check_mobs = { }, region(where) { push_undo() + log("More mobs than support in %" + where + ".") remove_governor(where) goto_support_check_mobs() }, @@ -3670,6 +3688,7 @@ states.support_check_mobs = { function goto_expand_pretender_empire() { for (let where = 1; where < 12; ++where) { if (is_expand_pretender_province(where)) { + log_h4("Expand Pretender Empire") game.state = "expand_pretender_empire" return } @@ -3688,6 +3707,7 @@ states.expand_pretender_empire = { }, region(where) { push_undo() + logi("Breakaway %" + where) add_breakaway(where) remove_quaestor(where) // no effect anymore goto_expand_pretender_empire() @@ -3697,8 +3717,7 @@ states.expand_pretender_empire = { // === GAIN LEGACY === function goto_gain_legacy() { - log_br() - log("Gain Legacy") + log_h4("Gain Legacy") if (is_only_pretender_player()) award_legacy_summary(game.current, "Pretender", count_own_breakaway_provinces()) @@ -3768,11 +3787,16 @@ function count_political_points() { } function goto_buy_trash_cards() { + //log_h4("Played") + //logi(game.played.map(c=>card_name(c)).join(", ")) + log_br() + log("Played " + game.played.map(c=>card_name(c)).join(", ") + ".") log_br() let discard = current_discard() for (let c of game.played) set_add(discard, c) + game.played.length = 0 game.count = 0 game.pp = count_political_points() @@ -3795,6 +3819,7 @@ states.buy_trash_discard = { }, card(c) { push_undo() + log("Discarded " + card_name(c) + ".") set_delete(current_hand(), c) set_add(current_discard(), c) }, @@ -4070,6 +4095,12 @@ function vp_tie(p) { function goto_game_end() { log_h2("Game End") + game.crisis[0] = -1 + game.crisis[1] = 0 + game.crisis[2] = 0 + game.crisis[3] = 0 + game.crisis[4] = 0 + let cutoff = award_emperor_turns(10, 1000) cutoff = award_emperor_turns(6, cutoff) cutoff = award_emperor_turns(3, cutoff) @@ -4077,7 +4108,7 @@ function goto_game_end() { let victor = game.legacy.map((legacy,p) => [vp_tie(p),p]).sort((a,b) => b[0] - a[0])[0][1] - goto_game_over(PLAYER_NAMES[victor], PLAYER_NAMES[victor] + " won!") + goto_game_over(PLAYER_NAME[victor], PLAYER_NAME[victor] + " won!") } function goto_game_over(result, victory) { @@ -4236,7 +4267,7 @@ function load_game(state) { } function save_game() { - game.active = PLAYER_NAMES[game.current] + game.active = PLAYER_NAME[game.current] return game } @@ -4292,7 +4323,7 @@ exports.view = function (state, player_name) { view.prompt = game.victory } else if (game.current !== player) { let inactive = states[game.state].inactive || game.state - view.prompt = `Waiting for ${PLAYER_NAMES[game.current]}: ${inactive}.` + view.prompt = `Waiting for ${PLAYER_NAME[game.current]}: ${inactive}.` } else { view.actions = {} states[game.state].prompt() -- cgit v1.2.3