Merge branch 'master' into production

This commit is contained in:
Nick Winter 2014-11-21 13:09:02 -08:00
commit 472b05a5e0
13 changed files with 174 additions and 149 deletions

View file

@ -342,6 +342,7 @@ module.exports = Surface = class Surface extends CocoClass
@ended = true
@setPaused true
Backbone.Mediator.publish 'surface:playback-ended', {}
@updatePaths() # TODO: this is a hack to make sure paths are on the first time the level loads
else if @currentFrame < @world.totalFrames and @ended
@ended = false
@setPaused false
@ -586,7 +587,6 @@ module.exports = Surface = class Surface extends CocoClass
updatePaths: ->
return unless @options.paths and @heroLank
return unless me.isAdmin() # TODO: Fix world thang points, targets, then remove this
@hidePaths()
return if @world.showPaths is 'never'
layerAdapter = @lankBoss.layerAdapters['Path']

View file

@ -66,7 +66,7 @@
&.disabled
@include opacity(0.8)
.ace_cursor, .executing, .ace_active-line, .ace_gutter-active-line
@include opacity(0.2)
@include opacity(0.1)
.ace_gutter
background-color: transparent

View file

@ -45,11 +45,15 @@
// color: rgb(197, 6, 11)
color: rgb(243, 169, 49)
body:not(.dialogue-view-active)
.spell-palette-popover.popover
right: 45%
min-width: 350px
.spell-palette-popover.popover
// Only those popovers which are our direct children (spell documentation)
max-width: 600px
right: 45%
min-width: 350px
&.pinned
left: auto !important

View file

@ -378,11 +378,13 @@ module.exports = class InventoryModal extends ModalView
unless itemModel and heroClass in itemModel.classes
console.log 'Unequipping', itemModel.get('heroClass'), 'item', itemModel.get('name'), 'from slot due to class restrictions.'
@unequipItemFromSlot @$el.find(".item-slot[data-slot='#{slot}']")
delete equipment[slot]
for slot, item of restrictedGear
equipped = equipment[slot]
if equipped and equipped is gear[restrictedGear[slot]]
console.log 'Unequipping restricted item', restrictedGear[slot], 'for', slot, 'before level', @options.levelID
@unequipItemFromSlot @$el.find(".item-slot[data-slot='#{slot}']")
delete equipment[slot]
if heroClass is 'Warrior'
# After they switch to a ranger or wizard, we stop being so finicky about gear.
for slot, item of requiredGear

View file

@ -40,7 +40,7 @@ module.exports = class WorldMapView extends RootView
@levelStatusMap = {}
@levelPlayCountMap = {}
@sessions = @supermodel.loadCollection(new LevelSessionsCollection(), 'your_sessions', null, 0).model
# Temporary attempt to make sure all earned rewards are accounted for. Figure out a better solution...
@earnedAchievements = new CocoCollection([], {url: '/db/earned_achievement', model:EarnedAchievement, project: ['earnedRewards']})
@listenToOnce @earnedAchievements, 'sync', ->
@ -56,7 +56,7 @@ module.exports = class WorldMapView extends RootView
earned[group].push(reward)
addedSomething = true
@supermodel.loadCollection(@earnedAchievements, 'achievements')
@listenToOnce @sessions, 'sync', @onSessionsLoaded
@getLevelPlayCounts()
$(window).on 'resize', @onWindowResize
@ -136,7 +136,7 @@ module.exports = class WorldMapView extends RootView
if levelID = @$el.find('.level.next').data('level-id')
@$levelInfo = @$el.find(".level-info-container[data-level-id=#{levelID}]").show()
pos = @$el.find('.level.next').offset()
@adjustLevelInfoPosition pageX: pos.left, pageY: pos.top + 250
@adjustLevelInfoPosition pageX: pos.left, pageY: pos.top
@manuallyPositionedLevelInfoID = levelID
afterInsert: ->
@ -203,6 +203,7 @@ module.exports = class WorldMapView extends RootView
@$levelInfo = @$el.find(".level-info-container[data-level-id=#{levelID}]").show()
@adjustLevelInfoPosition e
@endHighlight()
@manuallyPositionedLevelInfoID = false
onMouseLeaveLevel: (e) ->
return if application.isIPadApp

View file

@ -19,13 +19,10 @@ module.exports = class LevelDialogueView extends CocoView
onClick: (e) ->
Backbone.Mediator.publish 'tome:focus-editor', {}
onFrameChanged: (e) ->
@timeProgress = e.progress
@update()
onSpriteDialogue: (e) ->
return unless e.message
@$el.addClass 'active speaking'
$('body').addClass('dialogue-view-active')
@setMessage e.message, e.mood, e.responses
window.tracker?.trackEvent 'Heard Sprite', {message: e.message, label: e.message}, ['Google Analytics']
@ -35,6 +32,7 @@ module.exports = class LevelDialogueView extends CocoView
onSpriteClearDialogue: ->
@$el.removeClass 'active speaking'
$('body').removeClass('dialogue-view-active')
setMessage: (message, mood, responses) ->
message = marked message

View file

@ -56,6 +56,7 @@ module.exports = class LevelHUDView extends CocoView
setThang: (thang, thangType) ->
if not thang? and not @thang? then return
if thang? and @thang? and thang.id is @thang.id then return
if thang? and @hidesHUD and thang.id isnt 'Hero Placeholder' then return # Don't let them find the names of their opponents this way
@thang = thang
@thangType = thangType
return unless @thang

View file

@ -37,10 +37,11 @@ module.exports = class SpellPaletteEntryView extends CocoView
afterRender: ->
super()
@$el.addClass(@doc.type)
placement = -> if $('body').hasClass('dialogue-view-active') then 'top' else 'left'
@$el.popover(
animation: false
html: true
placement: 'left'
placement: placement
trigger: 'manual' # Hover, until they click, which will then pin it until unclick.
content: @docFormatter.formatPopover()
container: 'body'

View file

@ -131,13 +131,12 @@ module.exports = class SpellView extends CocoView
addCommand
name: 'toggle-playing'
bindKey: {win: 'Ctrl-P', mac: 'Command-P|Ctrl-P'}
readOnly: true
exec: -> Backbone.Mediator.publish 'level:toggle-playing', {}
addCommand
name: 'end-current-script'
bindKey: {win: 'Shift-Space', mac: 'Shift-Space'}
# passEvent: true # https://github.com/ajaxorg/ace/blob/master/lib/ace/keyboard/keybinding.js#L114
# No easy way to selectively cancel shift+space, since we don't get access to the event.
# Maybe we could temporarily set ourselves to read-only if we somehow know that a script is active?
readOnly: true
exec: =>
if @scriptRunning
Backbone.Mediator.publish 'level:shift-space-pressed', {}
@ -147,34 +146,44 @@ module.exports = class SpellView extends CocoView
addCommand
name: 'end-all-scripts'
bindKey: {win: 'Escape', mac: 'Escape'}
exec: -> Backbone.Mediator.publish 'level:escape-pressed', {}
readOnly: true
exec: ->
console.log 'esc pressed'
Backbone.Mediator.publish 'level:escape-pressed', {}
addCommand
name: 'toggle-grid'
bindKey: {win: 'Ctrl-G', mac: 'Command-G|Ctrl-G'}
readOnly: true
exec: -> Backbone.Mediator.publish 'level:toggle-grid', {}
addCommand
name: 'toggle-debug'
bindKey: {win: 'Ctrl-\\', mac: 'Command-\\|Ctrl-\\'}
readOnly: true
exec: -> Backbone.Mediator.publish 'level:toggle-debug', {}
addCommand
name: 'toggle-pathfinding'
bindKey: {win: 'Ctrl-O', mac: 'Command-O|Ctrl-O'}
readOnly: true
exec: -> Backbone.Mediator.publish 'level:toggle-pathfinding', {}
addCommand
name: 'level-scrub-forward'
bindKey: {win: 'Ctrl-]', mac: 'Command-]|Ctrl-]'}
readOnly: true
exec: -> Backbone.Mediator.publish 'level:scrub-forward', {}
addCommand
name: 'level-scrub-back'
bindKey: {win: 'Ctrl-[', mac: 'Command-[|Ctrl-]'}
readOnly: true
exec: -> Backbone.Mediator.publish 'level:scrub-back', {}
addCommand
name: 'spell-step-forward'
bindKey: {win: 'Ctrl-Alt-]', mac: 'Command-Alt-]|Ctrl-Alt-]'}
readOnly: true
exec: -> Backbone.Mediator.publish 'tome:spell-step-forward', {}
addCommand
name: 'spell-step-backward'
bindKey: {win: 'Ctrl-Alt-[', mac: 'Command-Alt-[|Ctrl-Alt-]'}
readOnly: true
exec: -> Backbone.Mediator.publish 'tome:spell-step-backward', {}
addCommand
name: 'spell-beautify'
@ -815,10 +824,11 @@ module.exports = class SpellView extends CocoView
return if enabled is @controlsEnabled
@controlsEnabled = enabled and @writable
disabled = not enabled
$('body').focus() if disabled and $(document.activeElement).is('.ace_text-input')
wasFocused = @ace.isFocused()
@ace.setReadOnly disabled
@ace[if disabled then 'setStyle' else 'unsetStyle'] 'disabled'
@toggleBackground()
$('body').focus() if disabled and wasFocused
toggleBackground: =>
# TODO: make the background an actual background and do the CSS trick

View file

@ -67,7 +67,7 @@ class EarnedAchievementHandler extends Handler
if achievement.get('proportionalTo')
return @sendBadInputError(res, 'Cannot currently do this to repeatable docs...')
EarnedAchievement.createForAchievement(achievement, trigger, null, (earnedAchievementDoc) =>
@sendSuccess(res, earnedAchievementDoc.toObject())
@sendCreated(res, earnedAchievementDoc.toObject())
)
)

View file

@ -88,10 +88,13 @@ PaymentHandler = class PaymentHandler extends Handler
#- Check existence
transactionID = transaction.transaction_id
criteria = { recipient: req.user._id, 'ios.transactionID': transactionID }
criteria = { 'ios.transactionID': transactionID }
Payment.findOne(criteria).exec((err, payment) =>
if payment
unless payment.get('recipient').equals(req.user._id)
return @sendForbiddenError(res)
@recalculateGemsFor(req.user, (err) =>
return @sendDatabaseError(res, err) if err
@sendSuccess(res, @formatEntity(req, payment))

View file

@ -122,134 +122,133 @@ describe 'Achievement', ->
# TODO: Took level achievements out of this auto achievement business, so fix these tests
#describe 'Achieving Achievements', ->
# it 'wait for achievements to be loaded', (done) ->
# Achievement.loadAchievements (achievements) ->
# expect(Object.keys(achievements).length).toBe(1)
#
# loadedAchievements = Achievement.getLoadedAchievements()
# expect(Object.keys(loadedAchievements).length).toBe(1)
# done()
#
# it 'saving an object that should trigger an unlockable achievement', (done) ->
# unittest.getNormalJoe (joe) ->
# session = new LevelSession
# permissions: simplePermissions
# creator: joe._id
# level: original: 'dungeon-arena'
# session.save (err, doc) ->
# expect(err).toBeNull()
# expect(doc).toBeDefined()
# expect(doc.creator).toBe(session.creator)
# done()
#
# it 'verify that an unlockable achievement has been earned', (done) ->
# unittest.getNormalJoe (joe) ->
# EarnedAchievement.find {}, (err, docs) ->
# expect(err).toBeNull()
# expect(docs.length).toBe(1)
# achievement = docs[0]
# expect(achievement).toBeDefined()
#
# expect(achievement.get 'achievement').toBe unlockable._id
# expect(achievement.get 'user').toBe joe._id.toHexString()
# expect(achievement.get 'notified').toBeFalsy()
# expect(achievement.get 'earnedPoints').toBe unlockable.worth
# expect(achievement.get 'achievedAmount').toBeUndefined()
# expect(achievement.get 'previouslyAchievedAmount').toBeUndefined()
# done()
#
# it 'saving an object that should trigger a repeatable achievement', (done) ->
# unittest.getNormalJoe (joe) ->
# expect(joe.get 'simulatedBy').toBeFalsy()
# joe.set('simulatedBy', 2)
# joe.save (err, doc) ->
# expect(err).toBeNull()
# done()
#
# it 'verify that a repeatable achievement has been earned', (done) ->
# unittest.getNormalJoe (joe) ->
# EarnedAchievement.find {achievementName: repeatable.name}, (err, docs) ->
# expect(err).toBeNull()
# expect(docs.length).toBe(1)
# achievement = docs[0]
#
# expect(achievement.get 'achievement').toBe repeatable._id
# expect(achievement.get 'user').toBe joe._id.toHexString()
# expect(achievement.get 'notified').toBeFalsy()
# expect(achievement.get 'earnedPoints').toBe 2 * repeatable.worth
# expect(achievement.get 'achievedAmount').toBe 2
# expect(achievement.get 'previouslyAchievedAmount').toBeFalsy()
# done()
#
#
# it 'verify that the repeatable achievement with complex exp has been earned', (done) ->
# unittest.getNormalJoe (joe) ->
# EarnedAchievement.find {achievementName: diminishing.name}, (err, docs) ->
# expect(err).toBeNull()
# expect(docs.length).toBe 1
# achievement = docs[0]
#
# expect(achievement.get 'achievedAmount').toBe 2
# expect(achievement.get 'earnedPoints').toBe (Math.log(.5 * (2 + .5)) + 1) * diminishing.worth
#
# done()
describe 'Level Session Achievement', ->
it 'does not generate earned achievements automatically, they need to be created manually', (done) ->
unittest.getNormalJoe (joe) ->
session = new LevelSession
permissions: simplePermissions
creator: joe._id
level: original: 'dungeon-arena'
session.save (err, session) ->
expect(err).toBeNull()
expect(session).toBeDefined()
expect(session.creator).toBe(session.creator)
EarnedAchievement.find {}, (err, earnedAchievements) ->
expect(err).toBeNull()
expect(earnedAchievements.length).toBe(0)
json = {achievement: unlockable._id, triggeredBy: session._id, collection: 'level.sessions'}
request.post {uri: getURL('/db/earned_achievement'), json: json}, (err, res, body) ->
expect(res.statusCode).toBe(201)
expect(body.achievement).toBe unlockable._id+''
expect(body.user).toBe joe._id.toHexString()
expect(body.notified).toBeFalsy()
expect(body.earnedPoints).toBe unlockable.worth
expect(body.achievedAmount).toBeUndefined()
expect(body.previouslyAchievedAmount).toBeUndefined()
done()
#describe 'Recalculate Achievements', ->
# EarnedAchievementHandler = require '../../../server/achievements/earned_achievement_handler'
#
# it 'remove earned achievements', (done) ->
# clearModels [EarnedAchievement], (err) ->
# expect(err).toBeNull()
# EarnedAchievement.find {}, (err, earned) ->
# expect(earned.length).toBe 0
#
# User.update {}, {$set: {points: 0}}, {multi:true}, (err) ->
# expect(err).toBeNull()
# done()
#
# it 'can not be accessed by regular users', (done) ->
# loginJoe -> request.post {uri:getURL '/admin/earned_achievement/recalculate'}, (err, res, body) ->
# expect(res.statusCode).toBe 403
# done()
#
# it 'can recalculate a selection of achievements', (done) ->
# loginAdmin ->
# EarnedAchievementHandler.constructor.recalculate ['dungeon-arena-started'], ->
# EarnedAchievement.find {}, (err, earnedAchievements) ->
# expect(earnedAchievements.length).toBe 1
#
# # Recalculate again, doesn't change a thing
# EarnedAchievementHandler.constructor.recalculate ['dungeon-arena-started'], ->
# EarnedAchievement.find {}, (err, earnedAchievements) ->
# expect(earnedAchievements.length).toBe 1
#
# unittest.getNormalJoe (joe) ->
# User.findById joe.get('id'), (err, guy) ->
# expect(err).toBeNull()
# expect(guy.get 'points').toBe unlockable.worth
# done()
#
# it 'can recalculate all achievements', (done) ->
# loginAdmin ->
# Achievement.count {}, (err, count) ->
# expect(count).toBe 3
# EarnedAchievementHandler.constructor.recalculate ->
# EarnedAchievement.find {}, (err, earnedAchievements) ->
# expect(earnedAchievements.length).toBe 3
# unittest.getNormalJoe (joe) ->
# User.findById joe.get('id'), (err, guy) ->
# expect(err).toBeNull()
# expect(guy.get 'points').toBe unlockable.worth + 2 * repeatable.worth + (Math.log(.5 * (2 + .5)) + 1) * diminishing.worth
# done()
#
# it 'cleaning up test: deleting all Achievements and related', (done) ->
# clearModels [Achievement, EarnedAchievement, LevelSession], (err) ->
# expect(err).toBeNull()
#
# # reset achievements in memory as well
# Achievement.resetAchievements()
# loadedAchievements = Achievement.getLoadedAchievements()
# expect(Object.keys(loadedAchievements).length).toBe(0)
#
# done()
describe 'Achieving Achievements', ->
it 'wait for achievements to be loaded', (done) ->
Achievement.loadAchievements (achievements) ->
expect(Object.keys(achievements).length).toBe(1)
loadedAchievements = Achievement.getLoadedAchievements()
expect(Object.keys(loadedAchievements).length).toBe(1)
done()
it 'saving an object that should trigger a repeatable achievement', (done) ->
unittest.getNormalJoe (joe) ->
expect(joe.get 'simulatedBy').toBeFalsy()
joe.set('simulatedBy', 2)
joe.save (err, doc) ->
expect(err).toBeNull()
done()
it 'verify that a repeatable achievement has been earned', (done) ->
unittest.getNormalJoe (joe) ->
EarnedAchievement.find {achievementName: repeatable.name}, (err, docs) ->
expect(err).toBeNull()
expect(docs.length).toBe(1)
achievement = docs[0]
expect(achievement.get 'achievement').toBe repeatable._id
expect(achievement.get 'user').toBe joe._id.toHexString()
expect(achievement.get 'notified').toBeFalsy()
expect(achievement.get 'earnedPoints').toBe 2 * repeatable.worth
expect(achievement.get 'achievedAmount').toBe 2
expect(achievement.get 'previouslyAchievedAmount').toBeFalsy()
done()
it 'verify that the repeatable achievement with complex exp has been earned', (done) ->
unittest.getNormalJoe (joe) ->
EarnedAchievement.find {achievementName: diminishing.name}, (err, docs) ->
expect(err).toBeNull()
expect(docs.length).toBe 1
achievement = docs[0]
expect(achievement.get 'achievedAmount').toBe 2
expect(achievement.get 'earnedPoints').toBe (Math.log(.5 * (2 + .5)) + 1) * diminishing.worth
done()
describe 'Recalculate Achievements', ->
EarnedAchievementHandler = require '../../../server/achievements/earned_achievement_handler'
it 'remove earned achievements', (done) ->
clearModels [EarnedAchievement], (err) ->
expect(err).toBeNull()
EarnedAchievement.find {}, (err, earned) ->
expect(earned.length).toBe 0
User.update {}, {$set: {points: 0}}, {multi:true}, (err) ->
expect(err).toBeNull()
done()
it 'can not be accessed by regular users', (done) ->
loginJoe -> request.post {uri:getURL '/admin/earned_achievement/recalculate'}, (err, res, body) ->
expect(res.statusCode).toBe 403
done()
it 'can recalculate a selection of achievements', (done) ->
loginAdmin ->
EarnedAchievementHandler.constructor.recalculate ['dungeon-arena-started'], ->
EarnedAchievement.find {}, (err, earnedAchievements) ->
expect(earnedAchievements.length).toBe 1
# Recalculate again, doesn't change a thing
EarnedAchievementHandler.constructor.recalculate ['dungeon-arena-started'], ->
EarnedAchievement.find {}, (err, earnedAchievements) ->
expect(earnedAchievements.length).toBe 1
unittest.getNormalJoe (joe) ->
User.findById joe.get('id'), (err, guy) ->
expect(err).toBeNull()
expect(guy.get 'points').toBe unlockable.worth
done()
it 'can recalculate all achievements', (done) ->
loginAdmin ->
Achievement.count {}, (err, count) ->
expect(count).toBe 3
EarnedAchievementHandler.constructor.recalculate ->
EarnedAchievement.find {}, (err, earnedAchievements) ->
expect(earnedAchievements.length).toBe 3
unittest.getNormalJoe (joe) ->
User.findById joe.get('id'), (err, guy) ->
expect(err).toBeNull()
expect(guy.get 'points').toBe unlockable.worth + 2 * repeatable.worth + (Math.log(.5 * (2 + .5)) + 1) * diminishing.worth
done()
it 'cleaning up test: deleting all Achievements and related', (done) ->
clearModels [Achievement, EarnedAchievement, LevelSession], (err) ->
expect(err).toBeNull()
# reset achievements in memory as well
Achievement.resetAchievements()
loadedAchievements = Achievement.getLoadedAchievements()
expect(Object.keys(loadedAchievements).length).toBe(0)
done()

View file

@ -52,6 +52,12 @@ describe '/db/payment', ->
done()
)
it 'prevents other users from reusing payment receipts', (done) ->
loginSam ->
request.post {uri: paymentURL, json: firstApplePayment}, (err, res, body) ->
expect(res.statusCode).toBe 403
done()
it 'processes only the transactionID that is given', (done) ->
loginJoe ->
request.post {uri: paymentURL, json: secondApplePayment}, (err, res, body) ->