mirror of
https://github.com/VickScarlet/lifeRestart.git
synced 2026-08-29 01:36:47 +08:00
update: achievement
This commit is contained in:
@@ -17,7 +17,7 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
.title {
|
> .title {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import Pick from '@/containers/TalentPick'
|
|||||||
import Alloc from '@/containers/Alloc'
|
import Alloc from '@/containers/Alloc'
|
||||||
import Play from '@/containers/Play'
|
import Play from '@/containers/Play'
|
||||||
import Summary from '@/containers/Summary'
|
import Summary from '@/containers/Summary'
|
||||||
|
import Achv from '@/containers/Achv'
|
||||||
import './Game.css'
|
import './Game.css'
|
||||||
|
|
||||||
export function Container() {
|
export function Container() {
|
||||||
@@ -20,6 +21,7 @@ export function Container() {
|
|||||||
case Step.Alloc: return <Alloc />
|
case Step.Alloc: return <Alloc />
|
||||||
case Step.Play: return <Play />
|
case Step.Play: return <Play />
|
||||||
case Step.Summary: return <Summary />
|
case Step.Summary: return <Summary />
|
||||||
|
case Step.Achv: return <Achv />
|
||||||
default: return null
|
default: return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,8 +52,8 @@ export const JudgeLineMap = {
|
|||||||
spirit: [0, 1, 2, 4, 7, 9, 11],
|
spirit: [0, 1, 2, 4, 7, 9, 11],
|
||||||
intelligence: [0, 1, 2, 4, 7, 9, 11, 21, 131, 501],
|
intelligence: [0, 1, 2, 4, 7, 9, 11, 21, 131, 501],
|
||||||
strength: [0, 1, 2, 4, 7, 9, 11, 21, 101, 401, 1001, 2001],
|
strength: [0, 1, 2, 4, 7, 9, 11, 21, 101, 401, 1001, 2001],
|
||||||
talent: [0, 0.3, 0.6, 0.9],
|
talent: [0, 30, 60, 90],
|
||||||
event: [0, 0.2, 0.4, 0.6],
|
event: [0, 20, 40, 60],
|
||||||
}
|
}
|
||||||
// 可评级属性
|
// 可评级属性
|
||||||
export type Judges = keyof typeof JudgeLineMap
|
export type Judges = keyof typeof JudgeLineMap
|
||||||
|
|||||||
@@ -0,0 +1,120 @@
|
|||||||
|
.screen.achv {
|
||||||
|
height: calc(100dvh - 2rem);
|
||||||
|
width: min(calc(100dvw - 2rem), 48rem);
|
||||||
|
gap: 1rem;
|
||||||
|
> .stats {
|
||||||
|
> .details {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 0.5rem;
|
||||||
|
> div {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
border: 0.0625rem solid var(--grade-color);
|
||||||
|
color: var(--grade-color);
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: relative;
|
||||||
|
transform-style: preserve-3d;
|
||||||
|
user-select: none;
|
||||||
|
> span {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
> .title {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
height: 2.5rem;
|
||||||
|
}
|
||||||
|
> .value {
|
||||||
|
height: 2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
> .prg {
|
||||||
|
padding-bottom: 0.1875rem;
|
||||||
|
&::before,
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
height: 0.1875rem;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
transform: translateZ(-1px);
|
||||||
|
}
|
||||||
|
&::after {
|
||||||
|
width: var(--progress, 0%);
|
||||||
|
background: var(--grade-color);
|
||||||
|
transition: width 0.2s ease-in-out;
|
||||||
|
}
|
||||||
|
&::before {
|
||||||
|
width: 100%;
|
||||||
|
border-top: 0.0625rem solid var(--grade-color);
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
> .achievement-list {
|
||||||
|
flex: 1 0;
|
||||||
|
width: 100%;
|
||||||
|
> ul {
|
||||||
|
flex: 1 0;
|
||||||
|
display: grid;
|
||||||
|
flex-direction: column;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr));
|
||||||
|
gap: 0.5rem;
|
||||||
|
box-sizing: border-box;
|
||||||
|
> li.achievement {
|
||||||
|
margin-right: 0.0625rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
color: var(--grade-color);
|
||||||
|
border: 0.0625rem solid var(--grade-color);
|
||||||
|
padding: 0 0.0625rem;
|
||||||
|
box-sizing: border-box;
|
||||||
|
transform-style: preserve-3d;
|
||||||
|
position: relative;
|
||||||
|
user-select: none;
|
||||||
|
>.name {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
height: 2rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
>.description {
|
||||||
|
height: 1.5rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
&::before{
|
||||||
|
content: '';
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
padding: 0.0625rem;
|
||||||
|
margin: -0.0625rem;
|
||||||
|
transform: translateZ(1px);
|
||||||
|
background: var(--base-background-color);
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
&.colled {
|
||||||
|
&::before{
|
||||||
|
transform: translateZ(-1px);
|
||||||
|
background: var(--grade-color);
|
||||||
|
opacity: 0.15;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
import { useAchv, useGoHome } from '@remake/hooks'
|
||||||
|
import { judge, judgeGrade, judgeGradeByValue } from '@/config'
|
||||||
|
import { rates } from '@/display'
|
||||||
|
import { achievements, type Achievement } from '@remake/data'
|
||||||
|
import './Achv.css'
|
||||||
|
|
||||||
|
interface AchievementProps {
|
||||||
|
id: Achievement['id']
|
||||||
|
colled: boolean
|
||||||
|
}
|
||||||
|
export function AchievementItem({ id, colled }: AchievementProps) {
|
||||||
|
const { name, grade, description, hide } = achievements.get(id)!
|
||||||
|
return (
|
||||||
|
<li className={`achievement grade-${grade} ${colled ? 'colled' : ''}`}>
|
||||||
|
<span className="name">{hide && !colled ? '???' : name}</span>
|
||||||
|
<span className="description">
|
||||||
|
{hide && !colled ? '???' : description}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Achv() {
|
||||||
|
const goHome = useGoHome()
|
||||||
|
const [stats, sorted] = useAchv()
|
||||||
|
const jtimes = judge('times', stats.times)
|
||||||
|
const gtimes = judgeGrade('times', jtimes)
|
||||||
|
const jachv = judge('achievements', stats.achv)
|
||||||
|
const gachv = judgeGrade('achievements', jachv)
|
||||||
|
const gevent = judgeGradeByValue('event', stats.event)
|
||||||
|
const revent = stats.event.toFixed(2) + '%'
|
||||||
|
const gtalent = judgeGradeByValue('talent', stats.talent)
|
||||||
|
const rtalent = stats.talent.toFixed(2) + '%'
|
||||||
|
return (
|
||||||
|
<div className="screen achv">
|
||||||
|
<div className="controls">
|
||||||
|
<button className="info">排行榜</button>
|
||||||
|
<button className="primary" onClick={goHome}>
|
||||||
|
返回
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="section stats">
|
||||||
|
<div className="title">统计</div>
|
||||||
|
<div className="details">
|
||||||
|
<div className={`grade-${gtimes}`}>
|
||||||
|
<span className="title">已重开{stats.times}次</span>
|
||||||
|
<span className="value">{rates.times[jtimes]}</span>
|
||||||
|
</div>
|
||||||
|
<div className={`grade-${gachv}`}>
|
||||||
|
<span className="title">已收集成就{stats.achv}个</span>
|
||||||
|
<span className="value">
|
||||||
|
{rates.achievement[jachv]}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={`prg grade-${gevent}`}
|
||||||
|
style={{ '--progress': revent } as React.CSSProperties}
|
||||||
|
>
|
||||||
|
<span className="title">事件收集率</span>
|
||||||
|
<span className="value">{revent}</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={`prg grade-${gtalent}`}
|
||||||
|
style={{ '--progress': rtalent } as React.CSSProperties}
|
||||||
|
>
|
||||||
|
<span className="title">天赋收集率</span>
|
||||||
|
<span className="value">{rtalent}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="section achievement-list hide-scrollbar">
|
||||||
|
<div className="title">成就</div>
|
||||||
|
<ul className="details hide-scrollbar">
|
||||||
|
{sorted.map(({ id, colled }) => (
|
||||||
|
<AchievementItem key={id} id={id} colled={colled} />
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default Achv
|
||||||
@@ -21,4 +21,15 @@
|
|||||||
-webkit-tap-highlight-color: transparent;
|
-webkit-tap-highlight-color: transparent;
|
||||||
transition: background-color 0.3s;
|
transition: background-color 0.3s;
|
||||||
}
|
}
|
||||||
|
>.controls {
|
||||||
|
flex-direction: column;
|
||||||
|
> div {
|
||||||
|
flex: 1 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
> button { flex: 1 0; }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,19 +1,32 @@
|
|||||||
import { useRemake } from '@remake/hooks'
|
import { useRemake, useGoAchv } from '@remake/hooks'
|
||||||
import { TextSvg } from '@/components/TextSvg'
|
import { TextSvg } from '@/components/TextSvg'
|
||||||
import { ThemeToggle } from '@/components/ThemeToggle'
|
import { ThemeToggle } from '@/components/ThemeToggle'
|
||||||
import './Home.css'
|
import './Home.css'
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const remake = useRemake()
|
const remake = useRemake()
|
||||||
|
const goAchv = useGoAchv()
|
||||||
return (
|
return (
|
||||||
<div className="screen home">
|
<div className="screen home">
|
||||||
<div className="title">
|
<div className="title">
|
||||||
<TextSvg text="人生重开模拟器" className="main" />
|
<TextSvg text="人生重开模拟器" className="main" />
|
||||||
<TextSvg text="这垃圾人生一秒也不想待了" className="sub" />
|
<TextSvg text="这垃圾人生一秒也不想待了" className="sub" />
|
||||||
</div>
|
</div>
|
||||||
<button className="primary focus" onClick={remake}>
|
<div className="controls">
|
||||||
立即重开
|
<div>
|
||||||
</button>
|
<button className="primary focus" onClick={remake}>
|
||||||
|
立即重开
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button className="info" onClick={goAchv}>
|
||||||
|
成就
|
||||||
|
</button>
|
||||||
|
<button className="info" onClick={goAchv}>
|
||||||
|
感谢
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<ThemeToggle />
|
<ThemeToggle />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import { useCallback } from 'react'
|
||||||
|
import { events, talents, achievements } from '@remake/data'
|
||||||
|
import { useProfile, useSetStep, Step } from '.'
|
||||||
|
|
||||||
|
const TotalEvents = events.size
|
||||||
|
const TotalTalents = talents.size
|
||||||
|
|
||||||
|
export const useAchv = () => {
|
||||||
|
const [profile] = useProfile()
|
||||||
|
const o = new Map(Array.from(profile.achievements, (id, i) => [id, i]))
|
||||||
|
const sorted = Array.from(achievements.keys())
|
||||||
|
.sort((a, b) => {
|
||||||
|
if (o.has(a) && o.has(b)) return o.get(b)! - o.get(a)!
|
||||||
|
if (o.has(a)) return -1
|
||||||
|
if (o.has(b)) return 1
|
||||||
|
const { hide: ha, grade: ga } = achievements.get(a)!
|
||||||
|
const { hide: hb, grade: gb } = achievements.get(b)!
|
||||||
|
if (ha == hb) return gb - ga
|
||||||
|
return ha ? 1 : -1
|
||||||
|
})
|
||||||
|
.map(id => ({ id, colled: o.has(id) }))
|
||||||
|
const stats = {
|
||||||
|
times: profile.times,
|
||||||
|
achv: profile.achievements.size,
|
||||||
|
event: (profile.events.size / TotalEvents) * 100,
|
||||||
|
talent: (profile.talents.size / TotalTalents) * 100,
|
||||||
|
}
|
||||||
|
return [stats, sorted] as const
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useGoAchv = () => {
|
||||||
|
const setStep = useSetStep()
|
||||||
|
return useCallback(() => setStep(Step.Achv), [setStep])
|
||||||
|
}
|
||||||
@@ -42,12 +42,11 @@ export const usePoints = () => {
|
|||||||
const isClassic = useIsClassic()
|
const isClassic = useIsClassic()
|
||||||
const { additionalPoints } = useReplaced()
|
const { additionalPoints } = useReplaced()
|
||||||
const additional = additionalPoints.points
|
const additional = additionalPoints.points
|
||||||
if (!isClassic) {
|
const ba = useAtomValue(baseAllocAtom)
|
||||||
const base = useAtomValue(baseAllocAtom)
|
|
||||||
return { base: alloced(base), total: additional, additional }
|
|
||||||
}
|
|
||||||
const { points } = useConfig()
|
const { points } = useConfig()
|
||||||
return { base: points, total: points + additional, additional }
|
const base = isClassic ? points : alloced(ba)
|
||||||
|
const total = isClassic ? points + additional : additional
|
||||||
|
return { base, total, additional }
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useLeftPoints = () => {
|
export const useLeftPoints = () => {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useCallback, useState } from 'react'
|
import { useCallback, useState } from 'react'
|
||||||
import { atom, useAtom, useAtomValue, useSetAtom } from 'jotai'
|
import { atom, useAtom, useAtomValue, useSetAtom } from 'jotai'
|
||||||
import { pickedAtom, replacedAtom, baseAllocAtom } from '.'
|
import { pickedAtom, replacedAtom, baseAllocAtom } from '.'
|
||||||
import { useConfig, useProfile, useSetStep, Step } from '.'
|
import { useConfig, useSetStep, Step } from '.'
|
||||||
import { uniqueGenerate, startUnique, startChara } from '@remake/core'
|
import { uniqueGenerate, startUnique, startChara } from '@remake/core'
|
||||||
import { pullChara, pick } from '@remake/core'
|
import { pullChara, pick } from '@remake/core'
|
||||||
import type { BaseChara, PullCharaTms } from '@remake/core'
|
import type { BaseChara, PullCharaTms } from '@remake/core'
|
||||||
@@ -72,7 +72,6 @@ export const useCharaPicker = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const useCharaSubmit = () => {
|
export const useCharaSubmit = () => {
|
||||||
const [profile] = useProfile()
|
|
||||||
const unique = useAtomValue(uniqueAtom)
|
const unique = useAtomValue(uniqueAtom)
|
||||||
const setStep = useSetStep()
|
const setStep = useSetStep()
|
||||||
const setPicked = useSetAtom(pickedAtom)
|
const setPicked = useSetAtom(pickedAtom)
|
||||||
@@ -93,7 +92,7 @@ export const useCharaSubmit = () => {
|
|||||||
setBaseAlloc(result.allocation)
|
setBaseAlloc(result.allocation)
|
||||||
setStep(Step.Alloc)
|
setStep(Step.Alloc)
|
||||||
},
|
},
|
||||||
[profile, unique, setPicked, setReplaced, setStep],
|
[unique, setPicked, setReplaced, setBaseAlloc, setStep],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,4 +4,5 @@ export * from './talent'
|
|||||||
export * from './play'
|
export * from './play'
|
||||||
export * from './alloc'
|
export * from './alloc'
|
||||||
export * from './character'
|
export * from './character'
|
||||||
|
export * from './achievement'
|
||||||
export type * from '@remake/core'
|
export type * from '@remake/core'
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export enum Step {
|
|||||||
Alloc = 'alloc',
|
Alloc = 'alloc',
|
||||||
Play = 'play',
|
Play = 'play',
|
||||||
Summary = 'summary',
|
Summary = 'summary',
|
||||||
|
Achv = 'achv',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum Mode {
|
export enum Mode {
|
||||||
@@ -168,7 +169,7 @@ export const useEnd = () => {
|
|||||||
return next
|
return next
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
[lock, setLocked],
|
[lock, isClassic, setLocked],
|
||||||
)
|
)
|
||||||
const ender = useCallback(() => {
|
const ender = useCallback(() => {
|
||||||
if (!state)
|
if (!state)
|
||||||
@@ -183,6 +184,11 @@ export const useEnd = () => {
|
|||||||
setProfile(result.profile)
|
setProfile(result.profile)
|
||||||
reset()
|
reset()
|
||||||
return result.achievements
|
return result.achievements
|
||||||
}, [state, profile, locked, setStep, setProfile, reset])
|
}, [state, profile, locked, isClassic, setStep, setProfile, reset])
|
||||||
return [locked, picker, ender] as const
|
return [locked, picker, ender] as const
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const useGoHome = () => {
|
||||||
|
const setStep = useSetAtom(stepAtom)
|
||||||
|
return useCallback(() => setStep(Step.Idle), [setStep])
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user