diff options
author | Tor Andersson <tor@ccxvii.net> | 2023-06-18 14:31:32 +0200 |
---|---|---|
committer | Tor Andersson <tor@ccxvii.net> | 2023-07-07 18:39:33 +0200 |
commit | 19fd5d79b8e58b6806bb711c2c53054f73b2a282 (patch) | |
tree | b07ef2e730182b16dcf63a87b6a6f9369bb521fb | |
parent | 495090873cf62a1ca698cee18a2d292fc30aa292 (diff) | |
download | time-of-crisis-19fd5d79b8e58b6806bb711c2c53054f73b2a282.tar.gz |
Lots of stuff.
-rw-r--r-- | play.css | 37 | ||||
-rw-r--r-- | play.js | 15 | ||||
-rw-r--r-- | rules.js | 724 |
3 files changed, 551 insertions, 225 deletions
@@ -53,6 +53,8 @@ body.p3 #crisis_table { background-image: url(overlay_3p_75.jpg); } +body.Solo #.role { display: none } + svg { position: absolute; } @@ -65,17 +67,23 @@ svg .region, svg .sea { svg .region.action { fill: gold; - fill-opacity: 0.2; + fill-opacity: 0.1; stroke: yellow; + stroke-opacity: 0.5; + stroke-width: 3px; +} + +svg .region.selected { + stroke: gold; stroke-opacity: 0.8; stroke-width: 3px; } svg .sea.action { - fill: blue; - fill-opacity: 0.2; - stroke: blue; - stroke-opacity: 0.8; + fill: dodgerblue; + fill-opacity: 0.1; + stroke: dodgerblue; + stroke-opacity: 0.5; stroke-width: 3px; } @@ -149,17 +157,6 @@ body.p2 #Aegyptus_NPG { display: block } body.p2 #Syria_NPG { display: block } body.p2 #Galatia_NPG { display: block } -body.p3 #Hispania_Governor { display: none } -body.p3 #Africa_Governor { display: none } -body.p3 #Aegyptus_Governor { display: none } - -body.p2 #Britannia_Governor { display: none } -body.p2 #Hispania_Governor { display: none } -body.p2 #Africa_Governor { display: none } -body.p2 #Aegyptus_Governor { display: none } -body.p2 #Syria_Governor { display: none } -body.p2 #Galatia_Governor { display: none } - /* COUNTERS */ .amphitheater, .basilica, .limes { background-color: #efebea; } @@ -199,6 +196,14 @@ body.p2 #Galatia_Governor { display: none } .yellow.action { box-shadow: 0 0 0 1px #553a00, 0 0 0 4px white; } .green.action { box-shadow: 0 0 0 1px #033600, 0 0 0 4px white; } +.selected { box-shadow: 0 0 0 3px black, 0 0 4px 6px white } +.selected { box-shadow: 0 0 0 4px black } + +.red.selected { box-shadow: 0 0 0 1px #680000, 0 0 0 4px yellow; } +.blue.selected { box-shadow: 0 0 0 1px #113854, 0 0 0 4px red; } +.yellow.selected { box-shadow: 0 0 0 1px #553a00, 0 0 0 4px red; } +.green.selected { box-shadow: 0 0 0 1px #033600, 0 0 0 4px yellow; } + #legion_0 { background-position: 0px 0px } #legion_1 { background-position: -55px 0px } #legion_2 { background-position: -110px 0px } @@ -509,7 +509,6 @@ function on_update() { stack_cache = {} - ui.body.classList.toggle("p1", view.solo === 1) ui.body.classList.toggle("p2", player_count === 2) ui.body.classList.toggle("p3", player_count === 3) ui.body.classList.toggle("p4", player_count === 4) @@ -657,6 +656,10 @@ function on_update() { } } + for (let region = 0; region < 21; ++region) { + ui.regions[region].classList.toggle("selected", view.selected_region === region) + } + for (let region = 0; region < 12; ++region) { if (has_militia(region)) { let lone_militia = true @@ -738,6 +741,7 @@ function on_update() { avail_stack.push(e) } e.classList.toggle("unavailable", region === UNAVAILABLE) + e.classList.toggle("selected", view.selected_general === pi * 6 + ai) } layout_available(avail_stack, 63, pi * 625 + 0, 30) } @@ -748,7 +752,7 @@ function on_update() { let id = 100 + 100 * pi + ai let region = get_governor_location(first_governor[pi] + ai) let e = ui.governors[pi][ai] - if (region >= 1 && region < 12) { + if (region < 12) { layout_governor(e, PLAYER_CLASS[pi], region) } else { if (region === AVAILABLE) @@ -758,6 +762,7 @@ function on_update() { avail_stack.push(e) } e.classList.toggle("unavailable", region === UNAVAILABLE) + e.classList.toggle("selected", view.selected_governor === pi * 6 + ai) } layout_available(avail_stack, 58, pi * 625 + 325, 27) } @@ -797,9 +802,9 @@ function on_update() { } ui.market.replaceChildren() - for (let pile of view.market) { - if (pile.length > 0) - ui.market.appendChild(ui.cards[pile[0]]) + for (let c of view.market) { + if (c > 0) + ui.market.appendChild(ui.cards[c]) } for (let e of action_register) @@ -1,5 +1,7 @@ "use strict" +// TODO: barbarian leaders -> barbarian list + var game var view const states = {} @@ -269,6 +271,22 @@ function card_name(c) { return "??" } +function card_cost(c) { + if (c >= CARD_M1[0] && c <= CARD_M1[1]) return 1 + if (c >= CARD_M2[0] && c <= CARD_M2[1]) return 2 + if (c >= CARD_M3[0] && c <= CARD_M3[1]) return 3 + if (c >= CARD_M4[0] && c <= CARD_M4[1]) return 4 + if (c >= CARD_S1[0] && c <= CARD_S1[1]) return 1 + if (c >= CARD_S2[0] && c <= CARD_S2[1]) return 2 + if (c >= CARD_S3[0] && c <= CARD_S3[1]) return 3 + if (c >= CARD_S4[0] && c <= CARD_S4[1]) return 4 + if (c >= CARD_P1[0] && c <= CARD_P1[1]) return 1 + if (c >= CARD_P2[0] && c <= CARD_P2[1]) return 2 + if (c >= CARD_P3[0] && c <= CARD_P3[1]) return 3 + if (c >= CARD_P4[0] && c <= CARD_P4[1]) return 4 + return "??" +} + function card_event_name(c) { if (c >= CARD_M1[0] && c <= CARD_M1[1]) return "None" if (c >= CARD_M2[0] && c <= CARD_M2[1]) return "Castra" @@ -368,7 +386,7 @@ function add_militia(province) { game.militia |= (1 << province) } function remove_militia(province) { game.militia &= ~(1 << province) } function has_mob(province) { return game.mobs[province] > 0 } -function get_mobs(province) { return game.mobs[province] } +function count_mobs(province) { return game.mobs[province] } function add_one_mob(province) { game.mobs[province] ++ } function remove_one_mob(province) { game.mobs[province] -- } function remove_all_mobs(province) { game.mobs[province] = 0 } @@ -441,20 +459,30 @@ function find_governor(f) { return -1 } -function some_general(f) { - let n = game.legacy.length * 6 +function for_each_barbarian(f) { + let n = game.barbarians.length for (let id = 0; id < n; ++id) - if (f(id, get_general_location(id), is_general_inside_capital(id))) - return true - return false + f(id, get_barbarian_location(id), is_barbarian_inactive(id)) } -function some_governor(f) { - let n = game.legacy.length * 6 +function find_barbarian(f) { + let n = game.barbarians.length for (let id = 0; id < n; ++id) - if (f(id, get_governor_location(id))) - return true - return false + if (f(id, get_barbarian_location(id), is_barbarian_active(id))) + return id + return -1 +} + +function some_governor(f) { + return find_governor(f) >= 0 +} + +function some_general(f) { + return find_general(f) >= 0 +} + +function some_barbarian(f) { + return find_barbarian(f) >= 0 } function next_player() { @@ -480,7 +508,7 @@ function can_build_improvement(province) { return true } -function update_italia_support() { +function update_neutral_italia() { if (is_neutral_province(ITALIA)) { let n = 1 for (let s = 1; s < 12; ++s) @@ -507,13 +535,35 @@ function can_enter_capital(where) { // Occupied by opponent Militia if (has_militia(where)) { - if (!is_province_you_govern(where)) + if (!is_own_province(where)) return false } return true } +function is_own_general(id) { + let a = game.current * 6 + return id >= a && id < a + 6 +} + +function is_enemy_general(id) { + return id >= 0 && !is_own_general(id) +} + +function is_own_governor(id) { + let a = game.current * 6 + return id >= a && id < a + 6 +} + +function is_enemy_governor(id) { + return id >= 0 && !is_own_governor(id) +} + +function is_capital_free_of_enemy(where) { + return is_enemy_general(get_capital_general(where)) +} + function get_province_governor(where) { return find_governor((id, loc) => loc === where) } @@ -528,24 +578,53 @@ function is_neutral_province(where) { return get_province_governor(where) < 0 } -function is_province_you_govern(where) { - let a = game.current * 6 - let id = get_province_governor(where) - if (id >= a && id < a + 6) +function has_active_barbarians(where) { + if (has_barbarian_leader(where)) return true - return false + return some_barbarian((id, loc, active) => loc === where && active) } -function has_active_barbarians(where) { - return false // TODO +function has_barbarian_leader(where) { + for (let i = 0; i < 3; ++i) + if (game.barbarian_leaders[i] === where) + return true + return false } function has_rival_emperor(where) { - return false // TODO + for (let i = 0; i < 3; ++i) + if (game.rival_emperors[i] === where) + return true + return false +} + +function is_enemy_province(where) { + return is_enemy_governor(get_province_governor(where)) +} + +function is_own_province(where) { + return is_own_governor(get_province_governor(where)) +} + +function is_emperor(governor) { + let emperor = get_province_governor(ITALIA) + if (emperor >= 0 && governor >= 0) + return (emperor / 6 | 0) === (governor / 6 | 0) + return false } function has_enemy_army_in_capital(where) { - return false // TODO + if (is_enemy_general(get_capital_general(where))) + return true + if (has_militia(where) && is_enemy_governor(get_province_governor(where))) + return true + return false +} + +function has_enemy_army_in_province(where) { + if (some_general((id, loc) => loc === where && is_enemy_general(id))) + return true + return false } function spend_ip(type, n) { @@ -553,7 +632,7 @@ function spend_ip(type, n) { } function can_place_governor(where) { - if (is_province_you_govern(where)) + if (is_own_province(where)) return false // Recalled or already attempted to place. if (has_placed_governor(where)) @@ -564,7 +643,7 @@ function can_place_governor(where) { return true } -function find_active_barbarian(tribe) { +function find_active_barbarian_at_home(tribe) { let home = BARBARIAN_HOMELAND[tribe] for (let i = 0; i < 10; ++i) if (get_barbarian_location(tribe * 10 + i) === home) @@ -606,7 +685,6 @@ function count_barbarians_in_province(tribe, where) { return n } - function count_legions_in_army(id) { let n = 0 for (let i = 0; i < LEGION_COUNT; ++i) @@ -637,6 +715,22 @@ function count_units_in_army(id) { return n } +function count_own_provinces() { + let n = 0 + for (let where = 0; where < 12; ++where) + if (is_own_province(where)) + ++n + return n +} + +function count_own_basilicas() { + let n = 0 + for (let where = 0; where < 12; ++where) + if (is_own_province(where) && has_basilica(where)) + ++n + return n +} + function roll_dice(count, target) { let hits = 0 console.log("roll_dice", count, target) @@ -717,6 +811,24 @@ function goto_start_turn() { } function goto_upkeep() { + // TODO: manually remove Quaestor and Castra? + for (let i = 0; i < 6; ++i) { + let id = game.current * 6 + i + let where = get_governor_location(id) + if (is_province(where) && has_quaestor(where)) { + log("Removed Quaestor from S" + where) + remove_quaestor(where) + } + if (has_militia_castra(where)) { + log("Removed Castra from S" + where) + remove_militia_castra(where) + } + if (has_general_castra(id)) { + log("Removed Castra from S" + get_general_location(id)) + remove_general_castra(id) + } + } + goto_crisis() } @@ -732,6 +844,8 @@ function goto_crisis() { let sum = game.dice[0] + game.dice[1] + return goto_take_actions() // XXX + if (sum === 2) return goto_ira_deorum() if (sum === 12) @@ -787,7 +901,7 @@ function goto_barbarian_crisis(tribe) { function invade_with_active_barbarian(tribe, where) { // TODO: move leaders first - let b = find_active_barbarian(tribe) + let b = find_active_barbarian_at_home(tribe) if (b >= 0) set_barbarian_location(tribe * 10 + b, where) } @@ -829,9 +943,52 @@ function prompt(s) { view.prompt = s } -function prompt_take_actions(label) { +function prompt_take_actions(label, sel_gov, sel_gen) { let [ mip, sip, pip ] = game.ip + prompt(`${label}: ${mip} Military, ${sip} Senate, ${pip} Populace.`) + + if (sel_gov >= 0) + view.selected_governor = sel_gov + if (sel_gen >= 0) + view.selected_general = sel_gen + + for (let c of current_hand()) + gen_action_card(c) + + for (let i = 0; i < 6; ++i) { + let id = game.current * 6 + i + if (id !== sel_gov) { + let where = get_governor_location(id) + if ((where === UNAVAILABLE) && (sip >= i)) + gen_action_governor(id) + if ((where === AVAILABLE) && (sip >= 1)) + gen_action_governor(id) + if (is_region(where) && (sip >= 2 || pip >= 2)) + gen_action_governor(id) + } + } + + for (let i = 0; i < 6; ++i) { + let id = game.current * 6 + i + if (id !== sel_gen) { + let where = get_general_location(id) + if (where === UNAVAILABLE && mip >= i) + gen_action_general(id) + if (where === AVAILABLE && mip >= 1) + gen_action_general(id) + if (is_region(where)) { + if (mip >= 1) + gen_action_general(id) + else if (is_province(where)) { + if (is_general_inside_capital(id) || can_enter_capital(where)) + gen_action_general(id) + } + } + } + } + + view.actions.end_actions = 1 } function action_take_actions_card(c) { @@ -843,11 +1000,23 @@ function action_take_actions_card(c) { add_card_ip(c) } else if (set_has(game.played, c)) { log("TODO - use event") - set_remove(game.played, c) + set_delete(game.played, c) set_add(current_discard(), c) } } +function action_take_actions_governor(id) { + push_undo() + game.who = id + game.state = "take_actions_governor" +} + +function action_take_actions_general(id) { + push_undo() + game.who = id + game.state = "take_actions_general" +} + function action_take_actions_end_actions() { push_undo() goto_support_check() @@ -857,85 +1026,21 @@ states.take_actions = { prompt() { let player = game.current let [ mip, sip, pip ] = game.ip - - prompt_take_actions("Take Actions") - for (let c of current_hand()) - gen_action_card(c) - - for (let i = 0; i < 6; ++i) { - let id = game.current * 6 + i - let where = get_governor_location(id) - if ((where === UNAVAILABLE) && (sip >= i)) - gen_action_governor(id) - if ((where === AVAILABLE) && (sip >= 1)) - gen_action_governor(id) - if (is_region(where) && (sip >= 1 || pip >= 1)) - gen_action_governor(id) - } - - for (let i = 0; i < 6; ++i) { - let id = game.current * 6 + i - let where = get_general_location(id) - if ((where === UNAVAILABLE) && (mip >= i)) - gen_action_general(id) - if ((where === AVAILABLE) && (mip >= 1)) - gen_action_general(id) - // if (is_region(where) && (mip >= 1)) - if (is_region(where) && (mip >= 1 || where <= 12)) - gen_action_general(id) - } - - view.actions.end_actions = 1 + prompt_take_actions("Take Actions", -1, -1) }, - card: action_take_actions_card, end_actions: action_take_actions_end_actions, - governor(id) { - push_undo() - let where = get_governor_location(id) - if (where === UNAVAILABLE) { - spend_ip(SENATE, id % 6) - set_governor_location(id, AVAILABLE) - } - else if (where === AVAILABLE) { - game.who = id - spend_ip(SENATE, 1) - game.state = "place_governor_where" - game.misc = { spend: 1, where: -1 } - } - else { - game.who = id - game.state = "take_actions_governor" - } - }, - general(id) { - push_undo() - let where = get_general_location(id) - if (where === UNAVAILABLE) { - spend_ip(MILITARY, id % 6) - set_general_location(id, AVAILABLE) - } - else if (where === AVAILABLE) { - spend_ip(MILITARY, 1) - game.who = id - game.state = "create_army" - } - else { - game.who = id - game.state = "take_actions_general" - } - }, + card: action_take_actions_card, + governor: action_take_actions_governor, + general: action_take_actions_general, } states.take_actions_governor = { prompt() { let [ mip, sip, pip ] = game.ip - let governor = game.who + let where = get_governor_location(game.who) - prompt_take_actions("Take Governor Actions") - for (let c of current_hand()) - gen_action_card(c) - view.actions.done = 1 + prompt_take_actions("Take Governor Actions", game.who, -1) // Recruit Governor if (where === UNAVAILABLE) { @@ -949,21 +1054,20 @@ states.take_actions_governor = { view.actions.place = 1 } - if (where >= 0) { + if (is_province(where)) { // Recall Governor if (sip >= 2) view.actions.recall = 1 // Increase Support Level let support = game.support[where] - if (support < 4 && where !== ITALIA) { + if (where !== ITALIA && support < 4) { if (pip > support) view.actions.support = 1 - } // Place Militia - if (!has_militia(where)) { + if (!has_militia(where) && is_capital_free_of_enemy(where)) { if (pip >= 2) view.actions.militia = 1 } @@ -982,14 +1086,19 @@ states.take_actions_governor = { } // TODO: Initiate Battle with Militia not stacked with General + if (has_militia(where) && !is_own_general(get_capital_general(where))) { + if (has_enemy_army_in_province(where)) + if (mip >= 1) + view.actions.initiate_battle = 1 + } } }, - card: action_take_actions_card, + end_actions: action_take_actions_end_actions, - governor(id) { - push_undo() - game.state = "take_actions" - }, + card: action_take_actions_card, + governor: action_take_actions_governor, + general: action_take_actions_general, + recruit() { push_undo() log("Recruited Governor " + (game.who % 6) + ".") @@ -999,7 +1108,7 @@ states.take_actions_governor = { place() { push_undo() spend_ip(SENATE, 1) - game.state = "place_governor" + game.state = "place_governor_where" game.misc = { spend: 1 } }, recall() { @@ -1007,10 +1116,9 @@ states.take_actions_governor = { let where = get_governor_location(game.who) log("Recalled Governor from S" + where + ".") spend_ip(SENATE, 2) - set_governor_location(game.who, AVAILABLE) - set_support(where, 1) set_placed_governor(where) - update_italia_support() + remove_governor(where) + update_neutral_italia() }, support() { push_undo() @@ -1032,9 +1140,10 @@ states.take_actions_governor = { game.state = "build_improvement" spend_ip(POPULACE, 3) }, - done() { + initiate_battle() { push_undo() - game.state = "take_actions" + game.state = "initiate_battle" + game.misc = { attacker: -1, where: get_governor_location(game.who) } }, } @@ -1043,12 +1152,7 @@ states.take_actions_general = { let [ mip, sip, pip ] = game.ip let where = get_general_location(game.who) - prompt_take_actions("Take General Actions") - for (let c of current_hand()) - gen_action_card(c) - view.actions.done = 1 - - gen_action_general(game.who) + prompt_take_actions("Take General Actions", -1, game.who) // Recruit General if (where === UNAVAILABLE) { @@ -1062,9 +1166,9 @@ states.take_actions_general = { view.actions.create_army = 1 } - if (where >= 0) { + if (is_region(where)) { // Add Legion to Army - if (is_province_you_govern(where)) { + if (is_own_province(where)) { let cost = count_legions_in_army(game.who) + 1 if (mip >= cost) view.actions.add_legion_to_army = 1 @@ -1087,62 +1191,70 @@ states.take_actions_general = { if (mip >= 1) { for (let to of ADJACENT[where]) { if (!is_sea(to)) - gen_action_region(to) + gen_move_to_region(to) else if (mip >= 2) - gen_action_region(to) + gen_move_to_region(to) } } // Initiate Battle + if (has_enemy_army_in_province(where)) + if (mip >= 1) + view.actions.initiate_battle = 1 - // Free Action: Enter/Leave capital - if (where < 12) { - if (is_general_inside_capital(game.who)) + // Free Action: Enter/Leave Capital + if (is_province(where)) { + if (is_general_inside_capital(game.who)) { view.actions.leave = 1 - else if (can_enter_capital(where)) + } else if (can_enter_capital(where)) { view.actions.enter = 1 + gen_action_capital(where) + } } } } }, - card: action_take_actions_card, + end_actions: action_take_actions_end_actions, - general(id) { + card: action_take_actions_card, + governor: action_take_actions_governor, + general: action_take_actions_general, + + recruit() { push_undo() - game.state = "take_actions" + log("Recruited General " + (game.who % 6) + ".") + spend_ip(MILITARY, game.who % 6) + set_general_location(game.who, AVAILABLE) }, - recruit() { + create_army() { push_undo() - let general = game.who - log("Recruited General " + general + ".") - spend_ip(MILITARY, general) - set_piece_location(game.who, AVAILABLE) + spend_ip(MILITARY, 1) + game.state = "create_army" }, add_legion_to_army() { push_undo() - let general = game.who - let cost = count_legions_in_army(game.who) + 1 log("Added Legion to Army.") + let cost = count_legions_in_army(game.who) + 1 spend_ip(MILITARY, cost) set_legion_location(find_unused_legion(), ARMY + game.who) }, train_legions() { push_undo() - let general = game.who - let i = find_reduced_legion_in_army(game.who) log("Trained Legions.") spend_ip(MILITARY, 1) - set_legion_full_strength(i) + set_legion_full_strength(find_reduced_legion_in_army(game.who)) }, - create_army() { + region(to) { push_undo() - spend_ip(MILITARY, 1) - game.state = "create_army" + move_army_to(game.who, to, false) }, - region(to) { + capital(to) { push_undo() - move_army_to(game.who, to) + if (get_general_location(game.who) === to) + set_general_inside_capital(game.who) + else + move_army_to(game.who, to, true) }, enter() { push_undo() @@ -1152,71 +1264,108 @@ states.take_actions_general = { push_undo() set_general_outside_capital(game.who) }, - capital() { - this.enter() - }, - done() { + initiate_battle() { push_undo() - game.state = "take_actions" + game.state = "initiate_battle" + game.misc = { attacker: game.who, where: get_general_location(game.who) } }, } -states.place_governor_where = { - prompt() { - prompt("Place Governor.") - for (let where = 0; where < 12; ++where) { - if (can_place_governor(where)) - gen_action_region(where) - } - }, - region(where) { - push_undo() - game.misc.where = where - game.state = "place_governor_spend" - }, +// ACTION: PLACE GOVERNOR + +function reduce_support(where) { + if (game.support[where] === 1) + remove_governor(where) + else + game.support[where] -= 1 } -// ACTION: PLACE GOVERNOR +function increase_support(where) { + game.support[where] += 1 +} -function place_governor(new_governor, where) { - let support = get_support(where) - let new_support = Math.max(1, support - 1) +function remove_governor(where) { + log("Removed Governor from S" + where) - // TODO: Italia support + remove_all_mobs(where) let old_governor = get_province_governor(where) - if (old_governor >= 0) + if (old_governor >= 0) { + set_governor_location(old_governor, AVAILABLE) + if (where !== ITALIA && is_emperor(old_governor)) + reduce_support(ITALIA) + } + + if (where !== ITALIA) + game.support[where] = 1 + + update_neutral_italia() +} + +function place_governor(where, new_governor) { + remove_all_mobs(where) + + let old_governor = get_province_governor(where) + if (old_governor >= 0) { set_governor_location(old_governor, AVAILABLE) + if (where !== ITALIA && is_emperor(old_governor)) + reduce_support(ITALIA) + } set_governor_location(new_governor, where) - set_support(where, new_support) + if (where === ITALIA) + game.support[where] = count_own_provinces() + else + game.support[where] = Math.max(1, game.support[where] - 1) + + if (where !== ITALIA && is_emperor(new_governor)) + increase_support(ITALIA) + + update_neutral_italia() } function calc_needed_votes(where) { let n = get_support(where) * 2 // base number of votes let old_governor = get_province_governor(where) - if (old_governor >= 0) { - let old_player = old_governor / 6 | 0 - let army_general = get_capital_general(where) - if (army_general >= 0) { - let army_player = army_general / 6 | 0 - let army_size = count_units_in_army(army_general) - if (army_player === old_player) - n += army_size - else if (army_player === game.current) - n -= army_size - } - if (has_militia(where)) - n += 1 + let old_player = (old_governor < 0) ? -1 : (old_governor / 6 | 0) + let army_general = get_capital_general(where) + if (army_general >= 0) { + let army_player = army_general / 6 | 0 + let army_size = count_units_in_army(army_general) + console.log("VOTES", old_player, army_player, game.current) + if (army_player === old_player) + n += army_size + else if (army_player === game.current) + n -= army_size } + if (has_militia(where)) + n += 1 console.log("votes needed", where, n) return Math.max(1, n) } +states.place_governor_where = { + prompt() { + prompt("Place Governor.") + view.selected_governor = game.who + for (let where = 0; where < 12; ++where) { + if (can_place_governor(where)) + gen_action_region(where) + } + }, + region(where) { + push_undo() + game.misc.where = where + game.state = "place_governor_spend" + }, +} + states.place_governor_spend = { prompt() { let [ mip, sip, pip ] = game.ip let need = calc_needed_votes(game.misc.where) + view.selected_governor = game.who + view.selected_region = game.misc.where prompt("Place Governor: " + game.misc.spend + " IP spent; " + need + " votes needed.") if (sip >= 1) view.actions.spend = 1 @@ -1235,6 +1384,9 @@ states.place_governor_spend = { set_placed_governor(game.misc.where) + if (game.misc.where === ITALIA) + game.misc.spend += count_own_basilicas() + log("Place Governor in S" + game.misc.where) if (is_neutral_province(game.misc.where)) have = roll_dice(game.misc.spend, 1) @@ -1243,11 +1395,15 @@ states.place_governor_spend = { else have = roll_dice(game.misc.spend, 2) - if (have >= need) - place_governor(game.who, game.misc.where) + if (have >= need) { + logi("Success!") + place_governor(game.misc.where, game.who) + } else { + logi("Failed!") + } game.misc = null - game.state = "take_actions" + game.state = "take_actions_governor" }, } @@ -1256,6 +1412,7 @@ states.place_governor_spend = { states.build_improvement = { prompt() { let where = get_governor_location(game.who) + view.selected_governor = game.who prompt("Build Improvement.") if (!has_amphitheater(where)) view.actions.amphitheater = 1 @@ -1308,41 +1465,81 @@ states.create_army = { // ACTION: MOVE ARMY +function gen_move_to_region(to) { + if (is_province(to) && can_enter_capital(to)) + gen_action_capital(to) + gen_action_region(to) +} + states.move_army_at_sea = { prompt() { - prompt("Move Army.") let [ mip, sip, pip ] = game.ip - let who = view.who = game.who - let from = get_piece_location(who) + prompt("Move Army.") + view.selected_general = game.who + let from = get_general_location(game.who) for (let to of ADJACENT[from]) { if (is_sea(to)) { if (mip >= 2) - gen_action_region(to) + gen_move_to_region(to) } else { - gen_action_region(to) + gen_move_to_region(to) } } }, region(to) { push_undo() - move_army_to(game.who, to) + move_army_to(game.who, to, false) + }, + capital(to) { + push_undo() + move_army_to(game.who, to, true) }, } -function move_army_to(who, to) { +function move_army_to(who, to, go_inside) { log("Moved Army to S" + to + ".") spend_ip(MILITARY, 1) set_general_location(who, to) - if (can_enter_capital(to)) + if (can_enter_capital(to) && go_inside) set_general_inside_capital(who) if (is_sea(to)) game.state = "move_army_at_sea" - else if (game.ip[MILITARY] > 0) - game.state = "take_actions_general" else - game.state = "take_actions" + game.state = "take_actions_general" +} + +// === ACTION: INITIATE BATTLE === + +states.initiate_battle = { + prompt() { + prompt("Initiate Battle in " + REGION_NAME[game.misc.where] + "!") + + for_each_general((id, loc) => { + if (loc === game.misc.where && is_enemy_general(id)) + gen_action_general(id) + }) + + for_each_barbarian((id, loc) => { + if (loc === game.misc.where) + gen_action_barbarian(id) + }) + + if (is_enemy_province(game.misc.where)) { + if (has_militia(game.misc.where) && is_capital_free_of_enemy(where)) + gen_action_militia(game.misc.where) + } + }, + general(id) { + log("Initiate Battle vs Gen" + id) + }, + barbarian(id) { + log("Initiate Battle vs B" + id) + }, + militia(where) { + log("Initiate Battle vs Militia" + id) + }, } // === SUPPORT CHECK === @@ -1365,23 +1562,138 @@ function goto_gain_legacy() { // === BUY / TRASH CARDS === +function count_political_points() { + let pp = 0 + for (let where = 0; where < 12; ++where) { + if (is_own_province(where)) { + pp += game.support[where] + pp -= game.mobs[where] + } + } + return pp +} + function goto_buy_trash_cards() { let discard = current_discard() for (let c of game.played) { set_add(discard, c) } game.played.length = 0 - game.state = "buy_trash_discard" game.misc = { count: 0, - pp: 0, + pp: count_political_points(), } - goto_end_of_turn() + if (current_hand().length > 0) + game.state = "buy_trash_discard" + else + game.state = "buy_trash" +} + +states.buy_trash_discard = { + prompt() { + prompt("You may discard any number of cards.") + for (let c of current_hand()) + gen_action_card(c) + view.actions.done = 1 + }, + card(c) { + push_undo() + set_delete(current_hand(), c) + set_add(current_discard(), c) + }, + done() { + push_undo() + game.state = "buy_trash" + }, +} + +function find_market_with_card(c) { + for (let m of game.market) + if (set_has(m, c)) + return m + return null +} + +states.buy_trash = { + prompt() { + prompt("Buy/Trash cards: " + game.misc.pp + "PP left.") + let nprov = count_own_provinces() + if (game.misc.pp >= 3) { + for (let c of current_discard()) + gen_action_card(c) + } + for (let m of game.market) { + if (m.length > 0) { + let c = m[0] + let cost = card_cost(c) + if (cost > nprov) + cost *= 2 + cost += game.misc.count + if (game.misc.pp >= cost) + gen_action_card(c) + } + } + view.actions.done = 1 + }, + card(c) { + push_undo() + if (set_has(current_discard(), c)) { + log("Trashed " + card_name(c)) + set_delete(current_discard(), c) + game.misc.pp -= 3 + } else { + log("Bought " + card_name(c)) + set_add(current_discard(), c) + set_delete(find_market_with_card(c), c) + let cost = card_cost(c) + if (cost > count_own_provinces()) + cost *= 2 + cost += game.misc.count + game.misc.pp -= cost + game.misc.count += 1 + } + }, + done() { + push_undo() + goto_end_of_turn() + }, } // === END OF TURN === function goto_end_of_turn() { + // TODO: add mobs + // TODO: flip inactive barbarians + game.state = "refill_hand" +} + +states.refill_hand = { + prompt() { + view.prompt = "Refill your hand." + let hand = current_hand() + if (hand.length < 5) { + for (let c of current_draw()) + gen_action_card(c) + } else { + view.actions.done = 1 + } + }, + card(c) { + push_undo() + set_delete(current_draw(), c) + set_add(current_hand(), c) + if (current_draw().length === 0) { + game.draw[game.current] = game.discard[game.current] + game.discard[game.current] = [] + } + }, + done() { + clear_undo() + end_refill_hand() + } +} + +function end_refill_hand() { game.current = next_player() goto_start_turn() } @@ -1468,7 +1780,7 @@ exports.setup = function (seed, scenario, options) { generals: new Array(6 * player_count).fill(UNAVAILABLE), legions: new Array(LEGION_COUNT).fill(AVAILABLE), barbarians: new Array(10 * tribe_count).fill(0), - rivals: [ UNAVAILABLE, UNAVAILABLE, UNAVAILABLE ], + rival_emperors: [ UNAVAILABLE, UNAVAILABLE, UNAVAILABLE ], barbarian_leaders: [ UNAVAILABLE, UNAVAILABLE, UNAVAILABLE ], dice: [ 0, 0, 0, 0 ], // first two are crisis table dice, second two are barbarian homeland dice @@ -1513,7 +1825,7 @@ exports.setup = function (seed, scenario, options) { game.discard[player] = [] } - update_italia_support() + update_neutral_italia() game.first = game.current = random(player_count) log("First Player is " + PLAYER_NAMES[game.first] + "!") @@ -1579,13 +1891,13 @@ exports.view = function (state, player_name) { generals: game.generals, legions: game.legions, barbarians: game.barbarians, - rivals: game.rivals, + rival_emperors: game.rival_emperors, barbarian_leaders: game.barbarian_leaders, dice: game.dice, events: game.active_events, played: game.played, - market: game.market, + market: game.market.map(m => m[0] | 0), legacy: game.legacy, emperor_turns: game.emperor_turns, @@ -1678,6 +1990,10 @@ function gen_action_region(where) { gen_action("region", where) } +function gen_action_capital(where) { + gen_action("capital", where) +} + function gen_action_card(c) { gen_action("card", c) } |