mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-03-22 02:45:29 -04:00
Merge pull request #1797 from codecombat/master
Merge master into production
This commit is contained in:
commit
2300cab908
18 changed files with 229 additions and 86 deletions
app
locale
models
styles
templates/play/modal
views
game-menu
play
server
server_config.coffeeserver_setup.coffee
|
@ -96,10 +96,10 @@
|
|||
logging_in: "Logging In"
|
||||
log_out: "Log Out"
|
||||
recover: "recover account"
|
||||
authenticate_gplus: 'Authenticate G+'
|
||||
load_profile: 'Load G+ Profile'
|
||||
load_email: 'Load G+ Email'
|
||||
finishing: 'Finishing'
|
||||
authenticate_gplus: "Authenticate G+"
|
||||
load_profile: "Load G+ Profile"
|
||||
load_email: "Load G+ Email"
|
||||
finishing: "Finishing"
|
||||
|
||||
signup:
|
||||
create_account_title: "Create Account to Save Progress"
|
||||
|
@ -319,12 +319,15 @@
|
|||
unequip: "Unequip"
|
||||
|
||||
buy_gems:
|
||||
few_gems: 'A few gems'
|
||||
pile_gems: 'Pile of gems'
|
||||
chest_gems: 'Chest of gems'
|
||||
purchasing: 'Purchasing...'
|
||||
declined: 'Your card was declined'
|
||||
retrying: 'Server error, retrying.'
|
||||
few_gems: "A few gems"
|
||||
pile_gems: "Pile of gems"
|
||||
chest_gems: "Chest of gems"
|
||||
purchasing: "Purchasing..."
|
||||
declined: "Your card was declined"
|
||||
retrying: "Server error, retrying."
|
||||
prompt_title: "Not Enough Gems"
|
||||
prompt_body: "Do you want to get more?"
|
||||
prompt_button: "Enter Shop"
|
||||
|
||||
choose_hero:
|
||||
choose_hero: "Choose Your Hero"
|
||||
|
|
|
@ -10,7 +10,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
|
|||
for_beginners: "Para Iniciantes"
|
||||
multiplayer: "Multijogador" # Not currently shown on home page
|
||||
for_developers: "Para Programadores" # Not currently shown on home page.
|
||||
# or_ipad: "Or download for iPad"
|
||||
or_ipad: "Ou descarrega para iPad"
|
||||
|
||||
nav:
|
||||
play: "Níveis" # The top nav bar entry where players choose which levels to play
|
||||
|
@ -208,8 +208,8 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
|
|||
failing: "A falhar"
|
||||
action_timeline: "Linha do Tempo de Ações"
|
||||
click_to_select: "Clica numa unidade para selecioná-la."
|
||||
# control_bar_multiplayer: "Multiplayer"
|
||||
# control_bar_join_game: "Join Game"
|
||||
control_bar_multiplayer: "Multijogador"
|
||||
control_bar_join_game: "Entrar no Jogo"
|
||||
reload: "Recarregar"
|
||||
reload_title: "Recarregar o Código Todo?"
|
||||
reload_really: "Tens a certeza que queres recarregar este nível de volta ao início?"
|
||||
|
@ -322,9 +322,9 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
|
|||
few_gems: "Algumas gemas"
|
||||
pile_gems: "Pilha de gemas"
|
||||
chest_gems: "Arca de gemas"
|
||||
# purchasing: "Purchasing..."
|
||||
# declined: "Your card was declined"
|
||||
# retrying: "Server error, retrying."
|
||||
purchasing: "A adquirir..."
|
||||
declined: "O teu cartão foi recusado."
|
||||
retrying: "Erro do servidor, a tentar novamente."
|
||||
|
||||
choose_hero:
|
||||
choose_hero: "Escolhe o Teu Herói"
|
||||
|
@ -351,23 +351,23 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
|
|||
blocks: "Bloqueia" # As in "this shield blocks this much damage"
|
||||
skills: "Habilidades"
|
||||
|
||||
# skill_docs:
|
||||
# writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this
|
||||
# read_only: "read-only"
|
||||
# action_name: "name"
|
||||
# action_cooldown: "Takes"
|
||||
# action_specific_cooldown: "Cooldown"
|
||||
# action_damage: "Damage"
|
||||
# action_range: "Range"
|
||||
# action_radius: "Radius"
|
||||
# action_duration: "Duration"
|
||||
# example: "Example"
|
||||
# ex: "ex" # Abbreviation of "example"
|
||||
# current_value: "Current Value"
|
||||
# default_value: "Default value"
|
||||
# parameters: "Parameters"
|
||||
# returns: "Returns"
|
||||
# granted_by: "Granted by"
|
||||
skill_docs:
|
||||
writable: "escrevível" # Hover over "attack" in Your Skills while playing a level to see most of this
|
||||
read_only: "apenas leitura"
|
||||
action_name: "nome"
|
||||
action_cooldown: "Demora"
|
||||
action_specific_cooldown: "Tempo de Recarga"
|
||||
action_damage: "Dano"
|
||||
action_range: "Alcance"
|
||||
action_radius: "Raio"
|
||||
action_duration: "Duração"
|
||||
example: "Exemplo"
|
||||
ex: "ex" # Abbreviation of "example"
|
||||
current_value: "Valor Atual"
|
||||
default_value: "Valor Predefinido"
|
||||
parameters: "Parâmetros"
|
||||
returns: "Devolve"
|
||||
granted_by: "Garantido por"
|
||||
|
||||
save_load:
|
||||
granularity_saved_games: "Guardados"
|
||||
|
@ -769,8 +769,8 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
|
|||
amount_achieved: "Quantidade"
|
||||
achievement: "Conquista"
|
||||
category_contributor: "Contribuidor"
|
||||
# category_ladder: "Ladder"
|
||||
# category_level: "Level"
|
||||
category_ladder: "Classificação"
|
||||
category_level: "Nível"
|
||||
category_miscellaneous: "Vários"
|
||||
category_levels: "Níveis"
|
||||
category_undefined: "Sem Categoria"
|
||||
|
|
|
@ -19,6 +19,10 @@ module.exports = class ThangType extends CocoModel
|
|||
librarian: '52fbf74b7e01835453bd8d8e'
|
||||
'potion-master': '52e9adf7427172ae56002172'
|
||||
sorcerer: '52fd1524c7e6cf99160e7bc9'
|
||||
@heroClasses:
|
||||
Warrior: ['captain', 'knight', 'samurai']
|
||||
Ranger: ['ninja', 'forest-archer', 'trapper']
|
||||
Wizard: ['librarian', 'potion-master', 'sorcerer']
|
||||
@items:
|
||||
'simple-boots': '53e237bf53457600003e3f05'
|
||||
urlRoot: '/db/thang.type'
|
||||
|
@ -435,7 +439,7 @@ module.exports = class ThangType extends CocoModel
|
|||
name: name, display: display
|
||||
|
||||
isSilhouettedItem: ->
|
||||
return console.error "Trying to determine whether #{@get('name')} should be a silhouetted item, but it has no gem cost." unless @get 'gems'
|
||||
return console.error "Trying to determine whether #{@get('name')} should be a silhouetted item, but it has no gem cost." unless @get('gems') or @get('tier')
|
||||
console.info "Add (or make sure you have fetched) a tier for #{@get('name')} to more accurately determine whether it is silhouetted." unless @get('tier')?
|
||||
tier = @get 'tier'
|
||||
if tier?
|
||||
|
@ -446,5 +450,8 @@ module.exports = class ThangType extends CocoModel
|
|||
|
||||
levelRequiredForItem: ->
|
||||
return console.error "Trying to determine what level is required for #{@get('name')}, but it has no tier." unless @get('tier')?
|
||||
tier = @get 'tier'
|
||||
me.constructor.levelForTier(Math.pow(tier, 0.7))
|
||||
itemTier = @get 'tier'
|
||||
playerTier = itemTier / 2.5
|
||||
playerLevel = me.constructor.levelForTier playerTier
|
||||
#console.log 'Level required for', @get('name'), 'is', playerLevel, 'player tier', playerTier, 'because it is itemTier', itemTier, 'which is normally level', me.constructor.levelForTier(itemTier)
|
||||
playerLevel
|
||||
|
|
|
@ -95,6 +95,13 @@ module.exports = class User extends CocoModel
|
|||
ownsItem: (itemOriginal) -> itemOriginal in @items()
|
||||
ownsLevel: (levelOriginal) -> levelOriginal in @levels()
|
||||
|
||||
getHeroClasses: ->
|
||||
idsToSlugs = _.invert ThangType.heroes
|
||||
myHeroSlugs = (idsToSlugs[id] for id in @heroes())
|
||||
myHeroClasses = []
|
||||
myHeroClasses.push heroClass for heroClass, heroSlugs of ThangType.heroClasses when _.intersection(myHeroSlugs, heroSlugs).length
|
||||
myHeroClasses
|
||||
|
||||
getBranchingGroup: ->
|
||||
return @branchingGroup if @branchingGroup
|
||||
group = me.get('testGroupNumber') % 4
|
||||
|
@ -107,4 +114,14 @@ module.exports = class User extends CocoModel
|
|||
application.tracker.identify branchingGroup: @branchingGroup unless me.isAdmin()
|
||||
@branchingGroup
|
||||
|
||||
getGemPromptGroup: ->
|
||||
return @gemPromptGroup if @gemPromptGroup
|
||||
group = me.get('testGroupNumber') % 8
|
||||
@gemPromptGroup = switch group
|
||||
when 0, 1, 2, 3 then 'prompt'
|
||||
when 4, 5, 6, 7 then 'no-prompt'
|
||||
@gemPromptGroup = 'prompt' if me.isAdmin()
|
||||
application.tracker.identify gemPromptGroup: @gemPromptGroup unless me.isAdmin()
|
||||
@gemPromptGroup
|
||||
|
||||
tiersByLevel = [-1, 0, 0.05, 0.14, 0.18, 0.32, 0.41, 0.5, 0.64, 0.82, 0.91, 1.04, 1.22, 1.35, 1.48, 1.65, 1.78, 1.96, 2.1, 2.24, 2.38, 2.55, 2.69, 2.86, 3.03, 3.16, 3.29, 3.42, 3.58, 3.74, 3.89, 4.04, 4.19, 4.32, 4.47, 4.64, 4.79, 4.96]
|
||||
|
|
|
@ -228,6 +228,9 @@ kbd
|
|||
border-width: 15px 20px
|
||||
.arrow
|
||||
display: none
|
||||
.btn
|
||||
font-size: 20px
|
||||
width: 100%
|
||||
|
||||
.btn.btn-illustrated
|
||||
background: 0
|
||||
|
@ -242,6 +245,9 @@ kbd
|
|||
font-weight: bold
|
||||
color: rgb(248, 197, 146)
|
||||
|
||||
&:hover
|
||||
color: lighten(rgb(248, 197, 146), 5%)
|
||||
|
||||
&:active
|
||||
border-image: url(/images/common/button-background-pressed-border.png) 14 16 16 20 fill round
|
||||
padding: 2px 0 0 2px
|
||||
|
|
|
@ -307,6 +307,13 @@
|
|||
opacity: 1
|
||||
color: rgba(255,255,255, 0.4)
|
||||
|
||||
// Make sure this shows up above our modals.
|
||||
.popover.buy-gems-prompt
|
||||
z-index: 1050
|
||||
text-align: center
|
||||
|
||||
button
|
||||
margin-top: 20px
|
||||
|
||||
//- Use the two-column layout and background image if we are on a narrow screen.
|
||||
|
||||
|
|
6
app/templates/play/modal/buy-gems-prompt.jade
Normal file
6
app/templates/play/modal/buy-gems-prompt.jade
Normal file
|
@ -0,0 +1,6 @@
|
|||
.popover.buy-gems-prompt(role="tooltip")
|
||||
.arrow
|
||||
h2(data-i18n="buy_gems.prompt_title") Not Enough Gems
|
||||
p(data-i18n="buy_gems.prompt_body") Do you want to get more?
|
||||
button.btn.btn-success.btn-illustrated.btn-lg.buy-gems-prompt-button(data-i18n="buy_gems.prompt_button") Enter Shop
|
||||
|
|
@ -37,7 +37,7 @@ if item && !item.owned
|
|||
// Temp, while we only have Warriors: prevent them from buying non-Warrior stuff
|
||||
button.btn.big-font.disabled.unequippable #{item.get('heroClass')} Only
|
||||
else
|
||||
button#selected-item-unlock-button.btn.big-font.unlock-button(disabled=!item.affordable, data-item-id=item.id)
|
||||
button#selected-item-unlock-button.btn.big-font.unlock-button(data-item-id=item.id)
|
||||
span(data-i18n="play.unlock") Unlock
|
||||
span.spl= '('+item.get('gems')
|
||||
img(src="/images/common/gem.png", draggable="false")
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
// Temp, while we only have Warriors: prevent them from buying non-Warrior stuff
|
||||
span.big-font.unequippable= item.get('heroClass')
|
||||
else
|
||||
button.btn.unlock-button.big-font(data-i18n="play.unlock", disabled=!item.affordable, data-item-id=item.id)
|
||||
button.btn.unlock-button.big-font(data-i18n="play.unlock", data-item-id=item.id)
|
||||
.clearfix
|
||||
|
||||
#item-details-view
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
ModalView = require 'views/kinds/ModalView'
|
||||
template = require 'templates/game-menu/inventory-modal'
|
||||
buyGemsPromptTemplate = require 'templates/play/modal/buy-gems-prompt'
|
||||
{me} = require 'lib/auth'
|
||||
ThangType = require 'models/ThangType'
|
||||
CocoCollection = require 'collections/CocoCollection'
|
||||
|
@ -8,6 +9,7 @@ SpriteBuilder = require 'lib/sprites/SpriteBuilder'
|
|||
ItemDetailsView = require 'views/play/modal/ItemDetailsView'
|
||||
Purchase = require 'models/Purchase'
|
||||
LevelOptions = require 'lib/LevelOptions'
|
||||
BuyGemsModal = require 'views/play/modal/BuyGemsModal'
|
||||
|
||||
hasGoneFullScreenOnce = false
|
||||
|
||||
|
@ -31,6 +33,8 @@ module.exports = class InventoryModal extends ModalView
|
|||
'click #equip-item-viewed': 'onClickEquipItemViewed'
|
||||
'click #unequip-item-viewed': 'onClickUnequipItemViewed'
|
||||
'click #close-modal': 'hide'
|
||||
'click .buy-gems-prompt-button': 'onBuyGemsPromptButtonClicked'
|
||||
'click': 'onClickedSomewhere'
|
||||
|
||||
shortcuts:
|
||||
'esc': 'clearSelection'
|
||||
|
@ -224,40 +228,6 @@ module.exports = class InventoryModal extends ModalView
|
|||
@selectUnequippedItem(itemEl)
|
||||
@equipSelectedItem()
|
||||
|
||||
onUnlockButtonClicked: (e) ->
|
||||
button = $(e.target).closest('button')
|
||||
if button.hasClass('confirm')
|
||||
item = @items.get($(e.target).data('item-id'))
|
||||
purchase = Purchase.makeFor(item)
|
||||
purchase.save()
|
||||
|
||||
#- set local changes to mimic what should happen on the server...
|
||||
purchased = me.get('purchased') ? {}
|
||||
purchased.items ?= []
|
||||
purchased.items.push(item.get('original'))
|
||||
|
||||
me.set('purchased', purchased)
|
||||
me.set('spent', (me.get('spent') ? 0) + item.get('gems'))
|
||||
|
||||
#- ...then rerender key bits
|
||||
@itemGroups.lockedItems.remove(item)
|
||||
# Redo all item sorting to make sure that we don't clobber state changes since last render.
|
||||
equipped = _.values @getCurrentEquipmentConfig()
|
||||
@sortItem(item, equipped) for item in @items.models
|
||||
@renderSelectors('#unequipped', '#gems-count')
|
||||
|
||||
@requireLevelEquipment()
|
||||
@delegateEvents()
|
||||
@setUpDraggableEventsForAvailableEquipment()
|
||||
@itemDetailsView.setItem(item)
|
||||
|
||||
Backbone.Mediator.publish 'store:item-purchased', item: item, itemSlug: item.get('slug')
|
||||
else
|
||||
button.addClass('confirm').text($.i18n.t('play.confirm'))
|
||||
@$el.one 'click', (e) ->
|
||||
button.removeClass('confirm').text($.i18n.t('play.unlock')) if e.target isnt button[0]
|
||||
|
||||
|
||||
#- Select/equip higher-level, all encompassing methods the callbacks all use
|
||||
|
||||
selectItemSlot: (slotEl) ->
|
||||
|
@ -470,7 +440,70 @@ module.exports = class InventoryModal extends ModalView
|
|||
else
|
||||
callback?()
|
||||
|
||||
#- TODO: DRY this between PlayItemsModal and InventoryModal
|
||||
|
||||
onUnlockButtonClicked: (e) ->
|
||||
e.stopPropagation()
|
||||
button = $(e.target).closest('button')
|
||||
item = @items.get(button.data('item-id'))
|
||||
affordable = item.affordable
|
||||
if not affordable
|
||||
@askToBuyGems button
|
||||
else if button.hasClass('confirm')
|
||||
purchase = Purchase.makeFor(item)
|
||||
purchase.save()
|
||||
|
||||
#- set local changes to mimic what should happen on the server...
|
||||
purchased = me.get('purchased') ? {}
|
||||
purchased.items ?= []
|
||||
purchased.items.push(item.get('original'))
|
||||
|
||||
me.set('purchased', purchased)
|
||||
me.set('spent', (me.get('spent') ? 0) + item.get('gems'))
|
||||
|
||||
#- ...then rerender key bits
|
||||
@itemGroups.lockedItems.remove(item)
|
||||
# Redo all item sorting to make sure that we don't clobber state changes since last render.
|
||||
equipped = _.values @getCurrentEquipmentConfig()
|
||||
@sortItem(item, equipped) for item in @items.models
|
||||
@renderSelectors('#unequipped', '#gems-count')
|
||||
|
||||
@requireLevelEquipment()
|
||||
@delegateEvents()
|
||||
@setUpDraggableEventsForAvailableEquipment()
|
||||
@itemDetailsView.setItem(item)
|
||||
|
||||
Backbone.Mediator.publish 'store:item-purchased', item: item, itemSlug: item.get('slug')
|
||||
else
|
||||
button.addClass('confirm').text($.i18n.t('play.confirm'))
|
||||
@$el.one 'click', (e) ->
|
||||
button.removeClass('confirm').text($.i18n.t('play.unlock')) if e.target isnt button[0]
|
||||
|
||||
askToBuyGems: (unlockButton) ->
|
||||
if me.getGemPromptGroup() is 'no-prompt'
|
||||
return @openModalView new BuyGemsModal()
|
||||
@$el.find('.unlock-button').popover 'destroy'
|
||||
popoverTemplate = buyGemsPromptTemplate {}
|
||||
unlockButton.popover(
|
||||
animation: true
|
||||
trigger: 'manual'
|
||||
placement: 'top'
|
||||
content: ' ' # template has it
|
||||
container: @$el
|
||||
template: popoverTemplate
|
||||
).popover 'show'
|
||||
popover = unlockButton.data('bs.popover')
|
||||
popover?.$tip?.i18n()
|
||||
|
||||
onBuyGemsPromptButtonClicked: (e) ->
|
||||
@openModalView new BuyGemsModal()
|
||||
|
||||
onClickedSomewhere: (e) ->
|
||||
return if @destroyed
|
||||
@$el.find('.unlock-button').popover 'destroy'
|
||||
|
||||
destroy: ->
|
||||
@$el.find('.unlock-button').popover 'destroy'
|
||||
@stage?.removeAllChildren()
|
||||
super()
|
||||
|
||||
|
|
|
@ -697,7 +697,7 @@ module.exports = class PlayLevelView extends RootView
|
|||
data = snapshot.val()
|
||||
if data? and data.id isnt me.id
|
||||
@realTimeOpponentData = data
|
||||
console.log 'PlayLevelView onRealTimePlayerAdded opponent', @realTimeOpponentData, @realTimePlayersData
|
||||
# console.log 'PlayLevelView onRealTimePlayerAdded opponent', @realTimeOpponentData, @realTimePlayersData
|
||||
@realTimePlayersData[@realTimeOpponentData.id] = @realTimeOpponentData
|
||||
if @realTimeSessionData?.state is 'creating'
|
||||
@realTimeSessionRef.update 'state': 'coding'
|
||||
|
@ -758,7 +758,7 @@ module.exports = class PlayLevelView extends RootView
|
|||
# console.error 'Joining real-time multiplayer game with an existing @realTimeSessionRef.'
|
||||
|
||||
onRealTimeMultiplayerJoinedGame: (e) ->
|
||||
console.log 'PlayLevelView onRealTimeMultiplayerJoinedGame', e
|
||||
# console.log 'PlayLevelView onRealTimeMultiplayerJoinedGame', e
|
||||
@joinRealTimeMultiplayerGame e
|
||||
@realTimePlayerGameRef.update 'state': 'coding'
|
||||
@realTimePlayerRef.update 'state': 'unavailable'
|
||||
|
@ -828,7 +828,7 @@ module.exports = class PlayLevelView extends RootView
|
|||
if me.id is @realTimeSessionData.creator
|
||||
sessionState = @session.get('state')
|
||||
if sessionState?
|
||||
submissionCount = sessionState.submissionCount
|
||||
submissionCount = sessionState.submissionCount ? 0
|
||||
console.info 'Setting multiplayer submissionCount to', submissionCount
|
||||
@realTimeSessionRef.update 'submissionCount': submissionCount
|
||||
else
|
||||
|
@ -849,9 +849,13 @@ module.exports = class PlayLevelView extends RootView
|
|||
transpiledCode[thang][spellID] = aether.transpile spell
|
||||
# console.log "PlayLevelView transpiled code", transpiledCode
|
||||
@session.set 'transpiledCode', transpiledCode
|
||||
permissions = @session.get 'permissions' ? []
|
||||
unless _.find(permissions, (p) -> p.target is 'public' and p.access is 'read')
|
||||
permissions.push target:'public', access:'read'
|
||||
@session.set 'permissions', permissions
|
||||
@session.patch()
|
||||
@realTimePlayerGameRef.update 'state': 'submitted'
|
||||
|
||||
|
||||
console.info 'Other player is', @realTimeOpponentData.state
|
||||
if @realTimeOpponentData.state in ['submitted', 'ready']
|
||||
@realTimeOpponentSubmittedCode @realTimeOpponentData, @realTimePlayerGameData
|
||||
|
@ -926,7 +930,7 @@ module.exports = class PlayLevelView extends RootView
|
|||
[newThang, newSpell] = newSpellKey.split '/'
|
||||
if newSpell is oldSpell
|
||||
# Found spell location under new team
|
||||
console.log "Swapping spell=#{oldSpell} from #{oldThang} to #{newThang}"
|
||||
# console.log "Swapping spell=#{oldSpell} from #{oldThang} to #{newThang}"
|
||||
if code[newThang]?[oldSpell]?
|
||||
# Option 1: have a new spell to swap
|
||||
code[oldThang][oldSpell] = code[newThang][oldSpell]
|
||||
|
|
|
@ -77,7 +77,7 @@ module.exports = class ProblemAlertView extends CocoView
|
|||
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'error_appear', volume: 1.0
|
||||
pauseJiggle = =>
|
||||
@$el?.removeClass 'jiggling'
|
||||
_.delay pauseJiggle, 2000
|
||||
_.delay pauseJiggle, 1000
|
||||
|
||||
onHideProblemAlert: ->
|
||||
@onRemoveClicked()
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
ModalView = require 'views/kinds/ModalView'
|
||||
template = require 'templates/play/modal/play-items-modal'
|
||||
buyGemsPromptTemplate = require 'templates/play/modal/buy-gems-prompt'
|
||||
ItemDetailsView = require './ItemDetailsView'
|
||||
BuyGemsModal = require 'views/play/modal/BuyGemsModal'
|
||||
|
||||
CocoCollection = require 'collections/CocoCollection'
|
||||
ThangType = require 'models/ThangType'
|
||||
|
@ -46,7 +48,9 @@ module.exports = class PlayItemsModal extends ModalView
|
|||
'click .item': 'onItemClicked'
|
||||
'shown.bs.tab': 'onTabClicked'
|
||||
'click .unlock-button': 'onUnlockButtonClicked'
|
||||
'click .buy-gems-prompt-button': 'onBuyGemsPromptButtonClicked'
|
||||
'click #close-modal': 'hide'
|
||||
'click': 'onClickedSomewhere'
|
||||
|
||||
constructor: (options) ->
|
||||
super options
|
||||
|
@ -89,7 +93,7 @@ module.exports = class PlayItemsModal extends ModalView
|
|||
model.affordable = cost <= gemsOwned
|
||||
model.silhouetted = not model.owned and model.isSilhouettedItem()
|
||||
model.level = model.levelRequiredForItem() if model.get('tier')?
|
||||
model.unequippable = not ('Warrior' in model.getAllowedHeroClasses()) # Temp: while there are no wizards/rangers
|
||||
model.unequippable = not _.intersection(me.getHeroClasses(), model.getAllowedHeroClasses()).length
|
||||
model.comingSoon = not model.getFrontFacingStats().props.length and not _.size(model.getFrontFacingStats().stats) and not model.owned # Temp: while there are placeholder items
|
||||
@idToItem[model.id] = model
|
||||
|
||||
|
@ -140,9 +144,14 @@ module.exports = class PlayItemsModal extends ModalView
|
|||
$($(e.target).attr('href')).find('.nano').nanoScroller({alwaysVisible: true})
|
||||
|
||||
onUnlockButtonClicked: (e) ->
|
||||
e.stopPropagation()
|
||||
button = $(e.target).closest('button')
|
||||
if button.hasClass('confirm')
|
||||
item = @idToItem[$(e.target).data('item-id')]
|
||||
item = @idToItem[button.data('item-id')]
|
||||
affordable = item.affordable
|
||||
if not affordable
|
||||
@askToBuyGems button
|
||||
else if button.hasClass('confirm')
|
||||
|
||||
purchase = Purchase.makeFor(item)
|
||||
purchase.save()
|
||||
|
||||
|
@ -163,3 +172,29 @@ module.exports = class PlayItemsModal extends ModalView
|
|||
button.addClass('confirm').text($.i18n.t('play.confirm'))
|
||||
@$el.one 'click', (e) ->
|
||||
button.removeClass('confirm').text($.i18n.t('play.unlock')) if e.target isnt button[0]
|
||||
|
||||
askToBuyGems: (unlockButton) ->
|
||||
if me.getGemPromptGroup() is 'no-prompt'
|
||||
return @openModalView new BuyGemsModal()
|
||||
@$el.find('.unlock-button').popover 'destroy'
|
||||
popoverTemplate = buyGemsPromptTemplate {}
|
||||
unlockButton.popover(
|
||||
animation: true
|
||||
trigger: 'manual'
|
||||
content: ' ' # template has it
|
||||
container: @$el
|
||||
template: popoverTemplate
|
||||
).popover 'show'
|
||||
popover = unlockButton.data('bs.popover')
|
||||
popover?.$tip?.i18n()
|
||||
|
||||
onBuyGemsPromptButtonClicked: (e) ->
|
||||
@openModalView new BuyGemsModal()
|
||||
|
||||
onClickedSomewhere: (e) ->
|
||||
return if @destroyed
|
||||
@$el.find('.unlock-button').popover 'destroy'
|
||||
|
||||
destroy: ->
|
||||
@$el.find('.unlock-button').popover 'destroy'
|
||||
super()
|
||||
|
|
|
@ -5,7 +5,7 @@ if cluster.isMaster
|
|||
for i in [0...numCPUs]
|
||||
cluster.fork()
|
||||
cluster.on 'exit', (worker, code, signal) ->
|
||||
console.log 'worker' + worker.process.id + 'died'
|
||||
console.log 'worker ' + worker.id + ' died'
|
||||
cluster.fork()
|
||||
else
|
||||
require('coffee-script')
|
||||
|
|
|
@ -14,3 +14,15 @@ module.exports.sendHipChatMessage = sendHipChatMessage = (message) ->
|
|||
request.post {uri: url, json: form}, (err, res, body) ->
|
||||
return log.error 'Error sending HipChat patch request:', err or body if err or /error/i.test body
|
||||
#log.info "Got HipChat patch response:", body
|
||||
|
||||
module.exports.sendTowerHipChatMessage = sendTowerHipChatMessage = (message) ->
|
||||
return unless key = config.hipchatTowerAPIKey
|
||||
roomID = 318356
|
||||
form =
|
||||
color: 'red'
|
||||
notify: true
|
||||
message: message
|
||||
messageFormat: 'html'
|
||||
url = "https://api.hipchat.com/v2/room/#{roomID}/notification?auth_token=#{key}"
|
||||
request.post {uri: url, json: form}, (err, res, body) ->
|
||||
return log.error 'Error sending HipChat Tower message:', err or body if err or /error/i.test body
|
||||
|
|
|
@ -27,7 +27,10 @@ module.exports.setup = (app) ->
|
|||
request.get {uri: 'https://api.github.com/user', headers: headers}, (err, r, response) ->
|
||||
return log.error err if err?
|
||||
githubUser = JSON.parse response
|
||||
emailLower = githubUser.email.toLowerCase()
|
||||
log.info 'Got GitHub auth callback response', githubUser
|
||||
emailLower = githubUser.email?.toLowerCase()
|
||||
if not emailLower
|
||||
return errors.serverError res, "Problem finding GitHub user with that identity."
|
||||
|
||||
# GitHub users can change emails
|
||||
User.findOne {$or: [{emailLower: emailLower}, {githubID: githubUser.id}]}, (err, user) ->
|
||||
|
|
|
@ -48,7 +48,7 @@ config.mail =
|
|||
cronHandlerPrivateIP: process.env.COCO_CRON_PRIVATE_IP or ''
|
||||
|
||||
config.hipchatAPIKey = process.env.COCO_HIPCHAT_API_KEY or ''
|
||||
|
||||
config.hipchatTowerAPIKey = process.env.COCO_HIPCHAT_TOWER_API_KEY or ''
|
||||
config.queue =
|
||||
accessKeyId: process.env.COCO_AWS_ACCESS_KEY_ID or ''
|
||||
secretAccessKey: process.env.COCO_AWS_SECRET_ACCESS_KEY or ''
|
||||
|
|
|
@ -13,6 +13,7 @@ logging = require './server/commons/logging'
|
|||
config = require './server_config'
|
||||
auth = require './server/routes/auth'
|
||||
UserHandler = require './server/users/user_handler'
|
||||
hipchat = require './server/hipchat'
|
||||
global.tv4 = require 'tv4' # required for TreemaUtils to work
|
||||
global.jsondiffpatch = require 'jsondiffpatch'
|
||||
|
||||
|
@ -38,6 +39,14 @@ developmentLogging = (tokens, req, res) ->
|
|||
elapsedColor = if elapsed < 500 then 90 else 31
|
||||
"\x1b[90m#{req.method} #{req.originalUrl} \x1b[#{color}m#{res.statusCode} \x1b[#{elapsedColor}m#{elapsed}ms\x1b[0m"
|
||||
|
||||
setupErrorMiddleware = (app) ->
|
||||
app.use (err, req, res, next) ->
|
||||
if err
|
||||
res.status(500).send(error: "Something went wrong!")
|
||||
hipchat.sendTowerHipChatMessage("The server crashed. Stack trace: <br> <code>#{err.stack}</code>")
|
||||
console.log "Got a server error: #{err.stack}"
|
||||
else
|
||||
next(err)
|
||||
setupExpressMiddleware = (app) ->
|
||||
if config.isProduction
|
||||
express.logger.format('prod', productionLogging)
|
||||
|
@ -101,6 +110,7 @@ exports.setupMiddleware = (app) ->
|
|||
setupOneSecondDelayMiddleware app
|
||||
setupTrailingSlashRemovingMiddleware app
|
||||
setupRedirectMiddleware app
|
||||
setupErrorMiddleware app
|
||||
|
||||
###Routing function implementations###
|
||||
|
||||
|
|
Loading…
Reference in a new issue