Merge remote-tracking branch 'upstream/master'

This commit is contained in:
Barney 2015-02-13 12:34:37 +08:00
commit 295ecf53c2
24 changed files with 159 additions and 112 deletions

View file

@ -478,6 +478,7 @@ self.onWorldError = function onWorldError(error) {
else { else {
console.log("Non-UserCodeError:", error.toString() + "\n" + error.stack || error.stackTrace); console.log("Non-UserCodeError:", error.toString() + "\n" + error.stack || error.stackTrace);
self.postMessage({type: 'non-user-code-problem', problem: {message: error.toString()}}); self.postMessage({type: 'non-user-code-problem', problem: {message: error.toString()}});
return false;
} }
/* We don't actually have the recoverable property any more; hmm /* We don't actually have the recoverable property any more; hmm
if(!error.recoverable) { if(!error.recoverable) {

View file

@ -144,7 +144,7 @@ module.exports.getUTCDay = (offset=0) ->
# Fast, basic way to replace text in an element when you don't need much. # Fast, basic way to replace text in an element when you don't need much.
# http://stackoverflow.com/a/4962398/540620 # http://stackoverflow.com/a/4962398/540620
if document? if document?.createElement
dummy = document.createElement 'div' dummy = document.createElement 'div'
dummy.innerHTML = 'text' dummy.innerHTML = 'text'
TEXT = if dummy.textContent is 'text' then 'textContent' else 'innerText' TEXT = if dummy.textContent is 'text' then 'textContent' else 'innerText'
@ -155,7 +155,7 @@ if document?
# Add a stylesheet rule # Add a stylesheet rule
# http://stackoverflow.com/questions/524696/how-to-create-a-style-tag-with-javascript/26230472#26230472 # http://stackoverflow.com/questions/524696/how-to-create-a-style-tag-with-javascript/26230472#26230472
# Don't use wantonly, or we'll have to implement a simple mechanism for clearing out old rules. # Don't use wantonly, or we'll have to implement a simple mechanism for clearing out old rules.
if document? if document?.createElement
module.exports.injectCSS = ((doc) -> module.exports.injectCSS = ((doc) ->
# wrapper for all injected styles and temp el to create them # wrapper for all injected styles and temp el to create them
wrap = doc.createElement("div") wrap = doc.createElement("div")

View file

@ -91,18 +91,9 @@ module.exports = class LevelLoader extends CocoClass
loadDependenciesForSession: (session) -> loadDependenciesForSession: (session) ->
if me.id isnt session.get 'creator' if me.id isnt session.get 'creator'
session.patch = session.save = -> console.error "Not saving session, since we didn't create it." session.patch = session.save = -> console.error "Not saving session, since we didn't create it."
@loadCodeLanguagesForSession session
if session is @session if session is @session
codeLanguage = session.get('codeLanguage') or me.get('aceConfig')?.language or 'python'
modulePath = "vendor/aether-#{codeLanguage}"
loading = application.moduleLoader.load(modulePath)
if loading
@languageModuleResource = @supermodel.addSomethingResource 'language_module'
@listenTo application.moduleLoader, 'loaded', (e) ->
if e.id is modulePath
@languageModuleResource.markLoaded()
@stopListening application.moduleLoader
@addSessionBrowserInfo session @addSessionBrowserInfo session
# hero-ladder games require the correct session team in level:loaded # hero-ladder games require the correct session team in level:loaded
team = @team ? @session.get('team') team = @team ? @session.get('team')
Backbone.Mediator.publish 'level:loaded', level: @level, team: team Backbone.Mediator.publish 'level:loaded', level: @level, team: team
@ -138,6 +129,19 @@ module.exports = class LevelLoader extends CocoClass
if _.size(@sessionDependenciesRegistered) is 2 and @checkAllWorldNecessitiesRegisteredAndLoaded() if _.size(@sessionDependenciesRegistered) is 2 and @checkAllWorldNecessitiesRegisteredAndLoaded()
@onWorldNecessitiesLoaded() @onWorldNecessitiesLoaded()
loadCodeLanguagesForSession: (session) ->
codeLanguages = _.uniq _.filter [session.get('codeLanguage') or 'python', session.get('submittedCodeLanguage')]
for codeLanguage in codeLanguages
do (codeLanguage) =>
modulePath = "vendor/aether-#{codeLanguage}"
return unless application.moduleLoader?.load modulePath
languageModuleResource = @supermodel.addSomethingResource 'language_module'
onModuleLoaded = (e) ->
return unless e.id is modulePath
languageModuleResource.markLoaded()
@stopListening application.moduleLoader, 'loaded', onModuleLoaded # listenToOnce might work here instead, haven't tried
@listenTo application.moduleLoader, 'loaded', onModuleLoaded
addSessionBrowserInfo: (session) -> addSessionBrowserInfo: (session) ->
return unless me.id is session.get 'creator' return unless me.id is session.get 'creator'
return unless $.browser? return unless $.browser?

View file

@ -5,7 +5,7 @@ GoalManager = require 'lib/world/GoalManager'
God = require 'lib/God' God = require 'lib/God'
{createAetherOptions} = require 'lib/aether_utils' {createAetherOptions} = require 'lib/aether_utils'
SIMULATOR_VERSION = 1 SIMULATOR_VERSION = 3
simulatorInfo = {} simulatorInfo = {}
if $.browser if $.browser
@ -225,6 +225,12 @@ module.exports = class Simulator extends CocoClass
@god.setLevelSessionIDs (session.sessionID for session in @task.getSessions()) @god.setLevelSessionIDs (session.sessionID for session in @task.getSessions())
@god.setWorldClassMap @world.classMap @god.setWorldClassMap @world.classMap
@god.setGoalManager new GoalManager(@world, @level.get 'goals') @god.setGoalManager new GoalManager(@world, @level.get 'goals')
humanFlagHistory = _.filter @session.get('state')?.flagHistory ? [], (event) => event.source isnt 'code' and event.team is (@session.get('team') ? 'humans')
ogreFlagHistory = _.filter @otherSession.get('state')?.flagHistory ? [], (event) => event.source isnt 'code' and event.team is (@otherSession.get('team') ? 'ogres')
@god.lastFlagHistory = humanFlagHistory.concat ogreFlagHistory
#console.log 'got flag history', @god.lastFlagHistory, 'from', humanFlagHistory, ogreFlagHistory, @session.get('state'), @otherSession.get('state')
@god.lastSubmissionCount = 0 # TODO: figure out how to combine submissionCounts from both players so we can use submissionCount random seeds again.
@god.lastDifficulty = 0
commenceSimulationAndSetupCallback: -> commenceSimulationAndSetupCallback: ->
Backbone.Mediator.subscribeOnce 'god:infinite-loop', @onInfiniteLoop, @ Backbone.Mediator.subscribeOnce 'god:infinite-loop', @onInfiniteLoop, @
@ -328,6 +334,7 @@ module.exports = class Simulator extends CocoClass
calculationTime: 500 calculationTime: 500
sessions: [] sessions: []
simulator: @simulator simulator: @simulator
randomSeed: @task.world.randomSeed
for session in @task.getSessions() for session in @task.getSessions()
sessionResult = sessionResult =

View file

@ -474,6 +474,8 @@ module.exports = Lank = class Lank extends CocoClass
bar.scaleX = healthPct / @options.floatingLayer.resolutionFactor bar.scaleX = healthPct / @options.floatingLayer.resolutionFactor
if @thang.showsName if @thang.showsName
@setNameLabel(if @thang.health <= 0 then '' else @thang.id) @setNameLabel(if @thang.health <= 0 then '' else @thang.id)
else if @options.playerName
@setNameLabel @options.playerName
configureMouse: -> configureMouse: ->
@sprite.cursor = 'pointer' if @thang?.isSelectable @sprite.cursor = 'pointer' if @thang?.isSelectable

View file

@ -163,6 +163,8 @@ module.exports = class LankBoss extends CocoClass
options = @createLankOptions thang: thang options = @createLankOptions thang: thang
options.resolutionFactor = if thangType.get('kind') is 'Floor' then 2 else SPRITE_RESOLUTION_FACTOR options.resolutionFactor = if thangType.get('kind') is 'Floor' then 2 else SPRITE_RESOLUTION_FACTOR
if @options.playerNames and /Hero Placeholder/.test thang.id
options.playerName = @options.playerNames[thang.team]
lank = new Lank thangType, options lank = new Lank thangType, options
@listenTo lank, 'sprite:mouse-up', @onLankMouseUp @listenTo lank, 'sprite:mouse-up', @onLankMouseUp
@addLank lank, null, layer @addLank lank, null, layer

View file

@ -113,7 +113,7 @@ module.exports = Surface = class Surface extends CocoClass
canvasHeight = parseInt @normalCanvas.attr('height'), 10 canvasHeight = parseInt @normalCanvas.attr('height'), 10
@screenLayer.addChild new Letterbox canvasWidth: canvasWidth, canvasHeight: canvasHeight @screenLayer.addChild new Letterbox canvasWidth: canvasWidth, canvasHeight: canvasHeight
@lankBoss = new LankBoss camera: @camera, webGLStage: @webGLStage, surfaceTextLayer: @surfaceTextLayer, world: @world, thangTypes: @options.thangTypes, choosing: @options.choosing, navigateToSelection: @options.navigateToSelection, showInvisible: @options.showInvisible @lankBoss = new LankBoss camera: @camera, webGLStage: @webGLStage, surfaceTextLayer: @surfaceTextLayer, world: @world, thangTypes: @options.thangTypes, choosing: @options.choosing, navigateToSelection: @options.navigateToSelection, showInvisible: @options.showInvisible, playerNames: @options.playerNames
@countdownScreen = new CountdownScreen camera: @camera, layer: @screenLayer, showsCountdown: @world.showsCountdown @countdownScreen = new CountdownScreen camera: @camera, layer: @screenLayer, showsCountdown: @world.showsCountdown
@playbackOverScreen = new PlaybackOverScreen camera: @camera, layer: @screenLayer @playbackOverScreen = new PlaybackOverScreen camera: @camera, layer: @screenLayer
@normalStage.addChildAt @playbackOverScreen.dimLayer, 0 # Put this below the other layers, actually, so we can more easily read text on the screen. @normalStage.addChildAt @playbackOverScreen.dimLayer, 0 # Put this below the other layers, actually, so we can more easily read text on the screen.
@ -549,8 +549,9 @@ module.exports = Surface = class Surface extends CocoClass
@normalStage.scaleY *= newHeight / oldHeight @normalStage.scaleY *= newHeight / oldHeight
@camera.onResize newWidth, newHeight @camera.onResize newWidth, newHeight
if @options.spectateGame if @options.spectateGame
# Since normalCanvas is absolutely positioned, it needs help aligning with webGLCanvas. But not further than +149px (1920px screen). # Since normalCanvas is absolutely positioned, it needs help aligning with webGLCanvas.
@normalCanvas.css 'left', Math.min 149, @webGLCanvas.offset().left offset = @webGLCanvas.offset().left - ($('#page-container').innerWidth() - $('#canvas-wrapper').innerWidth()) / 2
@normalCanvas.css 'left', offset
#- Camera focus on hero #- Camera focus on hero
focusOnHero: -> focusOnHero: ->

View file

@ -360,7 +360,7 @@ module.exports = class World
#console.log "... world serializing frames from", startFrame, "to", endFrame, "of", @totalFrames #console.log "... world serializing frames from", startFrame, "to", endFrame, "of", @totalFrames
[transferableObjects, nontransferableObjects] = [0, 0] [transferableObjects, nontransferableObjects] = [0, 0]
delete flag.processed for flag in @flagHistory delete flag.processed for flag in @flagHistory
o = {totalFrames: @totalFrames, maxTotalFrames: @maxTotalFrames, frameRate: @frameRate, dt: @dt, victory: @victory, userCodeMap: {}, trackedProperties: {}, flagHistory: @flagHistory, difficulty: @difficulty, scores: @getScores()} o = {totalFrames: @totalFrames, maxTotalFrames: @maxTotalFrames, frameRate: @frameRate, dt: @dt, victory: @victory, userCodeMap: {}, trackedProperties: {}, flagHistory: @flagHistory, difficulty: @difficulty, scores: @getScores(), randomSeed: @randomSeed}
o.trackedProperties[prop] = @[prop] for prop in @trackedProperties or [] o.trackedProperties[prop] = @[prop] for prop in @trackedProperties or []
for thangID, methods of @userCodeMap for thangID, methods of @userCodeMap
@ -467,7 +467,7 @@ module.exports = class World
w.userCodeMap[thangID][methodName][aetherStateKey] = serializedAether[aetherStateKey] w.userCodeMap[thangID][methodName][aetherStateKey] = serializedAether[aetherStateKey]
else else
w = new World o.userCodeMap, classMap w = new World o.userCodeMap, classMap
[w.totalFrames, w.maxTotalFrames, w.frameRate, w.dt, w.scriptNotes, w.victory, w.flagHistory, w.difficulty, w.scores] = [o.totalFrames, o.maxTotalFrames, o.frameRate, o.dt, o.scriptNotes ? [], o.victory, o.flagHistory, o.difficulty, o.scores] [w.totalFrames, w.maxTotalFrames, w.frameRate, w.dt, w.scriptNotes, w.victory, w.flagHistory, w.difficulty, w.scores, w.randomSeed] = [o.totalFrames, o.maxTotalFrames, o.frameRate, o.dt, o.scriptNotes ? [], o.victory, o.flagHistory, o.difficulty, o.scores, o.randomSeed]
w[prop] = val for prop, val of o.trackedProperties w[prop] = val for prop, val of o.trackedProperties
perf.t1 = now() perf.t1 = now()

View file

@ -548,24 +548,24 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
# for_girls_1: "There are three game modes in CodeCombat: building, puzzles, and combat. We have intentionally designed each to appeal to both boys and girls and think that the building and puzzle levels especially differentiate the game from violent triple A titles that repel female players." # for_girls_1: "There are three game modes in CodeCombat: building, puzzles, and combat. We have intentionally designed each to appeal to both boys and girls and think that the building and puzzle levels especially differentiate the game from violent triple A titles that repel female players."
what_cover_title: "O que abordamos?" what_cover_title: "O que abordamos?"
# what_cover_1: "There are 20 levels in the Hour of Code tutorial that teach and reinforce 6 specific computer science concepts:" # what_cover_1: "There are 20 levels in the Hour of Code tutorial that teach and reinforce 6 specific computer science concepts:"
# what_cover_notation_1: "Formal notation" what_cover_notation_1: "Notação formal"
# what_cover_notation_2: "- builds an understanding of the importance of syntax in programming." # what_cover_notation_2: "- builds an understanding of the importance of syntax in programming."
# what_cover_methods_1: "Calling methods" what_cover_methods_1: "Chamar métodos"
# what_cover_methods_2: "- familiarizes students with the syntax of object-oriented method calls." # what_cover_methods_2: "- familiarizes students with the syntax of object-oriented method calls."
# what_cover_parameters_1: "Parameters" what_cover_parameters_1: "Parâmetros"
# what_cover_parameters_2: "- trains how to pass parameters to functions." # what_cover_parameters_2: "- trains how to pass parameters to functions."
# what_cover_strings_1: "Strings" what_cover_strings_1: "'Strings'"
# what_cover_strings_2: "- teaches students about string notation and passing strings as parameters." # what_cover_strings_2: "- teaches students about string notation and passing strings as parameters."
# what_cover_loops_1: "Loops" what_cover_loops_1: "'Loops'"
# what_cover_loops_2: "- develops the abstraction of designing short programs with loops." # what_cover_loops_2: "- develops the abstraction of designing short programs with loops."
# what_cover_variables_1: "Variables" what_cover_variables_1: "Variáveis"
# what_cover_variables_2: "- adds the skill of referencing values that change over time." # what_cover_variables_2: "- adds the skill of referencing values that change over time."
# what_cover_2: "Students may continue past level 20, depending on their speed and interest, to learn two additional concepts in later levels:" # what_cover_2: "Students may continue past level 20, depending on their speed and interest, to learn two additional concepts in later levels:"
# what_cover_logic_1: "Conditional logic" what_cover_logic_1: "Lógica condicional"
# what_cover_logic_2: "- when and how to use if/else to control in-game outcomes." # what_cover_logic_2: "- when and how to use if/else to control in-game outcomes."
# what_cover_input_1: "Handling player input" # what_cover_input_1: "Handling player input"
# what_cover_input_2: "- responding to input events to create a user interface." # what_cover_input_2: "- responding to input events to create a user interface."
# sys_requirements_title: "System Requirements" sys_requirements_title: "Requisitos do Sistema"
# sys_requirements_1: "Because CodeCombat is a game, it is more intensive for computers to run smoothly than video or written tutorials. We have optimized it to run quickly on all modern browsers and on older machines so that everyone can play. That said, here are our suggestions for getting the most out of your Hour of Code experience:" # sys_requirements_1: "Because CodeCombat is a game, it is more intensive for computers to run smoothly than video or written tutorials. We have optimized it to run quickly on all modern browsers and on older machines so that everyone can play. That said, here are our suggestions for getting the most out of your Hour of Code experience:"
# sys_requirements_2: "Use newer versions of Chrome or Firefox." # sys_requirements_2: "Use newer versions of Chrome or Firefox."
# sys_requirements_3: "Although CodeCombat will work on browsers as old as IE9, the performance is not as good. Chrome is best." # sys_requirements_3: "Although CodeCombat will work on browsers as old as IE9, the performance is not as good. Chrome is best."
@ -769,8 +769,8 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
achievement_search_title: "Procurar Conquistas" achievement_search_title: "Procurar Conquistas"
read_only_warning2: "Nota: não podes guardar nenhuma edição feita aqui, porque não tens sessão iniciada." read_only_warning2: "Nota: não podes guardar nenhuma edição feita aqui, porque não tens sessão iniciada."
no_achievements: "Ainda não foram adicionadas conquistas a este nível." no_achievements: "Ainda não foram adicionadas conquistas a este nível."
# achievement_query_misc: "Key achievement off of miscellanea" achievement_query_misc: "Conquista-chave de uma lista de variados"
# achievement_query_goals: "Key achievement off of level goals" achievement_query_goals: "Conquista-chave dos objetivos do nível"
level_completion: "Completação do Nível" level_completion: "Completação do Nível"
pop_i18n: "Propagar I18N" pop_i18n: "Propagar I18N"
tasks: "Tarefas" tasks: "Tarefas"
@ -794,7 +794,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
join_desc_2: "para começares, e assinalar a caixa abaixo para te declarares um bravo Arcomago e receberes as últimas notícias por e-mail. Queres falar sobre o que fazer ou como te envolveres mais profundamente no projeto? " join_desc_2: "para começares, e assinalar a caixa abaixo para te declarares um bravo Arcomago e receberes as últimas notícias por e-mail. Queres falar sobre o que fazer ou como te envolveres mais profundamente no projeto? "
join_desc_3: " ou encontra-nos na nossa " join_desc_3: " ou encontra-nos na nossa "
join_desc_4: "e começamos a partir daí!" join_desc_4: "e começamos a partir daí!"
join_url_email: "Envia-nos uma mensagem" join_url_email: "Contacta-nos"
join_url_hipchat: "sala HipChat pública" join_url_hipchat: "sala HipChat pública"
archmage_subscribe_desc: "Receber e-mails relativos a novas oportunidades de programação e anúncios." archmage_subscribe_desc: "Receber e-mails relativos a novas oportunidades de programação e anúncios."
artisan_introduction_pref: "Temos de construir mais níveis! As pessoas estão a pedir mais conteúdo, e nós mesmos só podemos construir estes tantos. Neste momento, a tua estação de trabalho é o nível um; o nosso editor de nível é pouco utilizável, até mesmo pelos seus criadores, por isso fica atento. Se tens visões de campanhas que abranjam 'for-loops' para o" artisan_introduction_pref: "Temos de construir mais níveis! As pessoas estão a pedir mais conteúdo, e nós mesmos só podemos construir estes tantos. Neste momento, a tua estação de trabalho é o nível um; o nosso editor de nível é pouco utilizável, até mesmo pelos seus criadores, por isso fica atento. Se tens visões de campanhas que abranjam 'for-loops' para o"

View file

@ -182,6 +182,7 @@ class CocoModel extends Backbone.Model
options ?= {} options ?= {}
options.data ?= {} options.data ?= {}
options.data.project = @project.join(',') if @project options.data.project = @project.join(',') if @project
#console.error @constructor.className, @, "fetching with cache?", options.cache, "options", options # Useful for debugging cached IE fetches
@jqxhr = super(options) @jqxhr = super(options)
@loading = true @loading = true
@jqxhr @jqxhr
@ -364,7 +365,7 @@ class CocoModel extends Backbone.Model
achievements = new NewAchievementCollection achievements = new NewAchievementCollection
achievements.fetch achievements.fetch
success: (collection) -> success: (collection) ->
me.fetch (success: -> Backbone.Mediator.publish('achievements:new', earnedAchievements: collection)) unless _.isEmpty(collection.models) me.fetch (cache: false, success: -> Backbone.Mediator.publish('achievements:new', earnedAchievements: collection)) unless _.isEmpty(collection.models)
error: -> error: ->
console.error 'Miserably failed to fetch unnotified achievements', arguments console.error 'Miserably failed to fetch unnotified achievements', arguments
cache: false cache: false

View file

@ -286,6 +286,7 @@ _.extend LevelSessionSchema.properties,
type: ['string', 'null'] # 'null' in case an opponent session got corrupted, don't care much here type: ['string', 'null'] # 'null' in case an opponent session got corrupted, don't care much here
description: 'What submittedCodeLanguage the opponent used during the match' description: 'What submittedCodeLanguage the opponent used during the match'
simulator: {type: 'object', description: 'Holds info on who simulated the match, and with what tools.'} simulator: {type: 'object', description: 'Holds info on who simulated the match, and with what tools.'}
randomSeed: {description: 'Stores the random seed that was used during this match.'}
c.extendBasicProperties LevelSessionSchema, 'level.session' c.extendBasicProperties LevelSessionSchema, 'level.session'
c.extendPermissionsProperties LevelSessionSchema, 'level.session' c.extendPermissionsProperties LevelSessionSchema, 'level.session'

View file

@ -63,7 +63,7 @@ $gameControlMargin: 30px
height: 100% height: 100%
background-size: 100% background-size: 100%
@include user-select(none) @include user-select(none)
.level, .level-shadow .level, .level-shadow
position: absolute position: absolute
border-radius: 50% border-radius: 50%
@ -108,26 +108,26 @@ $gameControlMargin: 30px
&.complete &.complete
border: 3px solid gold border: 3px solid gold
@include box-shadow(0px 0px 35px skyblue) @include box-shadow(0px 0px 35px skyblue)
img.banner img.banner
position: absolute position: absolute
bottom: 38% bottom: 38%
left: -50% left: -50%
width: 200% width: 200%
pointer-events: none pointer-events: none
img.star img.star
width: 100% width: 100%
bottom: 7% bottom: 7%
position: absolute position: absolute
pointer-events: none pointer-events: none
.glyphicon-star .glyphicon-star
position: absolute position: absolute
color: lightblue color: lightblue
font-size: 21px font-size: 21px
left: 1.5px left: 1.5px
&.started .glyphicon-star &.started .glyphicon-star
left: 0.5px left: 0.5px
@ -138,7 +138,7 @@ $gameControlMargin: 30px
left: 75% left: 75%
margin-left: 0 margin-left: 0
margin-bottom: 0 margin-bottom: 0
img.hero-portrait img.hero-portrait
position: absolute position: absolute
border: 1px solid black border: 1px solid black
@ -258,10 +258,10 @@ $gameControlMargin: 30px
border-radius: 50% border-radius: 50%
opacity: 1 opacity: 1
padding: 3px 9px padding: 3px 9px
&.complete &.complete
.start-level, .view-solutions .start-level, .view-solutions
min-width: initial min-width: calc(50% - 5px)
display: inline-block display: inline-block
width: calc(50% - 5px) width: calc(50% - 5px)
@ -277,10 +277,10 @@ $gameControlMargin: 30px
z-index: 1 z-index: 1
font-size: 2vw font-size: 2vw
text-shadow: 0 0 0.3vw white, 0 0 0.3vw white text-shadow: 0 0 0.3vw white, 0 0 0.3vw white
&:hover &:hover
text-decoration: none text-decoration: none
.next-level-line .next-level-line
transform-origin: 0 100% transform-origin: 0 100%
height: 8px height: 8px
@ -314,9 +314,9 @@ $gameControlMargin: 30px
margin-left: $gameControlMargin margin-left: $gameControlMargin
width: $gameControlSize width: $gameControlSize
height: $gameControlSize height: $gameControlSize
background: url(/images/pages/play/menu_icons.png) no-repeat background: url(/images/pages/play/menu_icons.png) no-repeat
position: relative position: relative
img img
position: absolute position: absolute
@ -324,21 +324,21 @@ $gameControlMargin: 30px
top: 0 top: 0
width: 100% width: 100%
height: 100% height: 100%
background-size: cover background-size: cover
@include transition(0.5s ease) @include transition(0.5s ease)
@include box-shadow(2px 2px 4px black) @include box-shadow(2px 2px 4px black)
border: 0 border: 0
border-radius: 12px border-radius: 12px
// IE9 shows a blank white button with this MS gradient filter in place // IE9 shows a blank white button with this MS gradient filter in place
filter: none filter: none
&:hover &:hover
@include box-shadow(0 0 12px #bbf) @include box-shadow(0 0 12px #bbf)
&:active &:active
@include box-shadow(0 0 20px white) @include box-shadow(0 0 20px white)
&.heroes &.heroes
background-position: (-1 * $gameControlSize) 0px background-position: (-1 * $gameControlSize) 0px
&.achievements &.achievements
@ -369,7 +369,7 @@ $gameControlMargin: 30px
.user-status-line .user-status-line
position: relative position: relative
button.btn.btn-illustrated button.btn.btn-illustrated
margin-left: 10px margin-left: 10px
min-width: 90px min-width: 90px
@ -390,7 +390,7 @@ $gameControlMargin: 30px
margin-left: 45px margin-left: 45px
$spriteSheetSize: 30px $spriteSheetSize: 30px
.player-level-icon, .player-hero-icon .player-level-icon, .player-hero-icon
background: transparent url(/images/pages/play/play-spritesheet.png) background: transparent url(/images/pages/play/play-spritesheet.png)
background-size: cover background-size: cover
@ -420,7 +420,7 @@ $gameControlMargin: 30px
background-position: (-11 * $spriteSheetSize) 0 background-position: (-11 * $spriteSheetSize) 0
&.sorcerer &.sorcerer
background-position: (-12 * $spriteSheetSize) 0 background-position: (-12 * $spriteSheetSize) 0
#volume-button #volume-button
position: absolute position: absolute

View file

@ -35,7 +35,7 @@
width: 300px width: 300px
h1 h1
font-size: 29px font-size: 28px
font-weight: bold font-weight: bold
color: black color: black

View file

@ -82,7 +82,7 @@ module.exports = class MyMatchesTabView extends CocoView
stale: match.date < submitDate stale: match.date < submitDate
fresh: fresh fresh: fresh
codeLanguage: match.codeLanguage codeLanguage: match.codeLanguage
simulator: JSON.stringify(match.simulator) simulator: JSON.stringify(match.simulator) + ' | seed ' + match.randomSeed
} }
for team in @teams for team in @teams

View file

@ -187,13 +187,13 @@ module.exports = class SpectateLevelView extends RootView
ctx.fillText("Loaded #{@modelsLoaded} thingies",50,50) ctx.fillText("Loaded #{@modelsLoaded} thingies",50,50)
insertSubviews: -> insertSubviews: ->
@insertSubView @tome = new TomeView levelID: @levelID, session: @session, thangs: @world.thangs, supermodel: @supermodel, spectateView: true, spectateOpponentCodeLanguage: @otherSession?.get('submittedCodeLanguage'), level: @level @insertSubView @tome = new TomeView levelID: @levelID, session: @session, otherSession: @otherSession, thangs: @world.thangs, supermodel: @supermodel, spectateView: true, spectateOpponentCodeLanguage: @otherSession?.get('submittedCodeLanguage'), level: @level
@insertSubView new PlaybackView {} @insertSubView new PlaybackView session: @session, level: @level
@insertSubView new GoldView {} @insertSubView new GoldView {}
@insertSubView new HUDView {} @insertSubView new HUDView {level: @level}
worldName = utils.i18n @level.attributes, 'name' worldName = utils.i18n @level.attributes, 'name'
@controlBar = @insertSubView new ControlBarView {worldName: worldName, session: @session, level: @level, supermodel: @supermodel, playableTeams: @world.playableTeams, spectateGame: true} @controlBar = @insertSubView new ControlBarView {worldName: worldName, session: @session, level: @level, supermodel: @supermodel, spectateGame: true}
# callbacks # callbacks
@ -207,7 +207,7 @@ module.exports = class SpectateLevelView extends RootView
initSurface: -> initSurface: ->
webGLSurface = $('canvas#webgl-surface', @$el) webGLSurface = $('canvas#webgl-surface', @$el)
normalSurface = $('canvas#normal-surface', @$el) normalSurface = $('canvas#normal-surface', @$el)
@surface = new Surface(@world, normalSurface, webGLSurface, thangTypes: @supermodel.getModels(ThangType), playJingle: not @isEditorPreview, spectateGame: true, wizards: @level.get('type', true) isnt 'hero') @surface = new Surface @world, normalSurface, webGLSurface, thangTypes: @supermodel.getModels(ThangType), playJingle: not @isEditorPreview, spectateGame: true, wizards: @level.get('type', true) is 'ladder', playerNames: @findPlayerNames()
worldBounds = @world.getBounds() worldBounds = @world.getBounds()
bounds = [{x:worldBounds.left, y:worldBounds.top}, {x:worldBounds.right, y:worldBounds.bottom}] bounds = [{x:worldBounds.left, y:worldBounds.top}, {x:worldBounds.right, y:worldBounds.bottom}]
@surface.camera.setBounds(bounds) @surface.camera.setBounds(bounds)
@ -215,6 +215,12 @@ module.exports = class SpectateLevelView extends RootView
@surface.camera.zoomTo({x: (worldBounds.right - worldBounds.left) / 2, y: (worldBounds.top - worldBounds.bottom) / 2}, 0.1, 0) @surface.camera.zoomTo({x: (worldBounds.right - worldBounds.left) / 2, y: (worldBounds.top - worldBounds.bottom) / 2}, 0.1, 0)
_.delay zoom, 4000 # call it later for some reason (TODO: figure this out) _.delay zoom, 4000 # call it later for some reason (TODO: figure this out)
findPlayerNames: ->
playerNames = {}
for session in [@session, @otherSession] when session?.get('team')
playerNames[session.get('team')] = session.get('creatorName') or 'Anoner'
playerNames
initGoalManager: -> initGoalManager: ->
@goalManager = new GoalManager(@world, @level.get('goals')) @goalManager = new GoalManager(@world, @level.get('goals'))
@god.setGoalManager @goalManager @god.setGoalManager @goalManager

View file

@ -56,27 +56,26 @@ module.exports = class LadderSubmissionView extends CocoView
failure = (jqxhr, textStatus, errorThrown) => failure = (jqxhr, textStatus, errorThrown) =>
console.log jqxhr.responseText console.log jqxhr.responseText
@setRankingButtonText 'failed' unless @destroyed @setRankingButtonText 'failed' unless @destroyed
transpiledCode = @transpileSession() @transpileSession (transpiledCode) =>
ajaxData = ajaxData =
session: @session.id session: @session.id
levelID: @level.id levelID: @level.id
originalLevelID: @level.get('original') originalLevelID: @level.get('original')
levelMajorVersion: @level.get('version').major levelMajorVersion: @level.get('version').major
transpiledCode: transpiledCode transpiledCode: transpiledCode
$.ajax '/queue/scoring', { $.ajax '/queue/scoring', {
type: 'POST' type: 'POST'
data: ajaxData data: ajaxData
success: success success: success
error: failure error: failure
} }
transpileSession: -> transpileSession: (callback) ->
submittedCode = @session.get('code') submittedCode = @session.get('code')
codeLanguage = @session.get('codeLanguage') or 'javascript' codeLanguage = @session.get('codeLanguage') or 'javascript'
@session.set('submittedCodeLanguage', codeLanguage) @session.set('submittedCodeLanguage', codeLanguage)
@session.save() # TODO: maybe actually use a callback to make sure this works?
transpiledCode = {} transpiledCode = {}
for thang, spells of submittedCode for thang, spells of submittedCode
transpiledCode[thang] = {} transpiledCode[thang] = {}
@ -85,7 +84,7 @@ module.exports = class LadderSubmissionView extends CocoView
aetherOptions = createAetherOptions functionName: spellID, codeLanguage: codeLanguage aetherOptions = createAetherOptions functionName: spellID, codeLanguage: codeLanguage
aether = new Aether aetherOptions aether = new Aether aetherOptions
transpiledCode[thang][spellID] = aether.transpile spell transpiledCode[thang][spellID] = aether.transpile spell
transpiledCode @session.save null, success: -> callback transpiledCode
onHelpSimulate: -> onHelpSimulate: ->
@playSound 'menu-button-click' @playSound 'menu-button-click'

View file

@ -305,12 +305,19 @@ module.exports = class PlayLevelView extends RootView
initSurface: -> initSurface: ->
webGLSurface = $('canvas#webgl-surface', @$el) webGLSurface = $('canvas#webgl-surface', @$el)
normalSurface = $('canvas#normal-surface', @$el) normalSurface = $('canvas#normal-surface', @$el)
@surface = new Surface(@world, normalSurface, webGLSurface, thangTypes: @supermodel.getModels(ThangType), playJingle: not @isEditorPreview, wizards: not (@level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop'])) @surface = new Surface(@world, normalSurface, webGLSurface, thangTypes: @supermodel.getModels(ThangType), playJingle: not @isEditorPreview, wizards: not (@level.get('type', true) in ['hero', 'hero-ladder', 'hero-coop']), observing: @observing, playerNames: @findPlayerNames())
worldBounds = @world.getBounds() worldBounds = @world.getBounds()
bounds = [{x: worldBounds.left, y: worldBounds.top}, {x: worldBounds.right, y: worldBounds.bottom}] bounds = [{x: worldBounds.left, y: worldBounds.top}, {x: worldBounds.right, y: worldBounds.bottom}]
@surface.camera.setBounds(bounds) @surface.camera.setBounds(bounds)
@surface.camera.zoomTo({x: 0, y: 0}, 0.1, 0) @surface.camera.zoomTo({x: 0, y: 0}, 0.1, 0)
findPlayerNames: ->
return {} unless @observing
playerNames = {}
for session in [@session, @otherSession] when session?.get('team')
playerNames[session.get('team')] = session.get('creatorName') or 'Anoner'
playerNames
# Once Surface is Loaded #################################################### # Once Surface is Loaded ####################################################
onLevelStarted: -> onLevelStarted: ->

View file

@ -206,7 +206,7 @@ module.exports = class Spell
@problemContext = { stringReferences: [], thisMethods: [], thisProperties: [] } @problemContext = { stringReferences: [], thisMethods: [], thisProperties: [] }
# TODO: These should be read from the database # TODO: These should be read from the database
@problemContext.commonThisMethods = ['moveRight', 'moveLeft', 'moveUp', 'moveDown', 'attack', 'findNearestEnemy', 'buildXY', 'moveXY', 'say', 'move', 'distance', 'findEnemies', 'getFriends', 'addFlag', 'getFlag', 'removeFlag', 'getFlags', 'attackRange', 'cast', 'buildTypes', 'jump', 'jumpTo', 'attackXY'] @problemContext.commonThisMethods = ['moveRight', 'moveLeft', 'moveUp', 'moveDown', 'attack', 'findNearestEnemy', 'buildXY', 'moveXY', 'say', 'move', 'distance', 'findEnemies', 'findFriends', 'addFlag', 'findFlag', 'removeFlag', 'findFlags', 'attackRange', 'cast', 'buildTypes', 'jump', 'jumpTo', 'attackXY']
return @problemContext unless thang? return @problemContext unless thang?
# Populate stringReferences # Populate stringReferences

View file

@ -22,7 +22,8 @@ options =
simulateOnlyOneGame: simulateOneGame simulateOnlyOneGame: simulateOneGame
options.heapdump = require('heapdump') if options.heapdump options.heapdump = require('heapdump') if options.heapdump
server = if options.testing then 'http://127.0.0.1:3000' else 'https://codecombat.com' server = if options.testing then 'http://127.0.0.1:3000' else 'http://direct.codecombat.com'
# Use direct instead of live site because jQlone's requests proxy doesn't do caching properly and CloudFlare gets too aggressive.
# Disabled modules # Disabled modules
disable = [ disable = [
@ -62,12 +63,16 @@ hookedLoader = (request, parent, isMain) ->
if request in disable or ~request.indexOf('templates') if request in disable or ~request.indexOf('templates')
console.log 'Ignored ' + request if options.debug console.log 'Ignored ' + request if options.debug
return class fake return class fake
else if /node_modules[\\\/]aether[\\\/]/.test parent.id
null # Let it through
else if '/' in request and not (request[0] is '.') or request is 'application' else if '/' in request and not (request[0] is '.') or request is 'application'
#console.log 'making path', path + '/app/' + request, 'from', path, request, 'with parent', parent
request = path + '/app/' + request request = path + '/app/' + request
else if request is 'underscore' else if request is 'underscore'
request = 'lodash' request = 'lodash'
console.log 'loading ' + request if options.debug console.log 'loading ' + request if options.debug
originalLoader request, parent, isMain originalLoader request, parent, isMain
unhook = () -> unhook = () ->
m._load = originalLoader m._load = originalLoader
hook = () -> hook = () ->
@ -90,6 +95,7 @@ GLOBAL.Backbone = require bowerComponentsPath + 'backbone/backbone'
unhook() unhook()
Backbone.$ = $ Backbone.$ = $
require bowerComponentsPath + 'validated-backbone-mediator/backbone-mediator' require bowerComponentsPath + 'validated-backbone-mediator/backbone-mediator'
Backbone.Mediator.setValidationEnabled false
GLOBAL.Aether = require 'aether' GLOBAL.Aether = require 'aether'
# Set up new loader. Again. # Set up new loader. Again.
hook() hook()

View file

@ -9,7 +9,7 @@ module.exports = $ = (input) ->
# Non-standard jQuery stuff. Don't use outside of server. # Non-standard jQuery stuff. Don't use outside of server.
$._debug = false $._debug = false
$._server = 'https://codecombat.com' $._server = 'http://direct.codecombat.com'
$._cookies = request.jar() $._cookies = request.jar()
$.when = Deferred.when $.when = Deferred.when

File diff suppressed because one or more lines are too long

View file

@ -100,12 +100,12 @@ work = () ->
self.postMessage type: 'start-load-frames' self.postMessage type: 'start-load-frames'
self.world.loadFrames self.onWorldLoaded, self.onWorldError, self.onWorldLoadProgress, true self.world.loadFrames self.onWorldLoaded, self.onWorldError, self.onWorldLoadProgress, null, true
self.onWorldLoaded = onWorldLoaded = -> self.onWorldLoaded = onWorldLoaded = ->
self.goalManager.worldGenerationEnded() self.goalManager.worldGenerationEnded()
goalStates = self.goalManager.getGoalStates() goalStates = self.goalManager.getGoalStates()
self.postMessage type: 'end-load-frames', goalStates: goalStates self.postMessage type: 'end-load-frames', goalStates: goalStates, overallStatus: goalManager.checkOverallStatus()
t1 = new Date() t1 = new Date()
diff = t1 - self.t0 diff = t1 - self.t0
@ -148,7 +148,9 @@ work = () ->
self.postedErrors[errorKey] = error self.postedErrors[errorKey] = error
else else
console.log 'Non-UserCodeError:', error.toString() + "\n" + error.stack or error.stackTrace console.log 'Non-UserCodeError:', error.toString() + "\n" + error.stack or error.stackTrace
self.postMessage type: 'non-user-code-problem', problem: {message: error.toString()}
self.cleanUp() self.cleanUp()
return false
return true return true
self.onWorldLoadProgress = onWorldLoadProgress = (progress) -> self.onWorldLoadProgress = onWorldLoadProgress = (progress) ->
@ -176,9 +178,19 @@ work = () ->
self.postMessage type: 'worker-initialized' self.postMessage type: 'worker-initialized'
worldCode = fs.readFileSync './public/javascripts/world.js', 'utf8' codeFileContents = []
lodashCode = fs.readFileSync './public/javascripts/lodash.js', 'utf8' for codeFile in [
aetherCode = fs.readFileSync './public/javascripts/aether.js', 'utf8' 'lodash.js'
'world.js'
'aether.js'
'app/vendor/aether-clojure.js'
'app/vendor/aether-coffeescript.js'
'app/vendor/aether-io.js'
'app/vendor/aether-javascript.js'
'app/vendor/aether-lua.js'
'app/vendor/aether-python.js'
]
codeFileContents.push fs.readFileSync("./public/javascripts/#{codeFile}", 'utf8')
#window.BOX2D_ENABLED = true; #window.BOX2D_ENABLED = true;
@ -195,9 +207,7 @@ ret = """
try { try {
// the world javascript file // the world javascript file
#{worldCode}; #{codeFileContents.join(';\n ')};
#{lodashCode};
#{aetherCode};
// Don't let user generated code access stuff from our file system! // Don't let user generated code access stuff from our file system!
self.importScripts = importScripts = null; self.importScripts = importScripts = null;

View file

@ -63,7 +63,7 @@
"aws-sdk": "~2.0.0", "aws-sdk": "~2.0.0",
"bayesian-battle": "0.0.x", "bayesian-battle": "0.0.x",
"redis": "", "redis": "",
"webworker-threads": "~0.4.11", "webworker-threads": "~0.5.5",
"node-gyp": "~0.13.0", "node-gyp": "~0.13.0",
"aether": "~0.3.0", "aether": "~0.3.0",
"JASON": "~0.1.3", "JASON": "~0.1.3",

View file

@ -15,7 +15,7 @@ bayes = new (require 'bayesian-battle')()
scoringTaskQueue = undefined scoringTaskQueue = undefined
scoringTaskTimeoutInSeconds = 600 scoringTaskTimeoutInSeconds = 600
SIMULATOR_VERSION = 1 SIMULATOR_VERSION = 3
module.exports.setup = (app) -> connectToScoringQueue() module.exports.setup = (app) -> connectToScoringQueue()
@ -611,6 +611,7 @@ updateMatchesInSession = (matchObject, sessionID, callback) ->
currentMatchObject.opponents = opponentsArray currentMatchObject.opponents = opponentsArray
currentMatchObject.codeLanguage = matchObject.opponents[opponentsArray[0].sessionID].codeLanguage currentMatchObject.codeLanguage = matchObject.opponents[opponentsArray[0].sessionID].codeLanguage
currentMatchObject.simulator = @clientResponseObject.simulator currentMatchObject.simulator = @clientResponseObject.simulator
currentMatchObject.randomSeed = parseInt(@clientResponseObject.randomSeed or 0, 10)
LevelSession.findOne {'_id': sessionID}, (err, session) -> LevelSession.findOne {'_id': sessionID}, (err, session) ->
session = session.toObject() session = session.toObject()
currentMatchObject.playtime = session.playtime ? 0 currentMatchObject.playtime = session.playtime ? 0