mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-03-14 07:00:01 -04:00
Merge branch 'master' of https://github.com/codecombat/codecombat
This commit is contained in:
commit
d61922ae42
33 changed files with 366 additions and 144 deletions
|
@ -2,7 +2,7 @@ var window = self;
|
|||
var Global = self;
|
||||
|
||||
importScripts("/javascripts/lodash.js", "/javascripts/aether.js");
|
||||
console.log("imported scripts!");
|
||||
console.log("Aether Tome worker has finished importing scripts.");
|
||||
var aethers = {};
|
||||
|
||||
var createAether = function (spellKey, options)
|
||||
|
@ -96,4 +96,4 @@ self.addEventListener('message', function(e) {
|
|||
var returnObject = {"message":message, "function":"none"};
|
||||
self.postMessage(JSON.stringify(returnObject));
|
||||
}
|
||||
}, false);
|
||||
}, false);
|
||||
|
|
|
@ -8,4 +8,9 @@ module.exports = class CocoCollection extends Backbone.Collection
|
|||
model.loaded = true for model in @models
|
||||
|
||||
getURL: ->
|
||||
return if _.isString @url then @url else @url()
|
||||
return if _.isString @url then @url else @url()
|
||||
|
||||
fetch: ->
|
||||
@jqxhr = super(arguments...)
|
||||
@loading = true
|
||||
@jqxhr
|
||||
|
|
14
app/collections/ThangNamesCollection.coffee
Normal file
14
app/collections/ThangNamesCollection.coffee
Normal file
|
@ -0,0 +1,14 @@
|
|||
ThangType = require 'models/ThangType'
|
||||
CocoCollection = require 'collections/CocoCollection'
|
||||
|
||||
module.exports = class ThangNamesCollection extends CocoCollection
|
||||
url: '/db/thang.type/names'
|
||||
model: ThangType
|
||||
isCachable: false
|
||||
|
||||
constructor: (@ids) -> super()
|
||||
|
||||
fetch: (options) ->
|
||||
options ?= {}
|
||||
_.extend options, {type:'POST', data:{ids:@ids}}
|
||||
super(options)
|
|
@ -4,6 +4,7 @@ LevelSystem = require 'models/LevelSystem'
|
|||
Article = require 'models/Article'
|
||||
LevelSession = require 'models/LevelSession'
|
||||
ThangType = require 'models/ThangType'
|
||||
ThangNamesCollection = require 'collections/ThangNamesCollection'
|
||||
|
||||
CocoClass = require 'lib/CocoClass'
|
||||
AudioPlayer = require 'lib/AudioPlayer'
|
||||
|
@ -21,8 +22,10 @@ World = require 'lib/world/world'
|
|||
module.exports = class LevelLoader extends CocoClass
|
||||
|
||||
constructor: (options) ->
|
||||
@t0 = new Date().getTime()
|
||||
super()
|
||||
@supermodel = options.supermodel
|
||||
@supermodel.setMaxProgress 0.2
|
||||
@levelID = options.levelID
|
||||
@sessionID = options.sessionID
|
||||
@opponentSessionID = options.opponentSessionID
|
||||
|
@ -103,17 +106,18 @@ module.exports = class LevelLoader extends CocoClass
|
|||
|
||||
objUniq = (array) -> _.uniq array, false, (arg) -> JSON.stringify(arg)
|
||||
|
||||
for thangID in _.uniq thangIDs
|
||||
url = "/db/thang.type/#{thangID}/version"
|
||||
url += "?project=true" if @headless and not @editorMode
|
||||
res = @maybeLoadURL url, ThangType, 'thang'
|
||||
@listenToOnce res.model, 'sync', @buildSpriteSheetsForThangType if res
|
||||
worldNecessities = []
|
||||
|
||||
@thangIDs = _.uniq thangIDs
|
||||
@thangNames = new ThangNamesCollection(@thangIDs)
|
||||
worldNecessities.push @supermodel.loadCollection(@thangNames, 'thang_names')
|
||||
|
||||
for obj in objUniq componentVersions
|
||||
url = "/db/level.component/#{obj.original}/version/#{obj.majorVersion}"
|
||||
@maybeLoadURL url, LevelComponent, 'component'
|
||||
worldNecessities.push @maybeLoadURL(url, LevelComponent, 'component')
|
||||
for obj in objUniq systemVersions
|
||||
url = "/db/level.system/#{obj.original}/version/#{obj.majorVersion}"
|
||||
@maybeLoadURL url, LevelSystem, 'system'
|
||||
worldNecessities.push @maybeLoadURL(url, LevelSystem, 'system')
|
||||
for obj in objUniq articleVersions
|
||||
url = "/db/article/#{obj.original}/version/#{obj.majorVersion}"
|
||||
@maybeLoadURL url, Article, 'article'
|
||||
|
@ -125,16 +129,51 @@ module.exports = class LevelLoader extends CocoClass
|
|||
wizard = ThangType.loadUniversalWizard()
|
||||
@supermodel.loadModel wizard, 'thang'
|
||||
|
||||
jqxhrs = (resource.jqxhr for resource in worldNecessities when resource?.jqxhr)
|
||||
$.when(jqxhrs...).done(@onWorldNecessitiesLoaded)
|
||||
|
||||
onWorldNecessitiesLoaded: =>
|
||||
@initWorld()
|
||||
@supermodel.clearMaxProgress()
|
||||
return if @headless and not @editorMode
|
||||
thangsToLoad = _.uniq( (t.spriteName for t in @world.thangs) )
|
||||
nameModelTuples = ([thangType.get('name'), thangType] for thangType in @thangNames.models)
|
||||
nameModelMap = _.zipObject nameModelTuples
|
||||
@spriteSheetsToBuild = []
|
||||
|
||||
for thangTypeName in thangsToLoad
|
||||
thangType = nameModelMap[thangTypeName]
|
||||
thangType.fetch()
|
||||
thangType = @supermodel.loadModel(thangType, 'thang').model
|
||||
res = @supermodel.addSomethingResource "sprite_sheet", 5
|
||||
res.thangType = thangType
|
||||
res.markLoading()
|
||||
@spriteSheetsToBuild.push res
|
||||
|
||||
@buildLoopInterval = setInterval @buildLoop, 5
|
||||
|
||||
maybeLoadURL: (url, Model, resourceName) ->
|
||||
return if @supermodel.getModel(url)
|
||||
model = new Model().setURL url
|
||||
@supermodel.loadModel(model, resourceName)
|
||||
|
||||
onSupermodelLoaded: ->
|
||||
console.log 'SuperModel for Level loaded in', new Date().getTime() - @t0, 'ms'
|
||||
@loadLevelSounds()
|
||||
@denormalizeSession()
|
||||
app.tracker.updatePlayState(@level, @session) unless @headless
|
||||
@initWorld()
|
||||
|
||||
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
|
||||
@spriteSheetsToBuild.splice i, 1
|
||||
@lastBuilt = new Date().getTime()
|
||||
spriteSheetResource.markLoaded()
|
||||
return
|
||||
|
||||
denormalizeSession: ->
|
||||
return if @headless or @sessionDenormalized or @spectateMode
|
||||
|
@ -156,6 +195,10 @@ module.exports = class LevelLoader extends CocoClass
|
|||
|
||||
buildSpriteSheetsForThangType: (thangType) ->
|
||||
return if @headless
|
||||
# TODO: Finish making sure the supermodel loads the raster image before triggering load complete, and that the cocosprite has access to the asset.
|
||||
# if f = thangType.get('raster')
|
||||
# queue = new createjs.LoadQueue()
|
||||
# queue.loadFile('/file/'+f)
|
||||
@grabThangTypeTeams() unless @thangTypeTeams
|
||||
for team in @thangTypeTeams[thangType.get('original')] ? [null]
|
||||
spriteOptions = {resolutionFactor: 4, async: false}
|
||||
|
@ -198,6 +241,7 @@ module.exports = class LevelLoader extends CocoClass
|
|||
@world = new World()
|
||||
serializedLevel = @level.serialize(@supermodel)
|
||||
@world.loadFromLevel serializedLevel, false
|
||||
console.log "World has been initialized from level loader."
|
||||
|
||||
# Initial Sound Loading
|
||||
|
||||
|
@ -223,3 +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()
|
||||
|
|
|
@ -74,7 +74,7 @@ module.exports = class Simulator extends CocoClass
|
|||
return
|
||||
|
||||
@supermodel ?= new SuperModel()
|
||||
|
||||
@supermodel.resetProgress()
|
||||
@levelLoader = new LevelLoader supermodel: @supermodel, levelID: levelID, sessionID: @task.getFirstSessionID(), headless: true
|
||||
if @supermodel.finished()
|
||||
@simulateGame()
|
||||
|
@ -269,7 +269,8 @@ module.exports = class Simulator extends CocoClass
|
|||
if spellTeam not in playerTeams then useProtectAPI = false
|
||||
@spells[spellKey].thangs[thang.id].aether = @createAether @spells[spellKey].name, method, useProtectAPI
|
||||
|
||||
transpileSpell: (thang, spellKey, methodName) ->
|
||||
transpileSpell: (thang, spellKey, methodName) ->
|
||||
|
||||
slugifiedThangID = _.string.slugify thang.id
|
||||
source = @currentUserCodeMap[[slugifiedThangID,methodName].join '/'] ? ""
|
||||
aether = @spells[spellKey].thangs[thang.id].aether
|
||||
|
@ -284,7 +285,7 @@ module.exports = class Simulator extends CocoClass
|
|||
functionName: methodName
|
||||
protectAPI: useProtectAPI
|
||||
includeFlow: false
|
||||
yieldConditionally: false
|
||||
yieldConditionally: methodName is "plan"
|
||||
globals: ['Vector', '_']
|
||||
problems:
|
||||
jshint_W040: {level: "ignore"}
|
||||
|
|
|
@ -70,7 +70,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
|
|||
@age = 0
|
||||
@scaleFactor = @targetScaleFactor = 1
|
||||
@displayObject = new createjs.Container()
|
||||
if @thangType.get('actions')
|
||||
if @thangType.isFullyLoaded()
|
||||
@setupSprite()
|
||||
else
|
||||
@stillLoading = true
|
||||
|
@ -79,9 +79,30 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
|
|||
|
||||
setupSprite: ->
|
||||
@stillLoading = false
|
||||
@actions = @thangType.getActions()
|
||||
@buildFromSpriteSheet @buildSpriteSheet()
|
||||
@createMarks()
|
||||
if @thangType.get('raster')
|
||||
@isRaster = true
|
||||
@setUpRasterImage()
|
||||
@actions = {}
|
||||
else
|
||||
@actions = @thangType.getActions()
|
||||
@buildFromSpriteSheet @buildSpriteSheet()
|
||||
@createMarks()
|
||||
|
||||
setUpRasterImage: ->
|
||||
raster = @thangType.get('raster')
|
||||
sprite = @imageObject = new createjs.Bitmap('/file/'+raster)
|
||||
$(sprite.image).one 'load', => @updateScale?()
|
||||
@displayObject.addChild(sprite)
|
||||
@configureMouse()
|
||||
@originalScaleX = sprite.scaleX
|
||||
@originalScaleY = sprite.scaleY
|
||||
@displayObject.sprite = @
|
||||
@displayObject.layerPriority = @thangType.get 'layerPriority'
|
||||
@displayObject.name = @thang?.spriteName or @thangType.get 'name'
|
||||
reg = @getOffset 'registration'
|
||||
@imageObject.regX = -reg.x
|
||||
@imageObject.regY = -reg.y
|
||||
@updateScale()
|
||||
|
||||
destroy: ->
|
||||
mark.destroy() for name, mark of @marks
|
||||
|
@ -126,6 +147,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
|
|||
|
||||
queueAction: (action) ->
|
||||
# The normal way to have an action play
|
||||
return unless @thangType.isFullyLoaded()
|
||||
action = @actions[action] if _.isString(action)
|
||||
action ?= @actions.idle
|
||||
@actionQueue = []
|
||||
|
@ -143,6 +165,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
|
|||
@playAction(@actionQueue.splice(0,1)[0]) if @actionQueue.length
|
||||
|
||||
playAction: (action) ->
|
||||
return if @isRaster
|
||||
@currentAction = action
|
||||
return @hide() unless action.animation or action.container or action.relatedActions
|
||||
@show()
|
||||
|
@ -245,15 +268,22 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
|
|||
@hasMoved = true
|
||||
|
||||
updateScale: ->
|
||||
return unless @imageObject
|
||||
if @thangType.get('matchWorldDimensions') and @thang
|
||||
if @thang.width isnt @lastThangWidth or @thang.height isnt @lastThangHeight
|
||||
[@lastThangWidth, @lastThangHeight] = [@thang.width, @thang.height]
|
||||
bounds = @imageObject.getBounds()
|
||||
return unless bounds
|
||||
@imageObject.scaleX = @thang.width * Camera.PPM / bounds.width
|
||||
@imageObject.scaleY = @thang.height * Camera.PPM * @options.camera.y2x / bounds.height
|
||||
@imageObject.regX ?= 0
|
||||
@imageObject.regX += bounds.width / 2
|
||||
@imageObject.regY ?= 0
|
||||
@imageObject.regY += bounds.height / 2
|
||||
|
||||
unless @thang.spriteName is 'Beam'
|
||||
@imageObject.scaleX *= @thangType.get('scale') ? 1
|
||||
@imageObject.scaleY *= @thangType.get('scale') ? 1
|
||||
[@lastThangWidth, @lastThangHeight] = [@thang.width, @thang.height]
|
||||
return
|
||||
scaleX = if @getActionProp 'flipX' then -1 else 1
|
||||
scaleY = if @getActionProp 'flipY' then -1 else 1
|
||||
|
@ -270,6 +300,12 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
|
|||
angle = -angle if angle < 0
|
||||
angle = 180 - angle if angle > 90
|
||||
scaleX = 0.5 + 0.5 * (90 - angle) / 90
|
||||
|
||||
if @isRaster # scale is worked into building the sprite sheet for animations
|
||||
scale = @thangType.get('scale') or 1
|
||||
scaleX *= scale
|
||||
scaleY *= scale
|
||||
|
||||
scaleFactorX = @thang.scaleFactorX ? @scaleFactor
|
||||
scaleFactorY = @thang.scaleFactorY ? @scaleFactor
|
||||
@imageObject.scaleX = @originalScaleX * scaleX * scaleFactorX
|
||||
|
@ -322,6 +358,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
|
|||
|
||||
##################################################
|
||||
updateAction: ->
|
||||
return if @isRaster
|
||||
action = @determineAction()
|
||||
isDifferent = action isnt @currentRootAction or action is null
|
||||
if not action and @thang?.actionActivated and not @stopLogging
|
||||
|
@ -443,10 +480,11 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
|
|||
def = x: 0, y: {registration: 0, torso: -50, mouth: -60, aboveHead: -100}[prop]
|
||||
pos = @getActionProp 'positions', prop, def
|
||||
pos = x: pos.x, y: pos.y
|
||||
scale = @getActionProp 'scale', null, 1
|
||||
scale *= @options.resolutionFactor if prop is 'registration'
|
||||
pos.x *= scale
|
||||
pos.y *= scale
|
||||
if not @isRaster
|
||||
scale = @getActionProp 'scale', null, 1
|
||||
scale *= @options.resolutionFactor if prop is 'registration'
|
||||
pos.x *= scale
|
||||
pos.y *= scale
|
||||
if @thang and prop isnt 'registration'
|
||||
scaleFactor = @thang.scaleFactor ? 1
|
||||
pos.x *= @thang.scaleFactorX ? scaleFactor
|
||||
|
@ -658,7 +696,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
|
|||
z = @shadow.pos.z
|
||||
@shadow.pos = pos
|
||||
@shadow.pos.z = z
|
||||
@imageObject.gotoAndPlay(endAnimation)
|
||||
@imageObject.gotoAndPlay?(endAnimation)
|
||||
return
|
||||
|
||||
@shadow.action = 'move'
|
||||
|
|
|
@ -101,4 +101,5 @@ module.exports = class Layer extends createjs.Container
|
|||
cache: ->
|
||||
return unless @children.length
|
||||
bounds = @getBounds()
|
||||
return unless bounds
|
||||
super bounds.x, bounds.y, bounds.width, bounds.height, 2
|
||||
|
|
|
@ -200,7 +200,7 @@ module.exports = class Mark extends CocoClass
|
|||
Backbone.Mediator.publish 'sprite:loaded'
|
||||
|
||||
update: (pos=null) ->
|
||||
return false unless @on and @mark
|
||||
return false unless @on and @mark and @sprite?.thangType.isFullyLoaded()
|
||||
@mark.visible = not @hidden
|
||||
@updatePosition pos
|
||||
@updateRotation()
|
||||
|
@ -242,7 +242,7 @@ module.exports = class Mark extends CocoClass
|
|||
oldMark.parent.removeChild oldMark
|
||||
return unless @name in ["selection", "target", "repair", "highlight"]
|
||||
scale = 0.5
|
||||
if @sprite
|
||||
if @sprite?.imageObject
|
||||
size = @sprite.getAverageDimension()
|
||||
size += 60 if @name is 'selection'
|
||||
size += 60 if @name is 'repair'
|
||||
|
|
|
@ -47,7 +47,7 @@ module.exports = class SpriteBoss extends CocoClass
|
|||
toString: -> "<SpriteBoss: #{@spriteArray.length} sprites>"
|
||||
|
||||
thangTypeFor: (type) ->
|
||||
_.find @options.thangTypes, (m) -> m.get('original') is type or m.get('name') is type
|
||||
_.find @options.thangTypes, (m) -> m.get('actions') and m.get('original') is type or m.get('name') is type
|
||||
|
||||
createLayers: ->
|
||||
@spriteLayers = {}
|
||||
|
@ -144,7 +144,11 @@ module.exports = class SpriteBoss extends CocoClass
|
|||
|
||||
addThangToSprites: (thang, layer=null) ->
|
||||
return console.warn 'Tried to add Thang to the surface it already has:', thang.id if @sprites[thang.id]
|
||||
thangType = _.find @options.thangTypes, (m) -> m.get('name') is thang.spriteName
|
||||
thangType = _.find @options.thangTypes, (m) ->
|
||||
return false unless m.get('actions') or m.get('raster')
|
||||
return m.get('name') is thang.spriteName
|
||||
thangType ?= _.find @options.thangTypes, (m) -> return m.get('name') is thang.spriteName
|
||||
|
||||
options = @createSpriteOptions thang: thang
|
||||
options.resolutionFactor = if thangType.get('kind') is 'Floor' then 2 else 4
|
||||
sprite = new CocoSprite thangType, options
|
||||
|
@ -196,6 +200,8 @@ module.exports = class SpriteBoss extends CocoClass
|
|||
cache: (update=false) ->
|
||||
return if @cached and not update
|
||||
wallSprites = (sprite for sprite in @spriteArray when sprite.thangType?.get('name').search(/(dungeon|indoor).wall/i) isnt -1)
|
||||
unless _.all (s.thangType.isFullyLoaded() for s in wallSprites)
|
||||
return
|
||||
walls = (sprite.thang for sprite in wallSprites)
|
||||
@world.calculateBounds()
|
||||
wallGrid = new Grid walls, @world.size()...
|
||||
|
|
|
@ -84,6 +84,8 @@ module.exports = Surface = class Surface extends CocoClass
|
|||
@initAudio()
|
||||
@onResize = _.debounce @onResize, 500
|
||||
$(window).on 'resize', @onResize
|
||||
if @world.ended
|
||||
_.defer => @setWorld @world
|
||||
|
||||
destroy: ->
|
||||
@dead = true
|
||||
|
|
|
@ -213,6 +213,9 @@ module.exports = class GoalManager extends CocoClass
|
|||
else
|
||||
state[progressObjectName][thang] = false
|
||||
|
||||
getGoalState: (goalID) ->
|
||||
@goalStates[goalID].status
|
||||
|
||||
setGoalState: (goalID, status) ->
|
||||
state = @goalStates[goalID]
|
||||
state.status = status
|
||||
|
|
|
@ -38,6 +38,9 @@ module.exports = class Thang
|
|||
event.thang = @
|
||||
@world.publishNote channel, event
|
||||
|
||||
getGoalState: (goalID) ->
|
||||
@world.getGoalState goalID
|
||||
|
||||
setGoalState: (goalID, status) ->
|
||||
@world.setGoalState goalID, status
|
||||
|
||||
|
|
|
@ -242,6 +242,9 @@ module.exports = class World
|
|||
return unless @goalManager
|
||||
@goalManager.submitWorldGenerationEvent(channel, event, @frames.length)
|
||||
|
||||
getGoalState: (goalID) ->
|
||||
@goalManager.getGoalState(goalID)
|
||||
|
||||
setGoalState: (goalID, status) ->
|
||||
@goalManager.setGoalState(goalID, status)
|
||||
|
||||
|
|
|
@ -120,7 +120,7 @@
|
|||
forum_suffix: " instead."
|
||||
send: "Send Feedback"
|
||||
contact_candidate: "Contact Candidate"
|
||||
recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 18% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns."
|
||||
recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns."
|
||||
|
||||
diplomat_suggestion:
|
||||
title: "Help translate CodeCombat!"
|
||||
|
@ -699,6 +699,7 @@
|
|||
user_schema: "User Schema"
|
||||
user_profile: "User Profile"
|
||||
patches: "Patches"
|
||||
patched_model: "Source Document"
|
||||
model: "Model"
|
||||
system: "System"
|
||||
component: "Component"
|
||||
|
@ -709,10 +710,12 @@
|
|||
opponent_session: "Opponent Session"
|
||||
article: "Article"
|
||||
user_names: "User Names"
|
||||
thang_names: "Thang Names"
|
||||
files: "Files"
|
||||
top_simulators: "Top Simulators"
|
||||
source_document: "Source Document"
|
||||
document: "Document" # note to diplomats: not a physical document, a document in MongoDB, ie a record in a database
|
||||
sprite_sheet: "Sprite Sheet"
|
||||
|
||||
delta:
|
||||
added: "Added"
|
||||
|
|
|
@ -37,6 +37,8 @@ class CocoModel extends Backbone.Model
|
|||
@loading = false
|
||||
@markToRevert()
|
||||
@loadFromBackup()
|
||||
|
||||
getNormalizedURL: -> "#{@urlRoot}/#{@id}"
|
||||
|
||||
set: ->
|
||||
res = super(arguments...)
|
||||
|
@ -76,9 +78,9 @@ class CocoModel extends Backbone.Model
|
|||
return super attrs, options
|
||||
|
||||
fetch: ->
|
||||
res = super(arguments...)
|
||||
@jqxhr = super(arguments...)
|
||||
@loading = true
|
||||
res
|
||||
@jqxhr
|
||||
|
||||
markToRevert: ->
|
||||
if @type() is 'ThangType'
|
||||
|
|
|
@ -5,6 +5,7 @@ module.exports = class SuperModel extends Backbone.Model
|
|||
@progress = 0
|
||||
@resources = {}
|
||||
@rid = 0
|
||||
@maxProgress = 1
|
||||
|
||||
@models = {}
|
||||
@collections = {}
|
||||
|
@ -19,7 +20,6 @@ module.exports = class SuperModel extends Backbone.Model
|
|||
loadModel: (model, name, fetchOptions, value=1) ->
|
||||
cachedModel = @getModelByURL(model.getURL())
|
||||
if cachedModel
|
||||
console.debug 'Model cache hit', cachedModel.getURL(), 'already loaded', cachedModel.loaded
|
||||
if cachedModel.loaded
|
||||
res = @addModelResource(cachedModel, name, fetchOptions, 0)
|
||||
res.markLoaded()
|
||||
|
@ -96,7 +96,7 @@ module.exports = class SuperModel extends Backbone.Model
|
|||
@registerCollection(collection)
|
||||
|
||||
registerCollection: (collection) ->
|
||||
@collections[collection.getURL()] = collection
|
||||
@collections[collection.getURL()] = collection if collection.isCachable
|
||||
# consolidate models
|
||||
for model, i in collection.models
|
||||
cachedModel = @getModelByURL(model.getURL())
|
||||
|
@ -141,7 +141,7 @@ module.exports = class SuperModel extends Backbone.Model
|
|||
@listenToOnce(resource, 'loaded', @onResourceLoaded)
|
||||
@listenTo(resource, 'failed', @onResourceFailed)
|
||||
@denom += value
|
||||
@updateProgress() if @denom
|
||||
_.defer @updateProgress if @denom
|
||||
|
||||
onResourceLoaded: (r) ->
|
||||
@num += r.value
|
||||
|
@ -155,11 +155,18 @@ module.exports = class SuperModel extends Backbone.Model
|
|||
# a bunch of things load all at once.
|
||||
# So make sure we only emit events if @progress has changed.
|
||||
newProg = if @denom then @num / @denom else 1
|
||||
return if @progress is newProg
|
||||
newProg = Math.min @maxProgress, newProg
|
||||
return if @progress >= newProg
|
||||
@progress = newProg
|
||||
@trigger('update-progress', @progress)
|
||||
@trigger('loaded-all') if @finished()
|
||||
|
||||
|
||||
setMaxProgress: (@maxProgress) ->
|
||||
resetProgress: -> @progress = 0
|
||||
clearMaxProgress: ->
|
||||
@maxProgress = 1
|
||||
_.defer @updateProgress
|
||||
|
||||
getProgress: -> return @progress
|
||||
|
||||
getResource: (rid) ->
|
||||
|
@ -202,6 +209,7 @@ class ModelResource extends Resource
|
|||
super(name, value)
|
||||
@model = modelOrCollection
|
||||
@fetchOptions = fetchOptions
|
||||
@jqxhr = @model.jqxhr
|
||||
|
||||
load: ->
|
||||
@markLoading()
|
||||
|
|
|
@ -26,12 +26,18 @@ module.exports = class ThangType extends CocoModel
|
|||
@buildActions()
|
||||
@spriteSheets = {}
|
||||
@building = {}
|
||||
|
||||
isFullyLoaded: ->
|
||||
# TODO: Come up with a better way to identify when the model doesn't have everything needed to build the sprite. ie when it's a projection without all the required data.
|
||||
return @get('actions') or @get('raster') # needs one of these two things
|
||||
|
||||
getActions: ->
|
||||
return {} unless @isFullyLoaded()
|
||||
return @actions or @buildActions()
|
||||
|
||||
buildActions: ->
|
||||
@actions = $.extend(true, {}, @get('actions') or {})
|
||||
return null unless @isFullyLoaded()
|
||||
@actions = $.extend(true, {}, @get('actions'))
|
||||
for name, action of @actions
|
||||
action.name = name
|
||||
for relatedName, relatedAction of action.relatedActions ? {}
|
||||
|
@ -52,9 +58,12 @@ module.exports = class ThangType extends CocoModel
|
|||
options
|
||||
|
||||
buildSpriteSheet: (options) ->
|
||||
return false unless @isFullyLoaded()
|
||||
@options = @fillOptions options
|
||||
key = @spriteSheetKey(@options)
|
||||
if ss = @spriteSheets[key] then return ss
|
||||
return if @building[key]
|
||||
@t0 = new Date().getTime()
|
||||
@initBuild(options)
|
||||
@addGeneralFrames() unless @options.portraitOnly
|
||||
@addPortrait()
|
||||
|
@ -144,9 +153,8 @@ module.exports = class ThangType extends CocoModel
|
|||
@builder.buildAsync() unless buildQueue.length > 1
|
||||
@builder.on 'complete', @onBuildSpriteSheetComplete, @, true, key
|
||||
return true
|
||||
t0 = new Date()
|
||||
spriteSheet = @builder.build()
|
||||
console.warn "Built #{@get('name')} in #{new Date() - t0}ms on main thread."
|
||||
console.debug "Built #{@get('name')} in #{new Date().getTime() - @t0}ms."
|
||||
@spriteSheets[key] = spriteSheet
|
||||
delete @building[key]
|
||||
spriteSheet
|
||||
|
@ -180,6 +188,7 @@ module.exports = class ThangType extends CocoModel
|
|||
stage?.toDataURL()
|
||||
|
||||
getPortraitStage: (spriteOptionsOrKey, size=100) ->
|
||||
return unless @isFullyLoaded()
|
||||
key = spriteOptionsOrKey
|
||||
key = if _.isString(key) then key else @spriteSheetKey(@fillOptions(key))
|
||||
spriteSheet = @spriteSheets[key]
|
||||
|
@ -210,8 +219,8 @@ module.exports = class ThangType extends CocoModel
|
|||
@tick = null
|
||||
stage
|
||||
|
||||
uploadGenericPortrait: (callback) ->
|
||||
src = @getPortraitSource()
|
||||
uploadGenericPortrait: (callback, src) ->
|
||||
src ?= @getPortraitSource()
|
||||
return callback?() unless src
|
||||
src = src.replace('data:image/png;base64,', '').replace(/\ /g, '+')
|
||||
body =
|
||||
|
|
|
@ -123,6 +123,7 @@ _.extend ThangTypeSchema.properties,
|
|||
title: 'Scale'
|
||||
type: 'number'
|
||||
positions: PositionsSchema
|
||||
raster: { type: 'string', format: 'image-file', title: 'Raster Image' }
|
||||
colorGroups: c.object
|
||||
title: 'Color Groups'
|
||||
additionalProperties:
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
.patches-view
|
||||
.status-buttons
|
||||
margin-bottom: 10px
|
||||
|
||||
.patch-icon
|
||||
cursor: pointer
|
||||
|
|
|
@ -4,7 +4,7 @@ block modal-header-content
|
|||
h3(data-i18n="contact.contact_candidate") Contact Candidate
|
||||
|
||||
block modal-body-content
|
||||
p(data-i18n="contact.recruitment_reminder") Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 18% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns.
|
||||
p(data-i18n="contact.recruitment_reminder") Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns.
|
||||
.form
|
||||
.form-group
|
||||
label.control-label(for="contact-email", data-i18n="general.email") Email
|
||||
|
|
|
@ -8,7 +8,7 @@ module.exports = class PatchModal extends ModalView
|
|||
template: template
|
||||
plain: true
|
||||
modalWidthPercent: 60
|
||||
|
||||
|
||||
events:
|
||||
'click #withdraw-button': 'withdrawPatch'
|
||||
'click #reject-button': 'rejectPatch'
|
||||
|
@ -22,7 +22,7 @@ module.exports = class PatchModal extends ModalView
|
|||
else
|
||||
@originalSource = new @targetModel.constructor({_id:targetID})
|
||||
@supermodel.loadModel @originalSource, 'source_document'
|
||||
|
||||
|
||||
getRenderData: ->
|
||||
c = super()
|
||||
c.isPatchCreator = @patch.get('creator') is auth.me.id
|
||||
|
@ -30,7 +30,7 @@ module.exports = class PatchModal extends ModalView
|
|||
c.status = @patch.get 'status'
|
||||
c.patch = @patch
|
||||
c
|
||||
|
||||
|
||||
afterRender: ->
|
||||
return unless @supermodel.finished()
|
||||
headModel = null
|
||||
|
@ -38,7 +38,7 @@ module.exports = class PatchModal extends ModalView
|
|||
headModel = @originalSource.clone(false)
|
||||
headModel.set(@targetModel.attributes)
|
||||
headModel.loaded = true
|
||||
|
||||
|
||||
pendingModel = @originalSource.clone(false)
|
||||
pendingModel.applyDelta(@patch.get('delta'))
|
||||
pendingModel.loaded = true
|
||||
|
@ -47,18 +47,18 @@ module.exports = class PatchModal extends ModalView
|
|||
changeEl = @$el.find('.changes-stub')
|
||||
@insertSubView(@deltaView, changeEl)
|
||||
super()
|
||||
|
||||
|
||||
acceptPatch: ->
|
||||
delta = @deltaView.getApplicableDelta()
|
||||
@targetModel.applyDelta(delta)
|
||||
@patch.setStatus('accepted')
|
||||
@trigger 'accepted-patch'
|
||||
@hide()
|
||||
|
||||
|
||||
rejectPatch: ->
|
||||
@patch.setStatus('rejected')
|
||||
@hide()
|
||||
|
||||
|
||||
withdrawPatch: ->
|
||||
@patch.setStatus('withdrawn')
|
||||
@hide()
|
||||
@hide()
|
||||
|
|
|
@ -8,7 +8,7 @@ module.exports = class PatchesView extends CocoView
|
|||
template: template
|
||||
className: 'patches-view'
|
||||
status: 'pending'
|
||||
|
||||
|
||||
events:
|
||||
'change .status-buttons': 'onStatusButtonsChanged'
|
||||
'click .patch-icon': 'openPatchModal'
|
||||
|
@ -16,16 +16,16 @@ module.exports = class PatchesView extends CocoView
|
|||
constructor: (@model, options) ->
|
||||
super(options)
|
||||
@initPatches()
|
||||
|
||||
|
||||
initPatches: ->
|
||||
@startedLoading = false
|
||||
@patches = new PatchesCollection([], {}, @model, @status)
|
||||
|
||||
|
||||
load: ->
|
||||
@initPatches()
|
||||
@patches = @supermodel.loadCollection(@patches, 'patches').model
|
||||
@listenTo @patches, 'sync', @onPatchesLoaded
|
||||
|
||||
|
||||
onPatchesLoaded: ->
|
||||
ids = (p.get('creator') for p in @patches.models)
|
||||
jqxhrOptions = nameLoader.loadNames ids
|
||||
|
@ -37,19 +37,20 @@ module.exports = class PatchesView extends CocoView
|
|||
c.patches = @patches.models
|
||||
c.status
|
||||
c
|
||||
|
||||
|
||||
afterRender: ->
|
||||
@$el.find(".#{@status}").addClass 'active'
|
||||
|
||||
onStatusButtonsChanged: (e) ->
|
||||
@status = $(e.target).val()
|
||||
@reloadPatches()
|
||||
|
||||
|
||||
reloadPatches: ->
|
||||
@load()
|
||||
@render()
|
||||
|
||||
openPatchModal: (e) ->
|
||||
console.log "open patch modal"
|
||||
patch = _.find @patches.models, {id:$(e.target).data('patch-id')}
|
||||
modal = new PatchModal(patch, @model)
|
||||
@openModalView(modal)
|
||||
|
|
|
@ -197,6 +197,7 @@ module.exports = class ThangTypeEditView extends View
|
|||
# animation select
|
||||
|
||||
refreshAnimation: ->
|
||||
return @showRasterImage() if @thangType.get('raster')
|
||||
options = @getSpriteOptions()
|
||||
@thangType.resetSpriteSheetCache()
|
||||
spriteSheet = @thangType.buildSpriteSheet(options)
|
||||
|
@ -207,6 +208,13 @@ module.exports = class ThangTypeEditView extends View
|
|||
@showAnimation()
|
||||
@updatePortrait()
|
||||
|
||||
showRasterImage: ->
|
||||
sprite = new CocoSprite(@thangType, @getSpriteOptions())
|
||||
@currentSprite?.destroy()
|
||||
@currentSprite = sprite
|
||||
@showDisplayObject(sprite.displayObject)
|
||||
@updateScale()
|
||||
|
||||
showAnimation: (animationName) ->
|
||||
animationName = @$el.find('#animations-select').val() unless _.isString animationName
|
||||
return unless animationName
|
||||
|
@ -310,8 +318,13 @@ module.exports = class ThangTypeEditView extends View
|
|||
|
||||
res.success =>
|
||||
url = "/editor/thang/#{newThangType.get('slug') or newThangType.id}"
|
||||
newThangType.uploadGenericPortrait ->
|
||||
document.location.href = url
|
||||
portraitSource = null
|
||||
if @thangType.get('raster')
|
||||
image = @currentSprite.imageObject.image
|
||||
portraitSource = imageToPortrait image
|
||||
# bit of a hacky way to get that portrait
|
||||
success = -> document.location.href = url
|
||||
newThangType.uploadGenericPortrait success, portraitSource
|
||||
|
||||
clearRawData: ->
|
||||
@thangType.resetRawData()
|
||||
|
@ -393,3 +406,14 @@ module.exports = class ThangTypeEditView extends View
|
|||
destroy: ->
|
||||
@camera?.destroy()
|
||||
super()
|
||||
|
||||
imageToPortrait = (img) ->
|
||||
canvas = document.createElement("canvas")
|
||||
canvas.width = 100
|
||||
canvas.height = 100
|
||||
ctx = canvas.getContext("2d")
|
||||
scaleX = 100 / img.width
|
||||
scaleY = 100 / img.height
|
||||
ctx.scale scaleX, scaleY
|
||||
ctx.drawImage img, 0, 0
|
||||
canvas.toDataURL("image/png")
|
|
@ -185,6 +185,6 @@ module.exports = class EmployersView extends View
|
|||
else
|
||||
window.location.hash = id
|
||||
url = "/account/profile/#{id}"
|
||||
app.router.navigate url, {trigger: true}
|
||||
window.open url,"_blank"
|
||||
else
|
||||
@openModalView new EmployerSignupView
|
||||
|
|
|
@ -182,8 +182,15 @@ module.exports = class MyMatchesTabView extends CocoView
|
|||
failure = (jqxhr, textStatus, errorThrown) =>
|
||||
console.log jqxhr.responseText
|
||||
@setRankingButtonText(button, 'failed')
|
||||
transpiledCode = @transpileSession session
|
||||
|
||||
ajaxData =
|
||||
session: sessionID
|
||||
levelID: @level.id
|
||||
originalLevelID: @level.attributes.original
|
||||
levelMajorVersion: @level.attributes.version.major
|
||||
transpiledCode: transpiledCode
|
||||
|
||||
ajaxData = {session: sessionID, levelID: @level.id, originalLevelID: @level.attributes.original, levelMajorVersion: @level.attributes.version.major}
|
||||
$.ajax '/queue/scoring', {
|
||||
type: 'POST'
|
||||
data: ajaxData
|
||||
|
@ -191,6 +198,28 @@ module.exports = class MyMatchesTabView extends CocoView
|
|||
error: failure
|
||||
}
|
||||
|
||||
transpileSession: (session) ->
|
||||
submittedCode = session.get('code')
|
||||
transpiledCode = {}
|
||||
for thang, spells of submittedCode
|
||||
transpiledCode[thang] = {}
|
||||
for spellID, spell of spells
|
||||
#DRY this
|
||||
aetherOptions =
|
||||
problems: {}
|
||||
language: "javascript"
|
||||
functionName: spellID
|
||||
functionParameters: []
|
||||
yieldConditionally: spellID is "plan"
|
||||
globals: ['Vector', '_']
|
||||
protectAPI: true
|
||||
includeFlow: false
|
||||
if spellID is "hear" then aetherOptions["functionParameters"] = ["speaker","message","data"]
|
||||
|
||||
aether = new Aether aetherOptions
|
||||
transpiledCode[thang][spellID] = aether.transpile spell
|
||||
transpiledCode
|
||||
|
||||
setRankingButtonText: (rankButton, spanClass) ->
|
||||
rankButton.find('span').addClass('hidden')
|
||||
rankButton.find(".#{spanClass}").removeClass('hidden')
|
||||
|
|
|
@ -109,19 +109,29 @@ module.exports = class HUDView extends View
|
|||
@update()
|
||||
|
||||
createAvatar: (thangType, thang, colorConfig) ->
|
||||
unless thangType.isFullyLoaded()
|
||||
args = arguments
|
||||
unless @listeningToCreateAvatar
|
||||
@listenToOnce thangType, 'sync', -> @createAvatar(args...)
|
||||
@listeningToCreateAvatar = true
|
||||
return
|
||||
@listeningToCreateAvatar = false
|
||||
options = thang.getSpriteOptions() or {}
|
||||
options.async = false
|
||||
options.colorConfig = colorConfig if colorConfig
|
||||
stage = thangType.getPortraitStage options
|
||||
wrapper = @$el.find '.thang-canvas-wrapper'
|
||||
newCanvas = $(stage.canvas).addClass('thang-canvas')
|
||||
wrapper.empty().append(newCanvas)
|
||||
team = @thang?.team or @speakerSprite?.thang?.team
|
||||
wrapper.removeClass (i, css) -> (css.match(/\bteam-\S+/g) or []).join ' '
|
||||
wrapper.addClass "team-#{team}"
|
||||
stage.update()
|
||||
@stage?.stopTalking()
|
||||
@stage = stage
|
||||
if thangType.get('raster')
|
||||
wrapper.empty().append($('<img />').attr('src', '/file/'+thangType.get('raster')))
|
||||
else
|
||||
stage = thangType.getPortraitStage options
|
||||
newCanvas = $(stage.canvas).addClass('thang-canvas')
|
||||
wrapper.empty().append(newCanvas)
|
||||
stage.update()
|
||||
@stage?.stopTalking()
|
||||
@stage = stage
|
||||
|
||||
onThangBeganTalking: (e) ->
|
||||
return unless @stage and @thang is e.thang
|
||||
|
|
|
@ -14,16 +14,28 @@ module.exports = class ThangAvatarView extends View
|
|||
super options
|
||||
@thang = options.thang
|
||||
@includeName = options.includeName
|
||||
@thangType = @getSpriteThangType()
|
||||
if not @thangType
|
||||
console.error 'Thang avatar view expected a thang type to be provided.'
|
||||
return
|
||||
|
||||
unless @thangType.isFullyLoaded() or @thangType.loading
|
||||
@thangType.fetch()
|
||||
|
||||
@supermodel.loadModel @thangType, 'thang'
|
||||
|
||||
getSpriteThangType: ->
|
||||
thangs = @supermodel.getModels(ThangType)
|
||||
thangs = (t for t in thangs when t.get('name') is @thang.spriteName)
|
||||
loadedThangs = (t for t in thangs when t.isFullyLoaded())
|
||||
return loadedThangs[0] or thangs[0] # try to return one with all the goods, otherwise a projection
|
||||
|
||||
getRenderData: (context={}) ->
|
||||
context = super context
|
||||
context.thang = @thang
|
||||
thangs = @supermodel.getModels(ThangType)
|
||||
thangs = (t for t in thangs when t.get('name') is @thang.spriteName)
|
||||
thang = thangs[0]
|
||||
options = @thang?.getSpriteOptions() or {}
|
||||
options.async = false
|
||||
context.avatarURL = thang.getPortraitSource(options)
|
||||
context.avatarURL = @thangType.getPortraitSource(options) unless @thangType.loading
|
||||
context.includeName = @includeName
|
||||
context
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ module.exports = class SpellView extends View
|
|||
@createFirepad()
|
||||
else
|
||||
# needs to happen after the code generating this view is complete
|
||||
setTimeout @onAllLoaded, 1
|
||||
_.defer @onAllLoaded
|
||||
|
||||
createACE: ->
|
||||
# Test themes and settings here: http://ace.ajax.org/build/kitchen-sink.html
|
||||
|
|
|
@ -60,7 +60,6 @@ module.exports = class PlayLevelView extends View
|
|||
'surface:world-set-up': 'onSurfaceSetUpNewWorld'
|
||||
'level:session-will-save': 'onSessionWillSave'
|
||||
'level:set-team': 'setTeam'
|
||||
'god:new-world-created': 'loadSoundsForWorld'
|
||||
'level:started': 'onLevelStarted'
|
||||
'level:loading-view-unveiled': 'onLoadingViewUnveiled'
|
||||
|
||||
|
@ -83,7 +82,6 @@ module.exports = class PlayLevelView extends View
|
|||
@sessionID = @getQueryVariable 'session'
|
||||
|
||||
$(window).on('resize', @onWindowResize)
|
||||
@listenToOnce(@supermodel, 'error', @onLevelLoadError)
|
||||
@saveScreenshot = _.throttle @saveScreenshot, 30000
|
||||
|
||||
if @isEditorPreview
|
||||
|
@ -102,7 +100,6 @@ module.exports = class PlayLevelView extends View
|
|||
@supermodel.models = givenSupermodel.models
|
||||
@supermodel.collections = givenSupermodel.collections
|
||||
@supermodel.shouldSaveBackups = givenSupermodel.shouldSaveBackups
|
||||
|
||||
@god?.level = @level.serialize @supermodel
|
||||
if @world
|
||||
serializedLevel = @level.serialize(@supermodel)
|
||||
|
@ -133,6 +130,9 @@ module.exports = class PlayLevelView extends View
|
|||
|
||||
updateProgress: (progress) ->
|
||||
super(progress)
|
||||
if not @worldInitialized and @levelLoader.session.loaded and @levelLoader.level.loaded and @levelLoader.world and (not @levelLoader.opponentSession or @levelLoader.opponentSession.loaded)
|
||||
@grabLevelLoaderData()
|
||||
@onWorldInitialized()
|
||||
return if @seenDocs
|
||||
return unless @levelLoader.session.loaded and @levelLoader.level.loaded
|
||||
return unless showFrequency = @levelLoader.level.get('showsGuide')
|
||||
|
@ -144,6 +144,21 @@ module.exports = class PlayLevelView extends View
|
|||
return unless article.loaded
|
||||
@showGuide()
|
||||
|
||||
onWorldInitialized: ->
|
||||
@worldInitialized = true
|
||||
team = @getQueryVariable("team") ? @world.teamForPlayer(0)
|
||||
@loadOpponentTeam(team)
|
||||
@god.setLevel @level.serialize @supermodel
|
||||
@god.setWorldClassMap @world.classMap
|
||||
@setTeam team
|
||||
@initGoalManager()
|
||||
@insertSubviews ladderGame: (@level.get('type') is "ladder")
|
||||
@initVolume()
|
||||
@listenTo(@session, 'change:multiplayer', @onMultiplayerChanged)
|
||||
@originalSessionState = $.extend(true, {}, @session.get('state'))
|
||||
@register()
|
||||
@controlBar.setBus(@bus)
|
||||
|
||||
showGuide: ->
|
||||
@seenDocs = true
|
||||
DocsModal = require './level/modal/docs_modal'
|
||||
|
@ -165,33 +180,16 @@ module.exports = class PlayLevelView extends View
|
|||
if not (@levelLoader.level.get('type') in ['ladder', 'ladder-tutorial'])
|
||||
me.set('lastLevel', @levelID)
|
||||
me.save()
|
||||
@grabLevelLoaderData()
|
||||
team = @getQueryVariable("team") ? @world.teamForPlayer(0)
|
||||
@loadOpponentTeam(team)
|
||||
@god.setLevel @level.serialize @supermodel
|
||||
@god.setWorldClassMap @world.classMap
|
||||
@setTeam team
|
||||
@levelLoader.destroy()
|
||||
@levelLoader = null
|
||||
@initSurface()
|
||||
@initGoalManager()
|
||||
@initScriptManager()
|
||||
@insertSubviews()
|
||||
@initVolume()
|
||||
@listenTo(@session, 'change:multiplayer', @onMultiplayerChanged)
|
||||
@originalSessionState = $.extend(true, {}, @session.get('state'))
|
||||
@register()
|
||||
@controlBar.setBus(@bus)
|
||||
@surface.showLevel()
|
||||
if @otherSession
|
||||
# TODO: colorize name and cloud by team, colorize wizard by user's color config
|
||||
@surface.createOpponentWizard id: @otherSession.get('creator'), name: @otherSession.get('creatorName'), team: @otherSession.get('team')
|
||||
|
||||
grabLevelLoaderData: ->
|
||||
@session = @levelLoader.session
|
||||
@world = @levelLoader.world
|
||||
@level = @levelLoader.level
|
||||
@otherSession = @levelLoader.opponentSession
|
||||
@levelLoader.destroy()
|
||||
@levelLoader = null
|
||||
|
||||
loadOpponentTeam: (myTeam) ->
|
||||
opponentSpells = []
|
||||
|
@ -212,6 +210,10 @@ module.exports = class PlayLevelView extends View
|
|||
@session.set 'multiplayer', false
|
||||
|
||||
onLevelStarted: (e) ->
|
||||
@surface.showLevel()
|
||||
if @otherSession
|
||||
# TODO: colorize name and cloud by team, colorize wizard by user's color config
|
||||
@surface.createOpponentWizard id: @otherSession.get('creator'), name: @otherSession.get('creatorName'), team: @otherSession.get('team')
|
||||
@loadingView?.unveil()
|
||||
|
||||
onLoadingViewUnveiled: (e) ->
|
||||
|
@ -306,9 +308,6 @@ module.exports = class PlayLevelView extends View
|
|||
$('#level-done-button', @$el).hide()
|
||||
application.tracker?.trackEvent 'Confirmed Restart', level: @world.name, label: @world.name
|
||||
|
||||
onNewWorld: (e) ->
|
||||
@world = e.world
|
||||
|
||||
onInfiniteLoop: (e) ->
|
||||
return unless e.firstWorld
|
||||
@openModalView new InfiniteLoopModal()
|
||||
|
@ -481,11 +480,11 @@ module.exports = class PlayLevelView extends View
|
|||
|
||||
# Dynamic sound loading
|
||||
|
||||
loadSoundsForWorld: (e) ->
|
||||
onNewWorld: (e) ->
|
||||
return if @headless
|
||||
world = e.world
|
||||
@world = e.world
|
||||
thangTypes = @supermodel.getModels(ThangType)
|
||||
for [spriteName, message] in world.thangDialogueSounds()
|
||||
for [spriteName, message] in @world.thangDialogueSounds()
|
||||
continue unless thangType = _.find thangTypes, (m) -> m.get('name') is spriteName
|
||||
continue unless sound = AudioPlayer.soundForDialogue message, thangType.get('soundTriggers')
|
||||
AudioPlayer.preloadSoundReference sound
|
||||
|
|
|
@ -13,18 +13,16 @@ Aether.addGlobal 'Vector', require '../app/lib/world/vector'
|
|||
Aether.addGlobal '_', _
|
||||
|
||||
transpileLevelSession = (sessionID, cb) ->
|
||||
query = LevelSession
|
||||
.findOne("_id": sessionID)
|
||||
.select("submittedCode")
|
||||
.lean()
|
||||
query = LevelSession.findOne("_id": sessionID).select("submittedCode").lean()
|
||||
query.exec (err, session) ->
|
||||
if err then return cb err
|
||||
submittedCode = session.submittedCode
|
||||
transpiledCode = {}
|
||||
|
||||
console.log "Updating session #{sessionID}"
|
||||
for thang, spells of submittedCode
|
||||
transpiledCode[thang] = {}
|
||||
for spellID, spell of spells
|
||||
|
||||
aetherOptions =
|
||||
problems: {}
|
||||
language: "javascript"
|
||||
|
@ -34,15 +32,18 @@ transpileLevelSession = (sessionID, cb) ->
|
|||
globals: ['Vector', '_']
|
||||
protectAPI: true
|
||||
includeFlow: false
|
||||
if spellID is "hear" then aetherOptions["functionParameters"] = ["speaker","message","data"]
|
||||
|
||||
aether = new Aether aetherOptions
|
||||
transpiledCode[thang][spellID] = aether.transpile spell
|
||||
cb null
|
||||
conditions =
|
||||
"_id": sessionID
|
||||
update =
|
||||
"transpiledCode"
|
||||
query = LevelSession
|
||||
.update("_id")
|
||||
"transpiledCode": transpiledCode
|
||||
"submittedCodeLanguage": "javascript"
|
||||
query = LevelSession.update(conditions,update)
|
||||
|
||||
query.exec (err, numUpdated) -> cb err
|
||||
|
||||
findLadderLevelSessions = (levelID, cb) ->
|
||||
queryParameters =
|
||||
|
@ -50,18 +51,14 @@ findLadderLevelSessions = (levelID, cb) ->
|
|||
submitted: true
|
||||
|
||||
selectString = "_id"
|
||||
query = LevelSession
|
||||
.find(queryParameters)
|
||||
.select(selectString)
|
||||
.lean()
|
||||
query = LevelSession.find(queryParameters).select(selectString).lean()
|
||||
|
||||
query.exec (err, levelSessions) ->
|
||||
if err then return cb err
|
||||
levelSessionIDs = _.pluck levelSessions, "_id"
|
||||
transpileLevelSession levelSessionIDs[0], (err) ->
|
||||
throw err if err
|
||||
#async.each levelSessionIDs, transpileLevelSession, (err) ->
|
||||
# if err then return cb err
|
||||
# cb null
|
||||
async.eachSeries levelSessionIDs, transpileLevelSession, (err) ->
|
||||
if err then return cb err
|
||||
cb null
|
||||
|
||||
|
||||
transpileLadderSessions = ->
|
||||
|
@ -70,17 +67,14 @@ transpileLadderSessions = ->
|
|||
"version.isLatestMajor": true
|
||||
"version.isLatestMinor": true
|
||||
selectString = "original"
|
||||
query = Level
|
||||
.find(queryParameters)
|
||||
.select(selectString)
|
||||
.lean()
|
||||
query = Level.find(queryParameters).select(selectString).lean()
|
||||
|
||||
query.exec (err, ladderLevels) ->
|
||||
throw err if err
|
||||
ladderLevels = _.pluck ladderLevels, "original"
|
||||
findLadderLevelSessions ladderLevels[3], (err) ->
|
||||
async.eachSeries ladderLevels, findLadderLevelSessions, (err) ->
|
||||
throw err if err
|
||||
#async.each ladderLevels, findLadderLevelSessions, (err) ->
|
||||
# throw err if err
|
||||
|
||||
serverSetup.connectToDatabase()
|
||||
transpileLadderSessions()
|
||||
|
||||
|
|
|
@ -112,7 +112,7 @@ module.exports = class Handler
|
|||
ids = ids.split(',') if _.isString ids
|
||||
ids = _.uniq ids
|
||||
|
||||
project = {name:1}
|
||||
project = {name:1, original:1}
|
||||
sort = {'version.major':-1, 'version.minor':-1}
|
||||
|
||||
makeFunc = (id) =>
|
||||
|
@ -120,8 +120,8 @@ module.exports = class Handler
|
|||
criteria = {original:mongoose.Types.ObjectId(id)}
|
||||
@modelClass.findOne(criteria, project).sort(sort).exec (err, document) ->
|
||||
return done(err) if err
|
||||
callback(null, document?.toObject() or {})
|
||||
|
||||
callback(null, document?.toObject() or null)
|
||||
|
||||
funcs = {}
|
||||
for id in ids
|
||||
return errors.badInput(res, "Given an invalid id: #{id}") unless Handler.isID(id)
|
||||
|
@ -129,7 +129,7 @@ module.exports = class Handler
|
|||
|
||||
async.parallel funcs, (err, results) ->
|
||||
return errors.serverError err if err
|
||||
res.send results
|
||||
res.send (d for d in _.values(results) when d)
|
||||
res.end()
|
||||
|
||||
getPatchesFor: (req, res, id) ->
|
||||
|
|
|
@ -5,21 +5,22 @@ ThangTypeHandler = class ThangTypeHandler extends Handler
|
|||
modelClass: ThangType
|
||||
jsonSchema: require '../../../app/schemas/models/thang_type'
|
||||
editableProperties: [
|
||||
'name',
|
||||
'raw',
|
||||
'actions',
|
||||
'soundTriggers',
|
||||
'rotationType',
|
||||
'matchWorldDimensions',
|
||||
'shadow',
|
||||
'layerPriority',
|
||||
'staticImage',
|
||||
'scale',
|
||||
'positions',
|
||||
'snap',
|
||||
'components',
|
||||
'colorGroups',
|
||||
'name'
|
||||
'raw'
|
||||
'actions'
|
||||
'soundTriggers'
|
||||
'rotationType'
|
||||
'matchWorldDimensions'
|
||||
'shadow'
|
||||
'layerPriority'
|
||||
'staticImage'
|
||||
'scale'
|
||||
'positions'
|
||||
'snap'
|
||||
'components'
|
||||
'colorGroups'
|
||||
'kind'
|
||||
'raster'
|
||||
]
|
||||
|
||||
hasAccess: (req) ->
|
||||
|
|
|
@ -104,13 +104,14 @@ module.exports.createNewTask = (req, res) ->
|
|||
requestSessionID = req.body.session
|
||||
originalLevelID = req.body.originalLevelID
|
||||
currentLevelID = req.body.levelID
|
||||
transpiledCode = req.body.transpiledCode
|
||||
requestLevelMajorVersion = parseInt(req.body.levelMajorVersion)
|
||||
|
||||
async.waterfall [
|
||||
validatePermissions.bind(@,req,requestSessionID)
|
||||
fetchAndVerifyLevelType.bind(@,currentLevelID)
|
||||
fetchSessionObjectToSubmit.bind(@, requestSessionID)
|
||||
updateSessionToSubmit
|
||||
updateSessionToSubmit.bind(@, transpiledCode)
|
||||
fetchInitialSessionsToRankAgainst.bind(@, requestLevelMajorVersion, originalLevelID)
|
||||
generateAndSendTaskPairsToTheQueue
|
||||
], (err, successMessageObject) ->
|
||||
|
@ -163,10 +164,11 @@ fetchSessionObjectToSubmit = (sessionID, callback) ->
|
|||
query.exec (err, session) ->
|
||||
callback err, session?.toObject()
|
||||
|
||||
updateSessionToSubmit = (sessionToUpdate, callback) ->
|
||||
updateSessionToSubmit = (transpiledCode, sessionToUpdate, callback) ->
|
||||
sessionUpdateObject =
|
||||
submitted: true
|
||||
submittedCode: sessionToUpdate.code
|
||||
transpiledCode: transpiledCode
|
||||
submitDate: new Date()
|
||||
meanStrength: 25
|
||||
standardDeviation: 25/3
|
||||
|
|
Loading…
Reference in a new issue