Skip to content

Commit 6375c01

Browse files
committed
feat: random deck button
1 parent 32001dd commit 6375c01

File tree

5 files changed

+225
-114
lines changed

5 files changed

+225
-114
lines changed

changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
1. Added a setting to switch minigame controls to WASD (disabled by default)
88
1. Added a random avatar button
99
1. Added setting to prefer shiny on import (enabled by default)
10+
1. Added "fill deck" button
1011
### Fixes
1112
1. Fixed bug with base card skin setting
1213
### Plugins

src/base/deck/random.js

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
wrap(() => {
2+
const limits = {
3+
BASE: 3,
4+
COMMON: 3,
5+
RARE: 3,
6+
EPIC: 2,
7+
LEGENDARY: 1,
8+
DETERMINATION: 1,
9+
};
10+
11+
onPage('Decks', () => {
12+
function buildPool(shinyFilter) {
13+
const cards = [{ id: 0, shiny: false, rarity: '' }];
14+
cards.shift();
15+
16+
const collection = global('deckCollections')[global('soul')];
17+
collection.forEach((card = { id: 0, quantity: 0, shiny: false }) => {
18+
if (shinyFilter !== undefined && card.shiny !== shinyFilter) return;
19+
for (let i = 0; i < card.quantity; i++) {
20+
cards.push(card);
21+
}
22+
});
23+
24+
fn.shuffle(cards);
25+
return cards;
26+
}
27+
28+
function buildArtifacts() {
29+
const current = [...global('decksArtifacts')[global('soul')]];
30+
const available = [...global('userArtifacts')].filter((a) => !current.includes(a));
31+
32+
if (current.length === 0) {
33+
current.push(random(available));
34+
}
35+
36+
if (current.length < 2 && !current[0].legendary) {
37+
const commons = available.filter((a) => !a.legendary && a !== current[0]);
38+
current.push(random(commons));
39+
}
40+
41+
return current;
42+
}
43+
44+
function fillDeck() {
45+
const cDeck = global('decks')[global('soul')];
46+
const cArts = global('decksArtifacts')[global('soul')];
47+
const artifacts = buildArtifacts();
48+
if (cDeck.length === 25 && cArts.length === artifacts.length) return;
49+
50+
const counts = new Map();
51+
const cards = [];
52+
let dtFlag = false;
53+
54+
function addCard(card) {
55+
const amt = counts.get(card.id) || 0;
56+
if (amt === limits[card.rarity]) return;
57+
if (card.rarity === 'DETERMINATION') {
58+
if (dtFlag) return;
59+
dtFlag = true;
60+
}
61+
cards.push(card);
62+
counts.set(card.id, amt + 1);
63+
}
64+
65+
cDeck.forEach(addCard);
66+
67+
// Fill deck with cards
68+
const pool = buildPool();
69+
while (cards.length < 25 && pool.length) {
70+
addCard(pool.shift());
71+
}
72+
73+
// Load deck
74+
fn.deckLoader.load({
75+
cards,
76+
artifacts,
77+
});
78+
}
79+
80+
eventManager.on(':loaded', () => {
81+
style.add('.btn { padding: 5px 6px; }');
82+
const button = $('<button>');
83+
const inner = $('<span>');
84+
inner.addClass('glyphicon glyphicon-random');
85+
button.addClass('btn btn-sm btn-primary');
86+
button.append(inner);
87+
button.click(fillDeck);
88+
button.hover(hover.show('Randomly fill deck'), hover.hide);
89+
const clearDeck = $('#yourCardList > button:last');
90+
clearDeck.after(' ', button);
91+
});
92+
});
93+
94+
function random(array = []) {
95+
return array[fn.rand(array.length)];
96+
}
97+
});

src/base/deck/storage.js

Lines changed: 11 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,6 @@ settings.register({
1717

1818
onPage('Decks', function deckStorage() {
1919
if (settings.value('underscript.storage.disable')) return;
20-
const mockLastOpenedDialog = {
21-
close() {},
22-
};
23-
let templastOpenedDialog;
2420
style.add('.btn-storage { margin-top: 5px; margin-right: 6px; width: 30px; padding: 5px 0; }');
2521

2622
function getFromLibrary(id, library, shiny) {
@@ -43,50 +39,6 @@ onPage('Decks', function deckStorage() {
4339
eventManager.on('jQuery', () => {
4440
const container = $('<p>');
4541
const buttons = [];
46-
let loading;
47-
let pending = [];
48-
49-
function processNext() {
50-
if (global('lastOpenedDialog') === mockLastOpenedDialog) {
51-
globalSet('lastOpenedDialog', templastOpenedDialog);
52-
}
53-
const job = pending.shift();
54-
if (job) {
55-
if (job.action === 'validate') {
56-
loadDeck(loading);
57-
if (!pending.length) {
58-
loading = null;
59-
}
60-
return;
61-
}
62-
if (job.action === 'clear') {
63-
global('removeAllCards')();
64-
} else if (job.action === 'remove') {
65-
global('removeCard')(parseInt(job.id, 10), job.shiny === true);
66-
} else if (job.action === 'clearArtifacts') {
67-
global('clearArtifacts')();
68-
} else if (job.action === 'addArtifact') {
69-
debug(`Adding artifact: ${job.id}`);
70-
templastOpenedDialog = global('lastOpenedDialog');
71-
globalSet('lastOpenedDialog', mockLastOpenedDialog);
72-
global('addArtifact')(job.id);
73-
} else {
74-
global('addCard')(parseInt(job.id, 10), job.shiny === true);
75-
}
76-
if (!pending.length) {
77-
pending.push({ action: 'validate' });
78-
}
79-
}
80-
}
81-
82-
eventManager.on('Deck:postChange', ({ action, data }) => {
83-
if (!['addCard', 'removeCard', 'removeAllCards', 'clearArtifacts', 'addArtifact'].includes(action)) return;
84-
if (data.status === 'error') {
85-
pending = [];
86-
return;
87-
}
88-
processNext();
89-
});
9042

9143
for (let i = 1, x = Math.max(parseInt(settings.value('underscript.storage.rows'), 10), 1) * 5; i <= x; i++) {
9244
buttons.push($('<button>')
@@ -98,8 +50,7 @@ onPage('Decks', function deckStorage() {
9850
container.append(button);
9951
});
10052

101-
const clearDeck = $('#yourCardList > button:last');
102-
clearDeck.after(container);
53+
$('#deckCardsCanvas').before(container);
10354
$('#yourCardList > br').remove();
10455
$('#yourCardList').css('margin-bottom', '35px');
10556

@@ -137,67 +88,10 @@ onPage('Decks', function deckStorage() {
13788

13889
function loadDeck(i) {
13990
if (i === null) return;
140-
debug('loading');
141-
pending = []; // Clear pending
142-
loading = i;
143-
let deck = getDeck(i, true);
144-
const cDeck = global('decks')[global('soul')];
145-
146-
if (cDeck.length) {
147-
const builtDeck = [];
148-
// Build deck options
149-
cDeck.forEach(({ id, shiny }) => {
150-
builtDeck.push({
151-
id,
152-
shiny,
153-
action: 'remove',
154-
});
155-
});
156-
157-
// Compare the decks
158-
const temp = deck.slice(0);
159-
const removals = builtDeck.filter((card) => !temp.some((card2, ind) => {
160-
const found = card2.id === card.id && (card.shiny && card2.shiny || true);
161-
if (found) { // Remove the item
162-
temp.splice(ind, 1);
163-
}
164-
return found;
165-
}));
166-
167-
// Check what we need to do
168-
if (!removals.length && !temp.length) { // There's nothing
169-
debug('Finished');
170-
deck = [];
171-
} else if (removals.length > 13) { // Too much to do (Cards in deck + 1)
172-
pending.push({
173-
action: 'clear',
174-
});
175-
} else {
176-
pending.push(...removals);
177-
deck = temp;
178-
}
179-
}
180-
pending.push(...deck);
181-
if (!matchingArtifacts(i)) {
182-
debug('Loading Artifacts');
183-
pending.push({
184-
action: 'clearArtifacts',
185-
});
186-
getArtifacts(i).forEach((id) => {
187-
pending.push({
188-
id,
189-
action: 'addArtifact',
190-
});
191-
});
192-
}
193-
processNext();
194-
}
195-
196-
197-
function matchingArtifacts(id) {
198-
const dArts = getArtifacts(id);
199-
const cArts = global('decksArtifacts')[global('soul')];
200-
return !dArts.length || dArts.length === cArts.length && cArts.every(({ id: id1 }) => !!~dArts.indexOf(id1));
91+
fn.deckLoader.load({
92+
cards: getDeck(i, true),
93+
artifacts: getArtifacts(i),
94+
});
20195
}
20296

20397
function getKey(id) {
@@ -210,7 +104,10 @@ onPage('Decks', function deckStorage() {
210104
const deck = JSON.parse(localStorage.getItem(key));
211105
if (!deck) return null;
212106
if (trim) {
213-
return deck.cards.filter(({ id, shiny }) => getCardData(id, shiny) !== null);
107+
return deck.cards.filter(({ id, shiny }) => {
108+
const data = getCardData(id, shiny);
109+
return data && data.name;
110+
});
214111
}
215112
return deck.cards;
216113
}
@@ -360,8 +257,8 @@ onPage('Decks', function deckStorage() {
360257
eventManager.on('Deck:Loaded', () => {
361258
loadStorage();
362259
});
363-
clearDeck.on('click', () => {
364-
pending = [];
260+
$('#yourCardList > button[onclick="removeAllCards();"]').on('click', () => {
261+
fn.deckLoader.clear();
365262
});
366263
});
367264
});

src/utils/loadDeck.js

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
fn.deckLoader = (() => {
2+
const actions = ['addCard', 'removeCard', 'removeAllCards', 'clearArtifacts', 'addArtifact'];
3+
const mockLastOpenedDialog = { close() {} };
4+
const pending = [];
5+
let templastOpenedDialog;
6+
7+
function clear() {
8+
pending.splice(0, pending.length);
9+
}
10+
11+
function processNext() {
12+
if (global('lastOpenedDialog') === mockLastOpenedDialog) {
13+
globalSet('lastOpenedDialog', templastOpenedDialog);
14+
}
15+
const job = pending.shift();
16+
if (!job) return;
17+
18+
if (job.action === 'validate') {
19+
loadDeck({ artifacts: job.artifacts, cards: job.cards });
20+
} else if (job.action === 'clear') {
21+
global('removeAllCards')();
22+
} else if (job.action === 'remove') {
23+
global('removeCard')(parseInt(job.id, 10), job.shiny === true);
24+
} else if (job.action === 'clearArtifacts') {
25+
global('clearArtifacts')();
26+
} else if (job.action === 'addArtifact') {
27+
templastOpenedDialog = global('lastOpenedDialog');
28+
globalSet('lastOpenedDialog', mockLastOpenedDialog);
29+
global('addArtifact')(job.id);
30+
} else {
31+
global('addCard')(parseInt(job.id, 10), job.shiny === true);
32+
}
33+
}
34+
35+
eventManager.on('Deck:postChange', ({ action, data }) => {
36+
if (!actions.includes(action) || !data) return;
37+
if (data.status === 'error') {
38+
clear();
39+
} else {
40+
processNext();
41+
}
42+
});
43+
44+
function loadDeck({ cards = [], artifacts = [] }) {
45+
clear();
46+
if (cards.length > 25 || artifacts > 2) return;
47+
// Get current deck
48+
const cDeck = global('decks')[global('soul')];
49+
// Remove unwanted cards
50+
if (cDeck.length) {
51+
const deck = [...cards];
52+
const removals = cDeck.map(({ id, shiny }) => ({
53+
id,
54+
shiny,
55+
action: 'remove',
56+
})).filter((card) => !deck.some((card2, ind) => {
57+
const found = card2.id === card.id && (card.shiny || false) === (card2.shiny || false);
58+
if (found) { // Remove the item
59+
deck.splice(ind, 1);
60+
}
61+
return found;
62+
}));
63+
64+
if (removals.length > 13 || removals.length === cDeck.length) { // Too much to do (Cards in deck + 1) OR removing all cards
65+
pending.push({ action: 'clear' });
66+
pending.push(...cards); // Add everything
67+
} else { // Remove bad cards
68+
pending.push(...removals);
69+
pending.push(...deck); // Add missing cards
70+
}
71+
} else { // All new cards
72+
pending.push(...cards);
73+
}
74+
const cArts = global('decksArtifacts')[global('soul')];
75+
if (!cArts.every((art) => artifacts.includes(art) || artifacts.includes(art.id))) { // Clear artifacts (if they don't match)
76+
pending.push({ action: 'clearArtifacts' });
77+
artifacts.forEach((art) => {
78+
pending.push({
79+
id: art.id || art,
80+
action: 'addArtifact',
81+
});
82+
});
83+
} else { // Partial or empty
84+
const ids = cArts.map((art) => art.id);
85+
artifacts.forEach((art) => {
86+
const id = art.id || art;
87+
if (ids.includes(id)) return;
88+
pending.push({
89+
id,
90+
action: 'addArtifact',
91+
});
92+
});
93+
}
94+
// Validate
95+
if (pending.length) {
96+
pending.push({
97+
action: 'validate',
98+
cards: cards.map(({ id, shiny }) => ({ id, shiny })),
99+
artifacts: artifacts.map((art) => art.id || art),
100+
});
101+
processNext();
102+
}
103+
}
104+
105+
return Object.freeze({
106+
load: onPage('Decks') ? loadDeck : noop,
107+
clear,
108+
});
109+
})();

src/utils/shuffle.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
fn.shuffle = (array = []) => {
2+
for (let i = array.length - 1; i > 0; i--) {
3+
const j = Math.floor(Math.random() * (i + 1));
4+
[array[i], array[j]] = [array[j], array[i]];
5+
}
6+
return array;
7+
};

0 commit comments

Comments
 (0)