diff options
author | Tor Andersson <tor@ccxvii.net> | 2023-12-13 15:08:05 +0100 |
---|---|---|
committer | Tor Andersson <tor@ccxvii.net> | 2024-01-08 16:36:48 +0100 |
commit | c5e067628fa60914c388164e72716e487862e68e (patch) | |
tree | 60e970a75ce27785eee35c0401605f3ba48215e0 /rules.js | |
parent | 82c6128f10dd19174b572e373e92810878b9f5d4 (diff) | |
download | table-battles-c5e067628fa60914c388164e72716e487862e68e.tar.gz |
Breastworks!
Diffstat (limited to 'rules.js')
-rw-r--r-- | rules.js | 147 |
1 files changed, 123 insertions, 24 deletions
@@ -19,16 +19,11 @@ Special card rules implemented: rout_with remove_with wild - -TODO: attack_reserve attack_choose_target take_from - -TODO: extra input steps may_take_from - */ // TODO: morale cube limit (cannot place on special if maxed) @@ -133,6 +128,11 @@ exports.view = function (state, player) { self: game.self, } + if (game.target2 >= 0 && game.hits2 >= 0) { + view.target2 = game.target2 + view.hits2 = game.hits2 + } + if (game.state === "game_over") { view.prompt = game.victory } else if (player !== game.active) { @@ -244,6 +244,10 @@ const S25_KELLY = find_card(25, "Kelly") const S26_PEACH_ORCHARD = find_scenario(26) const S26_FATAL_BLUNDER = find_card(26, "Fatal Blunder") +const S28_CULPS_HILL = find_scenario(28) +const S28_BREASTWORKS = find_card(28, "Breastworks") +const S28_GEARY = find_card(28, "Geary") + // === SETUP === exports.setup = function (seed, scenario, options) { @@ -284,6 +288,7 @@ exports.setup = function (seed, scenario, options) { reserve: [ [], [] ], // dice value placed on what card + rolled: 0, placed: [], // current action @@ -292,6 +297,11 @@ exports.setup = function (seed, scenario, options) { target: -1, hits: 0, self: 0, + + // for breastworks etc + self2: 0, + target2: -1, + hits2: 0, } function setup_formation(front, reserve, c) { @@ -570,6 +580,15 @@ function placed_any_dice_on_wing(w) { return false } +function is_straight_4_or_3(c) { + if (game.scenario === S28_CULPS_HILL) { + if (game.rolled >= 5) + return 4 + return 3 + } + throw new Error("Missing rule for Straight 3/4 choice") +} + const place_dice_once = { "(1)": true, "(2)": true, @@ -595,6 +614,7 @@ const place_dice_once = { const place_dice_check = { "Full House": check_full_house, + "Straight 4/3": check_straight_4_or_3, "Straight 3": check_straight_3, "Straight 4": check_straight_4, "Doubles": check_doubles, @@ -647,6 +667,7 @@ const place_dice_check = { const place_dice_gen = { "Full House": gen_full_house, + "Straight 4/3": gen_straight_4_or_3, "Straight 3": gen_straight_3, "Straight 4": gen_straight_4, "Doubles": gen_doubles, @@ -700,6 +721,7 @@ const place_dice_gen = { const place_dice_take = { "Full House": take_full_house, + "Straight 4/3": take_straight_4_or_3, "Straight 3": take_straight_3, "Straight 4": take_straight_4, "Doubles": take_doubles, @@ -870,6 +892,13 @@ function check_all_4(c, x, y, z, w) { return pool_has_single(x) && pool_has_single(y) && pool_has_single(z) && pool_has_single(w) } +function check_straight_4_or_3(c) { + if (is_straight_4_or_3(c) === 4) + check_straight_4(c) + else + check_straight_3(c) +} + function check_straight_3(c) { return ( check_all_3(c, 1, 2, 3) || @@ -939,6 +968,13 @@ function gen_range(c, lo, hi) { gen_single(c, v) } +function gen_straight_4_or_3(c) { + if (is_straight_4_or_3(c) === 4) + gen_straight_4(c) + else + gen_straight_3(c) +} + function gen_straight_3(c) { if (check_all_3(c, 1, 2, 3)) gen_pool_die(1) @@ -1026,6 +1062,13 @@ function take_full_house(c, d) { } } +function take_straight_4_or_3(c, d) { + if (is_straight_4_or_3(c) === 4) + take_straight_4(c, d) + else + take_straight_3(c, d) +} + function take_straight_3(c, d) { let v = get_dice_value(d) take_single(c, d) @@ -1044,6 +1087,7 @@ function take_straight_4(c, d) { function goto_roll_phase() { game.selected = -1 game.target = -1 + game.target2 = -1 game.action = 0 game.state = "roll" @@ -1085,12 +1129,16 @@ states.roll = { } function roll_dice_in_pool() { + game.rolled = 0 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) + for (let i = 0; i < 6; ++i) { + if (get_player_dice_location(p, i) < 0) { set_player_dice_value(p, i, random(6) + 1) + game.rolled++ + } + } game.state = "place" } @@ -1468,9 +1516,13 @@ function goto_action_phase() { } function end_action_phase() { - game.hits = game.self = 0 + game.hits = 0 + game.self = 0 + game.hits2 = 0 + game.self2 = 0 game.selected = -1 game.target = -1 + game.target2 = -1 goto_routing() } @@ -1673,8 +1725,17 @@ function goto_attack(target) { game.state = "attack" game.target = target + + update_attack1() + update_attack2() +} + +// Update hits and self hits. +function update_attack1() { + let a = current_action() + game.hits = get_attack_hits(game.selected, a) - game.self = get_attack_self(game.selected, a) + game.self = get_attack_self(game.selected, a) + game.self2 if (game.scenario === S2_MARSTON_MOOR) { if (is_card_in_play(S2_RUPERTS_LIFEGUARD)) { @@ -1722,6 +1783,26 @@ function goto_attack(target) { game.hits = Math.max(0, game.hits - 1) } +// Update hits and self hits for defensive abilities that redirect or steal hits. +function update_attack2() { + if (game.scenario === S28_CULPS_HILL) { + if (is_card_in_play(S28_BREASTWORKS)) { + if (data.cards[game.target].wing === DKBLUE) { + if (game.hits > 0) { + game.target2 = S28_BREASTWORKS + if (game.hits > 1) { + game.hits2 = game.hits - 1 + game.hits = 1 + } else { + game.hits2 = 1 + game.hits = 0 + } + } + } + } + } +} + states.attack = { prompt() { view.prompt = "Attack " + card_name(game.target) + "." @@ -1737,6 +1818,12 @@ states.attack = { gen_action_dice_on_card(from) } + let may_take_from_extra = card_has_rule(game.selected, "may_take_from_extra_self") + if (may_take_from_extra) { + for (let from of may_take_from_extra) + gen_action_dice_on_card(from) + } + view.actions.attack = 1 }, attack() { @@ -1761,15 +1848,27 @@ states.attack = { let may_take_from = card_has_rule(game.selected, "may_take_from") if (may_take_from) { move_dice(get_dice_location(d), game.selected) - goto_attack(game.target) // recompute hits + update_attack1() + update_attack2() + } + let may_take_from_extra = card_has_rule(game.selected, "may_take_from_extra_self") + if (may_take_from_extra) { + move_dice(get_dice_location(d), game.selected) + game.self2 = 1 + update_attack1() + update_attack2() } } } function resume_attack() { - apply_hits(game.hits) - apply_self(game.self) pay_for_action(game.selected) + + remove_sticks(game.selected, game.self) + remove_sticks(game.target, game.hits) + if (game.target2 >= 0) + remove_sticks(game.target2, game.hits2) + end_action_phase() } @@ -1788,7 +1887,6 @@ function goto_command() { game.state = "command" } - states.command = { prompt() { let list = find_all_targets_of_command(current_action()) @@ -2001,6 +2099,8 @@ function goto_screen(c, a) { game.target = c + update_attack1() + switch (a.effect) { default: @@ -2018,6 +2118,8 @@ function goto_screen(c, a) { break } + update_attack2() + game.state = "screen" } @@ -2044,6 +2146,8 @@ function goto_absorb(c, a) { game.target = c + update_attack1() + switch (a.effect) { default: @@ -2060,6 +2164,8 @@ function goto_absorb(c, a) { break } + update_attack2() + game.state = "absorb" } @@ -2080,6 +2186,8 @@ states.absorb = { function goto_counterattack(c, a) { game.reacted = player_index() + update_attack1() + switch (a.effect) { default: @@ -2112,6 +2220,8 @@ function goto_counterattack(c, a) { break } + update_attack2() + game.state = "counterattack" } @@ -2129,14 +2239,6 @@ states.counterattack = { // === ATTACK EFFECTS === -function apply_self(n) { - remove_sticks(game.selected, n) -} - -function apply_hits(n) { - remove_sticks(game.target, n) -} - function get_attack_hits(c, a) { switch (a.effect) { default: @@ -2329,8 +2431,6 @@ states.routing = { } function end_routing() { - console.log("END ROUTING", game.routed) - // Normal morale loss and gain if (game.morale[0] > 0 && game.morale[1] > 0) { if ((game.routed[0] > 0 && !game.routed[1]) || (game.routed[1] > 0 && !game.routed[0])) { @@ -2414,7 +2514,6 @@ function end_reserve() { goto_roll_phase() } - states.reserve = { prompt() { view.prompt = "Enter reserves!" |