summaryrefslogtreecommitdiff
path: root/rules.js
diff options
context:
space:
mode:
Diffstat (limited to 'rules.js')
-rw-r--r--rules.js236
1 files changed, 117 insertions, 119 deletions
diff --git a/rules.js b/rules.js
index ca2615b..aa246c5 100644
--- a/rules.js
+++ b/rules.js
@@ -241,6 +241,9 @@ const S25_WOFFORD = find_card(25, "Wofford")
const S25_ZOOK = find_card(25, "Zook")
const S25_KELLY = find_card(25, "Kelly")
+const S26_PEACH_ORCHARD = find_scenario(26)
+const S26_FATAL_BLUNDER = find_card(26, "Fatal Blunder")
+
// === SETUP ===
exports.setup = function (seed, scenario, options) {
@@ -558,6 +561,15 @@ function is_pool_die_range(i, lo, hi) {
return false
}
+function placed_any_dice_on_wing(w) {
+ for (let i = 0; i < game.placed.length; i += 2) {
+ let c = game.placed[i]
+ if (data.cards[c].wing === w)
+ return true
+ }
+ return false
+}
+
const place_dice_once = {
"(1)": true,
"(2)": true,
@@ -1064,6 +1076,7 @@ states.roll = {
else
view.prompt = "Roll the dice in your pool."
view.actions.roll = 1
+ view.actions.end_turn = 0
},
roll() {
clear_undo()
@@ -1141,9 +1154,7 @@ states.place_on_card = {
}
function end_roll_phase() {
- clear_undo()
- map_clear(game.placed)
- game.place_max = null
+ push_undo()
// Remove placed dice to add cube on special cards.
for (let c of game.front[player_index()]) {
@@ -1160,6 +1171,60 @@ function end_roll_phase() {
if (get_player_dice_location(p, i) < 0)
set_player_dice_value(p, i, 0)
+ if (game.scenario === S26_PEACH_ORCHARD) {
+ if (is_card_in_play(S26_FATAL_BLUNDER)) {
+ if (!placed_any_dice_on_wing(PINK)) {
+ game.state = "fatal_blunder"
+ return
+ }
+ }
+ }
+
+ end_turn()
+}
+
+states.fatal_blunder = {
+ prompt() {
+ view.prompt = "Fatal Blunder!"
+ if (is_card_in_play(S26_FATAL_BLUNDER)) {
+ gen_action_card(S26_FATAL_BLUNDER)
+ } else {
+ let done = true
+ for (let c of game.front[0]) {
+ if (data.cards[c].wing === PINK) {
+ gen_action_card(c)
+ done = false
+ }
+ }
+ if (done)
+ view.actions.end_turn = 1
+ }
+ },
+ card(c) {
+ if (c === S26_FATAL_BLUNDER) {
+ log("Fatal Blunder!")
+ eliminate_card(S26_FATAL_BLUNDER)
+ game.morale[0] ++
+ } else {
+ log("C" + c + " routed.")
+ eliminate_card(c)
+ game.morale[0] --
+ game.morale[1] ++
+ }
+ },
+ end_turn() {
+ if (check_morale_victory())
+ return
+ end_turn()
+ }
+}
+
+function end_turn() {
+ clear_undo()
+
+ map_clear(game.placed)
+ game.place_max = null
+
set_opponent_active()
goto_start_turn()
}
@@ -1413,6 +1478,7 @@ states.action = {
prompt() {
view.prompt = "Take an action."
view.actions.roll = 1
+ view.actions.end_turn = 0
let p = player_index()
for (let c of game.front[p]) {
@@ -2089,6 +2155,8 @@ function get_attack_hits(c, a) {
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.":
return count_dice_on_card(c)
case "1 hit per pair.":
case "1 hit per pair. 1 self per action.":
@@ -2121,6 +2189,8 @@ function get_attack_self(c, a) {
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 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!":
@@ -2128,7 +2198,7 @@ function get_attack_self(c, a) {
}
}
-// === ROUTING ===
+// === ROUTING/PURSUIT/REMOVE/FORCE-RETIRE ===
function find_card_owner(c) {
if (set_has(game.front[0], c) || set_has(game.reserve[0], c))
@@ -2142,15 +2212,16 @@ function should_rout_card(c) {
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
- }
+ 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
}
@@ -2215,18 +2286,51 @@ function goto_routing() {
function resume_routing() {
game.state = "routing"
+
for (let p = 0; p <= 1; ++p) {
for (let c of game.front[p])
- if (should_rout_card(c))
+ if (should_rout_card(c) || should_remove_card(c) || should_retire_card(c) || should_pursue(c))
return
for (let c of game.reserve[p])
- if (should_rout_card(c))
+ if (should_rout_card(c) || should_remove_card(c) || should_retire_card(c))
return
}
+
end_routing()
}
+states.routing = {
+ prompt() {
+ view.prompt = "Routing: Remove routing and pursuing cards from play!"
+ for (let p = 0; p <= 1; ++p) {
+ for (let c of game.front[p])
+ if (should_rout_card(c) || should_remove_card(c) || should_retire_card(c) || should_pursue(c))
+ gen_action_card(c)
+ for (let c of game.reserve[p])
+ if (should_rout_card(c) || should_remove_card(c) || should_retire_card(c))
+ gen_action_card(c)
+ }
+ },
+ card(c) {
+ if (should_rout_card(c)) {
+ log(card_name(c) + " routed.")
+ let p = find_card_owner(c)
+ game.routed[p] += data.cards[c].morale
+ } else if (should_retire_card(c)) {
+ log(card_name(c) + " retired.")
+ } else if (should_pursue(c)) {
+ log(card_name(c) + " pursued.")
+ } else {
+ log(card_name(c) + " removed.")
+ }
+ eliminate_card(c)
+ resume_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])) {
@@ -2272,115 +2376,9 @@ function end_routing() {
game.routed = null
- goto_pursuit()
-}
-
-states.routing = {
- prompt() {
- view.prompt = "Rout cards!"
- for (let p = 0; p <= 1; ++p) {
- for (let c of game.front[p])
- if (should_rout_card(c))
- gen_action_card(c)
- for (let c of game.reserve[p])
- if (should_rout_card(c))
- gen_action_card(c)
- }
- },
- card(c) {
- if (should_rout_card(c)) {
- log(card_name(c) + " routed.")
- let p = find_card_owner(c)
- game.routed[p] += data.cards[c].morale
- } else if (should_retire_card(c)) {
- log(card_name(c) + " retired.")
- } else {
- log(card_name(c) + " removed.")
- }
- eliminate_card(c)
- resume_routing()
- },
-}
-
-// === PURSUIT ===
-
-function goto_pursuit() {
- resume_pursuit()
-}
-
-function resume_pursuit() {
- game.state = "pursuit"
- for (let p = 0; p <= 1; ++p)
- for (let c of game.front[p])
- if (should_pursue(c))
- return
- end_pursuit()
-}
-
-function end_pursuit() {
- goto_removing()
-}
-
-states.pursuit = {
- prompt() {
- view.prompt = "Pursue cards!"
- for (let p = 0; p <= 1; ++p)
- for (let c of game.front[p])
- if (should_pursue(c))
- gen_action_card(c)
- },
- card(c) {
- log(card_name(c) + " pursued.")
- eliminate_card(c)
- resume_pursuit()
- },
-}
-
-// === REMOVING ===
-
-function goto_removing() {
- resume_removing()
-}
-
-function resume_removing() {
- game.state = "removing"
- for (let p = 0; p <= 1; ++p) {
- for (let c of game.front[p])
- if (should_remove_card(c) || should_retire_card(c))
- return
- for (let c of game.reserve[p])
- if (should_remove_card(c) || should_retire_card(c))
- return
- }
- end_removing()
-}
-
-function end_removing() {
goto_reserve()
}
-states.removing = {
- prompt() {
- view.prompt = "Remove or retire cards!"
- for (let p = 0; p <= 1; ++p) {
- for (let c of game.front[p])
- if (should_remove_card(c) || should_retire_card(c))
- gen_action_card(c)
- for (let c of game.reserve[p])
- if (should_remove_card(c) || should_retire_card(c))
- gen_action_card(c)
- }
- },
- card(c) {
- if (should_retire_card(c))
- log(card_name(c) + " retired.")
- else
- log(card_name(c) + " removed.")
- eliminate_card(c)
- resume_removing()
- },
-}
-
// === RESERVE ===
function should_enter_reserve(c) {