diff --git a/src/lib/player/Player.svelte b/src/lib/player/Player.svelte index 472029c..cc46c7e 100644 --- a/src/lib/player/Player.svelte +++ b/src/lib/player/Player.svelte @@ -22,7 +22,7 @@ height?: number; speed?: number; runningSpeed?: number; - playerLocation?: PlayerLocation; + playerLocation?: PlayerLocation | null; } = $props(); let isRunning = false; @@ -138,12 +138,17 @@ }); async function updatePosition() { - if (!playerLocation) return; + if (!playerLocation) { + console.log('no player location'); + return; + } - console.log('update location'); + console.log('update location', editingState.selectedCharacter); playerLocation.x = position[0]; playerLocation.y = position[1]; playerLocation.z = position[2]; + playerLocation.rotation = rotation; + playerLocation.model = editingState.selectedCharacter ?? 'female b'; playerLocation.time = Date.now(); playerLocation.commit(); } diff --git a/src/lib/roomy/components.ts b/src/lib/roomy/components.ts index c475b36..2b9a94a 100644 --- a/src/lib/roomy/components.ts +++ b/src/lib/roomy/components.ts @@ -275,7 +275,7 @@ export const WorldComponent = defComponent( export const PlayerLocationsComponent = defComponent( 'player:02JPFMTA2QAFY2D3CV1C0YBZQ1', - LoroMovableList + LoroList ); export const PlayerLocationComponent = defComponent( @@ -285,41 +285,44 @@ export const PlayerLocationComponent = defComponent( y: number; z: number; + model: string; + rotation: number; + time: number; }> ); -// export const WorldSettingsComponent = defComponent( -// 'worldSettings:02JPFMTA2QAFY2D3CV1C0YBZQ1', -// LoroMap<{ -// /** The JSON encoded content from the rich text editor. */ -// settings: string; -// }>, -// (map) => map.set('settings', '{}') -// ); - -export const GradientStopComponent = defComponent( - 'gradientStop:02JPFMTA2QAFY2D3CV1C0YBZQ1', +export const WorldSettingsComponent = defComponent( + 'worldSettings:02JPFMTA2QAFY2D3CV1C0YBZQ1', LoroMap<{ - r: number; - g: number; - b: number; - position: number; - }> + /** The JSON encoded content from the rich text editor. */ + settings: string; + }>, + (map) => map.set('settings', '{}') ); -export const GradientComponent = defComponent( - 'gradient:02JPFMTA2QAFY2D3CV1C0YBZQ1', - LoroMovableList -); +// export const GradientStopComponent = defComponent( +// 'gradientStop:02JPFMTA2QAFY2D3CV1C0YBZQ1', +// LoroMap<{ +// r: number; +// g: number; +// b: number; +// position: number; +// }> +// ); -export const WorldSettingsComponent = defComponent( - 'worldSettings:02JPFMTA2QAFY2D3CV1C0YBZQ1', - LoroMap<{ - seed: string; - size: number; - terrainGradient: EntityIdStr; - waterGradient: EntityIdStr; - waterPercentage: number; - }> -); \ No newline at end of file +// export const GradientComponent = defComponent( +// 'gradient:02JPFMTA2QAFY2D3CV1C0YBZQ1', +// LoroMovableList +// ); + +// export const WorldSettingsComponent = defComponent( +// 'worldSettings:02JPFMTA2QAFY2D3CV1C0YBZQ1', +// LoroMap<{ +// seed: string; +// size: number; +// terrainGradient: EntityIdStr; +// waterGradient: EntityIdStr; +// waterPercentage: number; +// }> +// ); \ No newline at end of file diff --git a/src/lib/roomy/index.ts b/src/lib/roomy/index.ts index 760a840..76ed24e 100644 --- a/src/lib/roomy/index.ts +++ b/src/lib/roomy/index.ts @@ -974,28 +974,30 @@ export class PlayerLocation extends EntityWrapper { set z(value: number) { this.entity.getOrInit(c.PlayerLocationComponent, (entity) => entity.set('z', value)); } -} -// export type WorldSettings = { -// seed: string; -// size: number; -// terrainGradient: { -// rgb: { r: number; g: number; b: number }; -// position: number; -// }[]; -// waterGradient: { -// rgb: { r: number; g: number; b: number }; -// position: number; -// }[]; -// version?: number; -// }; + get rotation() { + return this.entity.getOrInit(c.PlayerLocationComponent, (entity) => entity.get('rotation')); + } + + set rotation(value: number) { + this.entity.getOrInit(c.PlayerLocationComponent, (entity) => entity.set('rotation', value)); + } + + get model() { + return this.entity.getOrInit(c.PlayerLocationComponent, (entity) => entity.get('model')); + } + + set model(value: string) { + this.entity.getOrInit(c.PlayerLocationComponent, (entity) => entity.set('model', value)); + } +} export class World extends NamedEntity { get instances(): EntityList { return new EntityList(this.peer, this.entity, c.WorldComponent, TransformedGroup); } - get locations(): EntityList { + get players(): EntityList { return new EntityList(this.peer, this.entity, c.PlayerLocationsComponent, PlayerLocation); } @@ -1010,62 +1012,6 @@ export class World extends NamedEntity { entity.set('settings', JSON.stringify(value)) ); } - - get terrainGradient(): EntityIdStr { - return this.entity.getOrInit(c.WorldSettingsComponent, (entity) => - entity.get('terrainGradient') - ); - } - - async loadTerrainGradient(): Promise { - return await this.open(Gradient, this.terrainGradient); - } - - get seed(): string { - return this.entity.getOrInit(c.WorldSettingsComponent, (entity) => entity.get('seed')); - } - - set seed(value: string) { - this.entity.getOrInit(c.WorldSettingsComponent, (entity) => entity.set('seed', value)); - } -} - -export class GradientStop extends EntityWrapper { - get r() { - return this.entity.getOrInit(c.GradientStopComponent, (entity) => entity.get('r')); - } - - set r(value: number) { - this.entity.getOrInit(c.GradientStopComponent, (entity) => entity.set('r', value)); - } - - get g() { - return this.entity.getOrInit(c.GradientStopComponent, (entity) => entity.get('g')); - } - set g(value: number) { - this.entity.getOrInit(c.GradientStopComponent, (entity) => entity.set('g', value)); - } - - get b() { - return this.entity.getOrInit(c.GradientStopComponent, (entity) => entity.get('b')); - } - set b(value: number) { - this.entity.getOrInit(c.GradientStopComponent, (entity) => entity.set('b', value)); - } - - get position() { - return this.entity.getOrInit(c.GradientStopComponent, (entity) => entity.get('position')); - } - - set position(value: number) { - this.entity.getOrInit(c.GradientStopComponent, (entity) => entity.set('position', value)); - } -} - -export class Gradient extends EntityWrapper { - get stops(): EntityList { - return new EntityList(this.peer, this.entity, c.GradientComponent, GradientStop); - } } export type WorldSettings = { diff --git a/src/lib/world-editor/Player.svelte b/src/lib/world-editor/Player.svelte new file mode 100644 index 0000000..537b77e --- /dev/null +++ b/src/lib/world-editor/Player.svelte @@ -0,0 +1,70 @@ + + +{#if timeDist < 5000 && player.id !== playerId} + + {#key player.model} + + {/key} + +{/if} diff --git a/src/lib/world-editor/Players.svelte b/src/lib/world-editor/Players.svelte new file mode 100644 index 0000000..4eef2b3 --- /dev/null +++ b/src/lib/world-editor/Players.svelte @@ -0,0 +1,22 @@ + + +{#each players.value as player} + +{/each} diff --git a/src/lib/world-editor/WorldEditorScene.svelte b/src/lib/world-editor/WorldEditorScene.svelte index 79523ab..172c820 100644 --- a/src/lib/world-editor/WorldEditorScene.svelte +++ b/src/lib/world-editor/WorldEditorScene.svelte @@ -5,13 +5,14 @@ import Player from '$lib/player/Player.svelte'; import { onMount } from 'svelte'; import Instance from './Instance.svelte'; - import { addInstance, editingState } from './state.svelte'; + import { addInstance, editingState, getPlayerLocation } from './state.svelte'; import { ACESFilmicToneMapping } from 'three'; import Water from '$lib/world/Water.svelte'; import HudScene from './HudScene.svelte'; import { Debug } from '@threlte/rapier'; import { derivePromise } from '$lib/utils.svelte'; import { g } from '$lib/roomy.svelte'; + import Players from './Players.svelte'; interactivity({ filter: (hits) => { @@ -21,15 +22,13 @@ let instances = derivePromise([], async () => (g.world ? await g.world.instances.items() : [])); - let terrainGradient = derivePromise([], async () => - g.world ? (await g.world.loadTerrainGradient()).stops.items() : [] - ); + const { renderer } = useThrelte(); - $inspect(terrainGradient.value); + let playerLocation = derivePromise(null, async () => await getPlayerLocation()); - const { renderer } = useThrelte(); + $inspect(playerLocation); - onMount(() => { + onMount(async () => { window.addEventListener('keydown', (e) => { // on c switch camera if (e.key === 'c') { @@ -38,6 +37,7 @@ }); renderer.toneMapping = ACESFilmicToneMapping; + }); $effect(() => { @@ -59,7 +59,7 @@ {:else} - + {/if} @@ -87,6 +87,8 @@ {/each} + + diff --git a/src/lib/world-editor/state.svelte.ts b/src/lib/world-editor/state.svelte.ts index bba85fb..62f5f88 100644 --- a/src/lib/world-editor/state.svelte.ts +++ b/src/lib/world-editor/state.svelte.ts @@ -1,4 +1,4 @@ -import { TransformedGroup, type WorldSettings } from '$lib/roomy'; +import { PlayerLocation, TransformedGroup, type WorldSettings } from '$lib/roomy'; import { g, initRoomy } from '$lib/roomy.svelte'; import type { EntityIdStr } from '@muni-town/leaf'; import { Quaternion, Vector3 } from 'three'; @@ -105,3 +105,73 @@ export async function deleteInstance(id: string) { g.world?.instances.remove(instance); g.world?.commit(); } + +async function createPlayerLocation() { + console.log('createPlayerLocation'); + if (!g.roomy) { + await initRoomy(); + + if (!g.roomy) return; + } + + const player = await g.roomy.create(PlayerLocation); + + player.x = 0; + player.y = 0; + player.z = 0; + player.rotation = 0; + player.model = editingState.selectedCharacter ?? 'male f'; + player.time = Date.now(); + player.commit(); + + g.world?.players.push(player); + g.world?.commit(); + + localStorage.setItem('playerId', player.id); + return player; +} + +export async function getPlayerLocation() { + // get player location + const players = await g.world?.players.items(); + if (!players) return; + + // get saved id + const savedId = localStorage.getItem('playerId'); + console.log('saved id', savedId); + if (!savedId) { + return createPlayerLocation(); + } + + // find player by id + const player = players?.find((p) => p.id === savedId); + + if (!player) { + console.log('player id not found', players); + return createPlayerLocation(); + } + + console.log('reusing player location', player); + + return player; +} + +export async function removeOldPlayerLocations() { + // remove a random player location that is more than 10 seconds old + const players = await g.world?.players.items(); + if (!players) return; + + const now = Date.now(); + const tenSecondsAgo = now - 10000; + + // remove a random player location that is more than 10 seconds old + // first filter all by time + const oldPlayers = players.filter((p) => p.time && p.time < tenSecondsAgo); + if (oldPlayers.length === 0) return; + + // remove a random one + const randomIndex = Math.floor(Math.random() * oldPlayers.length); + + g.world?.players.remove(randomIndex); + g.world?.commit(); +} diff --git a/src/lib/world/Terrain.svelte b/src/lib/world/Terrain.svelte index eedb074..93c3a9d 100644 --- a/src/lib/world/Terrain.svelte +++ b/src/lib/world/Terrain.svelte @@ -41,9 +41,6 @@ instancedMesh.receiveShadow = true; const dummy = new THREE.Object3D(); - const color = new THREE.Color(0x00ff00); - - console.log('seed', settings.seed); const heightfield: number[] = []; const options = settings.terrainNoise ?? { @@ -93,7 +90,6 @@ dummy.position.set(x, height, z); dummy.scale.set(1, 3, 1); dummy.updateMatrix(); - //color.setRGB(0, 0.15 * noise + 0.2, 0); const color = gradient.get(noise);