update map
This commit is contained in:
@@ -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',
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user