diff options
Diffstat (limited to 'rules.js')
-rw-r--r-- | rules.js | 373 |
1 files changed, 180 insertions, 193 deletions
@@ -2,10 +2,6 @@ const data = require("./data.js") -function clamp(x, min, max) { - return Math.max(min, Math.min(max, x)) -} - function find_scenario(n) { let ix = data.scenarios.findIndex(s => s.number === n) if (ix < 0) @@ -20,9 +16,9 @@ function find_card(s, n) { return ix } -// for (let c of data.cards) for (let a of c.actions) console.log(a.type, a.effect) +for (let c of data.cards) for (let a of c.actions) console.log(a.type + ":", a.effect) // for (let c of data.cards) console.log(c.dice) -//for (let c of data.cards) for (let a of c.actions) { if (a.type === "Counterattack") console.log(c.number, a.type, a.sequence, a.target) } +// for (let c of data.cards) for (let a of c.actions) { if (a.type === "Counterattack") console.log(c.number, a.type, a.sequence, a.target) } function check_attack_res(c, a) { if (a.choice) @@ -53,7 +49,7 @@ function check_attack_res(c, a) { } } -for (let c of data.cards) for (let a of c.actions) { if (a.type === "Attack") check_attack_res(c, a) } +//for (let c of data.cards) for (let a of c.actions) { if (a.type === "Attack") check_attack_res(c, a) } const P1 = "First" const P2 = "Second" @@ -217,18 +213,8 @@ const S8_CLINTON = find_card(8, "Clinton") const S8_GRANT = find_card(8, "Grant") const S8_HESSIANS = find_card(8, "Hessians") -const S36_PHARSALUS = find_scenario(36) -const S36_SULLA = find_card(36, "Sulla") const S36_FOURTH_LINE = find_card(36, "The Fourth Line") -const S37_INKERMAN = find_scenario(37) -const S37_PAULOFFS_LEFT = find_card(37, "Pauloff's Left") -const S37_PAULOFFS_RIGHT = find_card(37, "Pauloff's Right") -const S37_BRITISH_TROOPS = find_card(37, "British Troops") -const S37_FRENCH_TROOPS = find_card(37, "French Troops") -const S37_SOIMONOFF = find_card(37, "Soimonoff") -const S37_THE_FOG = find_card(37, "The Fog") - const S3201_GAINES_MILL = find_scenario(3201) const S3201_JACKSON = find_card(3201, "Jackson") const S3201_DH_HILL = find_card(3201, "D.H. Hill") @@ -240,7 +226,6 @@ const S9_HENRY_VI = find_card(9, "Henry VI") const S9_SHROPSHIRE_LANE = find_card(9, "Shropshire Lane") 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) @@ -253,7 +238,6 @@ const S15_WENLOCK = find_card(15, "Wenlock") const S14_BARNET = find_scenario(14) const S14_TREASON = find_card(14, "\"Treason!\"") -const S14_OXFORD = find_card(14, "Oxford") const S16_STOKE_FIELD = find_scenario(16) @@ -289,28 +273,32 @@ const S30_STAPLETON = find_card(30, "Stapleton") const S30_RUPERT = find_card(30, "Rupert of the Rhine") const S30_WILMOT = find_card(30, "Wilmot") const S30_ESSEX = find_card(30, "Charles Essex") -const S30_GERARD = find_card(30, "Gerard") const S31_NEWBURY_1ST = find_scenario(31) const S31_BYRON = find_card(31, "Byron") const S31_SKIPPON = find_card(31, "Skippon") const S31_WENTWORTH = find_card(31, "Wentworth") -const S31_ROYALIST_GUNS = find_card(31, "Royalist Guns") const S31_GERARD = find_card(31, "Gerard") -const S31_STAPLETON = find_card(31, "Stapleton") -const S31_LONDON_TRAINED_BANDS = find_card(31, "London Trained Bands") + +const S34_TULLIBARDINE = find_card(34, "Tullibardine") const S35_AULDEARN = find_scenario(35) const S35_MONTROSE = find_card(35, "Montrose") const S35_GORDON = find_card(35, "Gordon") +const S37_INKERMAN = find_scenario(37) +const S37_PAULOFFS_LEFT = find_card(37, "Pauloff's Left") +const S37_BRITISH_TROOPS = find_card(37, "British Troops") +const S37_FRENCH_TROOPS = find_card(37, "French Troops") +const S37_SOIMONOFF = find_card(37, "Soimonoff") +const S37_THE_FOG = find_card(37, "The Fog") + const S38_FLEURUS = find_scenario(38) const S38_WALDECK = find_card(38, "Waldeck") const S38_RETREAT_TO_NIVELLES = find_card(38, "Retreat to Nivelles") const S38_LUXEMBOURGS_HORSE = find_card(38, "Luxembourg's Horse") const S38_GOURNAYS_HORSE = find_card(38, "Gournay's Horse") const S38_DUTCH_LEFT_FOOT = find_card(38, "Dutch Left Foot") -const S38_FRENCH_RIGHT = find_card(38, "French Right") const S38_DUTCH_HORSE = find_card(38, "Dutch Horse") const S39_MARSAGLIA = find_scenario(39) @@ -331,7 +319,6 @@ const S40_GUTTENSTEIN = find_card(40, "Guttenstein") const S41_BLENHEIM_SCENARIO = find_scenario(41) const S41_CLERAMBAULT = find_card(41, "Clerambault") -const S41_CUTTS_COLUMN = find_card(41, "Cutt's Column") const S41_BLENHEIM_CARD = find_card(41, "Blenheim") const S41_PRINCE_EUGENE = find_card(41, "Prince Eugene") @@ -375,7 +362,6 @@ const S48_BEVERN = find_card(48, "Bevern") const S48_GRENZERS = find_card(48, "Grenzers") const S49_LEUTHEN = find_scenario(49) -const S49_LUCCHESI = find_card(49, "Lucchesi") const S49_RETZOW = find_card(49, "Retzow") const S49_COLLOREDO = find_card(49, "Colloredo") const S49_NADASDY = find_card(49, "Nadasdy") @@ -760,6 +746,12 @@ function is_removed_from_play(c) { } function card_has_active_link(c) { + // Ignores Link. + if (game.scenario === S48_BRESLAU) { + if (game.selected === S48_GRENZERS) + return false + } + let link = data.cards[c].link if (link) { for (let t of link) @@ -2644,8 +2636,141 @@ function goto_attack(target) { function update_attack1(direct) { let a = current_action() - game.hits = get_attack_hits(game.selected, a) - game.self = get_attack_self(game.selected, a) + game.self2 + let n = count_dice_on_card(game.selected) + + switch (a.effect) { + default: + throw new Error("invalid attack effect: " + a.effect) + + case "1 hit per die.": + game.self = 0 + game.hits = n + break + + case "1 hit per die. 1 self.": + game.self = 1 + game.hits = n + break + + case "1 hit per die. 1 self. If reduced to one stick, no self hits.": + if (get_sticks(game.selected) === 1) + game.self = 0 + else + game.self = 1 + game.hits = n + break + + case "1 hit per pair.": + game.self = 0 + game.hits = n / 2 + break + + case "1 hit per pair. 1 self.": + game.self = 1 + game.hits = n / 2 + break + + case "1 hit plus 1 hit per die.": + game.self = 0 + game.hits = 1 + n + break + case "1 hit plus 1 hit per die. 1 self.": + game.self = 1 + game.hits = 1 + n + break + + case "1 hit. 1 self.": + game.self = 1 + game.hits = 1 + break + + case "2 hits per die.": + game.self = 0 + game.hits = 2 * n + break + + case "2 hits plus 1 hit per die. 1 self.": + game.self = 1 + game.hits = 2 + n + break + + case "1 hit.": + game.self = 0 + game.hits = 1 + break + + case "2 hits.": + game.self = 0 + game.hits = 2 + break + + case "5 hits.": + game.self = 0 + game.hits = 5 + break + + case "1 hit per die (2 hits per die versus Blenheim). 1 self.": + game.self = 1 + game.hits = n + if (direct && game.target === S41_BLENHEIM_CARD) + game.hits = 2 * n + break + + case "1 hit per die (2 hits per die versus Villars's Left). 1 self.": + game.self = 1 + game.hits = n + if (direct && game.target === S43_VILLARS_LEFT) + game.hits = 2 * n + break + + case "1 hit per die versus Driesen. 2 hits per die versus Retzow.": + case "1 hit per die (2 hits per die versus Retzow).": + game.self = 0 + game.hits = n + if (direct && game.target === S49_RETZOW) + game.hits = 2 * n + break + + case "1 hit per die. 1 extra hit if Fourth Line is in play.": + game.self = 0 + game.hits = n + if (is_card_in_play(S36_FOURTH_LINE)) + game.hits += 1 + break + + case "1 hit per die. 1 self. 1 extra hit if Dutch Horse routed.": + game.self = 1 + game.hits = n + if (is_removed_from_play(S38_DUTCH_HORSE)) + game.hits += 1 + break + + case "1 hit per die. 1 self. 1 extra vs Dutch Left Foot.": + game.self = 1 + game.hits = n + if (direct && game.target === S38_DUTCH_LEFT_FOOT) + game.hits += 1 + break + + case "1 hit per die. 1 self. 1 extra vs Essex.": + game.self = 1 + game.hits = n + if (game.target === S30_ESSEX) + game.hits += 1 + break + + case "1 hit per die. 1 self. 1 extra vs Tullibardine.": + game.self = 1 + game.hits = n + if (direct && game.target === S34_TULLIBARDINE) + game.hits += 1 + break + + case "Oxford immediately routs. This attack cannot be screened.": + game.self = 0 + game.hits = 8 + break + } if (game.scenario === S2_MARSTON_MOOR) { if (is_card_in_play(S2_RUPERTS_LIFEGUARD)) { @@ -2656,20 +2781,6 @@ function update_attack1(direct) { } } - if (game.scenario === S36_PHARSALUS) { - if (game.selected === S36_SULLA) { - if (is_card_in_play(S36_FOURTH_LINE)) - game.hits += 1 - } - } - - if (game.scenario === S37_INKERMAN) { - // Until the first Fog Cube is lifted. - if (get_cubes(S37_THE_FOG) === 3) { - game.hits -= 1 - } - } - if (game.scenario === S9_ST_ALBANS) { // Defensive Works (negated by Archers) if (game.target === S9_SHROPSHIRE_LANE || game.target === S9_SOPWELL_LANE) { @@ -2694,11 +2805,6 @@ function update_attack1(direct) { } } - if (game.scenario === S30_EDGEHILL) { - if (game.selected === S30_GERARD && game.target === S30_ESSEX) - game.hits += 1 - } - if (game.scenario === S31_NEWBURY_1ST) { if (game.selected === S31_WENTWORTH) { if (has_any_dice_on_card(S31_BYRON) && is_card_in_play(S31_SKIPPON)) { @@ -2712,12 +2818,10 @@ function update_attack1(direct) { } } - if (game.scenario === S38_FLEURUS) { - if (game.selected === S38_LUXEMBOURGS_HORSE && game.target === S38_DUTCH_LEFT_FOOT) { - game.hits += 1 - } - if (game.selected === S38_FRENCH_RIGHT && is_removed_from_play(S38_DUTCH_HORSE)) { - game.hits += 1 + if (game.scenario === S37_INKERMAN) { + // Until the first Fog Cube is lifted. + if (game.target === S37_SOIMONOFF && get_cubes(S37_THE_FOG) === 3) { + game.hits -= 1 } } @@ -2732,18 +2836,6 @@ function update_attack1(direct) { } } - if (game.scenario === S41_BLENHEIM_SCENARIO) { - if (direct) - if (game.selected === S41_CUTTS_COLUMN && game.target === S41_BLENHEIM_CARD) - game.hits *= 2 - } - - if (game.scenario === S43_DENAIN) { - if (direct) - if (game.selected === S43_DUTCH_HORSE && game.target === S43_VILLARS_LEFT) - game.hits *= 2 - } - if (game.scenario === S44_HOHENFRIEDBERG) { if (game.target === S44_CHARLES) { if (game.selected === S44_LEOPOLDS_L || game.selected === S44_LEOPOLDS_C || game.selected === S44_LEOPOLDS_R) @@ -2751,12 +2843,6 @@ function update_attack1(direct) { } } - if (game.scenario === S49_LEUTHEN) { - if (direct) - if (game.selected === S49_LUCCHESI && game.target === S49_RETZOW) - game.hits *= 2 - } - // Oblique Attack (CAL expansion rule) if (is_infantry(game.selected)) { if (get_sticks(game.selected) >= get_sticks(game.target) + 3) @@ -2775,7 +2861,7 @@ function update_attack1(direct) { game.hits = Math.max(0, game.hits - 1) if (card_has_rule(game.target, "suffer_1_less_1_max")) - game.hits = clamp(game.hits - 1, 0, 1) + game.hits = Math.max(0, Math.min(1, game.hits - 1)) } // Update hits and self hits for defensive abilities that redirect or steal hits. @@ -3148,6 +3234,7 @@ function can_take_reaction(c, a, wild) { if (game.scenario === S31_NEWBURY_1ST) { if (c === S31_GERARD) { + // London Trained Bands enter play when Skippon routs if (is_removed_from_play(S31_SKIPPON)) return false } @@ -3268,7 +3355,7 @@ function goto_screen(c, a) { game.hits = 0 game.self = 0 break - case "If either Chariot formation is screened, it suffers one Hit!": + case "If either Chariot formation is screened, it suffers one hit!": game.hits = 0 if (card_has_rule(game.selected, "is_chariot")) game.self = 1 @@ -3324,18 +3411,15 @@ function goto_absorb(c, a) { { default: throw new Error("invalid absorb effect: " + a.effect) - case "When target suffers Hits, this card suffers them instead.": - case "When target suffers Hits, this unit suffers them instead.": + case "Suffers hits.": break - case "When target suffers Hits, this card suffers 1 hit ONLY instead.": - case "When target suffers Hits, this unit suffers 1 hit ONLY instead.": + case "Suffers 1 hit only.": game.hits = 1 break - case "When target suffers Hits, this card suffers one less Hit instead.": + case "Suffers 1 less hit.": game.hits = Math.max(0, game.hits - 1) break - case "When target suffers Hits, this card suffers 1 less hit per die.": - case "When target suffers Hits, this unit suffers 1 less hit per die.": + case "Suffers 1 less hit per die.": game.hits = Math.max(0, game.hits - count_dice_on_card(c)) break } @@ -3347,7 +3431,7 @@ function goto_absorb(c, a) { states.s29_meade = { prompt() { - view.prompt = "Choosy any friendly Formation except Little Round Top to absorb the hits instead." + view.prompt = "Choose any friendly Formation except Little Round Top to absorb the hits instead." let p = player_index() for (let c of game.front[p]) { if (c !== S29_MEADE && c !== S29_LITTLE_ROUND_TOP && c !== game.target) @@ -3392,31 +3476,34 @@ function goto_counterattack(c, a) { { default: throw new Error("invalid counterattack effect: " + a.effect) - case "1 hit per die.": - game.self += count_dice_on_card(c) - break case "1 hit.": game.self += 1 break - case "1 hit. Additionally, this unit only suffers one hit.": + case "1 hit per die.": + game.self += count_dice_on_card(c) + break + case "1 hit. Suffers 1 hit only.": game.self += 1 game.hits = 1 break - case "1 hit. Additionally, this unit suffers one less hit per die.": + case "Suffers 1 less hit.": + game.hits = Math.max(0, game.hits - 1) + break + case "1 hit. Suffers 1 less hit.": game.self += 1 - game.hits = Math.max(0, game.hits - count_dice_on_card(c)) + game.hits = Math.max(0, game.hits - 1) break - case "1 hit. Additionally, this unit suffers one less hit.": + case "1 hit. Suffers 1 less hit per die.": game.self += 1 - game.hits -= 1 + game.hits = Math.max(0, game.hits - count_dice_on_card(c)) break - case "This unit suffers ONE less hit and never more than one.": + case "1 hit. Suffers 1 less hit and never more than 1.": game.self += 1 - game.hits = clamp(game.hits - 1, 0, 1) + game.hits = Math.max(0, Math.min(1, game.hits - 1)) break - case "This unit suffers TWO less hits and never more than one.": + case "1 hit. Suffers 2 less hits and never more than 1.": game.self += 1 - game.hits = clamp(game.hits - 2, 0, 1) + game.hits = Math.max(0, Math.min(1, game.hits - 2)) break } @@ -3450,106 +3537,6 @@ states.counterattack = { }, } -// === ATTACK EFFECTS === - -function get_attack_hits(c, a) { - switch (a.effect) { - default: - throw new Error("invalid attack effect: " + a.effect) - case "1 hit.": - case "1 hit. Warwick Retires upon completing this Attack Action.": - case "1 hit. You CHOOSE the target.": - case "1 hit. 1 self per action.": - case "1 hit per action. 1 self per action.": - return 1 - 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.": - case "1 hit per die (plus dice from E. Phalanx).": - case "1 hit per die. 1 self per action. (But see Sharpshooters.)": - case "1 hit per die. 1 self per action. (But see 4th Alabama.)": - case "1 hit per die. 1 self per action. (But see Semmes.)": - case "1 hit per die (also take dice from 141st Pennsylvania). 1 self per action.": - case "1 hit per die (also take dice from 68th Pennsylvania). 1 self per action.": - case "1 hit per die. 1 self per action. (But see William Fielding.)": - case "1 hit per die (1 extra vs Essex). 1 self per action. (See W. Fielding.)": - case "1 hit per die. 1 self per action (but see Cannons).": - case "1 hit per die. 1 self per action (but see Bayonets!).": - case "1 hit per die (2 hits per die vs. Blenheim). 1 self per action.": - case "1 hit per die (two per die vs. Villars's Left). 1 self per action.": - case "1 hit per die versus Driesen. Two hits per die versus Retzow.": - case "1 hit per die. 1 self per action. Does 1 extra hit versus Dutch Left Foot.": - case "1 hit per die. 1 self per action. Does 1 extra hit if Dutch Horse Routed.": - case "1 hit per die. 1 self per action. If reduced to one stick, no self hits.": - case "1 hit per die. 1 self per action. You CHOOSE the target.": - return count_dice_on_card(c) - case "1 hit per pair.": - case "1 hit per pair. 1 self per action.": - return count_dice_on_card(c) >> 1 - case "1 hit plus 1 hit per die.": - 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 "2 hits, PLUS 1 hit per die. 1 self per action.": - return 2 + count_dice_on_card(c) - case "Two hits per die.": - return 2 * count_dice_on_card(c) - case "2 hits.": - return 2 - case "5 hits.": - return 5 - case "Oxford immediately Routs. This cannot be Screened.": - return 5 - } -} - -function get_attack_self(c, a) { - switch (a.effect) { - default: - throw new Error("invalid attack effect: " + a.effect) - case "1 hit.": - case "1 hit. Warwick Retires upon completing this Attack Action.": - case "1 hit. You CHOOSE the target.": - case "1 hit per die.": - case "1 hit per die. Ignore first target until it comes out of Reserve.": - case "1 hit per die (plus dice from E. Phalanx).": - case "1 hit per pair.": - case "1 hit plus 1 hit per die.": - case "Two hits per die.": - case "2 hits.": - case "5 hits.": - case "Oxford immediately Routs. This cannot be Screened.": - return 0 - case "1 hit. 1 self per action.": - case "1 hit per action. 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 die. 1 self per action. (But see Sharpshooters.)": - case "1 hit per die. 1 self per action. (But see 4th Alabama.)": - case "1 hit per die. 1 self per action. (But see Semmes.)": - case "1 hit per die (also take dice from 141st Pennsylvania). 1 self per action.": - case "1 hit per die (also take dice from 68th Pennsylvania). 1 self per action.": - case "1 hit per die. 1 self per action. (But see William Fielding.)": - case "1 hit per die (1 extra vs Essex). 1 self per action. (See W. Fielding.)": - case "1 hit per die. 1 self per action (but see Cannons).": - case "1 hit per die. 1 self per action (but see Bayonets!).": - case "1 hit per die (2 hits per die vs. Blenheim). 1 self per action.": - case "1 hit per die (two per die vs. Villars's Left). 1 self per action.": - case "1 hit per die versus Driesen. Two hits per die versus Retzow.": - case "1 hit per die. 1 self per action. Does 1 extra hit versus Dutch Left Foot.": - case "1 hit per die. 1 self per action. Does 1 extra hit if Dutch Horse Routed.": - case "1 hit per die. 1 self per action. You CHOOSE the target.": - 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!": - case "2 hits, PLUS 1 hit per die. 1 self per action.": - return 1 - case "1 hit per die. 1 self per action. If reduced to one stick, no self hits.": - return (get_sticks(c) > 1) ? 1 : 0 - } -} - // === ROUTING/PURSUIT/REMOVE/FORCE-RETIRE === function find_card_owner(c) { |