1+ import {
2+ BarElement , CategoryScale , Chart as ChartJS , Legend , LinearScale , LineElement , PointElement , Tooltip
3+ } from "chart.js"
14import Head from "next/head"
2- import { useEffect , useState } from "react"
5+ import { DependencyList , EffectCallback , useEffect , useState } from "react"
6+ import { Chart } from "react-chartjs-2"
37import FormattedLink from "../../components/FormattedLink"
48import Main from "../../components/Main"
59import styles from "../style.module.css"
610
11+ ChartJS . register (
12+ LinearScale ,
13+ CategoryScale ,
14+ BarElement ,
15+ PointElement ,
16+ LineElement ,
17+ Legend ,
18+ Tooltip
19+ )
20+
721function pityRate ( baseRate : number , pityStart : number ) : ( pity : number ) => number {
822 return ( pity ) => pity < pityStart ? baseRate : baseRate + baseRate * 10 * ( pity - pityStart + 1 )
923}
@@ -69,6 +83,7 @@ type ReducedSim = {
6983 rate : number
7084}
7185
86+
7287export default function GachaCalc ( { location } : { location : string } ) {
7388 const [ current , setCurrent ] = useState ( - 1 )
7489 const [ pity , setPity ] = useState ( 0 )
@@ -82,10 +97,18 @@ export default function GachaCalc({ location }: { location: string }) {
8297
8398 const banner = Object . values ( gachas ) . find ( x => x . bannerName == gachaName ) ?? Object . values ( gachas ) [ 0 ]
8499
85- if ( pity >= banner . maxPity ) setPity ( banner . maxPity - 1 )
86- if ( banner . guaranteedPity && guaranteedPity >= banner . guaranteedPity ) setGuaranteedPity ( banner . guaranteedPity )
87- if ( current > banner . maxConst ) setCurrent ( banner . maxConst )
88- if ( current < banner . minConst ) setCurrent ( banner . minConst )
100+ function delayed ( f : EffectCallback ) {
101+ const timeout = setTimeout ( ( ) => {
102+ f ( )
103+ } , 3000 )
104+
105+ return ( ) => clearTimeout ( timeout )
106+ }
107+
108+ useEffect ( ( ) => delayed ( ( ) => { if ( pity >= banner . maxPity ) setPity ( banner . maxPity - 1 ) } ) , [ banner , pity ] )
109+ useEffect ( ( ) => delayed ( ( ) => { if ( banner . guaranteedPity && guaranteedPity >= banner . guaranteedPity ) setGuaranteedPity ( banner . guaranteedPity ) } ) , [ banner , guaranteedPity ] )
110+ useEffect ( ( ) => delayed ( ( ) => { if ( current > banner . maxConst ) setCurrent ( banner . maxConst ) } ) , [ banner , current ] )
111+ useEffect ( ( ) => delayed ( ( ) => { if ( current < banner . minConst ) setCurrent ( banner . minConst ) } ) , [ current , banner ] )
89112
90113 useEffect (
91114 ( ) => setCalculated ( calcSimsRegular ( current , pity , pulls , guaranteed , guaranteedPity , banner ) ) ,
@@ -115,7 +138,7 @@ export default function GachaCalc({ location }: { location: string }) {
115138
116139 < SelectInput label = "Banner type" set = { ( g ) => {
117140 if ( current == banner . minConst )
118- setCurrent ( - 5 )
141+ setCurrent ( ( Object . values ( gachas ) . find ( x => x . bannerName == g ) ?? Object . values ( gachas ) [ 0 ] ) . minConst )
119142 setGacha ( g )
120143 } } value = { gachaName } options = { Object . values ( gachas ) . map ( g => g . bannerName ) } />
121144 < NumberInput label = "Pulls" set = { setPulls } value = { pulls } min = { 0 } max = { 1260 } />
@@ -125,7 +148,76 @@ export default function GachaCalc({ location }: { location: string }) {
125148 { banner . guaranteedPity && < NumberInput label = "Epitomized Path" set = { setGuaranteedPity } value = { guaranteedPity } min = { 0 } max = { banner . guaranteedPity - 1 } /> }
126149
127150 < h3 className = "text-lg font-bold pt-1" id = "resistance" > Results:</ h3 >
128- < table className = { `table-auto w-80 ${ styles . table } ${ styles . stattable } mb-2 sm:text-base text-sm` } >
151+ < div className = "columns-1 md:columns-2 mr-2" >
152+ < div className = "w-full bg-slate-800 rounded-xl p-1 my-2 md:my-0 text-white col-start-1" >
153+ < Chart type = "bar" data = { ( {
154+ labels : calculated . filter ( x => x ) . map ( c => getName ( c , banner ) ) ,
155+ datasets : [
156+ {
157+ type : "bar" as const ,
158+ label : "Rate" ,
159+ backgroundColor : "rgb(75, 192, 192)" ,
160+ data : calculated . filter ( x => x ) . map ( ( c , i , a ) => c . rate * 100 ) ,
161+ borderColor : "white" ,
162+ borderWidth : 2 ,
163+ xAxisID : "xAxes"
164+ } ,
165+ ] ,
166+ } ) } options = { ( {
167+ indexAxis : "y" ,
168+ color : "white" ,
169+ backgroundColor : "#333333" ,
170+ scales : {
171+ xAxes : {
172+ min : 0 ,
173+ ticks : {
174+ color : "white" ,
175+ callback : ( v ) => `${ v } %`
176+ }
177+ } ,
178+ yAxes : {
179+ ticks : {
180+ color : "white"
181+ }
182+ }
183+ }
184+ } ) } /> </ div >
185+ < div className = "w-full bg-slate-800 rounded-xl p-1 my-2 md:my-0 text-white col-start-2" >
186+ < Chart type = "bar" data = { ( {
187+ labels : calculated . filter ( x => x ) . map ( c => getName ( c , banner ) ) ,
188+ datasets : [
189+ {
190+ type : "line" as const ,
191+ label : "Cumulative rate" ,
192+ borderColor : "rgb(255, 99, 132)" ,
193+ borderWidth : 2 ,
194+ fill : false ,
195+ data : calculated . filter ( x => x ) . map ( ( c , i , a ) => a . slice ( i , a . length ) . reduce ( ( p , c ) => p + c . rate , 0 ) * 100 ) ,
196+ } ,
197+ ] ,
198+ } ) } options = { ( {
199+ indexAxis : "y" ,
200+ color : "white" ,
201+ backgroundColor : "#333333" ,
202+ scales : {
203+ xAxes : {
204+ max : 100 ,
205+ min : 0 ,
206+ ticks : {
207+ color : "white" ,
208+ callback : ( v ) => `${ v } %`
209+ }
210+ } ,
211+ yAxes : {
212+ ticks : {
213+ color : "white"
214+ }
215+ }
216+ }
217+ } ) } />
218+ </ div >
219+ </ div >
220+ < table className = { `table-auto w-80 ${ styles . table } ${ styles . stattable } my-2 sm:text-base text-sm` } >
129221 < thead >
130222 < tr className = "divide-x divide-gray-200 dark:divide-gray-500" >
131223 < th > { banner . constName } </ th >
@@ -134,9 +226,9 @@ export default function GachaCalc({ location }: { location: string }) {
134226 </ tr >
135227 </ thead >
136228 < tbody className = "divide-y divide-gray-200 dark:divide-gray-500" >
137- { calculated
229+ { calculated . filter ( x => x )
138230 . map ( ( c , i , a ) => < tr className = { `pr-1 divide-x divide-gray-200 dark:divide-gray-500 ${ c . rate < 0.0005 ? "opacity-60" : "" } ` } key = { c . const } >
139- < td > { c . const == banner . minConst ? "Not owned" : ` ${ banner . constFormat } ${ c . const } ` } </ td >
231+ < td > { getName ( c , banner ) } </ td >
140232 < td > { ( c . rate * 100 ) . toFixed ( 3 ) } %</ td >
141233 < td > { ( a . slice ( i , a . length ) . reduce ( ( p , c ) => p + c . rate , 0 ) * 100 ) . toFixed ( 2 ) } %</ td >
142234 </ tr > ) }
@@ -146,6 +238,10 @@ export default function GachaCalc({ location }: { location: string }) {
146238 )
147239}
148240
241+ function getName ( c : ReducedSim , banner : Banner ) {
242+ return c . const == banner . minConst ? "Not owned" : `${ banner . constFormat } ${ c . const } `
243+ }
244+
149245function NumberInput ( { value, set, label, min, max } : { value : number , set : ( newValue : number ) => unknown , label : string , min ?: number , max ?: number } ) {
150246 return < div > < label >
151247 { label }
0 commit comments