Merge pull request from codecombat/master

Merge master into production
This commit is contained in:
Michael Schmatz 2014-02-12 14:59:09 -08:00
commit 186a77e827
31 changed files with 429 additions and 341 deletions

View file

@ -4,4 +4,4 @@
It just grants us a non-exclusive license to use your contribution and certifies you have the right to contribute the code you submit. For both our sakes, we need this before we can accept a pull request. Don't worry, it's super easy. It just grants us a non-exclusive license to use your contribution and certifies you have the right to contribute the code you submit. For both our sakes, we need this before we can accept a pull request. Don't worry, it's super easy.
For more info, see [http://codecombat.com/legal](http://codecombat.com/legal). For more info, see [http://codecombat.com/legal](http://codecombat.com/legal).

View file

@ -106,7 +106,7 @@ module.exports = class God
window.BOX2D_ENABLED = true window.BOX2D_ENABLED = true
@lastSerializedWorldFrames = serialized.frames @lastSerializedWorldFrames = serialized.frames
finishBeholdingWorld: (newWorld) -> finishBeholdingWorld: (newWorld) =>
newWorld.findFirstChangedFrame @world newWorld.findFirstChangedFrame @world
@world = newWorld @world = newWorld
errorCount = (t for t in @world.thangs when t.errorsOut).length errorCount = (t for t in @world.thangs when t.errorsOut).length
@ -131,6 +131,8 @@ module.exports = class God
@dead = true @dead = true
Backbone.Mediator.unsubscribe('tome:cast-spells', @onTomeCast, @) Backbone.Mediator.unsubscribe('tome:cast-spells', @onTomeCast, @)
@goalManager = null @goalManager = null
@fillWorkerPool = null
@simulateWorld = null
#### Bad code for running worlds on main thread (profiling / IE9) #### #### Bad code for running worlds on main thread (profiling / IE9) ####
simulateWorld: => simulateWorld: =>
@ -207,6 +209,7 @@ class Angel
if @worker if @worker
worker = @worker worker = @worker
_.defer -> worker.terminate _.defer -> worker.terminate
@worker.removeEventListener 'message', @onWorkerMessage
@worker = null @worker = null
@ @
@ -217,6 +220,7 @@ class Angel
terminate: => terminate: =>
@worker?.terminate() @worker?.terminate()
@worker?.removeEventListener 'message', @onWorkerMessage
@worker = null @worker = null
return if @dead return if @dead
@free() @free()
@ -224,7 +228,12 @@ class Angel
destroy: -> destroy: ->
@dead = true @dead = true
@finishBeholdingWorld = null
@abort() @abort()
@terminate = null
@testWorker = null
@condemnWorker = null
@onWorkerMessage = null
testWorker: => testWorker: =>
@worker.postMessage {func: 'reportIn'} @worker.postMessage {func: 'reportIn'}
@ -235,22 +244,24 @@ class Angel
@abort() @abort()
listen: -> listen: ->
@worker.addEventListener 'message', (event) => @worker.addEventListener 'message', @onWorkerMessage
switch event.data.type
when 'new-world' onWorkerMessage: (event) =>
@god.beholdWorld @, event.data.serialized, event.data.goalStates switch event.data.type
when 'world-load-progress-changed' when 'new-world'
Backbone.Mediator.publish 'god:world-load-progress-changed', event.data unless @dead @god.beholdWorld @, event.data.serialized, event.data.goalStates
when 'console-log' when 'world-load-progress-changed'
console.log "|" + @god.id + "'s " + @id + "|", event.data.args... Backbone.Mediator.publish 'god:world-load-progress-changed', event.data unless @dead
when 'user-code-problem' when 'console-log'
@god.angelUserCodeProblem @, event.data.problem console.log "|" + @god.id + "'s " + @id + "|", event.data.args...
when 'abort' when 'user-code-problem'
#console.log @id, "aborted." @god.angelUserCodeProblem @, event.data.problem
clearTimeout @abortTimeout when 'abort'
@free() #console.log @id, "aborted."
@god.angelAborted @ clearTimeout @abortTimeout
when 'reportIn' @free()
clearTimeout @condemnTimeout @god.angelAborted @
else when 'reportIn'
console.log "Unsupported message:", event.data clearTimeout @condemnTimeout
else
console.log "Unsupported message:", event.data

View file

@ -28,7 +28,7 @@ module.exports = class LevelLoader extends CocoClass
@loadLevelModels() @loadLevelModels()
@loadAudio() @loadAudio()
@playJingle() @playJingle()
setTimeout (=> @update()), 1 # lets everything else resolve first _.defer @update # Lets everything else resolve first
playJingle: -> playJingle: ->
jingles = ["ident_1", "ident_2"] jingles = ["ident_1", "ident_2"]
@ -41,9 +41,9 @@ module.exports = class LevelLoader extends CocoClass
@session = new LevelSession() @session = new LevelSession()
@session.url = -> url @session.url = -> url
@session.fetch() @session.fetch()
@session.once 'sync', @onSessionLoaded @session.once 'sync', @onSessionLoaded, @
onSessionLoaded: => onSessionLoaded: ->
# TODO: maybe have all non versioned models do this? Or make it work to PUT/PATCH to relative urls # TODO: maybe have all non versioned models do this? Or make it work to PUT/PATCH to relative urls
@session.url = -> '/db/level.session/' + @id @session.url = -> '/db/level.session/' + @id
@update() @update()
@ -51,9 +51,9 @@ module.exports = class LevelLoader extends CocoClass
# Supermodel (Level) Loading # Supermodel (Level) Loading
loadLevelModels: -> loadLevelModels: ->
@supermodel.once 'loaded-all', @onSupermodelLoadedAll @supermodel.once 'loaded-all', @onSupermodelLoadedAll, @
@supermodel.on 'loaded-one', @onSupermodelLoadedOne @supermodel.on 'loaded-one', @onSupermodelLoadedOne, @
@supermodel.once 'error', @onSupermodelError @supermodel.once 'error', @onSupermodelError, @
@level = @supermodel.getModel(Level, @levelID) or new Level _id: @levelID @level = @supermodel.getModel(Level, @levelID) or new Level _id: @levelID
levelID = @levelID levelID = @levelID
@ -65,12 +65,12 @@ module.exports = class LevelLoader extends CocoClass
@supermodel.populateModel @level @supermodel.populateModel @level
onSupermodelError: => onSupermodelError: ->
msg = $.i18n.t('play_level.level_load_error', msg = $.i18n.t('play_level.level_load_error',
defaultValue: "Level could not be loaded.") defaultValue: "Level could not be loaded.")
@$el.html('<div class="alert">' + msg + '</div>') @$el.html('<div class="alert">' + msg + '</div>')
onSupermodelLoadedOne: (e) => onSupermodelLoadedOne: (e) ->
@notifyProgress() @notifyProgress()
# if e.model.type() is 'ThangType' # if e.model.type() is 'ThangType'
# thangType = e.model # thangType = e.model
@ -80,18 +80,18 @@ module.exports = class LevelLoader extends CocoClass
# building = thangType.buildSpriteSheet options # building = thangType.buildSpriteSheet options
# if building # if building
# @spriteSheetsToBuild += 1 # @spriteSheetsToBuild += 1
# thangType.on 'build-complete', => # thangType.once 'build-complete', =>
# @spriteSheetsBuilt += 1 # @spriteSheetsBuilt += 1
# @notifyProgress() # @notifyProgress()
onSupermodelLoadedAll: => onSupermodelLoadedAll: ->
@trigger 'loaded-supermodel' @trigger 'loaded-supermodel'
@stopListening(@supermodel) @stopListening(@supermodel)
@update() @update()
# Things to do when either the Session or Supermodel load # Things to do when either the Session or Supermodel load
update: -> update: =>
@notifyProgress() @notifyProgress()
return if @updateCompleted return if @updateCompleted
@ -153,7 +153,7 @@ module.exports = class LevelLoader extends CocoClass
return unless building return unless building
console.log 'Building:', thangType.get('name'), options console.log 'Building:', thangType.get('name'), options
@spriteSheetsToBuild += 1 @spriteSheetsToBuild += 1
thangType.on 'build-complete', => thangType.once 'build-complete', =>
@spriteSheetsBuilt += 1 @spriteSheetsBuilt += 1
@notifyProgress() @notifyProgress()
@ -208,6 +208,8 @@ module.exports = class LevelLoader extends CocoClass
@trigger 'loaded-all' if @progress() is 1 @trigger 'loaded-all' if @progress() is 1
destroy: -> destroy: ->
super()
@world = null # don't hold onto garbage @world = null # don't hold onto garbage
@supermodel.off 'loaded-one', @onSupermodelLoadedOne @supermodel.off 'loaded-one', @onSupermodelLoadedOne
super() @onSupermodelLoadedOne = null
@notifyProgress = null

View file

@ -186,7 +186,7 @@ module.exports = class Camera extends CocoClass
# Target is either just a {x, y} pos or a display object with {x, y} that might change; surface coordinates. # Target is either just a {x, y} pos or a display object with {x, y} that might change; surface coordinates.
time = 0 if @instant time = 0 if @instant
newTarget ?= {x:0, y:0} newTarget ?= {x:0, y:0}
newTarget = (@newTarget or @target) if @locked newTarget = (@newTarget or @target) if @locked
newZoom = Math.min((Math.max @minZoom, newZoom), MAX_ZOOM) newZoom = Math.min((Math.max @minZoom, newZoom), MAX_ZOOM)
return if @zoom is newZoom and newTarget is newTarget.x and newTarget.y is newTarget.y return if @zoom is newZoom and newTarget is newTarget.x and newTarget.y is newTarget.y
@ -199,15 +199,13 @@ module.exports = class Camera extends CocoClass
@tweenProgress = 0.01 @tweenProgress = 0.01
createjs.Tween.get(@) createjs.Tween.get(@)
.to({tweenProgress: 1.0}, time, createjs.Ease.getPowInOut(3)) .to({tweenProgress: 1.0}, time, createjs.Ease.getPowInOut(3))
.call @onTweenEnd .call @finishTween
else else
@target = newTarget @target = newTarget
@zoom = newZoom @zoom = newZoom
@updateZoom true @updateZoom true
onTweenEnd: => @finishTween()
finishTween: (abort=false) => finishTween: (abort=false) =>
createjs.Tween.removeTweens(@) createjs.Tween.removeTweens(@)
return unless @newTarget return unless @newTarget
@ -263,4 +261,5 @@ module.exports = class Camera extends CocoClass
destroy: -> destroy: ->
super() super()
@onTweenEnd = null createjs.Tween.removeTweens @
@finishTween = null

View file

@ -80,6 +80,9 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
super() super()
mark.destroy() for name, mark of @marks mark.destroy() for name, mark of @marks
label.destroy() for name, label of @labels label.destroy() for name, label of @labels
@imageObject?.off 'animationend', @playNextAction
@playNextAction = null
@displayObject?.off()
toString: -> "<CocoSprite: #{@thang?.id}>" toString: -> "<CocoSprite: #{@thang?.id}>"
@ -108,7 +111,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
@displayObject.sprite = @ @displayObject.sprite = @
@displayObject.layerPriority = @thangType.get 'layerPriority' @displayObject.layerPriority = @thangType.get 'layerPriority'
@displayObject.name = @thang?.spriteName or @thangType.get 'name' @displayObject.name = @thang?.spriteName or @thangType.get 'name'
@imageObject.on 'animationend', @onActionEnd @imageObject.on 'animationend', @playNextAction
################################################## ##################################################
# QUEUEING AND PLAYING ACTIONS # QUEUEING AND PLAYING ACTIONS
@ -126,10 +129,9 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
@currentRootAction = action @currentRootAction = action
@playNextAction() @playNextAction()
onActionEnd: (e) => @playNextAction()
onSurfaceTicked: (e) -> @age += e.dt onSurfaceTicked: (e) -> @age += e.dt
playNextAction: -> playNextAction: =>
@playAction(@actionQueue.splice(0,1)[0]) if @actionQueue.length @playAction(@actionQueue.splice(0,1)[0]) if @actionQueue.length
playAction: (action) -> playAction: (action) ->
@ -411,7 +413,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
sound = e.sound ? AudioPlayer.soundForDialogue e.message, @thangType.get 'soundTriggers' sound = e.sound ? AudioPlayer.soundForDialogue e.message, @thangType.get 'soundTriggers'
@instance?.stop() @instance?.stop()
if @instance = @playSound sound, false if @instance = @playSound sound, false
@instance.addEventListener "complete", => Backbone.Mediator.publish 'dialogue-sound-completed' @instance.addEventListener "complete", -> Backbone.Mediator.publish 'dialogue-sound-completed'
@notifySpeechUpdated e @notifySpeechUpdated e
onClearDialogue: (e) -> onClearDialogue: (e) ->

View file

@ -7,14 +7,14 @@ CROSSFADE_LENGTH = 1500
module.exports = class MusicPlayer extends CocoClass module.exports = class MusicPlayer extends CocoClass
currentMusic: null currentMusic: null
standingBy: null standingBy: null
subscriptions: subscriptions:
'level-play-music': 'onPlayMusic' 'level-play-music': 'onPlayMusic'
'audio-player:loaded': 'onAudioLoaded' 'audio-player:loaded': 'onAudioLoaded'
constructor: -> constructor: ->
super(arguments...) super arguments...
me.on('change:music', @onMusicSettingChanged, @) me.on 'change:music', @onMusicSettingChanged, @
onAudioLoaded: -> onAudioLoaded: ->
@onPlayMusic(@standingBy) if @standingBy @onPlayMusic(@standingBy) if @standingBy
@ -29,12 +29,12 @@ module.exports = class MusicPlayer extends CocoClass
AudioPlayer.preloadSound(src) AudioPlayer.preloadSound(src)
@standingBy = e @standingBy = e
return return
@standingBy = null @standingBy = null
if @currentMusic if @currentMusic
f = -> @stop() f = -> @stop()
createjs.Tween.get(@currentMusic).to({volume:0.0}, CROSSFADE_LENGTH).call(f) createjs.Tween.get(@currentMusic).to({volume:0.0}, CROSSFADE_LENGTH).call(f)
@currentMusic = createjs.Sound.play(src, 'none', 0, 0, -1, 0.3) if src and e.play @currentMusic = createjs.Sound.play(src, 'none', 0, 0, -1, 0.3) if src and e.play
return unless @currentMusic return unless @currentMusic
@currentMusic.volume = 0.0 @currentMusic.volume = 0.0
@ -48,4 +48,7 @@ module.exports = class MusicPlayer extends CocoClass
return unless @currentMusic return unless @currentMusic
createjs.Tween.removeTweens(@currentMusic) createjs.Tween.removeTweens(@currentMusic)
@currentMusic.volume = if me.get('music') then 1.0 else 0.0 @currentMusic.volume = if me.get('music') then 1.0 else 0.0
destroy: ->
super()
me.off 'change:music', @onMusicSettingChanged, @

View file

@ -90,14 +90,19 @@ module.exports = Surface = class Surface extends CocoClass
@dimmer?.destroy() @dimmer?.destroy()
@stage.clear() @stage.clear()
@musicPlayer?.destroy() @musicPlayer?.destroy()
@stage.removeAllChildren()
@stage.removeEventListener 'stagemousemove', @onMouseMove @stage.removeEventListener 'stagemousemove', @onMouseMove
@stage.removeEventListener 'stagemousedown', @onMouseDown @stage.removeEventListener 'stagemousedown', @onMouseDown
@stage.removeAllEventListeners() @stage.removeAllEventListeners()
@stage.enableDOMEvents false
@stage.enableMouseOver 0
@playScrubbedSounds = null @playScrubbedSounds = null
@onMouseMove = null @onMouseMove = null
@onMouseDown = null @onMouseDown = null
@tick = null @tick = null
@canvas.off 'mousewheel', @onMouseWheel
@onMouseWheel = null
setWorld: (@world) -> setWorld: (@world) ->
@worldLoaded = true @worldLoaded = true
@world.getFrame(Math.min(@getCurrentFrame(), @world.totalFrames - 1)).restoreState() unless @options.choosing @world.getFrame(Math.min(@getCurrentFrame(), @world.totalFrames - 1)).restoreState() unless @options.choosing
@ -337,7 +342,7 @@ module.exports = Surface = class Surface extends CocoClass
@stage.enableMouseOver(10) @stage.enableMouseOver(10)
@stage.addEventListener 'stagemousemove', @onMouseMove @stage.addEventListener 'stagemousemove', @onMouseMove
@stage.addEventListener 'stagemousedown', @onMouseDown @stage.addEventListener 'stagemousedown', @onMouseDown
@hookUpZoomControls() @canvas.on 'mousewheel', @onMouseWheel
@hookUpChooseControls() if @options.choosing @hookUpChooseControls() if @options.choosing
console.log "Setting fps", @world.frameRate unless @world.frameRate is 30 console.log "Setting fps", @world.frameRate unless @world.frameRate is 30
createjs.Ticker.setFPS @world.frameRate createjs.Ticker.setFPS @world.frameRate
@ -436,12 +441,11 @@ module.exports = Surface = class Surface extends CocoClass
onBackground = not @stage.hitTest e.stageX, e.stageY onBackground = not @stage.hitTest e.stageX, e.stageY
Backbone.Mediator.publish 'surface:stage-mouse-down', onBackground: onBackground, x: e.stageX, y: e.stageY, originalEvent: e Backbone.Mediator.publish 'surface:stage-mouse-down', onBackground: onBackground, x: e.stageX, y: e.stageY, originalEvent: e
hookUpZoomControls: -> onMouseWheel: (e) =>
@canvas.bind 'mousewheel', (e) => # https://github.com/brandonaaron/jquery-mousewheel
# https://github.com/brandonaaron/jquery-mousewheel e.preventDefault()
e.preventDefault() return if @disabled
return if @disabled Backbone.Mediator.publish 'surface:mouse-scrolled', deltaX: e.deltaX, deltaY: e.deltaY unless @disabled
Backbone.Mediator.publish 'surface:mouse-scrolled', deltaX: e.deltaX, deltaY: e.deltaY unless @disabled
hookUpChooseControls: -> hookUpChooseControls: ->
chooserOptions = stage: @stage, surfaceLayer: @surfaceLayer, camera: @camera, restrictRatio: @options.choosing is 'ratio-region' chooserOptions = stage: @stage, surfaceLayer: @surfaceLayer, camera: @camera, restrictRatio: @options.choosing is 'ratio-region'

View file

@ -247,7 +247,7 @@ module.exports = nativeDescription: "português do Brasil", englishDescription:
and: "e" and: "e"
or: "ou" or: "ou"
name: "Nome" name: "Nome"
# body: "Body" body: "Principal"
version: "Versão" version: "Versão"
commit_msg: "Mensagem do Commit" commit_msg: "Mensagem do Commit"
version_history_for: "Histórico de Versão para: " version_history_for: "Histórico de Versão para: "
@ -260,182 +260,183 @@ module.exports = nativeDescription: "português do Brasil", englishDescription:
who_is_codecombat: "Quem é CodeCombat?" who_is_codecombat: "Quem é CodeCombat?"
why_codecombat: "Por que CodeCombat?" why_codecombat: "Por que CodeCombat?"
who_description_prefix: "juntos começamos o CodeCombat em 2013. Noós também criamos " who_description_prefix: "juntos começamos o CodeCombat em 2013. Noós também criamos "
# who_description_suffix: "in 2008, growing it to the #1 web and iOS application for learning to write Chinese and Japanese characters." who_description_suffix: "em 2008, subindo até o 1º lugar entre aplicativos web e para iOS para aprender caracteres chineses e japoneses."
# who_description_ending: "Now it's time to teach people to write code." who_description_ending: "Agora é a hora de ensinar as pessoas a escreverem código."
# why_paragraph_1: "When making Skritter, George didn't know how to program and was constantly frustrated by his inability to implement his ideas. Afterwards, he tried learning, but the lessons were too slow. His housemate, wanting to reskill and stop teaching, tried Codecademy, but \"got bored.\" Each week another friend started Codecademy, then dropped off. We realized it was the same problem we'd solved with Skritter: people learning a skill via slow, intensive lessons when what they need is fast, extensive practice. We know how to fix that." why_paragraph_1: " Quando estava desenvolvendo o Skritter, George não sabia como programar e ficava constantemente frustrado por causa de sua falta de habilidade para implementar suas idéias. Depois, ele tentou aprender, mas as aulas eram muito lentas. Seu colega de quarto, tentando inovar e parar de dar aulas, tentou o Codecademy, mas \"ficou entediado.\" A cada semana um novo amigo começava no Codecademy, e desistia em seguida. Nós percebemos que era o mesmo problema que havíamos resolvido com o Skritter: pessoas aprendendo uma habilidade através de aulas lentas e intensivas quando o que elas precisavam era de prática, rápida e extensa. Nós sabemos como consertar isso."
# why_paragraph_2: "Need to learn to code? You don't need lessons. You need to write a lot of code and have a great time doing it." why_paragraph_2: "Precisa aprender a codificar? Você não precisa de aulas. Você precisa escrever muito código e se divertir fazendo isso."
# why_paragraph_3_prefix: "That's what programming is about. It's gotta be fun. Not fun like" why_paragraph_3_prefix: "É disso que se trata a programação. Tem que ser divertido. Não divertido como"
# why_paragraph_3_italic: "yay a badge" why_paragraph_3_italic: "oba uma insígnia"
# why_paragraph_3_center: "but fun like" why_paragraph_3_center: "mas divertido como"
# why_paragraph_3_italic_caps: "NO MOM I HAVE TO FINISH THE LEVEL!" why_paragraph_3_italic_caps: "NÃO MÃE EU PRECISO TERMINAR ESSE NÍVEL!"
# why_paragraph_3_suffix: "That's why CodeCombat is a multiplayer game, not a gamified lesson course. We won't stop until you can't stop--but this time, that's a good thing." why_paragraph_3_suffix: "É por isso que o CodeCombat é um jogo multiplayer, não uma aula que imita um jogo. Nós não iremos parar até você não conseguir parar--mas agora, isso é uma coisa boa."
# why_paragraph_4: "If you're going to get addicted to some game, get addicted to this one and become one of the wizards of the tech age." why_paragraph_4: "Se você vai se viciar em algum jogo, fique viciado nesse e se torne um dos magos da era da tecnologia."
# why_ending: "And hey, it's free. " why_ending: "E é de graça. "
# why_ending_url: "Start wizarding now!" why_ending_url: "Comece a feitiçaria agora!"
# george_description: "CEO, business guy, web designer, game designer, and champion of beginning programmers everywhere." george_description: "CEO, um cara de negócios, web designer, designer de jogos, e campeão em iniciar programadores em qualquer lugar."
# scott_description: "Programmer extraordinaire, software architect, kitchen wizard, and master of finances. Scott is the reasonable one." scott_description: "Programador extraordinário, arquiteto de software, mago da cozinha e mestre de finanças. Scott é o racional."
# nick_description: "Programming wizard, eccentric motivation mage, and upside-down experimenter. Nick can do anything and chooses to build CodeCombat." nick_description: "Mago da programação, feiticeiro da motivação excêntrica e experimentador doido. Nick pode fazer qualquer coisa e escolheu desenvolver o CodeCombat."
# jeremy_description: "Customer support mage, usability tester, and community organizer; you've probably already spoken with Jeremy." jeremy_description: "Mago em suporte ao consumidor, testador de usabilidade, e organizador da comunidade; você provavelmente já falou com o Jeremy."
# michael_description: "Programmer, sys-admin, and undergrad technical wunderkind, Michael is the person keeping our servers online." michael_description: "Programador, administrador de sistemas, e um técnico prodígio não graduado, Michael é a pessoa que mantém os servidores funcionando."
# legal:
# page_title: "Legal"
# opensource_intro: "CodeCombat is free to play and completely open source."
# opensource_description_prefix: "Check out "
# github_url: "our GitHub"
# opensource_description_center: "and help out if you like! CodeCombat is built on dozens of open source projects, and we love them. See "
# archmage_wiki_url: "our Archmage wiki"
# opensource_description_suffix: "for a list of the software that makes this game possible."
# practices_title: "Respectful Best Practices"
# practices_description: "These are our promises to you, the player, in slightly less legalese."
# privacy_title: "Privacy"
# privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent."
# security_title: "Security"
# security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems."
# email_title: "Email"
# email_description_prefix: "We will not inundate you with spam. Through"
# email_settings_url: "your email settings"
# email_description_suffix: "or through links in the emails we send, you can change your preferences and easily unsubscribe at any time."
# cost_title: "Cost"
# cost_description: "Currently, CodeCombat is 100% free! One of our main goals is to keep it that way, so that as many people can play as possible, regardless of place in life. If the sky darkens, we might have to charge subscriptions or for some content, but we'd rather not. With any luck, we'll be able to sustain the company with:"
# recruitment_title: "Recruitment"
# recruitment_description_prefix: "Here on CodeCombat, you're going to become a powerful wizardnot just in the game, but also in real life."
# url_hire_programmers: "No one can hire programmers fast enough"
# recruitment_description_suffix: "so once you've sharpened your skills and if you agree, we will demo your best coding accomplishments to the thousands of employers who are drooling for the chance to hire you. They pay us a little, they pay you"
# recruitment_description_italic: "a lot"
# recruitment_description_ending: "the site remains free and everybody's happy. That's the plan."
# copyrights_title: "Copyrights and Licenses"
# contributor_title: "Contributor License Agreement"
# contributor_description_prefix: "All contributions, both on the site and on our GitHub repository, are subject to our"
# cla_url: "CLA"
# contributor_description_suffix: "to which you should agree before contributing."
# code_title: "Code - MIT"
# code_description_prefix: "All code owned by CodeCombat or hosted on codecombat.com, both in the GitHub repository or in the codecombat.com database, is licensed under the"
# mit_license_url: "MIT license"
# code_description_suffix: "This includes all code in Systems and Components that are made available by CodeCombat for the purpose of creating levels."
# art_title: "Art/Music - Creative Commons "
# art_description_prefix: "All common content is available under the"
# cc_license_url: "Creative Commons Attribution 4.0 International License"
# art_description_suffix: "Common content is anything made generally available by CodeCombat for the purpose of creating Levels. This includes:"
# art_music: "Music"
# art_sound: "Sound"
# art_artwork: "Artwork"
# art_sprites: "Sprites"
# art_other: "Any and all other non-code creative works that are made available when creating Levels."
# art_access: "Currently there is no universal, easy system for fetching these assets. In general, fetch them from the URLs as used by the site, contact us for assistance, or help us in extending the site to make these assets more easily accessible."
# art_paragraph_1: "For attribution, please name and link to codecombat.com near where the source is used or where appropriate for the medium. For example:"
# use_list_1: "If used in a movie or another game, include codecombat.com in the credits."
# use_list_2: "If used on a website, include a link near the usage, for example underneath an image, or in a general attributions page where you might also mention other Creative Commons works and open source software being used on the site. Something that's already clearly referencing CodeCombat, such as a blog post mentioning CodeCombat, does not need some separate attribution."
# art_paragraph_2: "If the content being used is created not by CodeCombat but instead by a user of codecombat.com, attribute them instead, and follow attribution directions provided in that resource's description if there are any."
# rights_title: "Rights Reserved"
# rights_desc: "All rights are reserved for Levels themselves. This includes"
# rights_scripts: "Scripts"
# rights_unit: "Unit configuration"
# rights_description: "Description"
# rights_writings: "Writings"
# rights_media: "Media (sounds, music) and any other creative content made specifically for that Level and not made generally available when creating Levels."
# rights_clarification: "To clarify, anything that is made available in the Level Editor for the purpose of making levels is under CC, whereas the content created with the Level Editor or uploaded in the course of creation of Levels is not."
# nutshell_title: "In a Nutshell"
# nutshell_description: "Any resources we provide in the Level Editor are free to use as you like for creating Levels. But we reserve the right to restrict distribution of the Levels themselves (that are created on codecombat.com) so that they may be charged for in the future, if that's what ends up happening."
# canonical: "The English version of this document is the definitive, canonical version. If there are any discrepencies between translations, the English document takes precedence."
# contribute: legal:
# page_title: "Contributing" page_title: "Jurídico"
# character_classes_title: "Character Classes" opensource_intro: "CodeCombat é grátis para jogar e de código completamente aberto."
# introduction_desc_intro: "We have high hopes for CodeCombat." opensource_description_prefix: "Confira "
# introduction_desc_pref: "We want to be where programmers of all stripes come to learn and play together, introduce others to the wonderful world of coding, and reflect the best parts of the community. We can't and don't want to do that alone; what makes projects like GitHub, Stack Overflow and Linux great are the people who use them and build on them. To that end, " github_url: "nosso GitHub"
# introduction_desc_github_url: "CodeCombat is totally open source" opensource_description_center: "e ajude-nos se você gostar! CodeCombat é construído a partir de dúzias de projetos de código aberto, e nós amamos eles. Veja "
# introduction_desc_suf: ", and we aim to provide as many ways as possible for you to take part and make this project as much yours as ours." archmage_wiki_url: "nossa wiki mágica"
# introduction_desc_ending: "We hope you'll join our party!" opensource_description_suffix: "para uma lista dos softwares que fazem esse jogo possível."
# introduction_desc_signature: "- Nick, George, Scott, Michael, and Jeremy" practices_title: "Respeitáveis Boas Práticas"
# alert_account_message_intro: "Hey there!" practices_description: "Essas são nossas promessas para você, o jogador, de uma maneira menos jurídica."
# alert_account_message_pref: "To subscribe for class emails, you'll need to " privacy_title: "Privacidade"
# alert_account_message_suf: "first." privacy_description: "Nós não venderemos nenhuma de suas informações pessoais. Nós pretendemos ganhar dinheiro através de recrutamento eventualmente, mas fiquem tranquilos que nós não distribuiremos suas informações pessoais para companhias interessadas sem a sua aprovação explícita."
# alert_account_message_create_url: "create an account" security_title: "Segurança"
# archmage_introduction: "One of the best parts about building games is they synthesize so many different things. Graphics, sound, real-time networking, social networking, and of course many of the more common aspects of programming, from low-level database management, and server administration to user facing design and interface building. There's a lot to do, and if you're an experienced programmer with a hankering to really dive into the nitty-gritty of CodeCombat, this class might be for you. We would love to have your help building the best programming game ever." security_description: "Nós lutamos para manter suas informações pessoais a salvo.Como um projeto de código aberto, nosso site é aberto para qualquer um rever e melhorar nossos sistemas de segurança."
# class_attributes: "Class Attributes" email_title: "Email"
# archmage_attribute_1_pref: "Knowledge in " email_description_prefix: "Nós não iremos te encher de spam. Através"
# archmage_attribute_1_suf: ", or a desire to learn. Most of our code is in this language. If you're a fan of Ruby or Python, you'll feel right at home. It's JavaScript, but with a nicer syntax." email_settings_url: "das suas configurações de email"
# archmage_attribute_2: "Some experience in programming and personal initiative. We'll help you get oriented, but we can't spend much time training you." email_description_suffix: "ou através de links nos emails que enviarmos, você pode trocar as preferências e facilmente se desinscrever a qualquer hora."
# how_to_join: "How To Join" cost_title: "Custo"
# join_desc_1: "Anyone can help out! Just check out our " cost_description: "Atualmente o CodeCombat é 100% grátis. Um dos nossos principais objetivos é mantê-lo dessa forma, para que, o maior número possível de pessoas possa jogar, independente de seu lugar na vida. Se o céu escurecer, nós poderemos ter que cobrar uma assinatura, ou por algum conteúdo, mas preferimos que não. Com alguma sorte, nós seremos capazes de manter a empresa com:"
# join_desc_2: "to get started, and check the box below to mark yourself as a brave Archmage and get the latest news by email. Want to chat about what to do or how to get more deeply involved? " recruitment_title: "Recrutamento"
# join_desc_3: ", or find us in our " recruitment_description_prefix: "Aqui no CodeCombat, você vai se tornar um poderoso feiticeiro, não apenas no jogo, mas também na vida real."
# join_desc_4: "and we'll go from there!" url_hire_programmers: "Ninguém pode contratar programadores rápido o bastante"
# join_url_email: "Email us" recruitment_description_suffix: "então quando você aumentar suas habilidade e, se concordar, vamos demonstrar suas melhores realizações em codificação para os milhares de empregadores que estão babando para ter a chance de contratá-lo. Eles nos pagam um pouco, eles te pagam"
# join_url_hipchat: "public HipChat room" recruitment_description_italic: "muito"
# more_about_archmage: "Learn More About Becoming A Powerful Archmage" recruitment_description_ending: "o site continua grátis e todo mundo fica feliz. Esse é o plano."
# archmage_subscribe_desc: "Get emails on new coding opportunities and announcements." copyrights_title: "Direitos Autorais e Licenças"
# artisan_introduction_pref: "We must construct additional levels! People be clamoring for more content, and we can only build so many ourselves. Right now your workstation is level one; our level editor is barely usable even by its creators, so be wary. If you have visions of campaigns spanning for-loops to" contributor_title: "Contrato de Licença de Colaborador"
# artisan_introduction_suf: "to then this class might be for you." contributor_description_prefix: "Todos os colaboradores, tanto no nosso site quando no nosso repositório no GitHub estão sujeitos ao nosso"
# artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!" cla_url: "CLA"
# artisan_attribute_2: "A hankering to do a whole lot of testing and iteration. To make good levels, you need to take it to others and watch them play it, and be prepared to find a lot of things to fix." contributor_description_suffix: " para o qual você deve estar de acordo antes de contribuir."
# artisan_attribute_3: "For the time being, endurance en par with an Adventurer. Our Level Editor is super preliminary and frustrating to use. You have been warned!" code_title: "Código - MIT"
# artisan_join_desc: "Use the Level Editor in these steps, give or take:" code_description_prefix: "Todo o código possuído pelo CodeCombat ou hospedado no codecombat.com, tanto no nosso repositório no GitHub como no banco de dados do codecombat.com, é licenciado sob"
# artisan_join_step1: "Read the documentation." mit_license_url: "Licença do MIT"
# artisan_join_step2: "Create a new level and explore existing levels." code_description_suffix: "Isso inclui todo o código nos sistemas e componentes que são disponibilizados pelo CodeCombat para o propósito de criar níveis."
# artisan_join_step3: "Find us in our public HipChat room for help." art_title: "Arte/Música - Creative Commons "
# artisan_join_step4: "Post your levels on the forum for feedback." art_description_prefix: "Todo o conteúdo comum está disponível sob a"
# more_about_artisan: "Learn More About Becoming A Creative Artisan" cc_license_url: "Creative Commons Attribution 4.0 International License"
# artisan_subscribe_desc: "Get emails on level editor updates and announcements." art_description_suffix: "Conteúdo comum é qualquer coisa disponível pelo CodeCombat com o propósito de criar níveis. Isto inclui:"
# adventurer_introduction: "Let's be clear about your role: you are the tank. You're going to take heavy damage. We need people to try out brand-new levels and help identify how to make things better. The pain will be enormous; making good games is a long process and no one gets it right the first time. If you can endure and have a high constitution score, then this class might be for you." art_music: "Música"
# adventurer_attribute_1: "A thirst for learning. You want to learn how to code and we want to teach you how to code. You'll probably be doing most of the teaching in this case, though." art_sound: "Som"
# adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve." art_artwork: "Obra de arte"
# adventurer_join_pref: "Either get together with (or recruit!) an Artisan and work with them, or check the box below to receive emails when there are new levels to test. We'll also be posting about levels to review on our networks like" art_sprites: "Sprites"
# adventurer_forum_url: "our forum" art_other: "Todo e qualquer outros trabalho criativo não relacionados a código que são disponibilizados para criar níveis."
# adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!" art_access: "Atualmente não existe um sistema universal e fácil para buscar por essas obras. Em geral, busque-os através das URLs utilizadas pelo site, entre em contato conosco para obter auxílio, ou nos ajude a estender o site para tornar as obras acessíveis mais facilmente."
# more_about_adventurer: "Learn More About Becoming A Brave Adventurer" art_paragraph_1: "Para atribuição, por favor, nomeie e referencie o codecombat.com perto de onde a fonte é utilizada, ou onde for apropriado para o meio. Por exemplo:"
# adventurer_subscribe_desc: "Get emails when there are new levels to test." use_list_1: "Se usado em um filme ou outro jogo, inclua codecombat.com nos créditos."
# scribe_introduction_pref: "CodeCombat isn't just going to be a bunch of levels. It will also include a resource for knowledge, a wiki of programming concepts that levels can hook into. That way rather than each Artisan having to describe in detail what a comparison operator is, they can simply link their level to the Article describing them that is already written for the player's edification. Something along the lines of what the " use_list_2: "Se usado em um site, incluir um link perto do local de uso, por exemplo, em baixo de uma imagem, ou em uma página de atribuições gerais onde você pode também mencionar outros trabalhos em Creative Commons e softwares de código aberto que estão sendo usados no site.Algo que já está referenciando claramente o CodeCombat, como um post no blog mencionando o CodeCombat, não precisa de atribuição separada."
# scribe_introduction_url_mozilla: "Mozilla Developer Network" art_paragraph_2: "Se o conteúdo que está sendo usado não é criado pelo CodeCombat mas por algum usuário do codecombat.com, você deve referenciá-los seguindo as normas contidas neste documento caso haja alguma."
# scribe_introduction_suf: " has built. If your idea of fun is articulating the concepts of programming in Markdown form, then this class might be for you." rights_title: "Direitos Reservados"
# scribe_attribute_1: "Skill in words is pretty much all you need. Not only grammar and spelling, but able to convey complicated ideas to others." rights_desc: "Todos os direitos são reservados para os Níveis em si. Isto inclui"
# contact_us_url: "Contact us" rights_scripts: "Scripts"
# scribe_join_description: "tell us a little about yourself, your experience with programming and what sort of things you'd like to write about. We'll go from there!" rights_unit: "Configurações de unidades"
# more_about_scribe: "Learn More About Becoming A Diligent Scribe" rights_description: "Descrição"
# scribe_subscribe_desc: "Get emails about article writing announcements." rights_writings: "Escritos"
# diplomat_introduction_pref: "So, if there's one thing we learned from the " rights_media: "Mídia (sons, música) e qualquer outro conteúdo criativo feito especificamente para esse nível e que não estão disponíveis quando se está criando níveis."
# diplomat_launch_url: "launch in October" rights_clarification: "Para esclarecer, tudo o que é disponibilizado no Editor de Níveis com o objetivo de criar os níveis está sob CC, enquanto que o conteúdo criado através do Editor de Níveis ou inserido durante o processo de criação dos níveis não é."
# diplomat_introduction_suf: "it's that there is sizeable interest in CodeCombat in other countries, particularly Brazil! We're building a corps of translators eager to turn one set of words into another set of words to get CodeCombat as accessible across the world as possible. If you like getting sneak peeks at upcoming content and getting these levels to your fellow nationals ASAP, then this class might be for you." nutshell_title: "Em poucas palavras"
# diplomat_attribute_1: "Fluency in English and the language you would like to translate to. When conveying complicated ideas, it's important to have a strong grasp in both!" nutshell_description: "Todos os recursos que oferecemos no Editor de Níveis é livre para usar como quiser para a criação de níveis. Mas nós nos reservamos o direito de restringir a distribuição dos próprios níveis (que são criados em codecombat.com) para que possam ser cobrados no futuro, se é que isso precise acontecer."
# diplomat_join_pref: "We've started a lot of initial translations at " canonical: "A versão em inglês deste documento é a versão canônica definitiva. Se houver discrepâncias entre traduções, o documento em inglês tem precedência."
# diplomat_doc_url: "this forum post"
# diplomat_join_suf: "so check it out and add things for your language. Also, check this box below to keep up-to-date on new internationalization developments!"
# more_about_diplomat: "Learn More About Becoming A Great Diplomat"
# diplomat_subscribe_desc: "Get emails about i18n developments and levels to translate."
# ambassador_introduction: "This is a community we're building, and you are the connections. We've got Olark chats, emails, and social networks with lots of people to talk with and help get acquainted with the game and learn from. If you want to help people get involved and have fun, and get a good feel of the pulse of CodeCombat and where we're going, then this class might be for you."
# ambassador_attribute_1: "Communication skills. Be able to identify the problems players are having and help them solve them. Also, keep the rest of us informed about what players are saying, what they like and don't like and want more of!"
# ambassador_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll go from there!"
# ambassador_join_note_strong: "Note"
# ambassador_join_note_desc: "One of our top priorities is to build multiplayer where players having difficulty solving levels can summon higher level wizards to help them. This will be a great way for ambassadors to do their thing. We'll keep you posted!"
# more_about_ambassador: "Learn More About Becoming A Helpful Ambassador"
# ambassador_subscribe_desc: "Get emails on support updates and multiplayer developments."
# counselor_introduction_1: "Do you have life experience? A different perspective on things that can help us decide how to shape CodeCombat? Of all these roles, this will probably take the least time, but individually you may make the most difference. We're on the lookout for wisened sages, particularly in areas like: teaching, game development, open source project management, technical recruiting, entrepreneurship, or design."
# counselor_introduction_2: "Or really anything that is relevant to the development of CodeCombat. If you have knowledge and want to share it to help grow this project, then this class might be for you."
# counselor_attribute_1: "Experience, in any of the areas above or something you think might be helpful."
# counselor_attribute_2: "A little bit of free time!"
# counselor_join_desc: "tell us a little about yourself, what you've done and what you'd be interested in doing. We'll put you in our contact list and be in touch when we could use advice (not too often)."
# more_about_counselor: "Learn More About Becoming A Valuable Counselor"
# changes_auto_save: "Changes are saved automatically when you toggle checkboxes."
# diligent_scribes: "Our Diligent Scribes:"
# powerful_archmages: "Our Powerful Archmages:"
# creative_artisans: "Our Creative Artisans:"
# brave_adventurers: "Our Brave Adventurers:"
# translating_diplomats: "Our Translating Diplomats:"
# helpful_ambassadors: "Our Helpful Ambassadors:"
# classes: contribute:
# archmage_title: "Archmage" page_title: "Contribuindo"
# archmage_title_description: "(Coder)" character_classes_title: "Classes de Personagem"
# artisan_title: "Artisan" introduction_desc_intro: "Nós temos grandes expectativas para o CodeCombat."
# artisan_title_description: "(Level Builder)" introduction_desc_pref: "Queremos ser o lugar onde os programadores de todos os tipos vêm para aprender e brincarem juntos, introduzir outros ao maravilhoso mundo da codificação, e refletir as melhores partes da comunidade. Não podemos e não queremos fazer isso sozinhos, o que faz de projetos como o GitHub, Stack Overflow e Linux ótimos são as pessoas que os utilizam e constróem sobre eles. Para esse fim, "
# adventurer_title: "Adventurer" introduction_desc_github_url: "CodeCombat é totalmente código aberto"
# adventurer_title_description: "(Level Playtester)" introduction_desc_suf: ", e nosso objetivo é oferecer quantas maneiras forem possíveis para você participar e fazer deste projeto tanto seu como nosso."
# scribe_title: "Scribe" introduction_desc_ending: "Nós esperamos que você se junte a nossa festa!"
# scribe_title_description: "(Article Editor)" introduction_desc_signature: "- Nick, George, Scott, Michael, and Jeremy"
# diplomat_title: "Diplomat" alert_account_message_intro: "Ei!"
# diplomat_title_description: "(Translator)" alert_account_message_pref: "Para se inscrever para os emails de classe, você vai precisar, "
# ambassador_title: "Ambassador" alert_account_message_suf: "primeiro."
# ambassador_title_description: "(Support)" alert_account_message_create_url: "criar uma conta"
# counselor_title: "Counselor" archmage_introduction: "Uma das melhores partes sobre a construção de jogos é que eles sintetizam diversas coisas diferentes. Gráficos, som, interação em tempo real, redes sociais, e, claro, muitos dos aspectos mais comuns da programação, desde a gestão em baixo nível de banco de dados, e administração do servidor até interação com o usuário e desenvolvimento da interface. Há muito a fazer, e se você é um programador experiente com um desejo ardente de realmente mergulhar no âmago da questão do CodeCombat, esta classe pode ser para você. Nós gostaríamos de ter sua ajuda para construir o melhor jogo de programação de todos os tempos."
# counselor_title_description: "(Expert/Teacher)" class_attributes: "Atributos da Classe"
archmage_attribute_1_pref: "Conhecimento em "
archmage_attribute_1_suf: ", ou um desejo de aprender. A maioria do nosso código é escrito nessa lingua. Se você é um fã de Ruby ou Python, você vai se sentir em casa. É JavaScript, mas com uma sintaxe mais agradável."
archmage_attribute_2: "Alguma experiência em programação e iniciativa pessoal. Nós vamos ajudá-lo a se orientar, mas não podemos passar muito tempo treinando você."
how_to_join: "Como Participar"
join_desc_1: "Qualquer um pode ajudar! Confira nosso "
join_desc_2: "para começar, e marque a caixa abaixo para marcar-se como um arquimago corajoso e receba as últimas notícias por email. Quer conversar sobre o que fazer ou como se envolver mais profundamente? "
join_desc_3: ", ou encontre-nos em nosso "
join_desc_4: "e começaremos a partir de lá!"
join_url_email: "Envie-nos um email"
join_url_hipchat: "Sala de bate-papo pública no HipChat"
more_about_archmage: "Saiba Mais Sobre Como Se Tornar Um Poderoso Arquimago"
archmage_subscribe_desc: "Receba email sobre novas oportunidades para codificar e anúncios."
artisan_introduction_pref: "Nós devemos contruir níveis adicionais! Pessoas estão clamando por mais conteúdo, e só podemos contruir tantos de nós mesmos. Agora sua estação de trabalho é o nível um; nosso Editor de Níveis é pouco utilizável até mesmo para seus criadores, então fique esperto. Se você tem visões de campanhas abrangendo for-loops para"
artisan_introduction_suf: "para, em seguida, esta classe pode ser para você."
artisan_attribute_1: "Qualquer experiência em construir conteúdo como esse seria legal, como usando os editores de nível da Blizzard. Mas não é obrigatório!"
artisan_attribute_2: "Um desejo ardente de fazer um monte de testes e iteração. Para fazer bons níveis, você precisa levá-lo para os outros e vê-los jogar, e estar preparado para encontrar muitas coisas para consertar."
artisan_attribute_3: "Por enquanto, a resistência em par com um Aventureiro. Nosso Editor de Níveis é super preliminar e frustrante para usar. Você foi avisado!"
artisan_join_desc: "Use o Editor de Níveis nestas etapas, mais ou menos:"
artisan_join_step1: "Leia a documentação."
artisan_join_step2: "Crie um novo nível e explore os níveis existentes."
artisan_join_step3: "Encontre-nos na nossa sala pública no HipChat para ajuda."
artisan_join_step4: "Publique seus níveis no fórum para avaliação."
more_about_artisan: "Saiba Mais Sobre Como Se Tornar Um Artesão Criativo"
artisan_subscribe_desc: "Receba emails com novidades sobre o editor de níveis e anúncios."
adventurer_introduction: "Vamos ser claros sobre o seu papel: você é o tanque. Você vai tomar dano pesado. Precisamos de pessoas para experimentar níveis inéditos e ajudar a identificar como fazer as coisas melhorarem. A dor será enorme, fazer bons jogos é um processo longo e ninguém acerta na primeira vez. Se você pode suportar e ter uma alta pontuação de constituição, então esta classe pode ser para você."
adventurer_attribute_1: "Sede de aprendizado. Você quer aprender a codificar e nós queremos ensiná-lo a codificar. Você provavelmente vai fazer a maior parte do ensino neste caso."
adventurer_attribute_2: "Carismático. Seja gentil, mas articulado sobre o que precisa melhorar, e ofereça sugestões sobre como melhorar."
adventurer_join_pref: "Se reuna (ou recrute!) um Artesão e trabalhe com ele, ou marque a caixa abaixo para receber emails quando houver novos níveis para testar. Também estaremos postando sobre níveis que precisam de revisão em nossas redes como"
adventurer_forum_url: "nosso fórum"
adventurer_join_suf: "então se você prefere ser notificado dessas formas, inscreva-se lá!"
more_about_adventurer: "Saiba Mais Sobre Como Se Tornar Um Valente Aventureiro"
adventurer_subscribe_desc: "Receba emails quando houver novos níveis para testar."
scribe_introduction_pref: "O CodeCombat não será apenas um monte de níveis. Ele também irá incluir uma fonte de conhecimento, um wiki de conceitos de programação que os níveis podem se basear. Dessa forma, em vez de cada Artesão ter que descrever em detalhes o que é um operador de comparação, eles podem simplesmente criar um link para o artigo que o descreve. Algo na linha do que a "
scribe_introduction_url_mozilla: "Mozilla Developer Network"
scribe_introduction_suf: " construiu. Se a sua idéia de diversão é articular os conceitos de programação em Markdown, então esta classe pode ser para você."
scribe_attribute_1: "Habilidade com palavras é praticamente tudo o que você precisa. Não só a gramática e ortografica, mas a capacidade de transmitir idéias muito complicadas para os outros."
contact_us_url: "Contate-nos"
scribe_join_description: "conte-nos um pouco sobre você, sua experiência com programação e que tipo de coisas você gostaria de escrever sobre. Nós começaremos a partir disso!"
more_about_scribe: "Saiba Mais Sobre Como Se Tornar Um Escriba Aplicado"
scribe_subscribe_desc: "Receba email sobre anúncios de escrita de artigos."
diplomat_introduction_pref: "Então, se há uma coisa que aprendemos com o "
diplomat_launch_url: "lançamento em Outubro"
diplomat_introduction_suf: "é que há um interesse considerável no CodeCombat em outros países, especialmente no Brasil! Estamos construindo um corpo de tradutores ansiosos para transformar um conjunto de palavras em outro conjunto de palavras para tornar o CodeCombat tão acessível em todo o mundo quanto for possível. Se você gosta de obter cenas inéditas do próximo conteúdo e obter esses níveis para os seus compatriotas o mais rápido possível, então esta classe pode ser para você."
diplomat_attribute_1: "Fluência no inglês e na língua para a qual você gostaria de traduzir. Ao transmitir idéias complicadas, é importante ter um forte domínio em ambos!"
diplomat_join_pref: "Nós começamos várias traduções iniciais "
diplomat_doc_url: "nesta publicação no fórum"
diplomat_join_suf: "então confira a publicação e comece a adicionar coisas para o seu idioma. Além disso, marque a caixa abaixo para se manter atualizado sobre os novos desenvolvimentos da internacionalização!"
more_about_diplomat: "Saiba Mais Sobre Como Se Tornar Um Ótimo Diplomata"
diplomat_subscribe_desc: "Receba emails sobre o desenvolvimento da i18n e níveis para traduzir."
ambassador_introduction: "Esta é uma comunidade que estamos construindo, e vocês são as conexões. Temos chats Olark, emails e redes sociais com muitas pessoas para conversar e ajudar a se familiarizar com o jogo e aprender. Se você quer ajudar as pessoas a se envolver e se divertir, e ter uma boa noção da pulsação do CodeCombat e para onde estamos indo em seguida, esta classe pode ser para você."
ambassador_attribute_1: "Habilidades de comunicação. Ser capaz de identificar os problemas que os jogadores estão tendo e ajudar a resolvê-los, Além disso, manter o resto de nós informados sobre o que os jogadores estão dizendo, o que gostam e não gostam e do que querem mais!"
ambassador_join_desc: "conte-nos um pouco sobre você, o que você fez e o que você estaria interessado em fazer. Nós começaremos a partir disso!"
ambassador_join_note_strong: "Nota"
ambassador_join_note_desc: "Uma das nossas principais prioridades é a construção de um multiplayer onde os jogadores que estão com dificuldade para resolver um nível podem invocar feitiçeiros com nível mais alto para ajudá-los. Esta será uma ótima maneira para os embaixadores fazerem suas tarefas. Vamos mantê-lo informado!"
more_about_ambassador: "Saiba Mais Sobre Como Se Tornar Um Embaixador Prestativo"
ambassador_subscribe_desc: "Receba emails sobre atualização do suporte e desenvolvimento do multiplayer."
counselor_introduction_1: "Você tem experiência de vida? Uma perspectiva diferente sobre as coisas podem nos ajudar a decidir como moldar o CodeCombat? De todos os papéis, este provavelmente vai demorar menos tempo, mas individualmente você pode fazer mais diferença. Estamos à procura de sábios, particularmente em áreas como: ensino, desenvolvimento de jogos, gerenciamente de projetos de código aberto, recrutamento técnico, empreendedorismo ou design."
counselor_introduction_2: "Ou realmente tudo o que é relevante para o desenvolvimento do CodeCombat. Se você tem conhecimento e quer compartilhá-lo para ajudar este projeto a crescer, esta classe pode ser para você."
counselor_attribute_1: "Experiência, em qualquer uma das áreas acima ou alguma coisa que você pense ser útil."
counselor_attribute_2: "Um pouco de tempo livre!"
counselor_join_desc: "conte-nos um pouco sobre você, o que você fez e o que você estaria interessado em fazer. Vamos colocá-lo em nossa lista de contatos e entraremos em contato quando precisarmos de um conselho (não muitas vezes)."
more_about_counselor: "Saiba Mais Sobre Como Se Tornar Um Conselheiro Valioso"
changes_auto_save: "As alterações são salvas automaticamente quando você marcar as caixas de seleção."
diligent_scribes: "Nossos Aplicados Escribas:"
powerful_archmages: "Nossos Poderosos Arquimagos:"
creative_artisans: "Nossos Criativos Artesãos:"
brave_adventurers: "Nossos Valentes Aventureiros:"
translating_diplomats: "Nossos Diplomatas Tradutores:"
helpful_ambassadors: "Nossos Prestativos Embaixadores:"
classes:
archmage_title: "Arquimago"
archmage_title_description: "(Codificador)"
artisan_title: "Artesão"
artisan_title_description: "(Construtor de Nível)"
adventurer_title: "Aventureiro"
adventurer_title_description: "(Testador de Nível)"
scribe_title: "Escriba"
scribe_title_description: "(Escritor de Artigos)"
diplomat_title: "Diplomata"
diplomat_title_description: "(Tradutor)"
ambassador_title: "Embaixador"
ambassador_title_description: "(Suporte)"
counselor_title: "Conselheiro"
counselor_title_description: "(Especialista/Professor)"

View file

@ -25,7 +25,7 @@ class CocoModel extends Backbone.Model
@loadSchema() @loadSchema()
@once 'sync', @onLoaded, @ @once 'sync', @onLoaded, @
@saveBackup = _.debounce(@saveBackup, 500) @saveBackup = _.debounce(@saveBackup, 500)
type: -> type: ->
@constructor.className @constructor.className
@ -36,18 +36,18 @@ class CocoModel extends Backbone.Model
if @saveBackups if @saveBackups
existing = storage.load @id existing = storage.load @id
if existing if existing
@set(existing, {silent:true}) @set(existing, {silent:true})
CocoModel.backedUp[@id] = @ CocoModel.backedUp[@id] = @
set: -> set: ->
res = super(arguments...) res = super(arguments...)
@saveBackup() if @saveBackups and @loaded and @hasLocalChanges() @saveBackup() if @saveBackups and @loaded and @hasLocalChanges()
res res
saveBackup: -> saveBackup: ->
storage.save(@id, @attributes) storage.save(@id, @attributes)
CocoModel.backedUp[@id] = @ CocoModel.backedUp[@id] = @
@backedUp = {} @backedUp = {}
loadSchema: -> loadSchema: ->
@ -55,7 +55,7 @@ class CocoModel extends Backbone.Model
@constructor.schema = new CocoSchema(@urlRoot) @constructor.schema = new CocoSchema(@urlRoot)
@constructor.schema.fetch() @constructor.schema.fetch()
@constructor.schema.on 'sync', => @constructor.schema.once 'sync', =>
@constructor.schema.loaded = true @constructor.schema.loaded = true
@addSchemaDefaults() @addSchemaDefaults()
@trigger 'schema-loaded' @trigger 'schema-loaded'
@ -90,7 +90,7 @@ class CocoModel extends Backbone.Model
revert: -> revert: ->
@set(@_revertAttributes, {silent: true}) if @_revertAttributes @set(@_revertAttributes, {silent: true}) if @_revertAttributes
@clearBackup() @clearBackup()
clearBackup: -> clearBackup: ->
storage.remove @id storage.remove @id

View file

@ -13,7 +13,7 @@ module.exports = class LevelComponent extends CocoModel
attrs.js = @compile attrs.code attrs.js = @compile attrs.code
super attrs, options super attrs, options
onLoaded: => onLoaded: ->
super() super()
@set 'js', @compile(@get 'code') unless @get 'js' @set 'js', @compile(@get 'code') unless @get 'js'

View file

@ -13,7 +13,7 @@ module.exports = class LevelSystem extends CocoModel
attrs.js = @compile attrs.code attrs.js = @compile attrs.code
super attrs, options super attrs, options
onLoaded: => onLoaded: ->
super() super()
@set 'js', @compile(@get 'code') unless @get 'js' @set 'js', @compile(@get 'code') unless @get 'js'

View file

@ -8,22 +8,23 @@ class SuperModel
@mustPopulate = model @mustPopulate = model
model.saveBackups = @shouldSaveBackups(model) model.saveBackups = @shouldSaveBackups(model)
model.fetch() unless model.loaded or model.loading model.fetch() unless model.loaded or model.loading
model.on('sync', @modelLoaded) unless model.loaded model.once('sync', @modelLoaded, @) unless model.loaded
model.once('error', @modelErrored) unless model.loaded model.once('error', @modelErrored, @) unless model.loaded
url = model.url() url = model.url()
@models[url] = model unless @models[url]? @models[url] = model unless @models[url]?
@modelLoaded(model) if model.loaded @modelLoaded(model) if model.loaded
# replace or overwrite # replace or overwrite
shouldPopulate: (url) -> return true shouldPopulate: (url) -> return true
shouldSaveBackups: (model) -> return false shouldSaveBackups: (model) -> return false
modelErrored: (model) => modelErrored: (model) ->
@trigger 'error' @trigger 'error'
@removeEventsFromModel(model)
modelLoaded: (model) => modelLoaded: (model) ->
schema = model.schema() schema = model.schema()
return schema.on('sync', => @modelLoaded(model)) unless schema.loaded return schema.once('sync', => @modelLoaded(model)) unless schema.loaded
refs = model.getReferencedModels(model.attributes, schema.attributes) refs = model.getReferencedModels(model.attributes, schema.attributes)
refs = [] unless @mustPopulate is model or @shouldPopulate(model) refs = [] unless @mustPopulate is model or @shouldPopulate(model)
# console.log 'Loaded', model.get('name') # console.log 'Loaded', model.get('name')
@ -33,10 +34,15 @@ class SuperModel
continue if @models[refURL] continue if @models[refURL]
@models[refURL] = ref @models[refURL] = ref
ref.fetch() ref.fetch()
ref.on 'sync', @modelLoaded ref.once 'sync', @modelLoaded, @
@trigger 'loaded-one', model: model @trigger 'loaded-one', model: model
@trigger 'loaded-all' if @finished() @trigger 'loaded-all' if @finished()
@removeEventsFromModel(model)
removeEventsFromModel: (model) ->
model.off 'sync', @modelLoaded, @
model.off 'error', @modelErrored, @
getModel: (ModelClass_or_url, id) -> getModel: (ModelClass_or_url, id) ->
return @getModelByURL(ModelClass_or_url) if _.isString(ModelClass_or_url) return @getModelByURL(ModelClass_or_url) if _.isString(ModelClass_or_url)

View file

@ -28,7 +28,6 @@ module.exports = class User extends CocoModel
profileUrl = "#{GRAVATAR_URL}#{emailHash}.json?callback=#{functionName}" profileUrl = "#{GRAVATAR_URL}#{emailHash}.json?callback=#{functionName}"
script = $("<script src='#{profileUrl}' type='text/javascript'></script>") script = $("<script src='#{profileUrl}' type='text/javascript'></script>")
$('head').append(script) $('head').append(script)
$('body').on('load',(e)->console.log('we did it!', e))
window[functionName] = (profile) => window[functionName] = (profile) =>
@gravatarProfile = profile @gravatarProfile = profile
@trigger('change', @) @trigger('change', @)

View file

@ -81,9 +81,7 @@ module.exports = class ScriptsTabView extends View
@selectedScriptPath = newPath @selectedScriptPath = newPath
getThangIDs: -> getThangIDs: ->
ids = (t.id for t in @level.get('thangs') when t.id isnt 'Interface') (t.id for t in @level.get('thangs') when t.id isnt 'Interface')
ids = ['My Wizard', 'Captain Anya'].concat(ids)
ids
onScriptChanged: => onScriptChanged: =>
@scriptsTreema.set(@selectedScriptPath, @scriptTreema.data) @scriptsTreema.set(@selectedScriptPath, @scriptTreema.data)

View file

@ -2,12 +2,13 @@ View = require 'views/kinds/CocoView'
template = require 'templates/editor/level/settings_tab' template = require 'templates/editor/level/settings_tab'
Level = require 'models/Level' Level = require 'models/Level'
Surface = require 'lib/surface/Surface' Surface = require 'lib/surface/Surface'
nodes = require './treema_nodes'
module.exports = class SettingsTabView extends View module.exports = class SettingsTabView extends View
id: 'editor-level-settings-tab-view' id: 'editor-level-settings-tab-view'
className: 'tab-pane' className: 'tab-pane'
template: template template: template
editableSettings: ['name', 'description', 'documentation', 'nextLevel', 'background', 'victory', 'i18n', 'icon'] # not thangs or scripts or the backend stuff editableSettings: ['name', 'description', 'documentation', 'nextLevel', 'background', 'victory', 'i18n', 'icon', 'goals'] # not thangs or scripts or the backend stuff
subscriptions: subscriptions:
'level-loaded': 'onLevelLoaded' 'level-loaded': 'onLevelLoaded'
@ -22,16 +23,24 @@ module.exports = class SettingsTabView extends View
schema = _.cloneDeep Level.schema.attributes schema = _.cloneDeep Level.schema.attributes
schema.properties = _.pick schema.properties, (value, key) => key in @editableSettings schema.properties = _.pick schema.properties, (value, key) => key in @editableSettings
schema.required = _.intersection schema.required, @editableSettings schema.required = _.intersection schema.required, @editableSettings
thangIDs = @getThangIDs()
treemaOptions = treemaOptions =
filePath: "db/level/#{@level.get('original')}" filePath: "db/level/#{@level.get('original')}"
supermodel: @supermodel supermodel: @supermodel
schema: schema schema: schema
data: data data: data
callbacks: {change: @onSettingsChanged} callbacks: {change: @onSettingsChanged}
thangIDs: thangIDs
nodeClasses:
thang: nodes.ThangNode
@settingsTreema = @$el.find('#settings-treema').treema treemaOptions @settingsTreema = @$el.find('#settings-treema').treema treemaOptions
@settingsTreema.build() @settingsTreema.build()
@settingsTreema.open() @settingsTreema.open()
getThangIDs: ->
(t.id for t in @level.get('thangs') when t.id isnt 'Interface')
onSettingsChanged: (e) => onSettingsChanged: (e) =>
$('.level-title').text @settingsTreema.data.name $('.level-title').text @settingsTreema.data.name
for key in @editableSettings for key in @editableSettings

View file

@ -42,6 +42,7 @@ module.exports = class CocoView extends Backbone.View
@undelegateEvents() # removes both events and subs @undelegateEvents() # removes both events and subs
view.destroy() for id, view of @subviews view.destroy() for id, view of @subviews
@modalClosed = null @modalClosed = null
$('#modal-wrapper .modal').off 'hidden.bs.modal', @modalClosed
afterInsert: -> afterInsert: ->
@ -85,14 +86,14 @@ module.exports = class CocoView extends Backbone.View
# Modals # Modals
toggleModal: (e) -> toggleModal: (e) ->
if $(e.currentTarget).prop('target') is '_blank' if $(e.currentTarget).prop('target') is '_blank'
return true return true
# special handler for opening modals that are dynamically loaded, rather than static in the page. It works (or should work) like Bootstrap's modals, except use coco-modal for the data-toggle value. # special handler for opening modals that are dynamically loaded, rather than static in the page. It works (or should work) like Bootstrap's modals, except use coco-modal for the data-toggle value.
elem = $(e.target) elem = $(e.target)
return unless elem.data('toggle') is 'coco-modal' return unless elem.data('toggle') is 'coco-modal'
target = elem.data('target') target = elem.data('target')
view = application.router.getView(target, '_modal') # could set up a system for loading cached modals, if told to view = application.router.getView(target, '_modal') # could set up a system for loading cached modals, if told to
@openModalView(view) @openModalView(view)
openModalView: (modalView) -> openModalView: (modalView) ->
@ -101,12 +102,12 @@ module.exports = class CocoView extends Backbone.View
waitingModal = modalView waitingModal = modalView
visibleModal.hide() visibleModal.hide()
return return
modalView.render() modalView.render()
$('#modal-wrapper').empty().append modalView.el $('#modal-wrapper').empty().append modalView.el
modalView.afterInsert() modalView.afterInsert()
visibleModal = modalView visibleModal = modalView
modalOptions = {show: true, backdrop: if modalView.closesOnClickOutside then true else 'static'} modalOptions = {show: true, backdrop: if modalView.closesOnClickOutside then true else 'static'}
$('#modal-wrapper .modal').modal(modalOptions).on('hidden.bs.modal', @modalClosed) $('#modal-wrapper .modal').modal(modalOptions).on 'hidden.bs.modal', @modalClosed
window.currentModal = modalView window.currentModal = modalView
@getRootView().stopListeningToShortcuts(true) @getRootView().stopListeningToShortcuts(true)
@ -114,7 +115,8 @@ module.exports = class CocoView extends Backbone.View
visibleModal.willDisappear() if visibleModal visibleModal.willDisappear() if visibleModal
visibleModal.destroy() visibleModal.destroy()
visibleModal = null visibleModal = null
if waitingModal $('#modal-wrapper .modal').off 'hidden.bs.modal', @modalClosed
if waitingModal
wm = waitingModal wm = waitingModal
waitingModal = null waitingModal = null
@openModalView(wm) @openModalView(wm)
@ -212,8 +214,8 @@ module.exports = class CocoView extends Backbone.View
slider.on('slide',changeCallback) slider.on('slide',changeCallback)
slider.on('slidechange',changeCallback) slider.on('slidechange',changeCallback)
slider slider
mobileRELong = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i mobileRELong = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i
mobileREShort = /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i mobileREShort = /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i

View file

@ -37,6 +37,7 @@ module.exports = class ControlBarView extends View
setBus: (@bus) -> setBus: (@bus) ->
onPlayerStatesChanged: (e) -> onPlayerStatesChanged: (e) ->
# TODO: this doesn't fire any more. Replacement?
return unless @bus is e.bus return unless @bus is e.bus
numPlayers = _.keys(e.players).length numPlayers = _.keys(e.players).length
return if numPlayers is @numPlayers return if numPlayers is @numPlayers

View file

@ -11,6 +11,8 @@ module.exports = class CastButtonView extends View
'tome:cast-spells': 'onCastSpells' 'tome:cast-spells': 'onCastSpells'
'god:world-load-progress-changed': 'onWorldLoadProgressChanged' 'god:world-load-progress-changed': 'onWorldLoadProgressChanged'
'god:new-world-created': 'onNewWorld' 'god:new-world-created': 'onNewWorld'
'click .cast-button': -> Backbone.Mediator.publish 'tome:manual-cast', {}
'click .cast-options a': 'onCastOptionsClick'
constructor: (options) -> constructor: (options) ->
super options super options
@ -26,8 +28,10 @@ module.exports = class CastButtonView extends View
afterRender: -> afterRender: ->
super() super()
@castButton = $('.cast-button', @$el)
@castButtonGroup = $('.cast-button-group', @$el)
@castOptions = $('.autocast-delays', @$el)
# TODO: use a User setting instead of localStorage # TODO: use a User setting instead of localStorage
@hookUpButtons()
delay = localStorage.getItem 'autocastDelay' delay = localStorage.getItem 'autocastDelay'
delay ?= 5000 delay ?= 5000
@setAutocastDelay delay @setAutocastDelay delay
@ -35,19 +39,11 @@ module.exports = class CastButtonView extends View
attachTo: (spellView) -> attachTo: (spellView) ->
@$el.detach().prependTo(spellView.toolbarView.$el).show() @$el.detach().prependTo(spellView.toolbarView.$el).show()
hookUpButtons: -> onCastOptionsClick: (e) ->
# hook up cast button callbacks Backbone.Mediator.publish 'focus-editor'
@castButton = $('.cast-button', @$el) @castButtonGroup.removeClass 'open'
@castButtonGroup = $('.cast-button-group', @$el) @setAutocastDelay $(e.target).attr 'data-delay'
@castOptions = $('.autocast-delays', @$el) false
@castButton.click (e) =>
Backbone.Mediator.publish 'tome:manual-cast', {}
@castOptions.find('a').click (e) =>
Backbone.Mediator.publish 'focus-editor'
@castButtonGroup.removeClass 'open'
@setAutocastDelay $(e.target).attr 'data-delay'
false
onSpellChanged: (e) -> onSpellChanged: (e) ->
@updateCastButton() @updateCastButton()

View file

@ -60,7 +60,7 @@ module.exports = class DebugView extends View
@variableChain = @markerRange = null @variableChain = @markerRange = null
@update() @update()
onMouseOut: (e) => onMouseOut: (e) ->
@variableChain = @markerRange = null @variableChain = @markerRange = null
@update() @update()
@ -138,3 +138,4 @@ module.exports = class DebugView extends View
destroy: -> destroy: ->
super() super()
@ace?.removeEventListener "mousemove", @onMouseMove @ace?.removeEventListener "mousemove", @onMouseMove
@onMouseMove = null

View file

@ -60,3 +60,4 @@ module.exports = class SpellPaletteView extends View
destroy: -> destroy: ->
super() super()
entry.destroy() for entry in @entries entry.destroy() for entry in @entries
@toggleBackground = null

View file

@ -74,47 +74,52 @@ module.exports = class SpellView extends View
$(@ace.container).find('.ace_gutter').on 'click', '.ace_error, .ace_warning, .ace_info', @onAnnotationClick $(@ace.container).find('.ace_gutter').on 'click', '.ace_error, .ace_warning, .ace_info', @onAnnotationClick
createACEShortcuts: -> createACEShortcuts: ->
@ace.commands.addCommand @aceCommands = aceCommands = []
ace = @ace
addCommand = (c) ->
ace.commands.addCommand c
aceCommands.push c.name
addCommand
name: 'run-code' name: 'run-code'
bindKey: {win: 'Shift-Enter|Ctrl-Enter|Ctrl-S', mac: 'Shift-Enter|Command-Enter|Ctrl-Enter|Command-S|Ctrl-S'} bindKey: {win: 'Shift-Enter|Ctrl-Enter|Ctrl-S', mac: 'Shift-Enter|Command-Enter|Ctrl-Enter|Command-S|Ctrl-S'}
exec: (e) => @recompile() exec: -> Backbone.Mediator.publish 'tome:manual-cast', {}
@ace.commands.addCommand addCommand
name: 'toggle-playing' name: 'toggle-playing'
bindKey: {win: 'Ctrl-P', mac: 'Command-P|Ctrl-P'} bindKey: {win: 'Ctrl-P', mac: 'Command-P|Ctrl-P'}
exec: -> Backbone.Mediator.publish 'level-toggle-playing' exec: -> Backbone.Mediator.publish 'level-toggle-playing'
@ace.commands.addCommand addCommand
name: 'end-current-script' name: 'end-current-script'
bindKey: {win: 'Shift-Space', mac: 'Shift-Space'} bindKey: {win: 'Shift-Space', mac: 'Shift-Space'}
exec: -> Backbone.Mediator.publish 'level:shift-space-pressed' exec: -> Backbone.Mediator.publish 'level:shift-space-pressed'
@ace.commands.addCommand addCommand
name: 'end-all-scripts' name: 'end-all-scripts'
bindKey: {win: 'Escape', mac: 'Escape'} bindKey: {win: 'Escape', mac: 'Escape'}
exec: -> Backbone.Mediator.publish 'level:escape-pressed' exec: -> Backbone.Mediator.publish 'level:escape-pressed'
@ace.commands.addCommand addCommand
name: 'toggle-grid' name: 'toggle-grid'
bindKey: {win: 'Ctrl-G', mac: 'Command-G|Ctrl-G'} bindKey: {win: 'Ctrl-G', mac: 'Command-G|Ctrl-G'}
exec: -> Backbone.Mediator.publish 'level-toggle-grid' exec: -> Backbone.Mediator.publish 'level-toggle-grid'
@ace.commands.addCommand addCommand
name: 'toggle-debug' name: 'toggle-debug'
bindKey: {win: 'Ctrl-\\', mac: 'Command-\\|Ctrl-\\'} bindKey: {win: 'Ctrl-\\', mac: 'Command-\\|Ctrl-\\'}
exec: -> Backbone.Mediator.publish 'level-toggle-debug' exec: -> Backbone.Mediator.publish 'level-toggle-debug'
@ace.commands.addCommand addCommand
name: 'toggle-pathfinding' name: 'toggle-pathfinding'
bindKey: {win: 'Ctrl-O', mac: 'Command-O|Ctrl-O'} bindKey: {win: 'Ctrl-O', mac: 'Command-O|Ctrl-O'}
exec: -> Backbone.Mediator.publish 'level-toggle-pathfinding' exec: -> Backbone.Mediator.publish 'level-toggle-pathfinding'
@ace.commands.addCommand addCommand
name: 'level-scrub-forward' name: 'level-scrub-forward'
bindKey: {win: 'Ctrl-]', mac: 'Command-]|Ctrl-]'} bindKey: {win: 'Ctrl-]', mac: 'Command-]|Ctrl-]'}
exec: -> Backbone.Mediator.publish 'level-scrub-forward' exec: -> Backbone.Mediator.publish 'level-scrub-forward'
@ace.commands.addCommand addCommand
name: 'level-scrub-back' name: 'level-scrub-back'
bindKey: {win: 'Ctrl-[', mac: 'Command-[|Ctrl-]'} bindKey: {win: 'Ctrl-[', mac: 'Command-[|Ctrl-]'}
exec: -> Backbone.Mediator.publish 'level-scrub-back' exec: -> Backbone.Mediator.publish 'level-scrub-back'
@ace.commands.addCommand addCommand
name: 'spell-step-forward' name: 'spell-step-forward'
bindKey: {win: 'Ctrl-Alt-]', mac: 'Command-Alt-]|Ctrl-Alt-]'} bindKey: {win: 'Ctrl-Alt-]', mac: 'Command-Alt-]|Ctrl-Alt-]'}
exec: -> Backbone.Mediator.publish 'spell-step-forward' exec: -> Backbone.Mediator.publish 'spell-step-forward'
@ace.commands.addCommand addCommand
name: 'spell-step-backward' name: 'spell-step-backward'
bindKey: {win: 'Ctrl-Alt-[', mac: 'Command-Alt-[|Ctrl-Alt-]'} bindKey: {win: 'Ctrl-Alt-[', mac: 'Command-Alt-[|Ctrl-Alt-]'}
exec: -> Backbone.Mediator.publish 'spell-step-backward' exec: -> Backbone.Mediator.publish 'spell-step-backward'
@ -212,7 +217,10 @@ module.exports = class SpellView extends View
@updateACEText @spell.originalSource @updateACEText @spell.originalSource
@recompile cast @recompile cast
recompile: (cast=true) => recompileIfNeeded: =>
@recompile() if @recompileNeeded
recompile: (cast=true) ->
@setRecompileNeeded false @setRecompileNeeded false
return if @spell.source is @getSource() return if @spell.source is @getSource()
@spell.transpile @getSource() @spell.transpile @getSource()
@ -238,7 +246,7 @@ module.exports = class SpellView extends View
autocastDelay = @autocastDelay ? 3000 autocastDelay = @autocastDelay ? 3000
onSignificantChange = [ onSignificantChange = [
_.debounce @setRecompileNeeded, autocastDelay - 100 _.debounce @setRecompileNeeded, autocastDelay - 100
@currentAutocastHandler = _.debounce (=> @recompile() if @recompileNeeded), autocastDelay @currentAutocastHandler = _.debounce @recompileIfNeeded, autocastDelay
] ]
onAnyChange = [ onAnyChange = [
_.debounce @updateAether, 500 _.debounce @updateAether, 500
@ -500,9 +508,17 @@ module.exports = class SpellView extends View
destroy: -> destroy: ->
super() super()
$(@ace?.container).find('.ace_gutter').off 'click', '.ace_error, .ace_warning, .ace_info', @onAnnotationClick
@firepad?.dispose() @firepad?.dispose()
@ace.destroy() @ace?.commands.removeCommand command for command in @aceCommands
@ace?.destroy()
@ace = null @ace = null
@aceDoc?.off 'change', @onCodeChangeMetaHandler
@aceDoc = null
@aceSession?.selection.off 'changeCursor', @onCursorActivity
@aceSession = null
@debugView?.destroy() @debugView?.destroy()
@spell = null @spell = null
@session.off 'change:multiplayer', @onMultiplayerChanged, @ @session.off 'change:multiplayer', @onMultiplayerChanged, @
for fat in ['notifySpellChanged', 'notifyEditingEnded', 'notifyEditingBegan', 'onFirepadLoaded', 'onLoaded', 'toggleBackground', 'setRecompileNeeded', 'onCursorActivity', 'highlightCurrentLine', 'updateAether', 'onCodeChangeMetaHandler', 'recompileIfNeeded', 'currentAutocastHandler']
@[fat] = null

View file

@ -97,12 +97,12 @@ module.exports = class ThangListEntryView extends View
@$el.popover('setContent').popover('show') @$el.popover('setContent').popover('show')
@$el.parent().parent().parent().i18n() @$el.parent().parent().parent().i18n()
clearTimeout @hideSpellsTimeout if @hideSpellsTimeout clearTimeout @hideSpellsTimeout if @hideSpellsTimeout
popover = @$el.parent().parent().parent().find('.popover') @popover = @$el.parent().parent().parent().find('.popover')
popover.off 'mouseenter mouseleave' @popover.off 'mouseenter mouseleave'
popover.mouseenter (e) => @onMouseEnter() @popover.mouseenter (e) => @onMouseEnter()
popover.mouseleave (e) => @onMouseLeave() @popover.mouseleave (e) => @onMouseLeave()
thangID = @thang.id thangID = @thang.id
popover.find('code').click (e) -> @popover.find('code').click (e) ->
Backbone.Mediator.publish "level-select-sprite", thangID: thangID, spellName: $(@).data 'spell-name' Backbone.Mediator.publish "level-select-sprite", thangID: thangID, spellName: $(@).data 'spell-name'
hideSpells: => hideSpells: =>
@ -139,3 +139,5 @@ module.exports = class ThangListEntryView extends View
destroy: -> destroy: ->
super() super()
@avatar?.destroy() @avatar?.destroy()
@popover?.off 'mouseenter mouseleave'
@popover?.find('code').off 'click'

View file

@ -76,9 +76,7 @@ module.exports = class PlayLevelView extends View
@sessionID = @getQueryVariable 'session' @sessionID = @getQueryVariable 'session'
$(window).on('resize', @onWindowResize) $(window).on('resize', @onWindowResize)
@supermodel.once 'error', => @supermodel.once 'error', @onLevelLoadError
msg = $.i18n.t('play_level.level_load_error', defaultValue: "Level could not be loaded.")
@$el.html('<div class="alert">' + msg + '</div>')
@saveScreenshot = _.throttle @saveScreenshot, 30000 @saveScreenshot = _.throttle @saveScreenshot, 30000
if @isEditorPreview if @isEditorPreview
@ -94,6 +92,10 @@ module.exports = class PlayLevelView extends View
if localStorage? if localStorage?
localStorage["lastLevel"] = @levelID localStorage["lastLevel"] = @levelID
onLevelLoadError: (e) =>
msg = $.i18n.t('play_level.level_load_error', defaultValue: "Level could not be loaded.")
@$el.html('<div class="alert">' + msg + '</div>')
setLevel: (@level, @supermodel) -> setLevel: (@level, @supermodel) ->
@god?.level = @level.serialize @supermodel @god?.level = @level.serialize @supermodel
if @world if @world
@ -181,21 +183,21 @@ module.exports = class PlayLevelView extends View
onWindowResize: (s...) -> onWindowResize: (s...) ->
$('#pointer').css('opacity', 0.0) $('#pointer').css('opacity', 0.0)
onDisableControls: (e) => onDisableControls: (e) ->
return if e.controls and not ('level' in e.controls) return if e.controls and not ('level' in e.controls)
@shortcutsEnabled = false @shortcutsEnabled = false
@wasFocusedOn = document.activeElement @wasFocusedOn = document.activeElement
$('body').focus() $('body').focus()
onEnableControls: (e) => onEnableControls: (e) ->
return if e.controls? and not ('level' in e.controls) return if e.controls? and not ('level' in e.controls)
@shortcutsEnabled = true @shortcutsEnabled = true
$(@wasFocusedOn).focus() if @wasFocusedOn $(@wasFocusedOn).focus() if @wasFocusedOn
@wasFocusedOn = null @wasFocusedOn = null
onDonePressed: => @showVictory() onDonePressed: -> @showVictory()
onShowVictory: (e) => onShowVictory: (e) ->
console.log 'show vict', e console.log 'show vict', e
$('#level-done-button').show() $('#level-done-button').show()
@showVictory() if e.showModal @showVictory() if e.showModal
@ -221,7 +223,7 @@ module.exports = class PlayLevelView extends View
@openModalView new InfiniteLoopModal() @openModalView new InfiniteLoopModal()
window.tracker?.trackEvent 'Saw Initial Infinite Loop', level: @world.name, label: @world.name window.tracker?.trackEvent 'Saw Initial Infinite Loop', level: @world.name, label: @world.name
onPlayNextLevel: => onPlayNextLevel: ->
nextLevel = @getNextLevel() nextLevel = @getNextLevel()
nextLevelID = nextLevel.get('slug') or nextLevel.id nextLevelID = nextLevel.get('slug') or nextLevel.id
url = "/play/level/#{nextLevelID}" url = "/play/level/#{nextLevelID}"
@ -235,7 +237,7 @@ module.exports = class PlayLevelView extends View
levels = @supermodel.getModels(Level) levels = @supermodel.getModels(Level)
return l for l in levels when l.get('original') is nextLevelOriginal return l for l in levels when l.get('original') is nextLevelOriginal
onHighlightDom: (e) => onHighlightDom: (e) ->
if e.delay if e.delay
delay = e.delay delay = e.delay
delete e.delay delete e.delay
@ -289,16 +291,16 @@ module.exports = class PlayLevelView extends View
), 1) ), 1)
animatePointer: => animatePointer: ->
pointer = $('#pointer') pointer = $('#pointer')
pointer.css('transition', 'all 0.6s ease-out') pointer.css('transition', 'all 0.6s ease-out')
pointer.css('transform', "rotate(#{@pointerRotation}rad) translate(-3px, #{@pointerRadialDistance-50}px)") pointer.css('transform', "rotate(#{@pointerRotation}rad) translate(-3px, #{@pointerRadialDistance-50}px)")
setTimeout((=> setTimeout((=>
pointer.css('transform', "rotate(#{@pointerRotation}rad) translate(-3px, #{@pointerRadialDistance}px)").css('transition', 'all 0.4s ease-in')), 800) pointer.css('transform', "rotate(#{@pointerRotation}rad) translate(-3px, #{@pointerRadialDistance}px)").css('transition', 'all 0.4s ease-in')), 800)
onFocusDom: (e) => $(e.selector).focus() onFocusDom: (e) -> $(e.selector).focus()
onEndHighlight: => onEndHighlight: ->
$('#pointer').css('opacity', 0.0) $('#pointer').css('opacity', 0.0)
clearInterval(@pointerInterval) clearInterval(@pointerInterval)
@ -380,6 +382,8 @@ module.exports = class PlayLevelView extends View
destroy: -> destroy: ->
super() super()
@supermodel.off 'error', @onLevelLoadError
@levelLoader?.off 'loaded-all', @onLevelLoaderLoaded
@levelLoader?.destroy() @levelLoader?.destroy()
@surface?.destroy() @surface?.destroy()
@god?.destroy() @god?.destroy()
@ -393,3 +397,8 @@ module.exports = class PlayLevelView extends View
#@instance.save() unless @instance.loading #@instance.save() unless @instance.loading
console.profileEnd?() if PROFILE_ME console.profileEnd?() if PROFILE_ME
@session.off 'change:multiplayer', @onMultiplayerChanged, @ @session.off 'change:multiplayer', @onMultiplayerChanged, @
@onLevelLoadError = null
@onLevelLoaderLoaded = null
@onSupermodelLoadedOne = null
@preloadNextLevel = null
@saveScreenshot = null

View file

@ -44,7 +44,9 @@ function checkNodeVersion {
checkDependencies deps[@] basicDependenciesErrorHandling checkDependencies deps[@] basicDependenciesErrorHandling
#check for node #check for node
checkNodeVersion if command -v node >/dev/null 2>&1; then
checkNodeVersion
fi
#install git repository #install git repository
git clone $repositoryUrl coco git clone $repositoryUrl coco
#python ./coco/scripts/devSetup/setup.py #python ./coco/scripts/devSetup/setup.py

View file

@ -1,6 +1,11 @@
from __future__ import print_function
__author__ = 'schmatz' __author__ = 'schmatz'
from configuration import Configuration from configuration import Configuration
import urllib import sys
if sys.version_info.major < 3:
import urllib
else:
import urllib.request as urllib
from dependency import Dependency from dependency import Dependency
class Downloader: class Downloader:
def __init__(self,dependency): def __init__(self,dependency):
@ -27,10 +32,10 @@ class Downloader:
progress_fraction = float(amount_of_data_downloaded_so_far) / float(totalsize) progress_fraction = float(amount_of_data_downloaded_so_far) / float(totalsize)
progress_percentage = progress_fraction * 1e2 progress_percentage = progress_fraction * 1e2
stringToDisplay = '\r[{0}] {1:.1f}%'.format('#'*int(bars_to_display*progress_fraction),progress_percentage) stringToDisplay = '\r[{0}] {1:.1f}%'.format('#'*int(bars_to_display*progress_fraction),progress_percentage)
print stringToDisplay, print(stringToDisplay,end=' ')
if amount_of_data_downloaded_so_far >= totalsize: if amount_of_data_downloaded_so_far >= totalsize:
print "\n", print("\n",end=' ')
else: else:
stringToDisplay = '\r File size unknown. Read {0} bytes.'.format(amount_of_data_downloaded_so_far) stringToDisplay = '\r File size unknown. Read {0} bytes.'.format(amount_of_data_downloaded_so_far)
print stringToDisplay, print(stringToDisplay,end=' ')

View file

@ -1,3 +1,4 @@
from __future__ import print_function
__author__ = u'schmatz' __author__ = u'schmatz'
from downloader import Downloader from downloader import Downloader
import tarfile import tarfile
@ -6,6 +7,8 @@ import warnings
import os import os
from configuration import Configuration from configuration import Configuration
from dependency import Dependency from dependency import Dependency
import sys
class MongoDB(Dependency): class MongoDB(Dependency):
def __init__(self,configuration): def __init__(self,configuration):
@ -39,7 +42,7 @@ class MongoDB(Dependency):
def findUnzippedMongoBinPath(self): def findUnzippedMongoBinPath(self):
return self.downloader.download_directory + os.sep + \ return self.downloader.download_directory + os.sep + \
(os.walk(self.downloader.download_directory).next()[1])[0] + os.sep + u"bin" (next(os.walk(self.downloader.download_directory))[1])[0] + os.sep + u"bin"
@ -55,15 +58,15 @@ class MongoDBDownloader(Downloader):
def downloaded_file_path(self): def downloaded_file_path(self):
return self.download_directory + os.sep + u"mongodb.tgz" return self.download_directory + os.sep + u"mongodb.tgz"
def download(self): def download(self):
print u"Downloading MongoDB from URL " + self.download_url print(u"Downloading MongoDB from URL " + self.download_url)
self.download_file(self.download_url,self.downloaded_file_path) self.download_file(self.download_url,self.downloaded_file_path)
self.check_download() self.check_download()
def decompress(self): def decompress(self):
print u"Decompressing MongoDB..." print(u"Decompressing MongoDB...")
tfile = tarfile.open(self.downloaded_file_path) tfile = tarfile.open(self.downloaded_file_path)
#TODO: make directory handler class #TODO: make directory handler class
tfile.extractall(self.download_directory) tfile.extractall(self.download_directory)
print u"Decompressed MongoDB into " + self.download_directory print(u"Decompressed MongoDB into " + self.download_directory)
def check_download(self): def check_download(self):
isFileValid = tarfile.is_tarfile(self.downloaded_file_path) isFileValid = tarfile.is_tarfile(self.downloaded_file_path)

View file

@ -1,3 +1,4 @@
from __future__ import print_function
__author__ = u'schmatz' __author__ = u'schmatz'
from downloader import Downloader from downloader import Downloader
import tarfile import tarfile
@ -10,6 +11,12 @@ from dependency import Dependency
import shutil import shutil
from which import which from which import which
import subprocess import subprocess
from stat import S_IRWXU,S_IRWXG,S_IRWXO
import sys
if sys.version_info.major >= 3:
raw_input = input
class Node(Dependency): class Node(Dependency):
def __init__(self,configuration): def __init__(self,configuration):
super(self.__class__, self).__init__(configuration) super(self.__class__, self).__init__(configuration)
@ -39,39 +46,42 @@ class Node(Dependency):
#check for node here #check for node here
unzipped_node_path = self.findUnzippedNodePath() unzipped_node_path = self.findUnzippedNodePath()
if self.config.system.operating_system in ["mac","linux"] and not which("node"): if self.config.system.operating_system in ["mac","linux"] and not which("node"):
print "Copying node into /usr/local/bin/..." print("Copying node into /usr/local/bin/...")
shutil.copy(unzipped_node_path + os.sep + "bin" + os.sep + "node","/usr/local/bin/") shutil.copy(unzipped_node_path + os.sep + "bin" + os.sep + "node","/usr/local/bin/")
os.chmod("/usr/local/bin/node",0777) os.chmod("/usr/local/bin/node",S_IRWXG|S_IRWXO|S_IRWXU)
shutil.copytree(self.findUnzippedNodePath(),install_directory) shutil.copytree(self.findUnzippedNodePath(),install_directory)
wants_to_upgrade = True wants_to_upgrade = True
if self.check_if_executable_installed(u"npm"): if self.check_if_executable_installed(u"npm"):
warning_string = u"A previous version of npm has been found. \nYou may experience problems if you have a version of npm that's too old.Would you like to upgrade?(y/n) " warning_string = u"A previous version of npm has been found. \nYou may experience problems if you have a version of npm that's too old.Would you like to upgrade?(y/n) "
from distutils.util import strtobool from distutils.util import strtobool
print warning_string print(warning_string)
#for bash script, you have to somehow redirect stdin to raw_input() #for bash script, you have to somehow redirect stdin to raw_input()
user_input = raw_input() user_input = raw_input()
while True: while True:
try: try:
wants_to_upgrade = strtobool(user_input) wants_to_upgrade = strtobool(user_input)
except: except:
print u"Please enter y or n. " print(u"Please enter y or n. ")
user_input = raw_input() user_input = raw_input()
continue continue
break break
if wants_to_upgrade: if wants_to_upgrade:
import urllib2, urllib if sys.version_info.major < 3:
print u"Retrieving npm update script..." import urllib2, urllib
else:
import urllib.request as urllib
print(u"Retrieving npm update script...")
npm_install_script_path = install_directory + os.sep + u"install.sh" npm_install_script_path = install_directory + os.sep + u"install.sh"
urllib.urlretrieve(u"https://npmjs.org/install.sh",filename=npm_install_script_path) urllib.urlretrieve(u"https://npmjs.org/install.sh",filename=npm_install_script_path)
print u"Retrieved npm install script. Executing..." print(u"Retrieved npm install script. Executing...")
subprocess.call([u"sh", npm_install_script_path]) subprocess.call([u"sh", npm_install_script_path])
print u"Updated npm version installed" print(u"Updated npm version installed")
def findUnzippedNodePath(self): def findUnzippedNodePath(self):
return self.downloader.download_directory + os.sep + \ return self.downloader.download_directory + os.sep + \
(os.walk(self.downloader.download_directory).next()[1])[0] (next(os.walk(self.downloader.download_directory))[1])[0]
def check_if_executable_installed(self,name): def check_if_executable_installed(self,name):
executable_path = which(name) executable_path = which(name)
if executable_path: if executable_path:
@ -98,15 +108,15 @@ class NodeDownloader(Downloader):
def downloaded_file_path(self): def downloaded_file_path(self):
return self.download_directory + os.sep + u"node.tgz" return self.download_directory + os.sep + u"node.tgz"
def download(self): def download(self):
print u"Downloading Node from URL " + self.download_url print(u"Downloading Node from URL " + self.download_url)
self.download_file(self.download_url,self.downloaded_file_path) self.download_file(self.download_url,self.downloaded_file_path)
self.check_download() self.check_download()
def decompress(self): def decompress(self):
print u"Decompressing Node..." print(u"Decompressing Node...")
tfile = tarfile.open(self.downloaded_file_path) tfile = tarfile.open(self.downloaded_file_path)
#TODO: make directory handler class #TODO: make directory handler class
tfile.extractall(self.download_directory) tfile.extractall(self.download_directory)
print u"Decompressed Node into " + self.download_directory print(u"Decompressed Node into " + self.download_directory)
def check_download(self): def check_download(self):
isFileValid = tarfile.is_tarfile(self.downloaded_file_path) isFileValid = tarfile.is_tarfile(self.downloaded_file_path)

View file

@ -1,3 +1,4 @@
from __future__ import print_function
__author__ = u'schmatz' __author__ = u'schmatz'
import configuration import configuration
import errors import errors
@ -36,11 +37,11 @@ class RepositoryInstaller():
else: else:
return False return False
def cloneRepository(self): def cloneRepository(self):
print u"Cloning repository..." print(u"Cloning repository...")
#TODO: CHANGE THIS BEFORE LAUNCH #TODO: CHANGE THIS BEFORE LAUNCH
return_code = True return_code = True
git_folder = self.config.directory.root_install_directory + os.sep + "coco" git_folder = self.config.directory.root_install_directory + os.sep + "coco"
print "Installing into " + git_folder print("Installing into " + git_folder)
return_code = subprocess.call("git clone " + self.config.repository_url +" coco",cwd=self.config.directory.root_install_directory,shell=True) return_code = subprocess.call("git clone " + self.config.repository_url +" coco",cwd=self.config.directory.root_install_directory,shell=True)
#TODO: remove this on windos #TODO: remove this on windos
subprocess.call("chown -R " +git_folder + " 0777",shell=True) subprocess.call("chown -R " +git_folder + " 0777",shell=True)
@ -51,15 +52,15 @@ class RepositoryInstaller():
#sys.stdout.flush() #sys.stdout.flush()
raw_input(u"Copy it now") raw_input(u"Copy it now")
#shutil.copytree(u"/Users/schmatz/coco",self.config.directory.root_install_directory + os.sep + u"coco") #shutil.copytree(u"/Users/schmatz/coco",self.config.directory.root_install_directory + os.sep + u"coco")
print u"Copied tree just for you" print(u"Copied tree just for you")
#print("FAILED TO CLONE GIT REPOSITORY") #print("FAILED TO CLONE GIT REPOSITORY")
#input("Clone the repository and click any button to continue") #input("Clone the repository and click any button to continue")
elif self.config.system.operating_system == u"windows": elif self.config.system.operating_system == u"windows":
raise errors.CoCoError(u"Windows doesn't support automated installations of npm at this point.") raise errors.CoCoError(u"Windows doesn't support automated installations of npm at this point.")
else: else:
print u"Cloned git repository" print(u"Cloned git repository")
def install_node_packages(self): def install_node_packages(self):
print u"Installing node packages..." print(u"Installing node packages...")
#TODO: "Replace npm with more robust package #TODO: "Replace npm with more robust package
#npm_location = self.config.directory.bin_directory + os.sep + "node" + os.sep + "bin" + os.sep + "npm" #npm_location = self.config.directory.bin_directory + os.sep + "node" + os.sep + "bin" + os.sep + "npm"
npm_location = u"npm" npm_location = u"npm"
@ -67,4 +68,4 @@ class RepositoryInstaller():
if return_code: if return_code:
raise errors.CoCoError(u"Failed to install node packages") raise errors.CoCoError(u"Failed to install node packages")
else: else:
print u"Installed node packages!" print(u"Installed node packages!")

View file

@ -1,3 +1,4 @@
from __future__ import print_function
__author__ = u'root' __author__ = u'root'
import dependency import dependency
@ -18,7 +19,7 @@ class Ruby(dependency.Dependency):
elif not is_ruby_installed: elif not is_ruby_installed:
self.install_ruby() self.install_ruby()
elif is_ruby_installed and is_gem_installed: elif is_ruby_installed and is_gem_installed:
print u"Ruby found." print(u"Ruby found.")
def check_if_ruby_exists(self): def check_if_ruby_exists(self):
ruby_path = which(u"ruby") ruby_path = which(u"ruby")
return bool(ruby_path) return bool(ruby_path)
@ -38,4 +39,4 @@ class Ruby(dependency.Dependency):
raise NotImplementedError raise NotImplementedError
def install_gems(self): def install_gems(self):
gem_install_status = subprocess.call([u"gem",u"install",u"sass"]) gem_install_status = subprocess.call([u"gem",u"install",u"sass"])

View file

@ -22,7 +22,10 @@ class SystemConfiguration(object):
raise NotSupportedError(u"Your platform," + sys.platform + u",isn't supported.") raise NotSupportedError(u"Your platform," + sys.platform + u",isn't supported.")
def get_current_working_directory(self): def get_current_working_directory(self):
return os.getcwdu() if sys.version_info.major < 3:
return os.getcwdu()
else:
return os.getcwd()
def get_virtual_memory_address_width(self): def get_virtual_memory_address_width(self):
is64Bit = sys.maxsize/3 > 2**32 is64Bit = sys.maxsize/3 > 2**32

View file

@ -223,6 +223,7 @@ _.extend LevelSchema.properties,
victory: c.object {title: "Victory Screen", default: {}, properties: {'body': {type: 'string', format: 'markdown', title: 'Body Text', description: 'Inserted into the Victory Modal once this level is complete. Tell the player they did a good job and what they accomplished!'}, i18n: {type: "object", format: 'i18n', props: ['body'], description: "Help translate this victory message"}}} victory: c.object {title: "Victory Screen", default: {}, properties: {'body': {type: 'string', format: 'markdown', title: 'Body Text', description: 'Inserted into the Victory Modal once this level is complete. Tell the player they did a good job and what they accomplished!'}, i18n: {type: "object", format: 'i18n', props: ['body'], description: "Help translate this victory message"}}}
i18n: {type: "object", format: 'i18n', props: ['name', 'description'], description: "Help translate this level"} i18n: {type: "object", format: 'i18n', props: ['name', 'description'], description: "Help translate this level"}
icon: { type: 'string', format: 'image-file', title: 'Icon' } icon: { type: 'string', format: 'image-file', title: 'Icon' }
goals: c.array {title: 'Goals', description: 'An array of goals which are visible to the player and can trigger scripts.'}, GoalSchema
c.extendBasicProperties LevelSchema, 'level' c.extendBasicProperties LevelSchema, 'level'