mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-03-13 22:49:51 -04:00
Working on level-specific coding languages, with non-writable code in JavaScript.
This commit is contained in:
parent
2a17ec5cb9
commit
dbcafbb29b
10 changed files with 76 additions and 53 deletions
|
@ -162,11 +162,11 @@ module.exports = class LevelLoader extends CocoClass
|
|||
@loadLevelSounds()
|
||||
@denormalizeSession()
|
||||
app.tracker.updatePlayState(@level, @session) unless @headless
|
||||
|
||||
|
||||
buildLoop: =>
|
||||
return if @lastBuilt and new Date().getTime() - @lastBuilt < 10
|
||||
return clearInterval @buildLoopInterval unless @spriteSheetsToBuild.length
|
||||
|
||||
|
||||
for spriteSheetResource, i in @spriteSheetsToBuild
|
||||
if spriteSheetResource.thangType.loaded
|
||||
@buildSpriteSheetsForThangType spriteSheetResource.thangType
|
||||
|
@ -267,7 +267,7 @@ module.exports = class LevelLoader extends CocoClass
|
|||
# everything else sound wise is loaded as needed as worlds are generated
|
||||
|
||||
progress: -> @supermodel.progress
|
||||
|
||||
|
||||
destroy: ->
|
||||
clearInterval @buildLoopInterval if @buildLoopInterval
|
||||
super()
|
||||
|
|
|
@ -263,8 +263,10 @@
|
|||
skip_tutorial: "Skip (esc)"
|
||||
editor_config: "Editor Config"
|
||||
editor_config_title: "Editor Configuration"
|
||||
editor_config_language_label: "Programming Language"
|
||||
editor_config_language_description: "Define the programming language you want to code in."
|
||||
editor_config_level_language_label: "Language for This Level"
|
||||
editor_config_level_language_description: "Define the programming language for this particular level."
|
||||
editor_config_default_language_label: "Default Programming Language"
|
||||
editor_config_default_language_description: "Define the programming language you want to code in when starting new levels."
|
||||
editor_config_keybindings_label: "Key Bindings"
|
||||
editor_config_keybindings_default: "Default (Ace)"
|
||||
editor_config_keybindings_description: "Adds additional shortcuts known from the common editors."
|
||||
|
@ -725,4 +727,3 @@
|
|||
text_diff: "Text Diff"
|
||||
merge_conflict_with: "MERGE CONFLICT WITH"
|
||||
no_changes: "No Changes"
|
||||
|
||||
|
|
|
@ -6,14 +6,19 @@ block modal-header-content
|
|||
block modal-body-content
|
||||
.form
|
||||
.form-group.select-group
|
||||
label.control-label(for="tome-language" data-i18n="play_level.editor_config_language_label") Programming Language
|
||||
label.control-label(for="tome-session-language" data-i18n="play_level.editor_config_level_language_label") Language for This Level
|
||||
select#tome-session-language(name="language")
|
||||
for option in languages
|
||||
option(value=option.id selected=(sessionLanguage === option.id))= option.name
|
||||
span.help-block(data-i18n="play_level.editor_config_level_language_description") Define the programming language for this particular level.
|
||||
|
||||
.form-group.select-group
|
||||
label.control-label(for="tome-language" data-i18n="play_level.editor_config_default_language_label") Default Programming Language
|
||||
select#tome-language(name="language")
|
||||
option(value="javascript" selected=(language === "javascript")) JavaScript
|
||||
option(value="coffeescript" selected=(language === "coffeescript")) CoffeeScript
|
||||
option(value="python" selected=(language === "python")) Python (Experimental)
|
||||
option(value="clojure" selected=(language === "clojure")) Clojure (Experimental)
|
||||
option(value="lua" selected=(language === "lua")) Lua (Experimental)
|
||||
span.help-block(data-i18n="play_level.editor_config_language_description") Define the programming language you want to code in.
|
||||
for option in languages
|
||||
option(value=option.id selected=(language === option.id))= option.name
|
||||
span.help-block(data-i18n="play_level.editor_config_default_language_description") Define the programming language you want to code in when starting new levels.
|
||||
|
||||
.form-group.select-group
|
||||
label.control-label(for="tome-key-bindings" data-i18n="play_level.editor_config_keybindings_label") Key Bindings
|
||||
select#tome-key-bindings(name="keyBindings")
|
||||
|
@ -21,16 +26,19 @@ block modal-body-content
|
|||
option(value="vim" selected=(keyBindings === "vim")) Vim
|
||||
option(value="emacs" selected=(keyBindings === "emacs")) Emacs
|
||||
span.help-block(data-i18n="play_level.editor_config_keybindings_description") Adds additional shortcuts known from the common editors.
|
||||
|
||||
.form-group.checkbox
|
||||
label(for="tome-invisibles")
|
||||
input#tome-invisibles(name="invisibles", type="checkbox", checked=invisibles)
|
||||
span(data-i18n="play_level.editor_config_invisibles_label") Show Invisibles
|
||||
span.help-block(data-i18n="play_level.editor_config_invisibles_description") Displays invisibles such as spaces or tabs.
|
||||
|
||||
.form-group.checkbox
|
||||
label(for="tome-indent-guides")
|
||||
input#tome-indent-guides(name="indentGuides", type="checkbox", checked=indentGuides)
|
||||
span(data-i18n="play_level.editor_config_indentguides_label") Show Indent Guides
|
||||
span.help-block(data-i18n="play_level.editor_config_indentguides_description") Displays vertical lines to see indentation better.
|
||||
|
||||
.form-group.checkbox
|
||||
label(for="tome-behaviors")
|
||||
input#tome-behaviors(name="behaviors", type="checkbox", checked=behaviors)
|
||||
|
|
|
@ -23,11 +23,20 @@ module.exports = class EditorConfigModal extends View
|
|||
|
||||
constructor: (options) ->
|
||||
super(options)
|
||||
@session = options.session
|
||||
|
||||
getRenderData: ->
|
||||
@aceConfig = _.cloneDeep me.get('aceConfig') ? {}
|
||||
@aceConfig = _.defaults @aceConfig, @defaultConfig
|
||||
c = super()
|
||||
c.languages = [
|
||||
{id: 'javascript', name: 'JavaScript'}
|
||||
{id: 'coffeescript', name: 'CoffeeScript'}
|
||||
{id: 'python', name: 'Python (Experimental)'}
|
||||
{id: 'clojure', name: 'Clojure (Experimental)'}
|
||||
{id: 'lua', name: 'Lua (Experimental)'}
|
||||
]
|
||||
c.sessionLanguage = @session.get('codeLanguage') ? @aceConfig.language
|
||||
c.language = @aceConfig.language
|
||||
c.keyBindings = @aceConfig.keyBindings
|
||||
c.invisibles = @aceConfig.invisibles
|
||||
|
@ -35,6 +44,9 @@ module.exports = class EditorConfigModal extends View
|
|||
c.behaviors = @aceConfig.behaviors
|
||||
c
|
||||
|
||||
updateSessionLanguage: ->
|
||||
@session.set 'codeLanguage', @$el.find('#tome-session-language').val()
|
||||
|
||||
updateLanguage: ->
|
||||
@aceConfig.language = @$el.find('#tome-language').val()
|
||||
|
||||
|
@ -54,7 +66,9 @@ module.exports = class EditorConfigModal extends View
|
|||
super()
|
||||
|
||||
onHidden: ->
|
||||
oldLanguage = @aceConfig.language
|
||||
oldLanguage = @session.get('codeLanguage') ? @aceConfig.language
|
||||
newLanguage = @$el.find('#tome-session-language').val()
|
||||
@session.set 'codeLanguage', newLanguage
|
||||
@aceConfig.language = @$el.find('#tome-language').val()
|
||||
@aceConfig.invisibles = @$el.find('#tome-invisibles').prop('checked')
|
||||
@aceConfig.keyBindings = @$el.find('#tome-key-bindings').val()
|
||||
|
@ -62,7 +76,8 @@ module.exports = class EditorConfigModal extends View
|
|||
@aceConfig.behaviors = @$el.find('#tome-behaviors').prop('checked')
|
||||
me.set 'aceConfig', @aceConfig
|
||||
Backbone.Mediator.publish 'tome:change-config'
|
||||
Backbone.Mediator.publish 'tome:change-language' unless @aceConfig.language isnt oldLanguage
|
||||
Backbone.Mediator.publish 'tome:change-language', language: newLanguage unless newLanguage is oldLanguage
|
||||
@session.save() unless newLanguage is oldLanguage
|
||||
me.save()
|
||||
|
||||
destroy: ->
|
||||
|
|
|
@ -156,16 +156,14 @@ module.exports = class PlaybackView extends View
|
|||
|
||||
@timePopup ?= new HoverPopup
|
||||
|
||||
|
||||
#TODO: Why do we need defaultValues here at all? Fallback language has been set to 'en'... oO
|
||||
t = $.i18n.t
|
||||
@second = t 'units.second', defaultValue: 'second'
|
||||
@seconds = t 'units.seconds', defaultValue: 'seconds'
|
||||
@minute = t 'units.minute', defaultValue: 'minute'
|
||||
@minutes = t 'units.minutes', defaultValue: 'minutes'
|
||||
@goto = t 'play_level.time_goto', defaultValue: "Go to:"
|
||||
@current = t 'play_level.time_current', defaultValue: "Now:"
|
||||
@total = t 'play_level.time_total', defaultValue: "Max:"
|
||||
@second = t 'units.second'
|
||||
@seconds = t 'units.seconds'
|
||||
@minute = t 'units.minute'
|
||||
@minutes = t 'units.minutes'
|
||||
@goto = t 'play_level.time_goto'
|
||||
@current = t 'play_level.time_current'
|
||||
@total = t 'play_level.time_total'
|
||||
|
||||
onToggleDebug: ->
|
||||
return if @shouldIgnore()
|
||||
|
@ -181,7 +179,7 @@ module.exports = class PlaybackView extends View
|
|||
Backbone.Mediator.publish 'edit-wizard-settings'
|
||||
|
||||
onEditEditorConfig: ->
|
||||
@openModalView(new EditorConfigModal())
|
||||
@openModalView new EditorConfigModal session: @options.session
|
||||
|
||||
onCastSpells: (e) ->
|
||||
return if e.preload
|
||||
|
@ -194,8 +192,8 @@ module.exports = class PlaybackView extends View
|
|||
$('button', @$el).addClass('disabled')
|
||||
try
|
||||
@$progressScrubber.slider('disable', true)
|
||||
catch e
|
||||
#console.warn('error disabling scrubber')
|
||||
catch error
|
||||
console.warn('error disabling scrubber', error)
|
||||
@timePopup?.disable()
|
||||
$('#volume-button', @$el).removeClass('disabled')
|
||||
|
||||
|
@ -205,8 +203,8 @@ module.exports = class PlaybackView extends View
|
|||
$('button', @$el).removeClass('disabled')
|
||||
try
|
||||
@$progressScrubber.slider('enable', true)
|
||||
catch e
|
||||
#console.warn('error enabling scrubber')
|
||||
catch error
|
||||
console.warn('error enabling scrubber', error)
|
||||
@timePopup?.enable()
|
||||
|
||||
onSetPlaying: (e) ->
|
||||
|
|
|
@ -25,6 +25,7 @@ module.exports = class Spell
|
|||
@parameters = p.parameters
|
||||
if @permissions.readwrite.length and sessionSource = @session.getSourceFor(@spellKey)
|
||||
@source = sessionSource
|
||||
@language = if @canWrite() then options.language else 'javascript'
|
||||
@thangs = {}
|
||||
@view = new SpellView {spell: @, session: @session, worker: @worker}
|
||||
@view.render() # Get it ready and code loaded in advance
|
||||
|
@ -100,6 +101,7 @@ module.exports = class Spell
|
|||
|
||||
createAether: (thang) ->
|
||||
aceConfig = me.get('aceConfig') ? {}
|
||||
writable = @permissions.readwrite.length > 0
|
||||
aetherOptions =
|
||||
problems:
|
||||
jshint_W040: {level: "ignore"}
|
||||
|
@ -109,13 +111,13 @@ module.exports = class Spell
|
|||
jshint_E043: {level: "ignore"} # https://github.com/codecombat/codecombat/issues/813 -- since we can't actually tell JSHint to really ignore things
|
||||
jshint_Unknown: {level: "ignore"} # E043 also triggers Unknown, so ignore that, too
|
||||
aether_MissingThis: {level: 'error'}
|
||||
language: aceConfig.language ? 'javascript'
|
||||
language: if @canWrite() then @language else 'javascript'
|
||||
functionName: @name
|
||||
functionParameters: @parameters
|
||||
yieldConditionally: thang.plan?
|
||||
globals: ['Vector', '_']
|
||||
# TODO: Gridmancer doesn't currently work with protectAPI, so hack it off
|
||||
protectAPI: not (@skipProtectAPI or window.currentView?.level.get('name').match("Gridmancer")) and @permissions.readwrite.length > 0 # If anyone can write to this method, we must protect it.
|
||||
protectAPI: not (@skipProtectAPI or window.currentView?.level.get('name').match("Gridmancer")) and writable # If anyone can write to this method, we must protect it.
|
||||
includeFlow: false
|
||||
#console.log "creating aether with options", aetherOptions
|
||||
aether = new Aether aetherOptions
|
||||
|
@ -126,15 +128,13 @@ module.exports = class Spell
|
|||
@worker.postMessage JSON.stringify workerMessage
|
||||
aether
|
||||
|
||||
updateLanguageAether: ->
|
||||
aceConfig = me.get('aceConfig') ? {}
|
||||
newLanguage = (aceConfig.language ? 'javascript')
|
||||
updateLanguageAether: (@language) ->
|
||||
for thangId, spellThang of @thangs
|
||||
spellThang.aether?.setLanguage newLanguage
|
||||
spellThang.aether?.setLanguage @language
|
||||
spellThang.castAether = null
|
||||
workerMessage =
|
||||
function: "updateLanguageAether"
|
||||
newLanguage: newLanguage
|
||||
newLanguage: @language
|
||||
@worker.postMessage JSON.stringify workerMessage
|
||||
@transpile()
|
||||
|
||||
|
|
|
@ -76,7 +76,7 @@ module.exports = class SpellView extends View
|
|||
@aceSession = @ace.getSession()
|
||||
@aceDoc = @aceSession.getDocument()
|
||||
@aceSession.setUseWorker false
|
||||
@aceSession.setMode @editModes[aceConfig.language ? 'javascript']
|
||||
@aceSession.setMode @editModes[@spell.language]
|
||||
@aceSession.setWrapLimitRange null
|
||||
@aceSession.setUseWrapMode true
|
||||
@aceSession.setNewLineMode "unix"
|
||||
|
@ -360,7 +360,7 @@ module.exports = class SpellView extends View
|
|||
displayAether: (aether) ->
|
||||
@displayedAether = aether
|
||||
isCast = not _.isEmpty(aether.metrics) or _.some aether.problems.errors, {type: 'runtime'}
|
||||
isCast = isCast or (me.get('aceConfig') ? {})['language'] isnt 'javascript' # Since we don't have linting for other languages
|
||||
isCast = isCast or @language isnt 'javascript' # Since we don't have linting for other languages
|
||||
problem.destroy() for problem in @problems # Just in case another problem was added since clearAetherDisplay() ran.
|
||||
@problems = []
|
||||
annotations = []
|
||||
|
@ -601,17 +601,15 @@ module.exports = class SpellView extends View
|
|||
@ace.setDisplayIndentGuides aceConfig.indentGuides # default false
|
||||
@ace.setShowInvisibles aceConfig.invisibles # default false
|
||||
@ace.setKeyboardHandler @keyBindings[aceConfig.keyBindings ? 'default']
|
||||
# @aceSession.setMode @editModes[aceConfig.language ? 'javascript']
|
||||
|
||||
onChangeLanguage: (e) ->
|
||||
aceConfig = me.get('aceConfig') ? {}
|
||||
@aceSession.setMode @editModes[aceConfig.language ? 'javascript']
|
||||
if @spell.canWrite()
|
||||
@aceSession.setMode @editModes[e.language]
|
||||
|
||||
dismiss: ->
|
||||
@spell.hasChangedSignificantly @getSource(), null, (hasChanged) =>
|
||||
@recompile() if hasChanged
|
||||
|
||||
|
||||
destroy: ->
|
||||
$(@ace?.container).find('.ace_gutter').off 'click', '.ace_error, .ace_warning, .ace_info', @onAnnotationClick
|
||||
@firepad?.dispose()
|
||||
|
|
|
@ -105,6 +105,7 @@ module.exports = class TomeView extends View
|
|||
return teamSpellMap
|
||||
|
||||
createSpells: (programmableThangs, world) ->
|
||||
language = @options.session.get('codeLanguage') ? me.get('aceConfig')?.language ? 'javascript'
|
||||
pathPrefixComponents = ['play', 'level', @options.levelID, @options.session.id, 'code']
|
||||
@spells ?= {}
|
||||
@thangSpells ?= {}
|
||||
|
@ -120,7 +121,7 @@ module.exports = class TomeView extends View
|
|||
@thangSpells[thang.id].push spellKey
|
||||
unless method.cloneOf
|
||||
skipProtectAPI = @getQueryVariable "skip_protect_api", (@options.levelID in ['gridmancer'])
|
||||
spell = @spells[spellKey] = new Spell programmableMethod: method, spellKey: spellKey, pathComponents: pathPrefixComponents.concat(pathComponents), session: @options.session, supermodel: @supermodel, skipProtectAPI: skipProtectAPI, worker: @worker
|
||||
spell = @spells[spellKey] = new Spell programmableMethod: method, spellKey: spellKey, pathComponents: pathPrefixComponents.concat(pathComponents), session: @options.session, supermodel: @supermodel, skipProtectAPI: skipProtectAPI, worker: @worker, language: language
|
||||
for thangID, spellKeys of @thangSpells
|
||||
thang = world.getThangByID thangID
|
||||
if thang
|
||||
|
@ -207,8 +208,9 @@ module.exports = class TomeView extends View
|
|||
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
|
||||
|
||||
updateLanguageForAllSpells: ->
|
||||
spell.updateLanguageAether() for spellKey, spell of @spells
|
||||
updateLanguageForAllSpells: (e) ->
|
||||
spell.updateLanguageAether e.language for spellKey, spell of @spells when spell.canWrite()
|
||||
@cast()
|
||||
|
||||
destroy: ->
|
||||
spell.destroy() for spellKey, spell of @spells
|
||||
|
|
|
@ -242,7 +242,7 @@ module.exports = class PlayLevelView extends View
|
|||
|
||||
insertSubviews: ->
|
||||
@insertSubView @tome = new TomeView levelID: @levelID, session: @session, thangs: @world.thangs, supermodel: @supermodel
|
||||
@insertSubView new PlaybackView {}
|
||||
@insertSubView new PlaybackView session: @session
|
||||
@insertSubView new GoalsView {}
|
||||
@insertSubView new GoldView {}
|
||||
@insertSubView new HUDView {}
|
||||
|
|
|
@ -63,7 +63,7 @@ LevelHandler = class LevelHandler extends Handler
|
|||
# TODO: generalize this for levels based on their teams
|
||||
else if level.get('type') is 'ladder'
|
||||
sessionQuery.team = 'humans'
|
||||
|
||||
|
||||
Session.findOne(sessionQuery).exec (err, doc) =>
|
||||
return @sendDatabaseError(res, err) if err
|
||||
return @sendSuccess(res, doc) if doc?
|
||||
|
@ -88,6 +88,7 @@ LevelHandler = class LevelHandler extends Handler
|
|||
access:'write'
|
||||
}
|
||||
]
|
||||
initVals.codeLanguage = req.user.get('aceConfig')?.language ? 'javascript'
|
||||
session = new Session(initVals)
|
||||
|
||||
session.save (err) =>
|
||||
|
@ -107,7 +108,7 @@ LevelHandler = class LevelHandler extends Handler
|
|||
query = Level.findOne(findParameters)
|
||||
.select(selectString)
|
||||
.lean()
|
||||
|
||||
|
||||
query.exec (err, level) =>
|
||||
return @sendDatabaseError(res, err) if err
|
||||
return @sendNotFoundError(res) unless level?
|
||||
|
@ -116,22 +117,22 @@ LevelHandler = class LevelHandler extends Handler
|
|||
original: level.original.toString()
|
||||
majorVersion: level.version.major
|
||||
creator: req.user._id+''
|
||||
|
||||
|
||||
query = Session.find(sessionQuery).select('-screenshot')
|
||||
query.exec (err, results) =>
|
||||
if err then @sendDatabaseError(res, err) else @sendSuccess res, results
|
||||
|
||||
|
||||
getHistogramData: (req, res,slug) ->
|
||||
query = Session.aggregate [
|
||||
{$match: {"levelID":slug, "submitted": true, "team":req.query.team}}
|
||||
{$project: {totalScore: 1, _id: 0}}
|
||||
]
|
||||
|
||||
|
||||
query.exec (err, data) =>
|
||||
if err? then return @sendDatabaseError res, err
|
||||
valueArray = _.pluck data, "totalScore"
|
||||
@sendSuccess res, valueArray
|
||||
|
||||
|
||||
checkExistence: (req, res, slugOrID) ->
|
||||
findParameters = {}
|
||||
if Handler.isID slugOrID
|
||||
|
@ -155,7 +156,7 @@ LevelHandler = class LevelHandler extends Handler
|
|||
sortParameters =
|
||||
"totalScore": req.query.order
|
||||
selectProperties = ['totalScore', 'creatorName', 'creator']
|
||||
|
||||
|
||||
query = Session
|
||||
.find(sessionsQueryParameters)
|
||||
.limit(req.query.limit)
|
||||
|
|
Loading…
Reference in a new issue