From db19134bda62ae02105d105cb381f11371e05354 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Thu, 22 Dec 2022 17:27:19 +0100 Subject: Relief sally. --- play.html | 74 +++++++++------- play.js | 53 ++++++------ rules.js | 288 +++++++++++++++++++++++++++++++++++++++++++++++++------------- 3 files changed, 297 insertions(+), 118 deletions(-) diff --git a/play.html b/play.html index 6ce99c3..0a87045 100644 --- a/play.html +++ b/play.html @@ -146,31 +146,37 @@ body.Teutons #plan_actions .russian { display: none } #battle.defender { background-image: url(images/mat_battle_defender.png); } #battle.attacker { background-image: url(images/mat_battle_attacker.png); } +#battle.defender #array_garrison { top: 172px; left: 8px; } #battle.defender #array_attacker_reserves { top: 4px; left: 8px; } -#battle.defender #array_attacker_left { top: 96px; left: 32px; } -#battle.defender #array_attacker_center { top: 96px; left: 162px; } -#battle.defender #array_attacker_right { top: 96px; right: 32px; } -#battle.defender #array_defender_garrison { top: 172px; left: 8px; } -#battle.defender #array_defender_left { top: 220px; left: 32px; } -#battle.defender #array_defender_center { top: 220px; left: 162px; } -#battle.defender #array_defender_right { top: 220px; right: 32px; } -#battle.defender #array_sally_left { bottom: 60px; left: 4px; } -#battle.defender #array_sally_center { bottom: 60px; right: 200px; } -#battle.defender #array_sally_right { bottom: 60px; right: 4px; } #battle.defender #array_defender_reserves { bottom: 4px; left: 8px; } - +#battle.defender #array_attacker_x { top: 96px; left: 32px; } +#battle.defender #array_attacker_c { top: 96px; left: 162px; } +#battle.defender #array_attacker_y { top: 96px; right: 32px; } +#battle.defender #array_defender_x { top: 220px; left: 32px; } +#battle.defender #array_defender_c { top: 220px; left: 162px; } +#battle.defender #array_defender_y { top: 220px; right: 32px; } +#battle.defender #array_reserve_x { bottom: 60px; left: 4px; } +#battle.defender #array_reserve_c { bottom: 60px; right: 200px; } +#battle.defender #array_reserve_y { bottom: 60px; right: 4px; } +#battle.defender #array_sally_x { bottom: 48px; left: 64px; } +#battle.defender #array_sally_c { bottom: 48px; left: 200px; } +#battle.defender #array_sally_y { bottom: 48px; right: 64px; } + +#battle.attacker #array_garrison { bottom: 172px; left: 8px; } #battle.attacker #array_attacker_reserves { bottom: 4px; left: 8px; } -#battle.attacker #array_attacker_left { bottom: 96px; right: 32px; } -#battle.attacker #array_attacker_center { bottom: 96px; left: 162px; } -#battle.attacker #array_attacker_right { bottom: 96px; left: 32px; } -#battle.attacker #array_defender_garrison { bottom: 172px; left: 8px; } -#battle.attacker #array_defender_left { bottom: 220px; right: 32px; } -#battle.attacker #array_defender_center { bottom: 220px; left: 162px; } -#battle.attacker #array_defender_right { bottom: 220px; left: 32px; } -#battle.attacker #array_sally_left { top: 60px; right: 4px; } -#battle.attacker #array_sally_center { top: 60px; left: 200px; } -#battle.attacker #array_sally_right { top: 60px; left: 4px; } #battle.attacker #array_defender_reserves { top: 4px; left: 8px; } +#battle.attacker #array_attacker_x { bottom: 96px; right: 32px; } +#battle.attacker #array_attacker_c { bottom: 96px; left: 162px; } +#battle.attacker #array_attacker_y { bottom: 96px; left: 32px; } +#battle.attacker #array_defender_x { bottom: 220px; right: 32px; } +#battle.attacker #array_defender_c { bottom: 220px; left: 162px; } +#battle.attacker #array_defender_y { bottom: 220px; left: 32px; } +#battle.attacker #array_reserve_x { top: 60px; right: 4px; } +#battle.attacker #array_reserve_c { top: 60px; left: 200px; } +#battle.attacker #array_reserve_y { top: 60px; left: 4px; } +#battle.attacker #array_sally_x { top: 48px; right: 64px; } +#battle.attacker #array_sally_c { top: 48px; right: 200px; } +#battle.attacker #array_sally_y { top: 48px; left: 64px; } #battle > div { position: absolute; @@ -180,6 +186,11 @@ body.Teutons #plan_actions .russian { display: none } align-items: center; } +#battle.defender #array_attacker_reserves { justify-content: end; } +#battle.defender #array_defender_reserves { justify-content: start; } +#battle.attacker #array_attacker_reserves { justify-content: start; } +#battle.attacker #array_defender_reserves { justify-content: end; } + #battle .reserves { width: 356px; height: 48px; @@ -1231,15 +1242,18 @@ body.shift .mustered_vassals {
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/play.js b/play.js index ace0b53..c396cf8 100644 --- a/play.js +++ b/play.js @@ -4,6 +4,9 @@ // fealty rating and starting assets + forces on calendar // current assets and forces on map +// TODO: battle.where marker on map +// TODO: battle.conceded pursuit marker on battle mat + // inactive command cylinder color - other color outline // moved/fought lord coloring - blue outline? @@ -55,17 +58,6 @@ const SLED = 4 const BOAT = 5 const SHIP = 6 -// battle array -const ARRAY_ATK_C = 0 -const ARRAY_DEF_C = 1 -const ARRAY_SALLY_C = 2 -const ARRAY_ATK_L = 3 -const ARRAY_DEF_R = 4 -const ARRAY_SALLY_R = 5 -const ARRAY_ATK_R = 6 -const ARRAY_DEF_L = 7 -const ARRAY_SALLY_L = 8 - const VECHE = 100 const on_click_asset = [ @@ -479,18 +471,21 @@ const ui = { vp2: document.getElementById("vp2"), battle_attacker_reserves: document.getElementById("array_attacker_reserves"), battle_defender_reserves: document.getElementById("array_defender_reserves"), - battle_garrison: document.getElementById("array_defender_garrison"), + battle_garrison: document.getElementById("array_garrison"), battle: document.getElementById("battle"), battle_array: [ - document.getElementById("array_attacker_center"), - document.getElementById("array_defender_center"), - document.getElementById("array_sally_center"), - document.getElementById("array_attacker_left"), - document.getElementById("array_defender_right"), - document.getElementById("array_sally_right"), - document.getElementById("array_attacker_right"), - document.getElementById("array_defender_left"), - document.getElementById("array_sally_left"), + document.getElementById("array_attacker_x"), + document.getElementById("array_attacker_c"), + document.getElementById("array_attacker_y"), + document.getElementById("array_defender_x"), + document.getElementById("array_defender_c"), + document.getElementById("array_defender_y"), + document.getElementById("array_reserve_x"), + document.getElementById("array_reserve_c"), + document.getElementById("array_reserve_y"), + document.getElementById("array_sally_x"), + document.getElementById("array_sally_c"), + document.getElementById("array_sally_y"), ], } @@ -1277,8 +1272,16 @@ function update_cards() { function update_battle() { let array = view.battle.array + ui.battle_attacker_reserves.replaceChildren() ui.battle_defender_reserves.replaceChildren() + for (let lord of view.battle.reserves) { + if (is_attacking_lord(lord)) + ui.battle_attacker_reserves.appendChild(ui.battle_cylinder[lord]) + else + ui.battle_defender_reserves.appendChild(ui.battle_cylinder[lord]) + } + for (let i = 0; i < array.length; ++i) { let lord = array[i] ui.battle_array[i].replaceChildren() @@ -1286,12 +1289,7 @@ function update_battle() { ui.battle_array[i].appendChild(ui.battle_cylinder[lord]) ui.battle_array[i].classList.toggle("action", is_battle_array_action(i)) } - for (let lord of view.battle.reserves) { - if (is_attacking_lord(lord)) - ui.battle_attacker_reserves.appendChild(ui.battle_cylinder[lord]) - else - ui.battle_defender_reserves.appendChild(ui.battle_cylinder[lord]) - } + for (let lord = 0; lord < 12; ++lord) { ui.battle_cylinder[lord].classList.toggle("action", is_battle_lord_action(lord)) ui.battle_cylinder[lord].classList.toggle("selected", view.who === lord) @@ -1441,6 +1439,7 @@ function on_update() { action_button("end_plow_and_reap", "End plow and reap") action_button("end_ransom", "End ransom") action_button("end_remove", "End remove") + action_button("end_sally", "End sally") action_button("end_setup", "End setup") action_button("end_spoils", "End spoils") action_button("end_supply", "End supply") diff --git a/rules.js b/rules.js index ad21604..0230f8b 100644 --- a/rules.js +++ b/rules.js @@ -89,18 +89,53 @@ const SLED = 4 const BOAT = 5 const SHIP = 6 +const asset_type_name = [ "prov", "coin", "loot", "cart", "sled", "boat", "ship" ] + // battle array -const ARRAY_AC = 0 -const ARRAY_DC = 1 -const ARRAY_SC = 2 -const ARRAY_AL = 3 -const ARRAY_DR = 4 -const ARRAY_SR = 5 -const ARRAY_AR = 6 -const ARRAY_DL = 7 -const ARRAY_SL = 8 +const ARRAY_AX = 0 // attackers +const ARRAY_AC = 1 +const ARRAY_AY = 2 +const ARRAY_DX = 3 // defenders +const ARRAY_DC = 4 +const ARRAY_DY = 5 +const ARRAY_RX = 6 // reserves vs relief sallying +const ARRAY_RC = 7 +const ARRAY_RY = 8 +const ARRAY_SX = 9 // relief sallying +const ARRAY_SC = 10 +const ARRAY_SY = 11 + +const battle_array_name = [ + "Attacking Right", + "Attacking Center", + "Attacking Left", + "Defending Left", + "Defending Center", + "Defending Right", + "Reserves Left", + "Reserves Center", + "Reserves Right", + "Sallying Left", + "Sallying Center", + "Sallying Right", +] -const asset_type_name = [ "prov", "coin", "loot", "cart", "sled", "boat", "ship" ] +// battle steps +const STEP_DEF_ARCHERY = 0 +const STEP_ATK_ARCHERY = 1 +const STEP_DEF_HORSE = 2 +const STEP_ATK_HORSE = 3 +const STEP_DEF_FOOT = 4 +const STEP_ATK_FOOT = 5 + +const battle_step_name = [ + "Defending Archery", + "Attacking Archery", + "Defending Horse", + "Attacking Horse", + "Defending Foot", + "Attacking Foot", +] function find_card(name) { return data.cards.findIndex((x) => x.name === name) @@ -930,6 +965,13 @@ function has_friendly_lord(loc) { return false } +function has_besieged_friendly_lord(loc) { + for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) + if (get_lord_locale(lord) === loc && is_lord_besieged(lord)) + return true + return false +} + function has_enemy_lord(loc) { for (let lord = first_enemy_lord; lord <= last_enemy_lord; ++lord) if (get_lord_locale(lord) === loc) @@ -4726,33 +4768,26 @@ function start_battle() { attacker: game.active, conceded: 0, array: [ - game.command, - NOBODY, NOBODY, - NOBODY, NOBODY, NOBODY, - NOBODY, NOBODY, NOBODY, + -1, game.command, -1, + -1, -1, -1, + -1, -1, -1, + -1, -1, -1, ], garrison: 0, reserves: [], routed: [], + step: 0, + hits: 0, loser: 0, } - for (let lord = first_lord; lord <= last_lord; ++lord) - if (get_lord_locale(lord) === here && !is_lord_besieged(lord)) + for (let lord = first_lord; lord <= last_lord; ++lord) { + if (get_lord_locale(lord) === here && !is_lord_besieged(lord)) { + set_lord_moved(lord, 1) if (lord !== game.command) set_add(game.battle.reserves, lord) - - // battle array - // events - // relief sally - // rounds: - // concede? - // reposition - // for each strike - // roll walls - // assign - // roll by hit - // end battle + } + } goto_attacker_battle_array() } @@ -4766,6 +4801,24 @@ function has_reserves() { return false } +function count_reserves() { + let n = 0 + for (let lord of game.battle.reserves) + if (is_friendly_lord(lord)) + ++n + return n +} + +function pop_first_reserve() { + for (let lord of game.battle.reserves) { + if (is_friendly_lord(lord)) { + set_delete(game.battle.reserves, lord) + return lord + } + } + return NOBODY +} + function prompt_array_lord() { for (let lord of game.battle.reserves) if (is_friendly_lord(lord)) @@ -4802,14 +4855,14 @@ states.attacker_battle_array = { if (array[ARRAY_AC] === NOBODY) { gen_action_array(ARRAY_AC) } else { - if (array[ARRAY_AL] === NOBODY) - gen_action_array(ARRAY_AL) - if (array[ARRAY_AR] === NOBODY) - gen_action_array(ARRAY_AR) + if (array[ARRAY_AX] === NOBODY) + gen_action_array(ARRAY_AX) + if (array[ARRAY_AY] === NOBODY) + gen_action_array(ARRAY_AY) } } - if (!has_reserves() || (array[ARRAY_AC] >= 0 && array[ARRAY_AL] >= 0 && array[ARRAY_AR] >= 0)) + if (!has_reserves() || (array[ARRAY_AC] >= 0 && array[ARRAY_AX] >= 0 && array[ARRAY_AY] >= 0)) view.actions.end_array = 1 }, battle_lord: action_select_lord, @@ -4824,8 +4877,14 @@ function goto_defender_battle_array() { set_active_defender() game.state = "defender_battle_array" game.who = NOBODY - if (!has_reserves()) + let n = count_reserves() + if (n === 1) { + game.battle.array[ARRAY_DC] = pop_first_reserve() + goto_attacker_events() + } + if (n === 0) { goto_attacker_events() + } } states.defender_battle_array = { @@ -4839,19 +4898,19 @@ states.defender_battle_array = { if (game.who !== NOBODY) { if (array[ARRAY_DC] === NOBODY) { gen_action_array(ARRAY_DC) - } else if (array[ARRAY_AL] !== NOBODY && array[ARRAY_AR] === NOBODY && array[ARRAY_DL] === NOBODY) { - gen_action_array(ARRAY_DL) - } else if (array[ARRAY_AR] !== NOBODY && array[ARRAY_AL] === NOBODY && array[ARRAY_DR] === NOBODY) { - gen_action_array(ARRAY_DR) + } else if (array[ARRAY_AX] !== NOBODY && array[ARRAY_AY] === NOBODY && array[ARRAY_DX] === NOBODY) { + gen_action_array(ARRAY_DX) + } else if (array[ARRAY_AX] === NOBODY && array[ARRAY_AY] !== NOBODY && array[ARRAY_DY] === NOBODY) { + gen_action_array(ARRAY_DY) } else { - if (array[ARRAY_DL] !== NOBODY) - gen_action_array(ARRAY_DL) - if (array[ARRAY_DR] === NOBODY) - gen_action_array(ARRAY_DR) + if (array[ARRAY_DX] === NOBODY) + gen_action_array(ARRAY_DX) + if (array[ARRAY_DY] === NOBODY) + gen_action_array(ARRAY_DY) } } - if (!has_reserves() || (array[ARRAY_DC] >= 0 && array[ARRAY_DL] >= 0 && array[ARRAY_DR] >= 0)) + if (!has_reserves() || (array[ARRAY_DC] >= 0 && array[ARRAY_DX] >= 0 && array[ARRAY_DY] >= 0)) view.actions.end_array = 1 }, battle_lord: action_select_lord, @@ -4862,28 +4921,147 @@ states.defender_battle_array = { }, } +// === BATTLE: RELIEF SALLY === + +// NOTE: sallying attackers are flagged as besieged + +function is_lord_arrayed(lord) { + return game.battle.array.includes(lord) +} + +function goto_relief_sally() { + set_active_attacker() + if (has_besieged_friendly_lord(game.battle.where)) { + game.state = "attacker_relief_sally" + game.who = NOBODY + } else { + goto_battle_rounds() + } +} + +states.attacker_relief_sally = { + prompt() { + view.prompt = "Battle: Relief Sally" + + // NOTE: max 3 lords stronghold so there's always room for all to sally + + if (game.who === NOBODY) { + let here = game.battle.where + // RULES: can lower lords sally without lieutenant? + for (let lord = first_friendly_lord; lord <= last_friendly_lord; ++lord) { + console.log("relsal", lord, here, get_lord_locale(lord), is_lord_besieged(lord)) + if (get_lord_locale(lord) === here && is_lord_besieged(lord)) { + if (!is_lord_arrayed(lord)) + gen_action_lord(lord) + } + } + view.actions.end_sally = 1 + } else { + let array = game.battle.array + if (array[ARRAY_SC] === NOBODY) { + gen_action_array(ARRAY_SC) + } else if (array[ARRAY_DX] !== NOBODY && array[ARRAY_DY] === NOBODY && array[ARRAY_SX] === NOBODY) { + gen_action_array(ARRAY_SX) + } else if (array[ARRAY_DX] === NOBODY && array[ARRAY_DY] !== NOBODY && array[ARRAY_SY] === NOBODY) { + gen_action_array(ARRAY_SY) + } else { + if (array[ARRAY_SX] === NOBODY) + gen_action_array(ARRAY_SX) + if (array[ARRAY_SY] === NOBODY) + gen_action_array(ARRAY_SY) + } + } + }, + lord(lord) { + push_undo_without_who() + set_add(game.battle.reserves, lord) + game.who = lord + }, + array(pos) { + set_delete(game.battle.reserves, game.who) + set_lord_moved(game.who, pos) + game.battle.array[pos] = game.who + game.who = NOBODY + }, + end_sally() { + clear_undo() + game.who = NOBODY + goto_defender_relief_sally() + }, +} + +function goto_defender_relief_sally() { + set_active_defender() + let array = game.battle.array + if (has_reserves() && (array[ARRAY_SC] !== NOBODY || array[ARRAY_SX] !== NOBODY || array[ARRAY_SY] !== NOBODY)) + game.state = "defender_relief_sally" + else + goto_battle_rounds() +} + +states.defender_relief_sally = { + prompt() { + view.prompt = "Battle: Array reserves against sallying lords." + + // NOTE: max 3 in reserve (3 already deployed on front) so always room for all + + let done = true + for (let lord of game.battle.reserves) { + if (is_friendly_lord(lord) && !is_lord_arrayed(lord)) { + done = false + if (lord !== game.who) + gen_action_battle_lord(lord) + } + } + + if (game.who !== NOBODY) { + let array = game.battle.array + if (array[ARRAY_RC] === NOBODY) { + gen_action_array(ARRAY_RC) + } else if (array[ARRAY_SX] !== NOBODY && array[ARRAY_SY] === NOBODY && array[ARRAY_RX] === NOBODY) { + gen_action_array(ARRAY_RX) + } else if (array[ARRAY_SX] === NOBODY && array[ARRAY_SY] !== NOBODY && array[ARRAY_RY] === NOBODY) { + gen_action_array(ARRAY_RY) + } else { + if (array[ARRAY_RX] === NOBODY) + gen_action_array(ARRAY_RX) + if (array[ARRAY_RY] === NOBODY) + gen_action_array(ARRAY_RY) + } + } + + if (done) + view.actions.end_array = 1 + }, + battle_lord: action_select_lord, + array: action_array_lord, + end_array() { + clear_undo() + goto_battle_rounds() + }, +} + +// === BATTLE: EVENTS === + function goto_attacker_events() { + set_active_attacker() log("TODO attacker events") goto_defender_events() } function goto_defender_events() { + set_active_defender() log("TODO defender events") goto_relief_sally() } -function goto_relief_sally() { - log("TODO relief sally") - goto_battle_rounds() -} +// === BATTLE: CONCEDE THE FIELD === function goto_battle_rounds() { set_active_attacker() goto_concede() } -// === BATTLE: CONCEDE THE FIELD === - function goto_concede() { game.state = "concede" } @@ -5381,18 +5559,6 @@ states.battle_service = { function goto_battle_aftermath() { set_active(game.battle.attacker) - // Moved/Fought - TODO: mark at start of battle instead - for (let lord of game.battle.array) - if (lord !== NOBODY) - if (is_lord_on_map(lord)) - set_lord_moved(lord, 1) - for (let lord of game.battle.reserves) - if (is_lord_on_map(lord)) - set_lord_moved(lord, 1) - for (let lord of game.battle.routed) - if (is_lord_on_map(lord)) - set_lord_moved(lord, 1) - game.battle = 0 // Events -- cgit v1.2.3