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,5 +1,8 @@
|
||||
import { useEffect } from 'react'
|
||||
import { getBackgroundImage, getStaticImage } from '@/lib/utils'
|
||||
import { Image } from '@/components'
|
||||
import { usePuzzleStore } from '@/store/puzzleStore'
|
||||
import { getResidenceStageUnlocks, isResidenceUnlocked } from '@/config/progression'
|
||||
import { useNavigate, useParams } from 'react-router-dom'
|
||||
import { useRongdeshengStore } from '@/store/rongdeshengStore'
|
||||
|
||||
@@ -11,6 +14,17 @@ const RongdeshengHome = () => {
|
||||
const starName = 'rongdesheng'
|
||||
const lastVisitedPlace = useRongdeshengStore((s) => s.lastVisitedPlace)
|
||||
|
||||
const puzzles = usePuzzleStore((state) => state.puzzles)
|
||||
const isLoaded = usePuzzleStore((state) => state.isLoaded)
|
||||
const { dialogueUnlocked, reflectionUnlocked, internalizationUnlocked } =
|
||||
getResidenceStageUnlocks(starName, puzzles)
|
||||
|
||||
useEffect(() => {
|
||||
if (isLoaded && !isResidenceUnlocked(starName, puzzles)) {
|
||||
navigate('/menu', { replace: true })
|
||||
}
|
||||
}, [isLoaded, navigate, puzzles, starName])
|
||||
|
||||
if (!moduleName) return null
|
||||
|
||||
return (
|
||||
@@ -30,23 +44,37 @@ const RongdeshengHome = () => {
|
||||
<div className="absolute right-20 top-75 flex flex-col space-y-10">
|
||||
{/* 对话 */}
|
||||
<Image
|
||||
button
|
||||
button={dialogueUnlocked}
|
||||
className={dialogueUnlocked ? undefined : 'opacity-50 grayscale cursor-not-allowed'}
|
||||
src={getStaticImage('star/一-解锁')}
|
||||
onClick={() => navigate(`/${moduleName}/${starName}/${lastVisitedPlace || 'place1'}`)}
|
||||
onClick={() => {
|
||||
if (!dialogueUnlocked) return
|
||||
navigate(`/${moduleName}/${starName}/${lastVisitedPlace || 'place1'}`)
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 体悟(占位) */}
|
||||
<Image
|
||||
button
|
||||
button={reflectionUnlocked}
|
||||
className={reflectionUnlocked ? undefined : 'opacity-50 grayscale cursor-not-allowed'}
|
||||
src={getStaticImage('star/二-解锁')}
|
||||
onClick={() => navigate(`/${moduleName}/${starName}/reflection`)}
|
||||
onClick={() => {
|
||||
if (!reflectionUnlocked) return
|
||||
navigate(`/${moduleName}/${starName}/reflection`)
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 内化(占位) */}
|
||||
<Image
|
||||
button
|
||||
button={internalizationUnlocked}
|
||||
className={
|
||||
internalizationUnlocked ? undefined : 'opacity-50 grayscale cursor-not-allowed'
|
||||
}
|
||||
src={getStaticImage('star/三-解锁')}
|
||||
onClick={() => navigate(`/${moduleName}/${starName}/internalization`)}
|
||||
onClick={() => {
|
||||
if (!internalizationUnlocked) return
|
||||
navigate(`/${moduleName}/${starName}/internalization`)
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -272,6 +272,7 @@ const RongdeshengPlace1 = () => {
|
||||
visible={showAfterDialogGuide}
|
||||
onContinue={() => setShowAfterDialogGuide(false)}
|
||||
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_01.mp3')}
|
||||
firstLineIndent="2em"
|
||||
>
|
||||
{` 同学你好,欢迎来到“走进故居·对话”学习模块!
|
||||
此刻你正伫立在荣德生旧居的石库门廊下,接下来请循着实业的足迹,一同探寻先生传奇人生里的4段壮阔征程:金融启蒙·资本筑基、实业拓疆·双业兴邦、山河危难·气节如钢、风雨坚守·初心不改。准备好共赴这场面粉与棉纱交织的民族工业史诗了吗?`}
|
||||
|
||||
@@ -222,7 +222,7 @@ const RongdeshengPlace2 = () => {
|
||||
)}
|
||||
{showVideo && videoForExhibitId === 2 && (
|
||||
<Video
|
||||
src={getPublicPath('video/rongdesheng/rongdesheng_3.mp4')}
|
||||
src={getPublicPath('video/rongdesheng/rongdesheng_4.mp4')}
|
||||
className="fixed inset-0 z-20000"
|
||||
autoPlay
|
||||
playsInline
|
||||
@@ -233,7 +233,7 @@ const RongdeshengPlace2 = () => {
|
||||
)}
|
||||
{showVideo && videoForExhibitId === 4 && (
|
||||
<Video
|
||||
src={getPublicPath('video/rongdesheng/rongdesheng_3.mp4')}
|
||||
src={getPublicPath('video/rongdesheng/rongdesheng_5.mp4')}
|
||||
className="fixed inset-0 z-20000"
|
||||
autoPlay
|
||||
playsInline
|
||||
|
||||
@@ -48,6 +48,7 @@ const RongdeshengPlace4 = () => {
|
||||
const [activeExhibitId, setActiveExhibitId] = useState<1 | 2 | 3 | 4 | null>(null)
|
||||
|
||||
const [showVideo, setShowVideo] = useState(false)
|
||||
const [showEndingVideo, setShowEndingVideo] = useState(false)
|
||||
const [guide1Visible, setGuide1Visible] = useState(false)
|
||||
const [guide1Step, setGuide1Step] = useState(0)
|
||||
const [guide1AfterVideoVisible, setGuide1AfterVideoVisible] = useState(false)
|
||||
@@ -95,6 +96,11 @@ const RongdeshengPlace4 = () => {
|
||||
setGuide1AfterVideoVisible(true)
|
||||
}
|
||||
|
||||
const handleEndingVideoEnded = () => {
|
||||
setShowEndingVideo(false)
|
||||
setEndingGuideVisible(true)
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="w-full h-full bg-cover bg-center bg-no-repeat"
|
||||
@@ -174,7 +180,7 @@ const RongdeshengPlace4 = () => {
|
||||
})
|
||||
|
||||
if (closingId === 4) {
|
||||
setEndingGuideVisible(true)
|
||||
setShowEndingVideo(true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,6 +202,18 @@ const RongdeshengPlace4 = () => {
|
||||
/>
|
||||
)}
|
||||
|
||||
{showEndingVideo && (
|
||||
<Video
|
||||
src={getPublicPath('video/rongdesheng/rongdesheng_11.mp4')}
|
||||
className="fixed inset-0 z-20000"
|
||||
autoPlay
|
||||
playsInline
|
||||
showSkipButton
|
||||
onEnded={handleEndingVideoEnded}
|
||||
onSkip={handleEndingVideoEnded}
|
||||
/>
|
||||
)}
|
||||
|
||||
<GuideSteps
|
||||
visible={guide1Visible}
|
||||
onVisibleChange={setGuide1Visible}
|
||||
|
||||
@@ -350,8 +350,8 @@ const RongdeshengReflection = () => {
|
||||
setShowGuide(false)
|
||||
markReflectionGuideShown()
|
||||
}}
|
||||
firstLineIndent="2em"
|
||||
audioSrc={getPublicPath('audio/rongdesheng/mp3_rongdesheng_31.mp3')}
|
||||
firstLineIndent="2em"
|
||||
>
|
||||
同学你好,欢迎来到“跨越时空·体悟”学习模块!{'\n'}
|
||||
请登陆“时空回溯・亲历时代”体验仓,下一站:1915年,上海。{'\n'}
|
||||
|
||||
@@ -171,27 +171,31 @@ export const MODULES: Module[] = [
|
||||
id: 'opt5-1',
|
||||
text: 'A.仿制外国先进设备,改良生产工艺',
|
||||
isCorrect: false,
|
||||
videoResourceId: 'video5-1',
|
||||
},
|
||||
{
|
||||
id: 'opt5-2',
|
||||
text: 'B.设立职员养成所,培养本土技术人才',
|
||||
isCorrect: true,
|
||||
videoResourceId: 'video5-2',
|
||||
videoResourceId: 'video5-1',
|
||||
},
|
||||
{
|
||||
id: 'opt5-3',
|
||||
text: 'C.降价促销,以低价抢占市场',
|
||||
videoResourceId: 'video5-2',
|
||||
isCorrect: true,
|
||||
},
|
||||
{ id: 'opt5-3', text: 'C.降价促销,以低价抢占市场', isCorrect: true },
|
||||
],
|
||||
videoResources: [
|
||||
{
|
||||
id: 'video5-1',
|
||||
thumbnail: 'rongdesheng/reflection/视频5-1',
|
||||
videoSrc: '/video/rongdesheng/rongdesheng_2_9.mp4',
|
||||
videoSrc: '/video/rongdesheng/rongdesheng_2_10.mp4',
|
||||
position: { top: '25%', left: '42%' },
|
||||
},
|
||||
{
|
||||
id: 'video5-2',
|
||||
thumbnail: 'rongdesheng/reflection/视频5-2',
|
||||
videoSrc: '/video/rongdesheng/rongdesheng_2_10.mp4',
|
||||
videoSrc: '/video/rongdesheng/rongdesheng_2_9.mp4',
|
||||
position: { bottom: '50%', left: '55%' },
|
||||
flipY: true,
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user