From 9952652c7c14c4b3e46d5110202112df048f499a Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Sun, 2 Jul 2023 01:26:40 +0200 Subject: Frumentarii and Spiculum. --- rules.js | 208 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 180 insertions(+), 28 deletions(-) diff --git a/rules.js b/rules.js index ed56611..b16070c 100644 --- a/rules.js +++ b/rules.js @@ -1662,6 +1662,12 @@ function goto_take_actions() { game.placed = 0 if (is_emperor_player()) set_placed_governor(ITALIA) + + if (game.frumentarii & (1 << game.current)) { + game.frumentarii &= ~(1 << game.current) + game.count = 2 + game.state = "frumentarii" + } } states.take_actions = { @@ -3022,6 +3028,7 @@ function play_force_march() { } states.force_march_who = { + inactive: "Force March", prompt() { prompt("Force March: Choose an army you command...") for (let i = 0; i < 6; ++i) { @@ -3043,6 +3050,7 @@ states.force_march_who = { } states.force_march = { + inactive: "Force March", prompt() { prompt("Force March: Move Army or Initiate Battle.") @@ -3138,6 +3146,28 @@ function play_frumentarii() { game.frumentarii |= (1 << game.current) } +states.frumentarii = { + inactive: "Frumentarii", + prompt() { + prompt("Frumentarii: Draw 2 cards.") + let hand = current_hand() + let draw = current_draw() + for (let c of draw) + gen_action_card(c) + }, + card(c) { + push_undo() + let hand = current_hand() + let draw = current_draw() + set_delete(draw, c) + set_add(hand, c) + if (draw.length === 0) + flip_discard_to_available() + if (--game.count === 0) + game.state = "take_actions" + }, +} + // CARD: Mobile Vulgus function can_play_mobile_vulgus() { @@ -3156,6 +3186,7 @@ function play_mobile_vulgus() { } states.mobile_vulgus_where = { + inactive: "Mobile Vulgus", prompt() { prompt("Mobile Vulgus: Choose a province...") view.color = POPULACE @@ -3175,6 +3206,7 @@ states.mobile_vulgus_where = { } states.mobile_vulgus = { + inactive: "Mobile Vulgus", prompt() { prompt("Mobile Vulgus: " + game.pip + " populace. Reduce support in " + REGION_NAME[game.where] + ".") let n = get_support(game.where) * 2 + count_units_in_capital(game.where) @@ -3218,17 +3250,19 @@ function play_demagogue() { // === COMBAT === function play_flanking_maneuver() { + log("Flanking Maneuver.") game.combat.flanking = 1 } function play_cavalry() { + log("Cavalry.") game.combat.cavalry = 1 } function play_spiculum() { game.combat.spiculum = 1 + game.combat.castra_used = 0 game.state = "spiculum" - // TODO } function goto_battle_vs_general(where, attacker, target) { @@ -3252,7 +3286,14 @@ function goto_battle(type, where, attacker, target) { log_h2("Battle in %" + where) spend_military(1) game.where = where - game.combat = { type, attacker, target, killed: 0 } + game.combat = { + type, attacker, target, + cavalry: 0, flanking: 0, spiculum: 0, + castra_used: 0, + dtaken: 0, ataken: 0, staken: 0, + dhits: 0, ahits: 0, shits: 0, + killed: 0, + } game.state = "initiate_battle" if (attacker >= 0) { if (is_general_inside_capital(attacker)) @@ -3307,16 +3348,16 @@ states.initiate_battle = { prompt() { prompt("Initiate Battle against " + format_battle_target() + " in " + REGION_NAME[game.where] + ".") - if (!game.combat.flanking && has_card_event(CARD_M3)) { - view.prompt += " Flanking Maneuver?" - gen_card_event(CARD_M3) - } - if (!game.combat.cavalry && has_card_event(CARD_M2X)) { view.prompt += " Cavalry?" gen_card_event(CARD_M2X) } + if (!game.combat.flanking && has_card_event(CARD_M3)) { + view.prompt += " Flanking Maneuver?" + gen_card_event(CARD_M3) + } + if (!game.combat.spiculum && has_card_event(CARD_M4X)) { view.prompt += " Spiculum?" gen_card_event(CARD_M4X) @@ -3326,7 +3367,6 @@ states.initiate_battle = { }, card(c) { push_undo() - log(card_event_name(c) + ".") set_add(game.used, c) play_card_event(c) }, @@ -3340,6 +3380,23 @@ states.initiate_battle = { }, } +states.spiculum = { + show_battle: true, + inactive: "Spiculum", + prompt() { + prompt("Spiculum: Roll two dice hitting on 3+ before the battle.") + view.actions.roll = 1 + }, + roll() { + clear_undo() + log_h3("SPICULUM") + game.combat.shits = roll_spiculum_dice() + log("Total hits: " + game.combat.shits) + log_br() + goto_assign_spiculum_hits() + }, +} + function format_hits() { let s = "Defender rolled " + game.combat.ahits + " hit" if (game.combat.ahits !== 1) @@ -3369,9 +3426,6 @@ states.flanking_maneuver = { } function roll_combat_dice() { - game.combat.dtaken = 0 - game.combat.ataken = 0 - log_h3("DEFENDER") game.combat.ahits = roll_defender_dice() log("Total hits: " + game.combat.ahits) @@ -3454,25 +3508,49 @@ function roll_barbarian_dice(tribe) { return roll_dice(n, 4) } +function roll_spiculum_dice() { + let n = roll_dice(2, 3 + get_roman_drm()) + if (n > 0) { + if (game.combat.type === "militia" && has_militia_castra(game.where)) { + log("Castra reduces 1 hit") + game.combat.castra_used = 1 + n -= 1 + } + if (game.combat.type === "general" && has_general_castra(game.combat.target)) { + log("Castra reduces 1 hit") + game.combat.castra_used = 1 + n -= 1 + } + if (game.combat.type === "barbarians" && get_barbarian_location(SHAPUR) === game.where) { + log("Shapur I reduces 1 hit") + game.combat.castra_used = 1 + n -= 1 + } + } + return n +} + function roll_attacker_dice() { let n = get_plague_hits() if (game.combat.attacker < 0) n += roll_militia_dice() else n += roll_general_dice(game.combat.attacker) - if (game.combat.type === "militia" && has_militia_castra(game.where)) { - log("Castra reduces 1 hit") - n -= 1 - } - if (game.combat.type === "general" && has_general_castra(game.combat.target)) { - log("Castra reduces 1 hit") - n -= 1 - } - if (game.combat.type === "barbarians" && get_barbarian_location(SHAPUR) === game.where) { - log("Shapur I reduces 1 hit") - n -= 1 + if (n > 0 && !game.combat.castra_used) { + if (game.combat.type === "militia" && has_militia_castra(game.where)) { + log("Castra reduces 1 hit") + n -= 1 + } + if (game.combat.type === "general" && has_general_castra(game.combat.target)) { + log("Castra reduces 1 hit") + n -= 1 + } + if (game.combat.type === "barbarians" && get_barbarian_location(SHAPUR) === game.where) { + log("Shapur I reduces 1 hit") + n -= 1 + } } - return Math.max(0, n) + return n } function roll_defender_dice() { @@ -3609,6 +3687,22 @@ function has_hits_on_defender() { return false } +function has_spiculum_hits() { + if (game.combat.staken < game.combat.shits) { + switch (game.combat.type) { + case "militia": + return has_hits_militia() + case "rival_emperor": + return has_hits_rival_emperor(game.combat.target) + case "barbarians": + return has_hits_barbarians(game.combat.target) + case "general": + return has_hits_general(game.combat.target) + } + } + return false +} + function goto_assign_hits() { goto_assign_hits_on_attacker() } @@ -3627,6 +3721,16 @@ function goto_assign_hits_on_defender() { game.state = "combat_victory" } +function goto_assign_spiculum_hits() { + if (has_spiculum_hits()) + game.state = "assign_spiculum_hits" + else if (is_defender_eliminated()) + // In case Spiculum eliminates defender... + game.state = "combat_victory" + else + game.state = "initiate_battle" +} + states.assign_hits_on_attacker = { show_battle: true, get inactive() { return "Combat. " + format_hits() }, @@ -3703,6 +3807,52 @@ states.assign_hits_on_defender = { } } +states.assign_spiculum_hits = { + show_battle: true, + get inactive() { return "Spiculum. " + game.combat.shits + " hits." }, + prompt() { + prompt("Spiculum: " + game.combat.shits + " hits.") + switch (game.combat.type) { + case "militia": + gen_hits_militia() + break + case "rival_emperor": + gen_hits_rival_emperor(game.combat.target) + break + case "barbarians": + gen_hits_barbarians(game.combat.target) + break + case "general": + gen_hits_general(game.combat.target) + break + } + }, + militia(where) { + push_undo() + game.combat.staken += 1 + eliminate_militia(where) + goto_assign_spiculum_hits() + }, + legion(id) { + push_undo() + game.combat.staken += 1 + assign_hit_to_legion(id) + goto_assign_spiculum_hits() + }, + barbarian(id) { + push_undo() + game.combat.staken += 1 + eliminate_barbarian(id) + goto_assign_spiculum_hits() + }, + rival_emperor(id) { + push_undo() + game.combat.staken += 1 + eliminate_rival_emperor(id) + goto_assign_spiculum_hits() + } +} + function is_attacker_eliminated() { if (game.selected_general < 0) return !has_militia(game.where) @@ -3735,9 +3885,7 @@ states.combat_victory = { let ae = is_attacker_eliminated() if (de && ae) prompt("Combat: There is no winner.") - else if (de || game.combat.dtaken > game.combat.ataken) - prompt("Combat: You win the battle!") - else if (game.combat.dtaken === game.combat.ataken && game.combat.cavalry) + else if (de || game.combat.dtaken + game.combat.staken + game.combat.cavalry > game.combat.ataken) prompt("Combat: You win the battle!") else prompt("Combat: Defenders win the battle!") @@ -3755,7 +3903,7 @@ function goto_combat_victory() { let ae = is_attacker_eliminated() if (de && ae) goto_combat_no_victory() - else if (de || game.combat.dtaken > game.combat.ataken) + else if (de || game.combat.dtaken + game.combat.staken + game.combat.cavalry > game.combat.ataken) goto_combat_victory_attacker() else goto_combat_victory_defender() @@ -3906,6 +4054,7 @@ states.free_increase_support_level = { } states.triumph = { + inactive: "Combat", prompt() { prompt("Combat: You may play Triumph.") gen_card_event(CARD_S4X) @@ -4398,7 +4547,10 @@ states.refill_hand = { prompt("End of Turn: Draw cards.") let hand = current_hand() let draw = current_draw() - if (hand.length < 5 && draw.length > 0) { + let n = 5 + if (game.frumentarii & (1 << game.current)) + n = 3 + if (hand.length < n && draw.length > 0) { for (let c of draw) gen_action_card(c) } else { -- cgit v1.2.3