feat: add common social fireplace

This commit is contained in:
2026-09-05 18:20:49 +05:00
parent 32527ec2ed
commit ca830b00b9
11 changed files with 539 additions and 121 deletions
+65 -1
View File
@@ -1,8 +1,10 @@
<script lang="ts">
import { onMount } from 'svelte';
import {
CAMPFIRE,
SKILL_TITLES,
itemTitle,
type CampfireState,
type GameEvent,
type SkillState,
type ViewerStat,
@@ -56,6 +58,10 @@
return `${e.name} выработал(ла) жилу`;
case 'rest':
return `🔥 ${e.name} отдыхает у костра — бафф ×${e.stacks}`;
case 'fire':
return `🪵 ${e.name} подкинул(ла) дров — костёр ${e.progress} (вклад ${e.contributed})`;
case 'firelevel':
return `🔥 Костёр достиг ${e.level}-го уровня!`;
case 'blocked':
return `${e.name}: ${e.reason}`;
}
@@ -69,9 +75,18 @@
return Math.max(1, ...Object.values(s.skills).map((v) => v.level));
}
function firePct(cs: CampfireState): number {
const thr = CAMPFIRE.levelThresholds;
if (cs.level >= thr.length) return 100;
const lo = thr[cs.level - 1] ?? 0;
const hi = thr[cs.level] ?? 1;
return Math.min(100, Math.max(0, Math.round(((cs.progress - lo) / (hi - lo)) * 100)));
}
let host = $state<HTMLDivElement | undefined>(undefined);
let status = $state<NetStatus>('connecting');
let stats = $state<ViewerStat[]>([]);
let campfire = $state<CampfireState | null>(null);
let feed = $state<FeedItem[]>([]);
let feedId = 0;
@@ -97,7 +112,7 @@
let disposeNet: (() => void) | undefined;
if (host) {
void mountWorld(host, { background: 0x1d232d }).then((w) => {
void mountWorld(host, { background: 0x1d232d, onCampfire: (cf) => (campfire = cf) }).then((w) => {
world = w;
disposeNet = connectWorld({
onStatus: (s) => {
@@ -171,6 +186,33 @@
{/if}
</section>
<section>
<h2>Костёр</h2>
{#if campfire}
<div class="fire-line">
<span class="k">Уровень {campfire.level}</span>
<span class="meta">{campfire.progress} очков</span>
</div>
<div class="fire-bar"><div class="fire-fill" style:width={`${firePct(campfire)}%`}></div></div>
{#if campfire.top.length > 0}
<p class="muted" style="margin-top:8px">Вкладчики:</p>
<ul>
{#each campfire.top as c (c.name)}
<li>
<span class="dot" style:background={c.color}></span>
<span class="name" style:color={c.color}>{c.name}</span>
<span class="meta">{c.contributed}</span>
</li>
{/each}
</ul>
{:else}
<p class="muted">никто ещё не подкинул дров — !костёр</p>
{/if}
{:else}
<p class="muted">костёр ещё не разожгли — !костёр</p>
{/if}
</section>
<section>
<h2>События</h2>
{#if feed.length === 0}
@@ -291,6 +333,28 @@
.inv .k {
color: #c7d3e0;
}
.fire-line {
display: flex;
align-items: baseline;
gap: 8px;
margin-bottom: 6px;
}
.fire-line .k {
color: #ffc46b;
font-weight: 600;
}
.fire-bar {
height: 8px;
background: #232b38;
border-radius: 4px;
overflow: hidden;
}
.fire-fill {
height: 100%;
background: linear-gradient(90deg, #ff8c3b, #ffd166);
border-radius: 4px;
transition: width 0.6s ease;
}
.feed li {
display: block;
font-size: 12.5px;