From da7fb6c52fcb520d3a1a270433e70b607a3fb800 Mon Sep 17 00:00:00 2001 From: Tor Andersson Date: Fri, 2 Sep 2022 18:29:50 +0200 Subject: Use svg lines to show supply lines. --- map.svg | 1 + play.html | 39 +++++++++++------------ play.js | 81 +++++++++++++++++++++++++++++++++------------- rules.js | 108 +++++++++++++++++++++++++++++++++++++++++++++----------------- 4 files changed, 157 insertions(+), 72 deletions(-) diff --git a/map.svg b/map.svg index 15b9d4e..30346a4 100644 --- a/map.svg +++ b/map.svg @@ -398,5 +398,6 @@ + diff --git a/play.html b/play.html index f439c31..a7775e8 100644 --- a/play.html +++ b/play.html @@ -45,7 +45,7 @@ header.your_turn { background-color: orange; } display: flex; flex-wrap: wrap; justify-content: center; - min-height: 350px; + min-height: 170px; max-width: 2672px; gap: 20px; } @@ -373,34 +373,31 @@ svg .hex.allied_supply { fill-opacity: 0.4; } -svg .side.axis_supply { - stroke: darkslategray !important; - stroke-width: 122px; - stroke-width: 121.5px; - stroke-width: 72px; - stroke-linecap: butt; - stroke-dasharray: 8 100; - stroke-dashoffset: -31; - stroke-opacity: 0.6; +svg .hex.axis_supply.allied_supply { + fill: darkgoldenrod; + fill-opacity: 0.4; +} + +svg #lines line { + pointer-events: none; + stroke: none; + stroke-width: 6px; + stroke-linecap: round; } -svg .side.allied_supply { - stroke: darkred !important; - stroke-width: 122px; - stroke-width: 121.5px; - stroke-width: 72px; - stroke-linecap: butt; - stroke-dasharray: 8 100; - stroke-dashoffset: -31; +svg #lines line.axis_supply { + stroke: darkslategray; stroke-opacity: 0.6; } -svg .hex.axis_supply.allied_supply { - fill: orange; +svg #lines line.allied_supply { + stroke: darkred; + stroke-opacity: 0.6; } -svg .side.allied_supply.axis_supply { +svg #lines line.axis_supply.allied_supply { stroke: darkorange; + stroke-opacity: 0.6; } /* UNITS */ diff --git a/play.js b/play.js index 9ab21b3..a200363 100644 --- a/play.js +++ b/play.js @@ -69,6 +69,7 @@ function set_has(set, item) { let ui = { hexes: [], sides: [], + lines: [], hex_x: [], hex_y: [], units: [], @@ -251,7 +252,7 @@ function focus_stack(stack, hex) { console.log("FOCUS STACK", stack, hex) ui.focus = stack update_map() - return stack.length <= 1 || hex === hexdeploy + view.month + return stack.length <= 1 || is_setup_hex(hex) } return true } @@ -309,6 +310,11 @@ document.getElementById("map").addEventListener("mousedown", function (evt) { } }) +function for_each_side_in_path(path, fn) { + for (let i = 1; i < path.length; ++i) + fn(to_side_id(path[i-1], path[i])) +} + function on_focus_hex(evt) { let h = evt.target.hex let text = "(" + h + ") " + hex_name[h] @@ -401,11 +407,13 @@ function show_supply(reply) { view.allied_supply_line = reply.allied_supply_line for (let x of all_hexes) { ui.hexes[x].classList.toggle("axis_supply", is_hex_axis_supply(x)) - for (let s = 0; s < 3; ++s) - ui.sides[x*3+s].classList.toggle("axis_supply", is_side_axis_supply_line(x*3+s)) ui.hexes[x].classList.toggle("allied_supply", is_hex_allied_supply(x)) - for (let s = 0; s < 3; ++s) - ui.sides[x*3+s].classList.toggle("allied_supply", is_side_allied_supply_line(x*3+s)) + for (let s = 0; s < 3; ++s) { + if (ui.lines[x*3+s]) { + ui.lines[x*3+s].classList.toggle("axis_supply", is_side_axis_supply_line(x*3+s)) + ui.lines[x*3+s].classList.toggle("allied_supply", is_side_allied_supply_line(x*3+s)) + } + } } } @@ -416,8 +424,10 @@ function hide_supply() { ui.hexes[x].classList.toggle("axis_supply", false) ui.hexes[x].classList.toggle("allied_supply", false) for (let s = 0; s < 3; ++s) { - ui.sides[x*3+s].classList.toggle("axis_supply", false) - ui.sides[x*3+s].classList.toggle("allied_supply", false) + if (ui.lines[x*3+s]) { + ui.lines[x*3+s].classList.toggle("axis_supply", false) + ui.lines[x*3+s].classList.toggle("allied_supply", false) + } } } } @@ -440,6 +450,21 @@ const hexdeploy = map_w * map_h let hexnext = [ 1, map_w, map_w-1, -1, -map_w, -(map_w-1) ] +function to_side_id(a, b) { + if (a > b) { + let c = b + b = a + a = c + } + if (a + hexnext[0] === b) + return a * 3 + 0 + else if (a + hexnext[1] === b) + return a * 3 + 1 + else if (a + hexnext[2] === b) + return a * 3 + 2 + throw new Error("not a hexside " + a + " to " + b) +} + function build_hexes() { let yoff = 4 let xoff = 62 @@ -482,6 +507,16 @@ function build_hexes() { side.side = side_id } + function add_path(x1, y1, x2, y2, side_id) { + let line = ui.lines[side_id] = document.createElementNS(svgNS, "line") + line.setAttribute("class", "path") + line.setAttribute("x1", x1) + line.setAttribute("y1", y1) + line.setAttribute("x2", x2) + line.setAttribute("y2", y2) + document.getElementById("mapsvg").getElementById("lines").appendChild(line) + } + function add_hex(x, y) { let sm_hex_w = hex_w - 8 let sm_hex_h = sm_hex_w / sqrt(3) * 2 @@ -525,18 +560,18 @@ function build_hexes() { hex.classList.add("refit") document.getElementById("mapsvg").getElementById("hexes").appendChild(hex) } + } + } - // Add hex sides - // if (hex_exists[hex_id]) - { - for (let s = 0; s < 3; ++s) { - let next_id = hex_id + hexnext[s] - // if (hex_exists[next_id]) - { - let side_id = hex_id * 3 + s - add_line(hex_x, hex_y, s, side_id) - } - } + for (let hex_id = 0; hex_id < map_w * map_h; ++hex_id) { + // Add hex sides + { + for (let s = 0; s < 3; ++s) { + let next_id = hex_id + hexnext[s] + let side_id = hex_id * 3 + s + add_line(ui.hex_x[hex_id], ui.hex_y[hex_id], s, side_id) + if (hex_exists[hex_id] && hex_exists[next_id]) + add_path(ui.hex_x[hex_id], ui.hex_y[hex_id], ui.hex_x[next_id], ui.hex_y[next_id], side_id) } } } @@ -621,11 +656,13 @@ function update_map() { let hex = unit_hex(u) if (hex >= hexdeploy + view.month + 10) hex = 0 - if (hex === hexdeploy + view.month) { + if (is_setup_hex(hex)) { if (player === "Axis" && !is_axis_unit(u)) hex = 0 if (player === "Allied" && !is_allied_unit(u)) hex = 0 + if (player !== "Axis" && player !== "Allied") + hex = 0 } if (view.month <= 10 && hex === MALTA) hex = 0 @@ -671,7 +708,7 @@ function update_map() { let start_y = ui.hex_y[hex] let wrap = 6 - if (hex === hexdeploy + view.month) { + if (is_setup_hex(hex)) { start_x = 1095 start_y = 25 + 8 } @@ -687,7 +724,7 @@ function update_map() { let e = ui.units[u] let x, y, z - if (hex === hexdeploy + view.month) { + if (is_setup_hex(hex)) { if (view.month == 8) wrap = 14 else @@ -761,7 +798,7 @@ function update_cards() { ui.cards[i+28].classList.toggle("hide", i >= view.cards[1]) } else { for (let i = 0; i < 42; ++i) - ui.cards[i+28].classList.add("hide") + ui.cards[i].classList.add("hide") } } diff --git a/rules.js b/rules.js index b650c97..314d7dc 100644 --- a/rules.js +++ b/rules.js @@ -1610,6 +1610,16 @@ function print_path(who, from, to, road) { log(">" + p.join(" - ") + ".") } +function show_path(from, to, p, speed) { + let road = can_move_road(to, speed) + p.push(to) + while (to && to !== from) { + to = path_from[road][to] + p.push(to) + } + return p +} + // normal move: may not leave battle hex. may engage any enemy. may move freely. // normal withdrawal: may not leave battle hex. may engage disrupted enemy. must follow supply lines. // retreat move: must leave battle hex via friendly side. may ignore disrupted enemy. may move freely. @@ -1834,6 +1844,18 @@ function search_path_redeploy_bfs(cost, start, road) { } } +function can_move_road(to, speed) { + if (path_cost[4][to] <= speed + 4) + return 4 + if (path_cost[2][to] <= speed + 2) + return 2 + if (path_cost[1][to] <= speed + 1) + return 1 + if (path_cost[0][to] <= speed) + return 0 + return -1 +} + function can_move_to(to, speed) { if (path_cost[4][to] <= speed + 4) return true @@ -3370,47 +3392,44 @@ function goto_overrun(where) { goto_rout(where, true, null) } +function do_gen_move_to(from, to, speed) { + if (can_move_to(to, speed)) { + gen_action_hex(to) + // view.path[to] = show_path(from, to, [], speed) + } else if (can_move_to(to, speed + 1)) { + gen_action_forced_march(to) + // view.path[to] = show_path(from, to, [], speed + 1) + } +} + function gen_move() { let rommel1 = (game.rommel === 1) ? 1 : 0 let rommel2 = (game.rommel === 2) ? 1 : 0 let speed = unit_speed[game.selected] let from = unit_hex(game.selected) + // view.path = {} + if (!game.to1 && game.from1 === from) { for (let to of all_hexes) { - if (to != from) { - if (can_move_to(to, speed + rommel1)) - gen_action_hex(to) - else if (can_move_to(to, speed + 1 + rommel1)) - gen_action_forced_march(to) - } + if (to != from) + do_gen_move_to(from, to, speed + rommel1) } } if (!game.to2 && game.from2 === from) { for (let to of all_hexes) { - if (to != from) { - if (can_move_to(to, speed + rommel2)) - gen_action_hex(to) - else if (can_move_to(to, speed + 1 + rommel2)) - gen_action_forced_march(to) - - } + if (to != from) + do_gen_move_to(from, to, speed + rommel2) } } if (game.to1 && is_hex_or_adjacent_to(from, game.from1)) { - if (can_move_to(game.to1, speed + rommel1)) - gen_action_hex(game.to1) - else if (can_move_to(game.to1, speed + 1 + rommel1)) - gen_action_forced_march(game.to1) + do_gen_move_to(from, game.to1, speed + rommel1) } if (game.to2 && is_hex_or_adjacent_to(from, game.from2)) { - if (can_move_to(game.to2, speed + rommel2)) - gen_action_hex(game.to2) - else if (can_move_to(game.to2, speed + 1 + rommel2)) - gen_action_forced_march(game.to2) + do_gen_move_to(from, game.to2, speed + rommel2) } } @@ -3419,24 +3438,32 @@ function gen_withdraw() { let speed = unit_speed[game.selected] let from = unit_hex(game.selected) + // view.path = {} + // Group Move Withdraw if (!game.to1) { for (let to of all_hexes) { if (to != from) { - if (can_move_to(to, speed + rommel1) && set_has(game.withdraw, to)) + if (can_move_to(to, speed + rommel1) && set_has(game.withdraw, to)) { gen_action_hex(to) - else if (can_move_to(to, speed + 1 + rommel1) && set_has(game.withdraw, to)) + // view.path[to] = show_path(from, to, [], speed + rommel1) + } else if (can_move_to(to, speed + 1 + rommel1) && set_has(game.withdraw, to)) { gen_action_forced_march(to) + // view.path[to] = show_path(from, to, [], speed + 1 + rommel1) + } } } } // Regroup Move Withdraw if (game.to1) { - if (can_move_to(game.to1, speed + rommel1)) + if (can_move_to(game.to1, speed + rommel1)) { gen_action_hex(game.to1) - else if (can_move_to(game.to1, speed + 1 + rommel1)) + // view.path[game.to1] = show_path(from, game.to1, [], speed + rommel1) + } else if (can_move_to(game.to1, speed + 1 + rommel1)) { gen_action_forced_march(game.to1) + // view.path[game.to1] = show_path(from, game.to1, [], speed + 1 + rommel1) + } } } @@ -3588,8 +3615,18 @@ states.forced_march_via = { prompt() { view.prompt = `Move: Select which path to take.` view.selected = game.hexside.who - for (let x of game.hexside.via) + + // view.path = {} + + let rommel = (game.hexside.move === game.rommel) ? 1 : 0 + let from = unit_hex(game.hexside.who) + let speed = unit_speed[game.hexside.who] + search_move(from, speed + 1 + rommel) + + for (let x of game.hexside.via) { gen_action_hex(x) + // view.path[x] = show_path(from, x, [ game.hexside.to ], speed + rommel) + } }, hex(via) { let rommel = (game.hexside.move === game.rommel) ? 1 : 0 @@ -3609,11 +3646,23 @@ states.engage_via = { prompt() { view.prompt = `Move: Select which hex side to cross.` view.selected = game.hexside.who - for (let i = 0; i < game.hexside.via.length; ++i) + + // view.path = {} + + let rommel = (game.hexside.move === game.rommel) ? 1 : 0 + let who = game.hexside.who + let from = unit_hex(who) + let speed = unit_speed[who] + search_move(from, speed + rommel) + + for (let i = 0; i < game.hexside.via.length; ++i) { + let x = game.hexside.via[i] if (game.hexside.forced[i]) - gen_action_forced_march(game.hexside.via[i]) + gen_action_forced_march(x) else - gen_action_hex(game.hexside.via[i]) + gen_action_hex(x) + // view.path[x] = show_path(from, x, [ game.hexside.to ], speed + rommel) + } }, forced_march(via) { let rommel = (game.hexside.move === game.rommel) ? 1 : 0 @@ -6069,6 +6118,7 @@ states.free_deployment = { let axis = (game.active === AXIS) view.prompt = `Setup: ${game.active} Deployment.` + view.deploy = 1 // view.prompt = `Setup: Deploy units in a supplied location in the setup area.` let done = true -- cgit v1.2.3