From a13ebf1939ca4418b7c830ee353083cf7b1b3d94 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Fri, 23 Jun 2023 16:55:53 +0200 Subject: Events + Barbarian Leaders + Rival Emperors. --- play.css | 3 + play.html | 3 +- play.js | 73 ++++++----- rules.js | 426 +++++++++++++++++++++++++++++++++++++++++++++----------------- 4 files changed, 360 insertions(+), 145 deletions(-) diff --git a/play.css b/play.css index 45e1c09..f21cb33 100644 --- a/play.css +++ b/play.css @@ -392,6 +392,9 @@ body.p2 #Galatia_NPG { display: block } #ardashir { background-image: url(images/ardashir.png) } #cniva { background-image: url(images/cniva.png) } #shapur { background-image: url(images/shapur.png) } +#ardashir.inactive { background-image: url(images/ardashir_back.png) } +#cniva.inactive { background-image: url(images/cniva_back.png) } +#shapur.inactive { background-image: url(images/shapur_back.png) } #postumus { background-image: url(images/rival_postumus.png) } #priest_king { background-image: url(images/rival_priest_king.png) } #zenobia { background-image: url(images/rival_zenobia.png) } diff --git a/play.html b/play.html index 5038688..2a68045 100644 --- a/play.html +++ b/play.html @@ -51,7 +51,6 @@
-
@@ -77,6 +76,8 @@ +
+
diff --git a/play.js b/play.js index 8a5cf42..da4efe3 100644 --- a/play.js +++ b/play.js @@ -1,10 +1,13 @@ "use strict" // TODO: battle dialog popup for rolling and assigning hits! -// TODO: move barbarian dice to current homeland +// TODO: show killed leaders taken for bonus purchase // === SYNC with rules.js === +const LEGION_COUNT = 33 +const BARBARIAN_COUNT = 53 + const CARD_M1 = [ 0, 11 ] const CARD_S1 = [ 12, 23 ] const CARD_P1 = [ 24, 35 ] @@ -77,6 +80,12 @@ const BRITANNIA = 9 const GALATIA = 10 const SYRIA = 11 +const ALAMANNI = 0 +const FRANKS = 1 +const GOTHS = 2 +const NOMADS = 3 +const SASSANIDS = 4 + const ALAMANNI_HOMELAND = 12 const FRANKS_HOMELAND = 13 const GOTHS_HOMELAND = 14 @@ -93,13 +102,15 @@ const UNAVAILABLE = 22 const ARMY = 23 -const TRIBE_COUNT = [ 0, 5, 3, 4, 5 ] -const BARBARIAN_COUNT = [ 0, 50, 30, 40, 50 ] -const first_barbarian = [ 0, 10, 20, 30, 40 ] -const last_barbarian = [ 9, 19, 29, 39, 49 ] +const first_barbarian = [ 0, 10, 20, 31, 41 ] +const last_barbarian = [ 9, 19, 30, 40, 52 ] const first_governor = [ 0, 6, 12, 18 ] const first_general = [ 0, 6, 12, 18 ] +const CNIVA = first_barbarian[GOTHS] + 0 +const ARDASHIR = first_barbarian[SASSANIDS] + 0 +const SHAPUR = first_barbarian[SASSANIDS] + 1 + const REGION_NAME = [ "Italia", "Asia", @@ -740,7 +751,7 @@ function on_init() { ui.event_cards[e] = create("div", { className: "card event event_" + e }) } - for (let i = 0; i < 33; ++i) + for (let i = 0; i < LEGION_COUNT; ++i) ui.legions[i] = create_piece(i, "legion", "legion", "legion_" + i) for (let p = 0; p < 4; ++p) { @@ -754,10 +765,6 @@ function on_init() { } } - ui.barbarian_leaders[0] = create_piece(0, "barbarian_leader", "goths", "cniva") - ui.barbarian_leaders[1] = create_piece(1, "barbarian_leader", "sassanids", "ardashir") - ui.barbarian_leaders[2] = create_piece(2, "barbarian_leader", "sassanids", "shapur") - ui.rival_emperors[0] = create_piece(0, "rival_emperor", "rival_emperor", "postumus") ui.rival_emperors[1] = create_piece(1, "rival_emperor", "rival_emperor", "priest_king") ui.rival_emperors[2] = create_piece(2, "rival_emperor", "rival_emperor", "zenobia") @@ -766,6 +773,10 @@ function on_init() { for (let id = first_barbarian[tribe]; id <= last_barbarian[tribe]; ++id) ui.barbarians[id] = create_piece(id, "barbarian", BARBARIAN_CLASS[tribe]) + ui.barbarians[CNIVA].id = "cniva" + ui.barbarians[SHAPUR].id = "shapur" + ui.barbarians[ARDASHIR].id = "ardashir" + for (let p = 0; p < 4; ++p) { for (let g = 0; g < 6; ++g) { ui.castra[p][g] = create_thing({ className: "castra hide" }) @@ -1013,7 +1024,7 @@ function on_update() { hide(ui.militia[region]) } - for (let i = 0; i < 33; ++i) { + for (let i = 0; i < LEGION_COUNT; ++i) { if (is_legion_unused(i)) hide(ui.legions[i]) else @@ -1024,28 +1035,27 @@ function on_update() { ui.legions[i].classList.toggle("reduced", false) } - // TODO: Cniva, Ardashir, and Shapur - // TODO: Zenobia, Postumus, Priest King - - for (let id = 0; id < BARBARIAN_COUNT[player_count]; ++id) { - show(ui.barbarians[id]) + for (let id = 0; id < BARBARIAN_COUNT; ++id) { + let loc = get_barbarian_location(id) + if (loc === UNAVAILABLE) + hide(ui.barbarians[id]) + else + show(ui.barbarians[id]) if (is_barbarian_inactive(id)) ui.barbarians[id].classList.toggle("inactive", true) else ui.barbarians[id].classList.toggle("inactive", false) } - for (let id = BARBARIAN_COUNT[player_count]; id < ui.barbarians.length; ++id) - hide(ui.barbarians[id]) - stack_count.fill(0) for (let region = 0; region < 12 + 5; ++region) { - for (let tribe = 0; tribe < TRIBE_COUNT[player_count]; ++tribe) { + for (let tribe = 0; tribe < 5; ++tribe) { let active_barbarians = [] let inactive_barbarians = [] - // TODO: Cniva, Ardashir, and Shapur for (let id = first_barbarian[tribe]; id <= last_barbarian[tribe]; ++id) { + if (id === CNIVA || id === ARDASHIR || id === SHAPUR) + continue let loc = get_barbarian_location(id) let inactive = is_barbarian_inactive(id) if (loc === region) { @@ -1065,13 +1075,19 @@ function on_update() { } } - for (let re = 0; re < 3; ++re) { - let loc = get_rival_emperor_location(re) + for (let id of [ CNIVA, ARDASHIR, SHAPUR ]) { + let loc = get_barbarian_location(id) + if (loc !== UNAVAILABLE) + layout_stack(-1, [ ui.barbarians[id] ], loc, false, 8, 8) + } + + for (let id = 0; id < 3; ++id) { + let loc = get_rival_emperor_location(id) if (loc === UNAVAILABLE) - hide(ui.rival_emperors[re]) + hide(ui.rival_emperors[id]) else { - show(ui.rival_emperors[re]) - layout_stack(-1, [ ui.rival_emperors[re] ], loc, false, 8, 8) + show(ui.rival_emperors[id]) + layout_stack(-1, [ ui.rival_emperors[id] ], loc, false, 8, 8) } } @@ -1144,13 +1160,13 @@ function on_update() { stack.push(e) - for (let i = 0; i < 33; ++i) { + for (let i = 0; i < LEGION_COUNT; ++i) { let loc = get_legion_location(i) if (loc === army) stack.push(ui.legions[i]) } - for (let tribe = 0; tribe < TRIBE_COUNT[player_count]; ++tribe) { + for (let tribe = 0; tribe < 5; ++tribe) { for (let id = first_barbarian[tribe]; id <= last_barbarian[tribe]; ++id) { let loc = get_barbarian_location(id) if (loc === army) @@ -1287,6 +1303,7 @@ function on_update() { action_button("end_actions", "End Actions") + action_button("save", "Save") action_button("pass", "Pass") action_button("done", "Done") action_button("undo", "Undo") diff --git a/rules.js b/rules.js index 9b51295..2c866d6 100644 --- a/rules.js +++ b/rules.js @@ -1,28 +1,14 @@ "use strict" -// TODO: barbarian leaders -> barbarian list /* TODO ---- +[ ] cut holes for capital in region svg [ ] crisis pax deorum [ ] combat battle screen - splay stack / dialog for watching contents and taking hits - -[ ] rival emperors - [ ] emperor turns - [ ] combat - [ ] support check - [ ] combat bonus - [ ] buy/trash bonus - -[ ] barbarian leaders - [ ] invasion - [ ] combat bonus - [ ] combat effect - [ ] buy/trash bonus - -[ ] other events +[ ] killed leader stash for buy/trash phase */ @@ -65,6 +51,7 @@ const SENATE = 1 const POPULACE = 2 const LEGION_COUNT = 33 +const BARBARIAN_COUNT = 53 // REGIONS @@ -175,9 +162,20 @@ const GOTHS = 2 const NOMADS = 3 const SASSANIDS = 4 -const BARBARIAN_COUNT = [ 0, 50, 30, 40, 50 ] -const first_barbarian = [ 0, 10, 20, 30, 40 ] -const last_barbarian = [ 9, 19, 29, 39, 49 ] +const first_barbarian = [ 0, 10, 20, 31, 41 ] +const last_barbarian = [ 9, 19, 30, 40, 52 ] + +const CNIVA = first_barbarian[GOTHS] + 0 +const ARDASHIR = first_barbarian[SASSANIDS] + 0 +const SHAPUR = first_barbarian[SASSANIDS] + 1 + +const POSTUMUS = 0 +const PRIEST_KING = 1 +const ZENOBIA = 2 + +const CNIVA_BONUS = 1 << 3 +const ARDASHIR_BONUS = 1 << 4 +const SHAPUR_BONUS = 1 << 5 const GENERAL_NAME = [ "Red 0", "Red 1", "Red 2", "Red 3", "Red 4", "Red 5", @@ -202,14 +200,6 @@ const BARBARIAN_HOMELAND = [ SASSANIDS_HOMELAND, ] -const CNIVA = 0 -const ARDASHIR = 1 -const SHAPUR = 2 - -const POSTUMUS = 0 -const PRIEST_KING = 1 -const ZENOBIA = 2 - const BARBARIAN_INVASION = [ // Alamanni [ @@ -456,6 +446,10 @@ function get_barbarian_tribe(id) { return -1 } +function is_barbarian_leader(id) { + return id === CNIVA || id === ARDASHIR || id === SHAPUR +} + // === BOARD STATE === function is_no_place_governor(where) { @@ -725,8 +719,6 @@ function is_neutral_province(where) { } function has_active_barbarians(where) { - if (has_barbarian_leader(where)) - return true return some_barbarian((id, loc, active) => loc === where && active) } @@ -748,6 +740,13 @@ function find_active_barbarian_of_tribe(where, tribe) { return -1 } +function find_active_non_leader_barbarian_of_tribe(where, tribe) { + for (let id = first_barbarian[tribe]; id <= last_barbarian[tribe]; ++id) + if (!is_barbarian_leader(id) && get_barbarian_location(id) === where && is_barbarian_active(id)) + return id + return -1 +} + function find_inactive_barbarian_of_tribe(where, tribe) { for (let id = first_barbarian[tribe]; id <= last_barbarian[tribe]; ++id) if (get_barbarian_location(id) === where && is_barbarian_inactive(id)) @@ -755,11 +754,11 @@ function find_inactive_barbarian_of_tribe(where, tribe) { return -1 } -function has_barbarian_leader(where) { - for (let i = 0; i < 3; ++i) - if (game.barbarian_leaders[i] === where) - return true - return false +function find_inactive_non_leader_barbarian_of_tribe(where, tribe) { + for (let id = first_barbarian[tribe]; id <= last_barbarian[tribe]; ++id) + if (!is_barbarian_leader(id) && get_barbarian_location(id) === where && is_barbarian_inactive(id)) + return id + return -1 } function has_rival_emperor(where) { @@ -1015,8 +1014,30 @@ function roll_dice_no_reroll(count, target) { function eliminate_barbarian(id) { let tribe = get_barbarian_tribe(id) - set_barbarian_location(id, BARBARIAN_HOMELAND[tribe]) - set_barbarian_inactive(id) + if (is_barbarian_leader(id) && game.battle) { + if (id === CNIVA) { + log("Cniva killed!") + game.battle.killed |= CNIVA_BONUS + } + if (id === ARDASHIR) { + log("Ardashir killed!") + game.battle.killed |= ARDASHIR_BONUS + } + if (id === SHAPUR) { + log("Shapur killed!") + game.battle.killed |= SHAPUR_BONUS + } + set_barbarian_location(id, UNAVAILABLE) + } else { + set_barbarian_location(id, BARBARIAN_HOMELAND[tribe]) + set_barbarian_inactive(id) + } +} + +function eliminate_rival_emperor(id) { + if (game.battle) + game.battle.killed |= (1 << id) + set_rival_emperor_location(id, UNAVAILABLE) } function eliminate_militia(where) { @@ -1035,6 +1056,7 @@ function assign_hit_to_legion(id) { if (is_legion_reduced(id)) { set_legion_location(id, AVAILABLE) if (count_legions_in_army(army) === 0) { + log(GENERAL_NAME[army] + " killed!") set_general_location(army, AVAILABLE) clear_general_battled(army) } @@ -1140,6 +1162,7 @@ states.setup_hand = { function goto_start_turn() { log_h2(PLAYER_NAMES[game.current]) + game.killed = 0 game.battled = 0 game.mbattled = 0 game.placed = 0 @@ -1273,6 +1296,11 @@ function roll_barbarian_crisis() { let black = game.dice[2] = roll_die() let white = game.dice[3] = roll_die() + if (game.active_event === EVENT_RAIDING_PARTIES) + black = Math.max(1, black - 2) + if (game.active_event === EVENT_PREPARING_FOR_WAR) + black = Math.min(6, black + 2) + logi(`B${black} W${white}`) if (black <= count_active_barbarians_at_home(tribe)) @@ -1284,6 +1312,13 @@ function roll_barbarian_crisis() { function goto_barbarian_invasion(tribe, black, white) { logi("Invasion!") game.count = game.dice[2] + + // Ardashir: ALL active Sassanids invade! + if (tribe === SASSANIDS) { + if (get_barbarian_location(ARDASHIR) === SASSANIDS_HOMELAND) + game.count = count_active_barbarians_at_home(tribe) + } + game.state = "barbarian_invasion" } @@ -1351,6 +1386,28 @@ function goto_crisis_event() { goto_take_actions() } +function get_improvement_cost() { + if (game.active_event === EVENT_INFLATION) + return 4 + return 3 +} + +function get_roman_drm() { + if (game.active_event === EVENT_BAD_AUGURIES) + return 1 + if (game.active_event === EVENT_GOOD_AUGURIES) + return -1 + return 0 +} + +function get_plague_hits() { + if (game.active_event === EVENT_PLAGUE_OF_CYPRIAN) { + log("Plague B0") + return 1 + } + return 0 +} + // CRISIS: BARBARIAN LEADER function goto_crisis_barbarian_leader(id, where) { @@ -1365,14 +1422,11 @@ states.crisis_barbarian_leader = { gen_action_region(game.where) }, region(where) { - set_barbarian_leader_location(game.count, game.where) + set_barbarian_location(game.count, game.where) goto_take_actions() }, } -function set_barbarian_leader_location(id, where) { -} - // CRISIS: RIVAL EMPEROR function goto_crisis_rival_emperor(id, where) { @@ -1387,7 +1441,6 @@ states.crisis_rival_emperor = { gen_action_region(game.where) }, region(where) { - console.log("RE", game.count, game.where) set_rival_emperor_location(game.count, game.where) goto_take_actions() }, @@ -1415,23 +1468,33 @@ function resume_palmyra_allies() { states.palmyra_allies = { prompt() { prompt("Palmyra Allies: Remove " + game.count + " active Sassanids.") - let id + let id, where + + where = get_barbarian_location(SHAPUR) + if (where === GALATIA || where === SYRIA || where === SASSANIDS_HOMELAND) + gen_action_barbarian(SHAPUR) + + where = get_barbarian_location(ARDASHIR) + if (where === GALATIA || where === SYRIA || where === SASSANIDS_HOMELAND) + gen_action_barbarian(ARDASHIR) - // TODO: barbarian leaders - id = find_active_barbarian_of_tribe(SASSANIDS, GALATIA) + id = find_active_non_leader_barbarian_of_tribe(SASSANIDS, GALATIA) if (id >= 0) gen_action_barbarian(id) - id = find_active_barbarian_of_tribe(SASSANIDS, SYRIA) + id = find_active_non_leader_barbarian_of_tribe(SASSANIDS, SYRIA) if (id >= 0) gen_action_barbarian(id) - id = find_active_barbarian_of_tribe(SASSANIDS, SASSANIDS_HOMELAND) + id = find_active_non_leader_barbarian_of_tribe(SASSANIDS, SASSANIDS_HOMELAND) if (id >= 0) gen_action_barbarian(id) }, barbarian(id) { push_undo() eliminate_barbarian(id) - resume_palmyra_allies() + if (--game.count === 0) + goto_take_actions() + else + resume_palmyra_allies() }, } @@ -1619,7 +1682,7 @@ states.take_actions = { // Build an Improvement if (can_build_improvement(where)) { - if (pip >= 3) { + if (pip >= get_improvement_cost()) { if (!has_amphitheater(where)) view.actions.amphitheater = 1 if (!has_basilica(where)) @@ -1764,13 +1827,13 @@ states.take_actions = { push_undo() let where = get_governor_location(game.selected_governor) log("Held Games in S" + where + ".") - spend_ip(POPULACE, 3) + spend_ip(POPULACE, 2) remove_one_mob(where) }, amphitheater() { push_undo() - spend_ip(POPULACE, 3) + spend_ip(POPULACE, get_improvement_cost()) let where = get_governor_location(game.selected_governor) add_amphitheater(where) log("Built Amphitheater in S" + where + ".") @@ -1778,7 +1841,7 @@ states.take_actions = { basilica() { push_undo() - spend_ip(POPULACE, 3) + spend_ip(POPULACE, get_improvement_cost()) let where = get_governor_location(game.selected_governor) add_basilica(where) log("Built Basilica in S" + where + ".") @@ -1786,7 +1849,7 @@ states.take_actions = { limes() { push_undo() - spend_ip(POPULACE, 3) + spend_ip(POPULACE, get_improvement_cost()) let where = get_governor_location(game.selected_governor) add_limes(where) log("Built Limes in S" + where + ".") @@ -1880,11 +1943,17 @@ function enter_capital() { if (is_pretender_province(where) && is_enemy_province(where)) { game.count = get_province_governor(where) / 6 | 0 game.where = where - if (is_seat_of_power(where)) + if (is_seat_of_power(where)) { game.state = "occupy_seat_of_power_1" - else + return + } else { game.state = "occupy_breakaway" + return + } } + + if (game.battle) + end_battle() } function resume_occupy_seat_of_power() { @@ -2408,8 +2477,11 @@ states.tribute = { function can_foederati_from_region(where) { let tribe_count = get_tribe_count() for (let tribe = 0; tribe < tribe_count; ++tribe) { - if (find_barbarian_of_tribe(where, tribe) >= 0) + if (find_active_non_leader_barbarian_of_tribe(where, tribe) >= 0) return true + if (is_province(where)) + if (find_inactive_non_leader_barbarian_of_tribe(where, tribe) >= 0) + return true } return false } @@ -2417,11 +2489,11 @@ function can_foederati_from_region(where) { function gen_foederati(where) { let tribe_count = get_tribe_count() for (let tribe = 0; tribe < tribe_count; ++tribe) { - let id = find_active_barbarian_of_tribe(where, tribe) + let id = find_active_non_leader_barbarian_of_tribe(where, tribe) if (id >= 0) gen_action_barbarian(id) if (is_province(where)) { - id = find_inactive_barbarian_of_tribe(where, tribe) + id = find_inactive_non_leader_barbarian_of_tribe(where, tribe) if (id >= 0) gen_action_barbarian(id) } @@ -2609,7 +2681,7 @@ function goto_battle_vs_militia(where, attacker) { function goto_battle(type, where, attacker, target) { spend_ip(MILITARY, 1) game.where = where - game.battle = { type, attacker, target, flanking: 0 } + game.battle = { type, attacker, target, flanking: 0, killed: 0 } game.state = "battle" if (attacker >= 0) { if (is_general_inside_capital(attacker)) @@ -2628,7 +2700,7 @@ function gen_initiate_battle(where) { if (loc === where && is_enemy_general(id)) gen_action_general(id) }) - let tribe_count = get_tribe_count() + let tribe_count = 5 for (let tribe = 0; tribe < tribe_count; ++tribe) { let id = find_active_barbarian_of_tribe(where, tribe) if (id >= 0) @@ -2643,6 +2715,9 @@ function gen_initiate_battle(where) { if (has_lone_militia(where)) gen_action_militia(where) } + for (let id = 0; id < 3; ++id) + if (get_rival_emperor_location(id) === where) + gen_action_rival_emperor(id) } } @@ -2688,44 +2763,40 @@ states.flanking_maneuver = { } function roll_combat_dice() { - game.battle.dhits = roll_attacker_dice() game.battle.dtaken = 0 + game.battle.ataken = 0 - if (game.battle.type === "militia" && has_militia_castra(game.where)) { - log("Castra reduces 1 hit") - if (game.battle.dhits > 0) - game.battle.dhits -= 1 - } - if (game.battle.type === "general" && has_general_castra(game.battle.target)) { - log("Castra reduces 1 hit") - if (game.battle.dhits > 0) - game.battle.dhits -= 1 - } + game.battle.dhits = roll_attacker_dice() + log("Total " + game.battle.dhits + " hits!") game.battle.ahits = roll_defender_dice() - game.battle.ataken = 0 + log("Total " + game.battle.ahits + " hits!") + log_br() } function roll_flanking_maneuver_dice() { log("Flanking Maneuver Reroll") - game.battle.ahits = roll_defender_dice() - game.battle.ataken = 0 + game.battle.dhits = roll_defender_dice() + log("Total " + game.battle.dhits + " hits!") + log_br() } function roll_general_dice(general) { let army = ARMY + general let n = 0 + let drm = get_roman_drm() + log(GENERAL_NAME[general]) if (is_general_inside_capital(general) && has_militia(game.where)) { log("Militia") - n += roll_dice(1, 5) + n += roll_dice(1, 5 + drm) } let full_strength = 0 let reduced = 0 - for (let id = 0; id < 33; ++id) { + for (let id = 0; id < LEGION_COUNT; ++id) { if (get_legion_location(id) === army) { if (is_legion_reduced(id)) reduced += 1 @@ -2735,11 +2806,11 @@ function roll_general_dice(general) { } if (full_strength > 0) { log("Legions") - n += roll_dice(full_strength, 3) + n += roll_dice(full_strength, 3 + drm) } if (reduced > 0) { log("Reduced Legions") - n += roll_dice(reduced, 5) + n += roll_dice(reduced, 5 + drm) } let barbarians = 0 @@ -2748,7 +2819,7 @@ function roll_general_dice(general) { barbarians += 1 if (barbarians > 0) { log("Barbarians") - n += roll_dice(barbarians, 4) + n += roll_dice(barbarians, 4 + drm) } return n @@ -2756,7 +2827,7 @@ function roll_general_dice(general) { function roll_militia_dice() { log("Militia") - return roll_dice(1, 5) + return roll_dice(1, 5 + get_roman_drm()) } function roll_rival_emperor_dice(rival_emperor) { @@ -2771,35 +2842,68 @@ function roll_barbarian_dice(tribe) { for (let id = first_barbarian[tribe]; id <= last_barbarian[tribe]; ++id) if (get_barbarian_location(id) === game.where) if (prov || is_barbarian_active(id)) - n += 1 + n += is_barbarian_leader(id) ? 2 : 1 return roll_dice(n, 4) } function roll_attacker_dice() { log_h3("ATTACKER") + + let n = get_plague_hits() if (game.battle.attacker < 0) - return roll_militia_dice() + n = roll_militia_dice() else - return roll_general_dice(game.battle.attacker) + n = roll_general_dice(game.battle.attacker) + + if (game.battle.type === "militia" && has_militia_castra(game.where)) { + log("Castra reduces 1 hit") + n -= 1 + } + if (game.battle.type === "general" && has_general_castra(game.battle.target)) { + log("Castra reduces 1 hit") + n -= 1 + } + + if (game.battle.type === "barbarians" && get_barbarian_location(SHAPUR) === game.where) { + log("Shapur I reduced 1 hit") + n -= 1 + } + + return Math.max(0, n) } function roll_defender_dice() { log_h3("DEFENDER") + + let n = get_plague_hits() switch (game.battle.type) { case "militia": - return roll_militia_dice() + n = roll_militia_dice() + break case "rival_emperor": - return roll_rival_emperor_dice(game.battle.target) + n = roll_rival_emperor_dice(game.battle.target) + break case "barbarians": - return roll_barbarian_dice(game.battle.target) + n = roll_barbarian_dice(game.battle.target) + break case "general": - return roll_general_dice(game.battle.target) + n = roll_general_dice(game.battle.target) + break } - return 0 + + return Math.max(0, n) } // COMBAT: ASSIGN HITS +function has_hits_rival_emperor(id) { + return get_rival_emperor_location(id) !== UNAVAILABLE +} + +function gen_hits_rival_emperor(id) { + gen_action_rival_emperor(id) +} + function has_hits_militia() { return has_militia(game.where) } @@ -2833,7 +2937,7 @@ function has_hits_general(general) { for (let id = 0; id < game.barbarians.length; ++id) if (get_barbarian_location(id) === army) return true - for (let id = 0; id < 33; ++id) + for (let id = 0; id < LEGION_COUNT; ++id) if (get_legion_location(id) === army) return true return false @@ -2860,7 +2964,7 @@ function gen_hits_general(general) { // NOTE: reduce all legions before eliminating any if (!done) { - for (let id = 0; id < 33; ++id) { + for (let id = 0; id < LEGION_COUNT; ++id) { if (get_legion_location(id) === army && !is_legion_reduced(id)) { gen_action_legion(id) done = true @@ -2869,7 +2973,7 @@ function gen_hits_general(general) { } if (!done) { - for (let id = 0; id < 33; ++id) { + for (let id = 0; id < LEGION_COUNT; ++id) { if (get_legion_location(id) === army && is_legion_reduced(id)) { gen_action_legion(id) done = true @@ -2984,6 +3088,11 @@ states.assign_hits_on_defender = { eliminate_barbarian(id) goto_assign_hits_on_defender() }, + rival_emperor(id) { + game.battle.dtaken += 1 + eliminate_rival_emperor(id) + goto_assign_hits_on_defender() + } } function is_attacker_eliminated() { @@ -3013,7 +3122,6 @@ function is_defender_eliminated() { function goto_combat_victory() { let de = is_defender_eliminated() let ae = is_attacker_eliminated() - console.log("goto_combat_victory", de, ae) if (de && ae) end_battle() else if (de || game.battle.dtaken > game.battle.ataken) @@ -3031,15 +3139,15 @@ function award_legacy(p, reason, n) { } function goto_combat_victory_defender() { + game.battle.killed = 0 if (game.battle.type === "general") award_legacy(game.battle.target / 6 | 0, "Victory", 2) if (game.battle.type === "militia") - award_legacy(get_province_governor(game.where) / 6 | 0, "Victory", 2) + award_legacy(get_province_player(game.where), "Victory", 2) end_battle() } function goto_combat_victory_attacker() { - if (game.battle.type === "barbarians") { award_legacy(game.current, "Victory", 2 + game.battle.dtaken) @@ -3058,8 +3166,6 @@ function goto_combat_victory_attacker() { remove_general_castra(game.battle.target) } - // TODO: barbarian leader / rival emperor bonus - if (can_enter_capital(game.where)) game.state = "advance_after_combat" else @@ -3074,7 +3180,6 @@ states.advance_after_combat = { }, capital(where) { push_undo() - end_battle() enter_capital() }, pass() { @@ -3084,10 +3189,43 @@ states.advance_after_combat = { } function end_battle() { + // Deselect eliminated general... + if (game.selected_general >= 0 && get_general_location(game.selected_general) === AVAILABLE) + game.selected_general = -1 + if (game.battle.killed && can_free_increase_support_level(game.where)) { + game.state = "free_increase_support_level" + return + } game.battle = null game.state = "take_actions" } +function can_free_increase_support_level(where) { + return where !== ITALIA && get_support(where) < 4 && is_own_province(where) +} + +states.free_increase_support_level = { + prompt() { + prompt("Combat: Increase Support Level or use Leader to buy cheaper card?") + view.color = POPULACE + gen_action_region(game.where) + view.actions.save = 1 + }, + region(where) { + push_undo() + increase_support(game.where) + game.battle = null + game.state = "take_actions" + }, + save() { + push_undo() + log("Saved for cost reduction.") + game.killed |= game.battle.killed + game.battle = null + game.state = "take_actions" + }, +} + // === SUPPORT CHECK === function goto_support_check() { @@ -3097,7 +3235,7 @@ function goto_support_check() { function is_any_rival_emperor_or_pretender() { for (let i = 0; i < 3; ++i) - if (get_rival_emperor_location(i) === UNAVAILABLE) + if (get_rival_emperor_location(i) !== UNAVAILABLE) return true for (let where = 0; where < 12; ++where) if (is_seat_of_power(where) && is_enemy_province(where)) @@ -3329,9 +3467,47 @@ function find_market_with_card(c) { return null } +function has_military_card_bonus() { + let military_bonus = 0 + if (game.killed & CNIVA_BONUS) + military_bonus += 2 + if (game.killed & ARDASHIR_BONUS) + military_bonus += 2 + if (game.killed & SHAPUR_BONUS) + military_bonus += 2 + return military_bonus +} + +function has_senate_card_bonus() { + let senate_bonus = 0 + if (game.killed & (1 << POSTUMUS)) + senate_bonus += 2 + if (game.killed & (1 << PRIEST_KING)) + senate_bonus += 2 + if (game.killed & (1 << ZENOBIA)) + senate_bonus += 2 + return senate_bonus +} + +function spend_military_card_bonus() { + game.killed &= ~(CNIVA_BONUS | ARDASHIR_BONUS | SHAPUR_BONUS) +} + +function spend_senate_card_bonus() { + game.killed &= ~((1 << POSTUMUS) | (1 << PRIEST_KING) | (1 << ZENOBIA)) +} + states.buy_trash = { prompt() { + let military_bonus = has_military_card_bonus() + let senate_bonus = has_senate_card_bonus() + prompt("Buy/Trash cards: " + game.pp + "PP left.") + if (military_bonus) + view.prompt += " First Military card is 2 cheaper." + if (senate_bonus) + view.prompt += " First Senate card is 2 cheaper." + let nprov = count_own_provinces() if (game.pp >= 3) { for (let c of current_discard()) @@ -3344,6 +3520,10 @@ states.buy_trash = { if (cost > nprov) cost *= 2 cost += game.count + if (military_bonus && card_influence(c) === MILITARY) + cost -= 2 + if (senate_bonus && card_influence(c) === SENATE) + cost -= 2 if (game.pp >= cost) gen_action_card(c) } @@ -3357,13 +3537,27 @@ states.buy_trash = { set_delete(current_discard(), c) game.pp -= 3 } else { + let military_bonus = has_military_card_bonus() + let senate_bonus = has_senate_card_bonus() + log("Bought " + card_name(c)) set_add(current_discard(), c) set_delete(find_market_with_card(c), c) + let cost = card_value(c) if (cost > count_own_provinces()) cost *= 2 cost += game.count + + if (military_bonus && card_influence(c) === MILITARY) { + spend_military_card_bonus() + cost -= 2 + } + if (senate_bonus && card_influence(c) === SENATE) { + spend_senate_card_bonus() + cost -= 2 + } + game.pp -= cost game.count += 1 } @@ -3588,8 +3782,12 @@ function setup_market_pile(cards) { function setup_barbarians(tribe, home) { for (let id = first_barbarian[tribe]; id <= last_barbarian[tribe]; ++id) { - set_barbarian_location(id, home) - set_barbarian_inactive(id) + if (is_barbarian_leader(id)) { + set_barbarian_location(id, UNAVAILABLE) + } else { + set_barbarian_location(id, home) + set_barbarian_inactive(id) + } } } @@ -3616,6 +3814,7 @@ exports.setup = function (seed, scenario, options) { selected_general: -1, played: [], used: [], + killed: 0, placed: 0, battled: 0, count: 0, @@ -3637,9 +3836,8 @@ exports.setup = function (seed, scenario, options) { governors: new Array(6 * player_count).fill(UNAVAILABLE), generals: new Array(6 * player_count).fill(UNAVAILABLE), legions: new Array(LEGION_COUNT).fill(AVAILABLE), - barbarians: new Array(BARBARIAN_COUNT[player_count]).fill(AVAILABLE), + barbarians: new Array(BARBARIAN_COUNT).fill(AVAILABLE), 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 market: null, @@ -3657,7 +3855,6 @@ exports.setup = function (seed, scenario, options) { game.events = setup_events() - /* game.market = [ setup_market_pile(CARD_M2), setup_market_pile(CARD_S2), @@ -3669,32 +3866,26 @@ exports.setup = function (seed, scenario, options) { setup_market_pile(CARD_S4), setup_market_pile(CARD_P4), ] - */ - game.market = [ - setup_market_pile(CARD_M2), - setup_market_pile(CARD_M3), - setup_market_pile(CARD_M4), - setup_market_pile(CARD_S2), - setup_market_pile(CARD_S3), - setup_market_pile(CARD_S4), - setup_market_pile(CARD_P2), - setup_market_pile(CARD_P3), - setup_market_pile(CARD_P4), - ] setup_barbarians(ALAMANNI, ALAMANNI_HOMELAND) setup_barbarians(FRANKS, FRANKS_HOMELAND) setup_barbarians(GOTHS, GOTHS_HOMELAND) + if (player_count >= 3) setup_barbarians(NOMADS, NOMADS_HOMELAND) + else + setup_barbarians(NOMADS, UNAVAILABLE) + if (player_count >= 4) setup_barbarians(SASSANIDS, SASSANIDS_HOMELAND) + else + setup_barbarians(SASSANIDS, UNAVAILABLE) for (let player = 0; player < player_count; ++player) { game.hand[player] = [] game.draw[player] = setup_player_deck(player) game.discard[player] = [] - for (let i = 0; i < 9; ++i) set_add(game.draw[player], game.market[i].pop()) + // for (let i = 0; i < 9; ++i) set_add(game.draw[player], game.market[i].pop()) } update_neutral_italia() @@ -3768,7 +3959,6 @@ exports.view = function (state, player_name) { legions: game.legions, barbarians: game.barbarians, rival_emperors: game.rival_emperors, - barbarian_leaders: game.barbarian_leaders, dice: game.dice, event: game.active_event, @@ -3872,6 +4062,10 @@ function gen_action_barbarian(id) { gen_action("barbarian", id) } +function gen_action_rival_emperor(id) { + gen_action("rival_emperor", id) +} + function gen_action_militia(where) { gen_action("militia", where) } -- cgit v1.2.3