diff --git a/app/lib/LevelOptions.coffee b/app/lib/LevelOptions.coffee
index 4b82d8764..6e036418a 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,8 +72,10 @@ 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
+    hidesPlayButton: true
     hidesRunShortcut: true
     hidesHUD: true
     hidesSay: true
@@ -82,6 +85,7 @@ module.exports = LevelOptions =
     restrictedGear: {feet: 'leather-boots'}
   'the-raised-sword':
     disableSpaces: true
+    hidesPlayButton: true
     hidesRunShortcut: true
     hidesHUD: true
     hidesSay: true
@@ -97,6 +101,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 +110,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 +146,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 +155,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 +164,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 +172,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 +187,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/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
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/schemas/subscriptions/tome.coffee b/app/schemas/subscriptions/tome.coffee
index b4530a9a8..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.'},
@@ -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/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'}
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
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/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/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/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'
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/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/WorldMapView.coffee b/app/views/play/WorldMapView.coffee
index fc556515d..a6e45686c 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()
@@ -771,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'
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/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, '&lt;').replace(/>/g, '&gt;').replace(/\n/g, '<br>')
+      format = (s) -> marked(s.replace(/</g, '&lt;').replace(/>/g, '&gt;')) if s?
       context.message = format @problem.aetherProblem.message
       context.hint = format @problem.aetherProblem.hint
     context
diff --git a/app/views/play/level/tome/SpellView.coffee b/app/views/play/level/tome/SpellView.coffee
index 986eb749b..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?)
@@ -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']
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