Add desktop games, robot pet and TCG app
This commit is contained in:
1 parent
812503251b
commit
c664f573b8
55 files changed
+5090
-15
No files matched your search
@@ -0,0 +1,147 @@
|
||||
import type { DeckCategory } from '#lib/content';
|
||||
import { cardCategory, categoryOrder, categoryTitles, copyLimit, deckSizeLimit, isBasicEnergy } from './cardKind';
|
||||
import type { TcgCard } from './types';
|
||||
|
||||
export interface BuilderCard {
|
||||
id: string;
|
||||
name: string;
|
||||
supertype?: string;
|
||||
subtypes?: string[];
|
||||
image: string;
|
||||
setCode?: string;
|
||||
number?: string;
|
||||
}
|
||||
|
||||
export interface BuilderEntry {
|
||||
card: BuilderCard;
|
||||
count: number;
|
||||
}
|
||||
|
||||
export interface BuilderSection {
|
||||
category: DeckCategory;
|
||||
title: string;
|
||||
total: number;
|
||||
entries: BuilderEntry[];
|
||||
}
|
||||
|
||||
const storageKey = 'tcg-deck-builder';
|
||||
const defaultDeckName = 'my deck';
|
||||
|
||||
export function toBuilderCard(card: TcgCard): BuilderCard {
|
||||
return {
|
||||
id: card.id,
|
||||
name: card.name,
|
||||
supertype: card.supertype,
|
||||
subtypes: card.subtypes,
|
||||
image: card.images.small,
|
||||
setCode: card.set.ptcgoCode,
|
||||
number: card.number
|
||||
};
|
||||
}
|
||||
|
||||
class DeckBuilderState {
|
||||
name = $state(defaultDeckName);
|
||||
entries = $state<BuilderEntry[]>([]);
|
||||
|
||||
private loaded = false;
|
||||
|
||||
total = $derived(this.entries.reduce((sum, entry) => sum + entry.count, 0));
|
||||
|
||||
sections = $derived<BuilderSection[]>(
|
||||
categoryOrder
|
||||
.map((category) => {
|
||||
const entries = this.entries.filter((entry) => cardCategory(entry.card) === category);
|
||||
return {
|
||||
category,
|
||||
title: categoryTitles[category],
|
||||
total: entries.reduce((sum, entry) => sum + entry.count, 0),
|
||||
entries
|
||||
};
|
||||
})
|
||||
.filter((section) => section.entries.length > 0)
|
||||
);
|
||||
|
||||
load() {
|
||||
if (this.loaded) return;
|
||||
this.loaded = true;
|
||||
try {
|
||||
const stored = JSON.parse(localStorage.getItem(storageKey) ?? 'null');
|
||||
if (!stored) return;
|
||||
this.name = typeof stored.name === 'string' ? stored.name : defaultDeckName;
|
||||
this.entries = Array.isArray(stored.entries) ? stored.entries : [];
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private save() {
|
||||
try {
|
||||
localStorage.setItem(storageKey, JSON.stringify({ name: this.name, entries: this.entries }));
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
countOf(cardId: string): number {
|
||||
return this.entries.find((entry) => entry.card.id === cardId)?.count ?? 0;
|
||||
}
|
||||
|
||||
private copiesNamed(name: string): number {
|
||||
return this.entries.filter((entry) => entry.card.name === name).reduce((sum, entry) => sum + entry.count, 0);
|
||||
}
|
||||
|
||||
canAdd(card: Pick<TcgCard, 'name' | 'supertype' | 'subtypes'>): boolean {
|
||||
if (this.total >= deckSizeLimit) return false;
|
||||
return isBasicEnergy(card) || this.copiesNamed(card.name) < copyLimit;
|
||||
}
|
||||
|
||||
add(card: TcgCard) {
|
||||
if (!this.canAdd(card)) return;
|
||||
const entry = this.entries.find((item) => item.card.id === card.id);
|
||||
if (entry) entry.count += 1;
|
||||
else this.entries.push({ card: toBuilderCard(card), count: 1 });
|
||||
this.save();
|
||||
}
|
||||
|
||||
increase(cardId: string) {
|
||||
const entry = this.entries.find((item) => item.card.id === cardId);
|
||||
if (!entry || !this.canAdd(entry.card)) return;
|
||||
entry.count += 1;
|
||||
this.save();
|
||||
}
|
||||
|
||||
remove(cardId: string) {
|
||||
const entry = this.entries.find((item) => item.card.id === cardId);
|
||||
if (!entry) return;
|
||||
entry.count -= 1;
|
||||
if (entry.count <= 0) this.entries = this.entries.filter((item) => item.card.id !== cardId);
|
||||
this.save();
|
||||
}
|
||||
|
||||
rename(name: string) {
|
||||
this.name = name;
|
||||
this.save();
|
||||
}
|
||||
|
||||
replace(name: string, entries: BuilderEntry[]) {
|
||||
this.name = name;
|
||||
this.entries = entries;
|
||||
this.save();
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.entries = [];
|
||||
this.save();
|
||||
}
|
||||
|
||||
exportText(): string {
|
||||
const lines = this.sections.flatMap((section) => [
|
||||
`${section.title}: ${section.total}`,
|
||||
...section.entries.map((entry) => [entry.count, entry.card.name, entry.card.setCode, entry.card.number].filter(Boolean).join(' ')),
|
||||
''
|
||||
]);
|
||||
return [...lines, `Total Cards: ${this.total}`].join('\n');
|
||||
}
|
||||
}
|
||||
|
||||
export const deckBuilder = new DeckBuilderState();
|
||||
Reference in new issue
Block a user