From d53d5885c021e9653c799154eaa88fdf799b7579 Mon Sep 17 00:00:00 2001 From: bridge Date: Sun, 1 Feb 2026 12:20:27 +0800 Subject: [PATCH] feat: add active domains serialization and UI integration - Implemented `serialize_active_domains` function to gather and format active hidden domains from the game world. - Updated `game_loop` to include active domains in the broadcast state. - Enhanced `StatusBar` component to display active domains with a new `StatusWidget`, including dynamic labels and colors based on the number of active domains. - Added localization support for hidden domain messages in both English and Chinese. - Updated the world store to manage active domains state and synchronize with the backend. --- src/server/main.py | 26 ++++- web/src/components/layout/StatusBar.vue | 80 +++++++--------- web/src/components/layout/StatusWidget.vue | 106 +++++++++++++++++++++ web/src/locales/en-US.json | 10 +- web/src/locales/zh-CN.json | 10 +- web/src/stores/world.ts | 17 +++- web/src/types/api.ts | 3 +- web/src/types/core.ts | 12 +++ 8 files changed, 213 insertions(+), 51 deletions(-) create mode 100644 web/src/components/layout/StatusWidget.vue diff --git a/src/server/main.py b/src/server/main.py index 51da8de..c499a35 100644 --- a/src/server/main.py +++ b/src/server/main.py @@ -172,6 +172,29 @@ class ConnectionManager: manager = ConnectionManager() +def serialize_active_domains(world: World) -> List[dict]: + """序列化当前开启的秘境列表""" + domains_data = [] + if not world or not world.gathering_manager: + return [] + + for gathering in world.gathering_manager.gatherings: + # Check by class name to avoid circular imports + if gathering.__class__.__name__ == "HiddenDomain": + # Accessing _active_domains safely + active_domains = getattr(gathering, "_active_domains", []) + for d in active_domains: + domains_data.append({ + "id": d.id, + "name": d.name, + "desc": d.desc, + # Use str() to trigger Realm.__str__ which returns translated text + "max_realm": str(d.max_realm), + "danger_prob": d.danger_prob, + "drop_prob": d.drop_prob + }) + return domains_data + def serialize_events_for_client(events: List[Event]) -> List[dict]: """将事件转换为前端可用的结构。""" serialized: List[dict] = [] @@ -553,7 +576,8 @@ async def game_loop(): "month": world.month_stamp.get_month().value, "events": serialize_events_for_client(events), "avatars": avatar_updates, - "phenomenon": serialize_phenomenon(world.current_phenomenon) + "phenomenon": serialize_phenomenon(world.current_phenomenon), + "active_domains": serialize_active_domains(world) } await manager.broadcast(state) except Exception as e: diff --git a/web/src/components/layout/StatusBar.vue b/web/src/components/layout/StatusBar.vue index 97f1128..4b4870f 100644 --- a/web/src/components/layout/StatusBar.vue +++ b/web/src/components/layout/StatusBar.vue @@ -4,6 +4,7 @@ import { useSocketStore } from '../../stores/socket' import { ref, computed } from 'vue' import { NPopover, NModal, NList, NListItem, NTag, NEmpty, useMessage } from 'naive-ui' import { useI18n } from 'vue-i18n' +import StatusWidget from './StatusWidget.vue' const { t } = useI18n() const store = useWorldStore() @@ -17,6 +18,17 @@ const phenomenonColor = computed(() => { return getRarityColor(p.rarity); }) +const domainLabel = computed(() => { + const count = store.activeDomains.length; + return count > 0 + ? t('game.status_bar.hidden_domain.label_active', { count }) + : t('game.status_bar.hidden_domain.label'); +}); + +const domainColor = computed(() => { + return store.activeDomains.length > 0 ? '#fa8c16' : '#666'; // 有秘境时亮橙色,否则灰色 +}); + function getRarityColor(rarity: string) { switch (rarity) { case 'N': return '#ccc'; @@ -26,17 +38,8 @@ function getRarityColor(rarity: string) { default: return '#ccc'; } } - -async function openPhenomenonSelector() { - showSelector.value = true; - await store.getPhenomenaList(); -} - -async function handleSelect(id: number, name: string) { - await store.changePhenomenon(id); - showSelector.value = false; - message.success(t('game.status_bar.change_success', { name })); -} +// ... +// ...