update: game core flow pages

This commit is contained in:
Vick Scarlet
2026-08-16 20:06:16 +08:00
committed by 神戸小鳥
parent 25bd9ce2b7
commit 0c0fac5bfd
34 changed files with 890 additions and 768 deletions
+13 -9
View File
@@ -8,21 +8,23 @@
.allocation-input {
display: flex;
gap: 0;
height: 2rem;
border: 0.0625rem solid var(--base-text-color);
box-sizing: border-box;
width: 100%;
button {
flex: 1 0;
padding: 0;
margin: 0;
width: 2rem;
height: 2rem;
height: 100%;
border: none;
text-align: center;
}
input {
flex: 1;
height: 2rem;
flex: 1 0;
height: 100%;
width: 2rem;
font-weight: bold;
}
}
.points-detail {
@@ -42,7 +44,8 @@
li {
display: flex;
justify-content: space-between;
line-height: 2rem;
align-items: center;
height: 2rem;
padding: 0 0.5rem;
gap: 0.5rem;
}
@@ -72,9 +75,11 @@
height: 2rem;
border: 0.0625rem solid var(--base-text-color);
border-bottom: none;
text-align: center;
line-height: 2rem;
display: flex;
justify-content: center;
align-items: center;
box-sizing: border-box;
font-weight: bold;
}
}
> li.left {
@@ -87,10 +92,9 @@
> button {
flex: 1;
font-size: 1rem;
line-height: 2rem;
height: 2rem;
width: 6rem;
padding: 0;
box-sizing: border-box;
}
}
}
+6 -3
View File
@@ -1,6 +1,6 @@
import { useState } from 'react'
import { useAllocator, usePointRandomizer, useLeftPoints } from '@remake/hooks'
import { usePicked, useReplaced } from '@remake/hooks'
import { usePicked, useReplaced, useStart } from '@remake/hooks'
import type { AdditionalPoint } from '@remake/hooks'
import { properties } from '@/display'
import { keys } from '@remake/vitex'
@@ -59,9 +59,12 @@ export function Alloc() {
const left = useLeftPoints()
const [allocation, allocator] = useAllocator()
const random = usePointRandomizer()
const start = useStart()
const [showDetail, setShowDetail] = useState(false)
const handleNext = () => {
// navigate('/life')
const achievements = start()
// TODO: Show achievements
console.log('Achievements', achievements)
}
return (
<div className="screen point-allocation">
@@ -97,7 +100,7 @@ export function Alloc() {
<button className="info" onClick={() => random()}>
</button>
<button className="primary" onClick={() => {}}>
<button className="primary" onClick={handleNext}>
</button>
</div>
+13 -1
View File
@@ -8,10 +8,22 @@
h1 {
font-size: 4rem;
margin: 0;
font-weight: bold;
}
h2 {
font-size: 2rem;
margin: 0;
}
}
}
.theme-toggle-btn {
display: flex;
width: 2.5rem;
height: 2.5rem;
padding: 0.25rem;
cursor: pointer;
align-items: center;
justify-content: center;
-webkit-tap-highlight-color: transparent;
transition: background-color 0.3s;
}
}
+2
View File
@@ -1,4 +1,5 @@
import { useRemake } from '@remake/hooks'
import { ThemeToggle } from '@/components/ThemeToggle'
import './Home.css'
export default function Home() {
@@ -12,6 +13,7 @@ export default function Home() {
<button className="primary" onClick={remake}>
</button>
<ThemeToggle />
</div>
)
}
+114
View File
@@ -0,0 +1,114 @@
.screen.play {
height: calc(100dvh - 2rem);
gap: 1rem;
> ul.properties {
display: flex;
flex-direction: row;
gap: 0.5rem;
width: 100%;
font-weight: bold;
> li {
display: flex;
flex-direction: column;
flex: 1 0;
justify-content: space-between;
text-align: center;
min-width: 6rem;
user-select: none;
span {
display: flex;
justify-content: center;
align-items: center;
border: 0.0625rem solid var(--base-text-color);
box-sizing: border-box;
height: 2rem;
&:first-child {
border-bottom: none;
}
}
.value {
color: var(--color-primary);
font-size: 1.25rem;
font-weight: bold;
}
&.charm { order: 1 }
&.intelligence { order: 2 }
&.strength { order: 3 }
&.money { order: 4 }
&.spirit { order: 5 }
}
}
> ul.logs {
display: flex;
flex-direction: column;
flex: 1 0;
gap: 0.5rem;
padding: 0 0.5rem;
min-width: 32rem;
max-width: 100%;
height: 100%;
border: 0.0625rem solid var(--base-text-color);
box-sizing: border-box;
> li.log {
display: flex;
flex-direction: row;
line-height: 2rem;
gap: 0.5rem;
width: 100%;
user-select: none;
cursor: pointer;
> .age {
flex-shrink: 0;
width: 3rem;
height: 2rem;
text-align: right;
color: var(--color-primary);
font-weight: bold;
/* border-bottom: 0.0625rem solid var(--base-text-color); */
box-sizing: border-box;
}
> .content {
position: relative;
&::before {
content: '';
position: absolute;
left: -0.5rem;
bottom: 0;
height: calc(100% - 2rem);
width: calc(100% + 0.5rem);
border: 0.0625rem solid var(--base-text-color);
border-top: none;
border-right: none;
box-sizing: border-box;
pointer-events: none;
}
.tag, .name {
font-weight: bold;
}
}
}
>li:last-child {
margin-bottom: 2rem;
}
}
ul.log-inner {
> li {
color: var(--grade-color);
display: flex;
flex-direction: row;
gap: 0.5rem;
}
}
.controls {
display: flex;
flex-direction: row;
gap: 0.5rem;
width: 100%;
> button {
flex: 1 0;
min-width: 6rem;
font-size: 1.25rem;
height: 2.5rem;
}
}
}
+150 -2
View File
@@ -1,3 +1,151 @@
export default function Life() {
return <></>
import { useState, useRef } from 'react'
import { useLayoutEffect, useEffect } from 'react'
import { useNext, useGotoSummary } from '@remake/hooks'
import type { Log } from '@remake/hooks'
import { properties } from '@/display'
import { keys } from '@remake/vitex'
import { achievements, events, talents } from '@remake/data'
import { AutoInterval } from '@/config'
import './Play.css'
function LogTalent({ id }: { id: number }) {
const { name, description, grade } = talents.get(id)!
return (
<li className={`grade-${grade}`}>
<span className="tag font-mono">[]</span>
<span className="name">{name}</span>
<span className="description">{description}</span>
</li>
)
}
function LogTalents({ items }: { items: number[] }) {
if (items.length === 0) return null
const els = items.map(id => <LogTalent key={id} id={id} />)
return <ul className="log-inner log-talents">{els}</ul>
}
interface LogEventProps {
id: number
post: boolean
}
function LogEvent({ id, post }: LogEventProps) {
const { event, postEvent, grade } = events.get(id)!
return (
<>
<li className={`grade-${grade}`}>{event}</li>
{post && postEvent && (
<li className={`grade-${grade}`}>{postEvent}</li>
)}
</>
)
}
function LogEvents({ items }: { items: number[] }) {
const last = items.length - 1
const els = items.map((id, i) => (
<LogEvent key={id} id={id} post={i == last} />
))
return <ul className="log-inner log-events">{els}</ul>
}
function LogAchievement({ id }: { id: number }) {
const { name, description, grade } = achievements.get(id)!
return (
<li className={`grade-${grade}`}>
<span className="tag font-mono">[]</span>
<span className="name">{name}</span>
<span className="description">{description}</span>
</li>
)
}
function LogAchievements({ items }: { items: number[] }) {
if (items.length === 0) return null
const els = items.map(id => <LogAchievement key={id} id={id} />)
return <ul className="log-inner log-achievements">{els}</ul>
}
interface LogProps {
log: Log
}
function Log({ log }: LogProps) {
return (
<li className="log">
<span className="age font-mono">{log.age}</span>
<div className="content">
<LogTalents items={log.talents} />
<LogEvents items={log.events} />
<LogAchievements items={log.achievements} />
</div>
</li>
)
}
export function Play() {
const [{ state, logs, ended }, next] = useNext()
const [auto, setAuto] = useState(false)
const logRef = useRef<HTMLUListElement>(null)
const autoRef = useRef(0)
const gotoSummary = useGotoSummary()
const handleNext = () => {
if (ended) return
const achievements = next()
// TODO: Show achievements
console.debug('Achievements:', achievements)
}
const handleGotoSummary = () => {
if (!ended) return
const achievements = gotoSummary()
// TODO: Show achievements
console.debug('Achievements:', achievements)
}
useLayoutEffect(() => {
requestAnimationFrame(() => {
if (!logRef.current) return
logRef.current.scrollTop = logRef.current.scrollHeight
})
}, [logs])
useEffect(() => {
if (!auto) window.clearInterval(autoRef.current)
else autoRef.current = window.setInterval(handleNext, AutoInterval)
return () => window.clearInterval(autoRef.current)
}, [auto, handleNext])
return (
<div className="screen play">
<ul className="properties">
{keys(state.props.current, ['age']).map(key => (
<li className={key} key={key}>
<span>{properties[key]}</span>
<span className="font-mono">
{state.props.current[key]}
</span>
</li>
))}
</ul>
<ul
className="logs hide-scrollbar"
onClick={handleNext}
ref={logRef}
>
{logs.map((log, index) => (
<Log key={index} log={log} />
))}
</ul>
<div className="controls">
{!ended && (
<button className="primary" onClick={() => setAuto(!auto)}>
{auto ? '手动' : '自动'}
</button>
)}
{ended && (
<button className="primary" onClick={handleGotoSummary}>
</button>
)}
</div>
</div>
)
}
export default Play
+43
View File
@@ -0,0 +1,43 @@
.screen.summary {
gap: 1rem;
ul.judge-list {
display: flex;
flex-direction: row;
gap: 0.125rem;
width: 100%;
> li {
display: flex;
flex-direction: column;
flex: 1 0;
justify-content: space-between;
text-align: center;
min-width: 4rem;
user-select: none;
span {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
font-weight: bold;
border: 0.0625rem solid var(--grade-color);
box-sizing: border-box;
height: 2rem;
color: var(--grade-color);
&:not(:first-child) {
border-top: none;
}
}
.level {
font-size: 0.95rem;
background-color: var(--grade-color);
color: var(--base-background-color);
}
}
}
ul.talent-list {
display: flex;
flex-direction: column;
gap: 0.5rem;
width: 100%;
}
}
+58 -4
View File
@@ -1,10 +1,64 @@
import { useState } from 'react'
import { usePicked, useEnd } from '@remake/hooks'
import { useJudge } from '@/hooks/judge'
import type { JudgeKeys, Judge } from '@/hooks/judge'
import { properties, judgeDisplay } from '@/display'
import TalentComponent from '@/components/Talent'
import './Summary.css'
interface JudgeItemProps {
prop: JudgeKeys
judge: Judge
}
function JudgeItem({ prop, judge }: JudgeItemProps) {
const { value, grade, level } = judge
return (
<li className={`${prop} grade-${grade}`}>
<span className="property">{properties[prop]}</span>
<span className="value font-mono">{value}</span>
<span className="level">{judgeDisplay(prop, level)}</span>
</li>
)
}
export default function Summary() {
const picked = usePicked()
const judges = useJudge()
const end = useEnd()
const [locked, setLocked] = useState<number | null>(null)
const handleSelect = (id: number) => {
if (locked === id) setLocked(null)
else setLocked(id)
}
const handleEnd = () => {
const achievements = end()
// TODO: Show achievements
console.log('Achievements', achievements)
}
return (
<div className="screen summary">
<h2></h2>
<p>: {}</p>
{/* 展现解锁的成就等 */}
<button onClick={() => {}}></button>
<ul className="judge-list">
{judges.map(([key, { value, grade, level }]) => (
<JudgeItem
key={key}
prop={key}
judge={{ value, grade, level }}
/>
))}
</ul>
<div className="section">
<div className="title"></div>
<ul className="talent-list">
{Array.from(picked, id => (
<li key={id} onClick={() => handleSelect(id)}>
<TalentComponent id={id} selected={locked === id} />
</li>
))}
</ul>
</div>
<button className="primary" onClick={handleEnd}>
</button>
</div>
)
}