diff options
-rw-r--r-- | data.js | 52 | ||||
-rw-r--r-- | play.css | 20 | ||||
-rw-r--r-- | play.html | 1 | ||||
-rw-r--r-- | play.js | 58 | ||||
-rw-r--r-- | rules.js | 160 | ||||
-rw-r--r-- | tools/borders.svg | 359 | ||||
-rw-r--r-- | tools/genborders.js | 53 | ||||
-rw-r--r-- | tools/makeborders.js | 32 |
8 files changed, 696 insertions, 39 deletions
@@ -90,6 +90,58 @@ let AREAS = [] let BORDERS = [] +const BORDERS_XY = { + "England / Dunbar": {"x":1285,"y":1320}, + "England / Annan": {"x":1065,"y":1630}, + "England / Teviot": {"x":1210,"y":1495}, + "Ross / Garmoran": {"x":505,"y":450}, + "Ross / Moray": {"x":665,"y":455}, + "Garmoran / Moray": {"x":550,"y":590}, + "Garmoran / Lochaber": {"x":445,"y":670}, + "Moray / Strathspey": {"x":860,"y":460}, + "Moray / Lochaber": {"x":565,"y":665}, + "Moray / Badenoch": {"x":715,"y":610}, + "Strathspey / Buchan": {"x":1110,"y":430}, + "Strathspey / Badenoch": {"x":880,"y":530}, + "Buchan / Badenoch": {"x":990,"y":565}, + "Buchan / Mar": {"x":1095,"y":605}, + "Buchan / Angus": {"x":1240,"y":645}, + "Lochaber / Badenoch": {"x":675,"y":730}, + "Lochaber / Argyll": {"x":530,"y":860}, + "Lochaber / Atholl": {"x":635,"y":855}, + "Badenoch / Mar": {"x":904,"y":672}, + "Badenoch / Atholl": {"x":730,"y":790}, + "Mar / Angus": {"x":1035,"y":750}, + "Mar / Atholl": {"x":835,"y":785}, + "Angus / Atholl": {"x":880,"y":855}, + "Angus / Fife": {"x":965,"y":900}, + "Argyll / Atholl": {"x":585,"y":950}, + "Argyll / Lennox": {"x":545,"y":1065}, + "Atholl / Lennox": {"x":615,"y":1025}, + "Atholl / Mentieth": {"x":690,"y":980}, + "Atholl / Fife": {"x":845,"y":905}, + "Lennox / Mentieth": {"x":725,"y":1185}, + "Lennox / Carrick": {"x":625,"y":1310}, + "Lennox / Lanark": {"x":725,"y":1260}, + "Mentieth / Fife": {"x":880,"y":1060}, + "Mentieth / Lanark": {"x":810,"y":1235}, + "Mentieth / Lothian": {"x":900,"y":1215}, + "Carrick / Lanark": {"x":790,"y":1450}, + "Carrick / Galloway": {"x":680,"y":1556}, + "Carrick / Annan": {"x":850,"y":1540}, + "Lanark / Lothian": {"x":905,"y":1275}, + "Lanark / Selkirk": {"x":922,"y":1377}, + "Lanark / Annan": {"x":888,"y":1470}, + "Lothian / Selkirk": {"x":1010,"y":1300}, + "Lothian / Dunbar": {"x":1100,"y":1235}, + "Selkirk / Dunbar": {"x":1115,"y":1310}, + "Selkirk / Annan": {"x":980,"y":1472}, + "Selkirk / Teviot": {"x":1080,"y":1405}, + "Dunbar / Teviot": {"x":1195,"y":1335}, + "Galloway / Annan": {"x":860,"y":1625}, + "Annan / Teviot": {"x":1070,"y":1525}, +} + ;(function () { function border(A,B,T) { A = area_index[A] @@ -186,6 +186,25 @@ body.shift .block.known:hover { z-index: 100; } +.border { + position: absolute; + width: 24px; + height: 24px; + border-radius: 50%; + text-align: center; + line-height: 24px; + font-size: 16px; + font-weight: bold; + color: white; + background-color: #654; +} + +.oldblocks .border.England { background-color: brown; } +.oldblocks .border.Scotland { background-color: #06a; } + +.newblocks .border.England { background-color: #a12; } +.newblocks .border.Scotland { background-color: #059; } + #blocks > .block { position: absolute; } @@ -202,7 +221,6 @@ body.shift .block.known:hover { box-shadow: 0 0 2px 1px #0002; } - .oldblocks .block.England { border: 4px solid brown; background-color: brown; } .oldblocks .block.Scotland { border: 4px solid #06a; background-color: #06a; } @@ -565,6 +565,7 @@ c50 53 55 80 28 143 -18 42 -21 62 -16 107 17 147 18 179 6 245 -15 91 -56 </svg> <div id="turn" class="turn year_1297"></div> +<div id="borders"></div> <div id="blocks"></div> <div id="offmap" style="visibility:hidden"></div> </div> @@ -16,6 +16,22 @@ function set_has(set, item) { return false } +function map_get(map, key, missing) { + let a = 0 + let b = (map.length >> 1) - 1 + while (a <= b) { + let m = (a + b) >> 1 + let x = map[m<<1] + if (key < x) + b = m - 1 + else if (key > x) + a = m + 1 + else + return map[(m<<1)+1] + } + return missing +} + const ENEMY = { Scotland: "England", England: "Scotland" } const ENGLAND_BAG = area_index["E. Bag"] @@ -70,6 +86,7 @@ let ui = { cards: {}, card_backs: {}, areas: [], + borders: [], blocks: [], battle_menu: [], battle_block: [], @@ -383,6 +400,21 @@ function build_map() { build_battle_block(b, block) build_map_block(b, block) } + + for (let name in BORDERS_XY) { + let xy = BORDERS_XY[name] + let [a, b] = name.split(" / ") + a = area_index[a] + b = area_index[b] + let id = a * 100 + b + let e = document.createElement("div") + e.my_id = id + e.className = "hide" + e.style.left = (xy.x - 12) + "px" + e.style.top = (xy.y - 12) + "px" + ui.borders.push(e) + document.getElementById("borders").appendChild(e) + } } build_map() @@ -612,6 +644,32 @@ function update_map() { } else { ui.areas[view.where].classList.add('battle') } + + for (let e of ui.borders) { + let u = map_get(view.last_used, e.my_id, 0) + let n = map_get(view.border_limit, e.my_id, "") + if (view.main_border && set_has(view.main_border, e.my_id)) + n += "*" + switch (u) { + case 1: + e.className = "border Scotland" + e.textContent = n + break + case 2: + e.className = "border England" + e.textContent = n + break + case 0: + if (n) { + e.className = "border" + e.textContent = n + } else { + e.className = "hide" + e.textContent = "" + } + break + } + } } function update_cards() { @@ -19,6 +19,9 @@ const first_map_area = 3 const ENEMY = { Scotland: "England", England: "Scotland" } +const PID = { "": 0, Scotland: 1, England: 2 } +const UNPID = [ "", "Scotland", "England" ] + const OBSERVER = "Observer" const BOTH = "Both" const ENGLAND = "England" @@ -389,7 +392,7 @@ function border_id(a, b) { } function border_was_last_used_by_enemy(from, to) { - return game.last_used[border_id(from, to)] === ENEMY[game.active] + return map_get(game.last_used, border_id(from, to), 0) === PID[ENEMY[game.active]] } function border_type(a, b) { @@ -397,11 +400,15 @@ function border_type(a, b) { } function border_limit(a, b) { - return game.border_limit[border_id(a,b)] || 0 + return map_get(game.border_limit, border_id(a,b), 0) +} + +function set_border_limit(a, b, n) { + map_set(game.border_limit, border_id(a,b), n) } function reset_border_limits() { - game.border_limit = {} + game.border_limit.length = 0 } function count_friendly(where) { @@ -576,13 +583,13 @@ function is_battle_reserve(b) { } function is_attacker(b) { - if (game.location[b] === game.where && block_owner(b) === game.attacker[game.where]) + if (game.location[b] === game.where && block_owner(b) === get_attacker(game.where)) return !set_has(game.reserves, b) return false } function is_defender(b) { - if (game.location[b] === game.where && block_owner(b) !== game.attacker[game.where]) + if (game.location[b] === game.where && block_owner(b) !== get_attacker(game.where)) return !set_has(game.reserves, b) return false } @@ -870,8 +877,8 @@ function start_game_turn() { // Reset movement and attack tracking state game.truce = false reset_border_limits() - game.last_used = {} - game.attacker = {} + game.last_used = [] + game.attacker = [] game.reserves = [] game.moved = [] @@ -1108,7 +1115,7 @@ function defect_nobles(list) { log(name + " defected.") who = swap_blocks(who) if (is_contested_area(where)) - game.attacker[where] = block_owner(who) + set_attacker(where, block_owner(who)) } } resume_coronation() @@ -1182,7 +1189,7 @@ states.herald = { let where = game.location[who] who = swap_blocks(who) if (is_contested_area(where)) { - game.attacker[where] = game.active + set_attacker(where, game.active) start_battle(where, 'herald') return } @@ -1373,7 +1380,7 @@ function end_pillage(where) { game.where = NOWHERE delete game.pillage if (is_contested_area(where)) { - game.attacker[where] = ENEMY[game.active] + set_attacker(where, ENEMY[game.active]) start_battle(where, 'pillage') } else { end_player_turn() @@ -1455,12 +1462,36 @@ states.sea_move_to = { // MOVE PHASE +function get_attacker(x) { + return UNPID[map_get(game.attacker, x, 0)] +} + +function set_attacker(x, who) { + return map_set(game.attacker, x, PID[who]) +} + +function main_border(to) { + return map_get(game.main_border, to, 0) +} + +function main_origin(to) { + return map_get(game.main_origin, to, 0) +} + +function set_main_border(to, x) { + map_set(game.main_border, to, x) +} + +function set_main_origin(to, x) { + map_set(game.main_origin, to, x) +} + function goto_move_phase(moves) { game.state = 'move_who' game.moves = moves game.activated = [] - game.main_origin = {} - game.main_border = {} + game.main_origin = [] + game.main_border = [] game.turn_log = [] clear_undo() } @@ -1507,17 +1538,17 @@ states.move_who = { function move_block(who, from, to) { game.location[who] = to - game.border_limit[border_id(from, to)] = border_limit(from, to) + 1 + set_border_limit(from, to, border_limit(from, to) + 1) game.distance ++ if (is_contested_area(to)) { - game.last_used[border_id(from, to)] = game.active - if (!game.attacker[to]) { - game.attacker[to] = game.active - game.main_border[to] = from - game.main_origin[to] = game.origin + map_set(game.last_used, border_id(from, to), PID[game.active]) + if (!get_attacker(to)) { + set_attacker(to, game.active) + set_main_border(to, from) + set_main_origin(to, game.origin) return ATTACK_MARK } else { - if (game.attacker[to] !== game.active || game.main_border[to] !== from || game.main_origin[to] !== game.origin) { + if (get_attacker(to) !== game.active || main_border(to) !== from || main_origin(to) !== game.origin) { set_add(game.reserves, who) return RESERVE_MARK } else { @@ -1567,9 +1598,9 @@ states.move_where = { game.location[game.who] = to set_add(game.moved, game.who) if (is_contested_area(to)) { - if (!game.attacker[to]) { + if (!get_attacker(to)) { game.turn_log.push([area_tag(from), area_tag(to) + ATTACK_MARK + " (Norse)"]) - game.attacker[to] = game.active + set_attacker(to, game.active) } else { game.turn_log.push([area_tag(from), area_tag(to) + RESERVE_MARK + " (Norse)"]) set_add(game.reserves, game.who) @@ -1627,6 +1658,7 @@ function bring_on_reserves() { } function goto_battle_phase() { + reset_border_limits() if (have_contested_areas()) { game.active = game.p1 game.state = 'battle_phase' @@ -1679,7 +1711,7 @@ function end_battle() { reset_border_limits() game.moved = [] - game.active = game.attacker[game.where] + game.active = get_attacker(game.where) let victor = game.active if (is_contested_area(game.where)) victor = ENEMY[game.active] @@ -1719,7 +1751,7 @@ function goto_battle_round(new_battle_round) { if (count_defenders() === 0) { log("Defending main force was eliminated.") log("Battlefield control changed.") - game.attacker[game.where] = ENEMY[game.attacker[game.where]] + set_attacker(game.where, ENEMY[get_attacker(game.where)]) } else if (count_attackers() === 0) { log("Attacking main force was eliminated.") } @@ -1780,7 +1812,7 @@ function battle_step(active, initiative, candidate) { } function pump_battle_step() { - let attacker = game.attacker[game.where] + let attacker = get_attacker(game.where) let defender = ENEMY[attacker] if (battle_step(defender, 'A', is_defender)) return @@ -1849,7 +1881,7 @@ function pass_with_block(b) { } function count_enemy_hp_in_battle() { - let is_candidate = (game.active === game.attacker[game.where]) ? is_defender : is_attacker + let is_candidate = (game.active === get_attacker(game.where)) ? is_defender : is_attacker let n = 0 for (let b = 0; b < block_count; ++b) if (is_candidate(b)) @@ -1962,7 +1994,7 @@ function apply_hit(who) { } function list_victims(p) { - let is_candidate = (p === game.attacker[game.where]) ? is_attacker : is_defender + let is_candidate = (p === get_attacker(game.where)) ? is_attacker : is_defender let max = 0 for (let b = 0; b < block_count; ++b) if (is_candidate(b) && game.steps[b] > max) @@ -1994,7 +2026,7 @@ states.battle_hits = { } function goto_retreat() { - game.active = game.attacker[game.where] + game.active = get_attacker(game.where) if (is_contested_area(game.where)) { game.state = 'retreat' game.turn_log = [] @@ -2131,7 +2163,7 @@ states.retreat_in_battle = { } function goto_regroup() { - game.active = game.attacker[game.where] + game.active = get_attacker(game.where) if (is_enemy_area(game.where)) game.active = ENEMY[game.active] game.state = 'regroup' @@ -2157,22 +2189,22 @@ states.regroup = { }, end_regroup: function () { print_turn_log("regrouped") - game.attacker[game.where] = null // XXX ??? + set_attacker(game.where, "") // XXX ??? game.where = NOWHERE clear_undo() game.active = game.battle_active delete game.battle_active if (game.battle_reason === 'herald') { delete game.battle_reason - game.last_used = {} + game.last_used = [] end_player_turn() } else if (game.battle_reason === 'pillage') { delete game.battle_reason - game.last_used = {} + game.last_used = [] end_player_turn() } else if (game.battle_reason === 'coronation') { delete game.battle_reason - game.last_used = {} + game.last_used = [] resume_coronation() } else { delete game.battle_reason @@ -3044,7 +3076,7 @@ function make_battle_view() { flash: game.flash } - battle.title = game.attacker[game.where] + " attacks " + area_name(game.where) + battle.title = get_attacker(game.where) + " attacks " + area_name(game.where) battle.title += " \u2014 round " + game.battle_round + " of 3" function fill_cell(cell, owner, fn) { @@ -3074,11 +3106,12 @@ exports.setup = function (seed, scenario, options) { moved: [], reserves: [], - attacker: {}, - border_limit: {}, - last_used: {}, - main_border: {}, - main_origin: {}, + attacker: [], + border_limit: [], + last_used: [], + main_border: [], + main_origin: [], + show_cards: 0, who: NOBODY, where: NOWHERE, @@ -3167,9 +3200,17 @@ exports.view = function(state, current) { location: game.location, steps: game.steps, moved: game.moved, + last_used: game.last_used, + border_limit: game.border_limit, active: game.active, } + if (game.main_border && game.main_border.length > 0) { + view.main_border = [] + for (let i = 0; i < game.main_border.length; i += 2) + set_add(view.main_border, border_id(game.main_border[i+0], game.main_border[i+1])) + } + states[game.state].prompt(view, current) if (states[game.state].show_battle) @@ -3197,6 +3238,15 @@ function array_insert(array, index, item) { return array } +function array_insert_pair(array, index, key, value) { + for (let i = array.length; i > index; i -= 2) { + array[i] = array[i-2] + array[i+1] = array[i-1] + } + array[index] = key + array[index+1] = value +} + function set_clear(set) { set.length = 0 } @@ -3265,6 +3315,40 @@ function set_toggle(set, item) { return array_insert(set, a, item) } +function map_get(map, key, missing) { + let a = 0 + let b = (map.length >> 1) - 1 + while (a <= b) { + let m = (a + b) >> 1 + let x = map[m<<1] + if (key < x) + b = m - 1 + else if (key > x) + a = m + 1 + else + return map[(m<<1)+1] + } + return missing +} + +function map_set(map, key, value) { + let a = 0 + let b = (map.length >> 1) - 1 + while (a <= b) { + let m = (a + b) >> 1 + let x = map[m<<1] + if (key < x) + b = m - 1 + else if (key > x) + a = m + 1 + else { + map[(m<<1)+1] = value + return + } + } + array_insert_pair(map, a<<1, key, value) +} + // Fast deep copy for objects without cycles function object_copy(original) { if (Array.isArray(original)) { diff --git a/tools/borders.svg b/tools/borders.svg new file mode 100644 index 0000000..0e05b74 --- /dev/null +++ b/tools/borders.svg @@ -0,0 +1,359 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="1688" + height="1950" + version="1.1" + id="svg102" + sodipodi:docname="borders.svg" + inkscape:version="1.0.2 (e86c870879, 2021-01-15)"> + <metadata + id="metadata108"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs106" /> + <sodipodi:namedview + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1" + objecttolerance="10" + gridtolerance="10" + guidetolerance="10" + inkscape:pageopacity="0" + inkscape:pageshadow="2" + inkscape:window-width="640" + inkscape:window-height="480" + id="namedview104" + showgrid="true" + inkscape:snap-bbox="true" + inkscape:snap-text-baseline="true" + inkscape:snap-object-midpoints="true" + inkscape:zoom="6.3795364" + inkscape:cx="542.64371" + inkscape:cy="1069.5661" + inkscape:current-layer="svg102" + inkscape:document-rotation="0"> + <inkscape:grid + type="xygrid" + id="grid110" /> + </sodipodi:namedview> + <image + sodipodi:absref="/home/tor/src/rally/public/hammer-of-the-scots/map75.png" + xlink:href="../map75.png" + id="image2" + sodipodi:insensitive="true" + image-rendering="pixelated" + height="1950" + width="1688" + y="0" + x="0" /> + <circle + inkscape:label="England / Dunbar" + cx="1285" + cy="1320" + r="16" + id="circle4" /> + <circle + inkscape:label="England / Annan" + cx="1065" + cy="1630" + r="16" + id="circle6" /> + <circle + inkscape:label="England / Teviot" + cx="1210" + cy="1495" + r="16" + id="circle8" /> + <circle + inkscape:label="Ross / Garmoran" + cx="505" + cy="450" + r="16" + id="circle10" /> + <circle + inkscape:label="Ross / Moray" + cx="665" + cy="455" + r="16" + id="circle12" /> + <circle + inkscape:label="Garmoran / Moray" + cx="550" + cy="590" + r="16" + id="circle14" /> + <circle + inkscape:label="Garmoran / Lochaber" + cx="445" + cy="670" + r="16" + id="circle16" /> + <circle + inkscape:label="Moray / Strathspey" + cx="860" + cy="460" + r="16" + id="circle18" /> + <circle + inkscape:label="Moray / Lochaber" + cx="565" + cy="665" + r="16" + id="circle20" /> + <circle + inkscape:label="Moray / Badenoch" + cx="715" + cy="610" + r="16" + id="circle22" /> + <circle + inkscape:label="Strathspey / Buchan" + cx="1110" + cy="430" + r="16" + id="circle24" /> + <circle + inkscape:label="Strathspey / Badenoch" + cx="880" + cy="530" + r="16" + id="circle26" /> + <circle + inkscape:label="Buchan / Badenoch" + cx="990" + cy="565" + r="16" + id="circle28" /> + <circle + inkscape:label="Buchan / Mar" + cx="1095" + cy="605" + r="16" + id="circle30" /> + <circle + inkscape:label="Buchan / Angus" + cx="1240" + cy="645" + r="16" + id="circle32" /> + <circle + inkscape:label="Lochaber / Badenoch" + cx="675" + cy="730" + r="16" + id="circle34" /> + <circle + inkscape:label="Lochaber / Argyll" + cx="530" + cy="860" + r="16" + id="circle36" /> + <circle + inkscape:label="Lochaber / Atholl" + cx="635" + cy="855" + r="16" + id="circle38" /> + <circle + inkscape:label="Badenoch / Mar" + cx="904" + cy="672" + r="16" + id="circle40" /> + <circle + inkscape:label="Badenoch / Atholl" + cx="730" + cy="790" + r="16" + id="circle42" /> + <circle + inkscape:label="Mar / Angus" + cx="1035" + cy="750" + r="16" + id="circle44" /> + <circle + inkscape:label="Mar / Atholl" + cx="835" + cy="785" + r="16" + id="circle46" /> + <circle + inkscape:label="Angus / Atholl" + cx="880" + cy="855" + r="16" + id="circle48" /> + <circle + inkscape:label="Angus / Fife" + cx="965" + cy="900" + r="16" + id="circle50" /> + <circle + inkscape:label="Argyll / Atholl" + cx="585" + cy="950" + r="16" + id="circle52" /> + <circle + inkscape:label="Argyll / Lennox" + cx="545" + cy="1065" + r="16" + id="circle54" /> + <circle + inkscape:label="Atholl / Lennox" + cx="615" + cy="1025" + r="16" + id="circle56" /> + <circle + inkscape:label="Atholl / Mentieth" + cx="690" + cy="980" + r="16" + id="circle58" /> + <circle + inkscape:label="Atholl / Fife" + cx="845" + cy="905" + r="16" + id="circle60" /> + <circle + inkscape:label="Lennox / Mentieth" + cx="725" + cy="1185" + r="16" + id="circle62" /> + <circle + inkscape:label="Lennox / Carrick" + cx="625" + cy="1310" + r="16" + id="circle64" /> + <circle + inkscape:label="Lennox / Lanark" + cx="725" + cy="1260" + r="16" + id="circle66" /> + <circle + inkscape:label="Mentieth / Fife" + cx="880" + cy="1060" + r="16" + id="circle68" /> + <circle + inkscape:label="Mentieth / Lanark" + cx="810" + cy="1235" + r="16" + id="circle70" /> + <circle + inkscape:label="Mentieth / Lothian" + cx="900" + cy="1215" + r="16" + id="circle72" /> + <circle + inkscape:label="Carrick / Lanark" + cx="790" + cy="1450" + r="16" + id="circle74" /> + <circle + inkscape:label="Carrick / Galloway" + cx="680" + cy="1556" + r="16" + id="circle76" /> + <circle + inkscape:label="Carrick / Annan" + cx="850" + cy="1540" + r="16" + id="circle78" /> + <circle + inkscape:label="Lanark / Lothian" + cx="905" + cy="1275" + r="16" + id="circle80" /> + <circle + inkscape:label="Lanark / Selkirk" + cx="922" + cy="1377" + r="16" + id="circle82" /> + <circle + inkscape:label="Lanark / Annan" + cx="888" + cy="1470" + r="16" + id="circle84" /> + <circle + inkscape:label="Lothian / Selkirk" + cx="1010" + cy="1300" + r="16" + id="circle86" /> + <circle + inkscape:label="Lothian / Dunbar" + cx="1100" + cy="1235" + r="16" + id="circle88" /> + <circle + inkscape:label="Selkirk / Dunbar" + cx="1115" + cy="1310" + r="16" + id="circle90" /> + <circle + inkscape:label="Selkirk / Annan" + cx="980" + cy="1472" + r="16" + id="circle92" /> + <circle + inkscape:label="Selkirk / Teviot" + cx="1080" + cy="1405" + r="16" + id="circle94" /> + <circle + inkscape:label="Dunbar / Teviot" + cx="1195" + cy="1335" + r="16" + id="circle96" /> + <circle + inkscape:label="Galloway / Annan" + cx="860" + cy="1625" + r="16" + id="circle98" /> + <circle + inkscape:label="Annan / Teviot" + cx="1070" + cy="1525" + r="16" + id="circle100" /> +</svg> diff --git a/tools/genborders.js b/tools/genborders.js new file mode 100644 index 0000000..9c50721 --- /dev/null +++ b/tools/genborders.js @@ -0,0 +1,53 @@ +const fs = require("fs") + +const { round, floor, ceil } = Math + +let output = {} +let mode, name, x, y, w, h, cx, cy, rx, ry + +function flush() { + if (mode === 'circle') { + output[name] = { x: cx, y: cy } + } + x = y = w = h = cx = cy = rx = ry = 0 + name = null +} + +for (let line of fs.readFileSync("tools/borders.svg", "utf-8").split("\n")) { + line = line.trim() + if (line.startsWith("<rect")) { + flush() + mode = "rect" + x = y = w = h = 0 + } else if (line.startsWith("<ellipse") || line.startsWith("<circle")) { + flush() + mode = "circle" + cx = cy = rx = ry = 0 + } else if (line.startsWith('x="')) + x = round(Number(line.split('"')[1])) + else if (line.startsWith('y="')) + y = round(Number(line.split('"')[1])) + else if (line.startsWith('width="')) + w = round(Number(line.split('"')[1])) + else if (line.startsWith('height="')) + h = round(Number(line.split('"')[1])) + else if (line.startsWith('cx="')) + cx = round(Number(line.split('"')[1])) + else if (line.startsWith('cy="')) + cy = round(Number(line.split('"')[1])) + else if (line.startsWith('r="')) + rx = ry = round(Number(line.split('"')[1])) + else if (line.startsWith('rx="')) + rx = round(Number(line.split('"')[1])) + else if (line.startsWith('ry="')) + ry = round(Number(line.split('"')[1])) + else if (line.startsWith('inkscape:label="')) + name = line.split('"')[1] +} + +flush() + +console.log("const BORDERS_XY = {") +for (let key in output) + console.log("\t\"" + key + "\": " + JSON.stringify(output[key]) + ",") +console.log("}") diff --git a/tools/makeborders.js b/tools/makeborders.js new file mode 100644 index 0000000..a54a36c --- /dev/null +++ b/tools/makeborders.js @@ -0,0 +1,32 @@ +const print = console.log + +const data = require("../data.js") + +var w = 1688 +var h = 1950 +var m = "../map75.png" + +print(`<?xml version="1.0" encoding="UTF-8"?> +<svg + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="${w}" + height="${h}" +> +<image xlink:href="${m}" x="0" y="0" width="${w}" height="${h}" image-rendering="pixelated" sodipodi:insensitive="true" />`) + +for (let id = 0; id < data.BORDERS.length; ++id) { + if (data.BORDERS[id]) { + let a = (id / 100) | 0 + let b = id % 100 + let x = (data.AREAS[a].x + data.AREAS[b].x) >> 1 + let y = (data.AREAS[a].y + data.AREAS[b].y) >> 1 + let label = data.AREAS[a].name + " / " + data.AREAS[b].name + print(`<circle inkscape:label="${label}" cx="${x}" cy="${y}" r="16"/>`) + + } +} + +print(`</svg>`) |