summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTor Andersson <tor@ccxvii.net>2024-06-04 21:52:04 +0200
committerTor Andersson <tor@ccxvii.net>2024-06-04 22:16:11 +0200
commit329248efeb8681cac69c7db451c4601301d40049 (patch)
tree2baf5f2bdf46e21436677ed926643bd8c481b084
parent7680628caef500413c8041fce62cd6e7ddc42029 (diff)
downloadfriedrich-329248efeb8681cac69c7db451c4601301d40049.tar.gz
Brief movement line in log + movement path on mouse-over.
-rw-r--r--data.js2
-rw-r--r--play.css19
-rw-r--r--play.html1
-rw-r--r--play.js95
-rw-r--r--rules.js82
-rw-r--r--tools/layout.svg18
-rw-r--r--tools/parse-layout.js10
7 files changed, 172 insertions, 55 deletions
diff --git a/data.js b/data.js
index 828f758..4d9698d 100644
--- a/data.js
+++ b/data.js
@@ -1494,7 +1494,7 @@ const data = {
[ 158, 548 ],
[ 562, 622, 637 ],
],
- major_roads: [
+ main_roads: [
[],
[],
[],
diff --git a/play.css b/play.css
index 56b77ed..d5128be 100644
--- a/play.css
+++ b/play.css
@@ -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) {
diff --git a/play.html b/play.html
index 5bd90a8..66365b9 100644
--- a/play.html
+++ b/play.html
@@ -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>
diff --git a/play.js b/play.js
index 15264b3..22982d6 100644
--- a/play.js
+++ b/play.js
@@ -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>`
diff --git a/rules.js b/rules.js
index 1506571..299b353 100644
--- a/rules.js
+++ b/rules.js
@@ -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: [],
}