diff options
-rw-r--r-- | data.js | 2 | ||||
-rw-r--r-- | play.css | 19 | ||||
-rw-r--r-- | play.html | 1 | ||||
-rw-r--r-- | play.js | 95 | ||||
-rw-r--r-- | rules.js | 82 | ||||
-rw-r--r-- | tools/layout.svg | 18 | ||||
-rw-r--r-- | tools/parse-layout.js | 10 |
7 files changed, 172 insertions, 55 deletions
@@ -1494,7 +1494,7 @@ const data = { [ 158, 548 ], [ 562, 622, 637 ], ], - major_roads: [ + main_roads: [ [], [], [], @@ -120,8 +120,8 @@ body { min-height: 6px; } -#log .city_tip:hover { cursor: pointer; text-decoration: underline; } -#log .piece_tip:hover { cursor: pointer; text-decoration: underline; } +.city_tip .piece_tip, .path_tip { cursor: pointer; } +.city_tip:hover, .piece_tip:hover, .path_tip:hover { text-decoration: underline; } #log .h { background-color: tan; @@ -429,6 +429,21 @@ span.suit.reserve { font-weight: bold; font-family: "Source Serif SmText"; } .number.france { background-color: var(--color-france); } */ +/* ROADS */ + +svg .road { + stroke: white; + stroke-width: 6; + stroke-linecap: round; + stroke-dashpattern: 6 6; +} + +svg .main_road { + stroke: white; + stroke-width: 6; + stroke-linecap: round; +} + /* CARD COUNTING AIDS */ @media (hover: hover) { @@ -41,6 +41,7 @@ <div id="mapwrap"> <div id="map"> + <svg id="roads" width="2485" height="1654"></svg> <div id="spaces"></div> <div id="markers"></div> <div id="pieces"></div> @@ -368,6 +368,79 @@ const the_austrian_theater_text = `<p>Prussia receives 5 TC per round. Every sub <p>Prussia wins by controlling all of her blue objectives in Bohemia, or if the game ends before Austria or the Imperial Army have won. ` +/* SHOW PATHS */ + +const svgNS = "http://www.w3.org/2000/svg" + +var _show_path = [] + +function make_road(a, b, type) { + let e = document.createElementNS(svgNS, "line") + e.setAttribute("class", type) + let x1 = data.cities.x[a] + let y1 = data.cities.y[a] + let x2 = data.cities.x[b] + let y2 = data.cities.y[b] + + let v = Math.hypot(x2 - x1, y2 - y1) + let dx = (x2 - x1) / v + let dy = (y2 - y1) / v + let r = 0 + x1 += r * dx + y1 += r * dy + x2 -= r * dx + y2 -= r * dy + + e.setAttribute("x1", x1) + e.setAttribute("y1", y1) + e.setAttribute("x2", x2) + e.setAttribute("y2", y2) + e.setAttribute("visibility", "hidden") + document.getElementById("roads").appendChild(e) + + if (!ui.roads[a]) ui.roads[a] = {} + if (!ui.roads[b]) ui.roads[b] = {} + ui.roads[a][b] = e + ui.roads[b][a] = e +} + +function on_focus_path_tip(list) { + on_focus_city_tip(list[list.length-1]) + hide_move_path() + _show_path = list + show_move_path() + ui.status.textContent = list.map(s => data.cities.name[s]).join(" - ") +} + +function on_blur_path_tip() { + if (_show_path.length > 0) { + on_blur_city_tip(_show_path[_show_path.length-1]) + hide_move_path() + } + ui.status.textContent = "" +} + +function show_move_path() { + if (_show_path) { + for (let i = 1; i < _show_path.length; ++i) { + let x = _show_path[i-1] + let y = _show_path[i] + ui.roads[x][y].setAttribute("visibility", "visible") + } + } +} + +function hide_move_path() { + if (_show_path) { + for (let i = 1; i < _show_path.length; ++i) { + let x = _show_path[i-1] + let y = _show_path[i] + ui.roads[x][y].setAttribute("visibility", "hidden") + } + _show_path = null + } +} + /* PANEL ORDER */ const panel_order = [ P_PRUSSIA, P_HANOVER, P_RUSSIA, P_SWEDEN, P_AUSTRIA, P_IMPERIAL, P_FRANCE, P_FRANCE+1 ] @@ -481,6 +554,7 @@ const ui = { ], cities: [], action_register: [], + roads: [], } function register_action(target, action, id) { @@ -735,6 +809,15 @@ function on_init() { ui.spaces_element.appendChild(e) } + for (let a = 0; a <= last_city; ++a) { + for (let b of data.cities.main_roads[a]) + if (a < b) + make_road(a, b, "main_road") + for (let b of data.cities.roads[a]) + if (a < b) + make_road(a, b, "road") + } + sort_power_panel(false) update_favicon() @@ -1289,6 +1372,12 @@ function sub_space(_match, p1) { return `<span class="city_tip" onclick="on_click_city_tip(${x})" onmouseenter="on_focus_city_tip(${x})" onmouseleave="on_blur_city_tip(${x})">${n}</span>` } +function sub_path(list) { + let x = list[list.length-1] + let name = data.cities.name[x] + return `<span class="path_tip" onclick="on_click_city_tip(${x})" onmouseenter="on_focus_path_tip([${list.join(",")}])" onmouseleave="on_blur_path_tip()">to ${name}</span>` +} + const suit_icon = [ '<span class="suit spades">\u2660</span>', '<span class="suit clubs">\u2663</span>', @@ -1332,7 +1421,11 @@ function on_log(text) { text = text.replace(/P(\d+)/g, sub_piece) text = text.replace(/C(\d+)/g, sub_tc) - if (text.match(/^\$(\d+)/)) { + if (text.startsWith("%")) { + p.className = "i" + text = sub_path(text.substring(1).split(",")) + } + else if (text.match(/^\$(\d+)/)) { let fx = parseInt(text.substring(1)) if (fx < 48) text = `<div class="q">${fate_flavor_text[fx]}</div><div></div><div>${fate_effect_text[fx]}</div><div></div>` @@ -361,6 +361,23 @@ function log_selected() { log(game.selected.map(p => "P" + p).join(" and ")) } +function is_important_move(s) { + return set_has(game.move_conq, s) || set_has(game.move_reconq, s) || set_has(game.retro, s) +} + +function log_selected_move_path() { + if (0) { + log_selected() + log(">from S" + game.move_path[0]) + for (let i = 1; i < game.move_path.length; ++i) + if (is_important_move(game.move_path[i]) || i === game.move_path.length-1) + log(">to S" + game.move_path[i]) + } else { + log_selected() + log("%" + game.move_path.join(",")) + } +} + /* OBJECTIVES */ const all_objectives = [] @@ -1365,10 +1382,10 @@ states.movement = { else prompt("Move your generals and supply trains.") - if (done_trains && done_generals) - view.actions.end_movement = 1 - else + if (game.moved.length === 0) view.actions.confirm_end_movement = 1 + else + view.actions.end_movement = 1 }, piece(p) { push_undo() @@ -1386,11 +1403,12 @@ states.movement = { game.count = 0 - if (data.cities.major_roads[here].length > 0) - game.major = 1 + if (data.cities.main_roads[here].length > 0) + game.main = 1 else - game.major = 0 + game.main = 0 + game.move_path = [ here ] if (is_supply_train(p)) game.state = "move_supply_train" else @@ -1417,7 +1435,7 @@ states.movement = { function format_move(max) { let n = max - game.count - if (game.major) + if (game.main) return ` up to ${n} cities (${n+1} on main roads).` return ` up to ${n} cities.` } @@ -1496,7 +1514,7 @@ function can_move_general_to(to) { let from = game.pos[game.selected[0]] if (!can_continue_general_from(to)) return false - if (game.major && set_has(data.cities.major_roads[from], to)) + if (game.main && set_has(data.cities.main_roads[from], to)) return game.count < movement_range() return game.count < movement_range() - 1 } @@ -1538,7 +1556,7 @@ function move_general_to(to) { if (is_protected_from_conquest(from)) { set_add(game.retro, from) } else { - game.move_conq.push(from) + set_add(game.move_conq, from) set_add(game.conquest, from) } } @@ -1548,7 +1566,7 @@ function move_general_to(to) { if (is_protected_from_reconquest(from)) { set_add(game.retro, from) } else { - game.move_reconq.push(from) + set_add(game.move_reconq, from) set_delete(game.conquest, from) } } @@ -1603,8 +1621,8 @@ states.move_supply_train = { let who = game.selected[0] let here = game.pos[who] - if (game.count < 2 + game.major) - for (let next of data.cities.major_roads[here]) + if (game.count < 2 + game.main) + for (let next of data.cities.main_roads[here]) if (!has_any_piece(next)) gen_action_space(next) if (game.count < 2) @@ -1628,20 +1646,15 @@ states.move_supply_train = { let who = game.selected[0] let from = game.pos[who] - if (game.count === 0) { - log_selected() - log(">from S" + from) - } - - log(">to S" + to) + game.move_path.push(to) - if (!set_has(data.cities.major_roads[from], to)) - game.major = 0 + if (!set_has(data.cities.main_roads[from], to)) + game.main = 0 set_add(game.moved, who) game.pos[who] = to - if (++game.count === 2 + game.major) + if (++game.count === 2 + game.main) end_move_piece() }, } @@ -1708,8 +1721,8 @@ states.move_general = { } } - if (game.count < movement_range() + game.major) - for (let next of data.cities.major_roads[here]) + if (game.count < movement_range() + game.main) + for (let next of data.cities.main_roads[here]) if (can_move_general_to(next)) gen_action_space_or_piece(next) @@ -1749,17 +1762,12 @@ states.move_general = { let who = game.selected[0] let from = game.pos[who] - if (game.count === 0) { - log_selected() - log(">from S" + from) - } - - log(">to S" + to) + game.move_path.push(to) - if (!set_has(data.cities.major_roads[from], to)) - game.major = 0 + if (!set_has(data.cities.main_roads[from], to)) + game.main = 0 - if (move_general_to(to) || ++game.count === movement_range() + game.major) + if (move_general_to(to) || ++game.count === movement_range() + game.main) end_move_piece() }, } @@ -1807,6 +1815,9 @@ states.move_give = { } function end_move_piece() { + log_selected_move_path() + + delete game.move_path game.selected = null game.state = "movement" } @@ -2052,13 +2063,10 @@ function end_recruit() { if (game.recruit) { if (game.recruit.used.length > 0) { log_br() - log("Recruited") - log(">" + game.recruit.used.map(format_card).join(", ")) + log("Recruited " + game.recruit.troops + " troops with " + game.recruit.used.map(format_card).join(", ") + ".") map_for_each(game.recruit.pieces, (p,s) => { - log(">P" + p + " at S" + s) + log("Re-entered P" + p + " at S" + s + ".") }) - if (game.recruit.troops) - log(">" + game.recruit.troops + " troops") } // put back into hand unused cards diff --git a/tools/layout.svg b/tools/layout.svg index 3e7c511..c075e05 100644 --- a/tools/layout.svg +++ b/tools/layout.svg @@ -40,10 +40,10 @@ inkscape:window-height="480" id="namedview6" showgrid="false" - inkscape:zoom="1.3492621" + inkscape:zoom="1.1602604" inkscape:cx="417.73716" inkscape:cy="1363.1515" - inkscape:current-layer="g2856" + inkscape:current-layer="svg4" inkscape:document-rotation="0" showguides="false"> <inkscape:grid @@ -53,13 +53,13 @@ <image sodipodi:absref="/home/tor/src/rally/public/friedrich/Fried.Gameboard.075.png" xlink:href="../Fried.Gameboard.075.png" - style="display:inline" - id="image2" - sodipodi:insensitive="true" - height="1654" - width="2485" + x="0" y="0" - x="0" /> + width="2485" + height="1654" + sodipodi:insensitive="true" + id="image2" + style="display:inline" /> <g id="g3080" inkscape:label="city" @@ -11152,7 +11152,7 @@ </g> <g id="g2271" - inkscape:label="major_road" + inkscape:label="main_road" style="display:none;stroke:#808080" sodipodi:insensitive="true"> <path diff --git a/tools/parse-layout.js b/tools/parse-layout.js index 3e6cf0f..e327867 100644 --- a/tools/parse-layout.js +++ b/tools/parse-layout.js @@ -309,7 +309,7 @@ for (let key in points) { x: Math.round(x), y: Math.round(y), adjacent: [], - major_roads: [], + main_roads: [], roads: [], }) } else { @@ -345,11 +345,11 @@ cities.sort((a,b) => { return b.y - a.y }) -for (let e of edges.major_road) { +for (let e of edges.main_road) { let a = find_closest_city(e.x1, e.y1) let b = find_closest_city(e.x2, e.y2) - set_add(cities[a].major_roads, b) - set_add(cities[b].major_roads, a) + set_add(cities[a].main_roads, b) + set_add(cities[b].main_roads, a) set_add(cities[a].adjacent, b) set_add(cities[b].adjacent, a) } @@ -371,7 +371,7 @@ let arrays = { x: [], y: [], adjacent: [], - major_roads: [], + main_roads: [], roads: [], } |