@@ -181,6 +181,7 @@ type ReducedSim = {
181181
182182type GachaTarget = {
183183 id : string
184+ enabled : boolean
184185 banner : Banner
185186 current : number
186187 target : number
@@ -194,6 +195,7 @@ let gtCounter = 0
194195function createDefaultTarget ( banner : Banner ) : GachaTarget {
195196 return {
196197 id : gtCounter ++ + Math . random ( ) . toString ( 36 ) . substring ( 2 , 15 ) ,
198+ enabled : true ,
197199 banner,
198200 current : banner . minConst ,
199201 target : banner . maxConst ,
@@ -208,11 +210,33 @@ export default function GachaCalc({ location }: { location: string }) {
208210 const [ gachaTargets , setGachaTargets ] = useState < GachaTarget [ ] > ( [ createDefaultTarget ( gachas . char ) ] )
209211 const [ pulls , setPulls ] = useState ( gachaTargets [ 0 ] . banner . maxPity )
210212
211- const calculated = useMemo ( ( ) => calcSimsRegular ( pulls , gachaTargets ) , [ gachaTargets , pulls ] )
213+ useEffect ( ( ) => {
214+ const items = JSON . parse ( localStorage . getItem ( "gachaTargets" ) ?? "[]" )
215+ if ( items . length > 0 ) {
216+ // Force low amount as calculations could have exploded
217+ if ( items . filter ( ( x : any ) => x . enabled ) . length > 5 ) setPulls ( 1 )
218+ else if ( items . filter ( ( x : any ) => x . enabled ) . length > 2 ) setPulls ( 10 )
219+
220+ setGachaTargets ( items . map ( ( gt : any ) => ( {
221+ ...gt ,
222+ banner : gachas [ gt . banner ] ?? Object . values ( gachas ) [ 0 ] ,
223+ } ) ) )
224+ }
225+ } , [ ] )
226+
227+ useEffect ( ( ) => {
228+ localStorage . setItem ( "gachaTargets" , JSON . stringify ( gachaTargets . map ( gt => ( {
229+ ...gt ,
230+ banner : Object . entries ( gachas ) . find ( ( [ _ , v ] ) => v . bannerName == gt . banner . bannerName ) ?. [ 0 ] ,
231+ } ) ) ) )
232+ } , [ gachaTargets ] )
233+
234+ const enabledTargets = useMemo ( ( ) => gachaTargets . filter ( gt => gt . enabled ) , [ gachaTargets ] )
235+ const calculated = useMemo ( ( ) => calcSimsRegular ( pulls , enabledTargets ) , [ enabledTargets , pulls ] )
212236
213237 const consts = [ ]
214- for ( let index = 0 ; index < gachaTargets . length ; index ++ ) {
215- const gachaTarget = gachaTargets [ index ]
238+ for ( let index = 0 ; index < enabledTargets . length ; index ++ ) {
239+ const gachaTarget = enabledTargets [ index ]
216240 for ( let i = gachaTarget . current + 1 ; i <= gachaTarget . target ; i ++ )
217241 consts . push ( {
218242 gachaTargetIndex : index ,
@@ -221,8 +245,8 @@ export default function GachaCalc({ location }: { location: string }) {
221245 } )
222246 }
223247
224- const constName = gachaTargets . map ( gt => gt . banner . constName ) . filter ( ( x , i , a ) => a . indexOf ( x ) == i ) . join ( "/" )
225- const constFormat = gachaTargets . map ( gt => gt . banner . constFormat ) . filter ( ( x , i , a ) => a . indexOf ( x ) == i ) . join ( "/" )
248+ const constName = enabledTargets . map ( gt => gt . banner . constName ) . filter ( ( x , i , a ) => a . indexOf ( x ) == i ) . join ( "/" )
249+ const constFormat = enabledTargets . map ( gt => gt . banner . constFormat ) . filter ( ( x , i , a ) => a . indexOf ( x ) == i ) . join ( "/" )
226250
227251 const desc = "Gacha rate calculator for Genshin Impact."
228252 return (
@@ -244,15 +268,42 @@ export default function GachaCalc({ location }: { location: string }) {
244268 < h1 className = "text-5xl font-bold pb-2" > Gacha rate calculator</ h1 >
245269
246270 < NumberInput label = "Pulls" set = { setPulls } value = { pulls } min = { 0 } max = { 1260 * gachaTargets . length } />
247- { gachaTargets . map ( ( gachaTarget , index ) => < div key = { gachaTarget . id } className = "bg-blend-multiply bg-slate-600 rounded-xl p-1 my-2" >
248- { gachaTargets . length > 1 &&
249- < button className = "bg-red-700 text-slate-50 cursor-pointer text-center rounded-lg px-2 py-1 my-2 float-right"
250- onClick = { ( ) =>
251- setGachaTargets ( gachaTargets . filter ( ( _ , i ) => i != index ) )
252- } >
253- Remove gacha target
254- </ button >
255- }
271+ { gachaTargets . map ( ( gachaTarget , index ) => < div key = { gachaTarget . id } className = { `bg-slate-600 ${ gachaTarget . enabled ? "" : "bg-opacity-25 bg-red-800" } rounded-xl p-1 my-2 flex flex-row gap-2` } >
272+ < div className = "flex flex-col items-center justify-center gap-2" >
273+ < div >
274+ < button className = "bg-slate-900 text-slate-50 cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed text-center rounded-lg px-2 py-1"
275+ disabled = { ! ( gachaTargets . length > 1 && index > 0 ) }
276+ onClick = { ( ) => {
277+ // Move index up
278+ const newGachaTargets = [ ...gachaTargets ]
279+ newGachaTargets [ index ] = gachaTargets [ index - 1 ]
280+ newGachaTargets [ index - 1 ] = gachaTargets [ index ]
281+ setGachaTargets ( newGachaTargets )
282+ } } >
283+ ↑
284+ </ button >
285+ </ div >
286+ < div >
287+ < button className = "bg-red-700 text-slate-50 cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed text-center rounded-lg px-2 py-1"
288+ disabled = { ! ( gachaTargets . length > 1 ) }
289+ onClick = { ( ) => setGachaTargets ( gachaTargets . filter ( ( _ , i ) => i != index ) ) } >
290+ ×
291+ </ button >
292+ </ div >
293+ < div >
294+ < button className = "bg-slate-900 text-slate-50 cursor-pointer disabled:opacity-50 disabled:cursor-not-allowed text-center rounded-lg px-2 py-1"
295+ disabled = { ! ( gachaTargets . length > 1 && index < gachaTargets . length - 1 ) }
296+ onClick = { ( ) => {
297+ // Move index down
298+ const newGachaTargets = [ ...gachaTargets ]
299+ newGachaTargets [ index ] = gachaTargets [ index + 1 ]
300+ newGachaTargets [ index + 1 ] = gachaTargets [ index ]
301+ setGachaTargets ( newGachaTargets )
302+ } } >
303+ ↓
304+ </ button >
305+ </ div >
306+ </ div >
256307 < GachaTargetInput value = { gachaTarget } set = { newGachaTarget => setGachaTargets ( gachaTargets . map ( ( gt , i ) => i == index ? newGachaTarget : gt ) ) } />
257308 </ div > ) }
258309
@@ -341,11 +392,11 @@ export default function GachaCalc({ location }: { location: string }) {
341392 datasets : consts
342393 . map ( ( gtc , x , arr ) => ( {
343394 label : getName ( gtc ) ,
344- backgroundColor : getColor ( gtc , gachaTargets , 1 ) ,
345- borderColor : getColor ( gtc , gachaTargets , 1 ) ,
395+ backgroundColor : getColor ( gtc , enabledTargets , 1 ) ,
396+ borderColor : getColor ( gtc , enabledTargets , 1 ) ,
346397 fill : {
347- above : getColor ( gtc , gachaTargets , 0.15 ) ,
348- below : getColor ( gtc , gachaTargets , 0.1 ) ,
398+ above : getColor ( gtc , enabledTargets , 0.15 ) ,
399+ below : getColor ( gtc , enabledTargets , 0.1 ) ,
349400 target : arr . indexOf ( gtc ) == arr . length - 1 ? "start" : x + 1 ,
350401 } ,
351402 data : calculated . map ( c => c
@@ -477,6 +528,7 @@ function GachaTargetInput({
477528 value : GachaTarget ;
478529 set : ( newValue : GachaTarget ) => unknown ;
479530} ) {
531+ const [ enabled , setEnabled ] = useState ( value . enabled )
480532 const [ current , setCurrent ] = useState ( value . current )
481533 const [ target , setTarget ] = useState ( value . target )
482534 const [ pity , setPity ] = useState ( value . pity )
@@ -498,6 +550,7 @@ function GachaTargetInput({
498550
499551 useEffect ( ( ) => {
500552 if ( banner . bannerName != value . banner . bannerName ) { }
553+ else if ( enabled != value . enabled ) { }
501554 else if ( current != value . current ) { }
502555 else if ( target != value . target ) { }
503556 else if ( pity != value . pity ) { }
@@ -508,6 +561,7 @@ function GachaTargetInput({
508561
509562 set ( {
510563 id : value . id ,
564+ enabled,
511565 banner,
512566 current,
513567 target,
@@ -516,31 +570,30 @@ function GachaTargetInput({
516570 guaranteedPity,
517571 lostPity,
518572 } )
519- } , [ set , value , banner , current , target , pity , guaranteed , guaranteedPity , lostPity ] )
520- return (
521- < >
522- < SelectInput label = "Banner type" set = { ( g ) => {
523- if ( current == banner . minConst )
524- setCurrent ( ( Object . values ( gachas ) . find ( ( x ) => x . bannerName == g ) ?? Object . values ( gachas ) [ 0 ] ) . minConst )
525- if ( target == banner . maxConst )
526- setTarget ( ( Object . values ( gachas ) . find ( ( x ) => x . bannerName == g ) ?? Object . values ( gachas ) [ 0 ] ) . maxConst )
527- setGacha ( g )
528- } }
529- value = { gachaName }
530- options = { Object . values ( gachas ) . map ( ( g ) => g . bannerName ) }
531- />
532- < NumberInput label = { `Current ${ banner . constName . toLowerCase ( ) } ` } set = { setCurrent } value = { current } min = { banner . minConst } max = { target - 1 } />
533- < NumberInput label = { `Target ${ banner . constName . toLowerCase ( ) } ` } set = { setTarget } value = { target } min = { current + 1 } max = { banner . maxConst } />
534- < NumberInput label = "Current pity" set = { setPity } value = { pity } min = { 0 } max = { banner . maxPity - 1 } />
535- < CheckboxInput label = "Next is guaranteed" set = { setGuaranteed } value = { guaranteed } />
536- { banner . guaranteedPity && (
537- < NumberInput label = "Epitomized Path" set = { setGuaranteedPity } value = { guaranteedPity } min = { 0 } max = { banner . guaranteedPity - 1 } />
538- ) }
539- { Array . isArray ( banner . banner ) && (
540- < NumberInput label = "Lost pity (Capturing Radiance)" set = { setLostPity } value = { lostPity } min = { 0 } max = { banner . banner . length - 1 } />
541- ) }
542- </ >
543- )
573+ } , [ set , value , enabled , banner , current , target , pity , guaranteed , guaranteedPity , lostPity ] )
574+ return < div >
575+ < CheckboxInput label = "Enabled" set = { setEnabled } value = { enabled } />
576+ < SelectInput label = "Banner type" set = { ( g ) => {
577+ if ( current == banner . minConst )
578+ setCurrent ( ( Object . values ( gachas ) . find ( ( x ) => x . bannerName == g ) ?? Object . values ( gachas ) [ 0 ] ) . minConst )
579+ if ( target == banner . maxConst )
580+ setTarget ( ( Object . values ( gachas ) . find ( ( x ) => x . bannerName == g ) ?? Object . values ( gachas ) [ 0 ] ) . maxConst )
581+ setGacha ( g )
582+ } }
583+ value = { gachaName }
584+ options = { Object . values ( gachas ) . map ( ( g ) => g . bannerName ) }
585+ />
586+ < NumberInput label = { `Current ${ banner . constName . toLowerCase ( ) } ` } set = { setCurrent } value = { current } min = { banner . minConst } max = { target - 1 } />
587+ < NumberInput label = { `Target ${ banner . constName . toLowerCase ( ) } ` } set = { setTarget } value = { target } min = { current + 1 } max = { banner . maxConst } />
588+ < NumberInput label = "Current pity" set = { setPity } value = { pity } min = { 0 } max = { banner . maxPity - 1 } />
589+ < CheckboxInput label = "Next is guaranteed" set = { setGuaranteed } value = { guaranteed } />
590+ { banner . guaranteedPity && (
591+ < NumberInput label = "Epitomized Path" set = { setGuaranteedPity } value = { guaranteedPity } min = { 0 } max = { banner . guaranteedPity - 1 } />
592+ ) }
593+ { Array . isArray ( banner . banner ) && (
594+ < NumberInput label = "Lost pity (Capturing Radiance)" set = { setLostPity } value = { lostPity } min = { 0 } max = { banner . banner . length - 1 } />
595+ ) }
596+ </ div >
544597}
545598
546599function NumberInput ( { value, set, label, min, max } : {
0 commit comments