mirror of
https://github.com/HabitRPG/habitica.git
synced 2026-04-22 19:39:25 -05:00
[WIP] Play Sounds in the new Client (#9012)
* move audioThemes to content - add audio settings * copy audioFiles to website/static/audio - playSounds on $emit() * Play Sounds for Rewards, Purchasing Items / Quests * remove shop http-api calls * play reward sound when buying gem * fix lint / always show mysterybox
This commit is contained in:
@@ -28,6 +28,10 @@
|
||||
div(:class='{sticky: user.preferences.stickyHeader}')
|
||||
router-view
|
||||
app-footer
|
||||
|
||||
audio#sound(autoplay, ref="sound")
|
||||
source#oggSource(type="audio/ogg", :src="sound.oggSource")
|
||||
source#mp3Source(type="audio/mp3", :src="sound.mp3Source")
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
@@ -86,6 +90,11 @@ export default {
|
||||
isUserLoaded: false,
|
||||
selectedItemToBuy: null,
|
||||
selectedCardToBuy: null,
|
||||
|
||||
sound: {
|
||||
oggSource: '',
|
||||
mp3Source: '',
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -99,6 +108,21 @@ export default {
|
||||
},
|
||||
},
|
||||
created () {
|
||||
this.$root.$on('playSound', (sound) => {
|
||||
let theme = this.user.preferences.sound;
|
||||
|
||||
if (!theme || theme === 'off')
|
||||
return;
|
||||
|
||||
let file = `/static/audio/${theme}/${sound}`;
|
||||
this.sound = {
|
||||
oggSource: `${file}.ogg`,
|
||||
mp3Source: `${file}.mp3`,
|
||||
};
|
||||
|
||||
this.$refs.sound.load();
|
||||
});
|
||||
|
||||
this.$root.$on('buyModal::showItem', (item) => {
|
||||
this.selectedItemToBuy = item;
|
||||
});
|
||||
|
||||
@@ -291,7 +291,7 @@ export default {
|
||||
|
||||
let specialArray = itemsByType.special;
|
||||
|
||||
if (this.user.purchased.plan.mysteryItems.length) {
|
||||
if (this.user.purchased.plan.customerId) {
|
||||
specialArray.push({
|
||||
key: 'mysteryItem',
|
||||
class: `inventory_present inventory_present_${moment().format('MM')}`,
|
||||
@@ -390,11 +390,16 @@ export default {
|
||||
if (item.key === 'timeTravelers') {
|
||||
this.$router.push({name: 'time'});
|
||||
} else if (item.key === 'mysteryItem') {
|
||||
if (item.quantity === 0)
|
||||
return;
|
||||
|
||||
let result = await this.$store.dispatch('user:openMysteryItem');
|
||||
|
||||
let openedItem = result.data.data;
|
||||
let text = this.content.gear.flat[openedItem.key].text();
|
||||
this.drop(this.$t('messageDropMysteryItem', {dropText: text}), openedItem);
|
||||
item.quantity--;
|
||||
this.$forceUpdate();
|
||||
} else {
|
||||
this.selectedSpell = item;
|
||||
}
|
||||
|
||||
@@ -250,8 +250,8 @@ export default {
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
playSound () {
|
||||
// @TODO:
|
||||
playSound (sound) {
|
||||
this.$root.$emit('playSound', sound);
|
||||
},
|
||||
async runYesterDailies () {
|
||||
// @TODO: Hopefully we don't need this even we load correctly
|
||||
|
||||
@@ -103,6 +103,8 @@ export default {
|
||||
this.$store.dispatch('shops:purchase', params);
|
||||
} catch (e) {
|
||||
alert(e.message);
|
||||
} finally {
|
||||
this.$root.$emit('playSound', 'Reward');
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
@@ -24,6 +24,13 @@
|
||||
option(v-for='dateFormat in availableFormats', :value='dateFormat') {{dateFormat}}
|
||||
hr
|
||||
|
||||
.form-horizontal
|
||||
h5 {{ $t('audioTheme') }}
|
||||
select.form-control(v-model='user.preferences.sound',
|
||||
@change='set("sound")')
|
||||
option(v-for='sound in availableAudioThemes', :value='sound') {{ $t(`audioTheme_${sound}`) }}
|
||||
hr
|
||||
|
||||
.form-horizontal(v-if='user.flags.classSelected && !user.preferences.disableClasses')
|
||||
h5 {{ $t('characterBuild') }}
|
||||
h6(v-once) {{ $t('class') + ': ' }}
|
||||
@@ -247,7 +254,11 @@ export default {
|
||||
...mapState({
|
||||
user: 'user.data',
|
||||
availableLanguages: 'i18n.availableLanguages',
|
||||
content: 'content',
|
||||
}),
|
||||
availableAudioThemes () {
|
||||
return ['off', ...this.content.audioThemes];
|
||||
},
|
||||
timezoneOffsetToUtc () {
|
||||
let offset = this.user.preferences.timezoneOffset;
|
||||
let sign = offset > 0 ? '-' : '+';
|
||||
|
||||
@@ -260,6 +260,7 @@
|
||||
|
||||
this.purchased(this.item.text);
|
||||
this.$root.$emit('buyModal::boughtItem', this.item);
|
||||
this.$root.$emit('playSound', 'Reward');
|
||||
}
|
||||
|
||||
this.$emit('buyPressed', this.item);
|
||||
|
||||
@@ -286,6 +286,7 @@
|
||||
currency: this.item.currency,
|
||||
});
|
||||
this.purchased(this.item.text);
|
||||
this.$root.$emit('playSound', 'Reward');
|
||||
this.$emit('buyPressed', this.item);
|
||||
this.hideDialog();
|
||||
},
|
||||
|
||||
@@ -339,8 +339,9 @@
|
||||
|
||||
import featuredItems from 'common/script/content/shop-featuredItems';
|
||||
import getItemInfo from 'common/script/libs/getItemInfo';
|
||||
import shops from 'common/script/libs/shops';
|
||||
|
||||
import { isPinned } from 'common/script/ops/pinnedGearUtils';
|
||||
import isPinned from 'common/script/libs/isPinned';
|
||||
|
||||
import _filter from 'lodash/filter';
|
||||
import _sortBy from 'lodash/sortBy';
|
||||
@@ -393,20 +394,22 @@ export default {
|
||||
computed: {
|
||||
...mapState({
|
||||
content: 'content',
|
||||
quests: 'shops.quests.data',
|
||||
user: 'user.data',
|
||||
userStats: 'user.data.stats',
|
||||
userItems: 'user.data.items',
|
||||
}),
|
||||
questCategories () {
|
||||
return shops.getQuestShopCategories(this.user);
|
||||
},
|
||||
categories () {
|
||||
if (this.quests) {
|
||||
this.quests.categories.map((category) => {
|
||||
if (this.questCategories) {
|
||||
this.questCategories.map((category) => {
|
||||
this.$set(this.viewOptions, category.identifier, {
|
||||
selected: true,
|
||||
});
|
||||
});
|
||||
|
||||
return this.quests.categories;
|
||||
return this.questCategories;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
@@ -477,8 +480,5 @@ export default {
|
||||
}
|
||||
},
|
||||
},
|
||||
created () {
|
||||
this.$store.dispatch('shops:fetchQuests');
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -306,10 +306,12 @@
|
||||
import _throttle from 'lodash/throttle';
|
||||
import _groupBy from 'lodash/groupBy';
|
||||
|
||||
import { isPinned } from 'common/script/ops/pinnedGearUtils';
|
||||
import isPinned from 'common/script/libs/isPinned';
|
||||
|
||||
import i18n from 'common/script/i18n';
|
||||
|
||||
import shops from 'common/script/libs/shops';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
ShopItem,
|
||||
@@ -367,19 +369,28 @@
|
||||
computed: {
|
||||
...mapState({
|
||||
content: 'content',
|
||||
seasonal: 'shops.seasonal.data',
|
||||
user: 'user.data',
|
||||
userStats: 'user.data.stats',
|
||||
}),
|
||||
seasonal () {
|
||||
return {
|
||||
text: this.$t('seasonalShop'),
|
||||
notes: this.$t('seasonalShopClosedText'),
|
||||
opened: false, // TODO
|
||||
};
|
||||
},
|
||||
seasonalCategories () {
|
||||
return shops.getSeasonalShopCategories(this.user);
|
||||
},
|
||||
categories () {
|
||||
if (this.seasonal) {
|
||||
this.seasonal.categories.map((category) => {
|
||||
if (this.seasonalCategories) {
|
||||
this.seasonalCategories.map((category) => {
|
||||
this.$set(this.viewOptions, category.identifier, {
|
||||
selected: true,
|
||||
});
|
||||
});
|
||||
|
||||
return this.seasonal.categories;
|
||||
return this.seasonalCategories;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
@@ -491,8 +502,5 @@
|
||||
}
|
||||
},
|
||||
},
|
||||
created () {
|
||||
this.$store.dispatch('shops:fetchSeasonal');
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -417,6 +417,22 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (this.task.type) {
|
||||
case 'habit':
|
||||
this.$root.$emit('playSound', direction === 'up' ? 'Plus_Habit' : 'Minus_Habit');
|
||||
break;
|
||||
case 'todo':
|
||||
this.$root.$emit('playSound', 'Todo');
|
||||
break;
|
||||
case 'daily':
|
||||
this.$root.$emit('playSound', 'Daily');
|
||||
break;
|
||||
case 'reward':
|
||||
this.$root.$emit('playSound', 'Reward');
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (task.group.approval.required) task.group.approval.requested = true;
|
||||
|
||||
const response = await axios.post(`/api/v3/tasks/${task._id}/score/${direction}`);
|
||||
@@ -443,7 +459,8 @@ export default {
|
||||
if (drop) {
|
||||
let text;
|
||||
let type;
|
||||
// TODO $rootScope.playSound('Item_Drop');
|
||||
|
||||
this.$root.$emit('playSound', 'Item_Drop');
|
||||
|
||||
// Note: For Mystery Item gear, drop.type will be 'head', 'armor', etc
|
||||
// so we use drop.notificationType below.
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import axios from 'axios';
|
||||
import { loadAsyncResource } from 'client/libs/asyncResource';
|
||||
import buyOp from 'common/script/ops/buy';
|
||||
import buyQuestOp from 'common/script/ops/buyQuest';
|
||||
import purchaseOp from 'common/script/ops/purchaseWithSpell';
|
||||
@@ -8,55 +7,6 @@ import hourglassPurchaseOp from 'common/script/ops/hourglassPurchase';
|
||||
import sellOp from 'common/script/ops/sell';
|
||||
import unlockOp from 'common/script/ops/unlock';
|
||||
|
||||
export function fetchMarket (store, forceLoad = false) { // eslint-disable-line no-shadow
|
||||
return loadAsyncResource({
|
||||
store,
|
||||
path: 'shops.market',
|
||||
url: '/api/v3/shops/market',
|
||||
deserialize (response) {
|
||||
return response.data.data;
|
||||
},
|
||||
forceLoad,
|
||||
});
|
||||
}
|
||||
|
||||
export function fetchQuests (store, forceLoad = false) { // eslint-disable-line no-shadow
|
||||
return loadAsyncResource({
|
||||
store,
|
||||
path: 'shops.quests',
|
||||
url: '/api/v3/shops/quests',
|
||||
deserialize (response) {
|
||||
return response.data.data;
|
||||
},
|
||||
forceLoad,
|
||||
});
|
||||
}
|
||||
|
||||
export function fetchSeasonal (store, forceLoad = false) { // eslint-disable-line no-shadow
|
||||
return loadAsyncResource({
|
||||
store,
|
||||
path: 'shops.seasonal',
|
||||
url: '/api/v3/shops/seasonal',
|
||||
deserialize (response) {
|
||||
return response.data.data;
|
||||
},
|
||||
forceLoad,
|
||||
});
|
||||
}
|
||||
|
||||
export function fetchTimeTravelers (store, forceLoad = false) { // eslint-disable-line no-shadow
|
||||
return loadAsyncResource({
|
||||
store,
|
||||
path: 'shops.time-travelers',
|
||||
url: '/api/v3/shops/time-travelers',
|
||||
deserialize (response) {
|
||||
return response.data.data;
|
||||
},
|
||||
forceLoad,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export function buyItem (store, params) {
|
||||
const user = store.state.user.data;
|
||||
let opResult = buyOp(user, {params});
|
||||
|
||||
@@ -46,6 +46,8 @@ api.gear = gear;
|
||||
api.spells = spells;
|
||||
api.subscriptionBlocks = subscriptionBlocks;
|
||||
|
||||
api.audioThemes = ['danielTheBard', 'gokulTheme', 'luneFoxTheme', 'wattsTheme', 'rosstavoTheme', 'dewinTheme', 'airuTheme', 'beatscribeNesTheme', 'arashiTheme'];
|
||||
|
||||
api.mystery = timeTravelers.mystery;
|
||||
api.timeTravelerStore = timeTravelers.timeTravelerStore;
|
||||
|
||||
|
||||
@@ -439,7 +439,7 @@ let schema = new Schema({
|
||||
skin: {type: String, default: '915533'},
|
||||
shirt: {type: String, default: 'blue'},
|
||||
timezoneOffset: {type: Number, default: 0},
|
||||
sound: {type: String, default: 'rosstavoTheme', enum: ['off', 'danielTheBard', 'gokulTheme', 'luneFoxTheme', 'wattsTheme', 'rosstavoTheme', 'dewinTheme', 'airuTheme', 'beatscribeNesTheme', 'arashiTheme']},
|
||||
sound: {type: String, default: 'rosstavoTheme', enum: ['off', ...shared.content.audioThemes]},
|
||||
chair: {type: String, default: 'none'},
|
||||
timezoneOffsetAtLastCron: Number,
|
||||
language: String,
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user