feat: add progression logic for puzzle residences
- Implemented functions to manage puzzle progress including checking if residences are started, complete, and unlocked. - Added utility functions to retrieve piece IDs based on residence and route. - Introduced a new TypeScript file for progression management. - Integrated puzzle data configuration and residence data for seamless progression tracking.
This commit is contained in:
1 parent
5a1c3cc368
commit
284302341c
72 files changed
+1104
-517
No files matched your search
@@ -1,11 +1,14 @@
|
||||
import { getBackgroundImage, getStaticImage } from '@/lib/utils'
|
||||
import { useNavigate, useParams } from 'react-router-dom'
|
||||
import { Image } from '@/components'
|
||||
import { isResidenceStarted, isResidenceUnlocked } from '@/config/progression'
|
||||
import { usePuzzleStore } from '@/store/puzzleStore'
|
||||
//英文命名 "革命先驱" | "文化名人" | "艺术名家" | "经济巨擘"
|
||||
type ModuleName = 'revolutionary' | 'cultural' | 'artistic' | 'economic'
|
||||
type EntranceActionState = 'locked' | 'unvisited' | 'visited'
|
||||
type EntranceSide = 'left' | 'right'
|
||||
|
||||
interface Star {
|
||||
image: string
|
||||
name: string
|
||||
starName: string
|
||||
}
|
||||
@@ -19,6 +22,36 @@ interface EntranceBackground {
|
||||
}
|
||||
}
|
||||
|
||||
const entranceActionConfig: Record<
|
||||
EntranceActionState,
|
||||
{
|
||||
image: string
|
||||
position: Record<EntranceSide, string>
|
||||
}
|
||||
> = {
|
||||
locked: {
|
||||
image: 'entrance/暂未解锁',
|
||||
position: {
|
||||
left: 'absolute left-95 bottom-58',
|
||||
right: 'absolute right-106 bottom-58',
|
||||
},
|
||||
},
|
||||
unvisited: {
|
||||
image: 'entrance/点击前往浏览',
|
||||
position: {
|
||||
left: 'absolute left-90.5 bottom-37',
|
||||
right: 'absolute right-90.5 bottom-37',
|
||||
},
|
||||
},
|
||||
visited: {
|
||||
image: 'entrance/该故居已访问',
|
||||
position: {
|
||||
left: 'absolute left-95 bottom-58',
|
||||
right: 'absolute right-102 bottom-58',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* 故居入口页
|
||||
* 根据模块名称显示不同的背景图
|
||||
@@ -34,12 +67,10 @@ const EntranceBackgroundMap: Record<ModuleName, EntranceBackground> = {
|
||||
backgroundImage: getBackgroundImage('entrance/革命先驱'),
|
||||
Star: {
|
||||
Star1: {
|
||||
image: getStaticImage('entrance/毛泽东故居入口'),
|
||||
name: '毛泽东',
|
||||
starName: 'maozedong',
|
||||
},
|
||||
Star2: {
|
||||
image: getStaticImage('entrance/孙中山故居入口-解锁'),
|
||||
name: '孙中山',
|
||||
starName: 'sunzhongshan',
|
||||
},
|
||||
@@ -49,12 +80,10 @@ const EntranceBackgroundMap: Record<ModuleName, EntranceBackground> = {
|
||||
backgroundImage: getBackgroundImage('entrance/文教名人'),
|
||||
Star: {
|
||||
Star1: {
|
||||
image: getStaticImage('entrance/鲁迅故居-解锁'),
|
||||
name: '鲁迅',
|
||||
starName: 'luxun',
|
||||
},
|
||||
Star2: {
|
||||
image: getStaticImage('entrance/黄元培-解锁'),
|
||||
name: '黄炎培',
|
||||
starName: 'huangyanpei',
|
||||
},
|
||||
@@ -64,12 +93,10 @@ const EntranceBackgroundMap: Record<ModuleName, EntranceBackground> = {
|
||||
backgroundImage: getBackgroundImage('entrance/艺术名家'),
|
||||
Star: {
|
||||
Star1: {
|
||||
image: getStaticImage('entrance/聂耳亮'),
|
||||
name: '聂耳',
|
||||
starName: 'nieer',
|
||||
},
|
||||
Star2: {
|
||||
image: getStaticImage('entrance/张乐平亮'),
|
||||
name: '张乐平',
|
||||
starName: 'zhangleping',
|
||||
},
|
||||
@@ -79,12 +106,10 @@ const EntranceBackgroundMap: Record<ModuleName, EntranceBackground> = {
|
||||
backgroundImage: getBackgroundImage('entrance/经济巨擎'),
|
||||
Star: {
|
||||
Star1: {
|
||||
image: getStaticImage('entrance/荣德生-解锁'),
|
||||
name: '荣德生',
|
||||
starName: 'rongdesheng',
|
||||
},
|
||||
Star2: {
|
||||
image: getStaticImage('entrance/陈云故居-解锁'),
|
||||
name: '陈云',
|
||||
starName: 'chenyun',
|
||||
},
|
||||
@@ -95,12 +120,39 @@ const EntranceBackgroundMap: Record<ModuleName, EntranceBackground> = {
|
||||
const Entrance = () => {
|
||||
const { moduleName } = useParams()
|
||||
const navigate = useNavigate()
|
||||
const puzzles = usePuzzleStore((state) => state.puzzles)
|
||||
// const decodedModuleName = decodeURIComponent(moduleName || "");
|
||||
const { backgroundImage, Star } = EntranceBackgroundMap[moduleName as ModuleName] || {}
|
||||
if (!Star) {
|
||||
return null
|
||||
}
|
||||
const { Star1, Star2 } = Star
|
||||
const isStar1Unlocked = isResidenceUnlocked(Star1.starName, puzzles)
|
||||
const isStar2Unlocked = isResidenceUnlocked(Star2.starName, puzzles)
|
||||
const isStar1Started = isResidenceStarted(Star1.starName, puzzles)
|
||||
const isStar2Started = isResidenceStarted(Star2.starName, puzzles)
|
||||
const getStarImage = (star: Star, isUnlocked: boolean) =>
|
||||
getStaticImage(`entrance/${star.name}-${isUnlocked ? '解锁' : '未解锁'}`)
|
||||
const getEntranceActionState = (isUnlocked: boolean, isStarted: boolean): EntranceActionState => {
|
||||
if (!isUnlocked) return 'locked'
|
||||
return isStarted ? 'visited' : 'unvisited'
|
||||
}
|
||||
const getEntranceAction = (side: EntranceSide, state: EntranceActionState) => {
|
||||
const config = entranceActionConfig[state]
|
||||
return {
|
||||
className: config.position[side],
|
||||
image: config.image,
|
||||
}
|
||||
}
|
||||
const star1Action = getEntranceAction(
|
||||
'left',
|
||||
getEntranceActionState(isStar1Unlocked, isStar1Started)
|
||||
)
|
||||
const star2Action = getEntranceAction(
|
||||
'right',
|
||||
getEntranceActionState(isStar2Unlocked, isStar2Started)
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="w-full h-full bg-cover bg-center bg-no-repeat" style={{ backgroundImage }}>
|
||||
<Image
|
||||
@@ -110,28 +162,33 @@ const Entrance = () => {
|
||||
onClick={() => navigate('/menu')}
|
||||
/>
|
||||
{/* 名人1 */}
|
||||
<Image className="absolute left-73 top-[calc(50%+38px)] -translate-y-1/2" src={Star1.image} />
|
||||
{Star1.image && (
|
||||
<Image
|
||||
className="absolute left-90.5 bottom-40"
|
||||
src={getStaticImage('entrance/点击前往浏览')}
|
||||
button
|
||||
onClick={() => navigate(`/${moduleName}/${Star1.starName}/home`)}
|
||||
/>
|
||||
)}
|
||||
<Image
|
||||
className="absolute left-73 top-[calc(50%+38px)] -translate-y-1/2"
|
||||
src={getStarImage(Star1, isStar1Unlocked)}
|
||||
/>
|
||||
<Image
|
||||
className={star1Action.className}
|
||||
src={getStaticImage(star1Action.image)}
|
||||
button={isStar1Unlocked}
|
||||
onClick={() => {
|
||||
if (!isStar1Unlocked) return
|
||||
navigate(`/${moduleName}/${Star1.starName}/home`)
|
||||
}}
|
||||
/>
|
||||
{/* 名人2 */}
|
||||
<Image
|
||||
className="absolute right-77.5 top-[calc(50%+39px)] -translate-y-1/2"
|
||||
src={Star2.image}
|
||||
src={getStarImage(Star2, isStar2Unlocked)}
|
||||
/>
|
||||
<Image
|
||||
className={star2Action.className}
|
||||
src={getStaticImage(star2Action.image)}
|
||||
button={isStar2Unlocked}
|
||||
onClick={() => {
|
||||
if (!isStar2Unlocked) return
|
||||
navigate(`/${moduleName}/${Star2.starName}/home`)
|
||||
}}
|
||||
/>
|
||||
{Star2.image && (
|
||||
<Image
|
||||
className="absolute right-90.5 bottom-40"
|
||||
src={getStaticImage('entrance/点击前往浏览')}
|
||||
button
|
||||
onClick={() => navigate(`/${moduleName}/${Star2.starName}/home`)}
|
||||
/>
|
||||
)}
|
||||
{/* <Image
|
||||
className="absolute right-5 top-1/2 -translate-y-1/2"
|
||||
src={Star2.image}
|
||||
|
||||
Reference in new issue
Block a user