summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--play.js3
-rw-r--r--rules.js239
2 files changed, 217 insertions, 25 deletions
diff --git a/play.js b/play.js
index 5838520..25acc5d 100644
--- a/play.js
+++ b/play.js
@@ -1445,14 +1445,11 @@ function on_update() {
action_button("end_cards", "End card draw")
action_button("end_setup", "End setup")
action_button("end_recruit", "End recruit")
- action_button("end_flanders", "Pass Flanders moves")
action_button("end_movement", "End movement")
action_button("end_combat", "End combat")
action_button("end_supply", "End supply")
action_button("end_turn", "End turn")
- confirm_action_button("confirm_end_flanders", "Pass Flanders moves",
- "You have NOT moved ANY pieces on the FLANDERS map!\nAre you sure you want to SKIP movement for the FLANDERS map?")
confirm_action_button("confirm_end_movement", "End movement",
"You have NOT moved ANY pieces!\nAre you sure you want to SKIP movement?")
diff --git a/rules.js b/rules.js
index 0e0be91..58bb707 100644
--- a/rules.js
+++ b/rules.js
@@ -2,7 +2,8 @@
/*
-TODO: confirm austria/pragmatic stacking cooperation
+TODO: confirm stacking when re-entering Arenberg on Flanders map?
+TODO: stacking order (supreme) when re-entering genarals.
OPTIMIZE: fewer/smarter/smaller lists, smarter power control checks
OPTIMIZE: range checks instead of set checks
@@ -1945,6 +1946,7 @@ function goto_movement_flanders() {
}
function goto_movement_bohemia() {
+ log_br()
game.flags &= ~F_MOVE_FLANDERS
game.state = "movement"
if (!has_unmoved_piece_on_bohemia_map(game.power))
@@ -1962,10 +1964,12 @@ function resume_movement() {
let row = sequence_of_play[game.stage+1]
if (row.action === goto_movement_flanders) {
let next = game.power === P_PRAGMATIC ? P_AUSTRIA : P_PRAGMATIC
- if (!has_unmoved_piece_on_flanders_map(next))
+ if (!has_unmoved_piece_on_flanders_map(next)) {
+ ++game.stage // no unmoved pieces, so can skip next step
next_sequence_of_play()
- else
+ } else {
game.state = "movement_flanders_next"
+ }
} else {
game.state = "movement"
}
@@ -1974,6 +1978,20 @@ function resume_movement() {
}
}
+function resume_movement_after_flanders_stacking() {
+ set_active_to_power(coop_major_power(game.power))
+ game.selected = -1
+ let row = sequence_of_play[game.stage+1]
+ if (row.action === goto_movement_flanders) {
+ next_sequence_of_play()
+ } else {
+ if (has_unmoved_piece_on_flanders_map(game.power))
+ game.state = "movement"
+ else
+ next_sequence_of_play()
+ }
+}
+
function is_forbidden_neutral_space(pow, to) {
if (is_saxony_neutral()) {
if (pow === P_SAXONY) {
@@ -2054,6 +2072,17 @@ function has_moved_any_bohemia_pieces() {
return false
}
+function may_end_movement() {
+ if (game.flags & F_MOVE_FLANDERS) {
+ // No passing of movement on Flanders map while alternating moves.
+ if (game.power === P_AUSTRIA)
+ return !has_unmoved_piece_on_flanders_map(P_PRAGMATIC)
+ if (game.power === P_PRAGMATIC)
+ return !has_unmoved_piece_on_flanders_map(P_AUSTRIA)
+ }
+ return true
+}
+
states.movement = {
inactive: "move",
prompt() {
@@ -2099,12 +2128,7 @@ states.movement = {
else
prompt("Move your generals and supply trains.")
- if (game.power === P_AUSTRIA && (game.flags & F_MOVE_FLANDERS)) {
- if (!has_moved_any_pieces())
- view.actions.confirm_end_flanders = 1
- else
- view.actions.end_flanders = 1
- } else {
+ if (may_end_movement()) {
if (!has_moved_any_pieces())
view.actions.confirm_end_movement = 1
else
@@ -2139,12 +2163,6 @@ states.movement = {
game.state = "move_general"
}
},
- confirm_end_flanders() {
- this.end_flanders()
- },
- end_flanders() {
- this.end_movement()
- },
confirm_end_movement() {
this.end_movement()
},
@@ -2197,6 +2215,8 @@ function is_illegal_cross_map_move(from, to) {
}
function can_move_general_to(p, from, to) {
+ if (to === game.forbidden)
+ return false
if (is_forbidden_neutral_space(piece_power[p], to))
return false
if (is_illegal_cross_map_move(from, to))
@@ -2349,7 +2369,10 @@ states.move_general = {
if (s_give > 0 && u_take > 0)
view.actions.give = 1
- view.actions.stop = 1
+ if (here === game.forbidden)
+ view.actions.stop = 0
+ else
+ view.actions.stop = 1
} else {
gen_action_piece(who)
view.actions.stop = 1
@@ -2517,6 +2540,35 @@ states.move_give = {
},
}
+function is_flanders_stack_move() {
+ let here = game.pos[game.selected]
+ if (is_flanders_space(here)) {
+ if (game.power === P_PRAGMATIC) {
+ if (find_general_of_power(here, P_AUSTRIA) >= 0)
+ return true
+ }
+ if (game.power === P_AUSTRIA) {
+ if (find_general_of_power(here, P_PRAGMATIC) >= 0)
+ return true
+ }
+ }
+ return false
+}
+
+function is_alternating_move() {
+ if (game.flags & F_MOVE_FLANDERS) {
+ if (game.power === P_PRAGMATIC) {
+ if (has_unmoved_piece_on_flanders_map(P_AUSTRIA))
+ return true
+ }
+ if (game.power === P_AUSTRIA) {
+ if (has_unmoved_piece_on_flanders_map(P_PRAGMATIC))
+ return true
+ }
+ }
+ return false
+}
+
function end_move_piece() {
let here = game.pos[game.selected]
@@ -2536,6 +2588,12 @@ function end_move_piece() {
delete game.move_path
+ if (is_alternating_move()) {
+ log_conquest(game.move_conq)
+ game.move_conq = []
+ log_br()
+ }
+
// uniting stacks: flag all as moved
let supreme = false
if (is_general(game.selected)) {
@@ -2548,8 +2606,13 @@ function end_move_piece() {
}
}
+ if (game.flags & F_MOVE_FLANDERS)
+ delete game.forbidden
+
if (supreme)
game.state = "move_supreme"
+ else if (is_flanders_stack_move())
+ goto_confirm_flanders_stack()
else
resume_movement()
}
@@ -2572,13 +2635,110 @@ states.move_supreme = {
if (game.pos[p] === here)
game.supreme &= ~(1<<p)
game.supreme |= (1<<p)
- resume_movement()
+ if (is_flanders_stack_move())
+ goto_confirm_flanders_stack()
+ else
+ resume_movement()
},
piece(p) {
this.supreme(p)
},
}
+function goto_confirm_flanders_stack() {
+ game.state = "confirm_flanders_stack_1"
+}
+
+states.confirm_flanders_stack_1 = {
+ inactive: "move",
+ prompt() {
+ let p = game.selected
+ let s = game.pos[p]
+ let other = -1
+ view.selected = game.selected
+ if (game.power === P_AUSTRIA) {
+ other = find_general_of_power(s, P_PRAGMATIC)
+ view.actions.power = [ P_PRAGMATIC ]
+ } else {
+ other = find_general_of_power(s, P_AUSTRIA)
+ view.actions.power = [ P_AUSTRIA ]
+ }
+ prompt(`${piece_name[p]} needs permission to stack with ${piece_name[other]} at ${data.cities.name[s]}.`)
+ },
+ power(_) {
+ // save last undo step in game.proposal
+ game.proposal = game.undo[game.undo.length - 1]
+
+ if (game.power === P_AUSTRIA)
+ set_active_to_power(P_PRAGMATIC)
+ else
+ set_active_to_power(P_AUSTRIA)
+ game.state = "confirm_flanders_stack_2"
+ },
+}
+
+states.confirm_flanders_stack_2 = {
+ inactive: "confirm mixed stack",
+ prompt() {
+ let p = game.selected
+ let s = game.pos[p]
+ view.selected = game.selected
+ let other = -1
+ if (game.power === P_AUSTRIA)
+ other = find_general_of_power(s, P_AUSTRIA)
+ else
+ other = find_general_of_power(s, P_PRAGMATIC)
+ prompt(`${piece_name[p]} wants to stack with ${piece_name[other]} at ${data.cities.name[s]}.`)
+ view.actions.accept = 1
+ view.actions.refuse = 1
+ },
+ accept() {
+ delete game.proposal
+ if (game.power === P_AUSTRIA)
+ set_active_to_power(P_PRAGMATIC)
+ else
+ set_active_to_power(P_AUSTRIA)
+ resume_movement_after_flanders_stacking()
+ },
+ refuse() {
+ // remember who moved, then restore back to saved undo point
+ // select general to move to another city
+ let p = game.selected
+ let s = game.pos[game.selected]
+ game.undo = [ game.proposal ]
+ delete game.proposal
+ pop_undo()
+ game.selected = p
+ game.forbidden = s
+ game.state = "confirm_flanders_stack_3"
+ },
+}
+
+states.confirm_flanders_stack_3 = {
+ inactive: "move",
+ prompt() {
+ prompt(`${piece_name[game.selected]} is not allowed to stack at ${data.cities.name[game.forbidden]}.`)
+ gen_action_piece(game.selected)
+ //view.selected = game.selected
+ //view.actions.next = 1
+ },
+ piece(p) {
+ this.next()
+ },
+ next() {
+ push_undo()
+ let s = game.forbidden
+ /*
+ if (game.power === P_AUSTRIA)
+ log("Pragmatic Army refused to stack at S" + s + ".")
+ else
+ log("Austria refused to stack at S" + s + ".")
+ */
+ states.movement.piece(game.selected)
+ game.undo.pop()
+ },
+}
+
/* RE-ENTER SUPPLY TRAIN */
function goto_re_enter_train() {
@@ -3674,12 +3834,23 @@ function finish_combat() {
/* RETRO-ACTIVE CONQUEST */
+function get_fortress_control_power(s) {
+ for (let pow of all_major_powers)
+ if (is_power_controlled_fortress(pow, s))
+ return pow
+ return -1
+}
+
function log_conquest(conq) {
if (conq.length > 0) {
- log_br()
- log("Conquered")
- for (let s of conq)
- log(">S" + s)
+ let groups = map_group_by(conq, get_fortress_control_power)
+ map_for_each(groups, (pow, list) => {
+ log_br()
+ log(power_name[pow] + " Conquered")
+ for (let s of list)
+ log(">S" + s)
+ })
+
}
}
@@ -5834,7 +6005,7 @@ function log_br() {
/* COMMON LIBRARY */
function clear_undo() {
- game.undo.length = 0
+ game.undo = []
}
function push_undo() {
@@ -6101,3 +6272,27 @@ function map_for_each(map, f) {
for (let i = 0; i < map.length; i += 2)
f(map[i], map[i+1])
}
+
+function map_group_by(items, callback) {
+ let groups = []
+ if (typeof callback === "function") {
+ for (let item of items) {
+ let key = callback(item)
+ let arr = map_get(groups, key)
+ if (arr)
+ arr.push(item)
+ else
+ map_set(groups, key, [ item ])
+ }
+ } else {
+ for (let item of items) {
+ let key = item[callback]
+ let arr = map_get(groups, key)
+ if (arr)
+ arr.push(item)
+ else
+ map_set(groups, key, [ item ])
+ }
+ }
+ return groups
+}