friend's neko in scene page
This commit is contained in:
96
src/events/friend-active-doll.ts
Normal file
96
src/events/friend-active-doll.ts
Normal file
@@ -0,0 +1,96 @@
|
||||
import { writable, get } from "svelte/store";
|
||||
import { events, type DollDto } from "$lib/bindings";
|
||||
import { createEventSource, removeFromStore } from "./listener-utils";
|
||||
import { getSpriteSheetUrl } from "$lib/utils/sprite-utils";
|
||||
import { appData } from "./app-data";
|
||||
import onekoGif from "../assets/oneko/oneko.gif";
|
||||
|
||||
export const friendsActiveDollSprites = writable<Record<string, string>>(
|
||||
{},
|
||||
);
|
||||
|
||||
async function getFriendSpriteUrl(doll: DollDto): Promise<string> {
|
||||
if (!doll.configuration?.colorScheme) {
|
||||
return onekoGif;
|
||||
}
|
||||
|
||||
try {
|
||||
return await getSpriteSheetUrl(doll.configuration.colorScheme);
|
||||
} catch (e) {
|
||||
console.error("Failed to generate friend sprite:", e);
|
||||
return onekoGif;
|
||||
}
|
||||
}
|
||||
|
||||
async function syncFromAppData() {
|
||||
const data = get(appData);
|
||||
if (!data?.friends) return;
|
||||
|
||||
const currentSprites = get(friendsActiveDollSprites);
|
||||
const newSprites: Record<string, string> = {};
|
||||
|
||||
const friendUpdates = data.friends
|
||||
.filter((f) => f.friend?.activeDoll)
|
||||
.map(async (f) => {
|
||||
const friendId = f.friend!.id;
|
||||
if (currentSprites[friendId]) {
|
||||
newSprites[friendId] = currentSprites[friendId];
|
||||
} else {
|
||||
newSprites[friendId] = await getFriendSpriteUrl(f.friend!.activeDoll!);
|
||||
}
|
||||
});
|
||||
|
||||
await Promise.all(friendUpdates);
|
||||
|
||||
Object.keys(currentSprites).forEach((friendId) => {
|
||||
if (!newSprites[friendId] && data.friends) {
|
||||
const friend = data.friends.find(
|
||||
(f) => f.friend?.id === friendId && f.friend?.activeDoll,
|
||||
);
|
||||
if (friend) {
|
||||
getFriendSpriteUrl(friend.friend!.activeDoll!).then((spriteUrl) => {
|
||||
friendsActiveDollSprites.update((current) => ({
|
||||
...current,
|
||||
[friendId]: spriteUrl,
|
||||
}));
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
friendsActiveDollSprites.set(newSprites);
|
||||
}
|
||||
|
||||
export const {
|
||||
start: startFriendActiveDollTracking,
|
||||
stop: stopFriendActiveDollTracking,
|
||||
} = createEventSource(async (addEventListener) => {
|
||||
await syncFromAppData();
|
||||
|
||||
addEventListener(
|
||||
await events.friendActiveDollChanged.listen(async (event) => {
|
||||
const { friendId, doll } = event.payload;
|
||||
|
||||
if (doll) {
|
||||
const spriteUrl = await getFriendSpriteUrl(doll);
|
||||
friendsActiveDollSprites.update((current) => ({
|
||||
...current,
|
||||
[friendId]: spriteUrl,
|
||||
}));
|
||||
} else {
|
||||
friendsActiveDollSprites.update((current) =>
|
||||
removeFromStore(current, friendId),
|
||||
);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
addEventListener(
|
||||
await events.friendDisconnected.listen((event) => {
|
||||
const { userId } = event.payload;
|
||||
friendsActiveDollSprites.update((current) =>
|
||||
removeFromStore(current, userId),
|
||||
);
|
||||
}),
|
||||
);
|
||||
});
|
||||
@@ -17,6 +17,10 @@
|
||||
stopSceneInteractive,
|
||||
} from "../events/scene-interactive";
|
||||
import { startUserStatus, stopUserStatus } from "../events/user-status";
|
||||
import {
|
||||
startFriendActiveDollTracking,
|
||||
stopFriendActiveDollTracking,
|
||||
} from "../events/friend-active-doll";
|
||||
|
||||
let { children } = $props();
|
||||
if (browser) {
|
||||
@@ -29,6 +33,7 @@
|
||||
await startSceneInteractive();
|
||||
await startInteraction();
|
||||
await startUserStatus();
|
||||
await startFriendActiveDollTracking();
|
||||
} catch (err) {
|
||||
console.error("Failed to initialize event listeners:", err);
|
||||
}
|
||||
@@ -41,6 +46,7 @@
|
||||
stopSceneInteractive();
|
||||
stopInteraction();
|
||||
stopUserStatus();
|
||||
stopFriendActiveDollTracking();
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { friendsCursorPositions } from "../../events/friend-cursor";
|
||||
import { appData } from "../../events/app-data";
|
||||
import { activeDollSpriteUrl } from "../../events/active-doll-sprite";
|
||||
import { friendsActiveDollSprites } from "../../events/friend-active-doll";
|
||||
import { sceneInteractive } from "../../events/scene-interactive";
|
||||
import {
|
||||
friendsPresenceStates,
|
||||
@@ -26,6 +27,15 @@
|
||||
targetY={$cursorPositionOnScreen.raw.y}
|
||||
spriteUrl={$activeDollSpriteUrl}
|
||||
/>
|
||||
{#each Object.entries($friendsCursorPositions) as [friendId, position]}
|
||||
{#if $friendsActiveDollSprites[friendId]}
|
||||
<Neko
|
||||
targetX={position.raw.x}
|
||||
targetY={position.raw.y}
|
||||
spriteUrl={$friendsActiveDollSprites[friendId]}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
<div id="debug-bar">
|
||||
<DebugBar
|
||||
isInteractive={$sceneInteractive}
|
||||
|
||||
Reference in New Issue
Block a user