mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-16 22:27:26 +01:00
Refactor armoire content to be cached by day
This commit is contained in:
@@ -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', () => {
|
||||
it('checks if canOwn has the same id', () => {
|
||||
console.log(armoireSet);
|
||||
Object.keys(armoireSet).forEach(type => {
|
||||
Object.keys(armoireSet[type]).forEach(itemKey => {
|
||||
const ownedKey = `${type}_armoire_${itemKey}`;
|
||||
|
||||
expect(armoireSet[type][itemKey].canOwn({
|
||||
items: {
|
||||
gear: {
|
||||
|
||||
@@ -17,9 +17,7 @@ function getFullArmoire () {
|
||||
|
||||
_.each(content.gearTypes, type => {
|
||||
_.each(content.gear.tree[type].armoire, gearObject => {
|
||||
if (gearObject.released) {
|
||||
fullArmoire[gearObject.key] = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -5,23 +5,15 @@ import {
|
||||
} from '../helpers/content.helper';
|
||||
|
||||
function makeArmoireIitemList () {
|
||||
const {
|
||||
armor,
|
||||
body,
|
||||
eyewear,
|
||||
head,
|
||||
headAccessory,
|
||||
shield,
|
||||
weapon,
|
||||
} = require('../../website/common/script/content/gear/sets/armoire');
|
||||
const armoire = require('../../website/common/script/content/gear/sets/armoire').default;
|
||||
const items = [];
|
||||
items.push(...Object.values(armor));
|
||||
items.push(...Object.values(body));
|
||||
items.push(...Object.values(eyewear));
|
||||
items.push(...Object.values(head));
|
||||
items.push(...Object.values(headAccessory));
|
||||
items.push(...Object.values(shield));
|
||||
items.push(...Object.values(weapon));
|
||||
items.push(...Object.values(armoire.armor));
|
||||
items.push(...Object.values(armoire.body));
|
||||
items.push(...Object.values(armoire.eyewear));
|
||||
items.push(...Object.values(armoire.head));
|
||||
items.push(...Object.values(armoire.headAccessory));
|
||||
items.push(...Object.values(armoire.shield));
|
||||
items.push(...Object.values(armoire.weapon));
|
||||
return items;
|
||||
}
|
||||
|
||||
@@ -37,9 +29,7 @@ describe('armoire', () => {
|
||||
clock = sinon.useFakeTimers(new Date('2024-01-01'));
|
||||
const items = makeArmoireIitemList();
|
||||
expect(items.length).to.equal(377);
|
||||
forEach(items, item => {
|
||||
expect(item.released, item.key).to.be.true;
|
||||
});
|
||||
expect(items.filter(item => item.set === 'pottersSet' || item.set === 'optimistSet' || item.set === 'schoolUniform')).to.be.an('array').that.is.empty;
|
||||
});
|
||||
|
||||
it('released gear has all required properties', async () => {
|
||||
@@ -52,7 +42,6 @@ describe('armoire', () => {
|
||||
expect(item.set, item.key).to.not.be.empty;
|
||||
}
|
||||
expectValidTranslationString(item.text);
|
||||
expect(item.released, item.key).to.be.a('boolean');
|
||||
expect(item.value, item.key).to.be.a('number');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -162,6 +162,7 @@
|
||||
"mysterySet202403": "Lucky Legend Set",
|
||||
"mysterySet202404": "Mycelial Magus Set",
|
||||
"mysterySet202405": "Gilded Dragon Set",
|
||||
"mysterySet202406": "Gilded Dragon Set",
|
||||
"mysterySet301404": "Steampunk Standard Set",
|
||||
"mysterySet301405": "Steampunk Accessories Set",
|
||||
"mysterySet301703": "Peacock Steampunk Set",
|
||||
|
||||
@@ -801,8 +801,8 @@ function getMonth (date) {
|
||||
}
|
||||
|
||||
function getGalaIndex (date) {
|
||||
const month = date instanceof moment ? date.month() : date.getMonth();
|
||||
const todayDay = date instanceof moment ? date.date() : date.getDate();
|
||||
const month = getMonth(date);
|
||||
const todayDay = getDay(date);
|
||||
let galaMonth = month;
|
||||
const galaCount = Object.keys(GALA_SCHEDULE).length;
|
||||
if (todayDay >= GALA_SWITCHOVER_DAY) {
|
||||
@@ -816,8 +816,8 @@ function getGalaIndex (date) {
|
||||
|
||||
export function assembleScheduledMatchers (date) {
|
||||
const items = [];
|
||||
const month = date instanceof moment ? date.month() : date.getMonth();
|
||||
const todayDay = date instanceof moment ? date.date() : date.getDate();
|
||||
const month = getMonth(date);
|
||||
const todayDay = getDay(date);
|
||||
const previousMonth = month === 0 ? 11 : month - 1;
|
||||
for (const [day, value] of Object.entries(MONTHLY_SCHEDULE[previousMonth])) {
|
||||
if (day > todayDay) {
|
||||
|
||||
@@ -7,7 +7,7 @@ import { armor as wizardArmor } from './sets/wizard';
|
||||
|
||||
import { armor as specialArmor } from './sets/special';
|
||||
import { armor as mysteryArmor } from './sets/mystery';
|
||||
import { armor as armoireArmor } from './sets/armoire';
|
||||
import armoire from './sets/armoire';
|
||||
|
||||
const armor = {
|
||||
base: baseArmor,
|
||||
@@ -19,7 +19,9 @@ const armor = {
|
||||
|
||||
special: specialArmor,
|
||||
mystery: mysteryArmor,
|
||||
armoire: armoireArmor,
|
||||
get armoire () {
|
||||
return armoire.armor;
|
||||
},
|
||||
};
|
||||
|
||||
export default armor;
|
||||
|
||||
@@ -2,13 +2,15 @@ import { body as baseBody } from './sets/base';
|
||||
|
||||
import { body as mysteryBody } from './sets/mystery';
|
||||
import { body as specialBody } from './sets/special';
|
||||
import { body as armoireBody } from './sets/armoire';
|
||||
import armoire from './sets/armoire';
|
||||
|
||||
const body = {
|
||||
base: baseBody,
|
||||
mystery: mysteryBody,
|
||||
special: specialBody,
|
||||
armoire: armoireBody,
|
||||
get armoire () {
|
||||
return armoire.body;
|
||||
},
|
||||
};
|
||||
|
||||
export default body;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
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 specialEyewear } from './sets/special';
|
||||
|
||||
@@ -8,7 +8,9 @@ const eyewear = {
|
||||
base: baseEyewear,
|
||||
special: specialEyewear,
|
||||
mystery: mysteryEyewear,
|
||||
armoire: armoireEyewear,
|
||||
get armoire () {
|
||||
return armoire.eyewear;
|
||||
},
|
||||
};
|
||||
|
||||
export default eyewear;
|
||||
|
||||
@@ -2,13 +2,15 @@ import { headAccessory as baseHeadAccessory } from './sets/base';
|
||||
|
||||
import { headAccessory as specialHeadAccessory } from './sets/special';
|
||||
import { headAccessory as mysteryHeadAccessory } from './sets/mystery';
|
||||
import { headAccessory as armoireHeadAccessory } from './sets/armoire';
|
||||
import armoire from './sets/armoire';
|
||||
|
||||
const headAccessory = {
|
||||
base: baseHeadAccessory,
|
||||
special: specialHeadAccessory,
|
||||
mystery: mysteryHeadAccessory,
|
||||
armoire: armoireHeadAccessory,
|
||||
get armoire () {
|
||||
return armoire.headAccessory;
|
||||
},
|
||||
};
|
||||
|
||||
export default headAccessory;
|
||||
|
||||
@@ -5,7 +5,7 @@ import { head as rogueHead } from './sets/rogue';
|
||||
import { head as warriorHead } from './sets/warrior';
|
||||
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 specialHead } from './sets/special';
|
||||
|
||||
@@ -19,7 +19,9 @@ const head = {
|
||||
|
||||
special: specialHead,
|
||||
mystery: mysteryHead,
|
||||
armoire: armoireHead,
|
||||
get armoire () {
|
||||
return armoire.head;
|
||||
},
|
||||
};
|
||||
|
||||
export default head;
|
||||
|
||||
@@ -15,6 +15,7 @@ import back from './back';
|
||||
import body from './body';
|
||||
import headAccessory from './head-accessory';
|
||||
import eyewear from './eyewear';
|
||||
import memoize from '../../fns/datedMemoize';
|
||||
|
||||
const gear = {
|
||||
weapon,
|
||||
@@ -27,19 +28,7 @@ const gear = {
|
||||
eyewear,
|
||||
};
|
||||
|
||||
/*
|
||||
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}`;
|
||||
function populateGear (key, klass, type, index, item) {
|
||||
const set = `${klass}-${index}`;
|
||||
|
||||
defaults(item, {
|
||||
@@ -55,16 +44,49 @@ each(GEAR_TYPES, type => {
|
||||
canBuy: () => false,
|
||||
});
|
||||
|
||||
if (item.mystery || key.indexOf('takeThis') !== -1) {
|
||||
if (item.canOwn === undefined && (item.mystery || key.indexOf('takeThis') !== -1)) {
|
||||
item.canOwn = ownsItem(key);
|
||||
}
|
||||
}
|
||||
|
||||
flat[key] = item;
|
||||
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;
|
||||
});
|
||||
});
|
||||
});
|
||||
return flat;
|
||||
}
|
||||
|
||||
const memoizedFlatList = memoize(buildFlatList);
|
||||
|
||||
export default {
|
||||
tree: gear,
|
||||
flat,
|
||||
get flat () {
|
||||
return memoizedFlatList();
|
||||
},
|
||||
};
|
||||
|
||||
@@ -6,6 +6,7 @@ import upperFirst from 'lodash/upperFirst';
|
||||
import { ownsItem } from '../gear-helper';
|
||||
import { ATTRIBUTES } from '../../../constants';
|
||||
import t from '../../translation';
|
||||
import memoize from '../../../fns/datedMemoize';
|
||||
|
||||
const armor = {
|
||||
lunarArmor: {
|
||||
@@ -1339,7 +1340,7 @@ const shield = {
|
||||
con: 8,
|
||||
set: 'pottersSet',
|
||||
},
|
||||
buoyantBeachball: {
|
||||
buoyantBeachBall: {
|
||||
str: 12,
|
||||
set: 'beachsideSet',
|
||||
},
|
||||
@@ -1853,8 +1854,6 @@ forEach({
|
||||
shield,
|
||||
weapon,
|
||||
}, (set, setKey) => {
|
||||
const today = moment();
|
||||
const releaseDateEndPart = `${String(releaseDay).padStart(2, '0')}T08:00-0500`;
|
||||
forEach(set, (gearItem, gearKey) => {
|
||||
const gearStats = {};
|
||||
const gearStatValues = [];
|
||||
@@ -1878,6 +1877,20 @@ forEach({
|
||||
} else {
|
||||
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;
|
||||
if (releaseDates[gearItem.set]) {
|
||||
const releaseDateString = `${releaseDates[gearItem.set].year}-${String(releaseDates[gearItem.set].month).padStart(2, '0')}-${releaseDateEndPart}`;
|
||||
@@ -1885,25 +1898,35 @@ forEach({
|
||||
} else {
|
||||
released = true;
|
||||
}
|
||||
defaults(gearItem, {
|
||||
released,
|
||||
canOwn: ownsItem(`${setKey}_armoire_${gearKey}`),
|
||||
notes,
|
||||
text: t(`${setKey}Armoire${upperFirst(gearKey)}Text`),
|
||||
value: 100,
|
||||
});
|
||||
if (gearItem.released === false) {
|
||||
delete set[gearKey];
|
||||
if (released) {
|
||||
returnType[gearKey] = gearItem;
|
||||
}
|
||||
});
|
||||
});
|
||||
return returnType;
|
||||
}
|
||||
|
||||
export {
|
||||
armor,
|
||||
body,
|
||||
eyewear,
|
||||
head,
|
||||
headAccessory,
|
||||
shield,
|
||||
weapon,
|
||||
const memoizedUpdatReleased = memoize(updateReleased);
|
||||
|
||||
export default {
|
||||
get armor () {
|
||||
return memoizedUpdatReleased({ identifier: 'armor', memoizeConfig: true }, armor);
|
||||
},
|
||||
get body () {
|
||||
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);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@ import { weapon as rogueWeapon } from './sets/rogue';
|
||||
import { shield as warriorShield } from './sets/warrior';
|
||||
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 specialShield } from './sets/special';
|
||||
|
||||
@@ -23,7 +23,9 @@ const shield = {
|
||||
|
||||
special: specialShield,
|
||||
mystery: mysteryShield,
|
||||
armoire: armoireShield,
|
||||
get armoire () {
|
||||
return armoire.shield;
|
||||
},
|
||||
};
|
||||
|
||||
export default shield;
|
||||
|
||||
@@ -7,7 +7,7 @@ import { weapon as rogueWeapon } from './sets/rogue';
|
||||
import { weapon as warriorWeapon } from './sets/warrior';
|
||||
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 specialWeapon } from './sets/special';
|
||||
|
||||
@@ -21,7 +21,9 @@ const weapon = {
|
||||
|
||||
special: specialWeapon,
|
||||
mystery: mysteryWeapon,
|
||||
armoire: armoireWeapon,
|
||||
get armoire () {
|
||||
return armoire.weapon;
|
||||
},
|
||||
};
|
||||
|
||||
// Add Two Handed message to all weapons
|
||||
|
||||
@@ -49,16 +49,17 @@ export function mountMasterProgress (mounts = {}) {
|
||||
|
||||
export function remainingGearInSet (userGear = {}, set) {
|
||||
const gear = filter(content.gear.flat, item => {
|
||||
const setMatches = item.klass === set;
|
||||
if (item.klass !== set) {
|
||||
return false;
|
||||
}
|
||||
const hasItem = userGear[item.key];
|
||||
if (has(item, 'released')) {
|
||||
return item.released && setMatches && !hasItem;
|
||||
return item.released && !hasItem;
|
||||
}
|
||||
return setMatches && !hasItem;
|
||||
return !hasItem;
|
||||
});
|
||||
|
||||
const count = size(gear);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
53
website/common/script/fns/datedMemoize.js
Normal file
53
website/common/script/fns/datedMemoize.js
Normal 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;
|
||||
@@ -32,7 +32,7 @@ export class BuyArmoireOperation extends AbstractGoldItemOperation { // eslint-d
|
||||
let result = {};
|
||||
|
||||
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);
|
||||
|
||||
if (
|
||||
|
||||
Reference in New Issue
Block a user