From 92cc6c3c2850dd7cd59021aba2df3ccc963dad87 Mon Sep 17 00:00:00 2001 From: core Date: Mon, 8 Jan 2024 18:03:25 -0500 Subject: [PATCH] client save editor --- starkingdoms-client/package.json | 1 + .../src/components/ui/Button.svelte | 26 +- .../src/css/themes/catppuccin-mocha.scss | 1 + starkingdoms-client/src/pages/Home.svelte | 14 +- .../src/pages/ShipEditor.svelte | 337 ++++++++++++++++-- starkingdoms-client/src/save.ts | 21 ++ starkingdoms-client/yarn.lock | 5 + 7 files changed, 370 insertions(+), 35 deletions(-) create mode 100644 starkingdoms-client/src/save.ts diff --git a/starkingdoms-client/package.json b/starkingdoms-client/package.json index 26276b8d0b7228f646e07dca4e2a8169a42bee23..369f5d1b86a22b65749e312b7ee1c7ed876bf0e1 100644 --- a/starkingdoms-client/package.json +++ b/starkingdoms-client/package.json @@ -26,6 +26,7 @@ "vite": "^5.0.0" }, "dependencies": { + "@msgpack/msgpack": "^3.0.0-beta2", "debug": "^4.3.4", "pixi.js": "^7.3.2" } diff --git a/starkingdoms-client/src/components/ui/Button.svelte b/starkingdoms-client/src/components/ui/Button.svelte index d462fbc767be736c95393fcf6c50cb24ff6984d8..0d7829b2a9106bc61cd93a184ad3456ce34278e6 100644 --- a/starkingdoms-client/src/components/ui/Button.svelte +++ b/starkingdoms-client/src/components/ui/Button.svelte @@ -4,25 +4,43 @@ export let id: string = ""; export let disabled = false; export let style = ""; + export let variant = "normal"; - diff --git a/starkingdoms-client/src/css/themes/catppuccin-mocha.scss b/starkingdoms-client/src/css/themes/catppuccin-mocha.scss index b943d9dc2dc35378a3681f584517c393260eb183..840180ff9579ff2548d1f7daa63fd8c83fb3d771 100644 --- a/starkingdoms-client/src/css/themes/catppuccin-mocha.scss +++ b/starkingdoms-client/src/css/themes/catppuccin-mocha.scss @@ -53,6 +53,7 @@ --success: rgb(var(--green)); --warning: rgb(var(--yellow)); --error: rgb(var(--red)); + --error-transparent: rgba(var(--red), 0.35); --tag: rgb(var(--blue)); --pill: rgb(var(--blue)); --sel-bg: rgba(var(--surface2), 0.4); diff --git a/starkingdoms-client/src/pages/Home.svelte b/starkingdoms-client/src/pages/Home.svelte index df2d231e439a3343d2b9723a5618ae76b4164533..d7fc15f7dcc926c793b5d1910754943a37230d81 100644 --- a/starkingdoms-client/src/pages/Home.svelte +++ b/starkingdoms-client/src/pages/Home.svelte @@ -117,12 +117,14 @@ - - - - - +{#if window.localStorage.getItem("save") !== null} + + + + + +{/if} StarKingdoms Client {APP_VERSION} ({COMMIT_HASH}) diff --git a/starkingdoms-client/src/pages/ShipEditor.svelte b/starkingdoms-client/src/pages/ShipEditor.svelte index 4d465c68eab5dd9f45b692fc06792312c8fc61c0..fc49cde69152bda75a8ed7ea3037faec9068e089 100644 --- a/starkingdoms-client/src/pages/ShipEditor.svelte +++ b/starkingdoms-client/src/pages/ShipEditor.svelte @@ -3,14 +3,13 @@ import createDebug from "debug"; import "../css/themes/catppuccin-mocha.scss"; import "../css/style.scss"; - import { parseJwt } from "../jwt.ts"; import HeartIcon from "../icons/HeartIcon.svelte"; import Popup from "../components/ui/Popup.svelte"; + import { unpack_save } from "../save.ts"; + import { PartType } from "../protocol.ts"; + import { onMount } from "svelte"; + import { part_texture_url } from "../textures.ts"; import Button from "../components/ui/Button.svelte"; - import TextInput from "../components/ui/TextInput.svelte"; - import PasswordInput from "../components/ui/PasswordInput.svelte"; - import tex_hearty from "../assets/hearty.svg"; - import tex_hub from "../assets/hub_off.svg"; let config = DEFAULT_CONFIG; // Top-level await. Sets the default config, and overwrites it when the new config is avail. Thanks reactivity! @@ -23,29 +22,290 @@ `Hello, world! StarKingdoms ${APP_VERSION} (${COMMIT_HASH}) at your service!`, ); logger("Current view: ShipEditor.svelte"); + + let canvas: HTMLCanvasElement; + + let x = window.innerWidth / 2 - 24; + let y = window.innerHeight / 2 - 24; + + let grid_x = 0; + let grid_y = 0; + + let scale = 1.0; + + $: grid_size = 48 * scale; + + function handleWheel(e: WheelEvent) { + let delta = e.shiftKey ? 0.01 : 0.1; + if (e.deltaY < 0) { + scale += delta; + } else { + scale -= delta; + } + if (scale > 5) { + scale = 5; + } else if (scale < 0.1) { + scale = 0.1; + } + } + + let isdragging = false; + + let last_cx = -1; + let last_cy = -1; + + function mousedown(e: MouseEvent) { + isdragging = true; + last_cx = e.clientX; + last_cy = e.clientY; + } + function mouseup(e) { + isdragging = false; + } + + function mousemove(e: MouseEvent) { + if (isdragging) { + if (last_cx != -1) { + x += e.clientX - last_cx; + } + last_cx = e.clientX; + if (last_cy != -1) { + y += e.clientY - last_cy; + } + last_cy = e.clientY; + } else { + grid_x = Math.round((e.clientX - x) / grid_size - 0.5); + grid_y = Math.round((e.clientY - y) / grid_size - 0.5); + } + } + + function goto(nx: number, ny: number) { + x = nx + (window.innerWidth / 2 - 24); + y = ny + (window.innerHeight / 2 - 24); + } + + if (window.localStorage.getItem("save") === null) { + window.location.href = "/"; + } + + let save = unpack_save(window.localStorage.getItem("save")!); + console.log(save); + + let grid: Map> = new Map(); + + function placePart(x: number, y: number, part: PartType) { + if (!grid.has(x)) { + grid.set(x, new Map()); + } + + grid.get(x)?.set(y, part); + } + + let textures: Map = new Map(); + let context: CanvasRenderingContext2D | null = null; + + function render( + ctx: CanvasRenderingContext2D | null, + x: number, + y: number, + scale: number, + grid: Map>, + grid_x: number, + grid_y: number, + ) { + if (ctx === null) { + return; + } + + // clear out the canvas + ctx.clearRect(0, 0, canvas.width, canvas.height); + + grid.forEach((row, x_coord) => { + row.forEach(async (part_type, y_coord) => { + // draw the image + if (!textures.has(part_type)) { + let promise = new Promise((resolve, reject) => { + let img = new Image(grid_size, grid_size); + img.onload = () => resolve(img); + img.onerror = reject; + img.src = part_texture_url(part_type, true); + }); + + let img = await promise; + + textures.set(part_type, img); + } + + let img = textures.get(part_type)!; + + let canvas_x = x_coord * grid_size + x; + let canvas_y = y_coord * grid_size + y; + + ctx.drawImage(img, canvas_x, canvas_y, grid_size, grid_size); + }); + }); + + ctx.beginPath(); + ctx.strokeStyle = "rgb(166, 227, 161)"; + ctx.lineWidth = 2; + ctx.strokeRect( + grid_x * grid_size + x, + grid_y * grid_size + y, + grid_size, + grid_size, + ); + ctx.stroke(); + } + + $: { + render(context, x, y, scale, grid, grid_x, grid_y); + } + + onMount(() => { + canvas.width = window.innerWidth; + canvas.height = window.innerHeight; + context = canvas.getContext("2d"); + render(context, x, y, scale, grid, grid_x, grid_y); + }); + + window.onresize = () => { + canvas.width = window.innerWidth; + canvas.height = window.innerHeight; + }; + + let confirm_reload_save = false; + let confirm_save = false; + + let part_counts: Map = + new Map(); + + function place(x: number, y: number, data: any) { + if (data === null) { + return; + } + + let [type, children] = data; + + placePart(x, y, type); + + let existing_part_count = + part_counts.get(type) === undefined + ? { used: 0, available: 0 } + : part_counts.get(type)!; + + part_counts.set(type, { + used: existing_part_count.used + 1, + available: existing_part_count.available + 1, + }); + + if (children[0] !== null) { + place(x, y + 1, children[0]); + } + if (children[1] !== null) { + place(x + 1, y, children[1]); + } + if (children[2] !== null) { + place(x, y - 1, children[2]); + } + if (children[3] !== null) { + place(x - 1, y, children[3]); + } + } + + function load_save_data(save: any) { + grid = new Map(); + place(0, 0, [PartType.Hearty, save[0]]); + } + + $: { + load_save_data(save); + } + + function reload_btn() { + if (confirm_reload_save) { + save = unpack_save(window.localStorage.getItem("save")!); + } + confirm_reload_save = !confirm_reload_save; + setTimeout(() => { + confirm_reload_save = false; + }, 5000); + } + + function save_btn() { + if (confirm_save) { + // todo: @ghostly you need to turn this back into a savefile and then call pack_partial on it + window.location.href = "/"; + } + confirm_save = !confirm_save; + setTimeout(() => { + confirm_save = false; + }, 5000); + } - -
    -
  • - -
  • -
  • - - 2 - - -
  • -
-
- + + {#each part_counts.entries() as [type, counts]} + {#if type !== PartType.Hearty} +
+ {type} + {type} - {counts.used} used of {counts.available} +
+ {/if} + {/each} +
+ + + + + + + + + + + ({grid_x}, {grid_y}) {scale.toFixed(2)}x - StarKingdoms Client {APP_VERSION} ({COMMIT_HASH}) @@ -53,3 +313,30 @@ Made with by the StarKingdoms team + + diff --git a/starkingdoms-client/src/save.ts b/starkingdoms-client/src/save.ts new file mode 100644 index 0000000000000000000000000000000000000000..124dec6bdb70714f53a40825889a51985b7770a0 --- /dev/null +++ b/starkingdoms-client/src/save.ts @@ -0,0 +1,21 @@ +import { decode, encode } from "@msgpack/msgpack"; + +function base64ToBytes(base64: string) { + const binString = atob(base64); + // @ts-ignore + return Uint8Array.from(binString, (m) => m.codePointAt(0)); +} + +function bytesToBase64(bytes: any) { + const binString = String.fromCodePoint(...bytes); + return btoa(binString); +} + +export function unpack_save(data: string): any { + // @ts-ignore + return decode(decode(base64ToBytes(data))[0]); +} + +export function pack_partial(data: any): string { + return bytesToBase64(encode(data)); +} diff --git a/starkingdoms-client/yarn.lock b/starkingdoms-client/yarn.lock index bba40c729cfd2d4fbd0d8d0dd2655ac434a18306..04863912a05883bd279fba91b3cfa86fc86847d8 100644 --- a/starkingdoms-client/yarn.lock +++ b/starkingdoms-client/yarn.lock @@ -152,6 +152,11 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" +"@msgpack/msgpack@^3.0.0-beta2": + version "3.0.0-beta2" + resolved "https://registry.yarnpkg.com/@msgpack/msgpack/-/msgpack-3.0.0-beta2.tgz#5bccee30f84df220b33905e3d8249ba96deca0b7" + integrity sha512-y+l1PNV0XDyY8sM3YtuMLK5vE3/hkfId+Do8pLo/OPxfxuFAUwcGz3oiiUuV46/aBpwTzZ+mRWVMtlSKbradhw== + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"