diff options
author | Tor Andersson <tor@ccxvii.net> | 2023-12-12 17:25:54 +0100 |
---|---|---|
committer | Tor Andersson <tor@ccxvii.net> | 2024-01-08 16:36:48 +0100 |
commit | 6195f24fc7abb0453356464a1f923a699e89da00 (patch) | |
tree | 9c18acf4b60cb077bb1a6131643926b218237f96 | |
parent | cc45c704c8338d92dca9e37c3fe448f58c0e4ad5 (diff) | |
download | table-battles-6195f24fc7abb0453356464a1f923a699e89da00.tar.gz |
Wild die.
-rw-r--r-- | rules.js | 115 |
1 files changed, 90 insertions, 25 deletions
@@ -14,11 +14,11 @@ Special card rules implemented: suffer_1_less start_with_no_cubes take_from + wild TODO: rout_with remove_with - wild attack_reserve no_morale @@ -217,6 +217,7 @@ const S9_SOPWELL_LANE = find_card(9, "Sopwell Lane") const S9_ARCHERS = find_card(9, "Archers") const S9_WARWICK = find_card(9, "Warwick") +const S11_MORTIMERS_CROSS = find_scenario(11) const S12_TOWTON = find_scenario(12) const S13_EDGECOTE_MOOR = find_scenario(13) @@ -225,6 +226,8 @@ const S15_A_PLUMP_OF_SPEARS = find_card(15, "A Plump of Spears") const S15_SOMERSET = find_card(15, "Somerset") const S15_WENLOCK = find_card(15, "Wenlock") +const S16_STOKE_FIELD = find_scenario(16) + // === SETUP === exports.setup = function (seed, scenario, options) { @@ -389,6 +392,16 @@ function move_dice(from, to) { } } +function take_wild_die(from, to) { + for (let i = 0; i < 12; ++i) { + if (get_dice_location(i) === from) { + set_dice_location(i, to) + set_dice_value(i, 0) + to = POOL + } + } +} + function eliminate_card(c) { remove_dice(c) remove_cubes(c, 3) @@ -1076,6 +1089,19 @@ function end_roll_phase() { // === ACTION PHASE === +function side_get_wild_die_card(p) { + if (game.scenario === S11_MORTIMERS_CROSS || game.scenario === S12_TOWTON || game.scenario === S16_STOKE_FIELD) { + for (let c of game.front[p]) + if (card_has_rule(c, "wild") && has_any_dice_on_card(c)) + return c + } + return -1 +} + +function side_has_wild_die(p) { + return side_get_wild_die_card(p) >= 0 +} + function has_any_dice_on_card(c) { for (let i = 0; i < 12; ++i) if (get_dice_location(i) === c) @@ -1150,12 +1176,15 @@ function check_cube_requirement(c, req) { } } -function check_dice_requirement(c, req) { +function check_dice_requirement(c, req, wild) { switch (req) { case "Full House": return require_full_house(c) case "Pair": case "Pair, Voluntary": + // NOTE: Only requirement needed for Wild die scenarios. + if (wild) + return has_any_dice_on_card(c) return require_pair(c) case "Triplet": return require_triplet(c) @@ -1217,7 +1246,7 @@ function can_take_action(c, a) { if (data.cards[c].special) return check_cube_requirement(c, a.requirement) else - return check_dice_requirement(c, a.requirement) + return check_dice_requirement(c, a.requirement, false) } return false } @@ -1454,6 +1483,11 @@ states.attack = { prompt() { view.prompt = "Attack " + card_name(game.target) + "." gen_action_card(game.target) + + let w = side_get_wild_die_card(player_index()) + if (w >= 0) + gen_action_dice_on_card(w) + view.actions.attack = 1 }, attack() { @@ -1469,6 +1503,13 @@ states.attack = { card(_) { this.attack() }, + die(d) { + let w = side_get_wild_die_card(player_index()) + if (w === get_dice_location(d)) { + log("Wild die from C" + w + ".") + take_wild_die(w, game.selected) + } + } } function resume_attack() { @@ -1546,29 +1587,30 @@ states.command = { function can_opponent_react() { let p = 1 - player_index() + let wild = side_has_wild_die(p) for (let c of game.front[p]) - if (can_card_react(c)) + if (can_card_react(c, wild)) return true return false } -function can_card_react(c) { +function can_card_react(c, wild) { let has_dice = has_any_dice_on_card(c) let has_cube = has_any_cubes_on_card(c) if (has_dice || has_cube) { if (data.cards[c].actions.length >= 1) if (is_reaction(c, data.cards[c].actions[0])) - if (can_take_reaction(c, data.cards[c].actions[0])) + if (can_take_reaction(c, data.cards[c].actions[0], wild)) return true if (data.cards[c].actions.length >= 2) if (is_reaction(c, data.cards[c].actions[1])) - if (can_take_reaction(c, data.cards[c].actions[1])) + if (can_take_reaction(c, data.cards[c].actions[1], wild)) return true } return false } -function can_take_reaction(c, a) { +function can_take_reaction(c, a, wild) { switch (a.type) { default: throw new Error("invalid reaction: " + a.type) @@ -1612,49 +1654,63 @@ function can_take_reaction(c, a) { if (data.cards[c].special) return check_cube_requirement(c, a.requirement) else - return check_dice_requirement(c, a.requirement) + return check_dice_requirement(c, a.requirement, wild) +} + +function take_wild_die_if_needed_for_reaction(c, ix) { + let w = side_get_wild_die_card(player_index()) + if (w >= 0) { + let a = data.cards[c].actions[ix] + if (!can_take_reaction(c, a, false)) { + log("Wild die from C" + w + ".") + take_wild_die(w, c) + } + } } states.react = { prompt() { view.prompt = card_name(game.selected) + " attacks " + card_name(game.target) + "!" - let voluntary = true let p = player_index() + let wild = side_has_wild_die(p) for (let c of game.front[p]) { let has_dice = has_any_dice_on_card(c) let has_cube = has_any_cubes_on_card(c) if (has_dice || has_cube) { - if (data.cards[c].actions.length >= 1) { - if (is_reaction(c, data.cards[c].actions[0])) { - if (can_take_reaction(c, data.cards[c].actions[0])) { - if (is_mandatory_reaction(c, data.cards[c].actions[0])) - voluntary = false - gen_action_action1(c) + for (let i = 0; i < data.cards[c].actions.length; ++i) { + let a = data.cards[c].actions[i] + if (is_reaction(c, a)) { + let must = false + let may = false + if (is_mandatory_reaction(c, a)) { + must = can_take_reaction(c, a, false) + if (!must && wild && can_take_reaction(c, a, true)) + may = true + } else { + may = can_take_reaction(c, a, wild) } - } - } - if (data.cards[c].actions.length >= 2) { - if (is_reaction(c, data.cards[c].actions[1])) { - if (can_take_reaction(c, data.cards[c].actions[1])) { - if (is_mandatory_reaction(c, data.cards[c].actions[1])) - voluntary = false - gen_action_action2(c) + if (must) + voluntary = false + if (must || may) { + if (i === 0) gen_action_action1(c) + if (i === 1) gen_action_action2(c) } } } } } - if (voluntary) view.actions.pass = 1 }, a1(c) { push_undo() + take_wild_die_if_needed_for_reaction(c, 0) goto_take_reaction(c, 0) }, a2(c) { push_undo() + take_wild_die_if_needed_for_reaction(c, 1) goto_take_reaction(c, 1) }, pass() { @@ -2124,6 +2180,15 @@ function gen_action(action, argument) { set_add(view.actions[action], argument) } +function gen_action_dice_on_card(c) { + for (let d = 0; d < 12; ++d) { + if (get_dice_location(d) === c) { + gen_action_die(d) + return + } + } +} + function gen_action_card(c) { gen_action("card", c) } |