Refactor armoire content to be cached by day

This commit is contained in:
Phillip Thelen
2024-05-15 16:51:09 +02:00
parent 46d164ddd1
commit 4d38880249
17 changed files with 198 additions and 97 deletions

View File

@@ -1,11 +1,11 @@
import * as armoireSet from '../../../website/common/script/content/gear/sets/armoire'; import armoireSet from '../../../website/common/script/content/gear/sets/armoire';
describe('armoireSet items', () => { describe('armoireSet items', () => {
it('checks if canOwn has the same id', () => { it('checks if canOwn has the same id', () => {
console.log(armoireSet);
Object.keys(armoireSet).forEach(type => { Object.keys(armoireSet).forEach(type => {
Object.keys(armoireSet[type]).forEach(itemKey => { Object.keys(armoireSet[type]).forEach(itemKey => {
const ownedKey = `${type}_armoire_${itemKey}`; const ownedKey = `${type}_armoire_${itemKey}`;
expect(armoireSet[type][itemKey].canOwn({ expect(armoireSet[type][itemKey].canOwn({
items: { items: {
gear: { gear: {

View File

@@ -17,9 +17,7 @@ function getFullArmoire () {
_.each(content.gearTypes, type => { _.each(content.gearTypes, type => {
_.each(content.gear.tree[type].armoire, gearObject => { _.each(content.gear.tree[type].armoire, gearObject => {
if (gearObject.released) {
fullArmoire[gearObject.key] = true; fullArmoire[gearObject.key] = true;
}
}); });
}); });

View File

@@ -5,23 +5,15 @@ import {
} from '../helpers/content.helper'; } from '../helpers/content.helper';
function makeArmoireIitemList () { function makeArmoireIitemList () {
const { const armoire = require('../../website/common/script/content/gear/sets/armoire').default;
armor,
body,
eyewear,
head,
headAccessory,
shield,
weapon,
} = require('../../website/common/script/content/gear/sets/armoire');
const items = []; const items = [];
items.push(...Object.values(armor)); items.push(...Object.values(armoire.armor));
items.push(...Object.values(body)); items.push(...Object.values(armoire.body));
items.push(...Object.values(eyewear)); items.push(...Object.values(armoire.eyewear));
items.push(...Object.values(head)); items.push(...Object.values(armoire.head));
items.push(...Object.values(headAccessory)); items.push(...Object.values(armoire.headAccessory));
items.push(...Object.values(shield)); items.push(...Object.values(armoire.shield));
items.push(...Object.values(weapon)); items.push(...Object.values(armoire.weapon));
return items; return items;
} }
@@ -37,9 +29,7 @@ describe('armoire', () => {
clock = sinon.useFakeTimers(new Date('2024-01-01')); clock = sinon.useFakeTimers(new Date('2024-01-01'));
const items = makeArmoireIitemList(); const items = makeArmoireIitemList();
expect(items.length).to.equal(377); expect(items.length).to.equal(377);
forEach(items, item => { expect(items.filter(item => item.set === 'pottersSet' || item.set === 'optimistSet' || item.set === 'schoolUniform')).to.be.an('array').that.is.empty;
expect(item.released, item.key).to.be.true;
});
}); });
it('released gear has all required properties', async () => { it('released gear has all required properties', async () => {
@@ -52,7 +42,6 @@ describe('armoire', () => {
expect(item.set, item.key).to.not.be.empty; expect(item.set, item.key).to.not.be.empty;
} }
expectValidTranslationString(item.text); expectValidTranslationString(item.text);
expect(item.released, item.key).to.be.a('boolean');
expect(item.value, item.key).to.be.a('number'); expect(item.value, item.key).to.be.a('number');
}); });
}); });

View File

@@ -162,6 +162,7 @@
"mysterySet202403": "Lucky Legend Set", "mysterySet202403": "Lucky Legend Set",
"mysterySet202404": "Mycelial Magus Set", "mysterySet202404": "Mycelial Magus Set",
"mysterySet202405": "Gilded Dragon Set", "mysterySet202405": "Gilded Dragon Set",
"mysterySet202406": "Gilded Dragon Set",
"mysterySet301404": "Steampunk Standard Set", "mysterySet301404": "Steampunk Standard Set",
"mysterySet301405": "Steampunk Accessories Set", "mysterySet301405": "Steampunk Accessories Set",
"mysterySet301703": "Peacock Steampunk Set", "mysterySet301703": "Peacock Steampunk Set",

View File

@@ -801,8 +801,8 @@ function getMonth (date) {
} }
function getGalaIndex (date) { function getGalaIndex (date) {
const month = date instanceof moment ? date.month() : date.getMonth(); const month = getMonth(date);
const todayDay = date instanceof moment ? date.date() : date.getDate(); const todayDay = getDay(date);
let galaMonth = month; let galaMonth = month;
const galaCount = Object.keys(GALA_SCHEDULE).length; const galaCount = Object.keys(GALA_SCHEDULE).length;
if (todayDay >= GALA_SWITCHOVER_DAY) { if (todayDay >= GALA_SWITCHOVER_DAY) {
@@ -816,8 +816,8 @@ function getGalaIndex (date) {
export function assembleScheduledMatchers (date) { export function assembleScheduledMatchers (date) {
const items = []; const items = [];
const month = date instanceof moment ? date.month() : date.getMonth(); const month = getMonth(date);
const todayDay = date instanceof moment ? date.date() : date.getDate(); const todayDay = getDay(date);
const previousMonth = month === 0 ? 11 : month - 1; const previousMonth = month === 0 ? 11 : month - 1;
for (const [day, value] of Object.entries(MONTHLY_SCHEDULE[previousMonth])) { for (const [day, value] of Object.entries(MONTHLY_SCHEDULE[previousMonth])) {
if (day > todayDay) { if (day > todayDay) {

View File

@@ -7,7 +7,7 @@ import { armor as wizardArmor } from './sets/wizard';
import { armor as specialArmor } from './sets/special'; import { armor as specialArmor } from './sets/special';
import { armor as mysteryArmor } from './sets/mystery'; import { armor as mysteryArmor } from './sets/mystery';
import { armor as armoireArmor } from './sets/armoire'; import armoire from './sets/armoire';
const armor = { const armor = {
base: baseArmor, base: baseArmor,
@@ -19,7 +19,9 @@ const armor = {
special: specialArmor, special: specialArmor,
mystery: mysteryArmor, mystery: mysteryArmor,
armoire: armoireArmor, get armoire () {
return armoire.armor;
},
}; };
export default armor; export default armor;

View File

@@ -2,13 +2,15 @@ import { body as baseBody } from './sets/base';
import { body as mysteryBody } from './sets/mystery'; import { body as mysteryBody } from './sets/mystery';
import { body as specialBody } from './sets/special'; import { body as specialBody } from './sets/special';
import { body as armoireBody } from './sets/armoire'; import armoire from './sets/armoire';
const body = { const body = {
base: baseBody, base: baseBody,
mystery: mysteryBody, mystery: mysteryBody,
special: specialBody, special: specialBody,
armoire: armoireBody, get armoire () {
return armoire.body;
},
}; };
export default body; export default body;

View File

@@ -1,6 +1,6 @@
import { eyewear as baseEyewear } from './sets/base'; import { eyewear as baseEyewear } from './sets/base';
import { eyewear as armoireEyewear } from './sets/armoire'; import armoire from './sets/armoire';
import { eyewear as mysteryEyewear } from './sets/mystery'; import { eyewear as mysteryEyewear } from './sets/mystery';
import { eyewear as specialEyewear } from './sets/special'; import { eyewear as specialEyewear } from './sets/special';
@@ -8,7 +8,9 @@ const eyewear = {
base: baseEyewear, base: baseEyewear,
special: specialEyewear, special: specialEyewear,
mystery: mysteryEyewear, mystery: mysteryEyewear,
armoire: armoireEyewear, get armoire () {
return armoire.eyewear;
},
}; };
export default eyewear; export default eyewear;

View File

@@ -2,13 +2,15 @@ import { headAccessory as baseHeadAccessory } from './sets/base';
import { headAccessory as specialHeadAccessory } from './sets/special'; import { headAccessory as specialHeadAccessory } from './sets/special';
import { headAccessory as mysteryHeadAccessory } from './sets/mystery'; import { headAccessory as mysteryHeadAccessory } from './sets/mystery';
import { headAccessory as armoireHeadAccessory } from './sets/armoire'; import armoire from './sets/armoire';
const headAccessory = { const headAccessory = {
base: baseHeadAccessory, base: baseHeadAccessory,
special: specialHeadAccessory, special: specialHeadAccessory,
mystery: mysteryHeadAccessory, mystery: mysteryHeadAccessory,
armoire: armoireHeadAccessory, get armoire () {
return armoire.headAccessory;
},
}; };
export default headAccessory; export default headAccessory;

View File

@@ -5,7 +5,7 @@ import { head as rogueHead } from './sets/rogue';
import { head as warriorHead } from './sets/warrior'; import { head as warriorHead } from './sets/warrior';
import { head as wizardHead } from './sets/wizard'; import { head as wizardHead } from './sets/wizard';
import { head as armoireHead } from './sets/armoire'; import armoire from './sets/armoire';
import { head as mysteryHead } from './sets/mystery'; import { head as mysteryHead } from './sets/mystery';
import { head as specialHead } from './sets/special'; import { head as specialHead } from './sets/special';
@@ -19,7 +19,9 @@ const head = {
special: specialHead, special: specialHead,
mystery: mysteryHead, mystery: mysteryHead,
armoire: armoireHead, get armoire () {
return armoire.head;
},
}; };
export default head; export default head;

View File

@@ -15,6 +15,7 @@ import back from './back';
import body from './body'; import body from './body';
import headAccessory from './head-accessory'; import headAccessory from './head-accessory';
import eyewear from './eyewear'; import eyewear from './eyewear';
import memoize from '../../fns/datedMemoize';
const gear = { const gear = {
weapon, weapon,
@@ -27,19 +28,7 @@ const gear = {
eyewear, eyewear,
}; };
/* function populateGear (key, klass, type, index, item) {
The gear is exported as a tree (defined above), and a flat list
(eg, {weapon_healer_1: .., shield_special_0: ...}) since
they are needed in different forms at different points in the app
*/
const flat = {};
each(GEAR_TYPES, type => {
const allGearTypes = CLASSES.concat(['base', 'special', 'mystery', 'armoire']);
each(allGearTypes, klass => {
each(gear[type][klass], (item, index) => {
const key = `${type}_${klass}_${index}`;
const set = `${klass}-${index}`; const set = `${klass}-${index}`;
defaults(item, { defaults(item, {
@@ -55,16 +44,49 @@ each(GEAR_TYPES, type => {
canBuy: () => false, canBuy: () => false,
}); });
if (item.mystery || key.indexOf('takeThis') !== -1) { if (item.canOwn === undefined && (item.mystery || key.indexOf('takeThis') !== -1)) {
item.canOwn = ownsItem(key); item.canOwn = ownsItem(key);
} }
}
each(GEAR_TYPES, type => {
const allGearTypes = CLASSES.concat(['base', 'special', 'mystery', 'armoire']);
each(allGearTypes, klass => {
each(gear[type][klass], (item, index) => {
const key = `${type}_${klass}_${index}`;
populateGear(key, klass, type, index, item);
});
});
});
function buildFlatList () {
/*
The gear is exported as a tree (defined above), and a flat list
(eg, {weapon_healer_1: .., shield_special_0: ...}) since
they are needed in different forms at different points in the app
*/
const flat = {};
each(GEAR_TYPES, type => {
const allGearTypes = CLASSES.concat(['base', 'special', 'mystery', 'armoire']);
each(allGearTypes, klass => {
each(gear[type][klass], (item, index) => {
const key = `${type}_${klass}_${index}`;
populateGear(key, klass, type, index, item);
flat[key] = item; flat[key] = item;
}); });
}); });
}); });
return flat;
}
const memoizedFlatList = memoize(buildFlatList);
export default { export default {
tree: gear, tree: gear,
flat, get flat () {
return memoizedFlatList();
},
}; };

View File

@@ -6,6 +6,7 @@ import upperFirst from 'lodash/upperFirst';
import { ownsItem } from '../gear-helper'; import { ownsItem } from '../gear-helper';
import { ATTRIBUTES } from '../../../constants'; import { ATTRIBUTES } from '../../../constants';
import t from '../../translation'; import t from '../../translation';
import memoize from '../../../fns/datedMemoize';
const armor = { const armor = {
lunarArmor: { lunarArmor: {
@@ -1339,7 +1340,7 @@ const shield = {
con: 8, con: 8,
set: 'pottersSet', set: 'pottersSet',
}, },
buoyantBeachball: { buoyantBeachBall: {
str: 12, str: 12,
set: 'beachsideSet', set: 'beachsideSet',
}, },
@@ -1853,8 +1854,6 @@ forEach({
shield, shield,
weapon, weapon,
}, (set, setKey) => { }, (set, setKey) => {
const today = moment();
const releaseDateEndPart = `${String(releaseDay).padStart(2, '0')}T08:00-0500`;
forEach(set, (gearItem, gearKey) => { forEach(set, (gearItem, gearKey) => {
const gearStats = {}; const gearStats = {};
const gearStatValues = []; const gearStatValues = [];
@@ -1878,6 +1877,20 @@ forEach({
} else { } else {
notes = t(`${setKey}Armoire${upperFirst(gearKey)}Notes`); notes = t(`${setKey}Armoire${upperFirst(gearKey)}Notes`);
} }
defaults(gearItem, {
canOwn: ownsItem(`${setKey}_armoire_${gearKey}`),
notes,
text: t(`${setKey}Armoire${upperFirst(gearKey)}Text`),
value: 100,
});
});
});
function updateReleased (type) {
const today = moment();
const releaseDateEndPart = `${String(releaseDay).padStart(2, '0')}T08:00-0500`;
const returnType = {};
forEach(type, (gearItem, gearKey) => {
let released; let released;
if (releaseDates[gearItem.set]) { if (releaseDates[gearItem.set]) {
const releaseDateString = `${releaseDates[gearItem.set].year}-${String(releaseDates[gearItem.set].month).padStart(2, '0')}-${releaseDateEndPart}`; const releaseDateString = `${releaseDates[gearItem.set].year}-${String(releaseDates[gearItem.set].month).padStart(2, '0')}-${releaseDateEndPart}`;
@@ -1885,25 +1898,35 @@ forEach({
} else { } else {
released = true; released = true;
} }
defaults(gearItem, { if (released) {
released, returnType[gearKey] = gearItem;
canOwn: ownsItem(`${setKey}_armoire_${gearKey}`),
notes,
text: t(`${setKey}Armoire${upperFirst(gearKey)}Text`),
value: 100,
});
if (gearItem.released === false) {
delete set[gearKey];
} }
}); });
}); return returnType;
}
export { const memoizedUpdatReleased = memoize(updateReleased);
armor,
body, export default {
eyewear, get armor () {
head, return memoizedUpdatReleased({ identifier: 'armor', memoizeConfig: true }, armor);
headAccessory, },
shield, get body () {
weapon, return memoizedUpdatReleased({ identifier: 'body', memoizeConfig: true }, body);
},
get eyewear () {
return memoizedUpdatReleased({ identifier: 'eyewear', memoizeConfig: true }, eyewear);
},
get head () {
return memoizedUpdatReleased({ identifier: 'head', memoizeConfig: true }, head);
},
get headAccessory () {
return memoizedUpdatReleased({ identifier: 'headAccessory', memoizeConfig: true }, headAccessory);
},
get shield () {
return memoizedUpdatReleased({ identifier: 'shield', memoizeConfig: true }, shield);
},
get weapon () {
return memoizedUpdatReleased({ identifier: 'weapon', memoizeConfig: true }, weapon);
},
}; };

View File

@@ -7,7 +7,7 @@ import { weapon as rogueWeapon } from './sets/rogue';
import { shield as warriorShield } from './sets/warrior'; import { shield as warriorShield } from './sets/warrior';
import { shield as wizardShield } from './sets/wizard'; import { shield as wizardShield } from './sets/wizard';
import { shield as armoireShield } from './sets/armoire'; import armoire from './sets/armoire';
import { shield as mysteryShield } from './sets/mystery'; import { shield as mysteryShield } from './sets/mystery';
import { shield as specialShield } from './sets/special'; import { shield as specialShield } from './sets/special';
@@ -23,7 +23,9 @@ const shield = {
special: specialShield, special: specialShield,
mystery: mysteryShield, mystery: mysteryShield,
armoire: armoireShield, get armoire () {
return armoire.shield;
},
}; };
export default shield; export default shield;

View File

@@ -7,7 +7,7 @@ import { weapon as rogueWeapon } from './sets/rogue';
import { weapon as warriorWeapon } from './sets/warrior'; import { weapon as warriorWeapon } from './sets/warrior';
import { weapon as wizardWeapon } from './sets/wizard'; import { weapon as wizardWeapon } from './sets/wizard';
import { weapon as armoireWeapon } from './sets/armoire'; import armoire from './sets/armoire';
import { weapon as mysteryWeapon } from './sets/mystery'; import { weapon as mysteryWeapon } from './sets/mystery';
import { weapon as specialWeapon } from './sets/special'; import { weapon as specialWeapon } from './sets/special';
@@ -21,7 +21,9 @@ const weapon = {
special: specialWeapon, special: specialWeapon,
mystery: mysteryWeapon, mystery: mysteryWeapon,
armoire: armoireWeapon, get armoire () {
return armoire.weapon;
},
}; };
// Add Two Handed message to all weapons // Add Two Handed message to all weapons

View File

@@ -49,16 +49,17 @@ export function mountMasterProgress (mounts = {}) {
export function remainingGearInSet (userGear = {}, set) { export function remainingGearInSet (userGear = {}, set) {
const gear = filter(content.gear.flat, item => { const gear = filter(content.gear.flat, item => {
const setMatches = item.klass === set; if (item.klass !== set) {
return false;
}
const hasItem = userGear[item.key]; const hasItem = userGear[item.key];
if (has(item, 'released')) { if (has(item, 'released')) {
return item.released && setMatches && !hasItem; return item.released && !hasItem;
} }
return setMatches && !hasItem; return !hasItem;
}); });
const count = size(gear); const count = size(gear);
return count; return count;
} }

View File

@@ -0,0 +1,53 @@
import moment from 'moment';
function getDay (date) {
if (date === undefined) {
return 0;
}
return date instanceof moment ? date.date() : date.getDate();
}
function getMonth (date) {
if (date === undefined) {
return 0;
}
return date instanceof moment ? date.month() : date.getMonth();
}
const memoize = fn => {
const cache = {};
const cacheDate = {};
return (...args) => {
let checkedDate;
let identifier = '';
if (args.length > 0) {
if (typeof args[0] === 'object' && args[0].memoizeConfig) {
const config = args.shift();
checkedDate = config.date;
if (config.identifier) {
identifier = config.identifier;
}
}
}
if (!checkedDate) {
checkedDate = new Date();
}
if (cacheDate[identifier] && (getDay(checkedDate) !== getDay(cacheDate[identifier])
|| getMonth(checkedDate) !== getMonth(cacheDate[identifier]))) {
// Clear cached results, since they are old
cache[identifier] = undefined;
cacheDate[identifier] = undefined;
}
if (cache[identifier]) {
// result is already cached
return cache[identifier];
}
const result = fn(...args);
cache[identifier] = result;
cacheDate[identifier] = checkedDate;
return result;
};
};
export default memoize;

View File

@@ -32,7 +32,7 @@ export class BuyArmoireOperation extends AbstractGoldItemOperation { // eslint-d
let result = {}; let result = {};
const armoireResult = randomValFns.trueRandom(); const armoireResult = randomValFns.trueRandom();
const eligibleEquipment = filter(content.gear.flat, eligible => eligible.klass === 'armoire' && eligible.released && !user.items.gear.owned[eligible.key]); const eligibleEquipment = filter(content.gear.flat, eligible => eligible.klass === 'armoire' && !user.items.gear.owned[eligible.key]);
const armoireHasEquipment = !isEmpty(eligibleEquipment); const armoireHasEquipment = !isEmpty(eligibleEquipment);
if ( if (