From 7b3fb28c180a5e44fc4632fba890688c101765ab Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Wed, 13 Dec 2023 13:21:39 +0100 Subject: Wheatfield. --- rules.js | 122 +++++++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 99 insertions(+), 23 deletions(-) (limited to 'rules.js') diff --git a/rules.js b/rules.js index 4ba0f2a..ca2615b 100644 --- a/rules.js +++ b/rules.js @@ -262,7 +262,7 @@ exports.setup = function (seed, scenario, options) { active: P1, state: "roll", - reacted: 0, + reacted: -1, // dice value and position dice: [ @@ -329,7 +329,7 @@ exports.setup = function (seed, scenario, options) { map_set(game.cubes, S37_THE_FOG, 3) } - goto_action_phase() + goto_start_turn() return game } @@ -516,7 +516,7 @@ function is_card_attack_with_target_in_play(c) { return false } -function check_impossible_to_attack_victory() { +function is_impossible_to_attack() { let p = player_index() for (let c of game.front[p]) if (is_card_attack_with_target_in_play(c)) @@ -524,6 +524,24 @@ function check_impossible_to_attack_victory() { return true } +function check_impossible_to_attack_victory() { + if (is_impossible_to_attack()) { + if (player_index() === 0) + return goto_game_over(P2, P1 + " has no more attacks!") + else + return goto_game_over(P1, P2 + " has no more attacks!") + } + return false +} + +function check_morale_victory() { + if (game.morale[0] === 0) + return goto_game_over(P2, P1 + " has run out of morale!") + if (game.morale[1] === 0) + return goto_game_over(P1, P2 + " has run out of morale!") + return false +} + // === ROLL PHASE === function is_pool_die(i, v) { @@ -1041,7 +1059,10 @@ function goto_roll_phase() { states.roll = { prompt() { - view.prompt = "Roll the dice in your pool." + if (game.reacted === player_index()) + view.prompt = "Skipped action phase; roll the dice in your pool." + else + view.prompt = "Roll the dice in your pool." view.actions.roll = 1 }, roll() { @@ -1051,6 +1072,8 @@ states.roll = { } function roll_dice_in_pool() { + if (game.reacted === player_index()) + game.reacted = -1 let p = player_index() for (let i = 0; i < 6; ++i) if (get_player_dice_location(p, i) < 0) @@ -1138,8 +1161,7 @@ function end_roll_phase() { set_player_dice_value(p, i, 0) set_opponent_active() - - goto_action_phase() + goto_start_turn() } // === ACTION PHASE === @@ -1317,17 +1339,60 @@ function can_take_any_action() { return false } -function goto_action_phase() { - if (check_impossible_to_attack_victory()) { - if (player_index() === 0) - goto_game_over(P2, P1 + " has no more attacks!") - else - goto_game_over(P1, P2 + " has no more attacks!") +function count_cards_in_play_from_wing(w) { + let n = 0 + for (let c of game.front[0]) + if (data.cards[c].wing === w) + ++n + for (let c of game.front[1]) + if (data.cards[c].wing === w) + ++n + for (let c of game.reserve[0]) + if (data.cards[c].wing === w) + ++n + for (let c of game.reserve[1]) + if (data.cards[c].wing === w) + ++n + return n +} + +function goto_start_turn() { + if (check_impossible_to_attack_victory()) return + + if (game.scenario === S25_WHEATFIELD) { + // Rout Stony Hill at start of Union turn if it is the only Blue card left. + if (player_index() === 1) { + if (is_card_in_play(S25_STONY_HILL)) { + if (count_cards_in_play_from_wing(BLUE) === 1) { + game.state = "stony_hill" + return + } + } + } } - if (game.reacted) { - game.reacted = 0 + goto_action_phase() +} + +states.stony_hill = { + prompt() { + view.prompt = "Rout Stony Hill!" + gen_action_card(S25_STONY_HILL) + }, + card(c) { + log("Stony Hill routed!") + eliminate_card(S25_STONY_HILL) + game.morale[0] -- + game.morale[1] ++ + if (check_morale_victory()) + return + goto_action_phase() + }, +} + +function goto_action_phase() { + if (game.reacted === player_index()) { end_action_phase() } else { if (can_take_any_action()) @@ -1866,7 +1931,7 @@ function end_reaction() { // === SCREEN === function goto_screen(c, a) { - game.reacted = 1 + game.reacted = player_index() game.target = c @@ -1909,7 +1974,7 @@ states.screen = { // === ABSORB === function goto_absorb(c, a) { - game.reacted = 1 + game.reacted = player_index() game.target = c @@ -1947,7 +2012,7 @@ states.absorb = { // === COUNTERATTACK === function goto_counterattack(c, a) { - game.reacted = 1 + game.reacted = player_index() switch (a.effect) { @@ -2029,6 +2094,7 @@ function get_attack_hits(c, a) { case "1 hit per pair. 1 self per action.": return count_dice_on_card(c) >> 1 case "1 hit, PLUS 1 hit per die. 1 self per action.": + case "1 hit, PLUS 1 hit per die. 1 self per action. Fightin' Irish!": return 1 + count_dice_on_card(c) case "5 hits.": return 5 @@ -2057,6 +2123,7 @@ function get_attack_self(c, a) { case "1 hit per die. 1 self per action. (But see Semmes.)": case "1 hit per pair. 1 self per action.": case "1 hit, PLUS 1 hit per die. 1 self per action.": + case "1 hit, PLUS 1 hit per die. 1 self per action. Fightin' Irish!": return 1 } } @@ -2113,6 +2180,14 @@ function should_retire_card(c) { return false return true } + + if (game.scenario === S25_WHEATFIELD) { + if (c === S25_ZOOK || c === S25_KELLY) { + if (is_card_in_front(S25_WOFFORD)) + return true + } + } + return false } @@ -2169,10 +2244,8 @@ function end_routing() { game.morale[0] += game.routed[1] } } - if (game.morale[0] === 0) - return goto_game_over(P2, P1 + " has run out of morale!") - if (game.morale[1] === 0) - return goto_game_over(P1, P2 + " has run out of morale!") + if (check_morale_victory()) + return } else { // SPECIAL: S3 - Plains of Abraham // SPECIAL: S34 - Tippermuir - Royalists @@ -2288,7 +2361,7 @@ function end_removing() { states.removing = { prompt() { - view.prompt = "Remove cards!" + view.prompt = "Remove or retire cards!" for (let p = 0; p <= 1; ++p) { for (let c of game.front[p]) if (should_remove_card(c) || should_retire_card(c)) @@ -2299,7 +2372,10 @@ states.removing = { } }, card(c) { - log(card_name(c) + " removed.") + if (should_retire_card(c)) + log(card_name(c) + " retired.") + else + log(card_name(c) + " removed.") eliminate_card(c) resume_removing() }, -- cgit v1.2.3