diff options
author | MetroWind <chris.corsair@gmail.com> | 2025-08-29 21:58:39 -0700 |
---|---|---|
committer | MetroWind <chris.corsair@gmail.com> | 2025-08-29 21:58:39 -0700 |
commit | 12fff735867d3984f7ff824b2a93b1093f8910d7 (patch) | |
tree | 8cf5d9b8e00de1cb5503d8f21c447a3ec9eef697 |
WIP
-rw-r--r-- | index.html | 22 | ||||
-rw-r--r-- | scripts/lib.js | 10 | ||||
-rw-r--r-- | scripts/main.js | 93 | ||||
-rw-r--r-- | scripts/tiles.js | 94 | ||||
-rw-r--r-- | styles.css | 23 |
5 files changed, 242 insertions, 0 deletions
diff --git a/index.html b/index.html new file mode 100644 index 0000000..f46f57c --- /dev/null +++ b/index.html | |||
@@ -0,0 +1,22 @@ | |||
1 | <!doctype html> | ||
2 | <html lang="en-US"> | ||
3 | <head> | ||
4 | <meta charset="utf-8" /> | ||
5 | <meta name="viewport" | ||
6 | content="width=device-width, initial-scale=1" /> | ||
7 | <link rel="stylesheet" href="styles.css" /> | ||
8 | <script src="https://cdnjs.cloudflare.com/ajax/libs/preact/10.27.1/preact.min.js" | ||
9 | integrity="sha512-tJqZlYpxIwLhPlIXYqzFfeEzWsb0eBbDJbAJcnfIhFKkF1pQ6Q5SkB+gFvZi0jt+7BPY69WrWyxBu5PLn1BJpw==" | ||
10 | crossorigin="anonymous" | ||
11 | referrerpolicy="no-referrer"></script> | ||
12 | <script src="https://cdnjs.cloudflare.com/ajax/libs/preact/10.27.1/hooks.umd.min.js" integrity="sha512-mODWC9TZn8iSfmbGnNgHErn0TQSFstq5vEBnkKJK7HPDgt9MpTECcYg50whbqW2yAHIYVnvpnW+owcEx/FnR3g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script> | ||
13 | <script src="scripts/lib.js"></script> | ||
14 | <script src="scripts/tiles.js"></script> | ||
15 | <script type="module" defer src="scripts/main.js"></script> | ||
16 | <title>My test page</title> | ||
17 | </head> | ||
18 | <body> | ||
19 | <div id="Plan"> | ||
20 | </div> | ||
21 | </body> | ||
22 | </html> | ||
diff --git a/scripts/lib.js b/scripts/lib.js new file mode 100644 index 0000000..4e4850e --- /dev/null +++ b/scripts/lib.js | |||
@@ -0,0 +1,10 @@ | |||
1 | const h = preact.h; | ||
2 | const CELL_SIZE = 32; | ||
3 | const GRID_SIZE_X = 21; | ||
4 | const GRID_SIZE_Y = 21; | ||
5 | |||
6 | const ASSET_SVG_PROPS = {"width": CELL_SIZE, "height": CELL_SIZE, | ||
7 | "version": "1.1"}; | ||
8 | const ASSET_WHITE_BG = h("rect", {"x": 0, "y": 0, "width": CELL_SIZE, | ||
9 | "height": CELL_SIZE, "fill": "white", | ||
10 | "stroke-width": 0 }); | ||
diff --git a/scripts/main.js b/scripts/main.js new file mode 100644 index 0000000..7a8b9d4 --- /dev/null +++ b/scripts/main.js | |||
@@ -0,0 +1,93 @@ | |||
1 | function genGrid(x_count, y_count, cell_size) | ||
2 | { | ||
3 | let lines = []; | ||
4 | for(let x = 0; x < x_count+1; x++) | ||
5 | { | ||
6 | lines.push(h("line", { | ||
7 | "x1": x * cell_size + 0.5, "x2": x * cell_size + 0.5, "y1": 0.5, | ||
8 | "y2": y_count * cell_size + 0.5, | ||
9 | })); | ||
10 | } | ||
11 | for(let y = 0; y < y_count+1; y++) | ||
12 | { | ||
13 | lines.push(h("line", { | ||
14 | "y1": y * cell_size + 0.5, "y2": y * cell_size + 0.5, "x1": 0.5, | ||
15 | "x2": x_count * cell_size + 0.5, | ||
16 | })); | ||
17 | } | ||
18 | return h("g", {"stroke": "black", "stroke-width": 1}, lines); | ||
19 | } | ||
20 | |||
21 | function AssetView({asset_index}) | ||
22 | { | ||
23 | return h("div", {"class": "Asset", "draggable": true, | ||
24 | "id": `Asset${asset_index}`}, | ||
25 | h("svg", ASSET_SVG_PROPS, ASSET_WHITE_BG, | ||
26 | TILES[asset_index].inner_svg)); | ||
27 | } | ||
28 | |||
29 | function AssetsView() | ||
30 | { | ||
31 | let views = TILES.slice(1).map((t, i) => | ||
32 | h(AssetView, {asset_index: i + 1})); | ||
33 | return h("div", {}, views); | ||
34 | } | ||
35 | |||
36 | function PlanGridView({content}) | ||
37 | { | ||
38 | const [mouse_coord, setMouseCoord] = preactHooks.useState(null); | ||
39 | |||
40 | function onDragOver(e) | ||
41 | { | ||
42 | e.preventDefault(); | ||
43 | let b = e.target.getBoundingClientRect(); | ||
44 | let x = e.clientX - b.left; | ||
45 | let y = e.clientY - b.top; | ||
46 | setMouseCoord([x, y]); | ||
47 | } | ||
48 | |||
49 | function onDragLeave(e) | ||
50 | { | ||
51 | setMouseCoord(null); | ||
52 | } | ||
53 | |||
54 | let hilight_box = null; | ||
55 | if(mouse_coord !== null) | ||
56 | { | ||
57 | let cell_x = Math.floor(mouse_coord[0] / (CELL_SIZE + 1)); | ||
58 | let cell_y = Math.floor(mouse_coord[1] / (CELL_SIZE + 1)); | ||
59 | hilight_box = h("rect", { | ||
60 | "x": cell_x * (CELL_SIZE + 1) + 0.5, "y": cell_y * (CELL_SIZE + 1) + 0.5, | ||
61 | "width": CELL_SIZE + 1, "height": CELL_SIZE + 1, | ||
62 | "stroke": "#2ed573", "stroke-width": 3, "fill": "transparent"}); | ||
63 | } | ||
64 | let grid_content = content.map((idx, i) => { | ||
65 | let cell_x = i % GRID_SIZE_X; | ||
66 | let cell_y = Math.floor(i / GRID_SIZE_Y); | ||
67 | return h("g", {"transform": `translate(${cell_x * (CELL_SIZE + 1)} \ | ||
68 | ${cell_y * (CELL_SIZE + 1)})`}, TILES[idx]); | ||
69 | }); | ||
70 | |||
71 | return h("svg", {"width": GRID_SIZE_X * (CELL_SIZE + 1) + 1, | ||
72 | "height": GRID_SIZE_Y * (CELL_SIZE + 1) + 1, | ||
73 | "version": "1.1", "ondragover": onDragOver, | ||
74 | "id": "Grid", "ondragleave": onDragLeave}, | ||
75 | grid_content, genGrid(GRID_SIZE_X, GRID_SIZE_Y, CELL_SIZE + 1), | ||
76 | hilight_box); | ||
77 | } | ||
78 | |||
79 | function PlanView(props) | ||
80 | { | ||
81 | return h("div", {}, | ||
82 | h("p", {}, "The plan:"), | ||
83 | h(PlanGridView, {})); | ||
84 | } | ||
85 | |||
86 | function App(props) | ||
87 | { | ||
88 | return h("div", {}, | ||
89 | h(AssetsView, {}), | ||
90 | h(PlanView, {})); | ||
91 | } | ||
92 | |||
93 | preact.render(h(App, {}), document.getElementById("Plan")); | ||
diff --git a/scripts/tiles.js b/scripts/tiles.js new file mode 100644 index 0000000..f35887a --- /dev/null +++ b/scripts/tiles.js | |||
@@ -0,0 +1,94 @@ | |||
1 | const TILES = [ | ||
2 | null, | ||
3 | { | ||
4 | id: "blank", | ||
5 | name: "black", | ||
6 | inner_svg: null, | ||
7 | }, { | ||
8 | id: "teleporter", | ||
9 | name: "teleporter", | ||
10 | inner_svg: h("circle", { | ||
11 | "cx": CELL_SIZE * 0.5, "cy": CELL_SIZE * 0.5, | ||
12 | "r": (CELL_SIZE - 10) * 0.5, "stroke": "black", | ||
13 | "fill": "transparent", "stroke-width": 6, | ||
14 | }), | ||
15 | }, { | ||
16 | id: "fleet_command", | ||
17 | name: "fleet command", | ||
18 | inner_svg: h("polyline", { | ||
19 | "points": `3, ${CELL_SIZE} \ | ||
20 | 3, ${CELL_SIZE - 15} \ | ||
21 | 13, ${CELL_SIZE - 25} \ | ||
22 | 13, ${CELL_SIZE - 5} \ | ||
23 | ${CELL_SIZE - 13}, ${CELL_SIZE - 5} \ | ||
24 | ${CELL_SIZE - 13}, ${CELL_SIZE - 25} \ | ||
25 | ${CELL_SIZE - 3}, ${CELL_SIZE - 15} \ | ||
26 | ${CELL_SIZE - 3}, ${CELL_SIZE}`, | ||
27 | "fill": "black", "stroke-width": 0, | ||
28 | }), | ||
29 | }, { | ||
30 | id: "scanner", | ||
31 | name: "scanner", | ||
32 | inner_svg: | ||
33 | h(preact.Fragment, {}, | ||
34 | h("circle", {"cx": CELL_SIZE * 0.5, "cy": CELL_SIZE * 0.5, | ||
35 | "r": CELL_SIZE * 0.5 - 2, "fill": "transparent", | ||
36 | "stroke": "black", "stroke-width": 2}), | ||
37 | h("circle", {"cx": CELL_SIZE * 0.5, "cy": CELL_SIZE * 0.5, | ||
38 | "r": CELL_SIZE * 0.25, "fill": "transparent", | ||
39 | "stroke": "black", "stroke-width": 2}), | ||
40 | h("line", {"x1": 2, "y1": CELL_SIZE * 0.5, | ||
41 | "x2": CELL_SIZE - 2, "y2": CELL_SIZE * 0.5, | ||
42 | "stroke": "black", "stroke-width": 2}), | ||
43 | h("line", {"y1": 2, "x1": CELL_SIZE * 0.5, | ||
44 | "y2": CELL_SIZE - 2, "x2": CELL_SIZE * 0.5, | ||
45 | "stroke": "black", "stroke-width": 2}), | ||
46 | h("line", {"x1": CELL_SIZE * 0.5, "y1": CELL_SIZE * 0.5, | ||
47 | "x2": CELL_SIZE * 0.5 + (CELL_SIZE * 0.5 - 2) * Math.cos(Math.PI / 6), | ||
48 | "y2": CELL_SIZE * 0.5 - (CELL_SIZE * 0.5 - 2) * Math.sin(Math.PI / 6), | ||
49 | "stroke": "black", "stroke-width": 2}), | ||
50 | h("circle", {"cx": 22, "cy": 6, "r": 2, "fill": "black", | ||
51 | "stroke-width": 0}), | ||
52 | h("circle", {"cx": 10, "cy": 22, "r": 4, "fill": "black", | ||
53 | "stroke-width": 0}), | ||
54 | |||
55 | ), | ||
56 | }, { | ||
57 | id: "extractor", | ||
58 | name: "stellar extractor", | ||
59 | inner_svg: | ||
60 | h(preact.Fragment, {}, | ||
61 | h("circle", {"cx": CELL_SIZE * 0.5, "cy": 21, | ||
62 | "r": 10, "fill": "black", "stroke-width": 0}), | ||
63 | h("polygon", {"points": `${CELL_SIZE * 0.5 - 4}, 20 \ | ||
64 | ${CELL_SIZE * 0.5 - 4}, 10 \ | ||
65 | ${CELL_SIZE * 0.5 - 8}, 10 \ | ||
66 | ${CELL_SIZE * 0.5}, 3 \ | ||
67 | ${CELL_SIZE * 0.5 + 8}, 10 \ | ||
68 | ${CELL_SIZE * 0.5 + 4}, 10 \ | ||
69 | ${CELL_SIZE * 0.5 + 4}, 20`, | ||
70 | "stroke": "black", "stroke-width": 2, | ||
71 | "fill": "white"}), | ||
72 | ), | ||
73 | }, { | ||
74 | id: "storage", | ||
75 | name: "storage", | ||
76 | inner_svg: | ||
77 | h(preact.Fragment, {}, | ||
78 | h("rect", {"x": 4, "y": 4, "width": CELL_SIZE - 8, | ||
79 | "height": CELL_SIZE - 8, "rx": 2, "ry": 2, | ||
80 | "fill": "black", "stroke-width": 0}), | ||
81 | h("line", {"x1": 4, "y1": 14, "x2": CELL_SIZE - 4, "y2": 14, | ||
82 | "stroke": "white", "stroke-width": 2}), | ||
83 | h("rect", {"x": CELL_SIZE * 0.5 - 4, "y": 10, "width": 8, "height": 8, | ||
84 | "fill": "white", "stroke-width": 0}), | ||
85 | ), | ||
86 | }, { | ||
87 | id: "plant", | ||
88 | name: "double cultivation chamber", | ||
89 | inner_svg: h("path", {"d": "m3.02 17.7a13 13 0 0013 13 13 13 0 00-13-13m13 13a13 13 0 0013-13 13 13 0 00-13 13m8.66-27.4v7.21a8.66 8.66 0 01-8.66 8.66 8.66 8.66 0 01-8.66-8.66v-7.21c1.07 0 2.12.173 3.12.563.793.332 1.5.821 2.09 1.44l3.43-3.43 3.43 3.43c.591-.622 1.3-1.11 2.09-1.44.996-.388 2.05-.563 3.12-.563z", | ||
90 | "fill": "black", "stroke-width": 0}), | ||
91 | } | ||
92 | ]; | ||
93 | |||
94 | const TILE_MAP = Object.fromEntries(TILES.slice(1).map(t => [t.id, t])); | ||
diff --git a/styles.css b/styles.css new file mode 100644 index 0000000..aca9b9b --- /dev/null +++ b/styles.css | |||
@@ -0,0 +1,23 @@ | |||
1 | body | ||
2 | { | ||
3 | background-color: #a4b0be; | ||
4 | } | ||
5 | |||
6 | * | ||
7 | { | ||
8 | box-sizing: border-box; | ||
9 | } | ||
10 | |||
11 | .Asset | ||
12 | { | ||
13 | display: inline-block; | ||
14 | border: solid black 1px; | ||
15 | width: 34px; | ||
16 | height: 34px; | ||
17 | } | ||
18 | |||
19 | /* Prevent SVG elements from interferring with drag n drop. */ | ||
20 | #Grid * | ||
21 | { | ||
22 | pointer-events: none; | ||
23 | } | ||