Merge branch 'master' into production

This commit is contained in:
Nick Winter 2014-11-26 08:53:44 -08:00
commit 8624a82036
33 changed files with 169 additions and 44 deletions

View file

@ -81,3 +81,6 @@ module.exports = class CocoClass
stopListeningToShortcuts: -> stopListeningToShortcuts: ->
return unless key? return unless key?
key.deleteScope(@scope) key.deleteScope(@scope)
playSound: (trigger, volume=1) ->
Backbone.Mediator.publish 'audio-player:play-sound', trigger: trigger, volume: volume

View file

@ -249,9 +249,11 @@ module.exports = LevelOptions =
'swift-dagger': 'swift-dagger':
requiredGear: {waist: 'leather-belt', 'programming-book': 'programmaticon-ii', eyes: 'wooden-glasses', 'right-hand': 'crude-crossbow', 'left-hand': 'crude-dagger', wrists: 'sundial-wristwatch'} requiredGear: {waist: 'leather-belt', 'programming-book': 'programmaticon-ii', eyes: 'wooden-glasses', 'right-hand': 'crude-crossbow', 'left-hand': 'crude-dagger', wrists: 'sundial-wristwatch'}
restrictedGear: {eyes: 'crude-glasses'} restrictedGear: {eyes: 'crude-glasses'}
allowedHeroes: ['ninja', 'trapper', 'forest-archer']
'shrapnel': 'shrapnel':
requiredGear: {waist: 'leather-belt', 'programming-book': 'programmaticon-ii', eyes: 'wooden-glasses', 'right-hand': 'crude-crossbow', 'left-hand': 'weak-charge', wrists: 'sundial-wristwatch'} requiredGear: {waist: 'leather-belt', 'programming-book': 'programmaticon-ii', eyes: 'wooden-glasses', 'right-hand': 'crude-crossbow', 'left-hand': 'weak-charge', wrists: 'sundial-wristwatch'}
restrictedGear: {eyes: 'crude-glasses', 'left-hand': 'crude-dagger'} restrictedGear: {eyes: 'crude-glasses', 'left-hand': 'crude-dagger'}
allowedHeroes: ['ninja', 'trapper', 'forest-archer']
# Wizard branch # Wizard branch
'arcane-ally': 'arcane-ally':
@ -260,10 +262,12 @@ module.exports = LevelOptions =
'touch-of-death': 'touch-of-death':
requiredGear: {waist: 'leather-belt', 'programming-book': 'programmaticon-ii', eyes: 'wooden-glasses', 'right-hand': 'enchanted-stick', 'left-hand': 'unholy-tome-i', wrists: 'sundial-wristwatch'} requiredGear: {waist: 'leather-belt', 'programming-book': 'programmaticon-ii', eyes: 'wooden-glasses', 'right-hand': 'enchanted-stick', 'left-hand': 'unholy-tome-i', wrists: 'sundial-wristwatch'}
restrictedGear: {} restrictedGear: {}
allowedHeroes: ['librarian', 'potion-master', 'sorcerer']
'bonemender': 'bonemender':
requiredGear: {waist: 'leather-belt', 'programming-book': 'programmaticon-ii', eyes: 'wooden-glasses', 'right-hand': 'enchanted-stick', 'left-hand': 'book-of-life-i', wrists: 'sundial-wristwatch'} requiredGear: {waist: 'leather-belt', 'programming-book': 'programmaticon-ii', eyes: 'wooden-glasses', 'right-hand': 'enchanted-stick', 'left-hand': 'book-of-life-i', wrists: 'sundial-wristwatch'}
restrictedGear: {'left-hand': 'unholy-tome-i'} restrictedGear: {'left-hand': 'unholy-tome-i'}
requiredCode: ['canCast'] requiredCode: ['canCast']
allowedHeroes: ['librarian', 'potion-master', 'sorcerer']
'coinucopia': 'coinucopia':
requiredGear: {'programming-book': 'programmaticon-i', feet: 'leather-boots', 'programming-book': 'programmaticon-ii', flag: 'basic-flags'} requiredGear: {'programming-book': 'programmaticon-i', feet: 'leather-boots', 'programming-book': 'programmaticon-ii', flag: 'basic-flags'}

View file

@ -3,6 +3,12 @@ PlayHeroesModal = require 'views/play/modal/PlayHeroesModal'
InventoryModal = require 'views/game-menu/InventoryModal' InventoryModal = require 'views/game-menu/InventoryModal'
LevelSession = require 'models/LevelSession' LevelSession = require 'models/LevelSession'
SuperModel = require 'models/SuperModel' SuperModel = require 'models/SuperModel'
ThangType = require 'models/ThangType'
LevelOptions = require 'lib/LevelOptions'
lastHeroesEarned = me.get('earned')?.heroes ? []
lastHeroesPurchased = me.get('purchased')?.heroes ? []
module.exports = class LevelSetupManager extends CocoClass module.exports = class LevelSetupManager extends CocoClass
@ -46,9 +52,19 @@ module.exports = class LevelSetupManager extends CocoClass
open: -> open: ->
firstModal = if @options.hadEverChosenHero then @inventoryModal else @heroesModal firstModal = if @options.hadEverChosenHero then @inventoryModal else @heroesModal
if (not _.isEqual(lastHeroesEarned, me.get('earned')?.heroes ? []) or
not _.isEqual(lastHeroesPurchased, me.get('purchased')?.heroes ? []))
console.log 'Showing hero picker because heroes earned/purchased has changed.'
firstModal = @heroesModal
else if allowedHeroSlugs = LevelOptions[@options.levelID]?.allowedHeroes
unless _.find(allowedHeroSlugs, (slug) -> ThangType.heroes[slug] is me.get('heroConfig')?.thangType)
firstModal = @heroesModal
lastHeroesEarned = me.get('earned')?.heroes ? []
lastHeroesPurchased = me.get('purchased')?.heroes ? []
@options.parent.openModalView(firstModal) @options.parent.openModalView(firstModal)
# @inventoryModal.onShown() # replace? # @inventoryModal.onShown() # replace?
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-open', volume: 1 @playSound 'game-menu-open'
#- Modal events #- Modal events

View file

@ -247,6 +247,7 @@
tome_select_a_thang: "Select Someone for " tome_select_a_thang: "Select Someone for "
tome_available_spells: "Available Spells" tome_available_spells: "Available Spells"
tome_your_skills: "Your Skills" tome_your_skills: "Your Skills"
tome_help: "Help"
tome_current_method: "Current Method" tome_current_method: "Current Method"
hud_continue_short: "Continue" hud_continue_short: "Continue"
code_saved: "Code Saved" code_saved: "Code Saved"
@ -356,6 +357,7 @@
skills: "Skills" skills: "Skills"
available_for_purchase: "Available for Purchase" available_for_purchase: "Available for Purchase"
level_to_unlock: "Level to unlock:" level_to_unlock: "Level to unlock:"
restricted_to_certain_heroes: "Only certain heroes can play this level."
skill_docs: skill_docs:
writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this

View file

@ -341,6 +341,18 @@ $itemSlotGridHeight: 70px
position: relative position: relative
width: 60px width: 60px
&:not(.equipped):not(.restricted):not(.silhouette)
cursor: pointer
&:hover
padding: 0
img
margin: 1px
button
margin-top: -2px
height: 19px
font-size: 12px
img img
width: 56px width: 56px
height: 56px height: 56px
@ -354,10 +366,27 @@ $itemSlotGridHeight: 70px
font-size: 11px font-size: 11px
border-radius: 1px border-radius: 1px
padding: 0 padding: 0
@include transition(0.1s ease)
.required-level
@include opacity(0)
@include transition(0.6s ease-in)
pointer-events: none
font-size: 14px
text-align: center
width: 100%
bottom: 20px
top: inherit
position: absolute
color: white
&.active &.active
background-color: rgb(81,153,236) background-color: rgb(81,153,236)
button
background-color: lighten(rgb(89,136,47), 10%)
box-shadow: 1px 1px 4px #333
color: white
//.status-message .should-equip-message //.status-message .should-equip-message
// display: inline // display: inline
@ -375,7 +404,6 @@ $itemSlotGridHeight: 70px
&.equipped &.equipped
background-color: #ff5 background-color: #ff5
display: none display: none
cursor: default
//.item-view //.item-view
// cursor: default // cursor: default
@ -385,7 +413,6 @@ $itemSlotGridHeight: 70px
&.restricted &.restricted
background-color: rgba(190, 190, 190, 1) background-color: rgba(190, 190, 190, 1)
cursor: default
@include filter(contrast(50%) brightness(100%)) @include filter(contrast(50%) brightness(100%))
//.item-view //.item-view
@ -394,16 +421,18 @@ $itemSlotGridHeight: 70px
//.status-message .restricted-message //.status-message .restricted-message
// display: inline // display: inline
&.locked &.locked:not(:hover)
cursor: default @include filter(contrast(75%))
//background-color: gray
&.silhouette &.silhouette
cursor: default img
pointer-events: none @include filter(contrast(25%) brightness(25%))
@include filter(contrast(25%) brightness(25%)) opacity: 0.5
opacity: 0.5
&:hover
.required-level
@include opacity(1)
text-shadow: 0 1px 0 black, 1px 0 0 black, 0 -1px 0 black, -1px 0 0 black
//- Hero/Play buttons //- Hero/Play buttons

View file

@ -107,6 +107,11 @@ $heroCanvasHeight: 265px
.hero-avatar .hero-avatar
background-color: goldenrod background-color: goldenrod
&.restricted
@include opacity(0.25)
.hero-avatar
background-color: black
//- Small transformations to jumble the hero icons a little //- Small transformations to jumble the hero icons a little
@ -147,7 +152,7 @@ $heroCanvasHeight: 265px
#hero-carousel #hero-carousel
.hero-item .hero-item
&.locked:not(.purchasable) &.locked:not(.purchasable), &.restricted
@include opacity(0.6) @include opacity(0.6)
canvas, .hero-feature-image canvas, .hero-feature-image
@ -255,7 +260,7 @@ $heroCanvasHeight: 265px
color: #333 color: #333
text-align: center text-align: center
#locked-hero-explanation #locked-hero-explanation, #restricted-hero-explanation
position: absolute position: absolute
left: 32px left: 32px
top: 527px top: 527px

View file

@ -46,6 +46,10 @@
for item in itemGroups.lockedItems.models for item in itemGroups.lockedItems.models
.item(class=item.classes, data-item-id=item.id) .item(class=item.classes, data-item-id=item.id)
img(src=item.getPortraitURL(), draggable="false") img(src=item.getPortraitURL(), draggable="false")
if item.level
.required-level
span(data-i18n="general.player_level")
span.spl= item.level
.clearfix .clearfix
#item-details-view #item-details-view

View file

@ -13,6 +13,7 @@ if entryGroupSlugs
div(class="properties properties-" + slug + " nano-content") div(class="properties properties-" + slug + " nano-content")
else else
// Hero; group by items, no tabs. // Hero; group by items, no tabs.
h4(data-i18n="play_level.tome_your_skills") //h4(data-i18n="play_level.tome_your_skills")
h4(data-i18n="play_level.tome_help")
.properties .properties

View file

@ -12,13 +12,13 @@
.carousel-indicator-container .carousel-indicator-container
ol.carousel-indicators ol.carousel-indicators
for hero, index in heroes for hero, index in heroes
li(data-hero-id=hero.get('original'), title=hero.name, data-slide-to=index, data-target="#hero-carousel", class="hero-indicator hero-index-" + index + (hero.locked ? " locked" : "") + (hero.purchasable ? " purchasable" : "")) li(data-hero-id=hero.get('original'), title=hero.name, data-slide-to=index, data-target="#hero-carousel", class="hero-indicator hero-index-" + index + (hero.locked ? " locked" : "") + (hero.purchasable ? " purchasable" : "") + (hero.restricted ? " restricted" : ""))
.hero-avatar .hero-avatar
if hero.locked && !hero.purchasable if hero.locked && !hero.purchasable
img.lock-indicator(src="/images/pages/game-menu/lock.png", draggable="false") img.lock-indicator(src="/images/pages/game-menu/lock.png", draggable="false")
.carousel-inner .carousel-inner
for hero in heroes for hero in heroes
div(class="item hero-item" + (hero.locked ? " locked" : "") + (hero.purchasable ? " purchasable" : ""), data-hero-id=hero.get('original')) div(class="item hero-item" + (hero.locked ? " locked" : "") + (hero.purchasable ? " purchasable" : "") + (hero.restricted ? " restricted" : ""), data-hero-id=hero.get('original'))
canvas.hero-canvas canvas.hero-canvas
.hero-feature-image .hero-feature-image
img(draggable="false") img(draggable="false")
@ -28,7 +28,7 @@
.hero-stat-row .hero-stat-row
.stat-label(data-i18n='choose_hero.status') .stat-label(data-i18n='choose_hero.status')
.stat-value.hero-status-value(data-i18n=hero.purchasable ? 'play.purchasable' : (hero.locked ? 'play.locked' : 'play.available')) .stat-value.hero-status-value(data-i18n=hero.restricted ? 'inventory.restricted_title' : (hero.purchasable ? 'play.purchasable' : (hero.locked ? 'play.locked' : 'play.available')))
.hero-stat-row .hero-stat-row
.stat-label(data-i18n='choose_hero.weapons') .stat-label(data-i18n='choose_hero.weapons')
@ -53,7 +53,14 @@
#hero-footer #hero-footer
if visibleHero if visibleHero
if visibleHero.purchasable if visibleHero.restricted
#restricted-hero-explanation
h2
span= visibleHero.name
span.spl(data-i18n="inventory.restricted_title") Restricted
span.spr(data-i18n="choose_hero.restricted_to_certain_heroes") Only certain heroes can play this level.
else if visibleHero.purchasable
#purchasable-hero-explanation #purchasable-hero-explanation
h2(data-i18n="choose_hero.available_for_purchase") Available for Purchase h2(data-i18n="choose_hero.available_for_purchase") Available for Purchase
button.btn.unlock-button#purchase-hero-button button.btn.unlock-button#purchase-hero-button

View file

@ -31,6 +31,7 @@ module.exports = class HomeView extends RootView
c c
onClickBeginnerCampaign: (e) -> onClickBeginnerCampaign: (e) ->
@playSound 'menu-button-click'
e.preventDefault() e.preventDefault()
e.stopImmediatePropagation() e.stopImmediatePropagation()
window.tracker?.trackEvent 'Homepage', Action: 'Play' window.tracker?.trackEvent 'Homepage', Action: 'Play'

View file

@ -51,15 +51,15 @@ module.exports = class GameMenuModal extends ModalView
firstView = (@subviews.options_view) firstView = (@subviews.options_view)
firstView.$el.addClass 'active' firstView.$el.addClass 'active'
firstView.onShown?() firstView.onShown?()
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-open', volume: 1 @playSound 'game-menu-open'
@$el.find('.nano:visible').nanoScroller() @$el.find('.nano:visible').nanoScroller()
onTabShown: (e) -> onTabShown: (e) ->
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-tab-switch', volume: 1 @playSound 'game-menu-tab-switch'
@subviews[e.target.hash.substring(1).replace(/-/g, '_')].onShown?() @subviews[e.target.hash.substring(1).replace(/-/g, '_')].onShown?()
onHidden: -> onHidden: ->
super() super()
subview.onHidden?() for subviewKey, subview of @subviews subview.onHidden?() for subviewKey, subview of @subviews
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-close', volume: 1 @playSound 'game-menu-close'
Backbone.Mediator.publish 'music-player:exit-menu', {} Backbone.Mediator.publish 'music-player:exit-menu', {}

View file

@ -44,11 +44,11 @@ module.exports = class LevelGuideView extends CocoView
@$el.find('.nav-tabs li:first').addClass('active') @$el.find('.nav-tabs li:first').addClass('active')
@$el.find('.tab-content .tab-pane:first').addClass('active') @$el.find('.tab-content .tab-pane:first').addClass('active')
@$el.find('.nav-tabs a').click(@clickTab) @$el.find('.nav-tabs a').click(@clickTab)
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'guide-open', volume: 1 @playSound 'guide-open'
clickTab: (e) => clickTab: (e) =>
@$el.find('li.active').removeClass('active') @$el.find('li.active').removeClass('active')
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'guide-tab-switch', volume: 1 @playSound 'guide-tab-switch'
afterInsert: -> afterInsert: ->
super() super()

View file

@ -103,6 +103,9 @@ module.exports = class InventoryModal extends ModalView
else else
@itemGroups.availableItems.add(item) @itemGroups.availableItems.add(item)
# level to unlock
item.level = item.levelRequiredForItem() if item.get('tier')?
onLoaded: -> onLoaded: ->
# Both items and session have been loaded. # Both items and session have been loaded.
@onItemsLoaded() @onItemsLoaded()
@ -205,11 +208,14 @@ module.exports = class InventoryModal extends ModalView
onItemSlotClick: (e) -> onItemSlotClick: (e) ->
return if @remainingRequiredEquipment?.length # Don't let them select a slot if we need them to first equip some require gear. return if @remainingRequiredEquipment?.length # Don't let them select a slot if we need them to first equip some require gear.
@playSound 'menu-button-click'
@selectItemSlot($(e.target).closest('.item-slot')) @selectItemSlot($(e.target).closest('.item-slot'))
onUnequippedItemClick: (e) -> onUnequippedItemClick: (e) ->
return if @justDoubleClicked return if @justDoubleClicked
itemEl = $(e.target).closest('.item') itemEl = $(e.target).closest('.item')
return if itemEl.hasClass('silhouette')
@playSound 'menu-button-click'
@selectUnequippedItem(itemEl) @selectUnequippedItem(itemEl)
onUnequippedItemDoubleClick: (e) -> onUnequippedItemDoubleClick: (e) ->
@ -224,6 +230,7 @@ module.exports = class InventoryModal extends ModalView
onClickUnequipItemViewed: -> @unequipSelectedItem() onClickUnequipItemViewed: -> @unequipSelectedItem()
onClickEquipItemButton: (e) -> onClickEquipItemButton: (e) ->
@playSound 'menu-button-click'
itemEl = $(e.target).closest('.item') itemEl = $(e.target).closest('.item')
@selectUnequippedItem(itemEl) @selectUnequippedItem(itemEl)
@equipSelectedItem() @equipSelectedItem()
@ -403,11 +410,13 @@ module.exports = class InventoryModal extends ModalView
super() super()
onClickChooseHero: -> onClickChooseHero: ->
@playSound 'menu-button-click'
@hide() @hide()
@trigger 'choose-hero-click' @trigger 'choose-hero-click'
onClickPlayLevel: (e) -> onClickPlayLevel: (e) ->
return if @$el.find('#play-level-button').prop 'disabled' return if @$el.find('#play-level-button').prop 'disabled'
@playSound 'menu-button-click'
@showLoading() @showLoading()
ua = navigator.userAgent.toLowerCase() ua = navigator.userAgent.toLowerCase()
unless hasGoneFullScreenOnce or (/safari/.test(ua) and not /chrome/.test(ua)) or $(window).height() >= 658 # Min vertical resolution needed at 1366px wide unless hasGoneFullScreenOnce or (/safari/.test(ua) and not /chrome/.test(ua)) or $(window).height() >= 658 # Min vertical resolution needed at 1366px wide
@ -440,7 +449,7 @@ module.exports = class InventoryModal extends ModalView
else else
callback?() callback?()
#- TODO: DRY this between PlayItemsModal and InventoryModal #- TODO: DRY this between PlayItemsModal and InventoryModal and PlayHeroesModal
onUnlockButtonClicked: (e) -> onUnlockButtonClicked: (e) ->
e.stopPropagation() e.stopPropagation()
@ -448,8 +457,10 @@ module.exports = class InventoryModal extends ModalView
item = @items.get(button.data('item-id')) item = @items.get(button.data('item-id'))
affordable = item.affordable affordable = item.affordable
if not affordable if not affordable
@playSound 'menu-button-click'
@askToBuyGems button @askToBuyGems button
else if button.hasClass('confirm') else if button.hasClass('confirm')
@playSound 'menu-button-unlock-end'
purchase = Purchase.makeFor(item) purchase = Purchase.makeFor(item)
purchase.save() purchase.save()
@ -475,6 +486,7 @@ module.exports = class InventoryModal extends ModalView
Backbone.Mediator.publish 'store:item-purchased', item: item, itemSlug: item.get('slug') Backbone.Mediator.publish 'store:item-purchased', item: item, itemSlug: item.get('slug')
else else
@playSound 'menu-button-unlock-start'
button.addClass('confirm').text($.i18n.t('play.confirm')) button.addClass('confirm').text($.i18n.t('play.confirm'))
@$el.one 'click', (e) -> @$el.one 'click', (e) ->
button.removeClass('confirm').text($.i18n.t('play.unlock')) if e.target isnt button[0] button.removeClass('confirm').text($.i18n.t('play.unlock')) if e.target isnt button[0]
@ -496,6 +508,7 @@ module.exports = class InventoryModal extends ModalView
popover?.$tip?.i18n() popover?.$tip?.i18n()
onBuyGemsPromptButtonClicked: (e) -> onBuyGemsPromptButtonClicked: (e) ->
@playSound 'menu-button-click'
@openModalView new BuyGemsModal() @openModalView new BuyGemsModal()
onClickedSomewhere: (e) -> onClickedSomewhere: (e) ->

View file

@ -137,7 +137,7 @@ module.exports = class MultiplayerView extends CocoView
# console.log 'MultiplayerView found current real-time session', rts # console.log 'MultiplayerView found current real-time session', rts
@currentRealTimeSession = new RealTimeModel("multiplayer_level_sessions/#{@levelID}/#{rts.id}") @currentRealTimeSession = new RealTimeModel("multiplayer_level_sessions/#{@levelID}/#{rts.id}")
@currentRealTimeSession.on 'change', @onCurrentRealTimeSessionChanged @currentRealTimeSession.on 'change', @onCurrentRealTimeSessionChanged
# TODO: Is this necessary? Shouldn't everyone already know we joined a game at this point? # TODO: Is this necessary? Shouldn't everyone already know we joined a game at this point?
Backbone.Mediator.publish 'real-time-multiplayer:joined-game', realTimeSessionID: @currentRealTimeSession.id Backbone.Mediator.publish 'real-time-multiplayer:joined-game', realTimeSessionID: @currentRealTimeSession.id
@ -162,6 +162,7 @@ module.exports = class MultiplayerView extends CocoView
@render?() @render?()
onCreateRealTimeGame: -> onCreateRealTimeGame: ->
@playSound 'menu-button-click'
s = @realTimeSessions.create { s = @realTimeSessions.create {
creator: @session.get('creator') creator: @session.get('creator')
creatorName: @session.get('creatorName') creatorName: @session.get('creatorName')
@ -173,7 +174,7 @@ module.exports = class MultiplayerView extends CocoView
@currentRealTimeSession.on 'change', @onCurrentRealTimeSessionChanged @currentRealTimeSession.on 'change', @onCurrentRealTimeSessionChanged
# TODO: s.id === @currentRealTimeSession.id ? # TODO: s.id === @currentRealTimeSession.id ?
players = new RealTimeCollection("multiplayer_level_sessions/#{@levelID}/#{@currentRealTimeSession.id}/players") players = new RealTimeCollection("multiplayer_level_sessions/#{@levelID}/#{@currentRealTimeSession.id}/players")
players.create players.create
id: me.id id: me.id
state: 'coding' state: 'coding'
name: @session.get('creatorName') name: @session.get('creatorName')
@ -184,20 +185,21 @@ module.exports = class MultiplayerView extends CocoView
onJoinRealTimeGame: (e) -> onJoinRealTimeGame: (e) ->
return if @currentRealTimeSession return if @currentRealTimeSession
@playSound 'menu-button-click'
item = @$el.find(e.target).data('item') item = @$el.find(e.target).data('item')
@currentRealTimeSession = @realTimeSessions.get(item.id) @currentRealTimeSession = @realTimeSessions.get(item.id)
@currentRealTimeSession.on 'change', @onCurrentRealTimeSessionChanged @currentRealTimeSession.on 'change', @onCurrentRealTimeSessionChanged
if @realTimeSessionsPlayers[item.id] if @realTimeSessionsPlayers[item.id]
# TODO: SpellView updateTeam() should take care of this team swap update in the real-time multiplayer session # TODO: SpellView updateTeam() should take care of this team swap update in the real-time multiplayer session
creatorID = @currentRealTimeSession.get('creator') creatorID = @currentRealTimeSession.get('creator')
creator = @realTimeSessionsPlayers[item.id].get(creatorID) creator = @realTimeSessionsPlayers[item.id].get(creatorID)
creatorTeam = creator.get('team') creatorTeam = creator.get('team')
myTeam = @session.get('team') myTeam = @session.get('team')
if myTeam is creatorTeam if myTeam is creatorTeam
myTeam = if creatorTeam is 'humans' then 'ogres' else 'humans' myTeam = if creatorTeam is 'humans' then 'ogres' else 'humans'
@realTimeSessionsPlayers[item.id].create @realTimeSessionsPlayers[item.id].create
id: me.id id: me.id
state: 'coding' state: 'coding'
name: me.get('name') name: me.get('name')
@ -209,6 +211,7 @@ module.exports = class MultiplayerView extends CocoView
@render() @render()
onLeaveRealTimeGame: (e) -> onLeaveRealTimeGame: (e) ->
@playSound 'menu-button-click'
if @currentRealTimeSession if @currentRealTimeSession
@currentRealTimeSession.off 'change', @onCurrentRealTimeSessionChanged @currentRealTimeSession.off 'change', @onCurrentRealTimeSessionChanged
@currentRealTimeSession = null @currentRealTimeSession = null

View file

@ -63,6 +63,7 @@ module.exports = class OptionsView extends CocoView
me.set 'volume', volume me.set 'volume', volume
@$el.find('#option-volume-value').text (volume * 100).toFixed(0) + '%' @$el.find('#option-volume-value').text (volume * 100).toFixed(0) + '%'
Backbone.Mediator.publish 'level:set-volume', volume: volume Backbone.Mediator.publish 'level:set-volume', volume: volume
@playSound 'menu-button-click' # Could have another volume-indicating noise
onHidden: -> onHidden: ->
if @playerName and @playerName isnt me.get('name') if @playerName and @playerName isnt me.get('name')
@ -109,6 +110,7 @@ module.exports = class OptionsView extends CocoView
onEditProfilePhoto: (e) -> onEditProfilePhoto: (e) ->
return if window.application.isIPadApp # TODO: have an iPad-native way of uploading a photo, since we don't want to load FilePicker on iPad (memory) return if window.application.isIPadApp # TODO: have an iPad-native way of uploading a photo, since we don't want to load FilePicker on iPad (memory)
@playSound 'menu-button-click'
photoContainer = @$el.find('.profile-photo') photoContainer = @$el.find('.profile-photo')
onSaving = => onSaving = =>
photoContainer.addClass('saving') photoContainer.addClass('saving')

View file

@ -19,6 +19,7 @@ module.exports = class SaveLoadView extends CocoView
super() super()
onSaveGranularityChanged: (e) -> onSaveGranularityChanged: (e) ->
@playSound 'menu-button-click'
toShow = $(e.target).val() toShow = $(e.target).val()
@$el.find('.save-list, .save-pane').hide() @$el.find('.save-list, .save-pane').hide()
@$el.find('.save-list.' + toShow + ', .save-pane.' + toShow).show() @$el.find('.save-list.' + toShow + ', .save-pane.' + toShow).show()

View file

@ -341,10 +341,11 @@ module.exports = class CocoView extends Backbone.View
@pointerRadialDistance = -47 @pointerRadialDistance = -47
@pointerRotation = options.rotation ? Math.atan2(@$el.outerWidth() * 0.5 - targetLeft, targetTop - @$el.outerHeight() * 0.5) @pointerRotation = options.rotation ? Math.atan2(@$el.outerWidth() * 0.5 - targetLeft, targetTop - @$el.outerHeight() * 0.5)
initialScale = Math.max 1, 20 - me.level()
$pointer.css $pointer.css
opacity: 1.0 opacity: 1.0
transition: 'none' transition: 'none'
transform: "rotate(#{@pointerRotation}rad) translate(-3px, #{@pointerRadialDistance}px) scale(20)" transform: "rotate(#{@pointerRotation}rad) translate(-3px, #{@pointerRadialDistance}px) scale(#{initialScale})"
top: targetTop - 50 top: targetTop - 50
left: targetLeft - 50 left: targetLeft - 50
_.defer => _.defer =>
@ -427,7 +428,7 @@ module.exports = class CocoView extends Backbone.View
d.msRequestFullscreen or d.msRequestFullscreen or
(if d.webkitRequestFullscreen then -> d.webkitRequestFullscreen Element.ALLOW_KEYBOARD_INPUT else null) (if d.webkitRequestFullscreen then -> d.webkitRequestFullscreen Element.ALLOW_KEYBOARD_INPUT else null)
req?.call d req?.call d
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'full-screen-start', volume: 1 if req @playSound 'full-screen-start' if req
else else
nah = document.exitFullscreen or nah = document.exitFullscreen or
document.mozCancelFullScreen or document.mozCancelFullScreen or
@ -435,9 +436,12 @@ module.exports = class CocoView extends Backbone.View
document.msExitFullscreen or document.msExitFullscreen or
document.webkitExitFullscreen document.webkitExitFullscreen
nah?.call document nah?.call document
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'full-screen-end', volume: 1 if nah @playSound 'full-screen-end' if req
return return
playSound: (trigger, volume=1) ->
Backbone.Mediator.publish 'audio-player:play-sound', trigger: trigger, volume: volume
mobileRELong = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i mobileRELong = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i
mobileREShort = /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i mobileREShort = /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i

View file

@ -50,23 +50,27 @@ module.exports = class AuthModal extends ModalView
_.delay (=> $('input:visible:first', @$el).focus()), 500 _.delay (=> $('input:visible:first', @$el).focus()), 500
onSignupInstead: (e) -> onSignupInstead: (e) ->
@playSound 'menu-button-click'
@mode = 'signup' @mode = 'signup'
@previousFormInputs = forms.formToObject @$el @previousFormInputs = forms.formToObject @$el
@render() @render()
_.delay application.router.renderLoginButtons, 500 _.delay application.router.renderLoginButtons, 500
onLoginInstead: (e) -> onLoginInstead: (e) ->
@playSound 'menu-button-click'
@mode = 'login' @mode = 'login'
@previousFormInputs = forms.formToObject @$el @previousFormInputs = forms.formToObject @$el
@render() @render()
_.delay application.router.renderLoginButtons, 500 _.delay application.router.renderLoginButtons, 500
onSubmitForm: (e) -> onSubmitForm: (e) ->
@playSound 'menu-button-click'
e.preventDefault() e.preventDefault()
if @mode is 'login' then @loginAccount() else @createAccount() if @mode is 'login' then @loginAccount() else @createAccount()
false false
checkAge: (e) -> checkAge: (e) ->
@playSound 'menu-button-click'
$('#signup-button', @$el).prop 'disabled', not $(e.target).prop('checked') $('#signup-button', @$el).prop 'disabled', not $(e.target).prop('checked')
loginAccount: -> loginAccount: ->
@ -116,6 +120,7 @@ module.exports = class AuthModal extends ModalView
forms.setErrorToProperty @$el, 'name', "That name is taken! How about #{newName}?", true forms.setErrorToProperty @$el, 'name', "That name is taken! How about #{newName}?", true
onGitHubLoginClicked: -> onGitHubLoginClicked: ->
@playSound 'menu-button-click'
Backbone.Mediator.publish 'auth:log-in-with-github', {} Backbone.Mediator.publish 'auth:log-in-with-github', {}
gplusAuthSteps: [ gplusAuthSteps: [
@ -126,6 +131,7 @@ module.exports = class AuthModal extends ModalView
] ]
onClickGPlusLogin: -> onClickGPlusLogin: ->
@playSound 'menu-button-click'
step.done = false for step in @gplusAuthSteps step.done = false for step in @gplusAuthSteps
handler = application.gplusHandler handler = application.gplusHandler

View file

@ -27,6 +27,7 @@ module.exports = class ContactModal extends ModalView
'click #contact-submit-button': 'contact' 'click #contact-submit-button': 'contact'
contact: -> contact: ->
@playSound 'menu-button-click'
forms.clearFormAlerts @$el forms.clearFormAlerts @$el
contactMessage = forms.formToObject @$el contactMessage = forms.formToObject @$el
res = tv4.validateMultiple contactMessage, contactSchema res = tv4.validateMultiple contactMessage, contactSchema

View file

@ -28,6 +28,7 @@ module.exports = class RecoverModal extends ModalView
super options super options
recoverAccount: (e) => recoverAccount: (e) =>
@playSound 'menu-button-click'
forms.clearFormAlerts(@$el) forms.clearFormAlerts(@$el)
email = (forms.formToObject @$el).email email = (forms.formToObject @$el).email
return unless email return unless email

View file

@ -48,6 +48,7 @@ module.exports = class LadderSubmissionView extends CocoView
rankSession: (e) -> rankSession: (e) ->
return unless @session.readyToRank() return unless @session.readyToRank()
@playSound 'menu-button-click'
@setRankingButtonText 'submitting' @setRankingButtonText 'submitting'
success = => success = =>
@setRankingButtonText 'submitted' unless @destroyed @setRankingButtonText 'submitted' unless @destroyed
@ -87,4 +88,5 @@ module.exports = class LadderSubmissionView extends CocoView
transpiledCode transpiledCode
onHelpSimulate: -> onHelpSimulate: ->
@playSound 'menu-button-click'
$('a[href="#simulate"]').tab('show') $('a[href="#simulate"]').tab('show')

View file

@ -49,6 +49,7 @@ module.exports = class LevelFlagsView extends CocoView
onFlagSelected: (e) -> onFlagSelected: (e) ->
return unless @realTime return unless @realTime
@playSound 'menu-button-click' if e.color
color = if e.color is @flagColor then null else e.color color = if e.color is @flagColor then null else e.color
@flagColor = color @flagColor = color
Backbone.Mediator.publish 'level:flag-color-selected', color: color Backbone.Mediator.publish 'level:flag-color-selected', color: color
@ -57,6 +58,7 @@ module.exports = class LevelFlagsView extends CocoView
onStageMouseDown: (e) -> onStageMouseDown: (e) ->
return unless @flagColor and @realTime return unless @flagColor and @realTime
@playSound 'menu-button-click' # TODO: different flag placement sound?
pos = x: e.worldPos.x, y: e.worldPos.y pos = x: e.worldPos.x, y: e.worldPos.y
delay = if @realTimeFlags then multiplayerFlagDelay else 0 delay = if @realTimeFlags then multiplayerFlagDelay else 0
now = @world.dt * @world.frames.length now = @world.dt * @world.frames.length

View file

@ -72,6 +72,7 @@ module.exports = class LevelLoadingView extends CocoView
@$el.find('.start-level-button').removeClass 'secret' @$el.find('.start-level-button').removeClass 'secret'
startUnveiling: (e) -> startUnveiling: (e) ->
@playSound 'menu-button-click'
Backbone.Mediator.publish 'level:loading-view-unveiling', {} Backbone.Mediator.publish 'level:loading-view-unveiling', {}
_.delay @onClickStartLevel, 1000 # If they never mouse-up for the click (or a modal shows up and interrupts the click), do it anyway. _.delay @onClickStartLevel, 1000 # If they never mouse-up for the click (or a modal shows up and interrupts the click), do it anyway.

View file

@ -293,6 +293,7 @@ module.exports = class HeroVictoryModal extends ModalView
levelInfo?.nextLevels?[type] # 'more_practice', 'skip_ahead', 'continue' levelInfo?.nextLevels?[type] # 'more_practice', 'skip_ahead', 'continue'
onClickContinue: (e) -> onClickContinue: (e) ->
@playSound 'menu-button-click'
nextLevelLink = @continueLevelLink nextLevelLink = @continueLevelLink
if me.getBranchingGroup() is 'all-practice' and @morePracticeLevelLink if me.getBranchingGroup() is 'all-practice' and @morePracticeLevelLink
nextLevelLink = @morePracticeLevelLink nextLevelLink = @morePracticeLevelLink
@ -307,6 +308,7 @@ module.exports = class HeroVictoryModal extends ModalView
@$el.find('.next-levels-prompt').show() @$el.find('.next-levels-prompt').show()
onClickNextLevelBranch: (e) -> onClickNextLevelBranch: (e) ->
@playSound 'menu-button-click'
e.preventDefault() e.preventDefault()
route = $(e.target).data('href') or "/play/#{@getNextLevelCampaign()}" route = $(e.target).data('href') or "/play/#{@getNextLevelCampaign()}"
application.tracker?.trackEvent 'Branch Selected', level: @level.get('slug'), label: @level.get('slug'), branch: $(e.target).data('branch-key'), branchingGroup: me.getBranchingGroup(), route: route application.tracker?.trackEvent 'Branch Selected', level: @level.get('slug'), label: @level.get('slug'), branch: $(e.target).data('branch-key'), branchingGroup: me.getBranchingGroup(), route: route
@ -314,6 +316,7 @@ module.exports = class HeroVictoryModal extends ModalView
Backbone.Mediator.publish 'router:navigate', route: route, viewClass: require('views/play/WorldMapView'), viewArgs: [{supermodel: if @options.hasReceivedMemoryWarning then null else @supermodel}, @getNextLevelCampaign()] Backbone.Mediator.publish 'router:navigate', route: route, viewClass: require('views/play/WorldMapView'), viewArgs: [{supermodel: if @options.hasReceivedMemoryWarning then null else @supermodel}, @getNextLevelCampaign()]
onClickReturnToLadder: (e) -> onClickReturnToLadder: (e) ->
@playSound 'menu-button-click'
e.preventDefault() e.preventDefault()
route = $(e.target).data('href') route = $(e.target).data('href')
# Preserve the supermodel as we navigate back to the ladder. # Preserve the supermodel as we navigate back to the ladder.

View file

@ -9,6 +9,7 @@ module.exports = class ReloadLevelModal extends ModalView
'click #restart-level-confirm-button': 'onClickRestart' 'click #restart-level-confirm-button': 'onClickRestart'
onClickRestart: (e) -> onClickRestart: (e) ->
@playSound 'menu-button-click'
if key.shift if key.shift
Backbone.Mediator.publish 'level:restart', {} Backbone.Mediator.publish 'level:restart', {}
else else

View file

@ -83,6 +83,7 @@ module.exports = class ProblemAlertView extends CocoView
@onRemoveClicked() @onRemoveClicked()
onRemoveClicked: -> onRemoveClicked: ->
@playSound 'menu-button-click'
@$el.hide() @$el.hide()
onWindowResize: (e) => onWindowResize: (e) =>

View file

@ -48,6 +48,7 @@ module.exports = class BuyGemsModal extends ModalView
@render() @render()
onClickProductButton: (e) -> onClickProductButton: (e) ->
@playSound 'menu-button-click'
productID = $(e.target).closest('button').val() productID = $(e.target).closest('button').val()
product = _.find @products, { id: productID } product = _.find @products, { id: productID }
@ -60,9 +61,9 @@ module.exports = class BuyGemsModal extends ModalView
description: $.t(product.i18n) description: $.t(product.i18n)
amount: product.amount amount: product.amount
}) })
@productBeingPurchased = product @productBeingPurchased = product
onStripeReceivedToken: (e) -> onStripeReceivedToken: (e) ->
@timestampForPurchase = new Date().getTime() @timestampForPurchase = new Date().getTime()
data = { data = {

View file

@ -69,7 +69,7 @@ module.exports = class ItemDetailsView extends CocoView
c.stats = _.values(stats.stats) c.stats = _.values(stats.stats)
_.last(c.stats).isLast = true if c.stats.length _.last(c.stats).isLast = true if c.stats.length
c.props = [] c.props = []
stats.props = stats.props.concat _.keys @spellDocs stats.props = _.union stats.props, _.keys @spellDocs
codeLanguage = (me.get('aceConfig') ? {}).language or 'python' codeLanguage = (me.get('aceConfig') ? {}).language or 'python'
for prop in stats.props for prop in stats.props
doc = @propDocs[prop] ? @spellDocs[prop] ? {} doc = @propDocs[prop] ? @spellDocs[prop] ? {}

View file

@ -21,7 +21,7 @@ module.exports = class PlayAccountModal extends ModalView
afterRender: -> afterRender: ->
super() super()
return unless @supermodel.finished() return unless @supermodel.finished()
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-open', volume: 1 @playSound 'game-menu-open'
@accountSettingsView = new AccountSettingsView() @accountSettingsView = new AccountSettingsView()
@insertSubView(@accountSettingsView) @insertSubView(@accountSettingsView)
@listenTo @accountSettingsView, 'input-changed', @onInputChanged @listenTo @accountSettingsView, 'input-changed', @onInputChanged
@ -31,7 +31,7 @@ module.exports = class PlayAccountModal extends ModalView
onHidden: -> onHidden: ->
super() super()
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-close', volume: 1 @playSound 'game-menu-close'
onInputChanged: -> onInputChanged: ->
@$el.find('#save-button') @$el.find('#save-button')

View file

@ -92,8 +92,8 @@ module.exports = class PlayAchievementsModal extends ModalView
afterRender: -> afterRender: ->
super() super()
return unless @supermodel.finished() return unless @supermodel.finished()
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-open', volume: 1 @playSound 'game-menu-open'
onHidden: -> onHidden: ->
super() super()
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-close', volume: 1 @playSound 'game-menu-close'

View file

@ -8,6 +8,7 @@ AudioPlayer = require 'lib/AudioPlayer'
utils = require 'lib/utils' utils = require 'lib/utils'
BuyGemsModal = require 'views/play/modal/BuyGemsModal' BuyGemsModal = require 'views/play/modal/BuyGemsModal'
Purchase = require 'models/Purchase' Purchase = require 'models/Purchase'
LevelOptions = require 'lib/LevelOptions'
module.exports = class PlayHeroesModal extends ModalView module.exports = class PlayHeroesModal extends ModalView
className: 'modal fade play-modal' className: 'modal fade play-modal'
@ -54,6 +55,8 @@ module.exports = class PlayHeroesModal extends ModalView
original = hero.get('original') original = hero.get('original')
hero.locked = not me.ownsHero(original) hero.locked = not me.ownsHero(original)
hero.purchasable = hero.locked and (original in (me.get('earned')?.heroes ? [])) hero.purchasable = hero.locked and (original in (me.get('earned')?.heroes ? []))
if @options.levelID and allowedHeroSlugs = LevelOptions[@options.levelID]?.allowedHeroes
hero.restricted = not (hero.get('slug') in allowedHeroSlugs)
hero.class = (hero.get('heroClass') or 'warrior').toLowerCase() hero.class = (hero.get('heroClass') or 'warrior').toLowerCase()
hero.stats = hero.getHeroStats() hero.stats = hero.getHeroStats()
@ -225,9 +228,10 @@ module.exports = class PlayHeroesModal extends ModalView
button = $(e.target).closest('button') button = $(e.target).closest('button')
affordable = @visibleHero.get('gems') <= me.gems() affordable = @visibleHero.get('gems') <= me.gems()
if not affordable if not affordable
@playSound 'menu-button-click'
@askToBuyGems button @askToBuyGems button
else if button.hasClass('confirm') else if button.hasClass('confirm')
@playSound 'menu-button-unlock-end'
purchase = Purchase.makeFor(@visibleHero) purchase = Purchase.makeFor(@visibleHero)
purchase.save() purchase.save()
@ -246,6 +250,7 @@ module.exports = class PlayHeroesModal extends ModalView
Backbone.Mediator.publish 'store:hero-purchased', hero: @visibleHero, heroSlug: @visibleHero.get('slug') Backbone.Mediator.publish 'store:hero-purchased', hero: @visibleHero, heroSlug: @visibleHero.get('slug')
else else
@playSound 'menu-button-unlock-start'
button.addClass('confirm').text($.i18n.t('play.confirm')) button.addClass('confirm').text($.i18n.t('play.confirm'))
@$el.one 'click', (e) -> @$el.one 'click', (e) ->
button.removeClass('confirm').text($.i18n.t('play.unlock')) if e.target isnt button[0] button.removeClass('confirm').text($.i18n.t('play.unlock')) if e.target isnt button[0]
@ -267,6 +272,7 @@ module.exports = class PlayHeroesModal extends ModalView
popover?.$tip?.i18n() popover?.$tip?.i18n()
onBuyGemsPromptButtonClicked: (e) -> onBuyGemsPromptButtonClicked: (e) ->
@playSound 'menu-button-click'
@openModalView new BuyGemsModal() @openModalView new BuyGemsModal()
onClickedSomewhere: (e) -> onClickedSomewhere: (e) ->

View file

@ -127,6 +127,7 @@ module.exports = class PlayItemsModal extends ModalView
onItemClicked: (e) -> onItemClicked: (e) ->
return if $(e.target).closest('.unlock-button').length return if $(e.target).closest('.unlock-button').length
@playSound 'menu-button-click'
itemEl = $(e.target).closest('.item') itemEl = $(e.target).closest('.item')
wasSelected = itemEl.hasClass('selected') wasSelected = itemEl.hasClass('selected')
@$el.find('.item.selected').removeClass('selected') @$el.find('.item.selected').removeClass('selected')
@ -141,6 +142,7 @@ module.exports = class PlayItemsModal extends ModalView
@itemDetailsView.setItem(item) @itemDetailsView.setItem(item)
onTabClicked: (e) -> onTabClicked: (e) ->
@playSound 'game-menu-tab-switch'
$($(e.target).attr('href')).find('.nano').nanoScroller({alwaysVisible: true}) $($(e.target).attr('href')).find('.nano').nanoScroller({alwaysVisible: true})
onUnlockButtonClicked: (e) -> onUnlockButtonClicked: (e) ->
@ -149,9 +151,10 @@ module.exports = class PlayItemsModal extends ModalView
item = @idToItem[button.data('item-id')] item = @idToItem[button.data('item-id')]
affordable = item.affordable affordable = item.affordable
if not affordable if not affordable
@playSound 'menu-button-click'
@askToBuyGems button @askToBuyGems button
else if button.hasClass('confirm') else if button.hasClass('confirm')
@playSound 'menu-button-unlock-end'
purchase = Purchase.makeFor(item) purchase = Purchase.makeFor(item)
purchase.save() purchase.save()
@ -169,6 +172,7 @@ module.exports = class PlayItemsModal extends ModalView
Backbone.Mediator.publish 'store:item-purchased', item: item, itemSlug: item.get('slug') Backbone.Mediator.publish 'store:item-purchased', item: item, itemSlug: item.get('slug')
else else
@playSound 'menu-button-unlock-start'
button.addClass('confirm').text($.i18n.t('play.confirm')) button.addClass('confirm').text($.i18n.t('play.confirm'))
@$el.one 'click', (e) -> @$el.one 'click', (e) ->
button.removeClass('confirm').text($.i18n.t('play.unlock')) if e.target isnt button[0] button.removeClass('confirm').text($.i18n.t('play.unlock')) if e.target isnt button[0]
@ -189,6 +193,7 @@ module.exports = class PlayItemsModal extends ModalView
popover?.$tip?.i18n() popover?.$tip?.i18n()
onBuyGemsPromptButtonClicked: (e) -> onBuyGemsPromptButtonClicked: (e) ->
@playSound 'menu-button-click'
@openModalView new BuyGemsModal() @openModalView new BuyGemsModal()
onClickedSomewhere: (e) -> onClickedSomewhere: (e) ->

View file

@ -21,8 +21,8 @@ module.exports = class PlaySettingsModal extends ModalView
afterRender: -> afterRender: ->
super() super()
return unless @supermodel.finished() return unless @supermodel.finished()
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-open', volume: 1 @playSound 'game-menu-open'
onHidden: -> onHidden: ->
super() super()
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-close', volume: 1 @playSound 'game-menu-close'