diff --git a/app/initialize.coffee b/app/initialize.coffee
index 11921b81b..fb744211a 100644
--- a/app/initialize.coffee
+++ b/app/initialize.coffee
@@ -12,6 +12,7 @@ channelSchemas =
   'tome': require './schemas/subscriptions/tome'
   'god': require './schemas/subscriptions/god'
   'scripts': require './schemas/subscriptions/scripts'
+  'world': require './schemas/subscriptions/world'
 
 definitionSchemas =
   'bus': require './schemas/definitions/bus'
diff --git a/app/lib/scripts/ScriptManager.coffee b/app/lib/scripts/ScriptManager.coffee
index 796551f58..7c05c58e6 100644
--- a/app/lib/scripts/ScriptManager.coffee
+++ b/app/lib/scripts/ScriptManager.coffee
@@ -271,7 +271,6 @@ module.exports = ScriptManager = class ScriptManager extends CocoClass
     @run()
 
   onEndNoteGroup: (e) ->
-    e?.preventDefault()
     # press enter
     return unless @currentNoteGroup?.script.skippable
     @endNoteGroup()
@@ -285,7 +284,7 @@ module.exports = ScriptManager = class ScriptManager extends CocoClass
     clearTimeout(timeout) for timeout in @currentTimeouts
     for module in @currentNoteGroup.modules
       @processNote(note, @currentNoteGroup) for note in module.endNotes()
-    Backbone.Mediator.publish 'script:note-group-ended' unless @quiet
+    Backbone.Mediator.publish 'script:note-group-ended', {} unless @quiet
     @scriptInProgress = false
     @trackScriptCompletionsFromNoteGroup(@currentNoteGroup)
     @currentNoteGroup = null
diff --git a/app/lib/surface/SpriteBoss.coffee b/app/lib/surface/SpriteBoss.coffee
index bedb45d6c..511c2830a 100644
--- a/app/lib/surface/SpriteBoss.coffee
+++ b/app/lib/surface/SpriteBoss.coffee
@@ -297,7 +297,7 @@ module.exports = class SpriteBoss extends CocoClass
   selectSprite: (e, sprite=null, spellName=null, treemaThangSelected = null) ->
     return if e and (@disabled or @selectLocked)  # Ignore clicks for selection/panning/wizard movement while disabled or select is locked
     worldPos = sprite?.thang?.pos
-    worldPos ?= @camera.screenToWorld {x: e.originalEvent.rawX, y: e.originalEvent.rawY} if e
+    worldPos ?= @camera.screenToWorld {x: e.originalEvent.rawX, y: e.originalEvent.rawY} if e?.originalEvent
     if worldPos and (@options.navigateToSelection or not sprite or treemaThangSelected) and e?.originalEvent?.nativeEvent?.which isnt 3
       @camera.zoomTo(sprite?.imageObject or @camera.worldToSurface(worldPos), @camera.zoom, 1000, true)
     sprite = null if @options.choosing  # Don't select sprites while choosing
diff --git a/app/schemas/subscriptions/god.coffee b/app/schemas/subscriptions/god.coffee
index cd164806f..6121b838d 100644
--- a/app/schemas/subscriptions/god.coffee
+++ b/app/schemas/subscriptions/god.coffee
@@ -50,4 +50,4 @@ module.exports =
 
   'god:debug-value-return': c.object {required: ['key']},
     key: {type: 'string'}
-    value: {type: ['any', 'undefined']}
+    value: {}
diff --git a/app/schemas/subscriptions/misc.coffee b/app/schemas/subscriptions/misc.coffee
index bb41e1e7a..0f9d9b0c3 100644
--- a/app/schemas/subscriptions/misc.coffee
+++ b/app/schemas/subscriptions/misc.coffee
@@ -27,3 +27,7 @@ module.exports =
 
   'achievements:new': c.object {required: 'earnedAchievements'},
     earnedAchievements: {type: 'object'}
+
+  'ladder:game-submitted': c.object {required: ['session', 'level']},
+    session: {type: 'object'}
+    level: {type: 'object'}
diff --git a/app/schemas/subscriptions/play.coffee b/app/schemas/subscriptions/play.coffee
index a19530bf3..608086eb1 100644
--- a/app/schemas/subscriptions/play.coffee
+++ b/app/schemas/subscriptions/play.coffee
@@ -107,7 +107,7 @@ module.exports =
 
   'level:highlight-dom': c.object {required: ['selector']},
     selector: {type: 'string'}
-    delay: {type: 'number'}
+    delay: {type: ['number', 'null', 'undefined']}
     sides: {type: 'array', items: {'enum': ['left', 'right', 'top', 'bottom']}}
     offset: {type: 'object'}
     rotation: {type: 'number'}
diff --git a/app/schemas/subscriptions/surface.coffee b/app/schemas/subscriptions/surface.coffee
index 418e8f784..7bc8cea8b 100644
--- a/app/schemas/subscriptions/surface.coffee
+++ b/app/schemas/subscriptions/surface.coffee
@@ -102,13 +102,14 @@ module.exports =  # /app/lib/surface
 
   'sprite:highlight-sprites': c.object {},
     thangIDs: c.array {}, {type: 'string'}
-    delay: {type: 'number'}
+    delay: {type: ['number', 'null', 'undefined']}
 
   'sprite:move': c.object {required: ['spriteID', 'pos']},
     spriteID: {type: 'string'}
     pos: c.object {required: ['x', 'y']},
       x: {type: 'number'}
       y: {type: 'number'}
+      z: {type: 'number'}
     duration: {type: 'number', minimum: 0}
 
   'sprite:mouse-down': spriteMouseEventSchema
diff --git a/app/schemas/subscriptions/world.coffee b/app/schemas/subscriptions/world.coffee
new file mode 100644
index 000000000..4b2a42248
--- /dev/null
+++ b/app/schemas/subscriptions/world.coffee
@@ -0,0 +1,30 @@
+c = require 'schemas/schemas'
+
+module.exports =
+  'world:won': c.object {},
+    replacedNoteChain: {type: 'array'}
+
+  'world:thang-died': c.object {required: ['thang', 'killer']},
+    replacedNoteChain: {type: 'array'}
+    thang: {type: 'object'}
+    killer: {type: 'object'}
+
+  'world:thang-touched-goal': c.object {required: ['actor', 'touched']},
+    replacedNoteChain: {type: 'array'}
+    thang: {type: 'object'}
+    actor: {type: 'object'}
+    touched: {type: 'object'}
+
+  'world:thang-collected-item': c.object {required: ['actor', 'item']},
+    replacedNoteChain: {type: 'array'}
+    thang: {type: 'object'}
+    actor: {type: 'object'}
+    item: {type: 'object'}
+
+  'world:thang-finished-plans': c.object {required: ['thang']},
+    replacedNoteChain: {type: 'array'}
+    thang: {type: 'object'}
+
+  'world:attacked-when-out-of-range': c.object {required: ['thang']},
+    replacedNoteChain: {type: 'array'}
+    thang: {type: 'object'}
diff --git a/app/views/game-menu/ChooseHeroView.coffee b/app/views/game-menu/ChooseHeroView.coffee
index 88632e1c4..10069bc04 100644
--- a/app/views/game-menu/ChooseHeroView.coffee
+++ b/app/views/game-menu/ChooseHeroView.coffee
@@ -9,7 +9,7 @@ module.exports = class ChooseHeroView extends CocoView
   template: template
 
   events:
-    'click #restart-level-confirm-button': -> Backbone.Mediator.publish 'level:restart-level', {}
+    'click #restart-level-confirm-button': -> Backbone.Mediator.publish 'level:restart', {}
 
   getRenderData: (context={}) ->
     context = super(context)
diff --git a/app/views/play/level/tome/TomeView.coffee b/app/views/play/level/tome/TomeView.coffee
index 87fef0715..c8014f6a3 100644
--- a/app/views/play/level/tome/TomeView.coffee
+++ b/app/views/play/level/tome/TomeView.coffee
@@ -223,7 +223,7 @@ module.exports = class TomeView extends CocoView
 
   reloadAllCode: ->
     spell.view.reloadCode false for spellKey, spell of @spells when spell.team is me.team or (spell.team in ['common', 'neutral', null])
-    Backbone.Mediator.publish 'tome:cast-spells', spells: @spells, preload: false
+    Backbone.Mediator.publish 'tome:cast-spells', spells: @spells, preload: false, realTime: false
 
   updateLanguageForAllSpells: (e) ->
     spell.updateLanguageAether e.language for spellKey, spell of @spells when spell.canWrite()