From 2af827182904df6b1f0de3e5a746c523e8df258f Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Wed, 19 Nov 2014 09:01:01 -0800 Subject: [PATCH 01/11] Some size/styling tweaks for goals and gold. --- app/styles/play/level.sass | 7 +++++++ app/styles/play/level/goals.sass | 2 ++ app/styles/play/level/gold.sass | 15 ++++++++------- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/app/styles/play/level.sass b/app/styles/play/level.sass index cad311dad..c5145b627 100644 --- a/app/styles/play/level.sass +++ b/app/styles/play/level.sass @@ -43,6 +43,13 @@ $level-resize-transition-time: 0.5s visibility: hidden #gold-view right: 1% + @include box-shadow(-1px 1px 10px cyan) + .team-gold + font-size: 2vw + line-height: 2vw + img + width: 1.8vw + heighT: 1.8vw #control-bar-view .title left: 20% width: 60% diff --git a/app/styles/play/level/goals.sass b/app/styles/play/level/goals.sass index 310a1ee12..1d8e4a06f 100644 --- a/app/styles/play/level/goals.sass +++ b/app/styles/play/level/goals.sass @@ -12,11 +12,13 @@ padding: 19px 0px 2px 25px z-index: 3 font-size: 14px + min-width: 230px &.brighter font-size: 18px font-size: 1.4vw border-width: 0.91vw 1.22vw 3.10vw 0.91vw + min-width: 23vw .goals-status margin: 5px 0 0 0 diff --git a/app/styles/play/level/gold.sass b/app/styles/play/level/gold.sass index 370d9ec21..bc2331294 100644 --- a/app/styles/play/level/gold.sass +++ b/app/styles/play/level/gold.sass @@ -9,7 +9,7 @@ z-index: 6 @include transition(box-shadow .2s linear) @include user-select(none) - padding: 4px + padding: 0.4vw background: transparent url(/images/level/gold_background.png) no-repeat background-size: 100% 100% border-radius: 4px @@ -18,9 +18,9 @@ box-shadow: 2px 2px 2px black .team-gold - font-size: 18px + font-size: 1.4vw + line-height: 1.4vw margin: 0 - line-height: 20px color: hsla(205,0%,51%,1) display: inline-block padding: 0px 4px @@ -35,11 +35,12 @@ color: hsla(116,80%,51%,1) img - width: 16px - height: 16px + width: 1.2vw + height: 1.2vw border-radius: 2px - padding: 1px - margin-top: -1px + padding: 0.1vw + margin-top: -0.2vw + margin-right: 0.1vw .gold-amount display: inline-block From 63c516c5f510d594fb42cce1e09e7623ab7b77c8 Mon Sep 17 00:00:00 2001 From: Scott Erickson <sderickson@gmail.com> Date: Wed, 19 Nov 2014 10:05:02 -0800 Subject: [PATCH 02/11] Quick fix to get people to the forest levels even if, for some reason, the first forest level isn't in the User list of earned levels. --- app/views/play/WorldMapView.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/views/play/WorldMapView.coffee b/app/views/play/WorldMapView.coffee index fc556515d..7061f8e9f 100644 --- a/app/views/play/WorldMapView.coffee +++ b/app/views/play/WorldMapView.coffee @@ -102,7 +102,7 @@ module.exports = class WorldMapView extends RootView context.isIPadApp = application.isIPadApp context.mapType = _.string.slugify @terrain context.nextLevel = @nextLevel - context.forestIsAvailable = '541b67f71ccc8eaae19f3c62' in (me.get('earned')?.levels or []) + context.forestIsAvailable = @startedForestLevel or '541b67f71ccc8eaae19f3c62' in (me.get('earned')?.levels or []) context afterRender: -> @@ -131,8 +131,10 @@ module.exports = class WorldMapView extends RootView @openModalView authModal onSessionsLoaded: (e) -> + forestLevels = (f.id for f in forest) for session in @sessions.models @levelStatusMap[session.get('levelID')] = if session.get('state')?.complete then 'complete' else 'started' + @startedForestLevel = true if session.get('levelID') in forestLevels if @nextLevel and @levelStatusMap[@nextLevel] is 'complete' @nextLevel = null @render() From 0640f382ba52f412605685384fc5cf54ed915e6f Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Wed, 19 Nov 2014 13:23:55 -0800 Subject: [PATCH 03/11] Fixed coordinate hover cursors fighting flag cursors. Added suspect code check functionality for scripts to slap hands. --- app/lib/LevelOptions.coffee | 12 ++++++++++ app/lib/surface/CoordinateDisplay.coffee | 6 ++++- app/schemas/subscriptions/tome.coffee | 4 ++++ app/views/game-menu/InventoryModal.coffee | 2 +- app/views/play/level/tome/SpellView.coffee | 26 +++++++++++++--------- 5 files changed, 38 insertions(+), 12 deletions(-) diff --git a/app/lib/LevelOptions.coffee b/app/lib/LevelOptions.coffee index 4b82d8764..df0cfa2f6 100644 --- a/app/lib/LevelOptions.coffee +++ b/app/lib/LevelOptions.coffee @@ -10,6 +10,7 @@ module.exports = LevelOptions = hidesRealTimePlayback: true requiredGear: {feet: 'simple-boots'} restrictedGear: {feet: 'leather-boots'} + requiredCode: ['moveRight'] 'gems-in-the-deep': disableSpaces: true hidesSubmitUntilRun: true @@ -71,6 +72,7 @@ module.exports = LevelOptions = hidesRealTimePlayback: true requiredGear: {feet: 'simple-boots', 'right-hand': 'simple-sword', waist: 'leather-belt'} restrictedGear: {feet: 'leather-boots'} + requiredCode: ['Brak'] 'favorable-odds': disableSpaces: true hidesRunShortcut: true @@ -97,6 +99,7 @@ module.exports = LevelOptions = hidesRealTimePlayback: true requiredGear: {feet: 'simple-boots', 'programming-book': 'programmaticon-i'} restrictedGear: {feet: 'leather-boots'} + requiredCode: ['loop'] 'haunted-kithmaze': hidesRunShortcut: true hidesHUD: true @@ -105,6 +108,7 @@ module.exports = LevelOptions = hidesRealTimePlayback: true requiredGear: {feet: 'simple-boots', 'programming-book': 'programmaticon-i'} restrictedGear: {feet: 'leather-boots'} + requiredCode: ['loop'] 'descending-further': hidesHUD: true hidesSay: true @@ -140,6 +144,8 @@ module.exports = LevelOptions = hidesRealTimePlayback: true requiredGear: {feet: 'simple-boots', 'right-hand': 'simple-sword', 'programming-book': 'programmaticon-i', eyes: 'crude-glasses', torso: 'leather-tunic'} restrictedGear: {feet: 'leather-boots'} + requiredCode: ['findNearestEnemy'] + suspectCode: [{name: 'lone-find-nearest-enemy', pattern: /^[ ]*(self|this|@)?[:.]?findNearestEnemy()/m}] 'lowly-kithmen': hidesHUD: true hidesSay: true @@ -147,6 +153,8 @@ module.exports = LevelOptions = hidesRealTimePlayback: true requiredGear: {feet: 'simple-boots', 'right-hand': 'simple-sword', 'programming-book': 'programmaticon-i', eyes: 'crude-glasses', torso: 'leather-tunic'} restrictedGear: {feet: 'leather-boots'} + requiredCode: ['findNearestEnemy'] + suspectCode: [{name: 'lone-find-nearest-enemy', pattern: /^[ ]*(self|this|@)?[:.]?findNearestEnemy()/m}] 'closing-the-distance': hidesHUD: true hidesSay: true @@ -154,6 +162,7 @@ module.exports = LevelOptions = hidesRealTimePlayback: true requiredGear: {feet: 'simple-boots', 'right-hand': 'simple-sword', torso: 'leather-tunic', eyes: 'crude-glasses'} restrictedGear: {feet: 'leather-boots'} + suspectCode: [{name: 'lone-find-nearest-enemy', pattern: /^[ ]*(self|this|@)?[:.]?findNearestEnemy()/m}] 'tactical-strike': hidesHUD: true hidesSay: true @@ -161,12 +170,14 @@ module.exports = LevelOptions = hidesRealTimePlayback: true requiredGear: {feet: 'simple-boots', 'right-hand': 'simple-sword', torso: 'leather-tunic', eyes: 'crude-glasses'} restrictedGear: {feet: 'leather-boots'} + suspectCode: [{name: 'lone-find-nearest-enemy', pattern: /^[ ]*(self|this|@)?[:.]?findNearestEnemy()/m}] 'the-final-kithmaze': hidesHUD: true hidesSay: true hidesCodeToolbar: true hidesRealTimePlayback: true requiredGear: {feet: 'simple-boots', 'right-hand': 'simple-sword', torso: 'leather-tunic', 'programming-book': 'programmaticon-i', eyes: 'crude-glasses'} + suspectCode: [{name: 'lone-find-nearest-enemy', pattern: /^[ ]*(self|this|@)?[:.]?findNearestEnemy()/m}] 'the-gauntlet': hidesHUD: true hidesSay: true @@ -174,6 +185,7 @@ module.exports = LevelOptions = hidesRealTimePlayback: true requiredGear: {feet: 'simple-boots', 'right-hand': 'simple-sword', torso: 'leather-tunic', 'programming-book': 'programmaticon-i', eyes: 'crude-glasses'} restrictedGear: {feet: 'leather-boots'} + suspectCode: [{name: 'lone-find-nearest-enemy', pattern: /^[ ]*(self|this|@)?[:.]?findNearestEnemy()/m}] 'kithgard-gates': hidesSay: true hidesCodeToolbar: true diff --git a/app/lib/surface/CoordinateDisplay.coffee b/app/lib/surface/CoordinateDisplay.coffee index e9928ed60..5f4684884 100644 --- a/app/lib/surface/CoordinateDisplay.coffee +++ b/app/lib/surface/CoordinateDisplay.coffee @@ -6,6 +6,7 @@ module.exports = class CoordinateDisplay extends createjs.Container 'surface:mouse-over': 'onMouseOver' 'surface:stage-mouse-down': 'onMouseDown' 'camera:zoom-updated': 'onZoomUpdated' + 'level:flag-color-selected': 'onFlagColorSelected' constructor: (options) -> super() @@ -60,6 +61,9 @@ module.exports = class CoordinateDisplay extends createjs.Container @hide() @show() + onFlagColorSelected: (e) -> + @placingFlag = Boolean e.color + hide: -> return unless @label.parent @removeChild @label @@ -154,6 +158,6 @@ module.exports = class CoordinateDisplay extends createjs.Container @y = sup.y @addChild @background @addChild @label - @addChild @pointMarker + @addChild @pointMarker unless @placingFlag @updateCache() Backbone.Mediator.publish 'surface:coordinates-shown', {} diff --git a/app/schemas/subscriptions/tome.coffee b/app/schemas/subscriptions/tome.coffee index b4530a9a8..26712d1e1 100644 --- a/app/schemas/subscriptions/tome.coffee +++ b/app/schemas/subscriptions/tome.coffee @@ -122,6 +122,10 @@ module.exports = 'tome:required-code-fragment-deleted': c.object {title: 'Required Code Fragment Deleted', description: 'Published when a required code fragment is deleted from the sample code.', required: ['codeFragment']}, codeFragment: {type: 'string'} + 'tome:suspect-code-fragment-added': c.object {title: 'Suspect Code Fragment Added', description: 'Published when a suspect code fragment is added to the sample code.', required: ['codeFragment']}, + codeFragment: {type: 'string'} + codeLanguage: {type: 'string'} + 'tome:winnability-updated': c.object {title: 'Winnability Updated', description: 'When we think we can now win (or can no longer win), we may want to emphasize the submit button versus the run button (or vice versa), so this fires when we get new goal states (even preloaded goal states) suggesting success or failure change.', required: ['winnable']}, winnable: {type: 'boolean'} diff --git a/app/views/game-menu/InventoryModal.coffee b/app/views/game-menu/InventoryModal.coffee index 4de79d40d..d83089468 100644 --- a/app/views/game-menu/InventoryModal.coffee +++ b/app/views/game-menu/InventoryModal.coffee @@ -86,7 +86,7 @@ module.exports = class InventoryModal extends ModalView locked = not (item.get('original') in me.items()) locked = false if me.get('slug') is 'nick' - if not item.getFrontFacingStats().props.length and not _.size item.getFrontFacingStats().stats # Temp: while there are placeholder items + if not item.getFrontFacingStats().props.length and not _.size(item.getFrontFacingStats().stats) and not locked # Temp: while there are placeholder items null # Don't put into a collection else if locked and item.get('slug') isnt 'simple-boots' @itemGroups.lockedItems.add(item) diff --git a/app/views/play/level/tome/SpellView.coffee b/app/views/play/level/tome/SpellView.coffee index 986eb749b..209ac6f36 100644 --- a/app/views/play/level/tome/SpellView.coffee +++ b/app/views/play/level/tome/SpellView.coffee @@ -450,7 +450,8 @@ module.exports = class SpellView extends CocoView _.throttle @updateLines, 500 _.throttle @hideProblemAlert, 500 ] - onSignificantChange.push _.debounce @checkRequiredCode, 1500 if requiredCodePerLevel[@options.level.get('slug')] + onSignificantChange.push _.debounce @checkRequiredCode, 750 if LevelOptions[@options.level.get('slug')]?.requiredCode + onSignificantChange.push _.debounce @checkSuspectCode, 750 if LevelOptions[@options.level.get('slug')]?.suspectCode @onCodeChangeMetaHandler = => return if @eventsSuppressed Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'code-change', volume: 0.5 @@ -881,13 +882,26 @@ module.exports = class SpellView extends CocoView checkRequiredCode: => return if @destroyed source = @getSource().replace @singleLineCommentRegex(), '' - for requiredCodeFragment in requiredCodePerLevel[@options.level.get('slug')] + requiredCodeFragments = LevelOptions[@options.level.get('slug')].requiredCode + for requiredCodeFragment in requiredCodeFragments + # Could make this obey regular expressions like suspectCode if needed if source.indexOf(requiredCodeFragment) is -1 @warnedCodeFragments ?= {} unless @warnedCodeFragments[requiredCodeFragment] Backbone.Mediator.publish 'tome:required-code-fragment-deleted', codeFragment: requiredCodeFragment @warnedCodeFragments[requiredCodeFragment] = true + checkSuspectCode: => + return if @destroyed + source = @getSource().replace @singleLineCommentRegex(), '' + suspectCodeFragments = LevelOptions[@options.level.get('slug')].suspectCode + for suspectCodeFragment in suspectCodeFragments + if suspectCodeFragment.pattern.test source + @warnedCodeFragments ?= {} + unless @warnedCodeFragments[suspectCodeFragment.name] + Backbone.Mediator.publish 'tome:suspect-code-fragment-added', codeFragment: suspectCodeFragment.name, codeLanguage: @spell.language + @warnedCodeFragments[suspectCodeFragment] = true + destroy: -> $(@ace?.container).find('.ace_gutter').off 'click', '.ace_error, .ace_warning, .ace_info', @onAnnotationClick $(@ace?.container).find('.ace_gutter').off 'click', @onGutterClick @@ -900,11 +914,3 @@ module.exports = class SpellView extends CocoView @debugView?.destroy() $(window).off 'resize', @onWindowResize super() - - -requiredCodePerLevel = - 'dungeons-of-kithgard': ['moveRight'] - 'true-names': ['Brak'] - 'the-first-kithmaze': ['loop'] - 'haunted-kithmaze': ['loop'] - 'lowly-kithmen': ['findNearestEnemy'] From 5a71d93d8a5e1953db27a54d9ad0db10a851b6f3 Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Wed, 19 Nov 2014 13:25:09 -0800 Subject: [PATCH 04/11] Hid play button for a couple more levels. --- app/lib/LevelOptions.coffee | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/lib/LevelOptions.coffee b/app/lib/LevelOptions.coffee index df0cfa2f6..6e036418a 100644 --- a/app/lib/LevelOptions.coffee +++ b/app/lib/LevelOptions.coffee @@ -75,6 +75,7 @@ module.exports = LevelOptions = requiredCode: ['Brak'] 'favorable-odds': disableSpaces: true + hidesPlayButton: true hidesRunShortcut: true hidesHUD: true hidesSay: true @@ -84,6 +85,7 @@ module.exports = LevelOptions = restrictedGear: {feet: 'leather-boots'} 'the-raised-sword': disableSpaces: true + hidesPlayButton: true hidesRunShortcut: true hidesHUD: true hidesSay: true From fe018309d0b81afcb4ef7db2c8d1fb96714b6aab Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Wed, 19 Nov 2014 13:47:44 -0800 Subject: [PATCH 05/11] Don't show blurbs for aggro messages. Big announces action blurbs for heroes. --- app/lib/surface/Lank.coffee | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/lib/surface/Lank.coffee b/app/lib/surface/Lank.coffee index 731119d1c..4418ffbea 100644 --- a/app/lib/surface/Lank.coffee +++ b/app/lib/surface/Lank.coffee @@ -664,7 +664,9 @@ module.exports = Lank = class Lank extends CocoClass updateLabels: -> return unless @thang blurb = if @thang.health <= 0 then null else @thang.sayMessage # Dead men tell no tales - @addLabel 'say', Label.STYLE_SAY if blurb + blurb = null if blurb in ['For Thoktar!', 'Bones!', 'Behead!', 'Destroy!', 'Die, humans!'] # Let's just hear, not see, these ones. + labelStyle = if /Hero Placeholder/.test(@thang.id) then Label.STYLE_DIALOGUE else Label.STYLE_SAY + @addLabel 'say', labelStyle if blurb if @labels.say?.setText blurb @notifySpeechUpdated blurb: blurb label.update() for name, label of @labels From 13de055c0b306c04e7e8f63c277f8f92a60b7a93 Mon Sep 17 00:00:00 2001 From: Scott Erickson <sderickson@gmail.com> Date: Wed, 19 Nov 2014 14:55:01 -0800 Subject: [PATCH 06/11] Set up a basic achievements list modal for the world map. --- app/models/CocoModel.coffee | 2 + .../play/modal/play-achievements-modal.sass | 82 +++++++++++++++++- .../play/modal/play-achievements-modal.jade | 27 +++++- app/templates/play/world-map-view.jade | 2 +- .../play/modal/PlayAchievementsModal.coffee | 83 +++++++++++++++++-- server/achievements/EarnedAchievement.coffee | 6 -- .../earned_achievement_handler.coffee | 21 ++++- server/commons/Handler.coffee | 13 ++- 8 files changed, 215 insertions(+), 21 deletions(-) diff --git a/app/models/CocoModel.coffee b/app/models/CocoModel.coffee index 4aeff828f..c6238d86b 100644 --- a/app/models/CocoModel.coffee +++ b/app/models/CocoModel.coffee @@ -50,6 +50,8 @@ class CocoModel extends Backbone.Model @loading = false @jqxhr = null @loadFromBackup() + + getCreationDate: -> new Date(parseInt(@id.slice(0,8), 16)*1000) getNormalizedURL: -> "#{@urlRoot}/#{@id}" diff --git a/app/styles/play/modal/play-achievements-modal.sass b/app/styles/play/modal/play-achievements-modal.sass index 5f3652962..db553646e 100644 --- a/app/styles/play/modal/play-achievements-modal.sass +++ b/app/styles/play/modal/play-achievements-modal.sass @@ -1,4 +1,82 @@ #play-achievements-modal - .achievement-view - color: black + + .modal-header + padding-bottom: 20px + img.icon + float: left + width: 40px + margin-right: 10px + + + //- Unachieved Panels + + .panel + margin: 5px 0 + position: relative + border: 2px solid rgb(75,75,75) + padding: 2px + + h3 + margin: 0 0 0 50px + color: rgb(75,75,75) + + p + margin: 0 0 0 50px + color: rgb(75,75,75) + + .panel-body + padding: 5px 150px 5px 5px + border: 2px solid rgb(150,150,150) + + .created + position: absolute + right: 10px + top: 5px + color: rgb(75,75,75) + font-size: 12px + + + //- Achieved Panels + + .panel.earned + background: rgb(50,40,33) + border: 5px solid rgb(26,21,17) + padding: 0 + + h3 + color: white + + p + color: rgb(203,170,148) + + .panel-body + border: 2px solid rgb(75,62,51) + + .created + color: rgb(255,189,68) + + + //- Rewards + + .rewards + position: absolute + right: .2em + //bottom: 10px + top: 29px + + .label + font-size: 18px + margin-left: 5px + color: rgb(50,40,33) + background: rgb(203,170,148) + + .gems + background: #94ccc7 + + .worth + background: #d8c488 + + img + width: 12px + height: 12px \ No newline at end of file diff --git a/app/templates/play/modal/play-achievements-modal.jade b/app/templates/play/modal/play-achievements-modal.jade index 13d244310..b71a88d9d 100644 --- a/app/templates/play/modal/play-achievements-modal.jade +++ b/app/templates/play/modal/play-achievements-modal.jade @@ -4,4 +4,29 @@ block modal-header-content h3(data-i18n="play.achievements") Achievements block modal-body-content - p TODO: show all dem achievements + for achievement in achievements + .panel(class=achievement.earned ? 'earned' : '') + .panel-body + img.icon(src=achievement.getImageURL()) + h3= achievement.name + p= achievement.description + + if achievement.earnedDate + .created=moment(achievement.earnedDate).fromNow() + else + .created(data-i18n="user.status_unfinished") + + .rewards + - rewards = achievement.get('rewards') + - rewards = { gems: 100 } + if rewards && rewards.gems + span.gems.label.label-default + span= rewards.gems + img.gem(src="/images/common/gem.png") + + - worth = achievement.get('worth') + if worth + span.worth.label.label-default + span #{worth}xp + // maybe add more icons/numbers for items, heroes, levels, xp? +block modal-footer \ No newline at end of file diff --git a/app/templates/play/world-map-view.jade b/app/templates/play/world-map-view.jade index b56457b49..fc43175f7 100644 --- a/app/templates/play/world-map-view.jade +++ b/app/templates/play/world-map-view.jade @@ -43,10 +43,10 @@ .game-controls.header-font button.btn.items(data-toggle='coco-modal', data-target='play/modal/PlayItemsModal', data-i18n="[title]play.items") button.btn.heroes(data-toggle='coco-modal', data-target='play/modal/PlayHeroesModal', data-i18n="[title]play.heroes") + button.btn.achievements(data-toggle='coco-modal', data-target='play/modal/PlayAchievementsModal', data-i18n="[title]play.achievements") if me.get('anonymous') === false button.btn.gems(data-toggle='coco-modal', data-target='play/modal/BuyGemsModal', data-i18n="[title]play.buy_gems") if me.isAdmin() - button.btn.achievements(data-toggle='coco-modal', data-target='play/modal/PlayAchievementsModal', data-i18n="[title]play.achievements") button.btn.account(data-toggle='coco-modal', data-target='play/modal/PlayAccountModal', data-i18n="[title]play.account") button.btn.settings(data-toggle='coco-modal', data-target='play/modal/PlaySettingsModal', data-i18n="[title]play.settings") else if me.get('anonymous', true) diff --git a/app/views/play/modal/PlayAchievementsModal.coffee b/app/views/play/modal/PlayAchievementsModal.coffee index ca0940b88..360330685 100644 --- a/app/views/play/modal/PlayAchievementsModal.coffee +++ b/app/views/play/modal/PlayAchievementsModal.coffee @@ -2,27 +2,92 @@ ModalView = require 'views/kinds/ModalView' template = require 'templates/play/modal/play-achievements-modal' CocoCollection = require 'collections/CocoCollection' Achievement = require 'models/Achievement' -#AchievementView = require 'views/game-menu/AchievementView' +EarnedAchievement = require 'models/EarnedAchievement' + +utils = require 'lib/utils' + +PAGE_SIZE = 200 module.exports = class PlayAchievementsModal extends ModalView className: 'modal fade play-modal' template: template modalWidthPercent: 90 id: 'play-achievements-modal' - #instant: true - - #events: - # 'change input.select': 'onSelectionChanged' + plain: true + + earnedMap: {} constructor: (options) -> super options - #@achievements = new CocoCollection([], {model: Achievement}) - #@achievements.url = '/db/thang.type?view=achievements&project=name,description,components,original,rasterIcon' - #@supermodel.loadCollection(@achievements, 'achievements') + @achievements = new Backbone.Collection() + earnedMap = {} + + achievementsFetcher = new CocoCollection([], {url: '/db/achievement', model: Achievement}) + achievementsFetcher.setProjection([ + 'name' + 'description' + 'icon' + 'worth' + 'i18n' + 'rewards' + 'collection' + ]) + + earnedAchievementsFetcher = new CocoCollection([], {url: '/db/earned_achievement', model: EarnedAchievement}) + earnedAchievementsFetcher.setProjection([ 'achievement' ]) + + achievementsFetcher.skip = 0 + achievementsFetcher.fetch({data: {skip: 0, limit: PAGE_SIZE}}) + earnedAchievementsFetcher.skip = 0 + earnedAchievementsFetcher.fetch({data: {skip: 0, limit: PAGE_SIZE}}) + + @listenTo achievementsFetcher, 'sync', @onAchievementsLoaded + @listenTo earnedAchievementsFetcher, 'sync', @onEarnedAchievementsLoaded + + @supermodel.loadCollection(achievementsFetcher, 'achievement') + @supermodel.loadCollection(earnedAchievementsFetcher, 'achievement') + + @onEverythingLoaded = _.after(2, @onEverythingLoaded) + + onAchievementsLoaded: (fetcher) -> + needMore = fetcher.models.length is PAGE_SIZE + @achievements.add(fetcher.models) + if needMore + fetcher.skip += PAGE_SIZE + fetcher.fetch({data: {skip: fetcher.skip, limit: PAGE_SIZE}}) + else + @stopListening(fetcher) + @onEverythingLoaded() + + onEarnedAchievementsLoaded: (fetcher) -> + needMore = fetcher.models.length is PAGE_SIZE + for earned in fetcher.models + @earnedMap[earned.get('achievement')] = earned + if needMore + fetcher.skip += PAGE_SIZE + fetcher.fetch({data: {skip: fetcher.skip, limit: PAGE_SIZE}}) + else + @stopListening(fetcher) + @onEverythingLoaded() + + onEverythingLoaded: => + @achievements.set(@achievements.filter((m) -> m.get('collection') isnt 'level.sessions')) + for achievement in @achievements.models + if earned = @earnedMap[achievement.id] + achievement.earned = earned + achievement.earnedDate = earned.getCreationDate() + achievement.earnedDate ?= '' + @achievements.comparator = (m) -> m.earnedDate + @achievements.sort() + @achievements.set(@achievements.models.reverse()) + for achievement in @achievements.models + achievement.name = utils.i18n achievement.attributes, 'name' + achievement.description = utils.i18n achievement.attributes, 'description' + @render() getRenderData: (context={}) -> context = super(context) - #context.achievements = @achievements.models + context.achievements = @achievements.models context afterRender: -> diff --git a/server/achievements/EarnedAchievement.coffee b/server/achievements/EarnedAchievement.coffee index 685a502c6..80d6f6249 100644 --- a/server/achievements/EarnedAchievement.coffee +++ b/server/achievements/EarnedAchievement.coffee @@ -2,12 +2,6 @@ mongoose = require 'mongoose' jsonschema = require '../../app/schemas/models/earned_achievement' EarnedAchievementSchema = new mongoose.Schema({ - created: - type: Date - default: Date.now - changed: - type: Date - default: Date.now notified: type: Boolean default: false diff --git a/server/achievements/earned_achievement_handler.coffee b/server/achievements/earned_achievement_handler.coffee index e39e1dd4c..7eb2c7437 100644 --- a/server/achievements/earned_achievement_handler.coffee +++ b/server/achievements/earned_achievement_handler.coffee @@ -16,7 +16,26 @@ class EarnedAchievementHandler extends Handler get: (req, res) -> return @getByAchievementIDs(req, res) if req.query.view is 'get-by-achievement-ids' - super(arguments...) + query = { user: req.user._id+''} + + projection = {} + if req.query.project + projection[field] = 1 for field in req.query.project.split(',') + + q = EarnedAchievement.find(query, projection) + + skip = parseInt(req.query.skip) + if skip? and skip < 1000000 + q.skip(skip) + + limit = parseInt(req.query.limit) + if limit? and limit < 1000 + q.limit(limit) + + q.exec (err, documents) => + return @sendDatabaseError(res, err) if err + documents = (@formatEntity(req, doc) for doc in documents) + @sendSuccess(res, documents) getByAchievementIDs: (req, res) -> query = { user: req.user._id+''} diff --git a/server/commons/Handler.coffee b/server/commons/Handler.coffee index 15ecc3b11..b9a5d6f2e 100644 --- a/server/commons/Handler.coffee +++ b/server/commons/Handler.coffee @@ -129,6 +129,10 @@ module.exports = class Handler term = req.query.term matchedObjects = [] filters = if @modelClass.schema.uses_coco_versions or @modelClass.schema.uses_coco_permissions then [filter: {index: true}] else [filter: {}] + + skip = parseInt(req.query.skip) + limit = parseInt(req.query.limit) + if @modelClass.schema.uses_coco_permissions and req.user filters.push {filter: {index: req.user.get('id')}} projection = null @@ -158,7 +162,14 @@ module.exports = class Handler else args = [filter.filter] args.push projection if projection - @modelClass.find(args...).limit(FETCH_LIMIT).exec callback + q = @modelClass.find(args...) + if skip? and skip < 1000000 + q.skip(skip) + if limit? and limit < FETCH_LIMIT + q.limit(limit) + else + q.limit(FETCH_LIMIT) + q.exec callback # if it's not a text search but the user is an admin, let him try stuff anyway else if req.user?.isAdmin() # admins can send any sort of query down the wire From 07a09e34d87565cfaf8b92fa96c304680812b436 Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Wed, 19 Nov 2014 15:01:20 -0800 Subject: [PATCH 07/11] Fixed small-caps font-variant on docs popover titles. --- app/styles/play/level/tome/spell_palette_entry.sass | 1 + app/templates/play/level/tome/spell_palette_entry_popover.jade | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/styles/play/level/tome/spell_palette_entry.sass b/app/styles/play/level/tome/spell_palette_entry.sass index cb37378b5..b88b6bfb2 100644 --- a/app/styles/play/level/tome/spell_palette_entry.sass +++ b/app/styles/play/level/tome/spell_palette_entry.sass @@ -80,6 +80,7 @@ h1:not(.not-code), h2:not(.not-code), h3:not(.not-code), h4:not(.not-code), h5:not(.not-code), h6:not(.not-code) font-family: Menlo, Monaco, Consolas, "Courier New", monospace + font-variant: normal .popover-title background-color: transparent diff --git a/app/templates/play/level/tome/spell_palette_entry_popover.jade b/app/templates/play/level/tome/spell_palette_entry_popover.jade index c13df8394..bd10fc3dd 100644 --- a/app/templates/play/level/tome/spell_palette_entry_popover.jade +++ b/app/templates/play/level/tome/spell_palette_entry_popover.jade @@ -1,5 +1,5 @@ h4 - span= doc.shortName + span.prop-name= doc.shortName | - code.prop-type= doc.type == 'function' && doc.owner == 'this' ? 'method' : doc.type if doc.type != 'function' From c82d1d0f362c77260a5d34b27e1b032ed8d9e123 Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Wed, 19 Nov 2014 15:13:45 -0800 Subject: [PATCH 08/11] Additional scriptable property on thang-died events. --- app/schemas/subscriptions/world.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/app/schemas/subscriptions/world.coffee b/app/schemas/subscriptions/world.coffee index bd0b8c2ce..a3da038c3 100644 --- a/app/schemas/subscriptions/world.coffee +++ b/app/schemas/subscriptions/world.coffee @@ -9,6 +9,7 @@ module.exports = thang: {type: 'object'} killer: {type: 'object'} killerHealth: {type: ['number', 'undefined']} + maxHealth: {type: 'number'} 'world:thang-touched-goal': c.object {required: ['actor', 'touched']}, replacedNoteChain: {type: 'array'} From 6755c90f022f2dd971398351e5a6b87c9f567a6b Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Wed, 19 Nov 2014 15:18:59 -0800 Subject: [PATCH 09/11] Properly continue to Haunted Kithmaze from The Raised Sword if that's the A/B test group. --- app/views/play/WorldMapView.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/app/views/play/WorldMapView.coffee b/app/views/play/WorldMapView.coffee index 7061f8e9f..a6e45686c 100644 --- a/app/views/play/WorldMapView.coffee +++ b/app/views/play/WorldMapView.coffee @@ -773,3 +773,4 @@ if me.getKithmazeGroup() is 'the-first-kithmaze' _.remove dungeon, id: 'haunted-kithmaze' else _.remove dungeon, id: 'the-first-kithmaze' + _.find(dungeon, id: 'the-raised-sword').nextLevels.continue = 'haunted-kithmaze' From f310719ceee00c095e727d659a7093bda4c0bc75 Mon Sep 17 00:00:00 2001 From: Nick Winter <livelily@gmail.com> Date: Wed, 19 Nov 2014 15:24:50 -0800 Subject: [PATCH 10/11] Restarting level now doesn't clear anything except code unless you hold down shift. --- app/schemas/subscriptions/tome.coffee | 2 +- app/views/play/level/modal/ReloadLevelModal.coffee | 8 +++++++- app/views/play/level/tome/SpellView.coffee | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/schemas/subscriptions/tome.coffee b/app/schemas/subscriptions/tome.coffee index 26712d1e1..2d5d4a966 100644 --- a/app/schemas/subscriptions/tome.coffee +++ b/app/schemas/subscriptions/tome.coffee @@ -35,7 +35,7 @@ module.exports = 'tome:toggle-spell-list': c.object {title: 'Toggle Spell List', description: 'Published when you toggle the dropdown for a thang\'s spells'} - 'tome:reload-code': c.object {title: 'Reload Code', description: 'Published when you reset a spell to its original source', required: ['spell']}, + 'tome:reload-code': c.object {title: 'Reload Code', description: 'Published when you reset a spell to its original source', required: []}, spell: {type: 'object'} 'tome:palette-cleared': c.object {title: 'Palette Cleared', description: 'Published when the spell palette is about to be cleared and recreated.'}, diff --git a/app/views/play/level/modal/ReloadLevelModal.coffee b/app/views/play/level/modal/ReloadLevelModal.coffee index 320ecde21..d11bb4239 100644 --- a/app/views/play/level/modal/ReloadLevelModal.coffee +++ b/app/views/play/level/modal/ReloadLevelModal.coffee @@ -6,4 +6,10 @@ module.exports = class ReloadLevelModal extends ModalView template: template events: - 'click #restart-level-confirm-button': -> Backbone.Mediator.publish 'level:restart', {} + 'click #restart-level-confirm-button': 'onClickRestart' + + onClickRestart: (e) -> + if key.shift + Backbone.Mediator.publish 'level:restart', {} + else + Backbone.Mediator.publish 'tome:reload-code', {} diff --git a/app/views/play/level/tome/SpellView.coffee b/app/views/play/level/tome/SpellView.coffee index 209ac6f36..c1429a400 100644 --- a/app/views/play/level/tome/SpellView.coffee +++ b/app/views/play/level/tome/SpellView.coffee @@ -394,7 +394,7 @@ module.exports = class SpellView extends CocoView @focus() if cast onCodeReload: (e) -> - return unless e.spell is @spell + return unless e.spell is @spell or not e.spell @reloadCode true @ace.clearSelection() _.delay (=> @ace?.clearSelection()), 500 # Make double sure this gets done (saw some timing issues?) From 41db27c709471785201d065bb9619fdbe884be4d Mon Sep 17 00:00:00 2001 From: Matt Lott <mattlott@live.com> Date: Wed, 19 Nov 2014 15:27:06 -0800 Subject: [PATCH 11/11] Apply markdown to error messages and hints --- app/templates/play/level/tome/problem_alert.jade | 1 - app/views/play/level/tome/ProblemAlertView.coffee | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/templates/play/level/tome/problem_alert.jade b/app/templates/play/level/tome/problem_alert.jade index cc80b0a53..6951145b8 100644 --- a/app/templates/play/level/tome/problem_alert.jade +++ b/app/templates/play/level/tome/problem_alert.jade @@ -3,7 +3,6 @@ h3.problem-alert-title(data-i18n="play_level.problem_alert_title") Fix Your Code if hint span.problem-title!= hint br - br span.problem-subtitle!= message else span.problem-title!= message diff --git a/app/views/play/level/tome/ProblemAlertView.coffee b/app/views/play/level/tome/ProblemAlertView.coffee index 907621365..24eb6b1c9 100644 --- a/app/views/play/level/tome/ProblemAlertView.coffee +++ b/app/views/play/level/tome/ProblemAlertView.coffee @@ -34,7 +34,7 @@ module.exports = class ProblemAlertView extends CocoView getRenderData: (context={}) -> context = super context if @problem? - format = (s) -> s?.replace(/</g, '<').replace(/>/g, '>').replace(/\n/g, '<br>') + format = (s) -> marked(s.replace(/</g, '<').replace(/>/g, '>')) if s? context.message = format @problem.aetherProblem.message context.hint = format @problem.aetherProblem.hint context