mirror of
https://github.com/VickScarlet/lifeRestart.git
synced 2026-08-29 01:36:47 +08:00
feature: multiple lock, pick min max
This commit is contained in:
@@ -9,10 +9,13 @@
|
||||
display: flex;
|
||||
gap: 0;
|
||||
height: 2rem;
|
||||
border: 0.0625rem solid var(--base-text-color);
|
||||
border: 0.0625rem solid var(--grade-color);
|
||||
color: var(--grade-color);
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
transition: all 0.2s ease-in-out;
|
||||
button {
|
||||
--style-color: var(--grade-color);
|
||||
flex: 1 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
@@ -25,6 +28,8 @@
|
||||
height: 100%;
|
||||
width: 2rem;
|
||||
font-weight: bold;
|
||||
color: var(--grade-color);
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
}
|
||||
.points-detail {
|
||||
@@ -73,23 +78,27 @@
|
||||
> span {
|
||||
width: 100%;
|
||||
height: 2rem;
|
||||
border: 0.0625rem solid var(--base-text-color);
|
||||
color: var(--base-background-color);
|
||||
background: var(--grade-color);
|
||||
border: 0.0625rem solid var(--grade-color);
|
||||
border-bottom: none;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
font-weight: bold;
|
||||
transition: all 0.2s ease-in-out;
|
||||
}
|
||||
}
|
||||
> li.left {
|
||||
--left-color: var(--color-error);
|
||||
&.left-0 { --left-color: var(--color-primary); }
|
||||
> span {
|
||||
background: var(--color-primary);
|
||||
color: var(--base-background-color);
|
||||
border: 0.0625rem solid var(--color-primary);
|
||||
border-bottom: none;
|
||||
background: var(--left-color);
|
||||
border: 0.0625rem solid var(--left-color);
|
||||
}
|
||||
> button {
|
||||
--style-color: var(--left-color);
|
||||
flex: 1;
|
||||
font-size: 1rem;
|
||||
height: 2rem;
|
||||
|
||||
@@ -4,7 +4,7 @@ import { usePicked, useReplaced, useStart } from '@remake/hooks'
|
||||
import type { AdditionalPoint } from '@remake/hooks'
|
||||
import { properties } from '@/display'
|
||||
import { keys } from '@remake/vitex'
|
||||
import { BasePoints } from '@/config'
|
||||
import { BasePoints, judgeGradeByValue } from '@/config'
|
||||
import { talents } from '@remake/data'
|
||||
import Replaced from '@/components/Replaced'
|
||||
import './Alloc.css'
|
||||
@@ -62,6 +62,10 @@ export function Alloc() {
|
||||
const start = useStart()
|
||||
const [showDetail, setShowDetail] = useState(false)
|
||||
const handleNext = () => {
|
||||
if (left) {
|
||||
// TODO: Show a warning that there are still points left
|
||||
return
|
||||
}
|
||||
const achievements = start()
|
||||
// TODO: Show achievements
|
||||
console.log('Achievements', achievements)
|
||||
@@ -76,10 +80,10 @@ export function Alloc() {
|
||||
))}
|
||||
</ul>
|
||||
<ul className="alloc">
|
||||
<li className="left">
|
||||
<li className={`left left-${left}`}>
|
||||
<span>剩余点数</span>
|
||||
<button
|
||||
className="primary font-mono"
|
||||
className="font-mono"
|
||||
onClick={() => setShowDetail(!showDetail)}
|
||||
>
|
||||
{left}
|
||||
@@ -87,7 +91,10 @@ export function Alloc() {
|
||||
{showDetail && <PointsDetail source={source} />}
|
||||
</li>
|
||||
{keys(allocation).map(key => (
|
||||
<li key={key}>
|
||||
<li
|
||||
key={key}
|
||||
className={`property grade-${judgeGradeByValue(key, allocation[key])}`}
|
||||
>
|
||||
<span>{properties[key]}</span>
|
||||
<AllocInput
|
||||
point={allocation[key]}
|
||||
@@ -100,7 +107,10 @@ export function Alloc() {
|
||||
<button className="info" onClick={() => random()}>
|
||||
随机分配
|
||||
</button>
|
||||
<button className="primary" onClick={handleNext}>
|
||||
<button
|
||||
className={left ? 'error' : 'primary'}
|
||||
onClick={handleNext}
|
||||
>
|
||||
开始新人生
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -15,21 +15,21 @@
|
||||
text-align: center;
|
||||
min-width: 6rem;
|
||||
user-select: none;
|
||||
color: var(--grade-color);
|
||||
span {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border: 0.0625rem solid var(--base-text-color);
|
||||
border: 0.0625rem solid var(--grade-color);
|
||||
box-sizing: border-box;
|
||||
height: 2rem;
|
||||
&:first-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
.value {
|
||||
color: var(--color-primary);
|
||||
font-size: 1.25rem;
|
||||
font-weight: bold;
|
||||
.name {
|
||||
color: var(--base-background-color);
|
||||
background-color: var(--grade-color);
|
||||
}
|
||||
&.charm { order: 1 }
|
||||
&.intelligence { order: 2 }
|
||||
@@ -111,4 +111,55 @@
|
||||
height: 2.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.screen.play > ul.properties > li {
|
||||
.value {
|
||||
position: relative;
|
||||
background-color: var(--base-background-color);
|
||||
transition: background-color 0.5s ease;
|
||||
}
|
||||
&[class*="trend-up"] .value::after,
|
||||
&[class*="trend-down"] .value::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
right: 0.5rem;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
border-style: solid;
|
||||
animation: triangle-pop 0.3s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
|
||||
}
|
||||
&[class*="trend-up"] .value::after {
|
||||
border-width: 0 0.3125rem 0.375rem 0.3125rem;
|
||||
border-color: transparent transparent var(--color-success) transparent;
|
||||
}
|
||||
&[class*="trend-down"] .value::after {
|
||||
border-width: 0.375rem 0.3125rem 0 0.3125rem;
|
||||
border-color: var(--color-error) transparent transparent transparent;
|
||||
}
|
||||
&[class*="trend-up-1"] .value { animation: flash-success-odd 0.5s ease-out forwards; }
|
||||
&[class*="trend-up-0"] .value { animation: flash-success-even 0.5s ease-out forwards; }
|
||||
&[class*="trend-down-1"] .value { animation: flash-error-odd 0.5s ease-out forwards; }
|
||||
&[class*="trend-down-0"] .value { animation: flash-error-even 0.5s ease-out forwards; }
|
||||
}
|
||||
|
||||
@keyframes flash-success-odd {
|
||||
0% { background-color: color-mix(in srgb, var(--color-success) 30%, var(--base-background-color)); }
|
||||
100% { background-color: var(--base-background-color); }
|
||||
}
|
||||
@keyframes flash-success-even {
|
||||
0% { background-color: color-mix(in srgb, var(--color-success) 30%, var(--base-background-color)); }
|
||||
100% { background-color: var(--base-background-color); }
|
||||
}
|
||||
@keyframes flash-error-odd {
|
||||
0% { background-color: color-mix(in srgb, var(--color-error) 30%, var(--base-background-color)); }
|
||||
100% { background-color: var(--base-background-color); }
|
||||
}
|
||||
@keyframes flash-error-even {
|
||||
0% { background-color: color-mix(in srgb, var(--color-error) 30%, var(--base-background-color)); }
|
||||
100% { background-color: var(--base-background-color); }
|
||||
}
|
||||
@keyframes triangle-pop {
|
||||
0% { opacity: 0; transform: translateY(-50%) scale(0.5); }
|
||||
100% { opacity: 1; transform: translateY(-50%) scale(1); }
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useState, useRef } from 'react'
|
||||
import { useLayoutEffect, useEffect } from 'react'
|
||||
import { useNext, useGotoSummary } from '@remake/hooks'
|
||||
import { useJudge } from '@/hooks/judge'
|
||||
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'
|
||||
@@ -66,10 +66,7 @@ function LogAchievements({ items }: { items: number[] }) {
|
||||
return <ul className="log-inner log-achievements">{els}</ul>
|
||||
}
|
||||
|
||||
interface LogProps {
|
||||
log: Log
|
||||
}
|
||||
function Log({ log }: LogProps) {
|
||||
function Log({ log }: { log: Log }) {
|
||||
return (
|
||||
<li className="log">
|
||||
<span className="age font-mono">{log.age}岁</span>
|
||||
@@ -82,8 +79,51 @@ function Log({ log }: LogProps) {
|
||||
)
|
||||
}
|
||||
|
||||
interface PropProps {
|
||||
prop: keyof typeof properties
|
||||
value: number
|
||||
grade: number
|
||||
}
|
||||
|
||||
function Prop({ prop, value, grade }: PropProps) {
|
||||
const prevRef = useRef<number>(value)
|
||||
const [trend, setTrend] = useState<'up' | 'down' | 'normal'>('normal')
|
||||
const [animCount, setAnimCount] = useState<number>(0)
|
||||
useEffect(() => {
|
||||
const prev = prevRef.current
|
||||
if (value === prev) return
|
||||
prevRef.current = value
|
||||
setTrend(value > prev ? 'up' : 'down')
|
||||
setAnimCount(c => c + 1)
|
||||
const timer = setTimeout(() => {
|
||||
setTrend('normal')
|
||||
}, 2000)
|
||||
return () => clearTimeout(timer)
|
||||
}, [value])
|
||||
|
||||
return (
|
||||
<li
|
||||
className={`${prop} grade-${grade} trend-${trend}-${animCount % 2}`}
|
||||
>
|
||||
<span className="name">{properties[prop]}</span>
|
||||
<span className="value font-mono">{value}</span>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
function Properties() {
|
||||
const judges = useJudge()
|
||||
return (
|
||||
<ul className="properties">
|
||||
{judges.map(([key, { value, grade }]) => (
|
||||
<Prop key={key} prop={key} value={value} grade={grade} />
|
||||
))}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
|
||||
export function Play() {
|
||||
const [{ state, logs, ended }, next] = useNext()
|
||||
const [{ logs, ended }, next] = useNext()
|
||||
const [auto, setAuto] = useState(false)
|
||||
const logRef = useRef<HTMLUListElement>(null)
|
||||
const autoRef = useRef(0)
|
||||
@@ -113,16 +153,7 @@ export function Play() {
|
||||
}, [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>
|
||||
<Properties />
|
||||
<ul
|
||||
className="logs hide-scrollbar"
|
||||
onClick={handleNext}
|
||||
@@ -135,7 +166,7 @@ export function Play() {
|
||||
<div className="controls">
|
||||
{!ended && (
|
||||
<button className="primary" onClick={() => setAuto(!auto)}>
|
||||
{auto ? '手动' : '自动'}
|
||||
{auto ? '关闭自动' : '开启自动'}
|
||||
</button>
|
||||
)}
|
||||
{ended && (
|
||||
|
||||
@@ -1,35 +1,65 @@
|
||||
import { useState } from 'react'
|
||||
import { usePicked, useEnd } from '@remake/hooks'
|
||||
import { useJudge } from '@/hooks/judge'
|
||||
import type { JudgeKeys, Judge } from '@/hooks/judge'
|
||||
import { useEffect, useState, useRef } from 'react'
|
||||
import { usePicked, useEnd, useProfile } from '@remake/hooks'
|
||||
import { useEndJudge } from '@/hooks/judge'
|
||||
import { properties, judgeDisplay } from '@/display'
|
||||
import { isDev } from '@/config'
|
||||
import TalentComponent from '@/components/Talent'
|
||||
import './Summary.css'
|
||||
|
||||
interface JudgeItemProps {
|
||||
prop: JudgeKeys
|
||||
judge: Judge
|
||||
}
|
||||
function JudgeItem({ prop, judge }: JudgeItemProps) {
|
||||
const { value, grade, level } = judge
|
||||
function Judges() {
|
||||
const judges = useEndJudge()
|
||||
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>
|
||||
<ul className="judge-list">
|
||||
{judges.map(([key, { value, grade, level }]) => (
|
||||
<li className={`${key} grade-${grade}`} key={key}>
|
||||
<span className="property">{properties[key]}</span>
|
||||
<span className="value font-mono">{value}</span>
|
||||
<span className="level">{judgeDisplay(key, level)}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
|
||||
interface TalentListProps {
|
||||
picked: Set<number>
|
||||
picker: (id: number) => void
|
||||
}
|
||||
function TalentList({ picked, picker }: TalentListProps) {
|
||||
const [profile] = useProfile()
|
||||
const [talents, setTalents] = useState(usePicked())
|
||||
const hasInitialized = useRef(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (!profile.locked) return
|
||||
let currentTalents = talents
|
||||
if (isDev) {
|
||||
currentTalents = new Set([...talents, ...profile.locked])
|
||||
setTalents(currentTalents)
|
||||
}
|
||||
|
||||
if (!hasInitialized.current) {
|
||||
for (const id of profile.locked) {
|
||||
if (currentTalents.has(id) && !picked.has(id)) {
|
||||
picker(id)
|
||||
}
|
||||
}
|
||||
hasInitialized.current = true
|
||||
}
|
||||
}, [profile, talents, picked, picker])
|
||||
return (
|
||||
<ul className="talent-list">
|
||||
{Array.from(talents, id => (
|
||||
<li key={id} onClick={() => picker(id)}>
|
||||
<TalentComponent id={id} selected={picked.has(id)} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)
|
||||
}
|
||||
|
||||
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 [locked, picker, end] = useEnd()
|
||||
const handleEnd = () => {
|
||||
const achievements = end()
|
||||
// TODO: Show achievements
|
||||
@@ -37,24 +67,10 @@ export default function Summary() {
|
||||
}
|
||||
return (
|
||||
<div className="screen summary">
|
||||
<ul className="judge-list">
|
||||
{judges.map(([key, { value, grade, level }]) => (
|
||||
<JudgeItem
|
||||
key={key}
|
||||
prop={key}
|
||||
judge={{ value, grade, level }}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
<Judges />
|
||||
<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>
|
||||
<TalentList picked={locked} picker={picker} />
|
||||
</div>
|
||||
<button className="primary" onClick={handleEnd}>
|
||||
再次重开
|
||||
|
||||
@@ -1,20 +1,21 @@
|
||||
// import { useEffect } from 'react'
|
||||
import { useTalentPuller, useTalentPicker } from '@remake/hooks'
|
||||
import { useTalentSubmit, useSubmitIsEnable } from '@remake/hooks'
|
||||
import { PullCount } from '@/config'
|
||||
import TalentComponent from '@/components/Talent'
|
||||
import './TalentPick.css'
|
||||
|
||||
export default function TalentPick() {
|
||||
const [pulled, puller] = useTalentPuller()
|
||||
const [talents, picker] = useTalentPicker()
|
||||
const [enabled, limit] = useSubmitIsEnable()
|
||||
const { enabled, min, max } = useSubmitIsEnable()
|
||||
const submit = useTalentSubmit()
|
||||
// useEffect(puller, [])
|
||||
if (!pulled)
|
||||
return (
|
||||
<div className="screen talent-pick">
|
||||
<button className="primary" onClick={() => puller()}>
|
||||
十连抽!
|
||||
<button className="primary font-mono" onClick={() => puller()}>
|
||||
{PullCount}连抽!
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
@@ -32,7 +33,9 @@ export default function TalentPick() {
|
||||
下一步
|
||||
</button>
|
||||
) : (
|
||||
<button className="error">请选取 {limit} 个天赋</button>
|
||||
<button className="error">
|
||||
请选取 {min == max ? min : `${min}~${max}`} 个天赋
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user