-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
-
-
角色列表 (Debug)
-
- -
- {{ av.name || 'Unknown' }} [{{ av.x }}, {{ av.y }}] - {{ av.action }}
-
-
+
+
-
-
+
+
-
diff --git a/web/src/components/game/EntityLayer.vue b/web/src/components/game/EntityLayer.vue
new file mode 100644
index 0000000..6dfbef7
--- /dev/null
+++ b/web/src/components/game/EntityLayer.vue
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+ drawFallback(g, avatar)"
+ />
+
+
+
+
+
+
+
diff --git a/web/src/components/game/GameCanvas.vue b/web/src/components/game/GameCanvas.vue
new file mode 100644
index 0000000..4fc5706
--- /dev/null
+++ b/web/src/components/game/GameCanvas.vue
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
diff --git a/web/src/components/game/MapLayer.vue b/web/src/components/game/MapLayer.vue
new file mode 100644
index 0000000..b3ae348
--- /dev/null
+++ b/web/src/components/game/MapLayer.vue
@@ -0,0 +1,103 @@
+
+
+
+
+
+
+
+
+
diff --git a/web/src/components/game/Viewport.vue b/web/src/components/game/Viewport.vue
new file mode 100644
index 0000000..aae58c6
--- /dev/null
+++ b/web/src/components/game/Viewport.vue
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
diff --git a/web/src/components/game/composables/useTextures.ts b/web/src/components/game/composables/useTextures.ts
new file mode 100644
index 0000000..44e0e4c
--- /dev/null
+++ b/web/src/components/game/composables/useTextures.ts
@@ -0,0 +1,64 @@
+import { ref } from 'vue'
+import { Assets, Texture } from 'pixi.js'
+
+// 全局纹理缓存,避免重复加载
+const textures = ref
>({})
+const isLoaded = ref(false)
+
+export function useTextures() {
+ const loadTextures = async () => {
+ if (isLoaded.value) return
+
+ const manifest: Record = {
+ 'PLAIN': '/assets/tiles/plain.png',
+ 'WATER': '/assets/tiles/water.png',
+ 'SEA': '/assets/tiles/sea.png',
+ 'MOUNTAIN': '/assets/tiles/mountain.png',
+ 'FOREST': '/assets/tiles/forest.png',
+ 'CITY': '/assets/tiles/city.png',
+ 'DESERT': '/assets/tiles/desert.png',
+ 'RAINFOREST': '/assets/tiles/rainforest.png',
+ 'GLACIER': '/assets/tiles/glacier.png',
+ 'SNOW_MOUNTAIN': '/assets/tiles/snow_mountain.png',
+ 'VOLCANO': '/assets/tiles/volcano.png',
+ 'GRASSLAND': '/assets/tiles/grassland.png',
+ 'SWAMP': '/assets/tiles/swamp.png',
+ 'CAVE': '/assets/tiles/cave.png',
+ 'RUINS': '/assets/tiles/ruins.png',
+ 'FARM': '/assets/tiles/farm.png'
+ }
+
+ // 加载地图纹理
+ for (const [key, url] of Object.entries(manifest)) {
+ try {
+ textures.value[key] = await Assets.load(url)
+ } catch (e) {
+ console.error(`Failed to load texture: ${url}`, e)
+ }
+ }
+
+ // 加载角色立绘 (1-16)
+ for (let i = 1; i <= 16; i++) {
+ const maleUrl = `/assets/males/${i}.png`
+ const femaleUrl = `/assets/females/${i}.png`
+
+ try {
+ textures.value[`male_${i}`] = await Assets.load(maleUrl)
+ } catch (e) { /* ignore */ }
+
+ try {
+ textures.value[`female_${i}`] = await Assets.load(femaleUrl)
+ } catch (e) { /* ignore */ }
+ }
+
+ isLoaded.value = true
+ console.log('Textures loaded')
+ }
+
+ return {
+ textures,
+ isLoaded,
+ loadTextures
+ }
+}
+
diff --git a/web/src/counter.ts b/web/src/counter.ts
deleted file mode 100644
index 09e5afd..0000000
--- a/web/src/counter.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-export function setupCounter(element: HTMLButtonElement) {
- let counter = 0
- const setCounter = (count: number) => {
- counter = count
- element.innerHTML = `count is ${counter}`
- }
- element.addEventListener('click', () => setCounter(counter + 1))
- setCounter(0)
-}
diff --git a/web/src/stores/game.ts b/web/src/stores/game.ts
index 9e8b267..d1aef61 100644
--- a/web/src/stores/game.ts
+++ b/web/src/stores/game.ts
@@ -14,6 +14,7 @@ export const useGameStore = defineStore('game', () => {
const year = ref(0)
const month = ref(0)
const avatars = ref>({})
+ const events = ref([]) // 添加事件列表状态
// 计算属性:转换为数组以便遍历
const avatarList = computed(() => Object.values(avatars.value))
@@ -36,6 +37,12 @@ export const useGameStore = defineStore('game', () => {
year.value = data.year
month.value = data.month
+ // 更新事件日志
+ if (data.events && Array.isArray(data.events)) {
+ // 将新事件追加到开头
+ events.value = [...data.events, ...events.value].slice(0, 100) // 只保留最近100条
+ }
+
// 更新 Avatars(增量更新逻辑:这里后端暂发的是全量/部分列表,直接覆盖位置)
if (data.avatars && Array.isArray(data.avatars)) {
data.avatars.forEach((av: Avatar) => {
@@ -87,6 +94,7 @@ export const useGameStore = defineStore('game', () => {
month,
avatars,
avatarList,
+ events, // 导出 events
connect,
fetchInitialState
}
diff --git a/web/src/style.css b/web/src/style.css
index 3bcdbd0..9ac1a15 100644
--- a/web/src/style.css
+++ b/web/src/style.css
@@ -1,96 +1,44 @@
:root {
- font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
- line-height: 1.5;
+ font-family: "HarmonyOS Sans", "PingFang SC", "Microsoft YaHei", system-ui,
+ -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
+ line-height: 1.4;
font-weight: 400;
-
- color-scheme: light dark;
- color: rgba(255, 255, 255, 0.87);
- background-color: #242424;
-
+ color: #f6f6f6;
+ background-color: #050608;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
-a {
- font-weight: 500;
- color: #646cff;
- text-decoration: inherit;
+*,
+*::before,
+*::after {
+ box-sizing: border-box;
}
-a:hover {
- color: #535bf2;
+
+html,
+body {
+ width: 100%;
+ height: 100%;
+ margin: 0;
+ padding: 0;
+ background-color: #050608;
+ color: #f6f6f6;
}
body {
- margin: 0;
- display: flex;
- place-items: center;
- min-width: 320px;
- min-height: 100vh;
-}
-
-h1 {
- font-size: 3.2em;
- line-height: 1.1;
+ overflow: hidden;
}
#app {
- max-width: 1280px;
- margin: 0 auto;
- padding: 2rem;
- text-align: center;
+ width: 100%;
+ height: 100%;
+ margin: 0;
+ padding: 0;
}
-.logo {
- height: 6em;
- padding: 1.5em;
- will-change: filter;
- transition: filter 300ms;
-}
-.logo:hover {
- filter: drop-shadow(0 0 2em #646cffaa);
-}
-.logo.vanilla:hover {
- filter: drop-shadow(0 0 2em #3178c6aa);
-}
-
-.card {
- padding: 2em;
-}
-
-.read-the-docs {
- color: #888;
-}
-
-button {
- border-radius: 8px;
- border: 1px solid transparent;
- padding: 0.6em 1.2em;
- font-size: 1em;
- font-weight: 500;
- font-family: inherit;
- background-color: #1a1a1a;
- cursor: pointer;
- transition: border-color 0.25s;
-}
-button:hover {
- border-color: #646cff;
-}
-button:focus,
-button:focus-visible {
- outline: 4px auto -webkit-focus-ring-color;
-}
-
-@media (prefers-color-scheme: light) {
- :root {
- color: #213547;
- background-color: #ffffff;
- }
- a:hover {
- color: #747bff;
- }
- button {
- background-color: #f9f9f9;
- }
+a {
+ color: inherit;
+ text-decoration: none;
}
diff --git a/web/src/typescript.svg b/web/src/typescript.svg
deleted file mode 100644
index d91c910..0000000
--- a/web/src/typescript.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/web/vite.config.ts b/web/vite.config.ts
index 4190ce8..7436200 100644
--- a/web/vite.config.ts
+++ b/web/vite.config.ts
@@ -1,9 +1,16 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
+import { compilerOptions } from 'vue3-pixi'
// https://vite.dev/config/
export default defineConfig({
- plugins: [vue()],
+ plugins: [
+ vue({
+ template: {
+ compilerOptions,
+ },
+ }),
+ ],
server: {
proxy: {
'/api': {
@@ -14,8 +21,11 @@ export default defineConfig({
target: 'ws://localhost:8002',
ws: true,
changeOrigin: true,
+ },
+ '/assets': {
+ target: 'http://localhost:8002',
+ changeOrigin: true,
}
}
}
})
-