summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTor Andersson <tor@ccxvii.net>2023-08-13 19:43:43 +0200
committerTor Andersson <tor@ccxvii.net>2023-10-01 16:11:22 +0200
commit4afafd6752f3034da84f9ae2bd82e5f1763793ea (patch)
tree2c0ada939626ab9222d3c59a6d31b184372cbede
parent74d2e6efd24db8570cd64f74c7de109c5b094c70 (diff)
downloadwaterloo-campaign-1815-4afafd6752f3034da84f9ae2bd82e5f1763793ea.tar.gz
Reinforcements!
-rw-r--r--data.js63
-rw-r--r--play.html2
-rw-r--r--play.js101
-rw-r--r--rules.js68
4 files changed, 193 insertions, 41 deletions
diff --git a/data.js b/data.js
index 4ee2e26..5f7e8f8 100644
--- a/data.js
+++ b/data.js
@@ -22,6 +22,13 @@ data.map = {
towns: [1015,1018,1021,1024,1026,1100,1117,1118,1129,1201,1204,1209,1211,1215,1217,1221,1239,1340,1401,1407,1423,1433,1516,1526,1528,1534,1601,1603,1605,1623,1631,1716,1728,1737,1800,1810,1821,1825,1830,1903,1911,1915,1916,1919,1922,1928,1932,2001,2027,2035,2119,2122,2123,2219,2222,2223,2230,2308,2315,2317,2324,2327,2333,2337,2404,2500,2521,2529,2537,2604,2609,2618,2623,2715,2721,2723,2725,2730,2733,2736,2739,2827,2829,2840,2911,2936,3002,3013,3018,3020,3031,3125,3129,3135,3138,3204,3206,3226,3231,3233,3234,3240,3313,3327,3328,3402,3408,3417,3418,3438,3441,3512,3514,3523,3528,3614,3616,3617,3631,3636,3705,3708,3715,3718,3719,3723,3803,3828,3832,3915,3919,3925,3933,4006,4038],
streams: [1021,1024,1120,1124,1224,1300,1314,1324,1401,1415,1501,1502,1514,1600,1601,1603,1604,1704,1837,1937,2038,2138,2407,2507,2524,2540,2604,2608,2609,2620,2621,2624,2625,2637,2641,2704,2708,2718,2719,2721,2724,2725,2737,2740,2741,2805,2808,2817,2820,2821,2825,2838,2840,2905,2906,2907,2915,2916,2917,2920,2921,2925,2938,2940,3006,3017,3018,3019,3020,3021,3022,3025,3039,3040,3041,3106,3117,3118,3121,3122,3125,3141,3205,3207,3219,3220,3221,3222,3223,3225,3226,3305,3306,3320,3323,3324,3325,3406,3423,3502,3503,3506,3517,3518,3520,3521,3523,3534,3535,3536,3604,3605,3606,3607,3619,3622,3623,3624,3635,3637,3703,3704,3705,3706,3707,3708,3709,3710,3711,3712,3713,3714,3719,3723,3735,3736,3739,3802,3803,3806,3813,3820,3824,3825,3836,3837,3840,3906,3920,3925,3937,3938,3939,4007,4021,4026,4027,4038,4039],
brussels_couillet_road: [1018,1117,1217,1218,1317,1417,1516,1617,1716,1817,1917,2018,2117,2218,2317,2418,2517,2618,2717,2818,2917,3018,3116,3117,3216,3316,3416,3515,3616,3616,3715,3815,3915,4015],
+ forbidden: {
+ 1015: [ 1014, 1015, 1016, 1114, 1115 ],
+ 1018: [ 1017, 1018, 1019, 1117 ],
+ 1020: [ 1019, 1020, 1021, 1120 ],
+ 3000: [ 2900, 3000, 3001, 3100 ],
+ 4015: [ 4014, 4015, 4016, 3914, 3915 ],
+ },
names: {
1015: "Mortigny",
1018: "Couillet",
@@ -213,5 +220,61 @@ data.pieces = [
{ side: "Prussian", type: "det", stars: 0, parent: [21], name: "IV Detachment (Schwerin)" },
]
+data.reinforcements = [
+ {
+ turn: 1,
+ side: "French",
+ hex: 1015,
+ list: [
+ "II Corps (Reille)",
+ "I Corps (d'Erlon)",
+ ]
+ },
+ {
+ turn: 1,
+ side: "French",
+ hex: 1018,
+ list: [
+ "III Corps (Vandamme)",
+ "VI Corps (Lobau)",
+ "Guard Corps (Drouot)",
+ "Guard Cav Corps (Guyot)",
+ ]
+ },
+ {
+ turn: 1,
+ side: "French",
+ hex: 1020,
+ list: [
+ "Res Cav Corps (Grouchy)",
+ "IV Corps (Gerard)",
+ ]
+ },
+ {
+ turn: 3,
+ side: "Coalition",
+ hex: 3000,
+ list: [
+ "II Corps (Hill*)",
+ ],
+ },
+ {
+ turn: 3,
+ side: "Coalition",
+ hex: 3241,
+ list: [
+ "IV Corps (Bulow)",
+ ]
+ },
+ {
+ turn: 4,
+ side: "Coalition",
+ hex: 4015,
+ list: [
+ "Cav Corps (Uxbridge)",
+ ]
+ }
+]
+
if (typeof module !== "undefined")
module.exports = data
diff --git a/play.html b/play.html
index dd11cb7..e5f6228 100644
--- a/play.html
+++ b/play.html
@@ -135,12 +135,10 @@ main {
.large.selected, .small.selected {
box-shadow: 0 0 0 1px #444, 0 0 0 4px yellow;
- z-index: 102;
}
.large.target, .small.target {
box-shadow: 0 0 0 1px #444, 0 0 0 4px red;
- z-index: 101;
}
.marker { border-color: hsl(199,65%,85%) hsl(199,55%,50%) hsl(199,55%,50%) hsl(199,65%,85%) }
diff --git a/play.js b/play.js
index 8c85772..b022d8a 100644
--- a/play.js
+++ b/play.js
@@ -6,6 +6,22 @@ const piece_count = 39
const first_hex = 1000
const last_hex = 4041
+function find_piece(name) {
+ let id = data.pieces.findIndex(pc => pc.name === name)
+ if (id < 0)
+ throw new Error("PIECE NOT FOUND: " + name)
+ return id
+}
+
+for (let info of data.reinforcements)
+ info.list = info.list.map(name => find_piece(name))
+
+let yoff = 1555
+let xoff = 36
+let hex_dx = 58.67
+let hex_dy = 68
+let hex_r = 56 >> 1
+
function set_has(set, item) {
if (!set)
return false
@@ -31,6 +47,14 @@ const TURN_X = 20 - 70 + 35 + 8
const TURN_Y = 1745
const TURN_DX = 70
+const REINF_OFFSET = {
+ 1015: [ hex_dx/2, hex_dy * 3/4 ],
+ 1018: [ -hex_dx/2, hex_dy * 3/4 ],
+ 1020: [ -hex_dx/2, hex_dy * 3/4 ],
+ 3000: [ -hex_dx/2, 0 ],
+ 4015: [ 0, -hex_dy * 3/8 ],
+}
+
let ui = {
hexes: new Array(last_hex+1).fill(null),
sides: new Array((last_hex+1)*3).fill(null),
@@ -111,17 +135,11 @@ function toggle_pieces() {
}
function build_hexes() {
- let yoff = 1555
- let xoff = 36
- let hex_dx = 58.67
- let hex_dy = 68
- let hex_r = 56 >> 1
-
- for (let y = 0; y < data.map.rows; ++y) {
- for (let x = 0; x < data.map.cols; ++x) {
- let hex_id = first_hex + 100 * y + x
- let hex_x = ui.hex_x[hex_id] = Math.floor(xoff + hex_dx * (x + (y & 1) * 0.5 + 0.5))
- let hex_y = ui.hex_y[hex_id] = Math.floor(yoff - hex_dy * 3 / 4 * y + hex_dy/2)
+ for (let row = 0; row < data.map.rows; ++row) {
+ for (let col = 0; col < data.map.cols; ++col) {
+ let hex_id = first_hex + 100 * row + col
+ let hex_x = ui.hex_x[hex_id] = Math.floor(xoff + hex_dx * (col + (row & 1) * 0.5 + 0.5))
+ let hex_y = ui.hex_y[hex_id] = Math.floor(yoff - hex_dy * 3 / 4 * row + hex_dy/2)
let hex = ui.hexes[hex_id] = document.createElement("div")
hex.className = "hex"
@@ -189,6 +207,27 @@ function find_hex_side(a, b) {
return -1
}
+function find_reinforcement_hex(who) {
+ for (let info of data.reinforcements)
+ for (let p of info.list)
+ if (p === who)
+ return info.hex
+ return 102
+}
+
+function find_reinforcement_z(who) {
+ for (let info of data.reinforcements) {
+ let n = 0
+ for (let p of info.list) {
+ if (p === who)
+ return n
+ if ((view.pieces[p] >> 1) === 102)
+ ++n
+ }
+ }
+ return 0
+}
+
function on_update() {
ui.stack.fill(0)
@@ -202,13 +241,28 @@ function on_update() {
for (let id = 0; id < piece_count; ++id) {
let hex = view.pieces[id] >> 1
- if (hex >= first_hex) {
+ let z = 0
+ let s = 0
+ if (hex >= first_hex || hex === 102) {
// ON MAP
ui.pieces[id].classList.remove("hide")
ui.pieces[id].classList.toggle("flip", (view.pieces[id] & 1) === 1)
- let x = ui.hex_x[hex] - ui.stack[hex] * 18
- let y = ui.hex_y[hex] + ui.stack[hex] * 12
- ui.stack[hex] += 1
+ let x, y
+ if (hex === 102) {
+ hex = find_reinforcement_hex(id)
+ s = find_reinforcement_z(id)
+ z = 4 - s
+ x = ui.hex_x[hex] + s * 24
+ y = ui.hex_y[hex] + s * 18
+ if (REINF_OFFSET[hex]) {
+ x += REINF_OFFSET[hex][0]
+ y += REINF_OFFSET[hex][1]
+ }
+ } else {
+ s = z = ui.stack[hex]++
+ x = ui.hex_x[hex] - s * 18
+ y = ui.hex_y[hex] + s * 12
+ }
if (id <= last_corps) {
x -= (46>>1)
y -= (46>>1)
@@ -218,15 +272,17 @@ function on_update() {
}
ui.pieces[id].style.top = y + "px"
ui.pieces[id].style.left = x + "px"
- } else if (hex === 100 || hex === 101) {
- // AVAILABLE DETACHMENTS
+ ui.pieces[id].style.zIndex = z
+ } else if (hex >= 100) {
+ // OFF MAP DETACHMENTS / LEADERS / REINFORCEMENTS
ui.pieces[id].classList.remove("hide")
ui.pieces[id].classList.toggle("flip", (view.pieces[id] & 1) === 1)
- let x = 600 + 20 + ui.stack[hex] * 60
- let y = 1650 + 20 + 60 * (hex-100)
+ let x = 600 + 40 + ui.stack[hex] * 60
+ let y = 1650 + 40 + 60 * (hex-100)
ui.stack[hex] += 1
ui.pieces[id].style.top = y + "px"
ui.pieces[id].style.left = x + "px"
+ ui.pieces[id].style.zIndex = 0
} else if (hex >= 1) {
// ON TURN TRACK
ui.pieces[id].classList.remove("hide")
@@ -248,6 +304,12 @@ function on_update() {
// ELIMINATED
ui.pieces[id].classList.add("hide")
}
+ //if (is_action("piece", id)) z = 101
+ if (view.target === id)
+ z = 102
+ if (view.who === id)
+ z = 103
+ ui.pieces[id].style.zIndex = z
ui.pieces[id].classList.toggle("action", is_action("piece", id))
ui.pieces[id].classList.toggle("selected", view.who === id)
ui.pieces[id].classList.toggle("target", view.target === id)
@@ -263,6 +325,7 @@ function on_update() {
}
}
+ action_button("roll", "Roll")
action_button("next", "Next")
action_button("done", "Done")
action_button("pass", "Pass")
diff --git a/rules.js b/rules.js
index 3f2f371..033a124 100644
--- a/rules.js
+++ b/rules.js
@@ -23,23 +23,23 @@ var states = {}
const ELIMINATED = 0
const AVAILABLE_P1 = 100
const AVAILABLE_P2 = 101
+const REINFORCEMENTS = 102
-const ENTRY_A = 4006
-const ENTRY_B = 4015
-const ENTRY_C = 4025
-const ENTRY_D1 = 1017
-const ENTRY_D2 = 1018
+function find_piece(name) {
+ let id = data.pieces.findIndex(pc => pc.name === name)
+ if (id < 0)
+ throw new Error("PIECE NOT FOUND: " + name)
+ return id
+}
-const OFFMAP_A = 4106
-const OFFMAP_B = 4115
-const OFFMAP_C = 4125
-const OFFMAP_D = 917
+for (let info of data.reinforcements)
+ info.list = info.list.map(name => find_piece(name))
-const NAPOLEON_HQ = data.pieces.findIndex(pc => pc.name === "Napoleon HQ")
-const OLD_GUARD = data.pieces.findIndex(pc => pc.name === "Old Guard")
-const GRAND_BATTERY = data.pieces.findIndex(pc => pc.name === "Grand Battery")
-const HILL_1 = data.pieces.findIndex(pc => pc.name === "II Corps (Hill*)")
-const HILL_2 = data.pieces.findIndex(pc => pc.name === "II Corps (Hill**)")
+const NAPOLEON_HQ = find_piece("Napoleon HQ")
+const OLD_GUARD = find_piece("Old Guard")
+const GRAND_BATTERY = find_piece("Grand Battery")
+const HILL_1 = find_piece("II Corps (Hill*)")
+const HILL_2 = find_piece("II Corps (Hill**)")
const brussels_couillet_road_x3 = []
for (let a of data.map.brussels_couillet_road) {
@@ -936,6 +936,11 @@ function goto_movement_phase() {
game.active = P1
game.state = "movement"
game.remain = 0
+
+ for (let info of data.reinforcements)
+ if (info.turn === game.turn)
+ for (let p of info.list)
+ set_piece_hex(p, REINFORCEMENTS)
}
function next_movement() {
@@ -964,6 +969,17 @@ states.movement = {
if (piece_is_not_in_enemy_zoc(p))
gen_action_piece(p)
+ for (let info of data.reinforcements) {
+ if (info.turn === game.turn && info.side === game.active) {
+ for (let p of info.list) {
+ if (!piece_is_on_map(p)) {
+ gen_action_piece(p)
+ break
+ }
+ }
+ }
+ }
+
view.actions.pass = 1
},
piece(p) {
@@ -1094,17 +1110,31 @@ function must_flip_zoc(here, next, is_cav) {
const move_seen = new Array(last_hex - 999).fill(0)
const move_cost = new Array(last_hex - 999).fill(0)
+function find_reinforcement_hex(who) {
+ for (let info of data.reinforcements)
+ for (let p of info.list)
+ if (p === who)
+ return info.hex
+ return 0
+}
+
function search_move(p) {
move_seen.fill(0)
let x = piece_hex(p)
let m = piece_movement_allowance(p)
+ let u = 0
+ if (x === REINFORCEMENTS) {
+ x = find_reinforcement_hex(p)
+ u = 1
+ move_seen[x-1000] = 3
+ }
for (let hq of data.pieces[p].hq) {
let hq_hex = piece_hex(hq)
if (is_map_hex(hq_hex)) {
- search_move_offroad(x, m, hq_hex, piece_command_range(hq), piece_is_cavalry(p))
+ search_move_offroad(x, m - u, hq_hex, piece_command_range(hq), piece_is_cavalry(p))
if (!(piece_is_infantry(game.who) && piece_mode(game.who)))
if (is_road_hex(x))
- search_move_road(x, m * 2, hq_hex, piece_command_range(hq), piece_is_cavalry(p))
+ search_move_road(x, m * 2 - u, hq_hex, piece_command_range(hq), piece_is_cavalry(p))
}
}
}
@@ -1189,7 +1219,7 @@ function search_move_road_segment(queue, road, cur, dir, hq_hex, hq_range, is_ca
let here = road[cur]
let mp = move_cost[here-1000]
cur += dir
- while (mp > 0 && cur >= 0 && cur < road.length) {
+ while (mp >= 0 && cur >= 0 && cur < road.length) {
let next = road[cur]
if (!can_move_into(here, next, hq_hex, hq_range, is_cav))
break
@@ -1330,9 +1360,7 @@ function roll_attack() {
// === SETUP ===
function setup_piece(side, name, hex, mode = 0) {
- let id = data.pieces.findIndex(pc => pc.side === side && pc.name === name)
- if (id < 0)
- throw new Error("INVALID PIECE NAME: " + name)
+ let id = find_piece(name)
set_piece_hex(id, hex)
set_piece_mode(id, mode)
}