update map

This commit is contained in:
bridge
2025-12-03 22:38:53 +08:00
parent a5363a07ad
commit 9296cdde79
132 changed files with 751 additions and 223 deletions

View File

@@ -83,23 +83,23 @@ function getTexture() {
function getScale() {
const tex = getTexture()
if (!tex) return 1
// Scale up: 4.25x tile size (larger medium size)
return (props.tileSize * 4.25) / Math.max(tex.width, tex.height)
// Scale up: 3.0x tile size
return (props.tileSize * 3.0) / Math.max(tex.width, tex.height)
}
const drawFallback = (g: Graphics) => {
g.clear()
g.circle(0, 0, props.tileSize * 0.8) // Increased size
g.circle(0, 0, props.tileSize * 0.5)
g.fill({ color: props.avatar.gender === 'female' ? 0xffaaaa : 0xaaaaff })
g.stroke({ width: 3, color: 0x000000 })
g.stroke({ width: 2, color: 0x000000 })
}
const nameStyle = {
fontFamily: '"Microsoft YaHei", sans-serif',
fontSize: 65, // Larger medium size
fontSize: 42,
fontWeight: 'bold',
fill: '#ffffff',
stroke: { color: '#000000', width: 5.5 }, // Thicker stroke
stroke: { color: '#000000', width: 3.5 },
align: 'center',
dropShadow: {
color: '#000000',

View File

@@ -7,7 +7,7 @@ import type { RegionSummary } from '../../types/core'
const TILE_SIZE = 64
const mapContainer = ref<Container>()
const { textures, isLoaded, loadSectTexture } = useTextures()
const { textures, isLoaded, loadSectTexture, loadCityTexture } = useTextures()
const worldStore = useWorldStore()
const regionStyleCache = new Map<string, Record<string, unknown>>()
@@ -35,12 +35,13 @@ watch(
async function renderMap() {
if (!mapContainer.value || !worldStore.mapData.length) return
await preloadSectTextures()
await preloadRegionTextures()
mapContainer.value.removeChildren()
const rows = worldStore.mapData.length
const cols = worldStore.mapData[0]?.length ?? 0
// 1. Render Base Tiles
for (let y = 0; y < rows; y++) {
for (let x = 0; x < cols; x++) {
const type = worldStore.mapData[y][x]
@@ -71,14 +72,19 @@ async function renderMap() {
}
}
// 2. Render Large Regions (2x2)
renderLargeRegions()
emit('mapLoaded', {
width: cols * TILE_SIZE,
height: rows * TILE_SIZE
})
}
async function preloadSectTextures() {
async function preloadRegionTextures() {
const regions = Array.from(worldStore.regions.values());
// Sects
const sectNames = Array.from(
new Set(
regions
@@ -86,21 +92,73 @@ async function preloadSectTextures() {
.map(region => region.sect_name as string)
)
)
await Promise.all(sectNames.map(name => loadSectTexture(name)))
// Cities
const cityNames = Array.from(
new Set(
regions
.filter(region => region.type === 'city')
.map(region => region.name)
)
)
await Promise.all([
...sectNames.map(name => loadSectTexture(name)),
...cityNames.map(name => loadCityTexture(name))
])
}
function resolveSectTexture(x: number, y: number) {
const regions = Array.from(worldStore.regions.values());
const region = regions.find(r =>
r.type === 'sect' && Math.abs(r.x - x) < 3 && Math.abs(r.y - y) < 3
)
if (region?.sect_name) {
const key = `SECT_${region.sect_name}`
return textures.value[key] ?? null
}
// Sect tile rendering is now handled in renderLargeRegions via slices
function resolveSectTexture(_x: number, _y: number) {
// Legacy function - sect rendering is now done via slices in renderLargeRegions
return null
}
function renderLargeRegions() {
const regions = Array.from(worldStore.regions.values());
for (const region of regions) {
let baseName: string | null = null;
if (region.type === 'city') {
baseName = region.name
} else if (region.type === 'sect' && region.sect_name) {
baseName = region.sect_name
} else if (region.type === 'cultivate') {
if (region.name.includes('遗迹')) {
baseName = 'ruin'
} else if (region.name.includes('洞') || region.name.includes('府') || region.name.includes('秘境') || region.name.includes('宫')) {
baseName = 'cave'
}
}
if (baseName && mapContainer.value) {
// Render 4 slices as 2x2 grid
// Slice indices: 0=TL, 1=TR, 2=BL, 3=BR
const positions = [
{ dx: 0, dy: 0, idx: 0 }, // TL
{ dx: 1, dy: 0, idx: 1 }, // TR
{ dx: 0, dy: 1, idx: 2 }, // BL
{ dx: 1, dy: 1, idx: 3 }, // BR
]
for (const pos of positions) {
const sliceKey = `${baseName}_${pos.idx}`
const tex = textures.value[sliceKey]
if (tex) {
const sprite = new Sprite(tex)
sprite.x = (region.x + pos.dx) * TILE_SIZE
sprite.y = (region.y + pos.dy) * TILE_SIZE
sprite.width = TILE_SIZE
sprite.height = TILE_SIZE
sprite.roundPixels = true
sprite.eventMode = 'none'
mapContainer.value.addChild(sprite)
}
}
}
}
}
function getRegionStyle(type: string) {
if (regionStyleCache.has(type)) {
return regionStyleCache.get(type)

View File

@@ -43,14 +43,22 @@ export function useTextures() {
'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',
'ISLAND': '/assets/tiles/island.png',
'BAMBOO': '/assets/tiles/bamboo.png',
'GOBI': '/assets/tiles/gobi.png',
'TUNDRA': '/assets/tiles/tundra.png',
'MARSH': '/assets/tiles/swamp.png'
'MARSH': '/assets/tiles/swamp.png',
// Cave slices
'cave_0': '/assets/tiles/cave_0.png',
'cave_1': '/assets/tiles/cave_1.png',
'cave_2': '/assets/tiles/cave_2.png',
'cave_3': '/assets/tiles/cave_3.png',
// Ruin slices
'ruin_0': '/assets/tiles/ruin_0.png',
'ruin_1': '/assets/tiles/ruin_1.png',
'ruin_2': '/assets/tiles/ruin_2.png',
'ruin_3': '/assets/tiles/ruin_3.png',
}
const tilePromises = Object.entries(manifest).map(async ([key, url]) => {
@@ -86,32 +94,60 @@ export function useTextures() {
console.log('Base textures loaded')
}
// 动态加载宗门纹理(按需)
// 动态加载宗门纹理(按需)- 加载4个切片用于渲染
const loadSectTexture = async (sectName: string) => {
const key = `SECT_${sectName}`
if (textures.value[key]) return // 已经加载过
// 假设图片路径规则:/assets/sects/宗门名.png
// 使用 encodeURIComponent 处理中文路径
const url = `/assets/sects/${sectName}.png`
// 加载4个切片 _0, _1, _2, _3
const slicePromises = [0, 1, 2, 3].map(async (i) => {
const key = `${sectName}_${i}`
if (textures.value[key]) return
const url = `/assets/sects/${sectName}_${i}.png`
try {
const tex = await Assets.load(url)
textures.value[key] = tex
} catch (e) {
try {
const encodedUrl = `/assets/sects/${encodeURIComponent(`${sectName}_${i}`)}.png`
const tex = await Assets.load(encodedUrl)
textures.value[key] = tex
} catch (e2) {
console.warn(`Failed to load sect slice: ${key}`)
}
}
})
try {
// 尝试直接加载Pixi v7+ Assets.load 通常能处理 URL
// 为了兼容性,我们保留原始字符串,如果失败再尝试 encode
const tex = await Assets.load(url)
textures.value[key] = tex
console.log(`Loaded sect texture: ${sectName}`)
} catch (e) {
// 尝试 encode 再次加载
try {
const encodedUrl = `/assets/sects/${encodeURIComponent(sectName)}.png`
const tex = await Assets.load(encodedUrl)
textures.value[key] = tex
console.log(`Loaded sect texture (encoded): ${sectName}`)
} catch (e2) {
console.warn(`Failed to load sect texture for ${sectName} (both raw and encoded), using fallback.`, e)
}
}
await Promise.all(slicePromises)
}
// 动态加载城市纹理(按需)- 加载4个切片用于渲染
const loadCityTexture = async (cityName: string) => {
// 加载4个切片 _0, _1, _2, _3
const extensions = ['.jpg', '.png']
const slicePromises = [0, 1, 2, 3].map(async (i) => {
const key = `${cityName}_${i}`
if (textures.value[key]) return
for (const ext of extensions) {
const url = `/assets/cities/${cityName}_${i}${ext}`
try {
const tex = await Assets.load(url)
textures.value[key] = tex
return
} catch (e) {
try {
const encodedUrl = `/assets/cities/${encodeURIComponent(`${cityName}_${i}`)}${ext}`
const tex = await Assets.load(encodedUrl)
textures.value[key] = tex
return
} catch (e2) {
// continue
}
}
}
})
await Promise.all(slicePromises)
}
return {
@@ -119,6 +155,7 @@ export function useTextures() {
isLoaded,
loadBaseTextures,
loadSectTexture,
loadCityTexture,
availableAvatars
}
}