Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/components/Rendering/canvasRenderer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ export function SimpleRenderCanvas({ layers, invert = false }: SimpleRenderProps
}



// Advanced card rendering with canvas
export function RenderImagesWithCanvas({layers, invert = false, spacing = false}: RenderCanvasProps) {
const canvasRef = useRef(null);
Expand Down
127 changes: 80 additions & 47 deletions src/components/SeedInputAutoComplete.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,51 @@
import {Autocomplete, Button, Group, NativeSelect, Paper} from "@mantine/core";
import React, {useState, useRef} from "react";
import { Autocomplete, Button, Group, NativeSelect, Paper } from "@mantine/core";
import { useDebouncedCallback } from "@mantine/hooks";
import {popularSeeds, SeedsWithLegendary} from "../modules/const.ts";
import {useCardStore} from "../modules/state/store.ts";
import {sanitizeSeed} from "../modules/utils.ts";

const seedAutoCompleteData = [
{
group: 'Popular Seeds',
items: popularSeeds
}, {
group: 'Generated Seeds With Legendary Jokers',
items: SeedsWithLegendary
}
];

const allSuggestions = [...popularSeeds, ...SeedsWithLegendary];

interface SeedInputProps {
seed: string;
setSeed: (seed: string) => void;
w?: number | string;
showDeckSelect?: boolean;
label?: string;
placeholder?: string;
}

function SeedInputAutoComplete({ seed, setSeed, w, showDeckSelect, label = 'Seed', placeholder = 'Enter Seed' }: SeedInputProps) {
const [localSeed, setLocalSeed] = useState(seed);
const isDirty = useRef(false);

// Sync from store when not actively editing
if (!isDirty.current && localSeed !== seed) {
setLocalSeed(seed);
}

const debouncedSetSeed = useDebouncedCallback((value: string) => {
setLocalSeed(sanitizeSeed(value));
if (value) setSeed(value);
isDirty.current = false;
}, 160);

export function QuickAnalyze() {
const seed = useCardStore(state => state.immolateState.seed);
const setSeed = useCardStore(state => state.setSeed);
const deck = useCardStore(state => state.immolateState.deck);
const setDeck = useCardStore(state => state.setDeck);
const setStart = useCardStore(state => state.setStart);

const sectionWidth = 130;
const select = (
const deckSelect = showDeckSelect ? (
<NativeSelect
rightSectionWidth={28}
styles={{
Expand Down Expand Up @@ -41,56 +76,54 @@ export function QuickAnalyze() {
<option value="Plasma Deck">Plasma Deck</option>
<option value="Erratic Deck">Erratic Deck</option>
</NativeSelect>
) : undefined;

return (
<Autocomplete
flex={1}
w={w}
type="text"
label={label}
placeholder={placeholder}
data={seedAutoCompleteData}
value={localSeed}
onChange={(value) => {
isDirty.current = true;
setLocalSeed(value);
if (allSuggestions.includes(value)) {
setSeed(value);
isDirty.current = false;
} else {
debouncedSetSeed(value);
}
}}
rightSection={deckSelect}
rightSectionWidth={showDeckSelect ? sectionWidth : undefined}
/>
);
}

export function QuickAnalyze() {
const seed = useCardStore(state => state.immolateState.seed);
const setSeed = useCardStore(state => state.setSeed);
const setStart = useCardStore(state => state.setStart);

return (
<Paper withBorder shadow={'lg'} p={'1rem'} radius={'md'}>
<Group align={'flex-end'}>
<Autocomplete
flex={1}
<SeedInputAutoComplete
seed={seed}
setSeed={setSeed}
w={500}
type="text"
placeholder="Enter Seed"
showDeckSelect
label="Analyze Seed"
data={[
{
group: 'Popular Seeds',
items: popularSeeds
}, {
group: 'Generated Seeds With Legendary Jokers',
items: SeedsWithLegendary

}
]}
value={seed}
onChange={(e) => setSeed(e)}
rightSection={select}
rightSectionWidth={sectionWidth}
/>
<Button onClick={() => setStart(seed.length >= 5)}> Analyze Seed </Button>
<Button onClick={() => {
setStart(seed.length > 0);
}}> Analyze Seed </Button>
</Group>
</Paper>
);

}

export default function SeedInputAutoComplete({seed, setSeed}: { seed: string, setSeed: (seed: string) => void }) {
return (
<Autocomplete
flex={1}
label={'Seed'}
placeholder={'Enter Seed'}
value={seed}
onChange={(e) => setSeed(e)}
data={[
{
group: 'Popular Seeds',
items: popularSeeds
}, {
group: 'Generated Seeds With Legendary Jokers',
items: SeedsWithLegendary

}
]}
/>
);
}
export default SeedInputAutoComplete;
2 changes: 1 addition & 1 deletion src/components/blueprint/layout/footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {GaEvent} from "../../../modules/useGA.ts";


export default function Footer() {
const {data: supporters, isPending} = useQuery<Array<{ name: string, subscription: boolean }>>({
const {data: supporters, isPending: isPending} = useQuery<Array<{ name: string, subscription: boolean }>>({
Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my IDE said isPending was not defined, but, it was here but there's something weird with destructuring this

queryKey: ['supporters'],
queryFn: async () => {
const response = await fetch('https://ttyyetpmvt.a.pinggy.link/supporters', {
Expand Down
31 changes: 15 additions & 16 deletions src/components/blueprint/layout/navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
useMantineColorScheme,
useMantineTheme
} from "@mantine/core";
import React from "react";
import React, {useState, useEffect} from "react";
import {
IconFileText,
IconJoker,
Expand All @@ -30,9 +30,10 @@ import {
import { useCardStore } from "../../../modules/state/store.ts";
import UnlocksModal from "../../unlocksModal.tsx";
import FeaturesModal from "../../FeaturesModal.tsx";
import { RerollCalculatorModal } from "../../RerollCalculatorModal.tsx";
import {RerollCalculatorModal} from "../../RerollCalculatorModal.tsx";
import {GaEvent} from "../../../modules/useGA.ts";
import { useDebouncedCallback } from "@mantine/hooks";
import { DrawSimulatorModal } from "../../DrawSimulatorModal.tsx";
import { GaEvent } from "../../../modules/useGA.ts";
import SeedInputAutoComplete from "../../SeedInputAutoComplete.tsx";
import { useBlueprintTheme } from "../../../modules/state/themeProvider.tsx";
import type { KnownThemes } from "../../../modules/state/themeProvider.tsx";
Expand Down Expand Up @@ -72,13 +73,16 @@ export default function NavBar() {
const reset = useCardStore(state => state.reset);
const hasSettingsChanged = useCardStore((state) => state.applicationState.hasSettingsChanged);

const [localAntes, setLocalAntes] = useState<number | string>(antes);
useEffect(() => { setLocalAntes(antes); }, [antes]);
const debouncedSetAntes = useDebouncedCallback((val: number) => {
if (val !== antes) setAntes(val);
}, 200);

const handleAnalyzeClick = () => {
setStart(true);
}




return (
<AppShell.Navbar p="md">
<UnlocksModal />
Expand Down Expand Up @@ -160,16 +164,11 @@ export default function NavBar() {
id="setting-max-ante"
label={'Max Ante'}
defaultValue={8}
value={antes}
onChange={(val) => {
// Guard against null/NaN from the NumberInput
if (val === null || Number.isNaN(Number(val))) {
// keep previous value (do not set) or fallback to 1 to be defensive
// Here we fallback to 1 to avoid passing invalid values into the engine
setAntes(1);
} else {
setAntes(Math.floor(Number(val)));
}
value={localAntes}
onChange={(val: number | string) => {
const num = typeof val === 'string' ? parseInt(val) || 8 : val;
setLocalAntes(num);
debouncedSetAntes(num);
}}
/>
<NativeSelect
Expand Down
15 changes: 3 additions & 12 deletions src/components/blueprint/simpleView/simple.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
Tooltip
} from "@mantine/core";
import React, {useEffect, useRef, useState} from "react";
import {useDebouncedCallback, useIntersection} from "@mantine/hooks";
import {useIntersection} from "@mantine/hooks";
import {IconLockOpen} from "@tabler/icons-react";
import {
bosses,
Expand Down Expand Up @@ -348,11 +348,8 @@ function AnteSkeletonLoader() {
function Simple() {
const SeedResults = useSeedResultsContainer()
const containerRef = useRef<HTMLDivElement>(null);
const [visibleAntes, setVisibleAntes] = useState<Array<number>>([1]); // Start with first ante visible
const [loadingNextAnte, setLoadingNextAnte] = useState<number | null>(2); // Track which ante is loading
const selectedAnte = useCardStore(state => state.applicationState.selectedAnte);
const setSelectedAnte = useCardStore(state => state.setSelectedAnte);
const debouncedSetSelectedAnte = useDebouncedCallback(setSelectedAnte, 500)
const [visibleAntes, setVisibleAntes] = useState<Array<number>>([1]);
const [loadingNextAnte, setLoadingNextAnte] = useState<number | null>(2);
const lockedCards = useCardStore(state => state.lockState.lockedCards);
const clearLockedCards = useCardStore(state => state.clearLockedCards);
const hasLockedCards = Object.keys(lockedCards).length > 0;
Expand All @@ -373,12 +370,6 @@ function Simple() {

useEffect(() => {
if (entry?.isIntersecting) {
// When this ante is visible, make the next one available
const currentAnte = anteNumber;
if (currentAnte !== selectedAnte) {
debouncedSetSelectedAnte(currentAnte);
}
// Set the current ante as selected
const nextAnte = anteNumber + 1;

if (nextAnte <= anteEntries.length && !visibleAntes.includes(nextAnte)) {
Expand Down
2 changes: 1 addition & 1 deletion src/components/blueprint/standardView/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,7 @@ export function Blueprint() {
const outputOpened = useCardStore(state => state.applicationState.asideOpen);
const download = useDownloadSeedResults()
useEffect(() => {
if (typeof window !== 'undefined') {
if(typeof window !== 'undefined' && !!download) {
window.saveSeedDebug = download
}
}, [download]);
Expand Down
2 changes: 2 additions & 0 deletions src/modules/ImmolateWrapper/CardEngines/Cards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,9 +295,11 @@ export class Pack {
}

export class SeedResultsContainer {
isLoading: boolean;
antes: { [key: number]: Ante };
constructor() {
this.antes = {}
this.isLoading = true;
}
}
export interface Blind {
Expand Down
6 changes: 5 additions & 1 deletion src/modules/ImmolateWrapper/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,14 @@ import {
StandardCard_Final,
Tarot_Final
} from "./CardEngines/Cards.ts";

import type { Voucher } from "../balatrots/enum/Voucher.ts";
import { Edition, EditionItem } from "../balatrots/enum/Edition.ts";
import { Seal, SealItem } from "../balatrots/enum/Seal.ts";
import type { DeckCard } from "../deckUtils.ts";

import {sanitizeSeed} from "../utils.ts";

export type SpoilableItems = "The Soul" | "Judgement" | "Wraith";
export interface MiscCardSource {
name: SpoilableItems | string;
Expand Down Expand Up @@ -489,7 +492,7 @@ export const getMiscCardSources: (maxCards: number) => Array<MiscCardSource> = (

export function analyzeSeed(settings: AnalyzeSettings, analyzeOptions: AnalyzeOptions) {

const seed = settings.seed.toUpperCase().replace(/0/g, 'O').trim();
const seed = sanitizeSeed(settings.seed);

if (!seed) return;
// Sanitize antes coming from settings (could be null/NaN/0 if UI or URL provided empty value)
Expand All @@ -498,6 +501,7 @@ export function analyzeSeed(settings: AnalyzeSettings, analyzeOptions: AnalyzeOp
const maxAntes = Math.max(1, safeAntes);

const output = new SeedResultsContainer();
// isLoading starts true in constructor, analysis populates antes, provider sets false after
const deck = new Deck(deckMap[settings.deck])
const stake = new Stake(settings.stake as StakeType)
const version = Number(settings.gameVersion)
Expand Down
10 changes: 4 additions & 6 deletions src/modules/state/analysisResultProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ export const SeedResultContext = createContext<SeedResultsContainer | null | und

export function useSeedResultsContainer() {
const context = useContext(SeedResultContext);
console.log(context)
if (context === null) {
throw new Error("useSeedResultsContainer must be used within a SeedResultProvider");
}
Expand All @@ -29,14 +28,13 @@ export function SeedResultProvider({ children }: { children: React.ReactNode })
return analyzeSeed(analyzeState, {
...options,
customDeck: deckState.cards
})
});
}, [analyzeState, deckState.cards, options, start]);


return (
<SeedResultContext.Provider value={seedResult}>
{children}
{children}
</SeedResultContext.Provider>
)


}
}
9 changes: 5 additions & 4 deletions src/modules/state/downloadProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React, { createContext, useCallback, useContext } from "react";
import { useCardStore } from "./store.ts";
import { useSeedOptionsContainer } from "./optionsProvider.tsx";
import { useSeedResultsContainer } from "./analysisResultProvider.tsx";
import {useCardStore} from "./store.ts";
import {useSeedOptionsContainer} from "./optionsProvider.tsx";
import {SeedResultContext} from "./analysisResultProvider.tsx";


export type DownloadSeedResultFunction = () => void;
export const DownloadSeedResultContext = createContext<DownloadSeedResultFunction | undefined>(undefined);
Expand All @@ -18,7 +19,7 @@ export function DownloadSeedResultProvider({ children }: { children: React.React

const analyzeState = useCardStore(state => state.immolateState);
const options = useSeedOptionsContainer()
const SeedResults = useSeedResultsContainer()
const SeedResults = useContext(SeedResultContext);

const downloadImmolateResults = useCallback(() => {
const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(
Expand Down
Loading
Loading