summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTor Andersson <tor@ccxvii.net>2023-12-14 12:11:28 +0100
committerTor Andersson <tor@ccxvii.net>2024-01-08 16:36:48 +0100
commit000d7493517e569b9ebe5cb493c3541eef9589f4 (patch)
tree51ab43e0a3751f96cc438d7d125478884b489a23
parentfa11ea4c8dd0c423ad1e86ba0f6a7dd31a35c9af (diff)
downloadtable-battles-000d7493517e569b9ebe5cb493c3541eef9589f4.tar.gz
Links & Marsaglia.
-rw-r--r--rules.js83
1 files changed, 76 insertions, 7 deletions
diff --git a/rules.js b/rules.js
index 4cf3b99..2b5bb37 100644
--- a/rules.js
+++ b/rules.js
@@ -284,6 +284,12 @@ const S35_AULDEARN = find_scenario(35)
const S35_MONTROSE = find_card(35, "Montrose")
const S35_GORDON = find_card(35, "Gordon")
+const S39_MARSAGLIA = find_scenario(39)
+const S39_EUGENE = find_card(39, "Eugene")
+const S39_CANNONS = find_card(39, "Cannons")
+const S39_BAYONETS = find_card(39, "Bayonets!")
+const S39_CATINAT = find_card(39, "Catinat")
+
// === SETUP ===
exports.setup = function (seed, scenario, options) {
@@ -466,15 +472,20 @@ function remove_dice(c) {
}
}
-function move_dice(from, to) {
+function take_all_dice(from, to) {
+ log("Take dice from " + from + " to " + to + ".")
for (let i = 0; i < 12; ++i) {
if (get_dice_location(i) === from) {
set_dice_location(i, to)
+ if (to === POOL)
+ set_dice_value(i, 0)
+ to = POOL
}
}
}
function take_one_die(from, to) {
+ log("One die from " + from + " to " + to + ".")
for (let i = 0; i < 12; ++i) {
if (get_dice_location(i) === from) {
set_dice_location(i, to)
@@ -486,6 +497,7 @@ function take_one_die(from, to) {
}
function take_wild_die(from, to) {
+ log("Wild die from " + from + " to " + to + ".")
for (let i = 0; i < 12; ++i) {
if (get_dice_location(i) === from) {
set_dice_location(i, to)
@@ -594,6 +606,16 @@ function is_removed_from_play(c) {
return !is_card_in_play_or_reserve(c)
}
+function card_has_active_link(c) {
+ let link = data.cards[c].link
+ if (link) {
+ for (let t of link)
+ if (is_card_in_play(t))
+ return true
+ }
+ return false
+}
+
function card_has_attack_with_valid_target(c) {
for (let a of data.cards[c].actions) {
if (a.type === "Attack") {
@@ -1852,7 +1874,7 @@ function goto_attack(target) {
if (take_from) {
for (let from of take_from)
if (has_any_dice_on_card(from))
- move_dice(from, game.selected)
+ take_all_dice(from, game.selected)
}
let take_1_from = card_has_rule(game.selected, "take_1_from")
@@ -1934,6 +1956,17 @@ function update_attack1() {
}
}
+ if (game.scenario === S39_MARSAGLIA) {
+ if (game.selected === S39_EUGENE) {
+ if (has_any_dice_on_card(S39_CANNONS)) {
+ game.self = 0
+ }
+ }
+ if (game.selected === S39_CATINAT && game.target2 === S39_BAYONETS) {
+ game.self = 0
+ }
+ }
+
let extra = card_has_rule(game.selected, "extra_hit_if_dice_on")
if (extra && has_any_dice_on_card(extra[0]))
game.hits += 1
@@ -1942,6 +1975,9 @@ function update_attack1() {
game.hits = clamp(game.hits - 1, 0, 1)
if (card_has_rule(game.target, "suffer_1_less"))
game.hits = Math.max(0, game.hits - 1)
+
+ if (card_has_active_link(game.target))
+ game.hits = Math.max(0, game.hits - 1)
}
// Update hits and self hits for defensive abilities that redirect or steal hits.
@@ -1985,6 +2021,12 @@ states.attack = {
gen_action_dice_on_card(from)
}
+ if (game.scenario === S39_MARSAGLIA) {
+ if (game.selected === S39_CATINAT) {
+ gen_action_dice_on_card(S39_BAYONETS)
+ }
+ }
+
view.actions.attack = 1
},
attack() {
@@ -2001,24 +2043,42 @@ states.attack = {
this.attack()
},
die(d) {
+ let from = get_dice_location(d)
+
let w = side_get_wild_die_card(player_index())
- if (w === get_dice_location(d)) {
- log("Wild die from C" + w + ".")
+ if (w === from) {
take_wild_die(w, game.selected)
+ return
}
+
let may_take_from = card_has_rule(game.selected, "may_take_from")
if (may_take_from) {
- move_dice(get_dice_location(d), game.selected)
+ take_all_dice(from, game.selected)
update_attack1()
update_attack2()
+ return
}
+
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)
+ take_all_dice(from, game.selected)
game.self2 = 1
update_attack1()
update_attack2()
+ return
}
+
+ if (game.scenario === S39_MARSAGLIA) {
+ if (game.selected === S39_CATINAT && from === S39_BAYONETS) {
+ take_all_dice(from, game.selected)
+ game.target2 = S39_BAYONETS
+ update_attack1()
+ update_attack2()
+ return
+ }
+ }
+
+ throw new Error("no handler for taking dice from other card")
}
}
@@ -2099,6 +2159,12 @@ states.command = {
// === REACTION ===
function can_opponent_react() {
+
+ if (game.scenario === S39_MARSAGLIA) {
+ if (game.selected === S39_CATINAT && game.target2 === S39_BAYONETS)
+ return false
+ }
+
let p = 1 - player_index()
let wild = side_has_wild_die(p)
for (let c of game.front[p])
@@ -2183,7 +2249,6 @@ function take_wild_die_if_needed_for_reaction(c, ix) {
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)
}
}
@@ -2459,6 +2524,8 @@ function get_attack_hits(c, a) {
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. 1 self per action. You CHOOSE the target.":
return count_dice_on_card(c)
case "1 hit per pair.":
@@ -2501,6 +2568,8 @@ function get_attack_self(c, a) {
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. 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.":