diff options
author | Tor Andersson <tor@ccxvii.net> | 2023-12-09 19:58:23 +0100 |
---|---|---|
committer | Tor Andersson <tor@ccxvii.net> | 2024-01-08 16:36:47 +0100 |
commit | 3d4909f946f724562d354a55a721ded43a12c268 (patch) | |
tree | 7e6659790356e7c021c51f85b9537f85a8d54d23 | |
parent | c5f35f99aa9605a7baf43d6c407b126a51e878f8 (diff) | |
download | table-battles-3d4909f946f724562d354a55a721ded43a12c268.tar.gz |
Towton and Edgecote Moor. take_from, rout_with, and remove_with.
-rw-r--r-- | cards.css | 10 | ||||
-rw-r--r-- | rules.js | 77 | ||||
-rw-r--r-- | tools/gendata.js | 41 |
3 files changed, 99 insertions, 29 deletions
@@ -12,6 +12,10 @@ box-shadow: 1px 2px 8px #0008; } +.card.scenario { + background-color: #eecba1; +} + .card { user-select: none; } @@ -50,7 +54,7 @@ position: absolute; width: 32px; height: 16px; - top: 58px; + top: 55px; background-size: contain; } @@ -207,7 +211,6 @@ bottom: 0px; right: 0px; padding: 8px 8px; - opacity: 50%; } .extra { @@ -218,7 +221,6 @@ bottom: 0px; left: 0px; padding: 8px 8px; - opacity: 50%; } /* SCENARIO CARD */ @@ -259,6 +261,6 @@ .list{margin:4px;gap:4px!important;} .card{box-shadow:none!important;border-radius:0!important;} .card,.scenario_title,.scenario_player,.strength,.action_row *{background-color:white!important} - .number,.extra{opacity:1!important} + .number,.extra{display:block;} .extra{display:block} } @@ -13,12 +13,12 @@ Special card rules implemented: suffer_1_less_1_max suffer_1_less start_with_no_cubes + take_from TODO: - remove_with rout_with + remove_with wild - take_from attack_reserve no_morale @@ -210,6 +210,9 @@ const S3201_DH_HILL = find_card(3201, "D.H. Hill") const S3201_AP_HILL = find_card(3201, "A.P. Hill") const S3201_LONGSTREET = find_card(3201, "Longstreet") +const S12_TOWTON = find_scenario(12) +const S13_EDGECOTE_MOOR = find_scenario(13) + // === SETUP === exports.setup = function (seed, scenario, options) { @@ -296,6 +299,13 @@ exports.setup = function (seed, scenario, options) { // === GAME STATE ACCESSORS === +function count_total_cubes() { + let n = game.morale[0] + game.morale[1] + for (let i = 1; i < game.cubes.length; i += 2) + n += game.cubes[i] + return n +} + function card_has_rule(c, name) { let rules = data.cards[c].rules if (rules) @@ -359,6 +369,14 @@ function remove_dice(c) { } } +function move_dice(from, to) { + for (let i = 0; i < 12; ++i) { + if (get_dice_location(i) === from) { + set_dice_location(i, to) + } + } +} + function eliminate_card(c) { remove_dice(c) remove_cubes(c, 3) @@ -647,10 +665,22 @@ function can_place_dice(c) { } // At cube limit? - if (data.cards[c].special) + if (data.cards[c].special) { + // Max on card if (map_get(game.cubes, c, 0) >= data.cards[c].special) return false + // Max available + let n_cubes = count_total_cubes() + if (n_cubes >= 10) + return false + + if (game.scenario === S12_TOWTON) { + if (n_cubes >= 8) + return false + } + } + // At per wing limit? let wing = data.cards[c].wing let n_wing = 0 @@ -1355,7 +1385,11 @@ states.bombard = { function goto_attack() { let a = current_action() - // TODO: 88B German Infantry - take dice from Saxon Infantry + let take_from = card_has_rule(game.selected, "take_from") + if (take_from) { + for (let from of take_from) + move_dice(from, game.selected) + } game.state = "attack" game.target = find_target_of_attack(a) @@ -1457,6 +1491,18 @@ states.command = { } } + if (game.scenario === S13_EDGECOTE_MOOR) { + // TODO: pay all 3 cubes? remove cards from play? + if (game.reserve[0].length === 0 && game.reserve[1].length > 0) { + log("Gained a second morale cube.") + game.morale[0] += 1 + } + if (game.reserve[1].length === 0 && game.reserve[0].length > 0) { + log("Gained a second morale cube.") + game.morale[1] += 1 + } + } + if (find_first_target_of_command(game.selected, current_action()) < 0) { pay_for_action(game.selected) end_action_phase() @@ -1742,6 +1788,7 @@ function get_attack_hits(c, a) { case "1 hit per die.": case "1 hit per die. 1 self per action.": case "1 hit per die. Ignore first target until it comes out of Reserve.": + case "1 hit per die (but see below). 1 self per action.": return count_dice_on_card(c) case "1 hit per pair.": case "1 hit per pair. 1 self per action.": @@ -1762,6 +1809,7 @@ function get_attack_self(c, a) { return 0 case "1 hit. 1 self per action.": case "1 hit per die. 1 self per action.": + case "1 hit per die (but see below). 1 self per action.": case "1 hit per pair. 1 self per action.": case "1 hit, PLUS 1 hit per die. 1 self per action.": return 1 @@ -1771,15 +1819,30 @@ function get_attack_self(c, a) { // === ROUTING === function should_remove_card(c) { - // TODO: remove after X routs special rules + let remove_with = card_has_rule(c, "remove_with") + if (remove_with) { + for (let other of remove_with) + if (is_card_in_play(other)) + return false + return true + } + return false } function should_rout_card(c) { - // TODO: rout after X routs special rules - if (!data.cards[c].special) + if (!data.cards[c].special) { if (map_get(game.sticks, c, 0) === 0) return true + + let rout_with = card_has_rule(c, "rout_with") + if (rout_with) { + for (let other of rout_with) + if (is_card_in_play(other)) + return false + return true + } + } return false } diff --git a/tools/gendata.js b/tools/gendata.js index 9580130..1bc9099 100644 --- a/tools/gendata.js +++ b/tools/gendata.js @@ -75,21 +75,13 @@ for (let c of card_records) { result.push(`<div class="formation card">`) result.push(`<div class="name ${c.wing}">${c.name}</div>`) - if (c.symbol === "Inf1") { + if (c.symbol === "inf") { card.infantry = 1 - result.push(`<div class="symbol infantry1"></div>`) + result.push(`<div class="symbol infantry"></div>`) } - if (c.symbol === "Inf2") { - card.infantry = 2 - result.push(`<div class="symbol infantry2"></div>`) - } - if (c.symbol === "Cav1") { + if (c.symbol === "cav") { card.cavalry = 1 - result.push(`<div class="symbol cavalry1"></div>`) - } - if (c.symbol === "Cav2") { - card.cavalry = 2 - result.push(`<div class="symbol cavalry2"></div>`) + result.push(`<div class="symbol cavalry"></div>`) } if (card.strength) @@ -177,9 +169,7 @@ for (let c of card_records) { card.retire = 1 else if (c.reserve === "PURSUIT") card.pursuit = 1 - else if (c.reserve === "Commanded") - card.reserve = [] - else if (c.reserve === "See Above") + else if (c.reserve === "Commanded" || c.reserve === "See Above" || c.reserve === "Special Rule") card.reserve = [] else card.reserve = c.reserve.split(" or ") @@ -242,7 +232,7 @@ function find_wing_cards(scenario, wing) { } /* process action and reserve targets */ -for (let c of cards) { +function process_card(c) { for (let a of c.actions) { if (a.target) { let tname = a.target.replace(" out of reserve", "") @@ -260,12 +250,16 @@ for (let c of cards) { a.target_list = find_wing_cards(c.scenario, WING.pink) else if (tname === "Any Blue formation") a.target_list = find_wing_cards(c.scenario, WING.blue) + else if (tname === "Any Dark Blue formation") + a.target_list = find_wing_cards(c.scenario, WING.dkblue) else if (tname.startsWith("Any attack on ")) a.target_list = tname.replace("Any attack on ", "").split(" or ").map(name => find_card(c.scenario, name)) else if (tname === "Any friendly but Little Round Top") a.target_list = find_friendly_cards(c.scenario, c.wing).filter(x => x !== find_card(c.scenario, "Little Round Top")) + else if (tname === 'Activate "Retreat to Nivelles"') + a.target_list = [ "Retreat to Nivelles" ] else if (tname === "The Fog Lifts...") - a.target_list = [] + a.target_list = [ "The Fog" ] else a.target_list = tname.split(/, | OR | or | and /).map(name => find_card(c.scenario, name)) } @@ -286,7 +280,18 @@ for (let c of cards) { c.reserve = c.reserve.map(name => find_card(c.scenario, name)) } -fs.writeFileSync("info/all-cards.html", result.join("\n")) +let failed = false +for (let c of cards) { + try { + process_card(c) + } catch (err) { + console.log(err) + failed = true + } +} + +if (!failed) + fs.writeFileSync("info/all-cards.html", result.join("\n")) result = [ `<!doctype html> |