mirror of
https://github.com/VickScarlet/lifeRestart.git
synced 2026-08-29 01:36:47 +08:00
update: game core flow pages
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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%;
|
||||
}
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user