summaryrefslogtreecommitdiff
path: root/rules.js
diff options
context:
space:
mode:
authorTor Andersson <tor@ccxvii.net>2024-10-22 22:19:15 +0200
committerTor Andersson <tor@ccxvii.net>2024-10-22 23:37:57 +0200
commit23bd83c80d3e0a2d7dd18f76ae4039effd7ce448 (patch)
treefa5e2beeacbf025c8250cfe77059c7add6048363 /rules.js
parent78754928c9b1c55bdffe40ff33342c7329f3ce77 (diff)
downloadmaria-23bd83c80d3e0a2d7dd18f76ae4039effd7ce448.tar.gz
political shifts and troops+tcs
Diffstat (limited to 'rules.js')
-rw-r--r--rules.js327
1 files changed, 294 insertions, 33 deletions
diff --git a/rules.js b/rules.js
index 6a6d9d9..1450e9d 100644
--- a/rules.js
+++ b/rules.js
@@ -91,6 +91,7 @@ const F_ITALY_FRANCE = 4
const F_ITALY_AUSTRIA = 8
const F_SILESIA_ANNEXED = 16
const F_IMPERIAL_ELECTION = 32 // imperial election card revealed!
+const F_WAR_OF_JENKINS_EAR = 64
const SPADES = 0
const CLUBS = 1
@@ -741,6 +742,21 @@ function tc_per_turn() {
++n
}
+ // Jacobite Rising
+ if (game.power === P_PRAGMATIC) {
+ for (let p of all_power_generals[P_PRAGMATIC])
+ if (game.pos[p] === ENGLAND)
+ --n
+ }
+
+ // War of Jenkins' Ear
+ if (game.power === P_FRANCE) {
+ if (game.flags & F_WAR_OF_JENKINS_EAR) {
+ game.flags &= ~F_WAR_OF_JENKINS_EAR
+ --n
+ }
+ }
+
return n
}
@@ -2451,6 +2467,27 @@ states.recruit = {
},
}
+function enter_general_at(p, s) {
+ game.pos[p] = s
+ game.troops[p] = 1
+
+ // remove hussars
+ for (let p of all_hussars) {
+ if (game.pos[p] === s) {
+ log("P" + p + " removed.")
+ game.pos[p] = ELIMINATED
+ }
+ }
+
+ // remove enemy supply trains
+ for (let p of all_enemy_trains(game.power)) {
+ if (game.pos[p] === s) {
+ log("P" + p + " eliminated.")
+ game.pos[p] = ELIMINATED
+ }
+ }
+}
+
states.re_enter_general_where = {
inactive: "recruit",
prompt() {
@@ -2462,26 +2499,32 @@ states.re_enter_general_where = {
},
space(s) {
let p = game.selected
- game.pos[p] = s
set_add(game.recruit.pieces, p)
- if (is_general(p)) {
- game.recruit.troops += 1
- game.troops[p] = 1
- }
+ enter_general_at(p, s)
+ game.recruit.troops += 1
game.selected = -1
game.state = "recruit"
+ },
+}
- // remove hussars
- for (let p of all_hussars) {
- log("P" + p + " removed.")
- game.pos[p] = ELIMINATED
- }
-
- // remove enemy supply trains
- for (let p of all_enemy_trains(game.power)) {
- log("P" + p + " eliminated.")
- game.pos[p] = ELIMINATED
- }
+states.re_enter_general_from_political_card = {
+ inactive: "execute political card",
+ prompt() {
+ prompt("Re-enter " + format_selected() + ".")
+ view.selected = game.selected
+ for (let s of all_home_country_major_fortresses[game.power])
+ if (can_re_enter_general_at_city(s))
+ gen_action_space(s)
+ },
+ space(s) {
+ let p = game.selected
+ log("P" + p + " re-entered at S" + s + ".")
+ enter_general_at(p, s)
+ game.selected = -1
+ if (--game.count > 0)
+ game.state = "political_troops_place"
+ else
+ next_execute_political_card()
},
}
@@ -3239,7 +3282,7 @@ const INFLUENCE_ORDER = [
function goto_politics() {
game.political = []
- game.display = [ 0, 0, 0, 0 ]
+ game.placed = [ 0, 0, 0, 0 ]
game.stage = 0
log("Reveal")
@@ -3277,9 +3320,9 @@ states.determine_trump_suit = {
view.actions.suit = [ 0, 1, 2, 3 ]
},
suit(s) {
+ game.trump = s
log(power_name[game.power] + " chose " + suit_name[game.trump] + " as trump.")
log_br()
- game.trump = s
goto_place_tc_on_display()
}
}
@@ -3301,7 +3344,7 @@ states.place_tc_on_display = {
push_undo()
log(power_name[game.power] + " placed a TC.")
set_delete(game.hand[game.power], c)
- game.display[game.power] = c
+ game.placed[game.power] = c
game.state = "place_tc_on_display_done"
},
pass() {
@@ -3317,6 +3360,7 @@ states.place_tc_on_display_done = {
view.actions.next = 1
},
next() {
+ clear_undo()
end_place_tc_on_display()
},
}
@@ -3334,9 +3378,9 @@ function goto_determine_order_of_influence() {
// Turn cards face-up and return bluff cards
for (let pow of POWER_FROM_POLITICAL_STAGE) {
- let c = game.display[pow]
+ let c = game.placed[pow]
if (c > 0) {
- if (to_suit(c) === game.trump) {
+ if (is_reserve(c) || to_suit(c) === game.trump) {
log(">" + format_card(c) + " " + power_name[pow])
set_add(game.saved[pow], c)
} else {
@@ -3345,7 +3389,7 @@ function goto_determine_order_of_influence() {
}
}
}
- delete game.display
+ delete game.placed
log_br()
game.stage = 0
@@ -3404,7 +3448,10 @@ states.select_political_card = {
political(pc) {
push_undo()
log(power_name[game.power] + " chose C" + pc + ".")
- throw "TODO"
+ game.saved[game.power] = []
+ game.pc = pc
+ game.pcx = -1
+ next_execute_political_card()
},
pass() {
log(power_name[game.power] + " saved its TC.")
@@ -3430,6 +3477,221 @@ function end_politics() {
goto_place_hussars()
}
+/* POLITICAL CARDS */
+
+const event_shift = {
+ "Italy +1": { track: "italy", amount: [ 1 ] },
+ "Italy +2": { track: "italy", amount: [ 2 ] },
+ "Italy -1 or +1": { track: "italy", amount: [ -1, 1 ] },
+ "Italy -1 or +2": { track: "italy", amount: [ -1, 2 ] },
+ "Italy -1": { track: "italy", amount: [ -1 ] },
+ "Italy -2 or +1": { track: "italy", amount: [ -2, 1 ] },
+ "Italy -2": { track: "italy", amount: [ -2 ] },
+ "Russia +1": { track: "russia", amount: [ 1 ] },
+ "Russia -1 or +1": { track: "russia", amount: [ -1, 1 ] },
+ "Russia -1 or +2": { track: "russia", amount: [ -1, 2 ] },
+ "Russia -1": { track: "russia", amount: [ -1 ] },
+ "Russia -2": { track: "russia", amount: [ -2 ] },
+ "Saxony +1": { track: "saxony", amount: [ 1 ] },
+ "Saxony +4": { track: "saxony", amount: [ 4 ] },
+ "Saxony -1 if allied with Prussia": {
+ track: "saxony",
+ amount: [ -1 ],
+ condition: () => is_saxony_allied_with_prussia(),
+ },
+}
+
+const event_troops = {
+ "Your major power +3 troops":
+ { tcs: 0, troops: 3, power: () => [ game.power ] },
+ "France +1 TC and +3 troops":
+ { tcs: 1, troops: 3, power: () => [ P_FRANCE ] },
+ "Pragmatic +1 TC and +3 troops":
+ { tcs: 1, troops: 3, power: () => [ P_PRAGMATIC ] },
+ "Pragmatic or France +1 TC and +2 troops":
+ { tcs: 1, troops: 2, power: () => [ P_PRAGMATIC, P_FRANCE ] },
+ "France or Bavaria +2 troops":
+ { tcs: 0, troops: 2, power: () => [ P_FRANCE, P_BAVARIA ] },
+ "Saxony +2 troops":
+ { tcs: 0, troops: 2, power: () => [ P_SAXONY ] },
+}
+
+const event_misc = {
+ "Mannheim to French control": goto_mannheim_to_french_control,
+ "Pragmatic general to England": goto_pragmatic_general_to_england,
+ "France -1 TC this turn": goto_france_minus_tc_this_turn,
+}
+
+function current_political_effect() {
+ return political_cards[game.pc].effects[game.pcx]
+}
+
+function next_execute_political_card() {
+ if (++game.pcx === political_cards[game.pc].effects.length) {
+ game.state = "political_card_done"
+ return
+ }
+ let fx = current_political_effect()
+ if (fx in event_shift)
+ game.state = "political_shift"
+ else if (fx in event_troops)
+ game.state = "political_troop_power"
+ else if (fx in event_misc)
+ event_misc[fx]()
+}
+
+states.political_card_done = {
+ inactive: "execute political card",
+ prompt() {
+ prompt("Political card done.")
+ view.actions.end_political_card = 1
+ },
+ end_political_card() {
+ end_execute_political_card()
+ },
+}
+
+function end_execute_political_card() {
+ clear_undo()
+ array_remove_item(game.political, game.pc)
+ delete game.pc
+ delete game.pcx
+ goto_adjust_political_tracks()
+}
+
+const TRACK_NAME = { saxony: "Saxony marker", russia: "Russia marker", italy: "Italy marker" }
+const TRACK_SIZE = { saxony: 5, russia: 9, italy: 9 }
+
+states.political_shift = {
+ inactive: "execute political card",
+ prompt() {
+ let info = event_shift[current_political_effect()]
+ prompt("Shift " + TRACK_NAME[info.track] + ".")
+ if (info.condition === undefined || info.condition())
+ view.actions.shift = info.amount
+ view.actions.pass = 1
+ view.pc = game.pc
+ },
+ shift(n) {
+ push_undo()
+ let info = event_shift[current_political_effect()]
+ game[info.track] += n
+ game[info.track] = Math.max(1, Math.min(TRACK_SIZE[info.track], game[info.track]))
+ log("Shifted " + TRACK_NAME[info.track] + " to " + game[info.track] + ".")
+ next_execute_political_card()
+ },
+ pass() {
+ push_undo()
+ next_execute_political_card()
+ },
+}
+
+states.political_troop_power = {
+ inactive: "execute political card",
+ prompt() {
+ let fx = current_political_effect()
+ let info = event_troops[fx]
+ prompt(fx)
+ view.pc = game.pc
+ view.actions.power = info.power()
+ view.actions.pass = 1
+ },
+ power(pow) {
+ clear_undo()
+ let info = event_troops[current_political_effect()]
+ set_active_to_power(pow)
+ if (info.tcs > 0) {
+ draw_tc(info.tcs)
+ game.state = "political_troops_draw"
+ } else {
+ game.state = "political_troops_place"
+ }
+ game.count = info.troops
+ },
+ pass() {
+ push_undo()
+ next_execute_political_card()
+ },
+}
+
+states.political_troops_draw = {
+ inactive: "execute political card",
+ prompt() {
+ let info = event_troops[current_political_effect()]
+ prompt("Draw " + format_card_list_prompt(game.draw) + ".")
+ view.draw = game.draw
+ view.actions.next = 1
+ },
+ next() {
+ push_undo()
+ let info = event_troops[current_political_effect()]
+ if (info.tcs > 0) {
+ for (let c of game.draw)
+ set_add(game.hand[game.power], c)
+ delete game.draw
+ }
+ game.state = "political_troops_place"
+ },
+}
+
+states.political_troops_place = {
+ inactive: "execute political card",
+ prompt() {
+ let info = event_troops[current_political_effect()]
+ if (game.count > 1)
+ prompt("Add " + game.count + " troops.")
+ else if (game.count === 1)
+ prompt("Add 1 troop.")
+
+ if (game.count > 0) {
+ for (let p of all_power_generals[game.power]) {
+ if (is_piece_on_map(p) && game.troops[p] < 8)
+ gen_action_piece(p)
+ if (is_piece_eliminated(p) && has_re_entry_space_for_general())
+ gen_action_piece(p)
+ }
+ }
+
+ view.actions.pass = 1
+ },
+ piece(p) {
+ push_undo()
+ if (game.pos[p] === ELIMINATED) {
+ game.selected = p
+ game.state = "re_enter_general_from_political_card"
+ } else {
+ game.troops[p] += 1
+ if (--game.count === 0)
+ next_execute_political_card()
+ }
+ },
+ pass() {
+ push_undo()
+ next_execute_political_card()
+ },
+}
+
+function goto_mannheim_to_french_control() {
+ throw "TODO"
+}
+
+function goto_pragmatic_general_to_england() {
+ throw "TODO"
+}
+
+function goto_france_minus_tc_this_turn() {
+ throw "TODO"
+}
+
+/* POLITICAL TRACKS */
+
+function goto_adjust_political_tracks() {
+ // TODO: handle expeditionary corps
+ // TODO: handle victory points (italy)
+ // TODO: handle saxony alliance
+ goto_select_political_card()
+}
+
/* SETUP */
const POWER_FROM_SETUP_STAGE = [
@@ -3602,7 +3864,6 @@ exports.setup = function (seed, _scenario, _options) {
deck: null,
hand: [ [], [], [], [], [], [] ],
saved: [ [], [], [], [] ],
- display: [ [], [], [], [] ],
pos: setup_piece_position.slice(),
oos: 0,
@@ -3780,17 +4041,17 @@ function mask_hand(player) {
return view_hand
}
-function mask_display(player) {
- let view_display = []
+function mask_placed(player) {
+ let view_placed = []
for (let pow of all_major_powers) {
- if (game.display[pow] === 0)
- view_display[pow] = -1
+ if (game.placed[pow] === 0)
+ view_placed[pow] = -1
else if (player_from_power(pow) === player)
- view_display[pow] = game.display[pow]
+ view_placed[pow] = game.placed[pow]
else
- view_display[pow] = game.display[pow] & ~127
+ view_placed[pow] = game.placed[pow] & ~127
}
- return view_display
+ return view_placed
}
function mask_saved(player) {
@@ -3853,8 +4114,8 @@ exports.view = function (state, player) {
if (game.political)
view.political = game.political
- if (game.display)
- view.display = mask_display(player)
+ if (game.placed)
+ view.placed = mask_placed(player)
if (game.state === "game_over") {
view.prompt = game.victory