summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--play.html74
-rw-r--r--play.js53
-rw-r--r--rules.js288
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 {
<div class="garrison" id="array_defender_garrison"></div>
<div class="reserves" id="array_attacker_reserves"></div>
<div class="reserves" id="array_defender_reserves"></div>
-<div class="array" id="array_attacker_right"></div>
-<div class="array" id="array_attacker_center"></div>
-<div class="array" id="array_attacker_left"></div>
-<div class="array" id="array_defender_left"></div>
-<div class="array" id="array_defender_center"></div>
-<div class="array" id="array_defender_right"></div>
-<div class="array" id="array_sally_left"></div>
-<div class="array" id="array_sally_center"></div>
-<div class="array" id="array_sally_right"></div>
+<div class="array" id="array_attacker_x"></div>
+<div class="array" id="array_attacker_c"></div>
+<div class="array" id="array_attacker_y"></div>
+<div class="array" id="array_defender_x"></div>
+<div class="array" id="array_defender_c"></div>
+<div class="array" id="array_defender_y"></div>
+<div class="array" id="array_reserve_x"></div>
+<div class="array" id="array_reserve_c"></div>
+<div class="array" id="array_reserve_y"></div>
+<div class="array" id="array_sally_x"></div>
+<div class="array" id="array_sally_c"></div>
+<div class="array" id="array_sally_y"></div>
</div>
</div>
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