This commit is contained in:
GlenDC 2014-01-21 18:05:14 +01:00
commit b2ddde96cd
17 changed files with 342 additions and 43 deletions

View file

@ -8,6 +8,8 @@ CocoView = require 'views/kinds/CocoView'
preventBackspace = (event) ->
if event.keyCode is 8 and not elementAcceptsKeystrokes(event.srcElement or event.target)
event.preventDefault()
else if (key.ctrl or key.command) and not key.alt and event.keyCode in [219, 221] # prevent Ctrl/Cmd + [ / ]
event.preventDefault()
elementAcceptsKeystrokes = (el) ->
# http://stackoverflow.com/questions/1495219/how-can-i-prevent-the-backspace-key-from-navigating-back

View file

@ -152,7 +152,7 @@ class Angel
@ids[@lastID]
# https://github.com/codecombat/codecombat/issues/81 -- TODO: we need to wait for worker initialization first
infiniteLoopIntervalDuration: 15000 # check this often (must be more than the others added)
infiniteLoopIntervalDuration: 1500000 # check this often (must be more than the others added)
infiniteLoopTimeoutDuration: 1500 # wait this long when we check
abortTimeoutDuration: 500 # give in-process or dying workers this long to give up
constructor: (@god) ->

View file

@ -140,7 +140,7 @@ module.exports = class Mark extends CocoClass
worldZ = @sprite.thang.pos.z - @sprite.thang.depth / 2
@mark.alpha = 0.451 / Math.sqrt(worldZ / 2 + 1)
else
pos ?= @sprite.displayObject
pos ?= @sprite?.displayObject
@mark.x = pos.x
@mark.y = pos.y
if @name is 'highlight'

View file

@ -259,6 +259,6 @@ module.exports = class SpriteBoss extends CocoClass
target = thang?.target
targetPos = thang?.targetPos
targetPos = null if targetPos?.isZero?() # Null targetPos get serialized as (0, 0, 0)
@targetMark.toggle target or targetPos
@targetMark.setSprite if target then @sprites[target.id] else null
@targetMark.toggle @targetMark.sprite or targetPos
@targetMark.update if targetPos then @camera.worldToSurface targetPos else null

View file

@ -30,11 +30,13 @@
color: white
#cast-button-view
display: none
.cast-button-group
position: absolute
// Bottom/right margins must appear to scroll to size of any paper gashes
top: 2%
right: 4%
top: 55px
left: 20px
z-index: 2
@include opacity(77)
.button-progress-overlay

View file

@ -32,10 +32,11 @@
.ace_editor
@include box-sizing(border-box)
margin-top: 40px
width: 100%
height: 90%
height: -webkit-calc(100% - 60px)
height: calc(100% - 60px)
height: 83%
height: -webkit-calc(100% - 60px - 40px)
height: calc(100% - 60px - 40px)
position: relative
background-color: transparent
line-height: 20px
@ -75,11 +76,10 @@
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2hpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowMjgwMTE3NDA3MjA2ODExOEE2REU4Q0M1MTM1MkIxRiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpBQjVEQUNDMzQ4RUIxMUUxOEVGRUUyNzFENDM3RDVFMCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpBQjVEQUNDMjQ4RUIxMUUxOEVGRUUyNzFENDM3RDVFMCIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1LjEgTWFjaW50b3NoIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QTU1MjE3RDIzMTIwNjgxMThEQkI4NTlBMjQ1QTEwOTUiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MDI4MDExNzQwNzIwNjgxMThBNkRFOENDNTEzNTJCMUYiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7SazaGAAAAiElEQVR42mL8//8/AzUBEwOVweA3kAWboI2jCyhgDwBx4ZH9ey5Qy4UOQHweaHg/EAtQ08sFUIMDqBmGCkC8HmgoCCtQM1ICoK5toGYsg8KzHmjo+UGbDj8AcSMwORkSnQ7xgA3QtPmApISNBTyAGrSBGl6eAMSGxBhGyIVkZT3G0fKQYgAQYACL+C2ZM6PC7AAAAABJRU5ErkJggg==)
background-position: 0px center
//.user-code-problem
//.ace_scroller
// background-color: #470000
.ace_marker-layer
.ace_bracket
// Override faint gray
border-color: #0FF
border-color: #8FF
.ace_identifier
background-color: rgba(255, 128, 128, 0.15)

View file

@ -0,0 +1,10 @@
@import "../../../bootstrap/mixins"
.spell-debug-view
position: absolute
z-index: 9001
max-width: 400px
padding: 10px
background: transparent url(/images/level/popover_background.png)
background-size: 100% 100%

View file

@ -0,0 +1,54 @@
@import "../../../bootstrap/mixins"
.spell-toolbar-view
position: absolute
z-index: 2
top: 2px
left: 5px
box-sizing: border-box
padding-left: 150px
height: 36px
width: 95%
width: -webkit-calc(95% - 5px)
width: calc(95% - 5px)
background-color: rgba(100, 45, 210, 0.05)
.spell-progress
position: relative
height: 100%
width: 50%
display: inline-block
.progress
position: absolute
left: 0px
top: 8px
bottom: 0px
width: 100%
cursor: pointer
overflow: visible
.bar
@include transition(width .0s linear)
position: relative
pointer-events: none
background-color: #67A4C8
width: 50%
.scrubber-handle
position: absolute
pointer-events: none
right: -16px
top: -7px
background: transparent url(/images/level/playback_thumb.png)
width: 32px
height: 32px
.btn-group
// I don't know, I can figure this out for real later
margin: -26px 0 0 18px
.metrics
display: inline-block
margin: -30px 0 0 10px
vertical-align: middle

View file

@ -1,4 +1,4 @@
button.btn.btn-mini.btn-inverse#play-button.playing(title="Alt-P: Toggle level play/pause")
button.btn.btn-mini.btn-inverse#play-button.playing(title="Ctrl/Cmd + P: Toggle level play/pause")
i.icon-play.icon-white.big
i.icon-pause.icon-white.big
i.icon-repeat.icon-white.big
@ -16,19 +16,19 @@ button.btn.btn-mini.btn-inverse#music-button(title="Toggle Music")
.scrubber-handle
.btn-group.dropup#playback-settings
button.btn.btn-mini.btn-inverse#zoom-in-button
button.btn.btn-mini.btn-inverse#zoom-in-button(title="Zoom In (or scroll down)")
i.icon-zoom-in.icon-white
button.btn.btn-mini.btn-inverse#zoom-out-button
button.btn.btn-mini.btn-inverse#zoom-out-button(title="Zoom Out (or scroll up)")
i.icon-zoom-out.icon-white
button.btn.btn-mini.btn-inverse.dropdown-toggle(data-toggle="dropdown")#settings-button
i.icon-cog.icon-white.big
ul.dropdown-menu
if me.get('name') == "Nick"
li(title="\\: Toggle debug display").selectable#debug-toggle
li(title="Ctrl/Cmd + \\: Toggle debug display").selectable#debug-toggle
i.icon-globe
| Debug Mode
i.icon-ok.hide
li(title="G: Toggle grid display").selectable#grid-toggle
li(title="Ctrl/Cmd + G: Toggle grid display").selectable#grid-toggle
i.icon-th
span(data-i18n="play_level.grid") Grid
i.icon-ok.hide

View file

@ -0,0 +1,2 @@
pre
code

View file

@ -0,0 +1,22 @@
.spell-progress
.progress
.bar
.scrubber-handle
.btn-group
button.btn.btn-mini.btn-inverse.banner.step-backward(title="Ctrl/Cmd + Alt + [: Step Backward")
i.icon-arrow-left.icon-white
button.btn.btn-mini.btn-inverse.banner.step-forward(title="Ctrl/Cmd + Alt + ]: Step Forward")
i.icon-arrow-right.icon-white
.metrics
code.statements-metric
span.metric.statement-index
| /
span.metric.statements-executed
span.metric.statements-executed-total
|
code.calls-metric
span.metric.call-index
| /
span.metric.calls-executed

View file

@ -24,7 +24,6 @@ module.exports = class CastButtonView extends View
context.castShortcutVerbose = @castShortcutVerbose
context
afterRender: ->
super()
# TODO: use a User setting instead of localStorage

View file

@ -64,7 +64,11 @@ module.exports = class Spell
functionParameters: @parameters
yieldConditionally: thang.plan?
requiresThis: thang.requiresThis
if @name is 'chooseAction' or not (me.team in @permissions.readwrite) or thang.id is 'Thoktar' # Gridmancer can't handle it
includeFlow: true
#callIndex: 0
#timelessVariables: ['i']
#statementIndex: 9001
if not (me.team in @permissions.readwrite)# or @name is 'chooseAction' or thang.id is 'Thoktar' # Gridmancer can't handle it
#console.log "Turning off includeFlow for", @spellKey
aetherOptions.includeFlow = false
aether = new Aether aetherOptions

View file

@ -0,0 +1,63 @@
View = require 'views/kinds/CocoView'
template = require 'templates/play/level/tome/spell_debug'
Range = ace.require("ace/range").Range
module.exports = class DebugView extends View
className: 'spell-debug-view'
template: template
subscriptions: {}
events: {}
constructor: (options) ->
super options
@ace = options.ace
@variableStates = {}
afterRender: ->
super()
@ace.on "mousemove", @onMouseMove
#@ace.on "click", onClick # same ACE API as mousemove
setVariableStates: (@variableStates) ->
@update()
onMouseMove: (e) =>
pos = e.getDocumentPosition()
column = pos.column
until column < 0
if token = e.editor.session.getTokenAt pos.row, column
break if token.type is 'identifier'
column = token.start - 1
else
--column
if token?.type is 'identifier' and token.value of @variableStates
@variable = token.value
@pos = {left: e.domEvent.offsetX + 50, top: e.domEvent.offsetY + 50}
@markerRange = new Range pos.row, token.start, pos.row, token.start + token.value.length
else
@variable = @markerRange = null
@update()
onMouseOut: (e) =>
@variable = @markerRange = null
@update()
update: ->
if @variable
value = @variableStates[@variable]
@$el.find("code").text "#{@variable}: #{value}"
@$el.show().css(@pos)
else
@$el.hide()
@updateMarker()
updateMarker: ->
if @marker
@ace.getSession().removeMarker @marker
@marker = null
if @markerRange
@marker = @ace.getSession().addMarker @markerRange, "ace_bracket", "text"
destroy: ->
super()
@ace?.removeEventListener "mousemove", @onMouseMove

View file

@ -0,0 +1,94 @@
View = require 'views/kinds/CocoView'
template = require 'templates/play/level/tome/spell_toolbar'
module.exports = class SpellToolbarView extends View
className: 'spell-toolbar-view'
template: template
subscriptions:
'spell-step-backward': 'onStepBackward'
'spell-step-forward': 'onStepForward'
events:
'mousemove .progress': 'onProgressHover'
'mouseout .progress': 'onProgressMouseOut'
'click .step-backward': 'onStepBackward'
'click .step-forward': 'onStepForward'
constructor: (options) ->
super options
@ace = options.ace
afterRender: ->
super()
setStatementIndex: (statementIndex) ->
return unless total = @callState?.statementsExecuted
@statementIndex = Math.min(total - 1, Math.max(0, statementIndex))
@statementRatio = @statementIndex / (total - 1)
@statementTime = @callState.statements[@statementIndex].userInfo.time
@$el.find('.bar').css('width', 100 * @statementRatio + '%')
Backbone.Mediator.publish 'tome:spell-statement-index-updated', statementIndex: @statementIndex, ace: @ace
@$el.find('.step-backward').prop('disabled', @statementIndex is 0)
@$el.find('.step-forward').prop('disabled', @statementIndex is total - 1)
@updateMetrics()
updateMetrics: ->
statementsExecuted = @callState.statementsExecuted
$metrics = @$el.find('.metrics')
if @metrics.callsExecuted > 1
$metrics.find('.call-index').text @callIndex + 1
$metrics.find('.calls-executed').text @metrics.callsExecuted
$metrics.find('.calls-metric').show().attr('title', "Method call #{@callIndex + 1} of #{@metrics.callsExecuted} calls")
else
$metrics.find('.calls-metric').hide()
if @metrics.statementsExecuted
$metrics.find('.statement-index').text @statementIndex + 1
$metrics.find('.statements-executed').text statementsExecuted
if @metrics.statementsExecuted > statementsExecuted
$metrics.find('.statements-executed-total').text " (#{@metrics.statementsExecuted})"
titleSuffix = " (#{@metrics.statementsExecuted} statements total)"
else
$metrics.find('.statements-executed-total').text ""
titleSuffix = ""
$metrics.find('.statements-metric').show().attr('title', "Statement #{@statementIndex + 1} of #{statementsExecuted} this call#{titleSuffix}")
else
$metrics.find('.statements-metric').hide()
setStatementRatio: (ratio) ->
return unless total = @callState?.statementsExecuted
@setStatementIndex Math.floor ratio * total
onProgressHover: (e) ->
@setStatementRatio e.offsetX / @$el.find('.progress').width()
@updateTime()
@maintainIndexHover = true
onProgressMouseOut: (e) ->
@maintainIndexHover = false
onStepBackward: (e) -> @step -1
onStepForward: (e) -> @step 1
step: (delta) ->
lastTime = @statementTime
@setStatementIndex @statementIndex + delta
@updateTime() if @statementIndex isnt lastTime
updateTime: ->
@maintainIndexScrub = true
clearTimeout @maintainIndexScrubTimeout if @maintainIndexScrubTimeout
@maintainIndexScrubTimeout = _.delay (=> @maintainIndexScrub = false), 500
Backbone.Mediator.publish 'level-set-time', time: @statementTime, scrubDuration: 500
setCallState: (callState, statementIndex, @callIndex, @metrics) ->
return if callState is @callState and statementIndex is @statementIndex
return unless @callState = callState
if not @maintainIndexHover and not @maintainIndexScrub and statementIndex? and callState.statements[statementIndex].userInfo.time isnt @statementTime
@setStatementIndex statementIndex
else
@setStatementRatio @statementRatio
# Not sure yet whether it's better to maintain @statementIndex or @statementRatio
#else if @statementRatio is 1 or not @statementIndex?
# @setStatementRatio 1
#else
# @setStatementIndex @statementIndex

View file

@ -4,6 +4,8 @@ template = require 'templates/play/level/tome/spell'
filters = require 'lib/image_filter'
Range = ace.require("ace/range").Range
Problem = require './problem'
SpellDebugView = require './spell_debug_view'
SpellToolbarView = require './spell_toolbar_view'
module.exports = class SpellView extends View
id: 'spell-view'
@ -25,9 +27,10 @@ module.exports = class SpellView extends View
'level:session-will-save': 'onSessionWillSave'
'modal-closed': 'focus'
'focus-editor': 'focus'
'tome:spell-statement-index-updated': 'onStatementIndexUpdated'
events:
'click .ace': -> console.log 'clicked ace', @
'mouseout': 'onMouseOut'
constructor: (options) ->
super options
@ -48,6 +51,7 @@ module.exports = class SpellView extends View
else
# needs to happen after the code generating this view is complete
setTimeout @onLoaded, 1
@createDebugView()
createACE: ->
# Test themes and settings here: http://ace.ajax.org/build/kitchen-sink.html
@ -86,24 +90,30 @@ module.exports = class SpellView extends View
name: 'end-all-scripts'
bindKey: {win: 'Escape', mac: 'Escape'}
exec: -> Backbone.Mediator.publish 'level:escape-pressed'
# TODO: These don't work on, for example, Danish keyboards. Figure out a more universal solution.
# @ace.commands.addCommand
# name: 'toggle-grid'
# bindKey: {win: 'Alt-G', mac: 'Alt-G'}
# exec: -> Backbone.Mediator.publish 'level-toggle-grid'
# @ace.commands.addCommand
# name: 'toggle-debug'
# bindKey: {win: 'Alt-\\', mac: 'Alt-\\'}
# exec: -> Backbone.Mediator.publish 'level-toggle-debug'
# @ace.commands.addCommand
# name: 'level-scrub-forward'
# bindKey: {win: 'Alt-]', mac: 'Alt-]'}
# exec: -> Backbone.Mediator.publish 'level-scrub-forward'
# @ace.commands.addCommand
# name: 'level-scrub-back'
# bindKey: {win: 'Alt-[', mac: 'Alt-['}
# exec: -> Backbone.Mediator.publish 'level-scrub-back'
@ace.commands.addCommand
name: 'toggle-grid'
bindKey: {win: 'Ctrl-G', mac: 'Command-G|Ctrl-G'}
exec: -> Backbone.Mediator.publish 'level-toggle-grid'
@ace.commands.addCommand
name: 'toggle-debug'
bindKey: {win: 'Ctrl-\\', mac: 'Command-\\|Ctrl-\\'}
exec: -> Backbone.Mediator.publish 'level-toggle-debug'
@ace.commands.addCommand
name: 'level-scrub-forward'
bindKey: {win: 'Ctrl-]', mac: 'Command-]|Ctrl-]'}
exec: -> Backbone.Mediator.publish 'level-scrub-forward'
@ace.commands.addCommand
name: 'level-scrub-back'
bindKey: {win: 'Ctrl-[', mac: 'Command-[|Ctrl-]'}
exec: -> Backbone.Mediator.publish 'level-scrub-back'
@ace.commands.addCommand
name: 'spell-step-forward'
bindKey: {win: 'Ctrl-Alt-]', mac: 'Command-Alt-]|Ctrl-Alt-]'}
exec: -> Backbone.Mediator.publish 'spell-step-forward'
@ace.commands.addCommand
name: 'spell-step-backward'
bindKey: {win: 'Ctrl-Alt-[', mac: 'Command-Alt-[|Ctrl-Alt-]'}
exec: -> Backbone.Mediator.publish 'spell-step-backward'
fillACE: ->
@ace.setValue @spell.source
@ -145,6 +155,17 @@ module.exports = class SpellView extends View
Backbone.Mediator.publish 'tome:spell-loaded', spell: @spell
@eventsSuppressed = false # Now that the initial change is in, we can start running any changed code
createDebugView: ->
@debugView = new SpellDebugView ace: @ace
@$el.append @debugView.render().$el.hide()
createToolbarView: ->
@toolbarView = new SpellToolbarView ace: @ace
@$el.prepend @toolbarView.render().$el
onMouseOut: (e) ->
@debugView.onMouseOut e
getSource: ->
@ace.getValue() # could also do @firepad.getText()
@ -368,22 +389,33 @@ module.exports = class SpellView extends View
@thang = e.selectedThang # update our thang to the current version
@highlightCurrentLine()
onStatementIndexUpdated: (e) ->
return unless e.ace is @ace
@highlightCurrentLine()
highlightCurrentLine: (flow) =>
# TODO: move this whole thing into SpellDebugView or somewhere?
flow ?= @spellThang?.castAether?.flow
return unless flow
executed = []
matched = false
for callState, callNumber in flow.states or []
states = flow.states ? []
currentCallIndex = null
for callState, callNumber in states
if not currentCallIndex? and callState.userInfo?.time > @thang.world.age
currentCallIndex = callNumber - 1
if matched
executed.pop()
break
executed.push []
for state, statementNumber in callState
for state, statementNumber in callState.statements
if state.userInfo?.time > @thang.world.age
matched = true
break
_.last(executed).push state
#state.executing = true if state.userInfo?.time is @thang.world.age # no work
currentCallIndex ?= callNumber - 1
#console.log "got call index", currentCallIndex, "for time", @thang.world.age, "out of", states.length
# TODO: don't redo the markers if they haven't actually changed
text = @aceDoc.getValue()
@ -397,11 +429,20 @@ module.exports = class SpellView extends View
markerRange.end.detach()
@aceSession.removeMarker markerRange.id
@markerRanges = []
@debugView.setVariableStates {}
@aceSession.removeGutterDecoration row, 'executing' for row in [0 ... @aceSession.getLength()]
$(@ace.container).find('.ace_gutter-cell.executing').removeClass('executing')
return unless executed.length
unless executed.length
@toolbarView?.$el.hide()
return
unless @toolbarView or (@spell.name is "plan" and @spellThang.castAether.metrics.statementsExecuted < 20)
@createToolbarView()
lastExecuted = _.last executed
@toolbarView?.$el.show()
statementIndex = Math.max 0, lastExecuted.length - 1
@toolbarView?.setCallState states[currentCallIndex], statementIndex, currentCallIndex, @spellThang.castAether.metrics
marked = {}
lastExecuted = lastExecuted[0 .. @toolbarView.statementIndex] if @toolbarView?.statementIndex?
for state, i in lastExecuted
#clazz = if state.executing then 'executing' else 'executed' # doesn't work
clazz = if i is lastExecuted.length - 1 then 'executing' else 'executed'
@ -410,6 +451,9 @@ module.exports = class SpellView extends View
continue if marked[key] > 2 # don't allow more than three of the same marker
marked[key] ?= 0
++marked[key]
else
@debugView.setVariableStates state.variables
#console.log "at", state.userInfo.time, "vars are now:", state.variables
[start, end] = [offsetToPos(state.range[0]), offsetToPos(state.range[1])]
markerRange = new Range(start.row, start.column, end.row, end.column)
markerRange.start = @aceDoc.createAnchor markerRange.start
@ -454,3 +498,4 @@ module.exports = class SpellView extends View
super()
@firepad?.dispose()
@ace.destroy()
@debugView.destroy()

View file

@ -114,6 +114,7 @@ module.exports = class TomeView extends View
@spellTabView?.$el.after('<div id="' + @spellTabView.id + '"></div>').detach()
@spellTabView = null
@removeSubView @spellPaletteView if @spellPaletteView
@castButton?.$el.hide()
@thangList?.$el.show()
onSpriteSelected: (e) ->
@ -137,6 +138,7 @@ module.exports = class TomeView extends View
@$el.find('#' + @spellTabView.id).after(@spellTabView.el).remove()
@spellView.setThang thang
@spellTabView.setThang thang
@castButton.$el.show()
@thangList.$el.hide()
@spellPaletteView = @insertSubView new SpellPaletteView thang: thang
@spellPaletteView.toggleControls {}, @spellView.controlsEnabled # TODO: know when palette should have been disabled but didn't exist