From 0d300b3070f51910af34877de9de97d4fa163703 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Thu, 24 Oct 2024 13:45:01 +0200 Subject: Create and cancel subsidy contracts. --- images/shaking-hands.svg | 1 + play.html | 9 ++ play.js | 31 +++++- rules.js | 250 +++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 270 insertions(+), 21 deletions(-) create mode 100644 images/shaking-hands.svg diff --git a/images/shaking-hands.svg b/images/shaking-hands.svg new file mode 100644 index 0000000..f3b76cd --- /dev/null +++ b/images/shaking-hands.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/play.html b/play.html index 2975890..fc19c08 100644 --- a/play.html +++ b/play.html @@ -30,6 +30,15 @@ +
+ + + + +
  • Propose subsidy contract +
  • Cancel subsidy contract +
  • +
    diff --git a/play.js b/play.js index 5e8357f..f3f5630 100644 --- a/play.js +++ b/play.js @@ -22,6 +22,23 @@ function toggle_shift() { document.body.classList.toggle("shift") } +function action_menu_item(action) { + let menu = document.getElementById(action + "_menu") + if (view.actions && action in view.actions) { + menu.classList.toggle("disabled", view.actions[action] === 0) + return 1 + } else { + menu.classList.toggle("disabled", true) + return 0 + } +} + +function action_menu(menu, action_list) { + let x = 0 + for (let action of action_list) + x |= action_menu_item(action) +} + /* DATA (SHARED) */ const deck_name = [ "red", "green", "blue", "yellow" ] @@ -1267,6 +1284,11 @@ function on_update() { layout_combat_marker() } + action_menu(document.getElementById("subsidy_menu"), [ + "propose_subsidy", + "cancel_subsidy", + ]) + action_button_with_argument("suit", SPADES, colorize_S) action_button_with_argument("suit", CLUBS, colorize_C) action_button_with_argument("suit", HEARTS, colorize_H) @@ -1287,10 +1309,13 @@ function on_update() { for (let pow of all_powers) action_button_with_argument("power", pow, power_name[pow]) - action_button("reduce", "Reduce") - action_button("offer", "Offer") + action_button("subsidy", "Subsidy") + action_button("cancel", "Cancel") + + action_button("reduce", "Reduce military objectives") + action_button("offer", "Offer peace") action_button("accept", "Accept") - action_button("deny", "Deny") + action_button("refuse", "Refuse") action_button("re_enter", "Re-enter") action_button("force_march", "Force march") diff --git a/rules.js b/rules.js index 8ebf3cc..18ed12c 100644 --- a/rules.js +++ b/rules.js @@ -543,6 +543,14 @@ function all_enemy_powers(pow) { } } +function is_allied_power(a, b) { + return !all_enemy_powers(a).includes(b) +} + +function is_controlled_power(major, other) { + return player_from_power(major) === player_from_power(other) +} + function all_non_coop_powers(pow) { switch (pow) { case P_FRANCE: @@ -2485,13 +2493,12 @@ states.re_enter_train_power = { inactive: "move", prompt() { prompt("Re-enter supply train from which power?") - view.actions.power = [] for (let pow of all_controlled_powers(game.power)) { if (game.move_re_entered & (1 << pow)) continue for (let p of all_power_trains[pow]) { if (can_train_re_enter(p)) { - view.actions.power.push(pow) + gen_action_power(pow) break } } @@ -4530,12 +4537,12 @@ states.accept_peace = { prompt() { prompt("Accept Prussia's offer of temporary peace to annex Silesia?") view.actions.accept = 1 - view.actions.deny = 1 + view.actions.refuse = 1 }, accept() { goto_annex_silesia() }, - deny() { + refuse() { end_action_stage_2() }, } @@ -4874,6 +4881,195 @@ function end_imperial_election() { goto_start_turn() } +/* SUBSIDY CONTRACTS - CREATE */ + +function goto_propose_subsidy() { + game.proposal = { save_power: game.power, save_state: game.state, from: -1, to: -1, n: 0 } + game.state = "propose_subsidy_from" +} + +states.propose_subsidy_from = { + inactive: "create subsidy contract", + prompt() { + prompt("Subsidy contract from which major power?") + for (let from of all_major_powers) + if (is_allied_power(game.power, from)) + gen_action_power(from) + }, + power(from) { + game.proposal.from = from + game.state = "propose_subsidy_to" + } +} + +states.propose_subsidy_to = { + inactive: "create subsidy contract", + prompt() { + let player = game.power + let from = game.proposal.from + prompt(`Subsidy contract between ${power_name[from]} and who?`) + for (let to of all_powers) { + if (from !== to && is_allied_power(from, to)) { + if (is_controlled_power(player, from) || is_controlled_power(player, to)) + gen_action_power(to) + } + } + }, + power(to) { + game.proposal.to = to + game.state = "propose_subsidy_length" + } +} + +states.propose_subsidy_length = { + inactive: "create subsidy contract", + prompt() { + let from = game.proposal.from + let to = game.proposal.to + prompt(`Subsidy contract between ${power_name[from]} and ${power_name[to]} for how many turns?`) + view.actions.value = [ 1, 2, 3, 4, 5, 6 ] + }, + value(n) { + clear_undo() + game.proposal.n = n + set_active_to_power(game.proposal.from) + game.state = "propose_subsidy_approve" + }, +} + +states.propose_subsidy_approve = { + inactive: "approve subsidy contract", + prompt() { + let from = game.proposal.from + let to = game.proposal.to + let n = game.proposal.n + prompt(`Subsidy contract between ${power_name[from]} and ${power_name[to]} for ${n} turns?`) + view.actions.accept = 1 + view.actions.refuse = 1 + }, + accept() { + let from = game.proposal.from + let to = game.proposal.to + let n = game.proposal.n + log(`Subsidy contract between ${power_name[from]} and ${power_name[to]} for ${n} turns.`) + map_set(game.contracts[from], to, map_get(game.contracts[from], to, 0) + n) + end_propose_subsidy() + }, + refuse() { + let from = game.proposal.from + let to = game.proposal.to + let n = game.proposal.n + log(`${power_name[from]} refused to create subsidy contract to ${power_name[to]} for ${n} turns.`) + end_propose_subsidy() + }, +} + +function end_propose_subsidy() { + set_active_to_power(game.proposal.save_power) + game.state = game.proposal.save_state + delete game.proposal +} + +/* SUBSIDY CONTRACTS - CANCEL */ + +function may_cancel_subsidy() { + let player = game.power + let result = false + for (let from of all_major_powers) { + map_for_each(game.contracts[from], (to, n) => { + // cannot cancel initial contract! + if (from === P_FRANCE && to === P_BAVARIA && game.turn < 3) + return + if (is_controlled_power(player, from) || is_controlled_power(player, to)) + result = true + }) + } + return result +} + +function goto_cancel_subsidy() { + game.proposal = { save_power: game.power, save_state: game.state, from: -1, to: -1 } + game.state = "cancel_subsidy_from" +} + +states.cancel_subsidy_from = { + inactive: "cancel subsidy contract", + prompt() { + let player = game.power + prompt("Cancel which subsidy contract?") + for (let from of all_major_powers) { + map_for_each(game.contracts[from], (to, n) => { + // cannot cancel initial contract! + if (from === P_FRANCE && to === P_BAVARIA && game.turn < 3) + return + if (is_controlled_power(player, from) || is_controlled_power(player, to)) + gen_action_power(from) + }) + } + }, + power(from) { + game.proposal.from = from + game.state = "cancel_subsidy_to" + } +} + +states.cancel_subsidy_to = { + inactive: "cancel subsidy contract", + prompt() { + let player = game.power + let from = game.proposal.from + prompt(`Cancel subsidy contract between ${power_name[from]} and who?`) + map_for_each(game.contracts[from], (to, n) => { + // cannot cancel initial contract! + if (from === P_FRANCE && to === P_BAVARIA && game.turn < 3) + return + if (is_controlled_power(player, from) || is_controlled_power(player, to)) + gen_action_power(to) + }) + }, + power(to) { + let player = game.power + let from = game.proposal.from + clear_undo() + game.proposal.to = to + if (is_controlled_power(player, from)) + set_active_to_power(to) + else + set_active_to_power(from) + game.state = "cancel_subsidy_approve" + } +} + +states.cancel_subsidy_approve = { + inactive: "cancel subsidy contract", + prompt() { + let from = game.proposal.from + let to = game.proposal.to + prompt(`Cancel subsidy contract between ${power_name[from]} and ${power_name[to]}?`) + view.actions.accept = 1 + view.actions.refuse = 1 + }, + accept() { + let from = game.proposal.from + let to = game.proposal.to + log(`Canceled subsidy contract between ${power_name[from]} and ${power_name[to]}.`) + map_delete(game.contracts[from], to) + end_cancel_subsidy() + }, + refuse(n) { + let from = game.proposal.from + let to = game.proposal.to + log(power_name[game.power] + ` refused to cancel subsidy contract from ${power_name[from]}.`) + end_cancel_subsidy() + }, +} + +function end_cancel_subsidy() { + set_active_to_power(game.proposal.save_power) + game.state = game.proposal.save_state + delete game.proposal +} + /* SETUP */ const POWER_FROM_SETUP_STAGE = [ @@ -5334,11 +5530,39 @@ exports.view = function (state, player) { else view.actions.undo = 0 } + + // subsidy contracts actions for active player + if (!game.proposal) { + if (may_cancel_subsidy()) + view.actions.cancel_subsidy = 1 + view.actions.propose_subsidy = 1 + } + } return view } +exports.action = function (state, _player, action, arg) { + game = state + let S = states[game.state] + if (S && action in S) { + S[action](arg) + } else { + if (action === "undo" && game.undo && game.undo.length > 0) { + pop_undo() + } else if (action === "propose_subsidy") { + push_undo() + goto_propose_subsidy() + } else if (action === "cancel_subsidy") { + push_undo() + goto_cancel_subsidy() + } else + throw new Error("Invalid action: " + action) + } + return game +} + /* COMMON FRAMEWORK */ function goto_game_over(result, victory) { @@ -5355,20 +5579,6 @@ function prompt(str) { view.prompt = power_name[game.power] + ": " + str } -exports.action = function (state, _player, action, arg) { - game = state - let S = states[game.state] - if (S && action in S) { - S[action](arg) - } else { - if (action === "undo" && game.undo && game.undo.length > 0) - pop_undo() - else - throw new Error("Invalid action: " + action) - } - return game -} - function gen_action(action, argument) { if (view.actions[action] === undefined) view.actions[action] = [ argument ] @@ -5376,6 +5586,10 @@ function gen_action(action, argument) { set_add(view.actions[action], argument) } +function gen_action_power(p) { + gen_action("power", p) +} + function gen_action_piece(p) { gen_action("piece", p) } -- cgit v1.2.3