mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-05-04 18:03:45 -04:00
Press Ctrl+Shift+Enter to cast in real-time, and click on the ground to use your wizard as a flag. OMG, it not only works, but it is really fun!
This commit is contained in:
parent
6fee881efd
commit
9c0869e566
9 changed files with 63 additions and 98 deletions
app
assets/javascripts/workers
lib
views/play/level/tome
|
@ -346,6 +346,7 @@ self.runWorld = function runWorld(args) {
|
||||||
self.world.loadFromLevel(args.level, true);
|
self.world.loadFromLevel(args.level, true);
|
||||||
self.world.preloading = args.preload;
|
self.world.preloading = args.preload;
|
||||||
self.world.headless = args.headless;
|
self.world.headless = args.headless;
|
||||||
|
self.world.realTime = args.realTime;
|
||||||
self.goalManager = new GoalManager(self.world);
|
self.goalManager = new GoalManager(self.world);
|
||||||
self.goalManager.setGoals(args.goals);
|
self.goalManager.setGoals(args.goals);
|
||||||
self.goalManager.setCode(args.userCodeMap);
|
self.goalManager.setCode(args.userCodeMap);
|
||||||
|
@ -458,6 +459,11 @@ self.finalizePreload = function finalizePreload() {
|
||||||
self.world.finalizePreload(self.onWorldLoaded);
|
self.world.finalizePreload(self.onWorldLoaded);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
self.updateFlags = function updateFlags(flags) {
|
||||||
|
if(!self.world || self.world.framesSerializedSoFar == self.world.frames.length) return;
|
||||||
|
self.world.updateFlags(flags);
|
||||||
|
};
|
||||||
|
|
||||||
self.addEventListener('message', function(event) {
|
self.addEventListener('message', function(event) {
|
||||||
self[event.data.func](event.data.args);
|
self[event.data.func](event.data.args);
|
||||||
});
|
});
|
||||||
|
|
|
@ -12,6 +12,9 @@ module.exports = class Angel extends CocoClass
|
||||||
infiniteLoopTimeoutDuration: 7500 # wait this long for a response when checking
|
infiniteLoopTimeoutDuration: 7500 # wait this long for a response when checking
|
||||||
abortTimeoutDuration: 500 # give in-process or dying workers this long to give up
|
abortTimeoutDuration: 500 # give in-process or dying workers this long to give up
|
||||||
|
|
||||||
|
subscriptions:
|
||||||
|
'self-wizard:target-changed': 'onSelfWizardTargetChanged'
|
||||||
|
|
||||||
constructor: (@shared) ->
|
constructor: (@shared) ->
|
||||||
super()
|
super()
|
||||||
@say 'Got my wings.'
|
@say 'Got my wings.'
|
||||||
|
@ -205,6 +208,11 @@ module.exports = class Angel extends CocoClass
|
||||||
@worker.addEventListener 'message', @onWorkerMessage
|
@worker.addEventListener 'message', @onWorkerMessage
|
||||||
@worker.creationTime = new Date()
|
@worker.creationTime = new Date()
|
||||||
|
|
||||||
|
onSelfWizardTargetChanged: (e) ->
|
||||||
|
return unless @running and @work.realTime
|
||||||
|
targetPos = e.sender.targetPos
|
||||||
|
@worker.postMessage func: 'updateFlags', args: [{type: 'wizard', targetPos: targetPos}]
|
||||||
|
|
||||||
|
|
||||||
#### Synchronous code for running worlds on main thread (profiling / IE9) ####
|
#### Synchronous code for running worlds on main thread (profiling / IE9) ####
|
||||||
simulateSync: (work) =>
|
simulateSync: (work) =>
|
||||||
|
|
|
@ -53,9 +53,9 @@ module.exports = class God extends CocoClass
|
||||||
setWorldClassMap: (worldClassMap) -> @angelsShare.worldClassMap = worldClassMap
|
setWorldClassMap: (worldClassMap) -> @angelsShare.worldClassMap = worldClassMap
|
||||||
|
|
||||||
onTomeCast: (e) ->
|
onTomeCast: (e) ->
|
||||||
@createWorld e.spells, e.preload
|
@createWorld e.spells, e.preload, e.realTime
|
||||||
|
|
||||||
createWorld: (spells, preload=false) ->
|
createWorld: (spells, preload=false, realTime=false) ->
|
||||||
console.log "#{@nick}: Let there be light upon #{@level.name}! (preload: #{preload})"
|
console.log "#{@nick}: Let there be light upon #{@level.name}! (preload: #{preload})"
|
||||||
userCodeMap = @getUserCodeMap spells
|
userCodeMap = @getUserCodeMap spells
|
||||||
|
|
||||||
|
@ -84,6 +84,7 @@ module.exports = class God extends CocoClass
|
||||||
headless: @angelsShare.headless
|
headless: @angelsShare.headless
|
||||||
preload: preload
|
preload: preload
|
||||||
synchronous: not Worker? # Profiling world simulation is easier on main thread, or we are IE9.
|
synchronous: not Worker? # Profiling world simulation is easier on main thread, or we are IE9.
|
||||||
|
realTime: realTime
|
||||||
angel.workIfIdle() for angel in @angelsShare.angels
|
angel.workIfIdle() for angel in @angelsShare.angels
|
||||||
|
|
||||||
getUserCodeMap: (spells) ->
|
getUserCodeMap: (spells) ->
|
||||||
|
|
|
@ -197,6 +197,7 @@ module.exports = class LevelBus extends Bus
|
||||||
onNewGoalStates: ({goalStates})->
|
onNewGoalStates: ({goalStates})->
|
||||||
state = @session.get 'state'
|
state = @session.get 'state'
|
||||||
unless utils.kindaEqual state.goalStates, goalStates # Only save when goals really change
|
unless utils.kindaEqual state.goalStates, goalStates # Only save when goals really change
|
||||||
|
return console.error("Somehow trying to save null goal states!", goalStates) if _.find(goalStates, (gs) -> not gs.status)
|
||||||
state.goalStates = goalStates
|
state.goalStates = goalStates
|
||||||
@session.set 'state', state
|
@session.set 'state', state
|
||||||
@changedSessionProperties.state = true
|
@changedSessionProperties.state = true
|
||||||
|
|
|
@ -1,81 +0,0 @@
|
||||||
CocoClass = require 'lib/CocoClass'
|
|
||||||
|
|
||||||
module.exports = class CastingScreen extends CocoClass
|
|
||||||
subscriptions:
|
|
||||||
'tome:cast-spells': 'onCastingBegins'
|
|
||||||
'god:new-world-created': 'onCastingEnds'
|
|
||||||
'god:world-load-progress-changed': 'onWorldLoadProgressChanged'
|
|
||||||
|
|
||||||
constructor: (options) ->
|
|
||||||
super()
|
|
||||||
options ?= {}
|
|
||||||
@camera = options.camera
|
|
||||||
@layer = options.layer
|
|
||||||
console.error @toString(), 'needs a camera.' unless @camera
|
|
||||||
console.error @toString(), 'needs a layer.' unless @layer
|
|
||||||
@build()
|
|
||||||
|
|
||||||
onCastingBegins: (e) -> @show() unless e.preload
|
|
||||||
onCastingEnds: (e) -> @hide()
|
|
||||||
|
|
||||||
toString: -> '<CastingScreen>'
|
|
||||||
|
|
||||||
build: ->
|
|
||||||
@dimLayer = new createjs.Container()
|
|
||||||
@dimLayer.mouseEnabled = @dimLayer.mouseChildren = false
|
|
||||||
@dimLayer.layerIndex = -11
|
|
||||||
@dimLayer.addChild @dimScreen = new createjs.Shape()
|
|
||||||
@dimScreen.graphics.beginFill('rgba(0,0,0,0.5)').rect 0, 0, @camera.canvasWidth, @camera.canvasHeight
|
|
||||||
@dimLayer.alpha = 0
|
|
||||||
@layer.addChild @dimLayer
|
|
||||||
@dimLayer.addChild @makeProgressBar()
|
|
||||||
@dimLayer.addChild @makeCastingText()
|
|
||||||
|
|
||||||
onWorldLoadProgressChanged: (e) ->
|
|
||||||
if new Date().getTime() - @t0 > 500
|
|
||||||
createjs.Tween.removeTweens @progressBar
|
|
||||||
createjs.Tween.get(@progressBar).to({scaleX: e.progress}, 200)
|
|
||||||
|
|
||||||
makeProgressBar: ->
|
|
||||||
BAR_PIXEL_HEIGHT = 3
|
|
||||||
BAR_PCT_WIDTH = .75
|
|
||||||
pixelWidth = parseInt(@camera.canvasWidth * BAR_PCT_WIDTH)
|
|
||||||
pixelMargin = (@camera.canvasWidth - (@camera.canvasWidth * BAR_PCT_WIDTH)) / 2
|
|
||||||
barY = 3 * (@camera.canvasHeight / 5)
|
|
||||||
|
|
||||||
g = new createjs.Graphics()
|
|
||||||
g.beginFill(createjs.Graphics.getRGB(255, 255, 255))
|
|
||||||
g.drawRoundRect(0, 0, pixelWidth, BAR_PIXEL_HEIGHT, 3)
|
|
||||||
@progressBar = new createjs.Shape(g)
|
|
||||||
@progressBar.x = pixelMargin
|
|
||||||
@progressBar.y = barY
|
|
||||||
@progressBar.scaleX = 0
|
|
||||||
@dimLayer.addChild(@progressBar)
|
|
||||||
|
|
||||||
makeCastingText: ->
|
|
||||||
size = @camera.canvasHeight / 15
|
|
||||||
text = new createjs.Text('Casting', "#{size}px cursive", '#aaaaaa')
|
|
||||||
text.regX = text.getMeasuredWidth() / 2
|
|
||||||
text.regY = text.getMeasuredHeight() / 2
|
|
||||||
text.x = @camera.canvasWidth / 2
|
|
||||||
text.y = @camera.canvasHeight / 2
|
|
||||||
@text = text
|
|
||||||
return text
|
|
||||||
|
|
||||||
show: ->
|
|
||||||
return if @showing
|
|
||||||
@showing = true
|
|
||||||
@t0 = new Date().getTime()
|
|
||||||
|
|
||||||
@progressBar.scaleX = 0
|
|
||||||
@dimLayer.alpha = 0
|
|
||||||
createjs.Tween.removeTweens @dimLayer
|
|
||||||
createjs.Tween.get(@dimLayer).to({alpha: 1}, 500)
|
|
||||||
|
|
||||||
hide: ->
|
|
||||||
return unless @showing
|
|
||||||
@showing = false
|
|
||||||
|
|
||||||
createjs.Tween.removeTweens @progressBar
|
|
||||||
createjs.Tween.removeTweens @dimLayer
|
|
||||||
createjs.Tween.get(@dimLayer).to({alpha: 0}, 500)
|
|
|
@ -8,7 +8,6 @@ CameraBorder = require './CameraBorder'
|
||||||
Layer = require './Layer'
|
Layer = require './Layer'
|
||||||
Letterbox = require './Letterbox'
|
Letterbox = require './Letterbox'
|
||||||
Dimmer = require './Dimmer'
|
Dimmer = require './Dimmer'
|
||||||
CastingScreen = require './CastingScreen'
|
|
||||||
PlaybackOverScreen = require './PlaybackOverScreen'
|
PlaybackOverScreen = require './PlaybackOverScreen'
|
||||||
DebugDisplay = require './DebugDisplay'
|
DebugDisplay = require './DebugDisplay'
|
||||||
CoordinateDisplay = require './CoordinateDisplay'
|
CoordinateDisplay = require './CoordinateDisplay'
|
||||||
|
@ -97,7 +96,6 @@ module.exports = Surface = class Surface extends CocoClass
|
||||||
@spriteBoss.destroy()
|
@spriteBoss.destroy()
|
||||||
@chooser?.destroy()
|
@chooser?.destroy()
|
||||||
@dimmer?.destroy()
|
@dimmer?.destroy()
|
||||||
@castingScreen?.destroy()
|
|
||||||
@playbackOverScreen?.destroy()
|
@playbackOverScreen?.destroy()
|
||||||
@stage.clear()
|
@stage.clear()
|
||||||
@musicPlayer?.destroy()
|
@musicPlayer?.destroy()
|
||||||
|
@ -403,7 +401,6 @@ module.exports = Surface = class Surface extends CocoClass
|
||||||
@surfaceLayer.addChild @cameraBorder = new CameraBorder bounds: @camera.bounds
|
@surfaceLayer.addChild @cameraBorder = new CameraBorder bounds: @camera.bounds
|
||||||
@screenLayer.addChild new Letterbox canvasWidth: canvasWidth, canvasHeight: canvasHeight
|
@screenLayer.addChild new Letterbox canvasWidth: canvasWidth, canvasHeight: canvasHeight
|
||||||
@spriteBoss = new SpriteBoss camera: @camera, surfaceLayer: @surfaceLayer, surfaceTextLayer: @surfaceTextLayer, world: @world, thangTypes: @options.thangTypes, choosing: @options.choosing, navigateToSelection: @options.navigateToSelection, showInvisible: @options.showInvisible
|
@spriteBoss = new SpriteBoss camera: @camera, surfaceLayer: @surfaceLayer, surfaceTextLayer: @surfaceTextLayer, world: @world, thangTypes: @options.thangTypes, choosing: @options.choosing, navigateToSelection: @options.navigateToSelection, showInvisible: @options.showInvisible
|
||||||
#@castingScreen ?= new CastingScreen camera: @camera, layer: @screenLayer # STREAM: Not needed with world streaming.
|
|
||||||
@playbackOverScreen ?= new PlaybackOverScreen camera: @camera, layer: @screenLayer
|
@playbackOverScreen ?= new PlaybackOverScreen camera: @camera, layer: @screenLayer
|
||||||
@stage.enableMouseOver(10)
|
@stage.enableMouseOver(10)
|
||||||
@stage.addEventListener 'stagemousemove', @onMouseMove
|
@stage.addEventListener 'stagemousemove', @onMouseMove
|
||||||
|
|
|
@ -12,6 +12,8 @@ Component = require 'lib/world/component'
|
||||||
System = require 'lib/world/system'
|
System = require 'lib/world/system'
|
||||||
PROGRESS_UPDATE_INTERVAL = 100
|
PROGRESS_UPDATE_INTERVAL = 100
|
||||||
DESERIALIZATION_INTERVAL = 10
|
DESERIALIZATION_INTERVAL = 10
|
||||||
|
REAL_TIME_BUFFER_MIN = 2 * PROGRESS_UPDATE_INTERVAL
|
||||||
|
REAL_TIME_BUFFER_MAX = 5 * PROGRESS_UPDATE_INTERVAL
|
||||||
ITEM_ORIGINAL = '53e12043b82921000051cdf9'
|
ITEM_ORIGINAL = '53e12043b82921000051cdf9'
|
||||||
|
|
||||||
module.exports = class World
|
module.exports = class World
|
||||||
|
@ -89,6 +91,8 @@ module.exports = class World
|
||||||
console.log 'Warning: loadFrames called on empty World (no thangs).'
|
console.log 'Warning: loadFrames called on empty World (no thangs).'
|
||||||
t1 = now()
|
t1 = now()
|
||||||
@t0 ?= t1
|
@t0 ?= t1
|
||||||
|
@worldLoadStartTime ?= t1
|
||||||
|
@lastRealTimeUpdate ?= 0
|
||||||
if loadUntilFrame
|
if loadUntilFrame
|
||||||
frameToLoadUntil = loadUntilFrame + 1
|
frameToLoadUntil = loadUntilFrame + 1
|
||||||
else
|
else
|
||||||
|
@ -112,8 +116,16 @@ module.exports = class World
|
||||||
return unless errorCallback error # errorCallback tells us whether the error is recoverable
|
return unless errorCallback error # errorCallback tells us whether the error is recoverable
|
||||||
@unhandledRuntimeErrors = []
|
@unhandledRuntimeErrors = []
|
||||||
t2 = now()
|
t2 = now()
|
||||||
if t2 - t1 > PROGRESS_UPDATE_INTERVAL
|
if @realTime
|
||||||
loadProgressCallback? i / @totalFrames unless @preloading
|
shouldUpdateProgress = @shouldUpdateRealTimePlayback t2
|
||||||
|
shouldDelayRealTimeSimulation = not shouldUpdateProgress and @shouldDelayRealTimeSimulation t2
|
||||||
|
else
|
||||||
|
shouldUpdateProgress = t2 - t1 > PROGRESS_UPDATE_INTERVAL
|
||||||
|
shouldDelayRealTimeSimulation = false
|
||||||
|
if shouldUpdateProgress or shouldDelayRealTimeSimulation
|
||||||
|
if shouldUpdateProgress
|
||||||
|
@lastRealTimeUpdate = i * @dt if @realTime
|
||||||
|
loadProgressCallback? i / @totalFrames unless @preloading
|
||||||
t1 = t2
|
t1 = t2
|
||||||
if t2 - @t0 > 1000
|
if t2 - @t0 > 1000
|
||||||
console.log ' Loaded', i, 'of', @totalFrames, '(+' + (t2 - @t0).toFixed(0) + 'ms)'
|
console.log ' Loaded', i, 'of', @totalFrames, '(+' + (t2 - @t0).toFixed(0) + 'ms)'
|
||||||
|
@ -127,7 +139,8 @@ module.exports = class World
|
||||||
if skipDeferredLoading
|
if skipDeferredLoading
|
||||||
continueFn()
|
continueFn()
|
||||||
else
|
else
|
||||||
setTimeout(continueFn, 0)
|
delay = if shouldDelayRealTimeSimulation then PROGRESS_UPDATE_INTERVAL else 0
|
||||||
|
setTimeout(continueFn, delay)
|
||||||
return
|
return
|
||||||
unless @debugging
|
unless @debugging
|
||||||
@ended = true
|
@ended = true
|
||||||
|
@ -136,6 +149,19 @@ module.exports = class World
|
||||||
loadProgressCallback? 1
|
loadProgressCallback? 1
|
||||||
loadedCallback()
|
loadedCallback()
|
||||||
|
|
||||||
|
shouldDelayRealTimeSimulation: (t) ->
|
||||||
|
return false unless @realTime
|
||||||
|
timeSinceStart = t - @worldLoadStartTime
|
||||||
|
timeLoaded = @frames.length * @dt * 1000
|
||||||
|
timeBuffered = timeLoaded - timeSinceStart
|
||||||
|
timeBuffered > REAL_TIME_BUFFER_MAX
|
||||||
|
|
||||||
|
shouldUpdateRealTimePlayback: (t) ->
|
||||||
|
return false unless @realTime
|
||||||
|
timeSinceStart = t - @worldLoadStartTime
|
||||||
|
remainingBuffer = @lastRealTimeUpdate * 1000 - timeSinceStart
|
||||||
|
remainingBuffer < REAL_TIME_BUFFER_MIN
|
||||||
|
|
||||||
finalizePreload: (loadedCallback) ->
|
finalizePreload: (loadedCallback) ->
|
||||||
@preloading = false
|
@preloading = false
|
||||||
loadedCallback() if @ended
|
loadedCallback() if @ended
|
||||||
|
@ -143,6 +169,9 @@ module.exports = class World
|
||||||
abort: ->
|
abort: ->
|
||||||
@aborted = true
|
@aborted = true
|
||||||
|
|
||||||
|
updateFlags: (@flags) ->
|
||||||
|
console.log "updated flags", @flags
|
||||||
|
|
||||||
loadFromLevel: (level, willSimulate=true) ->
|
loadFromLevel: (level, willSimulate=true) ->
|
||||||
@levelComponents = level.levelComponents
|
@levelComponents = level.levelComponents
|
||||||
@thangTypes = level.thangTypes
|
@thangTypes = level.thangTypes
|
||||||
|
|
|
@ -114,6 +114,10 @@ module.exports = class SpellView extends CocoView
|
||||||
name: 'run-code'
|
name: 'run-code'
|
||||||
bindKey: {win: 'Shift-Enter|Ctrl-Enter', mac: 'Shift-Enter|Command-Enter|Ctrl-Enter'}
|
bindKey: {win: 'Shift-Enter|Ctrl-Enter', mac: 'Shift-Enter|Command-Enter|Ctrl-Enter'}
|
||||||
exec: -> Backbone.Mediator.publish 'tome:manual-cast', {}
|
exec: -> Backbone.Mediator.publish 'tome:manual-cast', {}
|
||||||
|
addCommand
|
||||||
|
name: 'run-code-real-time'
|
||||||
|
bindKey: {win: 'Ctrl-Shift-Enter', mac: 'Command-Shift-Enter|Ctrl-Shift-Enter'}
|
||||||
|
exec: -> Backbone.Mediator.publish 'tome:manual-cast', {realTime: true}
|
||||||
addCommand
|
addCommand
|
||||||
name: 'no-op'
|
name: 'no-op'
|
||||||
bindKey: {win: 'Ctrl-S', mac: 'Command-S|Ctrl-S'}
|
bindKey: {win: 'Ctrl-S', mac: 'Command-S|Ctrl-S'}
|
||||||
|
@ -269,8 +273,8 @@ module.exports = class SpellView extends CocoView
|
||||||
# @addZatannaSnippets()
|
# @addZatannaSnippets()
|
||||||
@highlightCurrentLine()
|
@highlightCurrentLine()
|
||||||
|
|
||||||
cast: (preload=false) ->
|
cast: (preload=false, realTime=false) ->
|
||||||
Backbone.Mediator.publish 'tome:cast-spell', spell: @spell, thang: @thang, preload: preload
|
Backbone.Mediator.publish 'tome:cast-spell', spell: @spell, thang: @thang, preload: preload, realTime: realTime
|
||||||
|
|
||||||
notifySpellChanged: =>
|
notifySpellChanged: =>
|
||||||
Backbone.Mediator.publish 'tome:spell-changed', spell: @spell
|
Backbone.Mediator.publish 'tome:spell-changed', spell: @spell
|
||||||
|
@ -285,7 +289,7 @@ module.exports = class SpellView extends CocoView
|
||||||
|
|
||||||
onManualCast: (e) ->
|
onManualCast: (e) ->
|
||||||
cast = @$el.parent().length
|
cast = @$el.parent().length
|
||||||
@recompile cast
|
@recompile cast, e.realTime
|
||||||
@focus() if cast
|
@focus() if cast
|
||||||
|
|
||||||
onCodeReload: (e) ->
|
onCodeReload: (e) ->
|
||||||
|
@ -299,12 +303,12 @@ module.exports = class SpellView extends CocoView
|
||||||
recompileIfNeeded: =>
|
recompileIfNeeded: =>
|
||||||
@recompile() if @recompileNeeded
|
@recompile() if @recompileNeeded
|
||||||
|
|
||||||
recompile: (cast=true) ->
|
recompile: (cast=true, realTime=false) ->
|
||||||
@setRecompileNeeded false
|
@setRecompileNeeded false
|
||||||
return if @spell.source is @getSource()
|
return if @spell.source is @getSource()
|
||||||
@spell.transpile @getSource()
|
@spell.transpile @getSource()
|
||||||
@updateAether true, false
|
@updateAether true, false
|
||||||
@cast() if cast
|
@cast(false, realTime) if cast
|
||||||
@notifySpellChanged()
|
@notifySpellChanged()
|
||||||
|
|
||||||
updateACEText: (source) ->
|
updateACEText: (source) ->
|
||||||
|
@ -472,7 +476,7 @@ module.exports = class SpellView extends CocoView
|
||||||
onSessionWillSave: (e) ->
|
onSessionWillSave: (e) ->
|
||||||
return unless @spellHasChanged
|
return unless @spellHasChanged
|
||||||
setTimeout(=>
|
setTimeout(=>
|
||||||
unless @spellHasChanged
|
unless @destroyed or @spellHasChanged
|
||||||
@$el.find('.save-status').finish().show().fadeOut(2000)
|
@$el.find('.save-status').finish().show().fadeOut(2000)
|
||||||
, 1000)
|
, 1000)
|
||||||
@spellHasChanged = false
|
@spellHasChanged = false
|
||||||
|
|
|
@ -152,10 +152,10 @@ module.exports = class TomeView extends CocoView
|
||||||
onCastSpell: (e) ->
|
onCastSpell: (e) ->
|
||||||
# A single spell is cast.
|
# A single spell is cast.
|
||||||
# Hmm; do we need to make sure other spells are all cast here?
|
# Hmm; do we need to make sure other spells are all cast here?
|
||||||
@cast e?.preload
|
@cast e?.preload, e?.realTime
|
||||||
|
|
||||||
cast: (preload=false) ->
|
cast: (preload=false, realTime=false) ->
|
||||||
Backbone.Mediator.publish 'tome:cast-spells', spells: @spells, preload: preload
|
Backbone.Mediator.publish 'tome:cast-spells', spells: @spells, preload: preload, realTime: realTime
|
||||||
|
|
||||||
onToggleSpellList: (e) ->
|
onToggleSpellList: (e) ->
|
||||||
@spellList.rerenderEntries()
|
@spellList.rerenderEntries()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue