summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTor Andersson <tor@ccxvii.net>2024-07-27 17:30:13 +0200
committerTor Andersson <tor@ccxvii.net>2024-08-21 00:28:20 +0200
commiteaa39d34fc8f20b5e996aa896f338d765456ad60 (patch)
tree81474030af1fb26b10bf8ea537994ecd2b5dcad3
parente78db48e0bf7aab788731f1856af49f2e7d3f27a (diff)
downloadwashingtons-war-eaa39d34fc8f20b5e996aa896f338d765456ad60.tar.gz
manual arnold. attrition fix.
-rw-r--r--play.js15
-rw-r--r--rules.js263
2 files changed, 183 insertions, 95 deletions
diff --git a/play.js b/play.js
index 9fee8e5..7e6852b 100644
--- a/play.js
+++ b/play.js
@@ -437,7 +437,7 @@ function get_static_xy(s) {
let x, y
if (s >= 66) {
x = data.spaces[s].x
- y = data.spaces[s].y
+ y = data.spaces[s].y + 60
} else {
x = data.spaces[s].x - 20
y = data.spaces[s].y + 15
@@ -568,7 +568,7 @@ function on_update() {
if (s === NOWHERE)
continue
- let pos = ui.layout_static[s]
+ let pos = (s < 66) ? ui.layout_static[s] : get_static_xy(s)
if (view.move && view.move.who === g)
pos = ui.layout_move
if (view.react && view.react.who === g)
@@ -576,6 +576,9 @@ function on_update() {
let [ x, y ] = pos
+ if (s >= 66)
+ y -= 60
+
if (view.move && view.move.who === g) {
ui.generals[g].classList.add("selected")
} else if (view.react && view.react.who === g) {
@@ -587,13 +590,13 @@ function on_update() {
let offset = general_offset(g)
x += offset * (45 + 9)
} else {
+ let wrap = (s === 66 ? 3 : 4)
let total = general_total(g)
let offset = general_offset(g)
- let off_x = offset % 3
- let off_y = offset / 3 | 0
- let ctr_x = total > 3 ? 3 : total
+ let off_x = offset % wrap
+ let off_y = offset / wrap | 0
+ let ctr_x = total > wrap ? wrap : total
off_x -= (ctr_x - 1) / 2
- // off_y -= (total / 3 | 0) / 2
x += off_x * (45 + 9)
y += off_y * (45 + 9)
y -= 15
diff --git a/rules.js b/rules.js
index f8d8d35..99bb69d 100644
--- a/rules.js
+++ b/rules.js
@@ -12,9 +12,6 @@
- move (no player shift)
*/
-// campaign
-// event
-
/* DATA */
const data = require("./data.js")
@@ -126,6 +123,14 @@ function card_name(c) {
return "\u201c" + CARDS[c].title + "\u201d"
}
+function space_name(s) {
+ return SPACES[s].name
+}
+
+function general_name(s) {
+ return GENERALS[s].name
+}
+
/* SETUP */
function setup_game(seed) {
@@ -158,6 +163,7 @@ function setup_game(seed) {
a_queue: 0,
b_queue: 0,
+ card: 0,
last_played: 0,
did_discard_event: 0,
deck: null,
@@ -285,7 +291,6 @@ function play_card(c, reason) {
if (CARDS[c].reshuffle === "if_played")
set_flag(F_RESHUFFLE)
set_delete(active_hand(), c)
- game.last_played = c
if (CARDS[c].once) {
log("Removed card " + c + ".")
set_add(game.removed, c)
@@ -300,7 +305,6 @@ function discard_card_from_hand(hand, c) {
}
function discard_card(c, reason) {
- game.last_played = c
discard_card_from_hand(active_hand(), c)
if (reason)
logp("discarded #" + c + " " + reason)
@@ -799,13 +803,6 @@ function capture_enemy_general(where) {
capture_british_general(where)
}
-function remove_benedict_arnold() {
- if (!is_general_at_location(ARNOLD, NOWHERE)) {
- log("Removed Arnold from the game!")
- set_general_location(ARNOLD, NOWHERE)
- }
-}
-
/* ARMIES */
function has_british_army(where) {
@@ -1476,7 +1473,7 @@ function end_strategy_card() {
states.end_strategy_card = {
prompt() {
- view.prompt = "Done."
+ view.prompt = "Card play done."
view.actions.next = 1
},
next() {
@@ -1486,6 +1483,8 @@ states.end_strategy_card = {
}
function next_strategy_card() {
+ game.card = 0
+
if (!has_flag(F_FRENCH_ALLIANCE_TRIGGERED) && game.french_alliance === 9) {
log("The French signed an alliance with the Americans!")
set_flag(F_FRENCH_ALLIANCE_TRIGGERED)
@@ -2178,13 +2177,14 @@ function disperse_and_overrun_after_move() {
let cu = game.move.carry_british + game.move.carry_american + game.move.carry_french
let to = game.move.to
if (cu > 0) {
- if (has_enemy_general(to))
+ let egen = has_enemy_general(to)
+ let ecu = count_enemy_cu(to)
+ if (egen && ecu === 0)
capture_enemy_general(to)
- if (cu >= 4 && count_enemy_cu(to) === 1 && !has_enemy_general(to))
+ if (cu >= 4 && ecu === 1 && !egen)
overrun(to)
- if (game.active === P_BRITAIN && game.congress === to && !has_enemy_cu(to)) {
+ if (game.active === P_BRITAIN && game.congress === to && ecu === 0)
disperse_continental_congress()
- }
}
}
@@ -2341,7 +2341,7 @@ function goto_intercept() {
states.intercept_who = {
prompt() {
- view.prompt = "Intercept " + game.move.who + " at " + game.move.to + "?"
+ view.prompt = "Intercept " + general_name(game.move.who) + " at " + space_name(game.move.to) + "?"
view.actions.pass = 1
gen_intercept()
},
@@ -2414,7 +2414,7 @@ function can_retreat_before_battle() {
return false
// can't retreat if attempted (successful or not) interception!
let g = find_american_or_french_general(game.move.to)
- if (g && !has_general_moved(g))
+ if (g !== NOBODY && !has_general_moved(g))
return true
return false
}
@@ -2532,6 +2532,41 @@ function end_retreat_before_battle() {
/* BATTLE CARDS */
+function is_trigger_remove_benedict_arnold(c) {
+ return (
+ game.active === P_BRITAIN &&
+ CARDS[c].event === "remove_benedict_arnold" &&
+ !is_general_at_location(ARNOLD, NOWHERE)
+ )
+}
+
+function remove_benedict_arnold() {
+ log("Removed Arnold from the game!")
+ set_general_location(ARNOLD, NOWHERE)
+}
+
+states.remove_benedict_arnold_attacker = {
+ prompt() {
+ view.prompt = "Remove Benedict Arnold from the game!"
+ gen_action_general(ARNOLD)
+ },
+ general(g) {
+ remove_benedict_arnold()
+ game.state = "play_attacker_battle_card_confirm"
+ }
+}
+
+states.remove_benedict_arnold_defender = {
+ prompt() {
+ view.prompt = "Remove Benedict Arnold from the game!"
+ gen_action_general(ARNOLD)
+ },
+ general(g) {
+ remove_benedict_arnold()
+ game.state = "play_defender_battle_card_confirm"
+ }
+}
+
function goto_play_attacker_battle_card() {
game.active = game.combat.attacker
game.state = "play_attacker_battle_card"
@@ -2550,15 +2585,16 @@ states.play_attacker_battle_card = {
push_undo()
play_card(c, "for +2 DRM")
if (game.active === P_BRITAIN) {
- if (CARDS[c].event === "remove_benedict_arnold")
- remove_benedict_arnold()
game.combat.b_draw_after_battle = true
game.combat.b_bonus += 2
} else {
game.combat.a_draw_after_battle = true
game.combat.a_bonus += 2
}
- game.state = "play_attacker_battle_card_confirm"
+ if (is_trigger_remove_benedict_arnold(c))
+ game.state = "remove_benedict_arnold_attacker"
+ else
+ game.state = "play_attacker_battle_card_confirm"
},
card_battle_discard(c) {
push_undo()
@@ -2613,15 +2649,16 @@ states.play_defender_battle_card = {
push_undo()
play_card(c, "for +2 DRM")
if (game.active === P_BRITAIN) {
- if (CARDS[c].event === "remove_benedict_arnold")
- remove_benedict_arnold()
game.combat.b_draw_after_battle = true
game.combat.b_bonus += 2
} else {
game.combat.a_draw_after_battle = true
game.combat.a_bonus += 2
}
- game.state = "play_defender_battle_card_confirm"
+ if (is_trigger_remove_benedict_arnold(c))
+ game.state = "remove_benedict_arnold_defender"
+ else
+ game.state = "play_defender_battle_card_confirm"
},
card_battle_discard(c) {
push_undo()
@@ -3014,13 +3051,14 @@ function gen_attacker_retreat() {
}
function goto_retreat_after_battle(victor) {
+ let from = game.move.to
if (victor === P_BRITAIN) {
- let who = find_american_or_french_general(game.move.to)
- if (who === NOBODY && count_american_and_french_cu(game.move.to) === 0)
+ let who = find_american_or_french_general(from)
+ if (who === NOBODY && count_american_and_french_cu(from) === 0)
return end_battle()
} else {
- let who = find_british_general(game.move.to)
- if (who === NOBODY && count_british_cu(game.move.to) === 0)
+ let who = find_british_general(from)
+ if (who === NOBODY && count_british_cu(from) === 0)
return end_battle()
}
game.active = ENEMY[victor]
@@ -3043,7 +3081,9 @@ states.retreat_after_battle = {
else
retreat_american_army(game.move.to, to)
- // TODO: disperse congress?
+ // TODO: disperse congress with end_battle?
+ if (game.active === P_BRITAIN && game.congress === to)
+ disperse_continental_congress()
if (has_enemy_general(to))
capture_enemy_general(to)
@@ -3105,7 +3145,6 @@ events.campaign = function (c, card) {
events.the_war_ends = function (c, card) {
logp("played #" + c)
log("The war will end in " + card.year)
- game.last_played = c
set_delete(active_hand(), c)
game.war_ends = c
end_strategy_card()
@@ -3660,18 +3699,40 @@ function goto_winter_attrition_phase() {
goto_american_winter_attrition()
}
+function has_american_winter_attrition(s) {
+ let n_american = count_american_cu(s)
+ let n_french = count_french_cu(s)
+
+ // EXCEPT: Single American CU in a space with American or French General.
+ if (n_american === 1 && n_french === 0 && has_american_or_french_general(s))
+ return false
+
+ // EXCEPT: Single French CU in a space with American or French General not at WQ.
+ if (n_american === 0 && n_french === 1 && has_american_or_french_general(s))
+ return false
+
+ // EXCEPT: Stack with up to 5 CU with Washington at WQ.
+ if (n_american + n_french <= 5 && is_general_at_location(WASHINGTON, s) && is_winter_quarter_space(s))
+ return false
+
+ // American (or mixed with French) suffer attrition regardless of location.
+ if (n_american > 0)
+ return true
+
+ // French suffer attrition only at WQ
+ if (n_french > 0 && !is_winter_quarter_space(s))
+ return true
+
+ return false
+}
+
function goto_american_winter_attrition() {
log("=a Winter Attrition")
game.active = P_AMERICA
game.attrition = []
- for (let space of all_spaces) {
- let wq = is_winter_quarter_space(space)
- let n_american = count_american_cu(space)
- let n_french = count_french_cu(space)
- let has_washington = is_general_at_location(WASHINGTON, space)
- if (n_american > 0 || (n_french > 0 && !wq))
- game.attrition.push(space)
- }
+ for (let s of all_spaces)
+ if (has_american_winter_attrition(s))
+ game.attrition.push(s)
if (game.attrition.length > 0)
game.state = "american_winter_attrition"
else
@@ -3708,11 +3769,14 @@ function end_british_winter_attrition() {
states.american_winter_attrition = {
prompt() {
- view.prompt = "Winter Attrition."
- for (let s of game.attrition)
- gen_action_space(s)
- if (game.attrition.length === 0)
+ if (game.attrition.length > 0) {
+ view.prompt = "Winter Attrition."
+ for (let s of game.attrition)
+ gen_action_space(s)
+ } else {
+ view.prompt = "Winter Attrition: Done."
view.actions.next = 1
+ }
},
space(s) {
let wq = is_winter_quarter_space(s)
@@ -3720,32 +3784,38 @@ states.american_winter_attrition = {
let n_french = count_french_cu(s)
let has_washington = is_general_at_location(WASHINGTON, s)
- if (n_american === 0 && n_french === 1 && !wq)
- apply_single_winter_attrition(remove_french_cu, s, has_american_or_french_general(s))
- if (n_american === 0 && n_french > 1 && !wq)
- apply_winter_attrition(remove_french_cu, s, n_french)
+ // single american
if (n_american === 1 && n_french === 0)
- apply_single_winter_attrition(remove_american_cu, s, has_american_or_french_general(s))
- if (n_american > 1 && n_french === 0) {
- let n = n_american
- if (has_washington && wq)
- n = Math.max(0, n - 5)
- apply_winter_attrition(remove_american_cu, s, n)
+ apply_single_winter_attrition(remove_american_cu, s)
+
+ // single french
+ else if (n_american === 0 && n_french === 1)
+ apply_single_winter_attrition(remove_french_cu, s)
+
+ // french stack
+ else if (n_american === 0 && n_french > 1)
+ apply_winter_attrition(remove_french_cu, s, n_french)
+
+ // american stack
+ else if (n_american > 0 && n_french === 0) {
+ if (wq && has_washington)
+ apply_winter_attrition(remove_american_cu, s, n_american - 5)
+ else
+ apply_winter_attrition(remove_american_cu, s, n_american)
}
- if (n_american > 0 && n_french > 0) {
- let n = n_american + n_french
- if (has_washington && wq)
- n = Math.max(0, n - 5)
- let half = Math.floor(n / 2)
+ // mixed stack
+ else if (n_american > 0 && n_french > 0) {
+ let n_total = n_american + n_french
+ if (wq && has_washington)
+ n_total = Math.max(0, n_total - 5)
- // TODO: let player choose (but why would he ever choose the french?)
+ // TODO: let player choose (but why would they ever choose French?)
+ let half = Math.floor(n_total / 2)
let lose_american = Math.min(half, n_american)
log("Lost " + lose_american + " American CU in " + s)
remove_american_cu(s, n_american)
half -= lose_american
- n_american -= lose_american
-
if (half > 0) {
log("Lost " + half + " French CU in " + s)
remove_french_cu(s, half)
@@ -3761,19 +3831,21 @@ states.american_winter_attrition = {
states.british_winter_attrition = {
prompt() {
- view.prompt = "Winter Attrition."
- for (let s of game.attrition)
- gen_action_space(s)
- if (game.attrition.length === 0)
+ if (game.attrition.length > 0) {
+ view.prompt = "Winter Attrition."
+ for (let s of game.attrition)
+ gen_action_space(s)
+ } else {
+ view.prompt = "Winter Attrition: Done."
view.actions.next = 1
+ }
},
space(s) {
- let wq = is_winter_quarter_space(s)
let n_british = count_british_cu(s)
- if (n_british === 1 && !wq)
- apply_single_winter_attrition(remove_british_cu, s, has_british_general(s))
- if (n_british > 1 && !wq)
+ if (n_british === 1)
+ apply_single_winter_attrition(remove_british_cu, s)
+ else if (n_british > 1)
apply_winter_attrition(remove_british_cu, s, n_british)
set_delete(game.attrition, s)
@@ -3960,22 +4032,7 @@ function has_british_place_pc_markers_segment() {
return false
}
-function gen_american_place_pc_markers_segment() {
- for (let space of all_spaces)
- if (has_american_army(space))
- if (has_no_pc(space) || has_british_pc(space))
- gen_action_space(space)
- if (!view.actions.space)
- view.actions.next = 1
-}
-
function gen_british_place_pc_markers_segment() {
- for (let space of all_spaces)
- if (has_british_army(space))
- if (has_no_pc(space) || has_american_pc(space))
- gen_action_space(space)
- if (!view.actions.space)
- view.actions.next = 1
}
function goto_place_pc_markers_segment() {
@@ -3992,8 +4049,22 @@ function goto_place_pc_markers_segment() {
states.place_american_pc_markers_segment = {
prompt() {
- view.prompt = "Place American PC markers."
- gen_american_place_pc_markers_segment()
+ let done = true
+ for (let space of all_spaces) {
+ if (has_american_army(space)) {
+ if (has_no_pc(space) || has_british_pc(space)) {
+ done = false
+ gen_action_space(space)
+ }
+ }
+ }
+ if (done) {
+ view.prompt = "Place American PC markers: Done."
+ view.actions.next = 1
+ } else {
+ view.prompt = "Place American PC markers."
+ view.actions.next = 0
+ }
},
space(s) {
if (has_no_pc(s))
@@ -4008,8 +4079,22 @@ states.place_american_pc_markers_segment = {
states.place_british_pc_markers_segment = {
prompt() {
- view.prompt = "Place British PC markers."
- gen_british_place_pc_markers_segment()
+ let done = true
+ for (let space of all_spaces) {
+ if (has_british_army(space)) {
+ if (has_no_pc(space) || has_american_pc(space)) {
+ done = false
+ gen_action_space(space)
+ }
+ }
+ }
+ if (done) {
+ view.prompt = "Place British PC markers: Done."
+ view.actions.next = 1
+ } else {
+ view.prompt = "Place British PC markers."
+ view.actions.next = 0
+ }
},
space(s) {
if (has_no_pc(s))
@@ -4339,7 +4424,7 @@ exports.view = function (state, current) {
b_cards: game.b_hand.length,
a_queue: game.a_queue,
b_queue: game.b_queue,
- last_played: game.last_played,
+ last_played: game.card ? game.card : game.did_discard_event,
move: game.move,
}