From e7bc505b886d3afc50698cc7c58a6846ef0f615b Mon Sep 17 00:00:00 2001 From: Keith Holliday Date: Fri, 2 Mar 2018 12:43:22 -0700 Subject: [PATCH 01/34] Modal popup fixes (#10080) * Added validation for modal stack * Lint fixes --- website/client/app.vue | 122 ++++++++++++-------- website/client/components/notifications.vue | 2 + 2 files changed, 77 insertions(+), 47 deletions(-) diff --git a/website/client/app.vue b/website/client/app.vue index c1c877dfa0..55400adec6 100644 --- a/website/client/app.vue +++ b/website/client/app.vue @@ -323,53 +323,7 @@ export default { this.hideLoadingScreen(); } - // Manage modals - this.$root.$on('bv::show::modal', (modalId, data = {}) => { - if (data.fromRoot) return; - - // Track opening of gems modal unless it's been already tracked - // For example the gems button in the menu already tracks the event by itself - if (modalId === 'buy-gems' && data.alreadyTracked !== true) { - Analytics.track({ - hitType: 'event', - eventCategory: 'button', - eventAction: 'click', - eventLabel: 'Gems > Wallet', - }); - } - - // Get last modal on stack and hide - let modalStackLength = this.$store.state.modalStack.length; - let modalOnTop = this.$store.state.modalStack[modalStackLength - 1]; - - // Add new modal to the stack - this.$store.state.modalStack.push(modalId); - - // Hide the previous top modal - if (modalOnTop) this.$root.$emit('bv::hide::modal', modalOnTop, {fromRoot: true}); - }); - - // @TODO: This part is hacky and could be solved with two options: - // 1 - Find a way to pass fromRoot to hidden - // 2 - Enforce that all modals use the hide::modal event - this.$root.$on('bv::modal::hidden', (bvEvent) => { - const modalId = bvEvent.target.id; - - let modalStackLength = this.$store.state.modalStack.length; - let modalSecondToTop = this.$store.state.modalStack[modalStackLength - 2]; - // Don't remove modal if hid was called from main app - // @TODO: I'd reather use this, but I don't know how to pass data to hidden event - // if (data && data.fromRoot) return; - if (modalId === modalSecondToTop) return; - - // Remove modal from stack - this.$store.state.modalStack.pop(); - - // Recalculate and show the last modal if there is one - modalStackLength = this.$store.state.modalStack.length; - let modalOnTop = this.$store.state.modalStack[modalStackLength - 1]; - if (modalOnTop) this.$root.$emit('bv::show::modal', modalOnTop, {fromRoot: true}); - }); + this.initializeModalStack(); }, beforeDestroy () { this.$root.$off('playSound'); @@ -384,6 +338,80 @@ export default { if (loadingScreen) document.body.removeChild(loadingScreen); }, methods: { + initializeModalStack () { + // Manage modals + this.$root.$on('bv::show::modal', (modalId, data = {}) => { + if (data.fromRoot) return; + const modalStack = this.$store.state.modalStack; + + this.trackGemPurchase(modalId, data); + + // Add new modal to the stack + const prev = modalStack[modalStack.length - 1]; + const prevId = prev ? prev.modalId : undefined; + modalStack.push({modalId, prev: prevId}); + }); + + this.$root.$on('bv::modal::hidden', (bvEvent) => { + const modalId = bvEvent.target && bvEvent.target.id; + if (!modalId) return; + + const modalStack = this.$store.state.modalStack; + + const modalOnTop = modalStack[modalStack.length - 1]; + + // Check for invalid modal. Event systems can send multiples + if (!this.validStack(modalStack)) return; + + // If we are moving forward + if (modalOnTop && modalOnTop.prev === modalId) return; + + // Remove modal from stack + this.$store.state.modalStack.pop(); + + // Get previous modal + const modalBefore = modalOnTop ? modalOnTop.prev : undefined; + if (modalBefore) this.$root.$emit('bv::show::modal', modalBefore, {fromRoot: true}); + }); + }, + validStack (modalStack) { + const modalsThatCanShowTwice = ['profile']; + const modalCount = {}; + const prevAndCurrent = 2; + + for (let index in modalStack) { + const current = modalStack[index]; + + if (!modalCount[current.modalId]) modalCount[current.modalId] = 0; + modalCount[current.modalId] += 1; + if (modalCount[current.modalId] > prevAndCurrent && modalsThatCanShowTwice.indexOf(current.modalId) === -1) { + this.$store.state.modalStack = []; + return false; + } + + if (!current.prev) continue; // eslint-disable-line + if (!modalCount[current.prev]) modalCount[current.prev] = 0; + modalCount[current.prev] += 1; + if (modalCount[current.prev] > prevAndCurrent && modalsThatCanShowTwice.indexOf(current.prev) === -1) { + this.$store.state.modalStack = []; + return false; + } + } + + return true; + }, + trackGemPurchase (modalId, data) { + // Track opening of gems modal unless it's been already tracked + // For example the gems button in the menu already tracks the event by itself + if (modalId === 'buy-gems' && data.alreadyTracked !== true) { + Analytics.track({ + hitType: 'event', + eventCategory: 'button', + eventAction: 'click', + eventLabel: 'Gems > Wallet', + }); + } + }, resetItemToBuy ($event) { // @TODO: Do we need this? I think selecting a new item // overwrites. @negue might know diff --git a/website/client/components/notifications.vue b/website/client/components/notifications.vue index d08d6b50b0..4d3b1f6eab 100644 --- a/website/client/components/notifications.vue +++ b/website/client/components/notifications.vue @@ -204,6 +204,7 @@ export default { }, watch: { userHp (after, before) { + if (this.user.needsCron) return; if (after <= 0) { this.playSound('Death'); this.$root.$emit('bv::show::modal', 'death'); @@ -252,6 +253,7 @@ export default { this.showLevelUpNotifications(after); }, userClassSelect (after) { + if (this.user.needsCron) return; if (!after) return; this.$root.$emit('bv::show::modal', 'choose-class'); // @TODO: {controller:'UserCtrl', keyboard:false, backdrop:'static'} From 7cff3318005f3943ba8cf07e192d85e648ab69b5 Mon Sep 17 00:00:00 2001 From: Keith Holliday Date: Fri, 2 Mar 2018 14:07:15 -0700 Subject: [PATCH 02/34] Removed uri for client side oauth (#10058) * Removed uri for client side oauth * Fixed lint --- website/client/components/settings/site.vue | 2 ++ 1 file changed, 2 insertions(+) diff --git a/website/client/components/settings/site.vue b/website/client/components/settings/site.vue index 946356ade3..22bc305d8d 100644 --- a/website/client/components/settings/site.vue +++ b/website/client/components/settings/site.vue @@ -241,6 +241,8 @@ export default { hello.init({ facebook: process.env.FACEBOOK_KEY, // eslint-disable-line no-process-env google: process.env.GOOGLE_CLIENT_ID, // eslint-disable-line no-process-env + }, { + redirect_uri: '', // eslint-disable-line }); }, computed: { From 9c9b67aa9d8161dec27bf37d1a4969875681759e Mon Sep 17 00:00:00 2001 From: Keith Holliday Date: Fri, 2 Mar 2018 14:30:11 -0700 Subject: [PATCH 03/34] Keys kennel fixes (#9848) * Show keys to pets immediately * Ensured keys to pets dissapear after use * Added resdirect to stable after purchase * Added mount check and updated keys to mounts and to both * Added api calls * Added check for beastmaster progress * Added mount check for release mounts. Added pets and mount check to release both * Added actions * Added catch to common tests * Added beast count and reload * Removed extra console log --- test/common/ops/releaseBoth.js | 55 ++++++++++++++----- test/common/ops/releaseMounts.js | 29 ++++++++-- test/common/ops/releasePets.js | 37 ++++++++++--- .../components/inventory/stable/index.vue | 7 +-- website/client/components/shops/buyModal.vue | 3 + .../client/components/shops/market/index.vue | 2 +- .../components/shops/market/keysToKennel.vue | 37 +++++++++---- website/client/store/actions/common.js | 4 +- website/client/store/actions/shops.js | 19 +++++++ website/common/locales/en/pets.json | 5 +- website/common/script/count.js | 11 ++++ website/common/script/ops/releaseBoth.js | 5 ++ website/common/script/ops/releaseMounts.js | 5 ++ website/common/script/ops/releasePets.js | 5 ++ 14 files changed, 174 insertions(+), 50 deletions(-) diff --git a/test/common/ops/releaseBoth.js b/test/common/ops/releaseBoth.js index 72411e419b..b5eae68863 100644 --- a/test/common/ops/releaseBoth.js +++ b/test/common/ops/releaseBoth.js @@ -41,6 +41,22 @@ describe('shared.ops.releaseBoth', () => { } }); + it('returns an error when user does not have all pets', (done) => { + const petKeys = Object.keys(user.items.pets); + delete user.items.pets[petKeys[0]]; + + const mountKeys = Object.keys(user.items.mounts); + delete user.items.mounts[mountKeys[0]]; + + try { + releaseBoth(user); + } catch (err) { + expect(err).to.be.an.instanceof(NotAuthorized); + expect(err.message).to.equal(i18n.t('notEnoughPetsMounts')); + done(); + } + }); + it('grants triad bingo with gems', () => { let message = releaseBoth(user)[1]; @@ -79,26 +95,33 @@ describe('shared.ops.releaseBoth', () => { it('does not increment beastMasterCount if any pet is level 0 (released)', () => { let beastMasterCountBeforeRelease = user.achievements.beastMasterCount; user.items.pets[animal] = 0; - - releaseBoth(user); - - expect(user.achievements.beastMasterCount).to.equal(beastMasterCountBeforeRelease); + try { + releaseBoth(user); + } catch (e) { + expect(user.achievements.beastMasterCount).to.equal(beastMasterCountBeforeRelease); + } }); it('does not increment beastMasterCount if any pet is missing (null)', () => { let beastMasterCountBeforeRelease = user.achievements.beastMasterCount; user.items.pets[animal] = null; - releaseBoth(user); - expect(user.achievements.beastMasterCount).to.equal(beastMasterCountBeforeRelease); + try { + releaseBoth(user); + } catch (e) { + expect(user.achievements.beastMasterCount).to.equal(beastMasterCountBeforeRelease); + } }); it('does not increment beastMasterCount if any pet is missing (undefined)', () => { let beastMasterCountBeforeRelease = user.achievements.beastMasterCount; delete user.items.pets[animal]; - releaseBoth(user); - expect(user.achievements.beastMasterCount).to.equal(beastMasterCountBeforeRelease); + try { + releaseBoth(user); + } catch (e) { + expect(user.achievements.beastMasterCount).to.equal(beastMasterCountBeforeRelease); + } }); it('releases mounts', () => { @@ -112,18 +135,22 @@ describe('shared.ops.releaseBoth', () => { let mountMasterCountBeforeRelease = user.achievements.mountMasterCount; user.items.mounts[animal] = null; - releaseBoth(user); - - expect(user.achievements.mountMasterCount).to.equal(mountMasterCountBeforeRelease); + try { + releaseBoth(user); + } catch (e) { + expect(user.achievements.mountMasterCount).to.equal(mountMasterCountBeforeRelease); + } }); it('does not increase mountMasterCount achievement if mount is missing (undefined)', () => { let mountMasterCountBeforeRelease = user.achievements.mountMasterCount; delete user.items.mounts[animal]; - releaseBoth(user); - - expect(user.achievements.mountMasterCount).to.equal(mountMasterCountBeforeRelease); + try { + releaseBoth(user); + } catch (e) { + expect(user.achievements.mountMasterCount).to.equal(mountMasterCountBeforeRelease); + } }); it('removes drop currentPet', () => { diff --git a/test/common/ops/releaseMounts.js b/test/common/ops/releaseMounts.js index dc8fbae7b4..816d5f035d 100644 --- a/test/common/ops/releaseMounts.js +++ b/test/common/ops/releaseMounts.js @@ -35,6 +35,19 @@ describe('shared.ops.releaseMounts', () => { } }); + it('returns an error when user does not have all pets', (done) => { + const mountsKeys = Object.keys(user.items.mounts); + delete user.items.mounts[mountsKeys[0]]; + + try { + releaseMounts(user); + } catch (err) { + expect(err).to.be.an.instanceof(NotAuthorized); + expect(err.message).to.equal(i18n.t('notEnoughMounts')); + done(); + } + }); + it('releases mounts', () => { let message = releaseMounts(user)[1]; @@ -71,18 +84,22 @@ describe('shared.ops.releaseMounts', () => { let mountMasterCountBeforeRelease = user.achievements.mountMasterCount; user.items.mounts[animal] = null; - releaseMounts(user); - - expect(user.achievements.mountMasterCount).to.equal(mountMasterCountBeforeRelease); + try { + releaseMounts(user); + } catch (e) { + expect(user.achievements.mountMasterCount).to.equal(mountMasterCountBeforeRelease); + } }); it('does not increase mountMasterCount achievement if mount is missing (undefined)', () => { let mountMasterCountBeforeRelease = user.achievements.mountMasterCount; delete user.items.mounts[animal]; - releaseMounts(user); - - expect(user.achievements.mountMasterCount).to.equal(mountMasterCountBeforeRelease); + try { + releaseMounts(user); + } catch (e) { + expect(user.achievements.mountMasterCount).to.equal(mountMasterCountBeforeRelease); + } }); it('subtracts gems from balance', () => { diff --git a/test/common/ops/releasePets.js b/test/common/ops/releasePets.js index 62ab66c540..97089607d3 100644 --- a/test/common/ops/releasePets.js +++ b/test/common/ops/releasePets.js @@ -35,6 +35,19 @@ describe('shared.ops.releasePets', () => { } }); + it('returns an error when user does not have all pets', (done) => { + const petKeys = Object.keys(user.items.pets); + delete user.items.pets[petKeys[0]]; + + try { + releasePets(user); + } catch (err) { + expect(err).to.be.an.instanceof(NotAuthorized); + expect(err.message).to.equal(i18n.t('notEnoughPets')); + done(); + } + }); + it('releases pets', () => { let message = releasePets(user)[1]; @@ -75,27 +88,35 @@ describe('shared.ops.releasePets', () => { }); it('does not increment beastMasterCount if any pet is level 0 (released)', () => { - let beastMasterCountBeforeRelease = user.achievements.beastMasterCount; - + const beastMasterCountBeforeRelease = user.achievements.beastMasterCount; user.items.pets[animal] = 0; - releasePets(user); - expect(user.achievements.beastMasterCount).to.equal(beastMasterCountBeforeRelease); + try { + releasePets(user); + } catch (e) { + expect(user.achievements.beastMasterCount).to.equal(beastMasterCountBeforeRelease); + } }); it('does not increment beastMasterCount if any pet is missing (null)', () => { let beastMasterCountBeforeRelease = user.achievements.beastMasterCount; user.items.pets[animal] = null; - releasePets(user); - expect(user.achievements.beastMasterCount).to.equal(beastMasterCountBeforeRelease); + try { + releasePets(user); + } catch (e) { + expect(user.achievements.beastMasterCount).to.equal(beastMasterCountBeforeRelease); + } }); it('does not increment beastMasterCount if any pet is missing (undefined)', () => { let beastMasterCountBeforeRelease = user.achievements.beastMasterCount; delete user.items.pets[animal]; - releasePets(user); - expect(user.achievements.beastMasterCount).to.equal(beastMasterCountBeforeRelease); + try { + releasePets(user); + } catch (e) { + expect(user.achievements.beastMasterCount).to.equal(beastMasterCountBeforeRelease); + } }); }); diff --git a/website/client/components/inventory/stable/index.vue b/website/client/components/inventory/stable/index.vue index e0b2816008..15f689a413 100644 --- a/website/client/components/inventory/stable/index.vue +++ b/website/client/components/inventory/stable/index.vue @@ -963,25 +963,21 @@ this.$root.$emit('bv::show::modal', 'hatching-modal'); } }, - async feedAction (petKey, foodKey) { - let result = await this.$store.dispatch('common:feed', {pet: petKey, food: foodKey}); + const result = await this.$store.dispatch('common:feed', {pet: petKey, food: foodKey}); if (result.message) { this.text(result.message); } }, - closeHatchPetDialog () { this.$root.$emit('bv::hide::modal', 'hatching-modal'); }, - resetHatchablePet ($event) { if (!$event) { this.hatchablePet = null; } }, - onFoodClicked ($event, food) { if (this.currentDraggingFood === null || this.currentDraggingFood !== food) { this.currentDraggingFood = food; @@ -995,7 +991,6 @@ this.foodClickMode = false; } }, - mouseMoved ($event) { if (this.foodClickMode) { this.$refs.clickFoodInfo.style.left = `${$event.x - 70}px`; diff --git a/website/client/components/shops/buyModal.vue b/website/client/components/shops/buyModal.vue index bc2a9978c4..b4cc1b6ea1 100644 --- a/website/client/components/shops/buyModal.vue +++ b/website/client/components/shops/buyModal.vue @@ -51,6 +51,9 @@ span(:class="{'notEnough': notEnoughCurrency}") span.svg-icon.inline.icon-32(aria-hidden="true", v-html="icons[getPriceClass()]") span.cost(:class="getPriceClass()") {{ item.value }} + div(v-else) + span.svg-icon.inline.icon-32(aria-hidden="true", v-html="icons[getPriceClass()]") + span.cost(:class="getPriceClass()") {{ item.value }} .gems-left(v-if='item.key === "gem"') strong(v-if='gemsLeft > 0') {{ gemsLeft }} {{ $t('gemsRemaining') }} diff --git a/website/client/components/shops/market/index.vue b/website/client/components/shops/market/index.vue index db3da6abda..f2345effe8 100644 --- a/website/client/components/shops/market/index.vue +++ b/website/client/components/shops/market/index.vue @@ -163,7 +163,7 @@ ) span.svg-icon.inline.icon-12.color(v-html="icons.pin") - //keys-to-kennel(v-if='category.identifier === "special"') + keys-to-kennel(v-if='category.identifier === "special"') div.fill-height diff --git a/website/client/components/shops/market/keysToKennel.vue b/website/client/components/shops/market/keysToKennel.vue index 3d7f57cf99..ad1a9a1b1f 100644 --- a/website/client/components/shops/market/keysToKennel.vue +++ b/website/client/components/shops/market/keysToKennel.vue @@ -6,7 +6,7 @@ :emptyItem="false", popoverPosition="'top'", @click="releasePets()", - v-if='this.user.achievements.beastMaster' + v-if='userHasAllPets' ) shopItem( :key="keysToMounts.key", @@ -14,7 +14,7 @@ :emptyItem="false", popoverPosition="'top'", @click="releaseMounts()", - v-if='this.user.achievements.mountMaster' + v-if='userHasAllMounts' ) shopItem( :key="keysToBoth.key", @@ -22,7 +22,7 @@ :emptyItem="false", popoverPosition="'top'", @click="releaseBoth()", - v-if='this.user.achievements.mountMaster' + v-if='userHasAllPets && userHasAllMounts' ) @@ -61,9 +61,7 @@ import { mapState } from 'client/libs/store'; import ShopItem from '../shopItem'; -import releasePets from 'common/script/ops/releasePets'; -import releaseMounts from 'common/script/ops/releaseMounts'; -import releaseBoth from 'common/script/ops/releaseBoth'; +import count from 'common/script/count'; import notifications from 'client/mixins/notifications'; @@ -88,11 +86,14 @@ export default { buy: () => { if (!confirm(this.$t('releasePetsConfirm'))) return; try { - releasePets(this.user); + this.$store.dispatch('shops:releasePets', {user: this.user}); + this.text(this.$t('releasePetsSuccess')); + // this.$router.push({name: 'stable'}); + // Reload because achievement is set in user.save instead of common + window.location.reload(true); } catch (err) { alert(err.message); } - this.text(this.$t('releasePetsSuccess')); }, }, keysToMounts: { @@ -108,11 +109,14 @@ export default { buy: () => { if (!confirm(this.$t('releaseMountsConfirm'))) return; try { - releaseMounts(this.user); + this.$store.dispatch('shops:releaseMounts', {user: this.user}); + this.text(this.$t('releaseMountsSuccess')); + // this.$router.push({name: 'stable'}); + // Reload because achievement is set in user.save instead of common + window.location.reload(true); } catch (err) { alert(err.message); } - this.text(this.$t('releaseMountsSuccess')); }, }, keysToBoth: { @@ -128,17 +132,26 @@ export default { buy: () => { if (!confirm(this.$t('releaseBothConfirm'))) return; try { - releaseBoth(this.user); + this.$store.dispatch('shops:releaseBoth', {user: this.user}); + this.text(this.$t('releaseBothSuccess')); + // this.$router.push({name: 'stable'}); + // Reload because achievement is set in user.save instead of common + window.location.reload(true); } catch (err) { alert(err.message); } - this.text(this.$t('releaseBothSuccess')); }, }, }; }, computed: { ...mapState({user: 'user.data'}), + userHasAllPets () { + return count.beastCount(this.user.items.pets) === 90; + }, + userHasAllMounts () { + return count.mountMasterProgress(this.user.items.mounts) === 90; + }, }, methods: { releasePets () { diff --git a/website/client/store/actions/common.js b/website/client/store/actions/common.js index e3e2ac8428..ab504c07a8 100644 --- a/website/client/store/actions/common.js +++ b/website/client/store/actions/common.js @@ -26,8 +26,8 @@ export function hatch (store, params) { export async function feed (store, params) { const user = store.state.user.data; feedOp(user, {params}); - let response = await axios - .post(`/api/v3/user/feed/${params.pet}/${params.food}`); + const response = await axios + .post(`/api/v3/user/feed/${params.pet}/${params.food}`); return response.data; } diff --git a/website/client/store/actions/shops.js b/website/client/store/actions/shops.js index 46480de461..ca109b735d 100644 --- a/website/client/store/actions/shops.js +++ b/website/client/store/actions/shops.js @@ -6,6 +6,10 @@ import hourglassPurchaseOp from 'common/script/ops/buy/hourglassPurchase'; import sellOp from 'common/script/ops/sell'; import unlockOp from 'common/script/ops/unlock'; import rerollOp from 'common/script/ops/reroll'; +import releasePetsOp from 'common/script/ops/releasePets'; +import releaseMountsOp from 'common/script/ops/releaseMounts'; +import releaseBothOp from 'common/script/ops/releaseBoth'; + import { getDropClass } from 'client/libs/notifications'; // @TODO: Purchase means gems and buy means gold. That wording is misused below, but we should also change @@ -176,3 +180,18 @@ export function sellItems (store, params) { sellOp(user, {params, query: {amount: params.amount}}); axios.post(`/api/v3/user/sell/${params.type}/${params.key}?amount=${params.amount}`); } + +export function releasePets (store, params) { + releasePetsOp(params.user); + axios.post('/api/v3/user/release-pets'); +} + +export function releaseMounts (store, params) { + releaseMountsOp(params.user); + axios.post('/api/v3/user/release-mounts'); +} + +export function releaseBoth (store, params) { + releaseBothOp(params.user); + axios.post('/api/v3/user/release-both'); +} diff --git a/website/common/locales/en/pets.json b/website/common/locales/en/pets.json index bd0653c9ae..2298efc77f 100644 --- a/website/common/locales/en/pets.json +++ b/website/common/locales/en/pets.json @@ -138,5 +138,8 @@ "dragThisPotion": "Drag this <%= potionName %> to an Egg and hatch a new pet!", "clickOnEggToHatch": "Click on an Egg to use your <%= potionName %> hatching potion and hatch a new pet!", "hatchDialogText": "Pour your <%= potionName %> hatching potion on your <%= eggName %> egg, and it will hatch into a <%= petName %>.", - "clickOnPotionToHatch": "Click on a hatching potion to use it on your <%= eggName %> and hatch a new pet!" + "clickOnPotionToHatch": "Click on a hatching potion to use it on your <%= eggName %> and hatch a new pet!", + "notEnoughPets": "You have not collected enough pets", + "notEnoughMounts": "You have not collected enough mounts", + "notEnoughPetsMounts": "You have not collected enough pets and mounts" } diff --git a/website/common/script/count.js b/website/common/script/count.js index 530c3d22e9..0a840e029d 100644 --- a/website/common/script/count.js +++ b/website/common/script/count.js @@ -17,6 +17,16 @@ function beastMasterProgress (pets = {}) { return count; } +function beastCount (pets = {}) { + let count = 0; + + each(DROP_ANIMALS, (animal) => { + if (pets[animal] > 0) count++; + }); + + return count; +} + function dropPetsCurrentlyOwned (pets = {}) { let count = 0; @@ -67,6 +77,7 @@ function questsOfCategory (userQuests = {}, category) { module.exports = { beastMasterProgress, + beastCount, dropPetsCurrentlyOwned, mountMasterProgress, remainingGearInSet, diff --git a/website/common/script/ops/releaseBoth.js b/website/common/script/ops/releaseBoth.js index 39b27e8926..7548a45d9c 100644 --- a/website/common/script/ops/releaseBoth.js +++ b/website/common/script/ops/releaseBoth.js @@ -1,4 +1,5 @@ import content from '../content/index'; +import {beastMasterProgress, mountMasterProgress} from '../count'; import i18n from '../i18n'; import { NotAuthorized, @@ -13,6 +14,10 @@ module.exports = function releaseBoth (user, req = {}, analytics) { throw new NotAuthorized(i18n.t('notEnoughGems', req.language)); } + if (beastMasterProgress(user.items.pets) !== 90 || mountMasterProgress(user.items.mounts) !== 90) { + throw new NotAuthorized(i18n.t('notEnoughPetsMounts', req.language)); + } + let giveTriadBingo = true; let giveBeastMasterAchievement = true; let giveMountMasterAchievement = true; diff --git a/website/common/script/ops/releaseMounts.js b/website/common/script/ops/releaseMounts.js index 0f7159502e..4462d11f44 100644 --- a/website/common/script/ops/releaseMounts.js +++ b/website/common/script/ops/releaseMounts.js @@ -1,4 +1,5 @@ import content from '../content/index'; +import {mountMasterProgress} from '../count'; import i18n from '../i18n'; import { NotAuthorized, @@ -9,6 +10,10 @@ module.exports = function releaseMounts (user, req = {}, analytics) { throw new NotAuthorized(i18n.t('notEnoughGems', req.language)); } + if (mountMasterProgress(user.items.mounts) !== 90) { + throw new NotAuthorized(i18n.t('notEnoughMounts', req.language)); + } + user.balance -= 1; let giveMountMasterAchievement = true; diff --git a/website/common/script/ops/releasePets.js b/website/common/script/ops/releasePets.js index 6a356b8629..0124474a37 100644 --- a/website/common/script/ops/releasePets.js +++ b/website/common/script/ops/releasePets.js @@ -1,4 +1,5 @@ import content from '../content/index'; +import {beastMasterProgress} from '../count'; import i18n from '../i18n'; import { NotAuthorized, @@ -9,6 +10,10 @@ module.exports = function releasePets (user, req = {}, analytics) { throw new NotAuthorized(i18n.t('notEnoughGems', req.language)); } + if (beastMasterProgress(user.items.pets) !== 90) { + throw new NotAuthorized(i18n.t('notEnoughPets', req.language)); + } + user.balance -= 1; let giveBeastMasterAchievement = true; From da50d2f4958d70e9f3666721d37261cd33306203 Mon Sep 17 00:00:00 2001 From: SabreCat Date: Mon, 5 Mar 2018 20:46:22 +0000 Subject: [PATCH 04/34] feat(world-boss): third Rage Strike support --- .../broken/quest_shop_broken_background.png | Bin 0 -> 11500 bytes .../npc/broken/quest_shop_broken_layer.png | Bin 0 -> 4499 bytes .../npc/broken/quest_shop_broken_npc.png | Bin 0 -> 6495 bytes .../broken/rage-strike-quests-scaled@2x.png | Bin 0 -> 13440 bytes website/client/components/groups/tavern.vue | 2 +- .../client/components/shops/quests/index.vue | 26 +++++++++++++++++- .../world-boss/worldBossRageModal.vue | 9 ++++++ website/common/locales/en/questsContent.json | 3 ++ 8 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 website/client/assets/images/npc/broken/quest_shop_broken_background.png create mode 100644 website/client/assets/images/npc/broken/quest_shop_broken_layer.png create mode 100644 website/client/assets/images/npc/broken/quest_shop_broken_npc.png create mode 100644 website/client/assets/images/npc/broken/rage-strike-quests-scaled@2x.png diff --git a/website/client/assets/images/npc/broken/quest_shop_broken_background.png b/website/client/assets/images/npc/broken/quest_shop_broken_background.png new file mode 100644 index 0000000000000000000000000000000000000000..1eb10f5cbf6e19d3e360f608045d953696ea5156 GIT binary patch literal 11500 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 literal 0 HcmV?d00001 diff --git a/website/client/assets/images/npc/broken/quest_shop_broken_layer.png b/website/client/assets/images/npc/broken/quest_shop_broken_layer.png new file mode 100644 index 0000000000000000000000000000000000000000..ac7f5f18ae6522e290c2de8f389547398fac589f GIT binary patch literal 4499 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 literal 0 HcmV?d00001 diff --git a/website/client/assets/images/npc/broken/quest_shop_broken_npc.png b/website/client/assets/images/npc/broken/quest_shop_broken_npc.png new file mode 100644 index 0000000000000000000000000000000000000000..e2ac91b7d2aabe3f7981ec3b43333cc5125f6142 GIT binary patch literal 6495 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 Onudxluv+P5 @@ -376,8 +394,14 @@ export default { hideLocked: false, hidePinned: false, + + broken: false, }; }, + async mounted () { + const worldState = await this.$store.dispatch('worldState:getWorldState'); + this.broken = worldState.worldBoss.extra.worldDmg.quests; + }, computed: { ...mapState({ content: 'content', diff --git a/website/client/components/world-boss/worldBossRageModal.vue b/website/client/components/world-boss/worldBossRageModal.vue index 73e8c1be27..3905a80ff8 100644 --- a/website/client/components/world-boss/worldBossRageModal.vue +++ b/website/client/components/world-boss/worldBossRageModal.vue @@ -18,6 +18,15 @@ .col-12.text-center.padding-24 h2 {{ $t('marketRageStrikeLead') }} p {{ $t('marketRageStrikeRecap') }} + div(v-if="npc === 'quests'") + .modal-header.d-flex.align-items-center.justify-content-center + .reduce {{ $t('questsRageStrikeHeader') }} + img.npc-background(src='~assets/images/npc/broken/rage-strike-quests-scaled@2x.png') + .modal-body + .row + .col-12.text-center.padding-24 + h2 {{ $t('questsRageStrikeLead') }} + p {{ $t('questsRageStrikeRecap') }}