<script lang="ts"> // storesLoaded refers to the window.stores object existing, not svelte stores import Group from "../../hud/Group.svelte"; import Button from "../../hud/components/Button.svelte"; import Slider from "../../hud/components/Slider.svelte"; import { serializer } from "../../network/schemaDecode"; import { storesLoaded, playerId } from "../../stores"; import { getUnsafeWindow } from "../../utils"; let keys = new Set(); let select: HTMLSelectElement; let freecamming = false; function onKeydown(event: KeyboardEvent) { if(!event.key.startsWith("Arrow")) return; // Prevent arrow keys from moving the character if(freecamming) { event.stopImmediatePropagation(); } keys.add(event.key); } function onKeyup(event: KeyboardEvent) { keys.delete(event.key); } let freecamPos = { x: 0, y: 0 }; let freecamUpdateInterval: number; let zoomValue = 1; function startFreecam() { let scene = getUnsafeWindow().stores.phaser.scene; let camera = scene.cameras.cameras[0]; scene.cameraHelper.stopFollow(); freecamPos = { x: camera.midPoint.x, y: camera.midPoint.y }; camera.useBounds = false; freecamUpdateInterval = setInterval(() => { let freecamSpeed = 20 / zoomValue * 1.75; if(keys.has("ArrowUp")) freecamPos.y -= freecamSpeed if(keys.has("ArrowDown")) freecamPos.y += freecamSpeed if(keys.has("ArrowLeft")) freecamPos.x -= freecamSpeed if(keys.has("ArrowRight")) freecamPos.x += freecamSpeed scene.cameraHelper.goTo(freecamPos) }, 1000 / 30) as any; } function stopFreecam() { let phaser = getUnsafeWindow().stores.phaser; let charObj = phaser.scene.characterManager.characters .get(phaser.mainCharacter.id).body phaser.scene.cameraHelper.startFollowingObject({ object: charObj }); phaser.scene.cameras.cameras[0].useBounds = true; clearInterval(freecamUpdateInterval); } storesLoaded.subscribe(loaded => { if(!loaded) return; let getZoomInterval = setInterval(() => { let zoom = getUnsafeWindow()?.stores?.phaser?.scene?.cameras?.cameras[0]?.zoom; if(zoom) { zoomValue = zoom; clearInterval(getZoomInterval); } }, 50) }) function setZoom(event: CustomEvent<number>) { zoomValue = event.detail; getUnsafeWindow().stores.phaser.scene .cameras.cameras[0].setZoom(event.detail); } let specCharacters = []; let unbinds = []; playerId.subscribe(id => { specCharacters = specCharacters.filter(char => char.id !== id); }) serializer.addEventListener("load", () => { const reloadCharacters = () => { specCharacters = []; for(let unbind of unbinds) unbind(); for(let [id, char] of serializer.state.characters.$items) { if(!char.isActive || id === $playerId) continue; specCharacters.push({ id, name: char.name }) let unbind = char.listen("isActive", (_: boolean, prevVal: boolean | undefined) => { if(prevVal === undefined) return; reloadCharacters(); }); unbinds.push(unbind); } } serializer.state.characters.onChange(reloadCharacters); reloadCharacters(); }) let spectating = false; function onBtnClick() { if(spectating) { spectating = false; stopFreecam(); select.value = ""; } else { freecamming = !freecamming; if(freecamming) startFreecam(); else stopFreecam(); } } function selectPlayer() { if(!select.value) return; spectating = true; freecamming = false; stopFreecam(); let char = getUnsafeWindow().stores.phaser.scene.characterManager.characters.get(select.value); if(!char) return; let camHelper = getUnsafeWindow().stores.phaser.scene.cameraHelper; camHelper.startFollowingObject({ object: char.body }); } </script> <svelte:window on:keydown={onKeydown} on:keyup={onKeyup} /> <Group name="Freecam"> <Button disabled={!$storesLoaded} disabledMsg="Camera hasn't loaded" on:click={onBtnClick}> {#if spectating} Stop Spectating {:else} {freecamming ? "Stop" : "Start"} Freecam {/if} </Button> <Slider title="Camera Zoom" min={0.1} max={5} step={0.1} on:input={setZoom} disabled={!$storesLoaded} value={zoomValue} disabledMsg="Camera hasn't loaded" /> <select disabled={specCharacters.length === 0} on:change={selectPlayer} bind:this={select} on:keydown|preventDefault title={specCharacters.length === 0 ? "No characters to spectate" : undefined}> <option value="" selected>Pick a player to spectate</option> {#each specCharacters as char} <option value={char.id}>{char.name}</option> {/each} </select> </Group> <style> select { width: calc(100% - 10px); padding: 5px; margin-left: 5px; margin-right: 5px; color: black; } select[disabled] { cursor: not-allowed; } </style>