mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-04-26 14:03:28 -04:00
Merge remote-tracking branch 'cocomaster/master'
This commit is contained in:
commit
3bf05ad5c4
150 changed files with 3308 additions and 1927 deletions
CONTRIBUTING.mdhome_view.coffee
app
assets
lib
AudioPlayer.coffeeCocoClass.coffeeGPlusHandler.coffeeGod.coffeeLevelBus.coffeeLevelLoader.coffeeRouter.coffeeerrors.coffee
scripts
simulator
storage.coffeesurface
Camera.coffeeCocoSprite.coffeeDimmer.coffeeMark.coffeeMusicPlayer.coffeePointChooser.coffeeRegionChooser.coffeeSpriteBoss.coffeeSurface.coffeeWizardSprite.coffeepath.coffee
world
locale
models
styles
templates
treema-ext.coffeeviews
account
editor
article
components
level
component
edit.coffeefork_view.coffeesave_view.coffeescripts_tab_view.coffeesettings_tab_view.coffeesystem
thang
thangs_tab_view.coffeethang
kinds
modal
play
|
@ -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.
|
||||
|
||||
For more info, see [http://codecombat.com/legal](http://codecombat.com/legal).
|
||||
For more info, see [http://codecombat.com/legal](http://codecombat.com/legal).
|
||||
|
|
BIN
app/assets/images/level/prop_gold.png
Normal file
BIN
app/assets/images/level/prop_gold.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 5.2 KiB |
|
@ -64,7 +64,7 @@ self.transferableSupported = transferableSupported = ->
|
|||
if args.level
|
||||
self.world.loadFromLevel args.level, true
|
||||
self.goalManager = new GoalManager self.world
|
||||
self.goalManager.setGoals args.goals
|
||||
self.goalManager.setGoals args.level?.goals or args.goals
|
||||
self.goalManager.setCode args.userCodeMap
|
||||
self.goalManager.worldGenerationWillBegin()
|
||||
self.world.setGoalManager self.goalManager
|
||||
|
|
|
@ -33,7 +33,7 @@ class Media
|
|||
|
||||
class AudioPlayer extends CocoClass
|
||||
subscriptions:
|
||||
'play-sound': (e) -> @playInterfaceSound e.trigger
|
||||
'play-sound': (e) -> @playInterfaceSound e.trigger, e.volume
|
||||
|
||||
constructor: () ->
|
||||
super()
|
||||
|
@ -70,17 +70,17 @@ class AudioPlayer extends CocoClass
|
|||
filename = "/file/interface/#{name}#{@ext}"
|
||||
@preloadSound filename, name
|
||||
|
||||
playInterfaceSound: (name) ->
|
||||
playInterfaceSound: (name, volume=1) ->
|
||||
filename = "/file/interface/#{name}#{@ext}"
|
||||
if filename of cache and createjs.Sound.loadComplete filename
|
||||
@playSound name
|
||||
@playSound name, volume
|
||||
createjs.Sound.play name
|
||||
else
|
||||
@preloadInterfaceSounds [name] unless filename of cache
|
||||
@soundsToPlayWhenLoaded[name] = true
|
||||
|
||||
playSound: (name) ->
|
||||
createjs.Sound.play name, {volume: me.get('volume')}
|
||||
@soundsToPlayWhenLoaded[name] = volume
|
||||
|
||||
playSound: (name, volume=1) ->
|
||||
createjs.Sound.play name, {volume: me.get('volume') * volume}
|
||||
|
||||
# # TODO: load Interface sounds somehow, somewhere, somewhen
|
||||
|
||||
|
@ -108,8 +108,8 @@ class AudioPlayer extends CocoClass
|
|||
return if not media
|
||||
media.loaded = true
|
||||
media.progress = 1.0
|
||||
if @soundsToPlayWhenLoaded[media.name]
|
||||
@playSound media.name
|
||||
if volume = @soundsToPlayWhenLoaded[media.name]
|
||||
@playSound media.name, volume
|
||||
@soundsToPlayWhenLoaded[media.name] = false
|
||||
@notifyProgressChanged()
|
||||
|
||||
|
@ -124,9 +124,3 @@ class AudioPlayer extends CocoClass
|
|||
|
||||
|
||||
module.exports = new AudioPlayer()
|
||||
|
||||
average = (numbers) ->
|
||||
return 0 if numbers.length is 0
|
||||
sum = 0
|
||||
sum += num for num in numbers
|
||||
return sum / numbers.length
|
||||
|
|
|
@ -6,7 +6,7 @@ makeScopeName = -> "class-scope-#{classCount++}"
|
|||
module.exports = class CocoClass
|
||||
subscriptions: {}
|
||||
shortcuts: {}
|
||||
|
||||
|
||||
# setup/teardown
|
||||
|
||||
constructor: ->
|
||||
|
@ -19,13 +19,15 @@ module.exports = class CocoClass
|
|||
|
||||
destroy: ->
|
||||
# teardown subscriptions, prevent new ones
|
||||
@destroyed = true
|
||||
@stopListening?()
|
||||
@unsubscribeAll()
|
||||
@stopListeningToShortcuts()
|
||||
@[key] = undefined for key of @
|
||||
@destroyed = true
|
||||
@destroy = ->
|
||||
|
||||
# subscriptions
|
||||
|
||||
|
||||
listenToSubscriptions: ->
|
||||
# for initting subscriptions
|
||||
return unless Backbone?.Mediator?
|
||||
|
@ -46,7 +48,7 @@ module.exports = class CocoClass
|
|||
for channel, func of @subscriptions
|
||||
func = utils.normalizeFunc(func, @)
|
||||
Backbone.Mediator.unsubscribe(channel, func, @)
|
||||
|
||||
|
||||
# keymaster shortcuts
|
||||
|
||||
listenToShortcuts: ->
|
||||
|
@ -54,7 +56,7 @@ module.exports = class CocoClass
|
|||
for shortcut, func of @shortcuts
|
||||
func = utils.normalizeFunc(func, @)
|
||||
key(shortcut, @scope, _.bind(func, @))
|
||||
|
||||
|
||||
stopListeningToShortcuts: ->
|
||||
return unless key?
|
||||
key.deleteScope(@scope)
|
||||
key.deleteScope(@scope)
|
||||
|
|
|
@ -76,6 +76,3 @@ module.exports = GPlusHandler = class GPlusHandler extends CocoClass
|
|||
storage.save(CURRENT_USER_KEY, model.attributes)
|
||||
window.location.reload()
|
||||
})
|
||||
|
||||
destroy: ->
|
||||
super()
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
{now} = require './world/world_utils'
|
||||
{now} = require 'lib/world/world_utils'
|
||||
World = require 'lib/world/world'
|
||||
|
||||
## Uncomment to imitate IE9 (and in world_utils.coffee)
|
||||
#window.Worker = null
|
||||
|
@ -11,19 +12,40 @@ module.exports = class God
|
|||
@lastID = (if @lastID? then @lastID + 1 else Math.floor(@ids.length * Math.random())) % @ids.length
|
||||
@ids[@lastID]
|
||||
|
||||
maxAngels: 2 # how many concurrent web workers to use; if set past 8, make up more names
|
||||
worldWaiting: false # whether we're waiting for a worker to free up and run the world
|
||||
constructor: (@world, @level) ->
|
||||
constructor: (options) ->
|
||||
@id = God.nextID()
|
||||
options ?= {}
|
||||
@maxAngels = options.maxAngels ? 2 # How many concurrent web workers to use; if set past 8, make up more names
|
||||
@maxWorkerPoolSize = options.maxWorkerPoolSize ? 2 # ~20MB per idle worker
|
||||
@angels = []
|
||||
@firstWorld = true
|
||||
Backbone.Mediator.subscribe 'tome:cast-spells', @onTomeCast, @
|
||||
@fillWorkerPool = _.throttle @fillWorkerPool, 3000, leading: false
|
||||
@fillWorkerPool()
|
||||
|
||||
onTomeCast: (e) ->
|
||||
return if @dead
|
||||
@spells = e.spells
|
||||
@createWorld()
|
||||
|
||||
fillWorkerPool: =>
|
||||
return unless Worker
|
||||
@workerPool ?= []
|
||||
if @workerPool.length < @maxWorkerPoolSize
|
||||
@workerPool.push @createWorker()
|
||||
if @workerPool.length < @maxWorkerPoolSize
|
||||
@fillWorkerPool()
|
||||
|
||||
getWorker: ->
|
||||
@fillWorkerPool()
|
||||
worker = @workerPool?.shift()
|
||||
return worker if worker
|
||||
@createWorker()
|
||||
|
||||
createWorker: ->
|
||||
new Worker '/javascripts/workers/worker_world.js'
|
||||
|
||||
getAngel: ->
|
||||
freeAngel = null
|
||||
for angel in @angels
|
||||
|
@ -67,7 +89,7 @@ module.exports = class God
|
|||
@worldWaiting = true
|
||||
return
|
||||
angel.worker.postMessage {func: 'runWorld', args: {
|
||||
worldName: @world.name
|
||||
worldName: @level.name
|
||||
userCodeMap: @getUserCodeMap()
|
||||
level: @level
|
||||
firstWorld: @firstWorld
|
||||
|
@ -81,7 +103,7 @@ module.exports = class God
|
|||
@latestWorldCreation = worldCreation
|
||||
@latestGoalStates = goalStates
|
||||
window.BOX2D_ENABLED = false # Flip this off so that if we have box2d in the namespace, the Collides Components still don't try to create bodies for deserialized Thangs upon attachment
|
||||
@world.constructor.deserialize serialized, @world.classMap, @lastSerializedWorldFrames, worldCreation, @finishBeholdingWorld
|
||||
World.deserialize serialized, @worldClassMap, @lastSerializedWorldFrames, worldCreation, @finishBeholdingWorld
|
||||
window.BOX2D_ENABLED = true
|
||||
@lastSerializedWorldFrames = serialized.frames
|
||||
|
||||
|
@ -89,7 +111,7 @@ module.exports = class God
|
|||
newWorld.findFirstChangedFrame @world
|
||||
@world = newWorld
|
||||
errorCount = (t for t in @world.thangs when t.errorsOut).length
|
||||
Backbone.Mediator.publish('god:new-world-created', world: @world, firstWorld: @firstWorld, errorCount: errorCount, goalStates: @latestGoalStates)
|
||||
Backbone.Mediator.publish('god:new-world-created', world: @world, firstWorld: @firstWorld, errorCount: errorCount, goalStates: @latestGoalStates, team: me.team)
|
||||
for scriptNote in @world.scriptNotes
|
||||
Backbone.Mediator.publish scriptNote.channel, scriptNote.event
|
||||
@goalManager?.world = newWorld
|
||||
|
@ -109,7 +131,10 @@ module.exports = class God
|
|||
angel.destroy() for angel in @angels
|
||||
@dead = true
|
||||
Backbone.Mediator.unsubscribe('tome:cast-spells', @onTomeCast, @)
|
||||
@goalManager.destroy()
|
||||
@goalManager = null
|
||||
@fillWorkerPool = null
|
||||
@simulateWorld = null
|
||||
|
||||
#### Bad code for running worlds on main thread (profiling / IE9) ####
|
||||
simulateWorld: =>
|
||||
|
@ -133,7 +158,7 @@ module.exports = class God
|
|||
@latestGoalStates = @testGM?.getGoalStates()
|
||||
serialized = @testWorld.serialize().serializedWorld
|
||||
window.BOX2D_ENABLED = false
|
||||
@testWorld.constructor.deserialize serialized, @world.classMap, @lastSerializedWorldFrames, @t0, @finishBeholdingWorld
|
||||
World.deserialize serialized, @worldClassMap, @lastSerializedWorldFrames, @t0, @finishBeholdingWorld
|
||||
window.BOX2D_ENABLED = true
|
||||
@lastSerializedWorldFrames = serialized.frames
|
||||
|
||||
|
@ -168,7 +193,7 @@ class Angel
|
|||
@spawnWorker()
|
||||
|
||||
spawnWorker: ->
|
||||
@worker = new Worker '/javascripts/workers/worker_world.js'
|
||||
@worker = @god.getWorker()
|
||||
@listen()
|
||||
|
||||
enslave: ->
|
||||
|
@ -183,16 +208,21 @@ class Angel
|
|||
@started = null
|
||||
clearInterval @purgatoryTimer
|
||||
@purgatoryTimer = null
|
||||
@worker?.terminate()
|
||||
@worker = null
|
||||
if @worker
|
||||
worker = @worker
|
||||
_.defer -> worker.terminate()
|
||||
@worker.removeEventListener 'message', @onWorkerMessage
|
||||
@worker = null
|
||||
@
|
||||
|
||||
abort: ->
|
||||
return unless @worker
|
||||
@abortTimeout = _.delay @terminate, @abortTimeoutDuration
|
||||
@worker.postMessage {func: 'abort'}
|
||||
|
||||
terminate: =>
|
||||
@worker?.terminate()
|
||||
@worker?.removeEventListener 'message', @onWorkerMessage
|
||||
@worker = null
|
||||
return if @dead
|
||||
@free()
|
||||
|
@ -200,7 +230,12 @@ class Angel
|
|||
|
||||
destroy: ->
|
||||
@dead = true
|
||||
@finishBeholdingWorld = null
|
||||
@abort()
|
||||
@terminate = null
|
||||
@testWorker = null
|
||||
@condemnWorker = null
|
||||
@onWorkerMessage = null
|
||||
|
||||
testWorker: =>
|
||||
@worker.postMessage {func: 'reportIn'}
|
||||
|
@ -211,25 +246,24 @@ class Angel
|
|||
@abort()
|
||||
|
||||
listen: ->
|
||||
@worker.addEventListener 'message', (event) =>
|
||||
switch event.data.type
|
||||
when 'new-world'
|
||||
@god.beholdWorld @, event.data.serialized, event.data.goalStates
|
||||
when 'world-load-progress-changed'
|
||||
Backbone.Mediator.publish 'god:world-load-progress-changed', event.data unless @dead
|
||||
when 'console-log'
|
||||
console.log "|" + @god.id + "'s " + @id + "|", event.data.args...
|
||||
when 'user-code-problem'
|
||||
@god.angelUserCodeProblem @, event.data.problem
|
||||
when 'abort'
|
||||
#console.log @id, "aborted."
|
||||
clearTimeout @abortTimeout
|
||||
@free()
|
||||
@god.angelAborted @
|
||||
if @god.dead
|
||||
@worker.terminate()
|
||||
@worker = null
|
||||
when 'reportIn'
|
||||
clearTimeout @condemnTimeout
|
||||
else
|
||||
console.log "Unsupported message:", event.data
|
||||
@worker.addEventListener 'message', @onWorkerMessage
|
||||
|
||||
onWorkerMessage: (event) =>
|
||||
switch event.data.type
|
||||
when 'new-world'
|
||||
@god.beholdWorld @, event.data.serialized, event.data.goalStates
|
||||
when 'world-load-progress-changed'
|
||||
Backbone.Mediator.publish 'god:world-load-progress-changed', event.data unless @dead
|
||||
when 'console-log'
|
||||
console.log "|" + @god.id + "'s " + @id + "|", event.data.args...
|
||||
when 'user-code-problem'
|
||||
@god.angelUserCodeProblem @, event.data.problem
|
||||
when 'abort'
|
||||
#console.log @id, "aborted."
|
||||
clearTimeout @abortTimeout
|
||||
@free()
|
||||
@god.angelAborted @
|
||||
when 'reportIn'
|
||||
clearTimeout @condemnTimeout
|
||||
else
|
||||
console.log "Unsupported message:", event.data
|
||||
|
|
|
@ -21,6 +21,7 @@ module.exports = class LevelBus extends Bus
|
|||
'thang-code-ran': 'onCodeRan'
|
||||
'level-show-victory': 'onVictory'
|
||||
'tome:spell-changed': 'onSpellChanged'
|
||||
'tome:spell-created': 'onSpellCreated'
|
||||
|
||||
constructor: ->
|
||||
super(arguments...)
|
||||
|
@ -32,7 +33,7 @@ module.exports = class LevelBus extends Bus
|
|||
@fireScriptsRef = @fireRef?.child('scripts')
|
||||
|
||||
setSession: (@session) ->
|
||||
@session.on('change:multiplayer', @onMultiplayerChanged)
|
||||
@session.on 'change:multiplayer', @onMultiplayerChanged, @
|
||||
|
||||
onPoint: ->
|
||||
return true unless @session?.get('multiplayer')
|
||||
|
@ -91,12 +92,26 @@ module.exports = class LevelBus extends Bus
|
|||
code = @session.get('code')
|
||||
code ?= {}
|
||||
parts = e.spell.spellKey.split('/')
|
||||
|
||||
code[parts[0]] ?= {}
|
||||
code[parts[0]][parts[1]] = e.spell.getSource()
|
||||
@changedSessionProperties.code = true
|
||||
@session.set({'code': code})
|
||||
@saveSession()
|
||||
|
||||
onSpellCreated: (e) ->
|
||||
return unless @onPoint()
|
||||
spellTeam = e.spell.team
|
||||
@teamSpellMap[spellTeam] ?= []
|
||||
|
||||
unless e.spell.spellKey in @teamSpellMap[spellTeam]
|
||||
@teamSpellMap[spellTeam].push e.spell.spellKey
|
||||
@changedSessionProperties.teamSpells = true
|
||||
@session.set({'teamSpells': @teamSpellMap})
|
||||
@saveSession()
|
||||
|
||||
|
||||
|
||||
onScriptStateChanged: (e) ->
|
||||
return unless @onPoint()
|
||||
@fireScriptsRef?.update(e)
|
||||
|
@ -198,7 +213,7 @@ module.exports = class LevelBus extends Bus
|
|||
@changedSessionProperties.chat = true
|
||||
@saveSession()
|
||||
|
||||
onMultiplayerChanged: =>
|
||||
onMultiplayerChanged: ->
|
||||
@changedSessionProperties.multiplayer = true
|
||||
@session.updatePermissions()
|
||||
@changedSessionProperties.permissions = true
|
||||
|
@ -217,3 +232,15 @@ module.exports = class LevelBus extends Bus
|
|||
# don't let what the server returns overwrite changes since the save began
|
||||
tempSession = new LevelSession _id:@session.id
|
||||
tempSession.save(patch, {patch: true})
|
||||
|
||||
destroy: ->
|
||||
@session.off 'change:multiplayer', @onMultiplayerChanged, @
|
||||
super()
|
||||
|
||||
setTeamSpellMap: (spellMap) ->
|
||||
@teamSpellMap = spellMap
|
||||
console.log @teamSpellMap
|
||||
@changedSessionProperties.teamSpells = true
|
||||
@session.set({'teamSpells': @teamSpellMap})
|
||||
@saveSession()
|
||||
|
||||
|
|
|
@ -22,79 +22,94 @@ module.exports = class LevelLoader extends CocoClass
|
|||
subscriptions:
|
||||
'god:new-world-created': 'loadSoundsForWorld'
|
||||
|
||||
constructor: (@levelID, @supermodel, @sessionID) ->
|
||||
constructor: (options) ->
|
||||
super()
|
||||
@supermodel = options.supermodel
|
||||
@levelID = options.levelID
|
||||
@sessionID = options.sessionID
|
||||
@opponentSessionID = options.opponentSessionID
|
||||
@team = options.team
|
||||
@headless = options.headless
|
||||
|
||||
@loadSession()
|
||||
@loadLevelModels()
|
||||
@loadAudio()
|
||||
@playJingle()
|
||||
setTimeout (=> @update()), 1 # lets everything else resolve first
|
||||
_.defer @update # Lets everything else resolve first
|
||||
|
||||
playJingle: ->
|
||||
return if @headless
|
||||
jingles = ["ident_1", "ident_2"]
|
||||
AudioPlayer.playInterfaceSound jingles[Math.floor Math.random() * jingles.length]
|
||||
|
||||
# Session Loading
|
||||
|
||||
loadSession: ->
|
||||
url = if @sessionID then "/db/level_session/#{@sessionID}" else "/db/level/#{@levelID}/session"
|
||||
if @sessionID
|
||||
url = "/db/level_session/#{@sessionID}"
|
||||
else
|
||||
url = "/db/level/#{@levelID}/session"
|
||||
url += "?team=#{@team}" if @team
|
||||
|
||||
@session = new LevelSession()
|
||||
@session.url = -> url
|
||||
@session.fetch()
|
||||
@session.once 'sync', @onSessionLoaded
|
||||
|
||||
onSessionLoaded: =>
|
||||
# Unless you specify cache:false, sometimes the browser will use a cached session
|
||||
# and players will 'lose' code
|
||||
@session.fetch({cache:false})
|
||||
@session.once 'sync', @onSessionLoaded, @
|
||||
|
||||
if @opponentSessionID
|
||||
@opponentSession = new LevelSession()
|
||||
@opponentSession.url = "/db/level_session/#{@opponentSessionID}"
|
||||
@opponentSession.fetch()
|
||||
@opponentSession.once 'sync', @onSessionLoaded, @
|
||||
|
||||
sessionsLoaded: ->
|
||||
@session.loaded and ((not @opponentSession) or @opponentSession.loaded)
|
||||
|
||||
onSessionLoaded: ->
|
||||
# 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
|
||||
@update()
|
||||
if @session.loaded
|
||||
@session.url = -> '/db/level.session/' + @id
|
||||
@update() if @sessionsLoaded()
|
||||
|
||||
# Supermodel (Level) Loading
|
||||
|
||||
loadLevelModels: ->
|
||||
@supermodel.once 'loaded-all', @onSupermodelLoadedAll
|
||||
@supermodel.on 'loaded-one', @onSupermodelLoadedOne
|
||||
@supermodel.once 'error', @onSupermodelError
|
||||
@supermodel.on 'loaded-one', @onSupermodelLoadedOne, @
|
||||
@supermodel.once 'error', @onSupermodelError, @
|
||||
@level = @supermodel.getModel(Level, @levelID) or new Level _id: @levelID
|
||||
levelID = @levelID
|
||||
headless = @headless
|
||||
|
||||
@supermodel.shouldPopulate = (model) =>
|
||||
@supermodel.shouldPopulate = (model) ->
|
||||
# if left unchecked, the supermodel would load this level
|
||||
# and every level next on the chain. This limits the population
|
||||
handles = [model.id, model.get 'slug']
|
||||
return model.constructor.className isnt "Level" or @levelID in handles
|
||||
return model.constructor.className isnt "Level" or levelID in handles
|
||||
|
||||
@supermodel.shouldLoadProjection = (model) ->
|
||||
return true if headless and model.constructor.className is 'ThangType'
|
||||
false
|
||||
|
||||
@supermodel.populateModel @level
|
||||
|
||||
onSupermodelError: =>
|
||||
onSupermodelError: ->
|
||||
msg = $.i18n.t('play_level.level_load_error',
|
||||
defaultValue: "Level could not be loaded.")
|
||||
@$el.html('<div class="alert">' + msg + '</div>')
|
||||
|
||||
onSupermodelLoadedOne: (e) =>
|
||||
@notifyProgress()
|
||||
# if e.model.type() is 'ThangType'
|
||||
# thangType = e.model
|
||||
# options = {async: true}
|
||||
# if thangType.get('name') is 'Wizard'
|
||||
# options.colorConfig = me.get('wizard')?.colorConfig or {}
|
||||
# building = thangType.buildSpriteSheet options
|
||||
# if building
|
||||
# @spriteSheetsToBuild += 1
|
||||
# thangType.on 'build-complete', =>
|
||||
# @spriteSheetsBuilt += 1
|
||||
# @notifyProgress()
|
||||
|
||||
onSupermodelLoadedAll: =>
|
||||
@trigger 'loaded-supermodel'
|
||||
@stopListening(@supermodel)
|
||||
onSupermodelLoadedOne: (e) ->
|
||||
@update()
|
||||
|
||||
# Things to do when either the Session or Supermodel load
|
||||
|
||||
update: ->
|
||||
update: =>
|
||||
@notifyProgress()
|
||||
|
||||
return if @updateCompleted
|
||||
return unless @supermodel.finished() and @session.loaded
|
||||
return unless @supermodel.finished() and @sessionsLoaded()
|
||||
@denormalizeSession()
|
||||
@loadLevelSounds()
|
||||
app.tracker.updatePlayState(@level, @session)
|
||||
|
@ -124,6 +139,7 @@ module.exports = class LevelLoader extends CocoClass
|
|||
@buildSpriteSheets()
|
||||
|
||||
buildSpriteSheets: ->
|
||||
return if @headless
|
||||
thangTypes = {}
|
||||
thangTypes[tt.get('name')] = tt for tt in @supermodel.getModels(ThangType)
|
||||
|
||||
|
@ -152,16 +168,18 @@ module.exports = class LevelLoader extends CocoClass
|
|||
return unless building
|
||||
console.log 'Building:', thangType.get('name'), options
|
||||
@spriteSheetsToBuild += 1
|
||||
thangType.on 'build-complete', =>
|
||||
thangType.once 'build-complete', =>
|
||||
@spriteSheetsBuilt += 1
|
||||
@notifyProgress()
|
||||
|
||||
# Initial Sound Loading
|
||||
|
||||
loadAudio: ->
|
||||
return if @headless
|
||||
AudioPlayer.preloadInterfaceSounds ["victory"]
|
||||
|
||||
loadLevelSounds: ->
|
||||
return if @headless
|
||||
scripts = @level.get 'scripts'
|
||||
return unless scripts
|
||||
|
||||
|
@ -178,6 +196,7 @@ module.exports = class LevelLoader extends CocoClass
|
|||
# Dynamic sound loading
|
||||
|
||||
loadSoundsForWorld: (e) ->
|
||||
return if @headless
|
||||
world = e.world
|
||||
thangTypes = @supermodel.getModels(ThangType)
|
||||
for [spriteName, message] in world.thangDialogueSounds()
|
||||
|
@ -188,16 +207,19 @@ module.exports = class LevelLoader extends CocoClass
|
|||
# everything else sound wise is loaded as needed as worlds are generated
|
||||
|
||||
allDone: ->
|
||||
@supermodel.finished() and @session.loaded and @spriteSheetsBuilt is @spriteSheetsToBuild
|
||||
@supermodel.finished() and @sessionsLoaded() and @spriteSheetsBuilt is @spriteSheetsToBuild
|
||||
|
||||
progress: ->
|
||||
return 0 unless @level.loaded
|
||||
overallProgress = 0
|
||||
supermodelProgress = @supermodel.progress()
|
||||
overallProgress += supermodelProgress * 0.7
|
||||
overallProgress += 0.1 if @session.loaded
|
||||
spriteMapProgress = if supermodelProgress is 1 then 0.2 else 0
|
||||
spriteMapProgress *= @spriteSheetsBuilt / @spriteSheetsToBuild if @spriteSheetsToBuild
|
||||
overallProgress += 0.1 if @sessionsLoaded()
|
||||
if @headless
|
||||
spriteMapProgress = 0.2
|
||||
else
|
||||
spriteMapProgress = if supermodelProgress is 1 then 0.2 else 0
|
||||
spriteMapProgress *= @spriteSheetsBuilt / @spriteSheetsToBuild if @spriteSheetsToBuild
|
||||
overallProgress += spriteMapProgress
|
||||
return overallProgress
|
||||
|
||||
|
@ -207,6 +229,7 @@ module.exports = class LevelLoader extends CocoClass
|
|||
@trigger 'loaded-all' if @progress() is 1
|
||||
|
||||
destroy: ->
|
||||
@world = null # don't hold onto garbage
|
||||
@supermodel.off 'loaded-one', @onSupermodelLoadedOne
|
||||
@world = null # don't hold onto garbage
|
||||
@update = null
|
||||
super()
|
||||
|
|
|
@ -20,6 +20,9 @@ module.exports = class CocoRouter extends Backbone.Router
|
|||
'db/*path': 'routeToServer'
|
||||
'file/*path': 'routeToServer'
|
||||
|
||||
'play/level/:levelID/leaderboard/:teamID/:startRank/:endRank': 'getPaginatedLevelRank'
|
||||
'play/level/:levelID/player/:playerID': 'getPlayerLevelInfo'
|
||||
|
||||
# most go through here
|
||||
'*name': 'general'
|
||||
|
||||
|
@ -27,6 +30,13 @@ module.exports = class CocoRouter extends Backbone.Router
|
|||
general: (name) ->
|
||||
@openRoute(name)
|
||||
|
||||
getPaginatedLevelRank: (levelID,teamID,startRank,endRank) ->
|
||||
return
|
||||
|
||||
getPlayerLevelInfo: (levelID,playerID) ->
|
||||
return
|
||||
|
||||
|
||||
editorModelView: (modelName, slugOrId, subview) ->
|
||||
modulePrefix = "views/editor/#{modelName}/"
|
||||
suffix = subview or (if slugOrId then 'edit' else 'home')
|
||||
|
@ -119,7 +129,7 @@ module.exports = class CocoRouter extends Backbone.Router
|
|||
view.render()
|
||||
|
||||
closeCurrentView: ->
|
||||
window.currentModal?.hide()
|
||||
window.currentModal?.hide?()
|
||||
return unless window.currentView?
|
||||
if window.currentView.cache
|
||||
window.currentView.scrollY = window.scrollY
|
||||
|
|
|
@ -16,18 +16,18 @@ module.exports.genericFailure = (jqxhr) ->
|
|||
error = module.exports.parseServerError(jqxhr.responseText)
|
||||
message = error.message
|
||||
message = error.property + ' ' + message if error.property
|
||||
res = errorModalTemplate(
|
||||
status:jqxhr.status
|
||||
statusText:jqxhr.statusText
|
||||
message: message
|
||||
)
|
||||
console.warn(jqxhr.status, jqxhr.statusText, error)
|
||||
existingForm = $('.form-inline:visible:first')
|
||||
existingForm = $('.form:visible:first')
|
||||
if existingForm[0]
|
||||
missingErrors = applyErrorsToForm(existingForm, [error])
|
||||
for error in missingErrors
|
||||
existingForm.append($('<div class="alert"></div>').text(error.message))
|
||||
else
|
||||
res = errorModalTemplate(
|
||||
status:jqxhr.status
|
||||
statusText:jqxhr.statusText
|
||||
message: message
|
||||
)
|
||||
showErrorModal(res)
|
||||
|
||||
module.exports.backboneFailure = (model, jqxhr, options) ->
|
||||
|
|
|
@ -120,8 +120,8 @@ module.exports = ScriptManager = class ScriptManager extends CocoClass
|
|||
@run() unless @worldLoading
|
||||
|
||||
destroy: ->
|
||||
super()
|
||||
@onEndAll()
|
||||
super()
|
||||
|
||||
# TRIGGERERING NOTES
|
||||
|
||||
|
@ -182,7 +182,7 @@ module.exports = ScriptManager = class ScriptManager extends CocoClass
|
|||
|
||||
# STARTING NOTES
|
||||
|
||||
run: =>
|
||||
run: ->
|
||||
# catch all for analyzing the current state and doing whatever needs to happen next
|
||||
return if @scriptInProgress
|
||||
@skipAhead()
|
||||
|
|
241
app/lib/simulator/Simulator.coffee
Normal file
241
app/lib/simulator/Simulator.coffee
Normal file
|
@ -0,0 +1,241 @@
|
|||
SuperModel = require 'models/SuperModel'
|
||||
LevelLoader = require 'lib/LevelLoader'
|
||||
GoalManager = require 'lib/world/GoalManager'
|
||||
God = require 'lib/God'
|
||||
|
||||
module.exports = class Simulator
|
||||
|
||||
constructor: ->
|
||||
@retryDelayInSeconds = 10
|
||||
@taskURL = '/queue/scoring'
|
||||
|
||||
fetchAndSimulateTask: =>
|
||||
$.ajax
|
||||
url: @taskURL
|
||||
type: "GET"
|
||||
error: @handleFetchTaskError
|
||||
success: @setupSimulationAndLoadLevel
|
||||
|
||||
handleFetchTaskError: (errorData) =>
|
||||
console.log "There were no games to score. Error: #{JSON.stringify errorData}"
|
||||
console.log "Retrying in #{@retryDelayInSeconds}"
|
||||
|
||||
@simulateAnotherTaskAfterDelay()
|
||||
|
||||
simulateAnotherTaskAfterDelay: =>
|
||||
retryDelayInMilliseconds = @retryDelayInSeconds * 1000
|
||||
_.delay @fetchAndSimulateTask, retryDelayInMilliseconds
|
||||
|
||||
setupSimulationAndLoadLevel: (taskData) =>
|
||||
@task = new SimulationTask(taskData)
|
||||
@supermodel = new SuperModel()
|
||||
@god = new God maxWorkerPoolSize: 1, maxAngels: 1 # Start loading worker.
|
||||
|
||||
@levelLoader = new LevelLoader supermodel: @supermodel, levelID: @task.getLevelName(), sessionID: @task.getFirstSessionID(), headless: true
|
||||
@levelLoader.once 'loaded-all', @simulateGame
|
||||
|
||||
simulateGame: =>
|
||||
@assignWorldAndLevelFromLevelLoaderAndDestroyIt()
|
||||
@setupGod()
|
||||
|
||||
try
|
||||
@commenceSimulationAndSetupCallback()
|
||||
catch err
|
||||
console.log "There was an error in simulation(#{err}). Trying again in #{@retryDelayInSeconds} seconds"
|
||||
@simulateAnotherTaskAfterDelay()
|
||||
|
||||
assignWorldAndLevelFromLevelLoaderAndDestroyIt: ->
|
||||
@world = @levelLoader.world
|
||||
@level = @levelLoader.level
|
||||
@levelLoader.destroy()
|
||||
@levelLoader = null
|
||||
|
||||
setupGod: ->
|
||||
@god.level = @level.serialize @supermodel
|
||||
@god.worldClassMap = @world.classMap
|
||||
@setupGoalManager()
|
||||
@setupGodSpells()
|
||||
|
||||
setupGoalManager: ->
|
||||
@god.goalManager = new GoalManager @world
|
||||
@god.goalManager.goals = @fetchGoalsFromWorldNoteChain()
|
||||
@god.goalManager.goalStates = @manuallyGenerateGoalStates()
|
||||
|
||||
commenceSimulationAndSetupCallback: ->
|
||||
@god.createWorld()
|
||||
Backbone.Mediator.subscribeOnce 'god:new-world-created', @processResults, @
|
||||
|
||||
processResults: (simulationResults) ->
|
||||
taskResults = @formTaskResultsObject simulationResults
|
||||
@sendResultsBackToServer taskResults
|
||||
|
||||
sendResultsBackToServer: (results) =>
|
||||
$.ajax
|
||||
url: @taskURL
|
||||
data: results
|
||||
type: "PUT"
|
||||
success: @handleTaskResultsTransferSuccess
|
||||
error: @handleTaskResultsTransferError
|
||||
complete: @cleanupAndSimulateAnotherTask
|
||||
|
||||
handleTaskResultsTransferSuccess: (result) ->
|
||||
console.log "Task registration result: #{JSON.stringify result}"
|
||||
|
||||
handleTaskResultsTransferError: (error) ->
|
||||
console.log "Task registration error: #{JSON.stringify error}"
|
||||
|
||||
cleanupAndSimulateAnotherTask: =>
|
||||
@cleanupSimulation()
|
||||
@fetchAndSimulateTask()
|
||||
|
||||
cleanupSimulation: ->
|
||||
@god.destroy()
|
||||
@god = null
|
||||
@world = null
|
||||
@level = null
|
||||
|
||||
formTaskResultsObject: (simulationResults) ->
|
||||
taskResults =
|
||||
taskID: @task.getTaskID()
|
||||
receiptHandle: @task.getReceiptHandle()
|
||||
calculationTime: 500
|
||||
sessions: []
|
||||
|
||||
for session in @task.getSessions()
|
||||
sessionResult =
|
||||
sessionID: session.sessionID
|
||||
sessionChangedTime: session.sessionChangedTime
|
||||
metrics:
|
||||
rank: @calculateSessionRank session.sessionID, simulationResults.goalStates, @task.generateTeamToSessionMap()
|
||||
|
||||
taskResults.sessions.push sessionResult
|
||||
|
||||
return taskResults
|
||||
|
||||
calculateSessionRank: (sessionID, goalStates, teamSessionMap) ->
|
||||
humansDestroyed = goalStates["destroy-humans"].status is "success"
|
||||
ogresDestroyed = goalStates["destroy-ogres"].status is "success"
|
||||
if humansDestroyed is ogresDestroyed
|
||||
return 0
|
||||
else if humansDestroyed and teamSessionMap["ogres"] is sessionID
|
||||
return 0
|
||||
else if humansDestroyed and teamSessionMap["ogres"] isnt sessionID
|
||||
return 1
|
||||
else if ogresDestroyed and teamSessionMap["humans"] is sessionID
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
|
||||
fetchGoalsFromWorldNoteChain: -> return @god.goalManager.world.scripts[0].noteChain[0].goals.add
|
||||
|
||||
manuallyGenerateGoalStates: ->
|
||||
goalStates =
|
||||
"destroy-humans":
|
||||
keyFrame: 0
|
||||
killed:
|
||||
"Human Base": false
|
||||
status: "incomplete"
|
||||
"destroy-ogres":
|
||||
keyFrame:0
|
||||
killed:
|
||||
"Ogre Base": false
|
||||
status: "incomplete"
|
||||
|
||||
setupGodSpells: ->
|
||||
@generateSpellsObject()
|
||||
@god.spells = @spells
|
||||
|
||||
generateSpellsObject: ->
|
||||
@currentUserCodeMap = @task.generateSpellKeyToSourceMap()
|
||||
@spells = {}
|
||||
for thang in @level.attributes.thangs
|
||||
continue if @thangIsATemplate thang
|
||||
@generateSpellKeyToSourceMapPropertiesFromThang thang
|
||||
|
||||
thangIsATemplate: (thang) ->
|
||||
for component in thang.components
|
||||
continue unless @componentHasProgrammableMethods component
|
||||
for methodName, method of component.config.programmableMethods
|
||||
return true if @methodBelongsToTemplateThang method
|
||||
|
||||
return false
|
||||
|
||||
componentHasProgrammableMethods: (component) -> component.config? and _.has component.config, 'programmableMethods'
|
||||
|
||||
methodBelongsToTemplateThang: (method) -> typeof method is 'string'
|
||||
|
||||
generateSpellKeyToSourceMapPropertiesFromThang: (thang) =>
|
||||
for component in thang.components
|
||||
continue unless @componentHasProgrammableMethods component
|
||||
|
||||
for methodName, method of component.config.programmableMethods
|
||||
spellKey = @generateSpellKeyFromThangIDAndMethodName thang.id, methodName
|
||||
|
||||
@createSpellAndAssignName spellKey, methodName
|
||||
@createSpellThang thang, method, spellKey
|
||||
@transpileSpell thang, spellKey, methodName
|
||||
|
||||
generateSpellKeyFromThangIDAndMethodName: (thang, methodName) ->
|
||||
spellKeyComponents = [thang.id, methodName]
|
||||
spellKeyComponents[0] = _.string.slugify spellKeyComponents[0]
|
||||
spellKeyComponents.join '/'
|
||||
|
||||
createSpellAndAssignName: (spellKey, spellName) ->
|
||||
@spells[spellKey] ?= {}
|
||||
@spells[spellKey].name = spellName
|
||||
|
||||
createSpellThang: (thang, method, spellKey) ->
|
||||
@spells[spellKey].thangs ?= {}
|
||||
@spells[spellKey].thangs[thang.id] ?= {}
|
||||
@spells[spellKey].thangs[thang.id].aether = @createAether @spells[spellKey].name, method
|
||||
|
||||
transpileSpell: (thang, spellKey, methodName) ->
|
||||
slugifiedThangID = _.string.slugify thang.id
|
||||
source = @currentUserCodeMap[slugifiedThangID]?[methodName] ? ""
|
||||
@spells[spellKey].thangs[thang.id].aether.transpile source
|
||||
|
||||
createAether: (methodName, method) ->
|
||||
aetherOptions =
|
||||
functionName: methodName
|
||||
protectAPI: false
|
||||
includeFlow: false
|
||||
return new Aether aetherOptions
|
||||
|
||||
class SimulationTask
|
||||
constructor: (@rawData) ->
|
||||
|
||||
getLevelName: ->
|
||||
levelName = @rawData.sessions?[0]?.levelID
|
||||
return levelName if levelName?
|
||||
@throwMalformedTaskError "The level name couldn't be deduced from the task."
|
||||
|
||||
generateTeamToSessionMap: ->
|
||||
teamSessionMap = {}
|
||||
for session in @rawData.sessions
|
||||
@throwMalformedTaskError "Two players share the same team" if teamSessionMap[session.team]?
|
||||
teamSessionMap[session.team] = session.sessionID
|
||||
|
||||
teamSessionMap
|
||||
|
||||
throwMalformedTaskError: (errorString) ->
|
||||
throw new Error "The task was malformed, reason: #{errorString}"
|
||||
|
||||
getFirstSessionID: -> @rawData.sessions[0].sessionID
|
||||
|
||||
getTaskID: -> @rawData.taskID
|
||||
|
||||
getReceiptHandle: -> @rawData.receiptHandle
|
||||
|
||||
getSessions: -> @rawData.sessions
|
||||
|
||||
generateSpellKeyToSourceMap: ->
|
||||
spellKeyToSourceMap = {}
|
||||
|
||||
for session in @rawData.sessions
|
||||
teamSpells = session.teamSpells[session.team]
|
||||
_.merge spellKeyToSourceMap, _.pick(session.code, teamSpells)
|
||||
|
||||
commonSpells = session.teamSpells["common"]
|
||||
_.merge spellKeyToSourceMap, _.pick(session.code, commonSpells) if commonSpells?
|
||||
|
||||
spellKeyToSourceMap
|
|
@ -11,5 +11,5 @@ module.exports.load = (key) ->
|
|||
module.exports.save = (key, value) ->
|
||||
s = JSON.stringify(value)
|
||||
localStorage.setItem(key, s)
|
||||
|
||||
module.exports.remove = (key) -> localStorage.removeItem key
|
||||
|
||||
module.exports.remove = (key) -> localStorage.removeItem key
|
||||
|
|
|
@ -148,7 +148,21 @@ module.exports = class Camera extends CocoClass
|
|||
onMouseScrolled: (e) ->
|
||||
ratio = 1 + 0.05 * Math.sqrt(Math.abs(e.deltaY))
|
||||
ratio = 1 / ratio if e.deltaY > 0
|
||||
@zoomTo @target, @zoom * ratio, 0
|
||||
newZoom = @zoom * ratio
|
||||
if e.surfacePos
|
||||
# zoom based on mouse position, adjusting the target so the point under the mouse stays the same
|
||||
mousePoint = @canvasToSurface(e.surfacePos)
|
||||
ratioPosX = (mousePoint.x - @surfaceViewport.x) / @surfaceViewport.width
|
||||
ratioPosY = (mousePoint.y - @surfaceViewport.y) / @surfaceViewport.height
|
||||
newWidth = @canvasWidth / newZoom
|
||||
newHeight = @canvasHeight / newZoom
|
||||
newTargetX = mousePoint.x - (newWidth * ratioPosX) + (newWidth / 2)
|
||||
newTargetY = mousePoint.y - (newHeight * ratioPosY) + (newHeight / 2)
|
||||
target = {x: newTargetX, y:newTargetY}
|
||||
else
|
||||
target = @target
|
||||
@zoomTo target, newZoom, 0
|
||||
|
||||
onLevelRestarted: ->
|
||||
@setBounds(@firstBounds)
|
||||
|
||||
|
@ -186,7 +200,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.
|
||||
time = 0 if @instant
|
||||
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)
|
||||
return if @zoom is newZoom and newTarget is newTarget.x and newTarget.y is newTarget.y
|
||||
|
||||
|
@ -199,15 +213,13 @@ module.exports = class Camera extends CocoClass
|
|||
@tweenProgress = 0.01
|
||||
createjs.Tween.get(@)
|
||||
.to({tweenProgress: 1.0}, time, createjs.Ease.getPowInOut(3))
|
||||
.call @onTweenEnd
|
||||
.call @finishTween
|
||||
|
||||
else
|
||||
@target = newTarget
|
||||
@zoom = newZoom
|
||||
@updateZoom true
|
||||
|
||||
onTweenEnd: => @finishTween()
|
||||
|
||||
finishTween: (abort=false) =>
|
||||
createjs.Tween.removeTweens(@)
|
||||
return unless @newTarget
|
||||
|
@ -260,3 +272,8 @@ module.exports = class Camera extends CocoClass
|
|||
@locked = true
|
||||
unlock: ->
|
||||
@locked = false
|
||||
|
||||
destroy: ->
|
||||
createjs.Tween.removeTweens @
|
||||
@finishTween = null
|
||||
super()
|
||||
|
|
|
@ -4,6 +4,7 @@ Camera = require './Camera'
|
|||
Mark = require './Mark'
|
||||
Label = require './Label'
|
||||
AudioPlayer = require 'lib/AudioPlayer'
|
||||
{me} = require 'lib/auth'
|
||||
|
||||
# We'll get rid of this once level's teams actually have colors
|
||||
healthColors =
|
||||
|
@ -76,9 +77,12 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
|
|||
@buildFromSpriteSheet @buildSpriteSheet()
|
||||
|
||||
destroy: ->
|
||||
super()
|
||||
mark.destroy() for name, mark of @marks
|
||||
label.destroy() for name, label of @labels
|
||||
@imageObject?.off 'animationend', @playNextAction
|
||||
@playNextAction = null
|
||||
@displayObject?.off()
|
||||
super()
|
||||
|
||||
toString: -> "<CocoSprite: #{@thang?.id}>"
|
||||
|
||||
|
@ -107,7 +111,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
|
|||
@displayObject.sprite = @
|
||||
@displayObject.layerPriority = @thangType.get 'layerPriority'
|
||||
@displayObject.name = @thang?.spriteName or @thangType.get 'name'
|
||||
@imageObject.on 'animationend', @onActionEnd
|
||||
@imageObject.on 'animationend', @playNextAction
|
||||
|
||||
##################################################
|
||||
# QUEUEING AND PLAYING ACTIONS
|
||||
|
@ -125,10 +129,9 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
|
|||
@currentRootAction = action
|
||||
@playNextAction()
|
||||
|
||||
onActionEnd: (e) => @playNextAction()
|
||||
onSurfaceTicked: (e) -> @age += e.dt
|
||||
|
||||
playNextAction: ->
|
||||
playNextAction: =>
|
||||
@playAction(@actionQueue.splice(0,1)[0]) if @actionQueue.length
|
||||
|
||||
playAction: (action) ->
|
||||
|
@ -149,7 +152,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
|
|||
hide: ->
|
||||
@hiding = true
|
||||
@updateAlpha()
|
||||
|
||||
|
||||
show: ->
|
||||
@hiding = false
|
||||
@updateAlpha()
|
||||
|
@ -165,6 +168,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
|
|||
@updateStats()
|
||||
@updateMarks()
|
||||
@updateLabels()
|
||||
@updateGold()
|
||||
|
||||
cache: ->
|
||||
bounds = @imageObject.getBounds()
|
||||
|
@ -409,7 +413,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
|
|||
sound = e.sound ? AudioPlayer.soundForDialogue e.message, @thangType.get 'soundTriggers'
|
||||
@instance?.stop()
|
||||
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
|
||||
|
||||
onClearDialogue: (e) ->
|
||||
|
@ -429,9 +433,20 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
|
|||
@notifySpeechUpdated blurb: blurb
|
||||
label.update() for name, label of @labels
|
||||
|
||||
updateGold: ->
|
||||
# TODO: eventually this should be moved into some sort of team-based update
|
||||
# rather than an each-thang-that-shows-gold-per-team thing.
|
||||
return if @thang.gold is @lastGold
|
||||
gold = Math.floor @thang.gold
|
||||
return if gold is @lastGold
|
||||
@lastGold = gold
|
||||
Backbone.Mediator.publish 'surface:gold-changed', {team: @thang.team, gold: gold}
|
||||
|
||||
playSounds: (withDelay=true, volume=1.0) ->
|
||||
for event in @thang.currentEvents ? []
|
||||
@playSound event, withDelay, volume
|
||||
if event is 'pay-bounty-gold' and @thang.bountyGold > 25 and @thang.team isnt me.team
|
||||
AudioPlayer.playInterfaceSound 'coin_1', 0.25
|
||||
if @thang.actionActivated and (action = @thang.getActionName()) isnt 'say'
|
||||
@playSound action, withDelay, volume
|
||||
if @thang.sayMessage and withDelay # don't play sayMessages while scrubbing, annoying
|
||||
|
|
|
@ -21,9 +21,6 @@ module.exports = class Dimmer extends CocoClass
|
|||
@highlightedThangIDs = []
|
||||
@sprites = {}
|
||||
|
||||
destroy: ->
|
||||
super()
|
||||
|
||||
toString: -> "<Dimmer>"
|
||||
|
||||
build: ->
|
||||
|
|
|
@ -19,6 +19,8 @@ module.exports = class Mark extends CocoClass
|
|||
|
||||
destroy: ->
|
||||
@mark?.parent?.removeChild @mark
|
||||
@markSprite?.destroy()
|
||||
@sprite = null
|
||||
super()
|
||||
|
||||
toString: -> "<Mark #{@name}: Sprite #{@sprite?.thang?.id ? 'None'}>"
|
||||
|
@ -119,6 +121,7 @@ module.exports = class Mark extends CocoClass
|
|||
markSprite = new CocoSprite @thangType, @thangType.spriteOptions
|
||||
markSprite.queueAction 'idle'
|
||||
@mark = markSprite.displayObject
|
||||
@markSprite = markSprite
|
||||
|
||||
update: (pos=null) ->
|
||||
return false unless @on
|
||||
|
|
|
@ -7,14 +7,14 @@ CROSSFADE_LENGTH = 1500
|
|||
module.exports = class MusicPlayer extends CocoClass
|
||||
currentMusic: null
|
||||
standingBy: null
|
||||
|
||||
|
||||
subscriptions:
|
||||
'level-play-music': 'onPlayMusic'
|
||||
'audio-player:loaded': 'onAudioLoaded'
|
||||
|
||||
|
||||
constructor: ->
|
||||
super(arguments...)
|
||||
me.on('change:music', @onMusicSettingChanged, @)
|
||||
super arguments...
|
||||
me.on 'change:music', @onMusicSettingChanged, @
|
||||
|
||||
onAudioLoaded: ->
|
||||
@onPlayMusic(@standingBy) if @standingBy
|
||||
|
@ -29,12 +29,12 @@ module.exports = class MusicPlayer extends CocoClass
|
|||
AudioPlayer.preloadSound(src)
|
||||
@standingBy = e
|
||||
return
|
||||
|
||||
|
||||
@standingBy = null
|
||||
if @currentMusic
|
||||
f = -> @stop()
|
||||
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
|
||||
return unless @currentMusic
|
||||
@currentMusic.volume = 0.0
|
||||
|
@ -48,4 +48,8 @@ module.exports = class MusicPlayer extends CocoClass
|
|||
return unless @currentMusic
|
||||
createjs.Tween.removeTweens(@currentMusic)
|
||||
@currentMusic.volume = if me.get('music') then 1.0 else 0.0
|
||||
|
||||
|
||||
destroy: ->
|
||||
me.off 'change:music', @onMusicSettingChanged, @
|
||||
super()
|
||||
|
|
@ -7,8 +7,8 @@ module.exports = class PointChooser extends CocoClass
|
|||
@options.stage.addEventListener 'stagemousedown', @onMouseDown
|
||||
|
||||
destroy: ->
|
||||
super()
|
||||
@options.stage.removeEventListener 'stagemousedown', @onMouseDown
|
||||
super()
|
||||
|
||||
# Called also from WorldSelectModal
|
||||
setPoint: (@point) ->
|
||||
|
|
|
@ -9,10 +9,10 @@ module.exports = class RegionChooser extends CocoClass
|
|||
@options.stage.addEventListener 'stagemouseup', @onMouseUp
|
||||
|
||||
destroy: ->
|
||||
super()
|
||||
@options.stage.removeEventListener 'stagemousedown', @onMouseDown
|
||||
@options.stage.removeEventListener 'stagemousemove', @onMouseMove
|
||||
@options.stage.removeEventListener 'stagemouseup', @onMouseUp
|
||||
super()
|
||||
|
||||
onMouseDown: (e) =>
|
||||
return unless key.shift
|
||||
|
|
|
@ -35,10 +35,10 @@ module.exports = class SpriteBoss extends CocoClass
|
|||
@spriteSheetCache = {}
|
||||
|
||||
destroy: ->
|
||||
super()
|
||||
@removeSprite sprite for thangID, sprite of @sprites
|
||||
@targetMark?.destroy()
|
||||
@selectionMark?.destroy()
|
||||
super()
|
||||
|
||||
toString: -> "<SpriteBoss: #{@sprites.length} sprites>"
|
||||
|
||||
|
@ -144,8 +144,8 @@ module.exports = class SpriteBoss extends CocoClass
|
|||
|
||||
removeSprite: (sprite) ->
|
||||
sprite.displayObject.parent.removeChild sprite.displayObject
|
||||
sprite.destroy()
|
||||
delete @sprites[sprite.thang.id]
|
||||
sprite.destroy()
|
||||
|
||||
updateSounds: ->
|
||||
sprite.playSounds() for thangID, sprite of @sprites # hmm; doesn't work for sprites which we didn't add yet in adjustSpriteExistence
|
||||
|
@ -222,7 +222,7 @@ module.exports = class SpriteBoss extends CocoClass
|
|||
selectThang: (thangID, spellName=null) ->
|
||||
@selectSprite null, @sprites[thangID], spellName
|
||||
|
||||
selectSprite: (e, sprite=null, spellName=null) =>
|
||||
selectSprite: (e, sprite=null, spellName=null) ->
|
||||
return if e and (@disabled or @selectLocked) # Ignore clicks for selection/panning/wizard movement while disabled or select is locked
|
||||
worldPos = sprite?.thang?.pos
|
||||
worldPos ?= @camera.canvasToWorld {x: e.originalEvent.rawX, y: e.originalEvent.rawY} if e
|
||||
|
@ -252,10 +252,12 @@ module.exports = class SpriteBoss extends CocoClass
|
|||
# Marks
|
||||
|
||||
updateSelection: ->
|
||||
if @selectedSprite and (not @selectedSprite.thang.exists or not @world.getThangByID @selectedSprite.thang.id)
|
||||
if @selectedSprite?.thang and (not @selectedSprite.thang.exists or not @world.getThangByID @selectedSprite.thang.id)
|
||||
@selectSprite null, null, null
|
||||
@selectionMark?.toggle false
|
||||
@updateTarget()
|
||||
return unless @selectionMark
|
||||
@selectedSprite = null unless @selectedSprite?.thang
|
||||
@selectionMark.toggle @selectedSprite?
|
||||
@selectionMark.setSprite @selectedSprite
|
||||
@selectionMark.update()
|
||||
|
|
|
@ -79,8 +79,8 @@ module.exports = Surface = class Surface extends CocoClass
|
|||
@initAudio()
|
||||
|
||||
destroy: ->
|
||||
super()
|
||||
@dead = true
|
||||
@camera?.destroy()
|
||||
createjs.Ticker.removeEventListener("tick", @tick)
|
||||
createjs.Sound.stop()
|
||||
layer.destroy() for layer in @layers
|
||||
|
@ -89,6 +89,19 @@ module.exports = Surface = class Surface extends CocoClass
|
|||
@dimmer?.destroy()
|
||||
@stage.clear()
|
||||
@musicPlayer?.destroy()
|
||||
@stage.removeAllChildren()
|
||||
@stage.removeEventListener 'stagemousemove', @onMouseMove
|
||||
@stage.removeEventListener 'stagemousedown', @onMouseDown
|
||||
@stage.removeAllEventListeners()
|
||||
@stage.enableDOMEvents false
|
||||
@stage.enableMouseOver 0
|
||||
@playScrubbedSounds = null
|
||||
@onMouseMove = null
|
||||
@onMouseDown = null
|
||||
@tick = null
|
||||
@canvas.off 'mousewheel', @onMouseWheel
|
||||
@onMouseWheel = null
|
||||
super()
|
||||
|
||||
setWorld: (@world) ->
|
||||
@worldLoaded = true
|
||||
|
@ -248,14 +261,14 @@ module.exports = Surface = class Surface extends CocoClass
|
|||
onSetLetterbox: (e) ->
|
||||
@setDisabled e.on
|
||||
|
||||
onSetPlaying: (e) =>
|
||||
onSetPlaying: (e) ->
|
||||
@playing = (e ? {}).playing ? true
|
||||
if @playing and @currentFrame >= (@world.totalFrames - 5)
|
||||
@currentFrame = 0
|
||||
if @fastForwarding and not @playing
|
||||
@setProgress @currentFrame / @world.totalFrames
|
||||
|
||||
onSetTime: (e) =>
|
||||
onSetTime: (e) ->
|
||||
toFrame = @currentFrame
|
||||
if e.time?
|
||||
@worldLifespan = @world.totalFrames / @world.frameRate
|
||||
|
@ -317,6 +330,7 @@ module.exports = Surface = class Surface extends CocoClass
|
|||
@stage = new createjs.Stage(@canvas[0])
|
||||
canvasWidth = parseInt(@canvas.attr('width'), 10)
|
||||
canvasHeight = parseInt(@canvas.attr('height'), 10)
|
||||
@camera?.destroy()
|
||||
@camera = new Camera canvasWidth, canvasHeight
|
||||
@layers.push @surfaceLayer = new Layer name: "Surface", layerPriority: 0, transform: Layer.TRANSFORM_SURFACE, camera: @camera
|
||||
@layers.push @surfaceTextLayer = new Layer name: "Surface Text", layerPriority: 1, transform: Layer.TRANSFORM_SURFACE_TEXT, camera: @camera
|
||||
|
@ -328,7 +342,7 @@ module.exports = Surface = class Surface extends CocoClass
|
|||
@stage.enableMouseOver(10)
|
||||
@stage.addEventListener 'stagemousemove', @onMouseMove
|
||||
@stage.addEventListener 'stagemousedown', @onMouseDown
|
||||
@hookUpZoomControls()
|
||||
@canvas.on 'mousewheel', @onMouseWheel
|
||||
@hookUpChooseControls() if @options.choosing
|
||||
console.log "Setting fps", @world.frameRate unless @world.frameRate is 30
|
||||
createjs.Ticker.setFPS @world.frameRate
|
||||
|
@ -419,6 +433,7 @@ module.exports = Surface = class Surface extends CocoClass
|
|||
# uh
|
||||
|
||||
onMouseMove: (e) =>
|
||||
@mouseSurfacePos = {x:e.stageX, y:e.stageY}
|
||||
return if @disabled
|
||||
Backbone.Mediator.publish 'surface:mouse-moved', x: e.stageX, y: e.stageY
|
||||
|
||||
|
@ -427,12 +442,15 @@ module.exports = Surface = class Surface extends CocoClass
|
|||
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
|
||||
|
||||
hookUpZoomControls: ->
|
||||
@canvas.bind 'mousewheel', (e) =>
|
||||
# https://github.com/brandonaaron/jquery-mousewheel
|
||||
e.preventDefault()
|
||||
return if @disabled
|
||||
Backbone.Mediator.publish 'surface:mouse-scrolled', deltaX: e.deltaX, deltaY: e.deltaY unless @disabled
|
||||
onMouseWheel: (e) =>
|
||||
# https://github.com/brandonaaron/jquery-mousewheel
|
||||
e.preventDefault()
|
||||
return if @disabled
|
||||
event =
|
||||
deltaX: e.deltaX
|
||||
deltaY: e.deltaY
|
||||
surfacePos: @mouseSurfacePos
|
||||
Backbone.Mediator.publish 'surface:mouse-scrolled', event unless @disabled
|
||||
|
||||
hookUpChooseControls: ->
|
||||
chooserOptions = stage: @stage, surfaceLayer: @surfaceLayer, camera: @camera, restrictRatio: @options.choosing is 'ratio-region'
|
||||
|
|
|
@ -183,7 +183,7 @@ module.exports = class WizardSprite extends IndieSprite
|
|||
Takes into account whether the wizard is in transit or not, and the bobbing up and down.
|
||||
Eventually will also adjust based on where other wizards are.
|
||||
"""
|
||||
@targetPos = @targetSprite.thang.pos if @targetSprite
|
||||
@targetPos = @targetSprite.thang.pos if @targetSprite?.thang
|
||||
pos = _.clone(@targetPos)
|
||||
pos.z = @defaultPos().z + @getBobOffset()
|
||||
@adjustPositionToSideOfTarget(pos) if @targetSprite # be off to the side depending on placement in world
|
||||
|
@ -213,7 +213,7 @@ module.exports = class WizardSprite extends IndieSprite
|
|||
targetPos.y += @spriteYOffset
|
||||
|
||||
faceTarget: ->
|
||||
if @targetSprite
|
||||
if @targetSprite?.thang
|
||||
@pointToward(@targetSprite.thang.pos)
|
||||
|
||||
updateMarks: ->
|
||||
|
|
|
@ -46,7 +46,6 @@ module.exports.Trailmaster = class Trailmaster
|
|||
clock: 0
|
||||
|
||||
constructor: (@camera) ->
|
||||
@listener = (e) => @tick(e)
|
||||
|
||||
tick: ->
|
||||
@clock += 1
|
||||
|
|
|
@ -15,7 +15,7 @@ module.exports = class GoalManager extends CocoClass
|
|||
|
||||
nextGoalID: 0
|
||||
|
||||
constructor: (@world) ->
|
||||
constructor: (@world, @initialGoals) ->
|
||||
super()
|
||||
@init()
|
||||
|
||||
|
@ -25,6 +25,7 @@ module.exports = class GoalManager extends CocoClass
|
|||
@userCodeMap = {} # @userCodeMap.thangID.methodName.aether.raw = codeString
|
||||
@thangTeams = {}
|
||||
@initThangTeams()
|
||||
@addGoal goal for goal in @initialGoals if @initialGoals
|
||||
|
||||
initThangTeams: ->
|
||||
return unless @world
|
||||
|
@ -34,8 +35,6 @@ module.exports = class GoalManager extends CocoClass
|
|||
@thangTeams[thang.team].push(thang.id)
|
||||
|
||||
subscriptions:
|
||||
'level-add-goals': 'onAddGoals'
|
||||
'level-remove-goals': 'onRemoveGoals'
|
||||
'god:new-world-created': 'onNewWorldCreated'
|
||||
'level:restarted': 'onLevelRestarted'
|
||||
|
||||
|
@ -46,25 +45,15 @@ module.exports = class GoalManager extends CocoClass
|
|||
'world:thang-collected-item': 'onThangCollectedItem'
|
||||
'world:ended': 'onWorldEnded'
|
||||
|
||||
onLevelRestarted: =>
|
||||
onLevelRestarted: ->
|
||||
@goals = []
|
||||
@goalStates = {}
|
||||
@userCodeMap = {}
|
||||
@notifyGoalChanges()
|
||||
@addGoal goal for goal in @initialGoals if @initialGoals
|
||||
|
||||
# INTERFACE AND LIFETIME OVERVIEW
|
||||
|
||||
# main instance receives goal updates from the script manager
|
||||
onAddGoals: (e) =>
|
||||
return unless e.worldName is @world.name
|
||||
goals = e.goals
|
||||
@addGoal(goal) for goal in goals
|
||||
|
||||
onRemoveGoals: (e) =>
|
||||
if e.goal in @goals
|
||||
@goals.remove(e.goal)
|
||||
delete @goalStates[e.goal]
|
||||
|
||||
# world generator gets current goals from the main instance
|
||||
getGoals: -> @goals
|
||||
|
||||
|
@ -79,7 +68,7 @@ module.exports = class GoalManager extends CocoClass
|
|||
func = @backgroundSubscriptions[channel]
|
||||
func = utils.normalizeFunc(func, @)
|
||||
return unless func
|
||||
func(event, frameNumber)
|
||||
func.call(@, event, frameNumber)
|
||||
|
||||
# after world generation, generated goal states
|
||||
# are grabbed to send back to main instance
|
||||
|
@ -88,7 +77,7 @@ module.exports = class GoalManager extends CocoClass
|
|||
|
||||
# main instance gets them and updates their existing goal states,
|
||||
# passes the word along
|
||||
onNewWorldCreated: (e) =>
|
||||
onNewWorldCreated: (e) ->
|
||||
@updateGoalStates(e.goalStates) if e.goalStates?
|
||||
@world = e.world
|
||||
|
||||
|
@ -101,6 +90,7 @@ module.exports = class GoalManager extends CocoClass
|
|||
# IMPLEMENTATION DETAILS
|
||||
|
||||
addGoal: (goal) ->
|
||||
goal = _.cloneDeep(goal)
|
||||
goal.id = @nextGoalID++ if not goal.id
|
||||
return if @goalStates[goal.id]?
|
||||
@goals.push(goal)
|
||||
|
@ -135,48 +125,47 @@ module.exports = class GoalManager extends CocoClass
|
|||
status: null # should eventually be either 'success', 'failure', or 'incomplete'
|
||||
keyFrame: 0 # when it became a 'success' or 'failure'
|
||||
}
|
||||
|
||||
@initGoalState(state, [goal.killThangs, goal.saveThangs], 'killed')
|
||||
@initGoalState(state, [goal.getToLocations?.who, goal.keepFromLocations?.who], 'arrived')
|
||||
@initGoalState(state, [goal.leaveOffSides?.who, goal.keepFromLeavingOffSides?.who], 'left')
|
||||
@initGoalState(state, [goal.collectThangs?.who, goal.keepFromCollectingThangs?.who], 'collected')
|
||||
@goalStates[goal.id] = state
|
||||
|
||||
onThangDied: (e, frameNumber) =>
|
||||
onThangDied: (e, frameNumber) ->
|
||||
for goal in @goals ? []
|
||||
@checkKillThangs(goal.id, goal.killThangs, e.thang, frameNumber) if goal.killThangs?
|
||||
@checkKillThangs(goal.id, goal.saveThangs, e.thang, frameNumber) if goal.saveThangs?
|
||||
|
||||
checkKillThangs: (goalID, targets, thang, frameNumber) =>
|
||||
checkKillThangs: (goalID, targets, thang, frameNumber) ->
|
||||
return unless thang.id in targets or thang.team in targets
|
||||
@updateGoalState(goalID, thang.id, 'killed', frameNumber)
|
||||
|
||||
onThangTouchedGoal: (e, frameNumber) =>
|
||||
onThangTouchedGoal: (e, frameNumber) ->
|
||||
for goal in @goals ? []
|
||||
@checkArrived(goal.id, goal.getToLocations.who, goal.getToLocations.targets, e.actor.id, e.touched.id, frameNumber) if goal.getToLocations?
|
||||
@checkArrived(goal.id, goal.keepFromLocations.who, goal.keepFromLocations.targets, e.actor.id, e.touched.id, frameNumber) if goal.keepFromLocations?
|
||||
|
||||
checkArrived: (goalID, who, targets, thangID, touchedID, frameNumber) =>
|
||||
checkArrived: (goalID, who, targets, thangID, touchedID, frameNumber) ->
|
||||
return unless touchedID in targets
|
||||
return unless thangID in who
|
||||
@updateGoalState(goalID, thangID, 'arrived', frameNumber)
|
||||
|
||||
onThangLeftMap: (e, frameNumber) =>
|
||||
onThangLeftMap: (e, frameNumber) ->
|
||||
for goal in @goals ? []
|
||||
@checkLeft(goal.id, goal.leaveOffSides.who, goal.leaveOffSides.sides, e.thang.id, e.side, frameNumber) if goal.leaveOffSides?
|
||||
@checkLeft(goal.id, goal.keepFromLeavingOffSides.who, goal.keepFromLeavingOffSides.sides, e.thang.id, e.side, frameNumber) if goal.keepFromLeavingOffSides?
|
||||
|
||||
checkLeft: (goalID, who, sides, thangID, side, frameNumber) =>
|
||||
checkLeft: (goalID, who, sides, thangID, side, frameNumber) ->
|
||||
return if sides and side and not (side in sides)
|
||||
return unless thangID in who
|
||||
@updateGoalState(goalID, thangID, 'left', frameNumber)
|
||||
|
||||
onThangCollectedItem: (e, frameNumber) =>
|
||||
onThangCollectedItem: (e, frameNumber) ->
|
||||
for goal in @goals ? []
|
||||
@checkCollected(goal.id, goal.collectThangs.who, goal.collectThangs.targets, e.actor.id, e.item.id, frameNumber) if goal.collectThangs?
|
||||
@checkCollected(goal.id, goal.keepFromCollectingThangs.who, goal.keepFromCollectingThangs.targets, e.actor.id, e.item.id, frameNumber) if goal.keepFromCollectingThangs?
|
||||
|
||||
checkCollected: (goalID, who, targets, thangID, itemID, frameNumber) =>
|
||||
checkCollected: (goalID, who, targets, thangID, itemID, frameNumber) ->
|
||||
return unless itemID in targets
|
||||
return unless thangID in who
|
||||
@updateGoalState(goalID, thangID, 'collected', frameNumber)
|
||||
|
|
|
@ -1,797 +0,0 @@
|
|||
{me} = require('lib/auth')
|
||||
|
||||
# If we use marked somewhere else, we'll have to make sure to preserve options
|
||||
marked.setOptions {gfm: true, sanitize: false, smartLists: true, breaks: true}
|
||||
|
||||
markedWithImages = (s) ->
|
||||
s = s.replace /!\[(.*?)\]\((.+)? (\d+) (\d+) ?(.*?)?\)/g, '<img src="/images/docs/$2" alt="$1" title="$1" style="width: $3px; height: $4px;" class="$5"></img>' # setting width/height attrs doesn't prevent flickering, but inline css does
|
||||
marked(s)
|
||||
|
||||
module.exports.getDocsFor = (thang, props, isSnippet=false) ->
|
||||
docs = []
|
||||
types = {}
|
||||
for prop in props ? []
|
||||
type = if isSnippet then 'snippet' else typeof thang[prop]
|
||||
(types[type] ?= []).push prop
|
||||
order = ["function", "object", "string", "number", "boolean", "undefined", "snippet"]
|
||||
order.push type for type of types when not (type in order)
|
||||
for type in order
|
||||
for prop in (types[type] ? [])
|
||||
docClass = if D.hasOwnProperty prop then D[prop] else Doc
|
||||
docs.push new docClass(thang, prop, type)
|
||||
docs
|
||||
|
||||
module.exports.hasLevelDocs = (levelID) ->
|
||||
D[levelID]?
|
||||
|
||||
module.exports.getLevelDocs = (levelID, world) ->
|
||||
levelDocsClass = D[levelID] ? Level
|
||||
new levelDocsClass world
|
||||
|
||||
module.exports.Doc = class Doc
|
||||
writable: false
|
||||
owner: "this"
|
||||
constructor: (@thang, @prop, @type) ->
|
||||
if @owner isnt "this"
|
||||
@type = typeof window[@owner][@prop]
|
||||
@buildShortName()
|
||||
|
||||
buildShortName: ->
|
||||
@shortName = @prop
|
||||
if @type is 'function' then @shortName += "()"
|
||||
if @type isnt 'snippet' then @shortName = "#{@owner}.#{@shortName}"
|
||||
@shorterName = @shortName.replace 'this.', ''
|
||||
@shortName += ';'
|
||||
|
||||
title: ->
|
||||
typeStr = @type
|
||||
if @type is 'function' and @owner is 'this'
|
||||
typeStr = 'method'
|
||||
if @type isnt 'function' and not @writable
|
||||
typeStr += ' (read-only)'
|
||||
nameStr = @shortName
|
||||
"""<h4>#{nameStr} - <code class=\"prop-type\">#{typeStr}</code></h4>"""
|
||||
|
||||
html: ->
|
||||
s = markedWithImages(@doc())
|
||||
if @type in ['function', 'snippet']
|
||||
exampleCode = @example()
|
||||
if exampleCode.split('\n').length > 1
|
||||
exampleCode = "```\n#{exampleCode}```"
|
||||
else
|
||||
exampleCode = "`#{exampleCode}`"
|
||||
s += marked("**Example**:\n#{exampleCode}")
|
||||
args = @args?() or []
|
||||
if args.length
|
||||
s += marked("**Arguments**:")
|
||||
s += (arg.html() for arg in args).join('')
|
||||
else
|
||||
s += @value()
|
||||
s
|
||||
|
||||
value: ->
|
||||
"""
|
||||
<strong>Current value</strong>:
|
||||
<code class=\"current-value\" data-prop=\"#{@prop}\">#{@formatValue()}</code>
|
||||
"""
|
||||
|
||||
doc: ->
|
||||
"""
|
||||
This does something. I think.
|
||||
"""
|
||||
|
||||
example: ->
|
||||
s = "#{@owner}.#{@shortName};"
|
||||
if @type is 'function'
|
||||
exampleArgs = (arg.example ? arg.default ? arg.name for arg in @args?() ? []).join ', '
|
||||
s = "#{@owner}.#{@prop}(#{exampleArgs});"
|
||||
s
|
||||
|
||||
formatValue: ->
|
||||
if @owner is 'this'
|
||||
v = @thang[@prop]
|
||||
else
|
||||
v = window[@owner][@prop]
|
||||
if @type is 'number' and not isNaN v
|
||||
if v == Math.round v
|
||||
return v
|
||||
return v.toFixed 2
|
||||
if _.isString v
|
||||
return "\"#{v}\""
|
||||
v
|
||||
|
||||
module.exports.Arg = class Arg
|
||||
constructor: (@name, @type, @example, @description, @default) ->
|
||||
|
||||
html: ->
|
||||
s = "`#{@name}`: `#{@type}`"
|
||||
if @example then s += " (ex: `#{@example}`)"
|
||||
if @description then s += "\n#{@description}"
|
||||
if @default? then s += "\n*Default value*: `#{@default}`"
|
||||
marked s
|
||||
|
||||
module.exports.Level = class Level
|
||||
constructor: (@world) ->
|
||||
|
||||
docID: (docName="doc") ->
|
||||
"level-doc-#{docName}"
|
||||
|
||||
html: (docName="doc", title, buttons) ->
|
||||
forVictory = docName is "victory"
|
||||
docID = @docID docName
|
||||
title ?= @world.name + (if forVictory then " Complete" else "")
|
||||
docHTML = if @[docName] then markedWithImages(@[docName]()) else "No #{docName}. Wail!"
|
||||
if forVictory
|
||||
buttonHTML = @victoryButtons()
|
||||
else
|
||||
buttonHTML = @buttons(buttons ? ["I'm Ready."])
|
||||
"""<div id="#{docID}" class="level-doc modal hide fade" tabindex="-1">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal">×</button>
|
||||
<h3>#{title}</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
#{docHTML}
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
#{buttonHTML}
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
|
||||
doc: ->
|
||||
"""
|
||||
Venusaur? Oh no, counterattack with whatever you feel like, man!
|
||||
"""
|
||||
|
||||
victory: ->
|
||||
"""
|
||||
Detect it, it it no going and you tell me do things I done runnin'.
|
||||
"""
|
||||
|
||||
buttons: (names) ->
|
||||
buttonHTML = []
|
||||
for name in names
|
||||
buttonHTML.push(
|
||||
"""
|
||||
<button class="btn btn-primary" data-dismiss="modal">#{name}</button>
|
||||
"""
|
||||
)
|
||||
buttonHTML.join ' '
|
||||
|
||||
victoryButtons: ->
|
||||
buttons = """
|
||||
<button id="victory-stay" data-dismiss="modal" class="btn">Stay A While</button>
|
||||
<a href="/" class="btn">Go Home</a>
|
||||
"""
|
||||
if me.get 'anonymous'
|
||||
buttons += """
|
||||
<button class="btn btn-success sign-up-button">Sign Up for Updates</button>
|
||||
"""
|
||||
if @world.nextLevel
|
||||
buttons += """
|
||||
<a href="/play/level/#{@world.nextLevel}" class="btn btn-primary" data-dismiss="modal" >Next Level</a>
|
||||
"""
|
||||
buttons += """
|
||||
<div class="share-buttons">
|
||||
<div class="g-plusone" data-href="http://codecombat.com" data-size="medium"></div>
|
||||
<div class="fb-like" data-href="http://codecombat.com" data-send="false" data-layout="button_count" data-width="350" data-show-faces="true"></div>
|
||||
</div>
|
||||
"""
|
||||
buttons
|
||||
|
||||
|
||||
D = {} # save typing for all these things
|
||||
# Markdown: http://daringfireball.net/projects/markdown/syntax
|
||||
# GitHub Flavored Markdown: https://help.github.com/articles/github-flavored-markdown
|
||||
# Our Markdown parser and compiler: https://github.com/chjj/marked
|
||||
# I have extended the img syntax to take width and height (preventing flicker).
|
||||
|
||||
D.ifElse = class IfElse extends Doc
|
||||
buildShortName: ->
|
||||
@shortName = @shorterName = "if/else"
|
||||
|
||||
doc: ->
|
||||
"""
|
||||
The `if` control statement lets you choose whether to run the following code based on whether the condition evaluates truthily.
|
||||
|
||||
You can add an optional `else` clause to run instead when the condition evaluates falsily.
|
||||
"""
|
||||
|
||||
example: ->
|
||||
"""
|
||||
if(2 + 2 === 4) {
|
||||
// Code here
|
||||
}
|
||||
else {
|
||||
// Code here
|
||||
}
|
||||
"""
|
||||
|
||||
D.forLoop = class ForLoop extends Doc
|
||||
buildShortName: ->
|
||||
@shortName = @shorterName = "for-loop"
|
||||
|
||||
doc: ->
|
||||
"""
|
||||
The `for` loop lets you run code many times. It has four parts:
|
||||
|
||||
* initial setup: `var i = 0;` (run at the beginning)
|
||||
* loop condition: `i < 10;` (code runs while this is true)
|
||||
* loop iteration: `i += 1` (runs after every iteration)
|
||||
* main loop code: `console.log("Counted to", i);`
|
||||
"""
|
||||
|
||||
example: ->
|
||||
"""
|
||||
for(var i = 0; i < 10; i += 1) {
|
||||
// Code here
|
||||
}
|
||||
"""
|
||||
|
||||
D.whileLoop = class WhileLoop extends Doc
|
||||
buildShortName: ->
|
||||
@shortName = @shorterName = "while-loop"
|
||||
|
||||
doc: ->
|
||||
"""
|
||||
The `while` loop lets you run code many times--as long as the condition is true.
|
||||
"""
|
||||
|
||||
example: ->
|
||||
"""
|
||||
var i = 10;
|
||||
while(i < 10) {
|
||||
// Code here
|
||||
i -= 1;
|
||||
}
|
||||
"""
|
||||
|
||||
D.rotateTo = class RotateTo extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
The `rotateTo` method rotates the #{@thang.spriteName}. 
|
||||
|
||||
Use this method to change the direction that the #{@thang.spriteName} shoots.
|
||||
"""
|
||||
|
||||
args: ->
|
||||
[new Arg "degrees", "number", "180", "Desired rotation in degrees"]
|
||||
|
||||
D.shoot = class Shoot extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
Calling `this.shoot();` makes the #{@thang.spriteName} choose the `shoot` action.
|
||||
|
||||
It's equivalent to: `this.setAction(this.actions.shoot);`
|
||||
"""
|
||||
|
||||
D.pos = class Pos extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
The `x` (horizontal) and `y` (vertical) coordinates of the #{@thang.spriteName}'s center.
|
||||
"""
|
||||
|
||||
D.rotation = class Rotation extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
The #{@thang.spriteName}'s rotation in radians (`0` to `2 * Math.PI`).
|
||||
|
||||
Use the `rotateTo` method to set this value.
|
||||
"""
|
||||
|
||||
D.degrees = class Degrees extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
The #{@thang.spriteName}'s rotation in degrees (`0` to `360`).
|
||||
|
||||
Use the `rotateTo` method to set this value.
|
||||
"""
|
||||
|
||||
D.attackRange = class AttackRange extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
How far the #{@thang.spriteName}'s attack reaches, in meters.
|
||||
"""
|
||||
|
||||
D.health = class Health extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
How many health points the #{@thang.spriteName} has left.
|
||||
"""
|
||||
|
||||
D.team = class Team extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
What team the #{@thang.spriteName} is on.
|
||||
"""
|
||||
|
||||
D.actions = class Actions extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
The #{@thang.spriteName}'s available actions.
|
||||
|
||||
To, say, move, use: `this.setAction("move");`
|
||||
"""
|
||||
|
||||
formatValue: () ->
|
||||
v = @thang[@prop]
|
||||
'[' + ("\"#{actionName}\"" for actionName of v).join(', ') + ']'
|
||||
|
||||
D.action = class Action extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
The current action the #{@thang.spriteName} is running.
|
||||
|
||||
To change this, use the `setAction` method.
|
||||
"""
|
||||
|
||||
D.setAction = class SetAction extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
Sets the action that the #{@thang.spriteName} is running. Only actions in `this.actions` are valid.
|
||||
"""
|
||||
#For example, if `this.action` is currently `"idle"` and you want it to move instead, you'd set a target location, then: `this.setAction("move");`
|
||||
|
||||
args: ->
|
||||
exampleAction = "idle"
|
||||
for action in ["move", "shoot", "attack"]
|
||||
if @thang.actions[action]
|
||||
exampleAction = action
|
||||
break
|
||||
unless exampleAction
|
||||
exampleAction = _.some @thang.actions, "name"
|
||||
exampleAction = "\"#{exampleAction}\""
|
||||
[new Arg "action", "object", exampleAction, "The action to perform (must be one of `this.actions`)."]
|
||||
|
||||
D.target = class Target extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
The current target upon which the #{@thang.spriteName} is running its `action`.
|
||||
|
||||
To change this, use the `setTarget` method.
|
||||
"""
|
||||
|
||||
formatValue: () ->
|
||||
v = @thang[@prop]
|
||||
if v? then v.toString() else 'null'
|
||||
|
||||
D.setTarget = class SetTarget extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
Sets what the #{@thang.spriteName} is targeting with its current `action`, such as an enemy to attack or a position to move to.
|
||||
"""
|
||||
#For example, if `this.action` is currently `this.actions.move`, the #{@thang.spriteName} will move toward its `target`; if its action is `this.actions.attack`, it will try to attack its target.
|
||||
#For some actions, you can also pass `x` and `y` coordinates as a target instead of another unit: `this.setTarget(65, 40);`
|
||||
|
||||
args: ->
|
||||
exampleTarget = "65, 40"
|
||||
for [methods, code] in [[["getNearestEnemy"], "this.getNearestEnemy()"],
|
||||
[["pos", "move"], "this.pos.x + 20, this.pos.y - 20"],
|
||||
[["attack"], "enemy"],
|
||||
[["shoot"], "enemy"]]
|
||||
if (m for m in methods when m in @thang.programmableProperties).length
|
||||
exampleTarget = code
|
||||
break
|
||||
|
||||
[new Arg "target", "object", exampleTarget, "The new target upon which to act."]
|
||||
|
||||
D.chooseAction = class ChooseAction extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
The `chooseAction` method is run every frame, allowing the #{@thang.spriteName} a chance to look at the world and decide what action to pursue this frame.
|
||||
|
||||
To see available actions, hover over the `actions` property below. To choose an action, say `attack`, you can write: `this.setAction(this.actions.attack);`
|
||||
"""
|
||||
|
||||
D.plan = class Plan extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
The `plan` method (spell) is where you write a sequence of methods (spells) to command the #{@thang.spriteName}.
|
||||
|
||||
Type your methods into the Spell Editor below. Hover over your available methods at the bottom to see what they do.
|
||||
"""
|
||||
|
||||
example: ->
|
||||
"""
|
||||
this.moveRight();
|
||||
this.moveRight();
|
||||
this.attackNearbyEnemy();
|
||||
"""
|
||||
|
||||
D.attack = class Attack extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
The `attack` method takes an enemy `target`, sets the #{@thang.spriteName}'s `target` to that `target` with the `setTarget` method, and sets the #{@thang.spriteName}'s `action` to the `this.actions.attack` action with the `setAction` method.
|
||||
"""
|
||||
|
||||
args: ->
|
||||
exampleTarget = "enemy"
|
||||
for [methods, code] in [[["getNearestEnemy"], "this.getNearestEnemy()"],
|
||||
[["getEnemies"], "this.getEnemies()[0]"]]
|
||||
if (m for m in methods when m in @thang.programmableProperties).length
|
||||
exampleTarget = code
|
||||
break
|
||||
|
||||
[new Arg "target", "object", exampleTarget, "The target enemy to attack."]
|
||||
|
||||
D.moveXY = class MoveXY extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
The `moveXY` method sets the #{@thang.spriteName}'s `targetPos` to the given `(x, y)` coordinates and also sets the #{@thang.spriteName}'s `action` to `move`.
|
||||
"""
|
||||
|
||||
args: ->
|
||||
[
|
||||
new Arg "x", "number", "24", "The x coordinate toward which to move."
|
||||
new Arg "y", "number", "35", "The y coordinate toward which to move."
|
||||
]
|
||||
|
||||
D.distanceTo = class DistanceTo extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
Returns the distance in meters to the `target` unit from the center of the #{@thang.spriteName}.
|
||||
"""
|
||||
|
||||
args: ->
|
||||
exampleTarget = "enemy"
|
||||
for [methods, code] in [#[["getNearestEnemy"], "this.getNearestEnemy()"],
|
||||
[["getEnemies"], "this.getEnemies()[0]"],
|
||||
[["getFriends"], "this.getFriends()[0]"]]
|
||||
if (m for m in methods when m in @thang.programmableProperties).length
|
||||
exampleTarget = code
|
||||
break
|
||||
|
||||
[new Arg "target", "object", exampleTarget, "The target unit whose distance you want to measure."]
|
||||
|
||||
D.getEnemies = class GetEnemies extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
Returns an array of all living enemies within eyesight.
|
||||
"""
|
||||
|
||||
example: ->
|
||||
"""
|
||||
var enemies = this.getEnemies();
|
||||
for(var i = 0; i < enemies.length; ++i) {
|
||||
var enemy = enemies[i];
|
||||
// Do something with each enemy here
|
||||
this.attack(enemy); // Example
|
||||
}
|
||||
"""
|
||||
|
||||
D.getFriends = class GetFriends extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
Returns an array of all living friends within eyesight.
|
||||
"""
|
||||
|
||||
example: ->
|
||||
"""
|
||||
var friends = this.getFriends();
|
||||
for(var i = 0; i < friends.length; ++i) {
|
||||
var friend = friends[i];
|
||||
// Do something with each friend here
|
||||
this.follow(friend); // Example
|
||||
}
|
||||
"""
|
||||
|
||||
D.getItems = class GetItems extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
Returns an array of all living items within eyesight.
|
||||
"""
|
||||
|
||||
example: ->
|
||||
"""
|
||||
var items = this.getItems()
|
||||
for(var i = 0; i < items.length; ++i) {
|
||||
var item = items[i];
|
||||
// Do something with each item here
|
||||
this.move(item.pos); // Example
|
||||
}
|
||||
"""
|
||||
|
||||
D.attackNearbyEnemy = class GetNearbyEnemy extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
Attacks any enemy within #{@thang.attackNearbyEnemyRange ? 5} meters of the #{@thang.spriteName}.
|
||||
"""
|
||||
|
||||
D.getNearestEnemy = class GetNearestEnemy extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
Returns the closest living enemy, or `null` if there aren\'t any.
|
||||
"""
|
||||
|
||||
example: ->
|
||||
"""
|
||||
var enemy = this.getNearestEnemy();
|
||||
"""
|
||||
|
||||
D.getNearestFriend = class GetNearestFriend extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
Returns the closest living friend, or `null` if there aren\'t any.
|
||||
"""
|
||||
|
||||
example: ->
|
||||
"""
|
||||
var friend = this.getNearestFriend();
|
||||
"""
|
||||
|
||||
D.getNearestCombtaant = class GetNearestCombatant extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
Returns the closest living friend or foe, or `null` if there aren\'t any.
|
||||
"""
|
||||
|
||||
example: ->
|
||||
"""
|
||||
var enemy = this.getNearestCombatant();
|
||||
"""
|
||||
|
||||
D.attackXY = class AttackXY extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
The `attackXY` method makes the #{@thang.spriteName} attack the ground at the given `(x, y)` coordinates.
|
||||
"""
|
||||
|
||||
args: ->
|
||||
[
|
||||
new Arg "x", "number", "24", "The x coordinate to attack."
|
||||
new Arg "y", "number", "35", "The y coordinate to attack."
|
||||
]
|
||||
|
||||
D.patrol = class Patrol extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
The `patrol` method causes the #{@thang.spriteName} to move between the given waypoints in a loop. When combined with code to `attack` nearby enemies, you can use it to guard an area.
|
||||
"""
|
||||
|
||||
args: ->
|
||||
[new Arg "patrolPoints", "array", "[{x: 15, y: 45}, {x: 30, y: 40}, {x: 25, y: 35}]", "An array of positions to move between."]
|
||||
|
||||
D.attackNearestEnemy = class AttackNearestEnemy extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
The `attackNearestEnemy` method causes the #{@thang.spriteName} to charge at the nearest enemy and try to slay it.
|
||||
"""
|
||||
|
||||
D.moveRight = class MoveRight extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
Moves the #{@thang.spriteName} right by #{@thang.simpleMoveDistance} meters.
|
||||
"""
|
||||
|
||||
example: ->
|
||||
"""
|
||||
this.moveRight();
|
||||
"""
|
||||
|
||||
D.moveLeft = class MoveLeft extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
Moves the #{@thang.spriteName} left by #{@thang.simpleMoveDistance} meters.
|
||||
"""
|
||||
|
||||
example: ->
|
||||
"""
|
||||
this.moveLeft();
|
||||
"""
|
||||
|
||||
D.moveUp = class MoveUp extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
Moves the #{@thang.spriteName} up by #{@thang.simpleMoveDistance} meters.
|
||||
"""
|
||||
|
||||
example: ->
|
||||
"""
|
||||
this.moveUp();
|
||||
"""
|
||||
|
||||
D.moveDown = class MoveDown extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
Moves the #{@thang.spriteName} down by #{@thang.simpleMoveDistance} meters.
|
||||
"""
|
||||
|
||||
example: ->
|
||||
"""
|
||||
this.moveDown();
|
||||
"""
|
||||
|
||||
D.say = class Say extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
Makes the #{@thang.spriteName} say the given message. Anything within #{@thang.voiceRange ? 20} meters will hear it.
|
||||
"""
|
||||
|
||||
args: ->
|
||||
[new Arg "message", "string", "\"Follow me!\"", "The message to say."]
|
||||
|
||||
D.chaseAndAttack = class ChaseAndAttack extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
Makes the #{@thang.spriteName} attack `target` if in range, otherwise move to `target`.
|
||||
"""
|
||||
|
||||
args: ->
|
||||
[new Arg "target", "object", "this.getNearestEnemy()", "The unit to chase and attack."]
|
||||
|
||||
|
||||
D.wait = class Wait extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
The `wait()` method makes the #{@thang.spriteName} wait for a moment before continuing to execute the rest of the code.
|
||||
|
||||
It currently doesn't work inside nested functions in a `plan()` method.
|
||||
"""
|
||||
|
||||
args: ->
|
||||
[
|
||||
new Arg "duration", "number", "0.1", "Number of seconds to wait."
|
||||
]
|
||||
|
||||
D.addRect = class AddRect extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
The `addRect()` method adds a rectangle centered at the given `(x, y)` coordinate with the given `width` and `height`.
|
||||
"""
|
||||
|
||||
args: ->
|
||||
[
|
||||
new Arg "x", "number", "30", "x-coordinate of center of the rectangle."
|
||||
new Arg "y", "number", "12", "y-coordinate of center of the rectangle."
|
||||
new Arg "width", "number", "4", "width of the rectangle."
|
||||
new Arg "height", "number", "24", "height of the rectangle."
|
||||
]
|
||||
|
||||
D.removeRectAt = class RemoveRectAt extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
The `removeRectAt()` method removes a previously added rectangle centered at the given `(x, y)` coordinate.
|
||||
"""
|
||||
|
||||
args: ->
|
||||
[
|
||||
new Arg "x", "number", "30", "x-coordinate of center of the rectangle to remove."
|
||||
new Arg "y", "number", "12", "y-coordinate of center of the rectangle to remove."
|
||||
]
|
||||
|
||||
D.getNavGrid = class GetNavGrid extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
The `getNavGrid()` method returns an undocumented data structure CodeCombat uses in its pathfinding system. Sorry--will improve.
|
||||
|
||||
Just use its `grid` property, which is a two-dimensional array with one-meter resolution indicating where the walls are:
|
||||
|
||||
```
|
||||
var grid = this.getNavGrid().grid;
|
||||
var y = 12;
|
||||
var x = 30;
|
||||
var occupied = grid[y][x].length > 0;
|
||||
```
|
||||
"""
|
||||
|
||||
D.spawnedRectangles = class SpawnedRectangles extends Doc
|
||||
doc: ->
|
||||
"""
|
||||
An array of rectangle objects which have been added with the `addRect()` method.
|
||||
|
||||
You can get a rectangle's dimensions, like this:
|
||||
|
||||
```
|
||||
for(var i = 0; i < this.spawnedRectangles.length; ++i) {
|
||||
var rect = this.spawnedRectangles[i];
|
||||
// rect.pos.x, rect.pos.y, rect.width, rect.height
|
||||
}
|
||||
```
|
||||
"""
|
||||
|
||||
|
||||
D.pow = class Pow extends Doc
|
||||
owner: "Math"
|
||||
doc: ->
|
||||
"""
|
||||
Returns `base` to the `exponent` power, that is, <code>base<sup>exponent</sup></code>.
|
||||
"""
|
||||
|
||||
example: ->
|
||||
"""
|
||||
Math.pow(7, 2); // returns 49
|
||||
"""
|
||||
|
||||
args: ->
|
||||
[
|
||||
new Arg "base", "number", "7", "The base number."
|
||||
new Arg "exponent", "number", "2", "The exponent used to raise the `base`."
|
||||
]
|
||||
|
||||
D.sqrt = class Sqrt extends Doc
|
||||
owner: "Math"
|
||||
doc: ->
|
||||
"""
|
||||
Returns the square root of a non-negative number. Equivalent to `Math.pow(x, 0.5)`.
|
||||
"""
|
||||
|
||||
example: ->
|
||||
"""
|
||||
Math.sqrt(49); // returns 7
|
||||
"""
|
||||
|
||||
args: ->
|
||||
[new Arg "x", "number", "49", ""]
|
||||
|
||||
D.sin = class Sin extends Doc
|
||||
owner: "Math"
|
||||
doc: ->
|
||||
"""
|
||||
Returns the sine of a number (between -1 and 1).
|
||||
"""
|
||||
|
||||
example: ->
|
||||
"""
|
||||
Math.sin(Math.PI / 4); // returns √2
|
||||
"""
|
||||
|
||||
args: ->
|
||||
[new Arg "x", "number", "Math.PI / 2", "A number in radians."]
|
||||
|
||||
D.cos = class Cos extends Doc
|
||||
owner: "Math"
|
||||
doc: ->
|
||||
"""
|
||||
Returns the cosine of a number (between -1 and 1).
|
||||
"""
|
||||
|
||||
example: ->
|
||||
"""
|
||||
Math.cos(3 * Math.PI / 4); // returns -√2
|
||||
"""
|
||||
|
||||
args: ->
|
||||
[new Arg "x", "number", "Math.PI / 2", "A number in radians."]
|
||||
|
||||
D.tan = class Tan extends Doc
|
||||
owner: "Math"
|
||||
doc: ->
|
||||
"""
|
||||
Returns the tangent of a number (between -1 and 1).
|
||||
"""
|
||||
|
||||
example: ->
|
||||
"""
|
||||
Math.tan(Math.PI / 4); // returns 0.9999999999999999
|
||||
"""
|
||||
|
||||
args: ->
|
||||
[new Arg "x", "number", "Math.PI / 2", "A number in radians."]
|
||||
|
||||
D.atan2 = class Atan2 extends Doc
|
||||
owner: "Math"
|
||||
doc: ->
|
||||
"""
|
||||
Returns the arctangent of the quotient of its arguments--a numeric value between -π and π representing the counterclockwise angle theta of an (x, y) point and the positive X axis.
|
||||
"""
|
||||
|
||||
args: ->
|
||||
[
|
||||
new Arg "y", "number", "90", ""
|
||||
new Arg "x", "number", "15", ""
|
||||
]
|
||||
|
||||
D.PI = class PI extends Doc
|
||||
owner: "Math"
|
||||
doc: ->
|
||||
"""
|
||||
The ratio of the circumference of a circle to its diameter, approximately 3.14159.
|
||||
"""
|
||||
|
||||
D.random = class Random extends Doc
|
||||
owner: "Math"
|
||||
doc: ->
|
||||
"""
|
||||
Returns a random number x such that 0 <= x < 1.
|
||||
"""
|
|
@ -2,7 +2,7 @@
|
|||
class Vector
|
||||
@className: "Vector"
|
||||
# Class methods for nondestructively operating
|
||||
for name in ['add', 'subtract', 'multiply', 'divide']
|
||||
for name in ['add', 'subtract', 'multiply', 'divide', 'limit', 'normalize']
|
||||
do (name) ->
|
||||
Vector[name] = (a, b, useZ) ->
|
||||
a.copy()[name](b, useZ)
|
||||
|
|
|
@ -31,21 +31,21 @@ module.exports = nativeDescription: "português do Brasil", englishDescription:
|
|||
about: "Sobre"
|
||||
contact: "Contate-nos"
|
||||
twitter_follow: "Seguir"
|
||||
# employers: "Employers"
|
||||
employers: "Empregadores"
|
||||
|
||||
# versions:
|
||||
# save_version_title: "Save New Version"
|
||||
# new_major_version: "New Major Version"
|
||||
# cla_prefix: "To save changes, first you must agree to our"
|
||||
# cla_url: "CLA"
|
||||
# cla_suffix: "."
|
||||
# cla_agree: "I AGREE"
|
||||
versions:
|
||||
save_version_title: "Salvar nova versão"
|
||||
new_major_version: "Nova versão principal"
|
||||
cla_prefix: "Para salvar as modificações, primeiro você deve concordar com nosso"
|
||||
cla_url: "CLA"
|
||||
cla_suffix: "."
|
||||
cla_agree: "EU CONCORDO"
|
||||
|
||||
login:
|
||||
sign_up: "Criar conta"
|
||||
log_in: "Entrar"
|
||||
log_out: "Sair"
|
||||
recover: "recuperar sua conta"
|
||||
recover: "Recuperar sua conta"
|
||||
|
||||
recover:
|
||||
recover_account_title: "Recuperar conta"
|
||||
|
@ -122,7 +122,7 @@ module.exports = nativeDescription: "português do Brasil", englishDescription:
|
|||
new_password_verify: "Confirmação"
|
||||
email_subscriptions: "Assinaturas para Notícias por Email"
|
||||
email_announcements: "Notícias"
|
||||
# email_notifications_description: "Get periodic notifications for your account."
|
||||
email_notifications_description: "Recebe notificações periódicas em sua conta."
|
||||
email_announcements_description: "Receba emails com as últimas notícias e desenvolvimentos do CodeCombat."
|
||||
contributor_emails: "Emails para as Classes de Contribuidores"
|
||||
contribute_prefix: "Estamos procurando pessoas para se juntar à nossa turma! Confira a nossa "
|
||||
|
@ -191,251 +191,252 @@ module.exports = nativeDescription: "português do Brasil", englishDescription:
|
|||
tome_select_a_thang: "Selecione alguém para "
|
||||
tome_available_spells: "Feitiços Disponíveis"
|
||||
hud_continue: "Continue (tecle Shift+Space)"
|
||||
# spell_saved: "Spell Saved"
|
||||
spell_saved: "Feitiço Salvo"
|
||||
|
||||
# admin:
|
||||
# av_title: "Admin Views"
|
||||
# av_entities_sub_title: "Entities"
|
||||
# av_entities_users_url: "Users"
|
||||
# av_entities_active_instances_url: "Active Instances"
|
||||
# av_other_sub_title: "Other"
|
||||
# av_other_debug_base_url: "Base (for debugging base.jade)"
|
||||
# u_title: "User List"
|
||||
# lg_title: "Latest Games"
|
||||
admin:
|
||||
av_title: "Visualização de Administrador"
|
||||
av_entities_sub_title: "Entidades"
|
||||
av_entities_users_url: "Usuários"
|
||||
av_entities_active_instances_url: "Instâncias Ativas"
|
||||
av_other_sub_title: "Outro"
|
||||
av_other_debug_base_url: "Base (para debugar base.jade)"
|
||||
u_title: "Lista de Usuários"
|
||||
lg_title: "Últimos Jogos"
|
||||
|
||||
# editor:
|
||||
# main_title: "CodeCombat Editors"
|
||||
# main_description: "Build your own levels, campaigns, units and educational content. We provide all the tools you need!"
|
||||
# article_title: "Article Editor"
|
||||
# article_description: "Write articles that give players overviews of programming concepts which can be used across a variety of levels and campaigns."
|
||||
# thang_title: "Thang Editor"
|
||||
# thang_description: "Build units, defining their default logic, graphics and audio. Currently only supports importing Flash exported vector graphics."
|
||||
# level_title: "Level Editor"
|
||||
# level_description: "Includes the tools for scripting, uploading audio, and constructing custom logic to create all sorts of levels. Everything we use ourselves!"
|
||||
# security_notice: "Many major features in these editors are not currently enabled by default. As we improve the security of these systems, they will be made generally available. If you'd like to use these features sooner, "
|
||||
# contact_us: "contact us!"
|
||||
# hipchat_prefix: "You can also find us in our"
|
||||
# hipchat_url: "HipChat room."
|
||||
# level_some_options: "Some Options?"
|
||||
# level_tab_thangs: "Thangs"
|
||||
# level_tab_scripts: "Scripts"
|
||||
# level_tab_settings: "Settings"
|
||||
# level_tab_components: "Components"
|
||||
# level_tab_systems: "Systems"
|
||||
# level_tab_thangs_title: "Current Thangs"
|
||||
# level_tab_thangs_conditions: "Starting Conditions"
|
||||
# level_tab_thangs_add: "Add Thangs"
|
||||
# level_settings_title: "Settings"
|
||||
# level_component_tab_title: "Current Components"
|
||||
# level_component_btn_new: "Create New Component"
|
||||
# level_systems_tab_title: "Current Systems"
|
||||
# level_systems_btn_new: "Create New System"
|
||||
# level_systems_btn_add: "Add System"
|
||||
# level_components_title: "Back to All Thangs"
|
||||
# level_components_type: "Type"
|
||||
# level_component_edit_title: "Edit Component"
|
||||
# level_system_edit_title: "Edit System"
|
||||
# create_system_title: "Create New System"
|
||||
# new_component_title: "Create New Component"
|
||||
# new_component_field_system: "System"
|
||||
editor:
|
||||
main_title: "Editores do CodeCombat"
|
||||
main_description: "Construa seus próprios níveis, campanhas, unidades e conteúdo educacional. Nós fornecemos todas as ferramentas que você precisa!"
|
||||
article_title: "Editor de Artigo"
|
||||
article_description: "Escreva artigos que forneçam aos jogadores explicações sobre conceitos de programação que podem ser utilizados em diversos níveis e campanhas."
|
||||
thang_title: "Editor de Thang"
|
||||
thang_description: "Construa unidades, definindo sua lógica padrão, gráfico e áudio. Atualmente só é suportado importação de vetores gráficos exportados do Flash."
|
||||
level_title: "Editor de Niível"
|
||||
level_description: "Inclui as ferramentas para codificar, fazer o upload de áudio e construir uma lógica diferente para criar todos os tipos de níveis. Tudo o que nós mesmos utilizamos!"
|
||||
security_notice: "Muitos recursos principais nestes editores não estão habilitados por padrão atualmente. A maneira que melhoramos a segurança desses sistemas, eles serão colocados a disposição. Se você quiser utilizar um desses recursos mais rápido, "
|
||||
contact_us: "entre em contato!"
|
||||
hipchat_prefix: "Você também pode nos encontrar na nossa"
|
||||
hipchat_url: "Sala do HipChat."
|
||||
level_some_options: "Algumas Opções?"
|
||||
level_tab_thangs: "Thangs"
|
||||
level_tab_scripts: "Scripts"
|
||||
level_tab_settings: "Configurações"
|
||||
level_tab_components: "Componentes"
|
||||
level_tab_systems: "Sistemas"
|
||||
level_tab_thangs_title: "Thangs Atuais"
|
||||
level_tab_thangs_conditions: "Condições de Início"
|
||||
level_tab_thangs_add: "Adicionar Thangs"
|
||||
level_settings_title: "Configurações"
|
||||
level_component_tab_title: "Componentess Atuais"
|
||||
level_component_btn_new: "Criar Novo Componente"
|
||||
level_systems_tab_title: "Sistemas Atuais"
|
||||
level_systems_btn_new: "Criar Novo Sistema"
|
||||
level_systems_btn_add: "Adicionar Sistema"
|
||||
level_components_title: "Voltar para Lista de Thangs"
|
||||
level_components_type: "Tipo"
|
||||
level_component_edit_title: "Editar Componente"
|
||||
level_system_edit_title: "Editar Sistema"
|
||||
create_system_title: "Criar Novo Sistema"
|
||||
new_component_title: "Criar Novo Componente"
|
||||
new_component_field_system: "Sistema"
|
||||
|
||||
# article:
|
||||
# edit_btn_preview: "Preview"
|
||||
# edit_article_title: "Edit Article"
|
||||
article:
|
||||
edit_btn_preview: "Prever"
|
||||
edit_article_title: "Editar Artigo "
|
||||
|
||||
general:
|
||||
# and: "and"
|
||||
and: "e"
|
||||
or: "ou"
|
||||
name: "Nome"
|
||||
# body: "Body"
|
||||
# version: "Version"
|
||||
# commit_msg: "Commit Message"
|
||||
# version_history_for: "Version History for: "
|
||||
# results: "Results"
|
||||
# description: "Description"
|
||||
body: "Principal"
|
||||
version: "Versão"
|
||||
commit_msg: "Mensagem do Commit"
|
||||
version_history_for: "Histórico de Versão para: "
|
||||
results: "Resultados"
|
||||
description: "Descrição"
|
||||
email: "Email"
|
||||
message: "Mensagem"
|
||||
|
||||
# about:
|
||||
# who_is_codecombat: "Who is CodeCombat?"
|
||||
# why_codecombat: "Why CodeCombat?"
|
||||
# who_description_prefix: "together started CodeCombat in 2013. We also created "
|
||||
# who_description_suffix: "in 2008, growing it to the #1 web and iOS application for learning to write Chinese and Japanese characters."
|
||||
# who_description_ending: "Now it's time to teach people to write code."
|
||||
# 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_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_3_prefix: "That's what programming is about. It's gotta be fun. Not fun like"
|
||||
# why_paragraph_3_italic: "yay a badge"
|
||||
# why_paragraph_3_center: "but fun like"
|
||||
# why_paragraph_3_italic_caps: "NO MOM I HAVE TO FINISH THE LEVEL!"
|
||||
# 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_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_ending: "And hey, it's free. "
|
||||
# why_ending_url: "Start wizarding now!"
|
||||
# george_description: "CEO, business guy, web designer, game designer, and champion of beginning programmers everywhere."
|
||||
# scott_description: "Programmer extraordinaire, software architect, kitchen wizard, and master of finances. Scott is the reasonable one."
|
||||
# nick_description: "Programming wizard, eccentric motivation mage, and upside-down experimenter. Nick can do anything and chooses to build CodeCombat."
|
||||
# jeremy_description: "Customer support mage, usability tester, and community organizer; you've probably already spoken with Jeremy."
|
||||
# michael_description: "Programmer, sys-admin, and undergrad technical wunderkind, Michael is the person keeping our servers online."
|
||||
about:
|
||||
who_is_codecombat: "Quem é CodeCombat?"
|
||||
why_codecombat: "Por que CodeCombat?"
|
||||
who_description_prefix: "juntos começamos o CodeCombat em 2013. Noós também criamos "
|
||||
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: "Agora é a hora de ensinar as pessoas a escreverem código."
|
||||
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: "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: "É disso que se trata a programação. Tem que ser divertido. Não divertido como"
|
||||
why_paragraph_3_italic: "oba uma insígnia"
|
||||
why_paragraph_3_center: "mas divertido como"
|
||||
why_paragraph_3_italic_caps: "NÃO MÃE EU PRECISO TERMINAR ESSE NÍVEL!"
|
||||
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: "Se você vai se viciar em algum jogo, fique viciado nesse e se torne um dos magos da era da tecnologia."
|
||||
why_ending: "E é de graça. "
|
||||
why_ending_url: "Comece a feitiçaria agora!"
|
||||
george_description: "CEO, um cara de negócios, web designer, designer de jogos, e campeão em iniciar programadores em qualquer lugar."
|
||||
scott_description: "Programador extraordinário, arquiteto de software, mago da cozinha e mestre de finanças. Scott é o racional."
|
||||
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: "Mago em suporte ao consumidor, testador de usabilidade, e organizador da comunidade; você provavelmente já falou com o Jeremy."
|
||||
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 wizard–not 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:
|
||||
# page_title: "Contributing"
|
||||
# character_classes_title: "Character Classes"
|
||||
# introduction_desc_intro: "We have high hopes for CodeCombat."
|
||||
# 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, "
|
||||
# introduction_desc_github_url: "CodeCombat is totally open source"
|
||||
# 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."
|
||||
# introduction_desc_ending: "We hope you'll join our party!"
|
||||
# introduction_desc_signature: "- Nick, George, Scott, Michael, and Jeremy"
|
||||
# alert_account_message_intro: "Hey there!"
|
||||
# alert_account_message_pref: "To subscribe for class emails, you'll need to "
|
||||
# alert_account_message_suf: "first."
|
||||
# alert_account_message_create_url: "create an account"
|
||||
# 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."
|
||||
# class_attributes: "Class Attributes"
|
||||
# archmage_attribute_1_pref: "Knowledge in "
|
||||
# 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."
|
||||
# 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."
|
||||
# how_to_join: "How To Join"
|
||||
# join_desc_1: "Anyone can help out! Just check out our "
|
||||
# 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? "
|
||||
# join_desc_3: ", or find us in our "
|
||||
# join_desc_4: "and we'll go from there!"
|
||||
# join_url_email: "Email us"
|
||||
# join_url_hipchat: "public HipChat room"
|
||||
# more_about_archmage: "Learn More About Becoming A Powerful Archmage"
|
||||
# archmage_subscribe_desc: "Get emails on new coding opportunities and announcements."
|
||||
# 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"
|
||||
# artisan_introduction_suf: "to then this class might be for you."
|
||||
# artisan_attribute_1: "Any experience in building content like this would be nice, such as using Blizzard's level editors. But not required!"
|
||||
# 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."
|
||||
# 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!"
|
||||
# artisan_join_desc: "Use the Level Editor in these steps, give or take:"
|
||||
# artisan_join_step1: "Read the documentation."
|
||||
# artisan_join_step2: "Create a new level and explore existing levels."
|
||||
# artisan_join_step3: "Find us in our public HipChat room for help."
|
||||
# artisan_join_step4: "Post your levels on the forum for feedback."
|
||||
# more_about_artisan: "Learn More About Becoming A Creative Artisan"
|
||||
# artisan_subscribe_desc: "Get emails on level editor updates and announcements."
|
||||
# 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."
|
||||
# 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."
|
||||
# adventurer_attribute_2: "Charismatic. Be gentle but articulate about what needs improving, and offer suggestions on how to improve."
|
||||
# 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"
|
||||
# adventurer_forum_url: "our forum"
|
||||
# adventurer_join_suf: "so if you prefer to be notified those ways, sign up there!"
|
||||
# more_about_adventurer: "Learn More About Becoming A Brave Adventurer"
|
||||
# adventurer_subscribe_desc: "Get emails when there are new levels to test."
|
||||
# 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 "
|
||||
# scribe_introduction_url_mozilla: "Mozilla Developer Network"
|
||||
# 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."
|
||||
# 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."
|
||||
# contact_us_url: "Contact us"
|
||||
# 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!"
|
||||
# more_about_scribe: "Learn More About Becoming A Diligent Scribe"
|
||||
# scribe_subscribe_desc: "Get emails about article writing announcements."
|
||||
# diplomat_introduction_pref: "So, if there's one thing we learned from the "
|
||||
# diplomat_launch_url: "launch in October"
|
||||
# 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."
|
||||
# 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!"
|
||||
# diplomat_join_pref: "We've started a lot of initial translations at "
|
||||
# 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:"
|
||||
legal:
|
||||
page_title: "Jurídico"
|
||||
opensource_intro: "CodeCombat é grátis para jogar e de código completamente aberto."
|
||||
opensource_description_prefix: "Confira "
|
||||
github_url: "nosso GitHub"
|
||||
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 "
|
||||
archmage_wiki_url: "nossa wiki mágica"
|
||||
opensource_description_suffix: "para uma lista dos softwares que fazem esse jogo possível."
|
||||
practices_title: "Respeitáveis Boas Práticas"
|
||||
practices_description: "Essas são nossas promessas para você, o jogador, de uma maneira menos jurídica."
|
||||
privacy_title: "Privacidade"
|
||||
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."
|
||||
security_title: "Segurança"
|
||||
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."
|
||||
email_title: "Email"
|
||||
email_description_prefix: "Nós não iremos te encher de spam. Através"
|
||||
email_settings_url: "das suas configurações de email"
|
||||
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."
|
||||
cost_title: "Custo"
|
||||
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:"
|
||||
recruitment_title: "Recrutamento"
|
||||
recruitment_description_prefix: "Aqui no CodeCombat, você vai se tornar um poderoso feiticeiro, não apenas no jogo, mas também na vida real."
|
||||
url_hire_programmers: "Ninguém pode contratar programadores rápido o bastante"
|
||||
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"
|
||||
recruitment_description_italic: "muito"
|
||||
recruitment_description_ending: "o site continua grátis e todo mundo fica feliz. Esse é o plano."
|
||||
copyrights_title: "Direitos Autorais e Licenças"
|
||||
contributor_title: "Contrato de Licença de Colaborador"
|
||||
contributor_description_prefix: "Todos os colaboradores, tanto no nosso site quando no nosso repositório no GitHub estão sujeitos ao nosso"
|
||||
cla_url: "CLA"
|
||||
contributor_description_suffix: " para o qual você deve estar de acordo antes de contribuir."
|
||||
code_title: "Código - MIT"
|
||||
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"
|
||||
mit_license_url: "Licença do MIT"
|
||||
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."
|
||||
art_title: "Arte/Música - Creative Commons "
|
||||
art_description_prefix: "Todo o conteúdo comum está disponível sob a"
|
||||
cc_license_url: "Creative Commons Attribution 4.0 International License"
|
||||
art_description_suffix: "Conteúdo comum é qualquer coisa disponível pelo CodeCombat com o propósito de criar níveis. Isto inclui:"
|
||||
art_music: "Música"
|
||||
art_sound: "Som"
|
||||
art_artwork: "Obra de arte"
|
||||
art_sprites: "Sprites"
|
||||
art_other: "Todo e qualquer outros trabalho criativo não relacionados a código que são disponibilizados para criar níveis."
|
||||
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."
|
||||
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:"
|
||||
use_list_1: "Se usado em um filme ou outro jogo, inclua codecombat.com nos créditos."
|
||||
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."
|
||||
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."
|
||||
rights_title: "Direitos Reservados"
|
||||
rights_desc: "Todos os direitos são reservados para os Níveis em si. Isto inclui"
|
||||
rights_scripts: "Scripts"
|
||||
rights_unit: "Configurações de unidades"
|
||||
rights_description: "Descrição"
|
||||
rights_writings: "Escritos"
|
||||
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."
|
||||
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 é."
|
||||
nutshell_title: "Em poucas palavras"
|
||||
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."
|
||||
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."
|
||||
|
||||
# classes:
|
||||
# archmage_title: "Archmage"
|
||||
# archmage_title_description: "(Coder)"
|
||||
# artisan_title: "Artisan"
|
||||
# artisan_title_description: "(Level Builder)"
|
||||
# adventurer_title: "Adventurer"
|
||||
# adventurer_title_description: "(Level Playtester)"
|
||||
# scribe_title: "Scribe"
|
||||
# scribe_title_description: "(Article Editor)"
|
||||
# diplomat_title: "Diplomat"
|
||||
# diplomat_title_description: "(Translator)"
|
||||
# ambassador_title: "Ambassador"
|
||||
# ambassador_title_description: "(Support)"
|
||||
# counselor_title: "Counselor"
|
||||
# counselor_title_description: "(Expert/Teacher)"
|
||||
contribute:
|
||||
page_title: "Contribuindo"
|
||||
character_classes_title: "Classes de Personagem"
|
||||
introduction_desc_intro: "Nós temos grandes expectativas para o CodeCombat."
|
||||
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, "
|
||||
introduction_desc_github_url: "CodeCombat é totalmente código aberto"
|
||||
introduction_desc_suf: ", e nosso objetivo é oferecer quantas maneiras forem possíveis para você participar e fazer deste projeto tanto seu como nosso."
|
||||
introduction_desc_ending: "Nós esperamos que você se junte a nossa festa!"
|
||||
introduction_desc_signature: "- Nick, George, Scott, Michael, and Jeremy"
|
||||
alert_account_message_intro: "Ei!"
|
||||
alert_account_message_pref: "Para se inscrever para os emails de classe, você vai precisar, "
|
||||
alert_account_message_suf: "primeiro."
|
||||
alert_account_message_create_url: "criar uma conta"
|
||||
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."
|
||||
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)"
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
module.exports = nativeDescription: "简体中文", englishDescription: "Chinese (Simplified)", translation:
|
||||
common:
|
||||
loading: "读取中..."
|
||||
loading: "读取中……"
|
||||
saving: "保存中……"
|
||||
sending: "发送中..."
|
||||
sending: "发送中……"
|
||||
cancel: "退出"
|
||||
# save: "Save"
|
||||
delay_1_sec: "1 秒"
|
||||
|
@ -53,33 +53,33 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
|
|||
|
||||
signup:
|
||||
# create_account_title: "Create Account to Save Progress"
|
||||
description: "这是免费的。简单易学:"
|
||||
description: "这是免费的,简单易学:"
|
||||
email_announcements: "通过邮件接收通知"
|
||||
coppa: "13 岁以上或非美国用户"
|
||||
coppa: "13岁以上或非美国用户"
|
||||
coppa_why: "为什么?"
|
||||
creating: "账户创建中..."
|
||||
creating: "账户创建中……"
|
||||
sign_up: "注册"
|
||||
log_in: "登录"
|
||||
|
||||
home:
|
||||
slogan: "通过玩儿游戏学到Javascript脚本语言"
|
||||
no_ie: "抱歉!Internet Explorer 9等更旧的预览器打不开此网站"
|
||||
no_ie: "抱歉!Internet Explorer 9等更旧的预览器打不开此网站。"
|
||||
no_mobile: "CodeCombat 不是针对手机设备设计的,所以可能不好用!"
|
||||
play: "玩"
|
||||
|
||||
play:
|
||||
choose_your_level: "选取难度"
|
||||
adventurer_prefix: "你可以选择以下任意关卡,或者讨论以上的关卡 "
|
||||
adventurer_forum: "冒险家论坛"
|
||||
adventurer_prefix: "你可以选择以下任意关卡,或者讨论以上的关卡。"
|
||||
adventurer_forum: "冒险者论坛"
|
||||
adventurer_suffix: "."
|
||||
campaign_beginner: "新手作战"
|
||||
campaign_beginner_description: "...在这里可以学到编程技巧。"
|
||||
campaign_beginner_description: "……在这里可以学到编程技巧。"
|
||||
campaign_dev: "随机困难关卡"
|
||||
campaign_dev_description: "...在这里你可以学到做一些复杂功能的接口。"
|
||||
campaign_dev_description: "……在这里你可以学到做一些复杂功能的接口。"
|
||||
campaign_multiplayer: "多人竞技场"
|
||||
campaign_multiplayer_description: "...在这里你可以和其他玩家们进行代码肉搏战。"
|
||||
campaign_multiplayer_description: "……在这里你可以和其他玩家们进行代码肉搏战。"
|
||||
campaign_player_created: "已创建的玩家"
|
||||
campaign_player_created_description: "...在这里你可以与你的小伙伴的创造力战斗 <a href=\"/contribute#artisan\">技术指导</a>."
|
||||
campaign_player_created_description: "……在这里你可以与你的小伙伴的创造力战斗 <a href=\"/contribute#artisan\">技术指导</a>."
|
||||
level_difficulty: "难度"
|
||||
|
||||
contact:
|
||||
|
@ -90,13 +90,13 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
|
|||
contribute_suffix: "!"
|
||||
forum_prefix: "对任何公共部分,放手去干吧 "
|
||||
forum_page: "我们的论坛"
|
||||
forum_suffix: "代替。"
|
||||
forum_suffix: "代替 "
|
||||
send: "意见反馈"
|
||||
|
||||
diplomat_suggestion:
|
||||
title: "帮我们翻译CodeCombat"
|
||||
title: "帮我们翻译 CodeCombat"
|
||||
sub_heading: "我们需要您的语言技能"
|
||||
pitch_body: "我们开发了CodeCombat的英文版,但是现在我们的玩家遍布全球。很多人想玩中文(简体)版的,却不会说英语,所以如果你中英文都会,请考虑一下参加我们的翻译工作,帮忙把 CodeCombat 网站还有所有的关卡翻译成中文(简体)。"
|
||||
pitch_body: "我们开发了 CodeCombat 的英文版,但是现在我们的玩家遍布全球。很多人想玩中文(简体)版的,却不会说英语,所以如果你中英文都会,请考虑一下参加我们的翻译工作,帮忙把 CodeCombat 网站还有所有的关卡翻译成中文(简体)。"
|
||||
missing_translations: "未翻译的文本将显示为英文。"
|
||||
learn_more: "了解更多有关成为翻译人员的说明"
|
||||
subscribe_as_diplomat: "提交翻译人员申请"
|
||||
|
@ -122,7 +122,7 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
|
|||
new_password_verify: "核实"
|
||||
email_subscriptions: "邮箱验证"
|
||||
email_announcements: "通知"
|
||||
# email_notifications_description: "Get periodic notifications for your account."
|
||||
email_notifications_description: "定期接受来自你的账户的通知。"
|
||||
email_announcements_description: "接收关于 CodeCombat 最近的新闻和发展的邮件。"
|
||||
contributor_emails: "贡献者通知"
|
||||
contribute_prefix: "我们在寻找志同道合的人!请到 "
|
||||
|
@ -153,7 +153,7 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
|
|||
level_load_error: "关卡不能载入。"
|
||||
done: "完成"
|
||||
grid: "格子"
|
||||
customize_wizard: "自定义巫师"
|
||||
customize_wizard: "自定义向导"
|
||||
home: "主页"
|
||||
guide: "指南"
|
||||
multiplayer: "多人游戏"
|
||||
|
@ -168,7 +168,7 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
|
|||
victory_title_suffix: " 完成"
|
||||
victory_sign_up: "保存进度"
|
||||
victory_sign_up_poke: "想保存你的代码?创建一个免费账户吧!"
|
||||
victory_rate_the_level: "评估关卡: "
|
||||
victory_rate_the_level: "评估关卡:"
|
||||
victory_play_next_level: "下一关"
|
||||
victory_go_home: "返回主页"
|
||||
victory_review: "给我们反馈!"
|
||||
|
@ -178,7 +178,7 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
|
|||
multiplayer_link_description: "把这个链接告诉小伙伴们,一起玩吧。"
|
||||
multiplayer_hint_label: "提示:"
|
||||
multiplayer_hint: " 点击全选,然后按 Apple-C(苹果电脑)或 Ctrl-C 复制链接。"
|
||||
multiplayer_coming_soon: "多人游戏的更多特性!"
|
||||
multiplayer_coming_soon: "多人游戏的更多特性!"
|
||||
guide_title: "指南"
|
||||
tome_minion_spells: "助手的咒语"
|
||||
tome_read_only_spells: "只读的咒语"
|
||||
|
@ -190,7 +190,7 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
|
|||
tome_select_spell: "选择一个法术"
|
||||
tome_select_a_thang: "选择人物来 "
|
||||
tome_available_spells: "可用的法术"
|
||||
hud_continue: "继续 (按 shift-空格)"
|
||||
hud_continue: "继续(按 shift-空格)"
|
||||
# spell_saved: "Spell Saved"
|
||||
|
||||
# admin:
|
||||
|
|
|
@ -3,14 +3,14 @@ module.exports = nativeDescription: "繁体中文", englishDescription: "Chinese
|
|||
loading: "Loading..."
|
||||
saving: "儲存中..."
|
||||
sending: "發送中...."
|
||||
cancel: "退出"
|
||||
# save: "Save"
|
||||
cancel: "取消"
|
||||
save: "存檔"
|
||||
delay_1_sec: "1 秒"
|
||||
delay_3_sec: "3 秒"
|
||||
delay_5_sec: "5 秒"
|
||||
manual: "手動手动"
|
||||
# fork: "Fork"
|
||||
play: "玩"
|
||||
manual: "手動發動"
|
||||
fork: "Fork"
|
||||
play: "播放" # Play timeline in a level.
|
||||
|
||||
modal:
|
||||
close: "關閉"
|
||||
|
@ -20,18 +20,18 @@ module.exports = nativeDescription: "繁体中文", englishDescription: "Chinese
|
|||
page_not_found: "找不到網頁"
|
||||
|
||||
nav:
|
||||
play: "玩"
|
||||
play: "開始遊戲"
|
||||
editor: "編輯"
|
||||
blog: "博客"
|
||||
blog: "官方部落格" # Official blog
|
||||
forum: "論壇"
|
||||
admin: "超級管理員"
|
||||
admin: "系統管理員"
|
||||
home: "首頁"
|
||||
contribute: "貢獻"
|
||||
legal: "法律"
|
||||
legal: "版權聲明" # Copyright
|
||||
about: "關於"
|
||||
contact: "聯繫我們"
|
||||
twitter_follow: "關注"
|
||||
# employers: "Employers"
|
||||
twitter_follow: "在Twitter關注"
|
||||
employers: "招募訊息"
|
||||
|
||||
# versions:
|
||||
# save_version_title: "Save New Version"
|
||||
|
@ -43,61 +43,61 @@ module.exports = nativeDescription: "繁体中文", englishDescription: "Chinese
|
|||
|
||||
login:
|
||||
sign_up: "註冊"
|
||||
log_in: "登錄"
|
||||
log_out: "退出"
|
||||
recover: "找回帳戶"
|
||||
log_in: "登入"
|
||||
log_out: "登出"
|
||||
recover: "找回帳號"
|
||||
|
||||
recover:
|
||||
recover_account_title: "復原帳戶"
|
||||
# send_password: "Send Recovery Password"
|
||||
recover_account_title: "復原帳號"
|
||||
send_password: "送出新密碼"
|
||||
|
||||
signup:
|
||||
# create_account_title: "Create Account to Save Progress"
|
||||
description: "這是免費的。超簡單的喲:"
|
||||
create_account_title: "建立帳號儲存進度"
|
||||
description: "登入以儲存遊戲進度:"
|
||||
email_announcements: "通過郵件接收通知"
|
||||
coppa: "13歲以上或非美國公民"
|
||||
coppa_why: "爲什麽?"
|
||||
creating: "帳戶創建中..."
|
||||
creating: "帳號建立中..."
|
||||
sign_up: "註冊"
|
||||
log_in: "登錄"
|
||||
log_in: "登入"
|
||||
|
||||
home:
|
||||
slogan: "通過玩遊戲學習Javascript 腳本語言"
|
||||
no_ie: "抱歉!Internet Explorer 9 等舊的瀏覽器打不開此網站"
|
||||
no_mobile: "CodeCombat 不是針對手機設備設計的,所以可能會出問題!"
|
||||
play: "玩"
|
||||
play: "開始遊戲"
|
||||
|
||||
play:
|
||||
choose_your_level: "選取難度"
|
||||
choose_your_level: "選取關卡"
|
||||
adventurer_prefix: "你可以選擇以下任意關卡,或者討論以上的關卡 "
|
||||
adventurer_forum: "冒險家論壇"
|
||||
adventurer_suffix: "."
|
||||
campaign_beginner: "新手作戰"
|
||||
campaign_beginner: "新手指南"
|
||||
campaign_beginner_description: "...在這裡可以學到編程技巧。"
|
||||
campaign_dev: "隨機關卡"
|
||||
campaign_dev_description: "...在這裡你可以學到做一些複雜功能的接口。"
|
||||
campaign_dev_description: "...在這裡你可以學到做一些較複雜的程式技巧。"
|
||||
campaign_multiplayer: "多人競技場"
|
||||
campaign_multiplayer_description: "...在這裡你可以和其他玩家們進行代碼近戰。"
|
||||
campaign_player_created: "已創建的玩家"
|
||||
campaign_player_created_description: "...在這裡你可以與你的小夥伴的創造力戰鬥 <a href=\"/contribute#artisan\">技術指導</a>."
|
||||
campaign_multiplayer_description: "...在這裡你可以和其他玩家進行對戰。"
|
||||
campaign_player_created: "玩家建立的關卡"
|
||||
campaign_player_created_description: "...挑戰同伴的創意 <a href=\"/contribute#artisan\">技術指導</a>."
|
||||
level_difficulty: "難度"
|
||||
|
||||
contact:
|
||||
contact_us: "聯繫我們"
|
||||
welcome: "很高興收到你的信!用這個表格給我們發電郵。 "
|
||||
contribute_prefix: "如果你想貢獻代碼,請看 "
|
||||
contribute_page: "貢獻代碼頁面"
|
||||
contribute_prefix: "如果你想貢獻程式,請看 "
|
||||
contribute_page: "程式貢獻頁面"
|
||||
contribute_suffix: "!"
|
||||
forum_prefix: "對於任何公共部份,放手去做吧 "
|
||||
forum_page: "我們的論壇"
|
||||
forum_suffix: "代替。"
|
||||
forum_prefix: "如果有任何問題, 請至"
|
||||
forum_page: "論壇"
|
||||
forum_suffix: "討論。"
|
||||
sending: "發送中。。。"
|
||||
send: "意見反饋"
|
||||
|
||||
diplomat_suggestion:
|
||||
title: "幫我們翻譯CodeCombat"
|
||||
sub_heading: "我們需要您的語言技能"
|
||||
pitch_body: "我們開發了CodeCombat的英文版,但是現在我們的玩家遍佈全球。很多人想玩中文(繁体)版的,卻不會說英文,所以如果你中英文都會,請考慮一下參加我們的翻譯工作,幫忙把 CodeCombat 網站還有所有的關卡翻譯成中文(繁体)。"
|
||||
pitch_body: "我們開發了CodeCombat的英文版,但是現在我們的玩家遍佈全球。很多人想玩中文版的,卻不會說英文,所以如果你中英文都會,請考慮一下參加我們的翻譯工作,幫忙把 CodeCombat 網站還有所有的關卡翻譯成中文(繁体)。"
|
||||
missing_translations: "直至所有正體中文的翻譯完畢,當無法提供正體中文時還會以英文顯示。"
|
||||
learn_more: "關於成為外交官"
|
||||
subscribe_as_diplomat: "註冊成為外交官"
|
||||
|
@ -107,91 +107,92 @@ module.exports = nativeDescription: "繁体中文", englishDescription: "Chinese
|
|||
# customize_avatar: "Customize Your Avatar"
|
||||
|
||||
account_settings:
|
||||
title: "帳戶設置"
|
||||
not_logged_in: "登錄或創建一個帳戶來修改設置。"
|
||||
title: "帳號設定"
|
||||
not_logged_in: "登錄或建立一個帳號來修改設置。"
|
||||
autosave: "自動保存修改"
|
||||
me_tab: "我"
|
||||
picture_tab: "圖片"
|
||||
picture_tab: "頭像"
|
||||
wizard_tab: "巫師"
|
||||
password_tab: "密碼"
|
||||
emails_tab: "郵件"
|
||||
gravatar_select: "選擇使用 Gravatar 照片"
|
||||
gravatar_add_photos: "添加小圖和照片到一个 Gravatar 帳戶供你選擇。"
|
||||
gravatar_add_more_photos: "添加更多照片到你的 Gravatar 帳戶并查看。"
|
||||
gravatar_select: "選擇一個Gravatar"
|
||||
gravatar_add_photos: "上傳頭像到Gravatar"
|
||||
# gravatar_add_more_photos: "Add thumbnails and photos to a Gravatar account for your email to choose an image."
|
||||
wizard_color: "巫師 衣服 顏色"
|
||||
new_password: "新密碼"
|
||||
new_password_verify: "核實"
|
||||
email_subscriptions: "郵箱驗證"
|
||||
new_password_verify: "確認密碼"
|
||||
email_subscriptions: "訂閱"
|
||||
email_announcements: "通知"
|
||||
email_announcements_description: "接收關於 CodeCombat 最近的新聞和發展的郵件。"
|
||||
email_notifications_description: "接收帳號通知"
|
||||
email_announcements_description: "接收關於 CodeCombat 的新聞和開發消息。"
|
||||
contributor_emails: "貢獻者電郵"
|
||||
contribute_prefix: "我們在尋找志同道合的人!請到 "
|
||||
contribute_page: "貢獻頁面"
|
||||
contribute_suffix: " 查看更多信息。"
|
||||
email_toggle: "切換所有"
|
||||
error_saving: "保存時出錯"
|
||||
saved: "保存修改"
|
||||
password_mismatch: "密碼不匹配。"
|
||||
email_toggle: "全選"
|
||||
error_saving: "保存時發生錯誤"
|
||||
saved: "修改已儲存"
|
||||
password_mismatch: "密碼不正確。"
|
||||
|
||||
account_profile:
|
||||
edit_settings: "編輯設置"
|
||||
profile_for_prefix: "關於TA的基本資料:"
|
||||
# profile_for_suffix: ""
|
||||
edit_settings: "帳號設定"
|
||||
profile_for_prefix: "關於"
|
||||
profile_for_suffix: "的基本資料"
|
||||
profile: "基本資料"
|
||||
user_not_found: "沒有找到用戶。檢查URL?"
|
||||
gravatar_not_found_mine: "我們找不到TA的基本資料:"
|
||||
gravatar_not_found_email_suffix: "."
|
||||
gravatar_signup_prefix: "去註冊 "
|
||||
gravatar_signup_suffix: " 去設置!"
|
||||
gravatar_not_found_other: "哎呦,沒有與這個人的郵箱相關的資料。"
|
||||
gravatar_contact: "聯繫"
|
||||
gravatar_not_found_mine: "我們找不到有關"
|
||||
gravatar_not_found_email_suffix: "的資料"
|
||||
gravatar_signup_prefix: "請至"
|
||||
gravatar_signup_suffix: " 註冊帳號"
|
||||
gravatar_not_found_other: "哎呦,找不到這個地址的資料。"
|
||||
gravatar_contact: "聯繫我們"
|
||||
gravatar_websites: "網站"
|
||||
gravatar_accounts: "顯示為"
|
||||
gravatar_profile_link: "完善 Gravatar 資料"
|
||||
|
||||
play_level:
|
||||
level_load_error: "關卡不能載入。"
|
||||
level_load_error: "載入關卡時發生錯誤。"
|
||||
done: "完成"
|
||||
grid: "格子"
|
||||
customize_wizard: "自定義巫師"
|
||||
home: "主頁"
|
||||
home: "首頁"
|
||||
guide: "指南"
|
||||
multiplayer: "多人遊戲"
|
||||
restart: "重新開始"
|
||||
goals: "目標"
|
||||
action_timeline: "行動時間軸"
|
||||
click_to_select: "點擊選擇一個單元。"
|
||||
reload_title: "重載所有代碼?"
|
||||
reload_really: "確定重載這一關,返回開始處?"
|
||||
reload_confirm: "重載所有"
|
||||
# victory_title_prefix: ""
|
||||
reload_title: "重新載入程式碼?"
|
||||
reload_really: "確定重設所有的程式碼?"
|
||||
reload_confirm: "重設所有程式碼"
|
||||
victory_title_prefix: ""
|
||||
victory_title_suffix: " 完成"
|
||||
victory_sign_up: "保存進度"
|
||||
victory_sign_up_poke: "想保存你的代碼?創建一個免費帳戶吧!"
|
||||
victory_sign_up_poke: "想保存你的程式碼?建立一個免費帳號吧!"
|
||||
victory_rate_the_level: "評估關卡: "
|
||||
victory_play_next_level: "下一關"
|
||||
victory_go_home: "返回主頁"
|
||||
victory_review: "給我們反饋!"
|
||||
victory_go_home: "返回首頁"
|
||||
victory_review: "給我們回饋!"
|
||||
victory_hour_of_code_done: "你完成了嗎?"
|
||||
victory_hour_of_code_done_yes: "是的,我完成了我的代碼!"
|
||||
multiplayer_title: "多人遊戲設置"
|
||||
multiplayer_link_description: "把這個鏈接告訴小夥伴們,一起玩吧。"
|
||||
victory_hour_of_code_done_yes: "是的,我完成了我的程式碼!"
|
||||
multiplayer_title: "多人遊戲設定"
|
||||
multiplayer_link_description: "把這個連結告訴同伴們,一起玩吧。"
|
||||
multiplayer_hint_label: "提示:"
|
||||
multiplayer_hint: " 點擊全選,然後按 Apple-C(苹果電腦)或 Ctrl-C 複製鏈接。"
|
||||
multiplayer_coming_soon: "多人遊戲的更多特性!"
|
||||
multiplayer_hint: " 點擊全選,然後按 ⌘-C 或 Ctrl-C 複製連結。"
|
||||
multiplayer_coming_soon: "請期待更多的多人關卡!"
|
||||
guide_title: "指南"
|
||||
tome_minion_spells: "助手的咒語"
|
||||
tome_read_only_spells: "只讀的咒語"
|
||||
tome_other_units: "其他單元"
|
||||
tome_read_only_spells: "唯讀的咒語"
|
||||
tome_other_units: "其他單位"
|
||||
tome_cast_button_castable: "發動"
|
||||
tome_cast_button_casting: "發動中"
|
||||
tome_cast_button_cast: "咒語"
|
||||
tome_autocast_delay: "自動施法延遲"
|
||||
tome_select_spell: "選擇一個法術"
|
||||
tome_select_a_thang: "選擇人物來 "
|
||||
tome_select_a_thang: "選擇一個人物來施放"
|
||||
tome_available_spells: "可用的法術"
|
||||
hud_continue: "繼續 (按 shift-空格)"
|
||||
# spell_saved: "Spell Saved"
|
||||
spell_saved: "咒語已儲存"
|
||||
|
||||
# admin:
|
||||
# av_title: "Admin Views"
|
||||
|
@ -245,33 +246,33 @@ module.exports = nativeDescription: "繁体中文", englishDescription: "Chinese
|
|||
|
||||
# general:
|
||||
# and: "and"
|
||||
or: "或"
|
||||
name: "名字"
|
||||
# or: "或"
|
||||
# name: "名字"
|
||||
# body: "Body"
|
||||
# version: "Version"
|
||||
# commit_msg: "Commit Message"
|
||||
# version_history_for: "Version History for: "
|
||||
# results: "Results"
|
||||
# description: "Description"
|
||||
email: "郵箱"
|
||||
message: "留言"
|
||||
# email: "Email"
|
||||
# message: "訊息"
|
||||
|
||||
# about:
|
||||
# who_is_codecombat: "Who is CodeCombat?"
|
||||
# why_codecombat: "Why CodeCombat?"
|
||||
# who_description_prefix: "together started CodeCombat in 2013. We also created "
|
||||
# who_description_suffix: "in 2008, growing it to the #1 web and iOS application for learning to write Chinese and Japanese characters."
|
||||
# who_description_ending: "Now it's time to teach people to write code."
|
||||
# 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_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_3_prefix: "That's what programming is about. It's gotta be fun. Not fun like"
|
||||
# why_paragraph_3_italic: "yay a badge"
|
||||
# why_paragraph_3_center: "but fun like"
|
||||
# why_paragraph_3_italic_caps: "NO MOM I HAVE TO FINISH THE LEVEL!"
|
||||
# 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_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_ending: "And hey, it's free. "
|
||||
# why_ending_url: "Start wizarding now!"
|
||||
about:
|
||||
who_is_codecombat: "什麼是CodeCombat?"
|
||||
why_codecombat: "為什麼使用CodeCombat?"
|
||||
who_description_prefix: "在2013年共同創立了CodeCombat. 在2008年, 我們創立了"
|
||||
who_description_suffix: ",排名第一的中、日文字的學習網頁及iOS系統應用程式。"
|
||||
who_description_ending: "這次,我們將教大家如何寫程式。"
|
||||
why_paragraph_1: "當我們在研發Skritter時,George不會寫程式,所以常常無法展現他的想法。他嘗試去學,然而課程成果緩慢。他的室友曾想學習新技能,試過Codecademy,但厭倦了。每周也都有其他朋友投入Codecademy,卻都以失敗告終。我們發現,這與我們藉由Skritter所想解決的問題是一致的─人們需要的不是繁瑣又密集的課程, 而是快速而大量的練習。我們知道該如何改善這個情況。"
|
||||
why_paragraph_2: "想學程式嗎? 你不需要課程。你需要的只是大量的時間去\"玩\"程式。"
|
||||
why_paragraph_3_prefix: "寫程式應該是有趣的。當然不是"
|
||||
why_paragraph_3_italic: "「耶!拿到獎章了。」"
|
||||
why_paragraph_3_center: "的有趣, 而是"
|
||||
why_paragraph_3_italic_caps: "「媽我不要出去玩,我要寫完這段!」"
|
||||
why_paragraph_3_suffix: "般引人入勝。這是為甚麼CodeCombat被設計成多人對戰「遊戲」,而不是遊戲化「課程」。在你對這遊戲無法自拔之前,我們是不會放棄的─幫然,這個遊戲,將是有益於你的。"
|
||||
why_paragraph_4: "如果你要沉迷遊戲的話,就來沉迷CodeCombat,成為科技時代的魔法師吧!"
|
||||
why_ending: "啊還有,他是免費的。"
|
||||
why_ending_url: "那還等什麼? 馬上開始!"
|
||||
# george_description: "CEO, business guy, web designer, game designer, and champion of beginning programmers everywhere."
|
||||
# scott_description: "Programmer extraordinaire, software architect, kitchen wizard, and master of finances. Scott is the reasonable one."
|
||||
# nick_description: "Programming wizard, eccentric motivation mage, and upside-down experimenter. Nick can do anything and chooses to build CodeCombat."
|
||||
|
|
|
@ -25,7 +25,7 @@ class CocoModel extends Backbone.Model
|
|||
@loadSchema()
|
||||
@once 'sync', @onLoaded, @
|
||||
@saveBackup = _.debounce(@saveBackup, 500)
|
||||
|
||||
|
||||
type: ->
|
||||
@constructor.className
|
||||
|
||||
|
@ -36,18 +36,18 @@ class CocoModel extends Backbone.Model
|
|||
if @saveBackups
|
||||
existing = storage.load @id
|
||||
if existing
|
||||
@set(existing, {silent:true})
|
||||
@set(existing, {silent:true})
|
||||
CocoModel.backedUp[@id] = @
|
||||
|
||||
|
||||
set: ->
|
||||
res = super(arguments...)
|
||||
@saveBackup() if @saveBackups and @loaded and @hasLocalChanges()
|
||||
res
|
||||
|
||||
|
||||
saveBackup: ->
|
||||
storage.save(@id, @attributes)
|
||||
CocoModel.backedUp[@id] = @
|
||||
|
||||
|
||||
@backedUp = {}
|
||||
|
||||
loadSchema: ->
|
||||
|
@ -55,7 +55,7 @@ class CocoModel extends Backbone.Model
|
|||
@constructor.schema = new CocoSchema(@urlRoot)
|
||||
@constructor.schema.fetch()
|
||||
|
||||
@constructor.schema.on 'sync', =>
|
||||
@constructor.schema.once 'sync', =>
|
||||
@constructor.schema.loaded = true
|
||||
@addSchemaDefaults()
|
||||
@trigger 'schema-loaded'
|
||||
|
@ -81,7 +81,7 @@ class CocoModel extends Backbone.Model
|
|||
return super attrs, options
|
||||
|
||||
fetch: ->
|
||||
super()
|
||||
super(arguments...)
|
||||
@loading = true
|
||||
|
||||
markToRevert: ->
|
||||
|
@ -90,7 +90,7 @@ class CocoModel extends Backbone.Model
|
|||
revert: ->
|
||||
@set(@_revertAttributes, {silent: true}) if @_revertAttributes
|
||||
@clearBackup()
|
||||
|
||||
|
||||
clearBackup: ->
|
||||
storage.remove @id
|
||||
|
||||
|
@ -127,7 +127,7 @@ class CocoModel extends Backbone.Model
|
|||
#console.log "setting", prop, "to", sch.default, "from sch.default" if sch.default?
|
||||
@set prop, sch.default if sch.default?
|
||||
|
||||
getReferencedModels: (data, schema, path='/') ->
|
||||
getReferencedModels: (data, schema, path='/', shouldLoadProjection=null) ->
|
||||
# returns unfetched model shells for every referenced doc in this model
|
||||
# OPTIMIZE so that when loading models, it doesn't cause the site to stutter
|
||||
data ?= @attributes
|
||||
|
@ -136,18 +136,18 @@ class CocoModel extends Backbone.Model
|
|||
|
||||
if $.isArray(data) and schema.items?
|
||||
for subData, i in data
|
||||
models = models.concat(@getReferencedModels(subData, schema.items, path+i+'/'))
|
||||
models = models.concat(@getReferencedModels(subData, schema.items, path+i+'/', shouldLoadProjection))
|
||||
|
||||
if $.isPlainObject(data) and schema.properties?
|
||||
for key, subData of data
|
||||
continue unless schema.properties[key]
|
||||
models = models.concat(@getReferencedModels(subData, schema.properties[key], path+key+'/'))
|
||||
models = models.concat(@getReferencedModels(subData, schema.properties[key], path+key+'/', shouldLoadProjection))
|
||||
|
||||
model = CocoModel.getReferencedModel data, schema
|
||||
model = CocoModel.getReferencedModel data, schema, shouldLoadProjection
|
||||
models.push model if model
|
||||
return models
|
||||
|
||||
@getReferencedModel: (data, schema) ->
|
||||
@getReferencedModel: (data, schema, shouldLoadProjection=null) ->
|
||||
return null unless schema.links?
|
||||
linkObject = _.find schema.links, rel: "db"
|
||||
return null unless linkObject
|
||||
|
@ -158,9 +158,9 @@ class CocoModel extends Backbone.Model
|
|||
link = link.replace('{(original)}', data.original)
|
||||
link = link.replace('{(majorVersion)}', '' + (data.majorVersion ? 0))
|
||||
link = link.replace('{($)}', data)
|
||||
@getOrMakeModelFromLink(link)
|
||||
@getOrMakeModelFromLink(link, shouldLoadProjection)
|
||||
|
||||
@getOrMakeModelFromLink: (link) ->
|
||||
@getOrMakeModelFromLink: (link, shouldLoadProjection=null) ->
|
||||
makeUrlFunc = (url) -> -> url
|
||||
modelUrl = link.split('/')[2]
|
||||
modelModule = _.string.classify(modelUrl)
|
||||
|
@ -175,6 +175,9 @@ class CocoModel extends Backbone.Model
|
|||
return
|
||||
|
||||
model = new Model()
|
||||
if shouldLoadProjection? model
|
||||
sep = if link.search(/\?/) is -1 then "?" else "&"
|
||||
link += sep + "project=true"
|
||||
model.url = makeUrlFunc(link)
|
||||
return model
|
||||
|
||||
|
|
|
@ -106,13 +106,13 @@ module.exports = class Level extends CocoModel
|
|||
height = c.height if c.height? and c.height > height
|
||||
return {width:width, height:height}
|
||||
|
||||
getReferencedModels: (data, schema, path='/') ->
|
||||
models = super data, schema, path
|
||||
getReferencedModels: (data, schema, path='/', shouldLoadProjection=null) ->
|
||||
models = super data, schema, path, shouldLoadProjection
|
||||
if path.match(/\/systems\/\d+\/config\//) and data?.indieSprites?.length
|
||||
# Ugh, we need to make sure we grab the IndieSprite ThangTypes
|
||||
for indieSprite in data.indieSprites
|
||||
link = "/db/thang_type/#{indieSprite.thangType}/version"
|
||||
model = CocoModel.getOrMakeModelFromLink link
|
||||
model = CocoModel.getOrMakeModelFromLink link, shouldLoadProjection
|
||||
models.push model if model
|
||||
else if path is '/'
|
||||
# We also we need to make sure we grab the Wizard ThangType and the Marks. Hackitrooooid!
|
||||
|
@ -124,6 +124,6 @@ module.exports = class Level extends CocoModel
|
|||
["Repair", "52bcc4591f766a891c000003"]
|
||||
]
|
||||
link = "/db/thang_type/#{original}/version"
|
||||
model = CocoModel.getOrMakeModelFromLink link
|
||||
model = CocoModel.getOrMakeModelFromLink link, shouldLoadProjection
|
||||
models.push model if model
|
||||
models
|
||||
|
|
|
@ -13,7 +13,7 @@ module.exports = class LevelComponent extends CocoModel
|
|||
attrs.js = @compile attrs.code
|
||||
super attrs, options
|
||||
|
||||
onLoaded: =>
|
||||
onLoaded: ->
|
||||
super()
|
||||
@set 'js', @compile(@get 'code') unless @get 'js'
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ module.exports = class LevelSystem extends CocoModel
|
|||
attrs.js = @compile attrs.code
|
||||
super attrs, options
|
||||
|
||||
onLoaded: =>
|
||||
onLoaded: ->
|
||||
super()
|
||||
@set 'js', @compile(@get 'code') unless @get 'js'
|
||||
|
||||
|
|
|
@ -8,35 +8,43 @@ class SuperModel
|
|||
@mustPopulate = model
|
||||
model.saveBackups = @shouldSaveBackups(model)
|
||||
model.fetch() unless model.loaded or model.loading
|
||||
model.on('sync', @modelLoaded) unless model.loaded
|
||||
model.once('error', @modelErrored) unless model.loaded
|
||||
model.once('sync', @modelLoaded, @) unless model.loaded
|
||||
model.once('error', @modelErrored, @) unless model.loaded
|
||||
url = model.url()
|
||||
@models[url] = model unless @models[url]?
|
||||
@modelLoaded(model) if model.loaded
|
||||
|
||||
# replace or overwrite
|
||||
shouldPopulate: (url) -> return true
|
||||
shouldSaveBackups: (model) -> return false
|
||||
shouldLoadReference: (model) -> true
|
||||
shouldLoadProjection: (model) -> false
|
||||
shouldPopulate: (url) -> true
|
||||
shouldSaveBackups: (model) -> false
|
||||
|
||||
modelErrored: (model) =>
|
||||
modelErrored: (model) ->
|
||||
@trigger 'error'
|
||||
@removeEventsFromModel(model)
|
||||
|
||||
modelLoaded: (model) =>
|
||||
modelLoaded: (model) ->
|
||||
schema = model.schema()
|
||||
return schema.on('sync', => @modelLoaded(model)) unless schema.loaded
|
||||
refs = model.getReferencedModels(model.attributes, schema.attributes)
|
||||
return schema.once('sync', => @modelLoaded(model)) unless schema.loaded
|
||||
refs = model.getReferencedModels(model.attributes, schema.attributes, '/', @shouldLoadProjection)
|
||||
refs = [] unless @mustPopulate is model or @shouldPopulate(model)
|
||||
# console.log 'Loaded', model.get('name')
|
||||
for ref, i in refs
|
||||
for ref, i in refs when @shouldLoadReference ref
|
||||
ref.saveBackups = @shouldSaveBackups(ref)
|
||||
refURL = ref.url()
|
||||
continue if @models[refURL]
|
||||
@models[refURL] = ref
|
||||
ref.fetch()
|
||||
ref.on 'sync', @modelLoaded
|
||||
ref.once 'sync', @modelLoaded, @
|
||||
|
||||
@trigger 'loaded-one', model: model
|
||||
@trigger 'loaded-all' if @finished()
|
||||
@removeEventsFromModel(model)
|
||||
|
||||
removeEventsFromModel: (model) ->
|
||||
model.off 'sync', @modelLoaded, @
|
||||
model.off 'error', @modelErrored, @
|
||||
|
||||
getModel: (ModelClass_or_url, id) ->
|
||||
return @getModelByURL(ModelClass_or_url) if _.isString(ModelClass_or_url)
|
||||
|
|
|
@ -28,7 +28,6 @@ module.exports = class User extends CocoModel
|
|||
profileUrl = "#{GRAVATAR_URL}#{emailHash}.json?callback=#{functionName}"
|
||||
script = $("<script src='#{profileUrl}' type='text/javascript'></script>")
|
||||
$('head').append(script)
|
||||
$('body').on('load',(e)->console.log('we did it!', e))
|
||||
window[functionName] = (profile) =>
|
||||
@gravatarProfile = profile
|
||||
@trigger('change', @)
|
||||
|
|
|
@ -1,6 +1,10 @@
|
|||
@import "bootstrap/variables"
|
||||
@import "bootstrap/mixins"
|
||||
|
||||
// https://github.com/twbs/bootstrap/issues/9237 -- need a version that's not !important
|
||||
.secret
|
||||
display: none
|
||||
|
||||
h1 h2 h3 h4
|
||||
letter-spacing: 2px
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
position: relative
|
||||
top: 3px
|
||||
|
||||
#marker-button
|
||||
#marker-button, #end-button
|
||||
float: right
|
||||
margin-right: 10px
|
||||
position: relative
|
||||
|
|
|
@ -1,2 +1,4 @@
|
|||
#ladder-view
|
||||
color: black
|
||||
color: black
|
||||
.score-cell
|
||||
width: 50px
|
|
@ -56,7 +56,7 @@
|
|||
|
||||
input, textarea
|
||||
position: absolute
|
||||
bottom: -10px
|
||||
bottom: 0px
|
||||
left: 20px
|
||||
right: 0px
|
||||
width: 280px
|
||||
|
|
44
app/styles/play/level/gold.sass
Normal file
44
app/styles/play/level/gold.sass
Normal file
|
@ -0,0 +1,44 @@
|
|||
@import "app/styles/mixins"
|
||||
|
||||
#gold-view
|
||||
position: absolute
|
||||
right: 46%
|
||||
top: 42px
|
||||
user-select: none
|
||||
-webkit-user-select: none
|
||||
|
||||
h3
|
||||
font-size: 16px
|
||||
margin: 0
|
||||
line-height: 20px
|
||||
color: hsla(205,0%,31%,1)
|
||||
text-shadow: 0px 1px 1px white, 0px -1px 1px white, 1px 0px 1px white, -1px 0px 1px white
|
||||
|
||||
&.team-humans
|
||||
color: hsla(4,80%,51%,1)
|
||||
|
||||
&.team-ogres
|
||||
color: hsla(205,100%,31%,1)
|
||||
|
||||
&.team-allies, &.team-minions
|
||||
color: hsla(116,80%,31%,1)
|
||||
|
||||
img
|
||||
width: 16px
|
||||
height: 16px
|
||||
border-radius: 2px
|
||||
padding: 2px
|
||||
@include gradient-radial-custom-stops(hsla(205,0%,74%,1), 20%, hsla(205,0%,31%,1), 70%)
|
||||
|
||||
&.team-humans img
|
||||
@include gradient-radial-custom-stops(hsla(4,80%,74%,1), 20%, hsla(4,80%,51%,1), 70%)
|
||||
|
||||
&.team-ogres img
|
||||
@include gradient-radial-custom-stops(hsla(205,100%,74%,1), 20%, hsla(205,100%,31%,1), 70%)
|
||||
|
||||
&.team-allies img, &.team-minions img
|
||||
@include gradient-radial-custom-stops(hsla(116,80%,74%,1), 20%, hsla(116,80%,31%,1), 70%)
|
||||
|
||||
.gold-amount
|
||||
display: inline-block
|
||||
width: 20px
|
|
@ -89,10 +89,13 @@
|
|||
margin: 8px 8px 0 0
|
||||
overflow-y: scroll
|
||||
float: left
|
||||
@include user-select(text)
|
||||
|
||||
.prop
|
||||
img
|
||||
margin-right: 5px
|
||||
width: 16px
|
||||
height: 16px
|
||||
|
||||
.text-prop
|
||||
width: 50%
|
||||
|
|
|
@ -6,12 +6,10 @@
|
|||
left: 10px
|
||||
right: 10px
|
||||
height: 140px
|
||||
padding-top: 50px
|
||||
// Height and padding-top relate to .tab-content height
|
||||
padding-left: 35px
|
||||
padding-right: 60px
|
||||
// Docs' popovers flicker when too far right, so we have big padding-right
|
||||
// twilight: color: #E2E2E2
|
||||
// Height relates to .tab-content height
|
||||
padding-top: 35px
|
||||
padding-left: 12px
|
||||
padding-right: 4px
|
||||
color: #333
|
||||
// Get crazy with the backgrounds so that we can lower the opacity on the editor background above it, making a gradient of the disabled background color on the top around where it's usually covered
|
||||
background-color: transparent
|
||||
|
@ -36,3 +34,23 @@
|
|||
line-height: 16px
|
||||
margin: 0 4px
|
||||
font-weight: normal
|
||||
|
||||
.nav > li > a
|
||||
padding: 2px 20px 0px 20px
|
||||
margin-bottom: 3px
|
||||
|
||||
ul.nav.nav-pills
|
||||
li.active a
|
||||
background-color: transparent
|
||||
&.multiple-tabs li.active a
|
||||
background-color: lighten(rgb(230, 212, 146), 10%)
|
||||
&.multiple-tabs li:not(.active) a
|
||||
cursor: pointer
|
||||
|
||||
//.nav-pills > li.active > a, .nav-pills > li.active > a:hover, .nav-pills > li.active > a:focus
|
||||
// background-color: lighten(rgb(230, 212, 146), 10%)
|
||||
|
||||
.property-entry-column
|
||||
display: inline-block
|
||||
margin-right: 3px
|
||||
vertical-align: top
|
||||
|
|
|
@ -1,28 +1,32 @@
|
|||
@import "../../../bootstrap/mixins"
|
||||
@import "app/styles/bootstrap/mixins"
|
||||
@import "app/styles/mixins"
|
||||
|
||||
.spell-palette-entry-view
|
||||
display: inline-block
|
||||
margin: 0px 4px
|
||||
display: block
|
||||
padding: 0px 4px
|
||||
font-family: Menlo, Monaco, Consolas, "Courier New", monospace
|
||||
font-size: 12px
|
||||
border: 1px solid transparent
|
||||
cursor: pointer
|
||||
@include user-select(all)
|
||||
|
||||
&:hover
|
||||
border: 1px solid #BFF
|
||||
|
||||
&.pinned
|
||||
background-color: darken(#BFF, 20%)
|
||||
|
||||
// Pulling these colors from the most relevant textmate-theme classes
|
||||
&.function
|
||||
// twilight: color: #7587A6
|
||||
color: #0000A2
|
||||
&.object
|
||||
// twilight: color: #AC885B
|
||||
color: rgb(6, 150, 14)
|
||||
&.string
|
||||
// twilight: color: #CDA869
|
||||
color: rgb(3, 106, 7)
|
||||
&.number
|
||||
// twilight: color: #F9EE98
|
||||
color: rgb(0, 0, 205)
|
||||
&.boolean
|
||||
// twilight: color: #C9CEA8
|
||||
color: rgb(88, 92, 246)
|
||||
&.undefined
|
||||
// twilight: color: #CF6A4C
|
||||
color: rgb(197, 6, 11)
|
||||
|
||||
|
|
|
@ -1,11 +1,23 @@
|
|||
@import "../../../bootstrap/mixins"
|
||||
@import "app/styles/bootstrap/mixins"
|
||||
@import "app/styles/mixins"
|
||||
|
||||
#tome-view
|
||||
height: 100%
|
||||
|
||||
> .popover
|
||||
// Only those popovers which are our direct children (spell documentation)
|
||||
left: auto !important
|
||||
top: auto !important
|
||||
right: 100%
|
||||
bottom: 151px
|
||||
@include user-select(text)
|
||||
|
||||
&.pinned
|
||||
@include box-shadow(0 0 500px white)
|
||||
|
||||
.popover
|
||||
padding: 10px
|
||||
max-width: 400px
|
||||
min-width: 400px
|
||||
background: transparent url(/images/level/popover_background.png)
|
||||
background-size: 100% 100%
|
||||
border: 0
|
||||
|
@ -33,3 +45,6 @@
|
|||
&.right .arrow
|
||||
left: -3%
|
||||
|
||||
pre
|
||||
display: inline-block
|
||||
padding: 5px
|
||||
|
|
|
@ -8,7 +8,7 @@ block content
|
|||
p(data-i18n="account_settings.not_logged_in") Log in or create an account to change your settings.
|
||||
|
||||
else
|
||||
button.btn#save-button.disabled.hide(data-i18n="account_settings.saveBackups") Changes Save Automatically
|
||||
button.btn#save-button.disabled.secret(data-i18n="account_settings.saveBackups") Changes Save Automatically
|
||||
|
||||
ul.nav.nav-pills#settings-tabs
|
||||
li
|
||||
|
|
|
@ -9,9 +9,9 @@ block content
|
|||
|
||||
button.btn.btn-warning#unsubscribe-button(data-i18n="account.unsubscribe_button") Do it
|
||||
|
||||
.progress.progress-striped.active.hide
|
||||
.progress.progress-striped.active.secret
|
||||
.progress-bar
|
||||
|
||||
p.hide#fail-alert(data-i18n="account.unsubscribe_failed").alert.alert-danger Failed
|
||||
p.secret#fail-alert(data-i18n="account.unsubscribe_failed").alert.alert-danger Failed
|
||||
|
||||
p.hide#success-alert(data-i18n="account.unsubscribe_success").alert.alert-success Success
|
||||
p.secret#success-alert(data-i18n="account.unsubscribe_success").alert.alert-success Success
|
||||
|
|
|
@ -57,7 +57,7 @@ block content
|
|||
| create an account
|
||||
span
|
||||
span(data-i18n="contribute.alert_account_message_suf")
|
||||
| first.
|
||||
| first.
|
||||
|
||||
label.checkbox(for="translator").well
|
||||
input(type='checkbox', name="translator", id="translator")
|
||||
|
|
|
@ -3,13 +3,13 @@ div.well
|
|||
span#thang-props
|
||||
a#thang-name-link
|
||||
span= thang.id
|
||||
input.hide(value=thang.id)
|
||||
input.secret(value=thang.id)
|
||||
| (
|
||||
span(data-i18n="editor.level_components_type") Type
|
||||
| :
|
||||
a#thang-type-link
|
||||
span= thang.thangType
|
||||
input.hide(value=thang.thangType)
|
||||
input.secret(value=thang.thangType)
|
||||
| )
|
||||
|
||||
#thang-components-edit-view
|
|
@ -31,4 +31,4 @@
|
|||
- path = '/file/db/thang.type/'+thangType.original+'/portrait.png'
|
||||
img(title="Add " + thangType.name, src=path, alt="")
|
||||
div.clearfix
|
||||
#editor-level-thang-edit.hide
|
||||
#editor-level-thang-edit.secret
|
|
@ -1,6 +1,6 @@
|
|||
div#color-groups-treema
|
||||
|
||||
div#color-group-settings.hide
|
||||
div#color-group-settings.secret
|
||||
div#shape-buttons
|
||||
|
||||
canvas#tinting-display(width=400, height=400)
|
||||
|
|
|
@ -39,6 +39,9 @@ block content
|
|||
button.btn.btn-small.btn-primary#marker-button
|
||||
i.icon-map-marker
|
||||
|
||||
button.btn.btn-small.btn-primary#end-button
|
||||
i.icon-stop
|
||||
|
||||
div.slider-cell
|
||||
| Rotation:
|
||||
span.rotation-label
|
||||
|
|
|
@ -25,4 +25,6 @@ block content
|
|||
div.homepage_button
|
||||
a#beginner-campaign(href="/play/level/rescue-mission")
|
||||
canvas(width="125", height="150")
|
||||
button(data-i18n="home.play").btn.btn-warning.btn-lg.highlight Play
|
||||
button(data-i18n="home.play").btn.btn-warning.btn-lg.highlight Play
|
||||
if me.isAdmin()
|
||||
button.btn.btn-warning.btn-lg.highlight#simulate-button SIMULATE
|
|
@ -21,7 +21,7 @@ block content
|
|||
.modal-footer
|
||||
button.btn(data-dismiss="modal") Cancel
|
||||
button.btn.btn-primary.new-model-submit Create
|
||||
.modal-body.wait.hide
|
||||
.modal-body.wait.secret
|
||||
h3 Reticulating Splines...
|
||||
.progress.progress-striped.active
|
||||
.progress-bar
|
||||
|
|
|
@ -22,6 +22,6 @@ block modal-body-content
|
|||
textarea#contact-message.form-control(name="message", rows=8)
|
||||
|
||||
block modal-footer-content
|
||||
span.sending-indicator.pull-left.hide(data-i18n="common.sending") Sending...
|
||||
span.sending-indicator.pull-left.secret(data-i18n="common.sending") Sending...
|
||||
a(href='#', data-dismiss="modal", aria-hidden="true", data-i18n="common.cancel").btn Cancel
|
||||
button.btn.btn-primary#contact-submit-button(data-i18n="contact.send") Send Feedback
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
img(src="http://www.manbitesgod.com/images/picturecoupleb.jpg")
|
||||
img(src="http://www.manbitesgod.com/images/manrantb.jpg")
|
||||
|
||||
.modal-body.wait.hide
|
||||
.modal-body.wait.secret
|
||||
block modal-body-wait-content
|
||||
h3 Reticulating Splines...
|
||||
.progress.progress-striped.active
|
||||
|
|
|
@ -5,7 +5,9 @@ block content
|
|||
h3= level.get('name')
|
||||
div#level-description
|
||||
!{description}
|
||||
|
||||
if me.isAdmin()
|
||||
button.btn.btn-warning.btn-lg.highlight#simulate-button(style="margin-bottom:10px") Simulate all games
|
||||
|
||||
div#leaderboard-column
|
||||
ul.nav.nav-pills
|
||||
for team in teams
|
||||
|
@ -19,13 +21,18 @@ block content
|
|||
h4 Leaderboard
|
||||
table.table
|
||||
for session in team.leaderboard.topPlayers.models
|
||||
tr= session.get('creatorName')
|
||||
.challengers
|
||||
h4 Challengers
|
||||
|
||||
if team.easyChallenger
|
||||
div.challenger-select
|
||||
| Easy challenger:
|
||||
button
|
||||
a(href=link+'?team='+team.id+'&opponent='+team.easyChallenger.id)
|
||||
//TODO: finish once the scoring system is finished
|
||||
tr
|
||||
td.score-cell= session.get('totalScore')
|
||||
td= session.get('creatorName')
|
||||
td
|
||||
a(href="/play/level/#{level.get('slug') || level.id}/?team=#{team.otherTeam}&opponent=#{session.id}") COMPETE
|
||||
|
||||
//.challengers
|
||||
// h4 Challengers
|
||||
//
|
||||
// if team.easyChallenger
|
||||
// div.challenger-select
|
||||
// | Easy challenger:
|
||||
// button
|
||||
// a(href=link+'?team='+team.id+'&opponent='+team.easyChallenger.id)
|
||||
// TODO: finish once the scoring system is finished
|
|
@ -11,10 +11,12 @@
|
|||
#canvas-left-gradient.gradient
|
||||
#canvas-top-gradient.gradient
|
||||
|
||||
a.btn.btn-primary.banner.hide#level-done-button(data-i18n="play_level.done") Done
|
||||
a.btn.btn-primary.banner.secret#level-done-button(data-i18n="play_level.done") Done
|
||||
|
||||
#goals-view.hide
|
||||
#goals-view.secret
|
||||
|
||||
#gold-view.secret.expanded
|
||||
|
||||
#level-chat-view
|
||||
|
||||
#playback-view
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
table
|
||||
tbody
|
||||
|
||||
.open-chat-area.hide
|
||||
.open-chat-area.secret
|
||||
table
|
||||
tbody
|
||||
|
||||
|
|
0
app/templates/play/level/gold.jade
Normal file
0
app/templates/play/level/gold.jade
Normal file
|
@ -25,14 +25,11 @@
|
|||
|
||||
p(data-i18n="play_level.multiplayer_coming_soon") More multiplayer features to come!
|
||||
|
||||
if playableTeams.length > 1
|
||||
#multiplayer-team-selection
|
||||
for team in playableTeams
|
||||
h4
|
||||
if team == me.team
|
||||
em= "Playing as " + team
|
||||
else
|
||||
a(href=joinLink + "&skip_protect_api=true&team=" + team)= "Play as " + team
|
||||
if ladderGame
|
||||
if me.get('anonymous')
|
||||
p Sign in or create an account and get your solution on the leaderboard!
|
||||
else
|
||||
#submit-session-button.btn.btn-primary Update Ladder Score
|
||||
|
||||
.modal-footer
|
||||
a(href='#', data-dismiss="modal", aria-hidden="true", data-i18n="modal.close").btn.btn-primary Close
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
button.btn.btn-success.sign-up-button.btn-large(data-toggle="coco-modal", data-target="modal/signup", data-i18n="play_level.victory_sign_up") Sign Up to Save Progress
|
||||
span(data-i18n="play_level.victory_sign_up_poke") Want to save your code? Create a free account!
|
||||
else
|
||||
div.rating.hide
|
||||
div.rating.secret
|
||||
span(data-i18n="play_level.victory_rate_the_level") Rate the level:
|
||||
i.icon-star-empty
|
||||
i.icon-star-empty
|
||||
|
@ -30,7 +30,7 @@
|
|||
else
|
||||
a.btn.btn-primary(href="/", data-dismiss="modal", data-i18n="play_level.victory_go_home") Go Home
|
||||
if !me.get('anonymous')
|
||||
div.review.hide
|
||||
div.review.secret
|
||||
span(data-i18n="play_level.victory_review") Tell us more!
|
||||
br
|
||||
textarea
|
||||
|
|
|
@ -11,7 +11,7 @@ button.btn.btn-xs.btn-inverse#music-button(title="Toggle Music")
|
|||
| ♫
|
||||
|
||||
.scrubber
|
||||
.progress.hide
|
||||
.progress.secret
|
||||
.progress-bar
|
||||
.scrubber-handle
|
||||
|
||||
|
@ -27,11 +27,11 @@ button.btn.btn-xs.btn-inverse#music-button(title="Toggle Music")
|
|||
li(title="Ctrl/Cmd + \\: Toggle debug display").selectable#debug-toggle
|
||||
i.icon-globe
|
||||
| Debug Mode
|
||||
i.icon-ok.hide
|
||||
i.icon-ok.secret
|
||||
li(title="Ctrl/Cmd + G: Toggle grid display").selectable#grid-toggle
|
||||
i.icon-th
|
||||
span(data-i18n="play_level.grid") Grid
|
||||
i.icon-ok.hide
|
||||
i.icon-ok.secret
|
||||
li.selectable#edit-wizard-settings
|
||||
i.icon-user
|
||||
span(data-i18n="play_level.customize_wizard") Customize Wizard
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
button.close(type="button", data-dismiss="alert") ×
|
||||
span.problem-message= message
|
||||
span.problem-message!= message
|
||||
if hint
|
||||
br
|
||||
span.problem-hint= hint
|
||||
span.problem-hint!= hint
|
|
@ -1,3 +1,10 @@
|
|||
img(src="/images/level/code_palette_background.png").code-palette-background
|
||||
h4(data-i18n="play_level.tome_available_spells") Available Spells
|
||||
.properties
|
||||
ul(class="nav nav-pills" + (tabbed ? ' multiple-tabs' : ''))
|
||||
each slug, group in entryGroupSlugs
|
||||
li(class=group == "this" || slug == "available-spells" ? "active" : "")
|
||||
a(data-toggle="pill", data-target='#palette-tab-' + slug)
|
||||
h4= group
|
||||
.tab-content
|
||||
each slug, group in entryGroupSlugs
|
||||
div(id="palette-tab-" + slug, class="tab-pane" + (group == "this" || slug == "available-spells" ? " active" : ""))
|
||||
div(class="properties properties-" + slug)
|
||||
|
|
|
@ -1 +1 @@
|
|||
// Later we'll put the crazy HTML from docs.coffee in here as jade
|
||||
span= doc.title
|
|
@ -0,0 +1,44 @@
|
|||
h4
|
||||
span= doc.shortName
|
||||
| -
|
||||
code.prop-type= doc.type == 'function' && doc.owner == 'this' ? 'method' : doc.type
|
||||
if doc.type != 'function'
|
||||
| (read-only)
|
||||
|
||||
.description!= marked(doc.description || 'Still undocumented, sorry.')
|
||||
|
||||
if doc.example
|
||||
p.example
|
||||
strong Example:
|
||||
div!= marked("```\n" + doc.example + "```")
|
||||
else if doc.type == 'function'
|
||||
p.example
|
||||
strong Example:
|
||||
div
|
||||
code= doc.owner + '.' + doc.name + '(' + argumentExamples.join(', ') + ');'
|
||||
|
||||
if (doc.type != 'function' && doc.type != 'snippet') || doc.name == 'now'
|
||||
p.value
|
||||
strong Current Value:
|
||||
pre
|
||||
code.current-value(data-prop=doc.name)= value
|
||||
|
||||
if doc.args && doc.args.length
|
||||
p.args
|
||||
strong Parameters:
|
||||
for arg in doc.args
|
||||
div
|
||||
code= arg.name
|
||||
| :
|
||||
code= arg.type
|
||||
if arg.example
|
||||
| (ex:
|
||||
code= arg.example
|
||||
| )
|
||||
if arg.description
|
||||
div!= marked(arg.description)
|
||||
if arg.default
|
||||
div
|
||||
em Default value:
|
||||
code= arg.default
|
||||
|
16
app/templates/play/spectate.jade
Normal file
16
app/templates/play/spectate.jade
Normal file
|
@ -0,0 +1,16 @@
|
|||
.level-content
|
||||
#control-bar-view
|
||||
|
||||
#canvas-wrapper
|
||||
canvas(width=924, height=589)#surface
|
||||
#canvas-left-gradient.gradient
|
||||
#canvas-top-gradient.gradient
|
||||
#goals-view.hide
|
||||
#gold-view.hide.expanded
|
||||
#level-chat-view
|
||||
#playback-view
|
||||
#thang-hud
|
||||
.footer
|
||||
.content
|
||||
p(class='footer-link-text')
|
||||
a(title='Contact', tabindex=-1, data-toggle="coco-modal", data-target="modal/contact", data-i18n="nav.contact") Contact
|
|
@ -284,7 +284,7 @@ class LatestVersionReferenceNode extends TreemaNode
|
|||
@lastTerm = term
|
||||
@getSearchResultsEl().empty().append('Searching')
|
||||
@collection = new LatestVersionCollection()
|
||||
@collection.url = @url+'?term='+term
|
||||
@collection.url = "#{@url}?term=#{term}&project=true"
|
||||
@collection.fetch()
|
||||
@collection.on 'sync', @searchCallback
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ module.exports = class SettingsView extends View
|
|||
@updateWizardColor()
|
||||
wizardSettingsTabView = new WizardSettingsTabView()
|
||||
wizardSettingsTabView.on 'change', @save, @
|
||||
@insertSubView wizardSettingsTabView
|
||||
@insertSubView wizardSettingsTabView
|
||||
|
||||
chooseTab: (category) ->
|
||||
id = "##{category}-pane"
|
||||
|
@ -102,7 +102,7 @@ module.exports = class SettingsView extends View
|
|||
res = me.save()
|
||||
return unless res
|
||||
save = $('#save-button', @$el).text($.i18n.t('common.saving', defaultValue: 'Saving...'))
|
||||
.addClass('btn-info').removeClass('hide').removeClass('btn-danger')
|
||||
.addClass('btn-info').show().removeClass('btn-danger')
|
||||
|
||||
res.error ->
|
||||
errors = JSON.parse(res.responseText)
|
||||
|
|
|
@ -5,7 +5,7 @@ template = require 'templates/account/unsubscribe'
|
|||
module.exports = class UnsubscribeView extends RootView
|
||||
id: "unsubscribe-view"
|
||||
template: template
|
||||
|
||||
|
||||
events:
|
||||
'click #unsubscribe-button': 'onUnsubscribeButtonClicked'
|
||||
|
||||
|
@ -15,21 +15,21 @@ module.exports = class UnsubscribeView extends RootView
|
|||
context
|
||||
|
||||
onUnsubscribeButtonClicked: ->
|
||||
@$el.find('#unsubscribe-button').addClass 'hide'
|
||||
@$el.find('.progress').removeClass 'hide'
|
||||
@$el.find('.alert').addClass 'hide'
|
||||
|
||||
@$el.find('#unsubscribe-button').hide()
|
||||
@$el.find('.progress').show()
|
||||
@$el.find('.alert').hide()
|
||||
|
||||
email = @getQueryVariable 'email'
|
||||
url = "/auth/unsubscribe?email=#{encodeURIComponent(email)}"
|
||||
|
||||
|
||||
success = =>
|
||||
@$el.find('.progress').addClass 'hide'
|
||||
@$el.find('#success-alert').removeClass 'hide'
|
||||
@$el.find('.progress').hide()
|
||||
@$el.find('#success-alert').show()
|
||||
me.fetch()
|
||||
|
||||
|
||||
error = =>
|
||||
@$el.find('.progress').addClass 'hide'
|
||||
@$el.find('#fail-alert').removeClass 'hide'
|
||||
@$el.find('#unsubscribe-button').removeClass 'hide'
|
||||
|
||||
@$el.find('.progress').hide()
|
||||
@$el.find('#fail-alert').show()
|
||||
@$el.find('#unsubscribe-button').show()
|
||||
|
||||
$.ajax { url: url, success: success, error: error }
|
||||
|
|
|
@ -7,7 +7,7 @@ SpriteBuilder = require 'lib/sprites/SpriteBuilder'
|
|||
module.exports = class WizardSettingsTabView extends RootView
|
||||
id: 'wizard-settings-tab-view'
|
||||
template: template
|
||||
|
||||
|
||||
events:
|
||||
'change .color-group-checkbox': (e) ->
|
||||
colorGroup = $(e.target).closest('.color-group')
|
||||
|
@ -17,13 +17,13 @@ module.exports = class WizardSettingsTabView extends RootView
|
|||
constructor: ->
|
||||
super(arguments...)
|
||||
@loadWizard()
|
||||
|
||||
|
||||
loadWizard: ->
|
||||
@wizardThangType = new ThangType()
|
||||
@wizardThangType.url = -> '/db/thang_type/wizard'
|
||||
@wizardThangType.fetch()
|
||||
@wizardThangType.once 'sync', @initCanvas, @
|
||||
|
||||
|
||||
initCanvas: ->
|
||||
@render()
|
||||
@spriteBuilder = new SpriteBuilder(@wizardThangType)
|
||||
|
@ -32,9 +32,9 @@ module.exports = class WizardSettingsTabView extends RootView
|
|||
getRenderData: ->
|
||||
c = super()
|
||||
wizardSettings = me.get('wizard')?.colorConfig or {}
|
||||
|
||||
|
||||
colorGroups = @wizardThangType.get('colorGroups') or {}
|
||||
f = (name) -> {
|
||||
f = (name) -> {
|
||||
dasherized: _.string.dasherize(name)
|
||||
humanized: _.string.humanize name
|
||||
name: name
|
||||
|
@ -42,26 +42,26 @@ module.exports = class WizardSettingsTabView extends RootView
|
|||
}
|
||||
c.colorGroups = (f(colorName) for colorName in _.keys colorGroups)
|
||||
c
|
||||
|
||||
|
||||
afterRender: ->
|
||||
wizardSettings = me.get('wizard') or {}
|
||||
wizardSettings.colorConfig ?= {}
|
||||
|
||||
|
||||
@$el.find('.selector').each (i, slider) =>
|
||||
[groupName, prop] = $(slider).attr('name').split('.')
|
||||
value = 100 * (wizardSettings.colorConfig[groupName]?[prop] ? 0.5)
|
||||
@initSlider $(slider), value, @onSliderChanged
|
||||
|
||||
|
||||
@$el.find('.color-group').each (i, colorGroup) =>
|
||||
@updateSliderVisibility($(colorGroup))
|
||||
|
||||
|
||||
updateSliderVisibility: (colorGroup) ->
|
||||
enabled = colorGroup.find('.color-group-checkbox').prop('checked')
|
||||
colorGroup.find('.sliders').toggleClass 'hide', not enabled
|
||||
|
||||
colorGroup.find('.sliders').toggle Boolean(enabled)
|
||||
|
||||
updateColorSettings: (colorGroup) ->
|
||||
wizardSettings = me.get('wizard') or {}
|
||||
wizardSettings.colorConfig ?= {}
|
||||
wizardSettings.colorConfig ?= {}
|
||||
colorName = colorGroup.data('name')
|
||||
wizardSettings.colorConfig[colorName] ?= {}
|
||||
if colorGroup.find('.color-group-checkbox').prop('checked')
|
||||
|
@ -75,7 +75,7 @@ module.exports = class WizardSettingsTabView extends RootView
|
|||
me.set('wizard', wizardSettings)
|
||||
@updateMovieClip()
|
||||
@trigger 'change'
|
||||
|
||||
|
||||
onSliderChanged: (e, result) =>
|
||||
@updateColorSettings $(result.handle).closest('.color-group')
|
||||
|
||||
|
@ -84,12 +84,12 @@ module.exports = class WizardSettingsTabView extends RootView
|
|||
@updateMovieClip()
|
||||
createjs.Ticker.setFPS 20
|
||||
createjs.Ticker.addEventListener("tick", @stage)
|
||||
|
||||
|
||||
updateMovieClip: ->
|
||||
return unless @wizardThangType.loaded
|
||||
wizardSettings = me.get('wizard') or {}
|
||||
wizardSettings.colorConfig ?= {}
|
||||
|
||||
|
||||
@stage.removeChild(@movieClip) if @movieClip
|
||||
options = {colorConfig: wizardSettings.colorConfig}
|
||||
@spriteBuilder.setOptions options
|
||||
|
@ -100,10 +100,10 @@ module.exports = class WizardSettingsTabView extends RootView
|
|||
@movieClip.scaleY = @movieClip.scaleX = 1.7 * (castAction.scale or 1)
|
||||
reg = castAction.positions?.registration
|
||||
if reg
|
||||
@movieClip.regX = reg.x
|
||||
@movieClip.regX = reg.x
|
||||
@movieClip.regY = reg.y
|
||||
@stage.addChild @movieClip
|
||||
@stage.update()
|
||||
|
||||
destroy: ->
|
||||
@stage?.removeAllEventListeners()
|
||||
@stage?.removeAllEventListeners()
|
||||
|
|
|
@ -52,7 +52,7 @@ module.exports = class ArticleEditView extends View
|
|||
b.find('#insert').html(m)
|
||||
b.find('#title').text(@treema.data.name)
|
||||
|
||||
getRenderData: (context={}) =>
|
||||
getRenderData: (context={}) ->
|
||||
context = super(context)
|
||||
context.article = @article
|
||||
context
|
||||
|
|
|
@ -20,7 +20,7 @@ module.exports = class ComponentConfigView extends CocoView
|
|||
@editing = options.editing
|
||||
@callback = options.callback
|
||||
|
||||
getRenderData: (context={}) =>
|
||||
getRenderData: (context={}) ->
|
||||
context = super(context)
|
||||
context.component = @component
|
||||
context.configProperties = []
|
||||
|
|
|
@ -16,7 +16,7 @@ module.exports = class LevelComponentEditView extends View
|
|||
@levelComponent = @supermodel.getModelByOriginalAndMajorVersion LevelComponent, options.original, options.majorVersion or 0
|
||||
console.log "Couldn't get levelComponent for", options, "from", @supermodel.models unless @levelComponent
|
||||
|
||||
getRenderData: (context={}) =>
|
||||
getRenderData: (context={}) ->
|
||||
context = super(context)
|
||||
context.editTitle = "#{@levelComponent.get('system')}.#{@levelComponent.get('name')}"
|
||||
context
|
||||
|
@ -89,5 +89,5 @@ module.exports = class LevelComponentEditView extends View
|
|||
null
|
||||
|
||||
destroy: ->
|
||||
super()
|
||||
@editor?.destroy()
|
||||
super()
|
||||
|
|
|
@ -60,7 +60,7 @@ module.exports = class EditorLevelView extends View
|
|||
initWorld: ->
|
||||
@world = new World @level.name
|
||||
|
||||
getRenderData: (context={}) =>
|
||||
getRenderData: (context={}) ->
|
||||
context = super(context)
|
||||
context.level = @level
|
||||
context
|
||||
|
|
|
@ -17,7 +17,7 @@ module.exports = class LevelForkView extends View
|
|||
super options
|
||||
@level = options.level
|
||||
|
||||
getRenderData: (context={}) =>
|
||||
getRenderData: (context={}) ->
|
||||
context = super(context)
|
||||
context.level = @level
|
||||
context
|
||||
|
|
|
@ -16,8 +16,8 @@ module.exports = class LevelSaveView extends SaveVersionModal
|
|||
constructor: (options) ->
|
||||
super options
|
||||
@level = options.level
|
||||
|
||||
getRenderData: (context={}) =>
|
||||
|
||||
getRenderData: (context={}) ->
|
||||
context = super(context)
|
||||
context.level = @level
|
||||
context.levelNeedsSave = @level.hasLocalChanges()
|
||||
|
@ -63,8 +63,8 @@ module.exports = class LevelSaveView extends SaveVersionModal
|
|||
console.log "Got errors:", JSON.parse(res.responseText)
|
||||
forms.applyErrorsToForm($(form), JSON.parse(res.responseText))
|
||||
res.success =>
|
||||
@hide()
|
||||
modelsToSave = _.without modelsToSave, newModel
|
||||
unless modelsToSave.length
|
||||
url = "/editor/level/#{@level.get('slug') or @level.id}"
|
||||
document.location.href = url
|
||||
@hide() # This will destroy everything, so do it last
|
||||
|
|
|
@ -81,9 +81,7 @@ module.exports = class ScriptsTabView extends View
|
|||
@selectedScriptPath = newPath
|
||||
|
||||
getThangIDs: ->
|
||||
ids = (t.id for t in @level.get('thangs') when t.id isnt 'Interface')
|
||||
ids = ['My Wizard', 'Captain Anya'].concat(ids)
|
||||
ids
|
||||
(t.id for t in @level.get('thangs') when t.id isnt 'Interface')
|
||||
|
||||
onScriptChanged: =>
|
||||
@scriptsTreema.set(@selectedScriptPath, @scriptTreema.data)
|
||||
|
|
|
@ -2,12 +2,13 @@ View = require 'views/kinds/CocoView'
|
|||
template = require 'templates/editor/level/settings_tab'
|
||||
Level = require 'models/Level'
|
||||
Surface = require 'lib/surface/Surface'
|
||||
nodes = require './treema_nodes'
|
||||
|
||||
module.exports = class SettingsTabView extends View
|
||||
id: 'editor-level-settings-tab-view'
|
||||
className: 'tab-pane'
|
||||
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:
|
||||
'level-loaded': 'onLevelLoaded'
|
||||
|
@ -22,16 +23,24 @@ module.exports = class SettingsTabView extends View
|
|||
schema = _.cloneDeep Level.schema.attributes
|
||||
schema.properties = _.pick schema.properties, (value, key) => key in @editableSettings
|
||||
schema.required = _.intersection schema.required, @editableSettings
|
||||
thangIDs = @getThangIDs()
|
||||
treemaOptions =
|
||||
filePath: "db/level/#{@level.get('original')}"
|
||||
supermodel: @supermodel
|
||||
schema: schema
|
||||
data: data
|
||||
callbacks: {change: @onSettingsChanged}
|
||||
thangIDs: thangIDs
|
||||
nodeClasses:
|
||||
thang: nodes.ThangNode
|
||||
|
||||
@settingsTreema = @$el.find('#settings-treema').treema treemaOptions
|
||||
@settingsTreema.build()
|
||||
@settingsTreema.open()
|
||||
|
||||
getThangIDs: ->
|
||||
(t.id for t in @level.get('thangs') when t.id isnt 'Interface')
|
||||
|
||||
onSettingsChanged: (e) =>
|
||||
$('.level-title').text @settingsTreema.data.name
|
||||
for key in @editableSettings
|
||||
|
|
|
@ -16,7 +16,7 @@ module.exports = class LevelSystemEditView extends View
|
|||
@levelSystem = @supermodel.getModelByOriginalAndMajorVersion LevelSystem, options.original, options.majorVersion or 0
|
||||
console.log "Couldn't get levelSystem for", options, "from", @supermodel.models unless @levelSystem
|
||||
|
||||
getRenderData: (context={}) =>
|
||||
getRenderData: (context={}) ->
|
||||
context = super(context)
|
||||
context.editTitle = "#{@levelSystem.get('name')}"
|
||||
context
|
||||
|
@ -89,5 +89,5 @@ module.exports = class LevelSystemEditView extends View
|
|||
null
|
||||
|
||||
destroy: ->
|
||||
super()
|
||||
@editor?.destroy()
|
||||
super()
|
||||
|
|
|
@ -9,10 +9,10 @@ module.exports = class LevelThangEditView extends View
|
|||
Everything below is part of the ThangComponentEditView, which is shared with the
|
||||
ThangType editor view.
|
||||
###
|
||||
|
||||
|
||||
id: "editor-level-thang-edit"
|
||||
template: template
|
||||
|
||||
|
||||
events:
|
||||
'click #all-thangs-link': 'navigateToAllThangs'
|
||||
'click #thang-name-link span': 'toggleNameEdit'
|
||||
|
@ -28,11 +28,11 @@ module.exports = class LevelThangEditView extends View
|
|||
@level = options.level
|
||||
@oldID = @thangData.id
|
||||
|
||||
getRenderData: (context={}) =>
|
||||
getRenderData: (context={}) ->
|
||||
context = super(context)
|
||||
context.thang = @thangData
|
||||
context
|
||||
|
||||
|
||||
afterRender: ->
|
||||
options =
|
||||
components: @thangData.components
|
||||
|
@ -40,7 +40,7 @@ module.exports = class LevelThangEditView extends View
|
|||
level: @level
|
||||
world: @world
|
||||
callback: @onComponentsChanged
|
||||
|
||||
|
||||
@thangComponentEditView = new ThangComponentEditView options
|
||||
@insertSubView @thangComponentEditView
|
||||
thangTypeNames = (m.get('name') for m in @supermodel.getModels ThangType)
|
||||
|
@ -57,35 +57,35 @@ module.exports = class LevelThangEditView extends View
|
|||
thangData: @thangData
|
||||
id: @oldID
|
||||
Backbone.Mediator.publish 'level-thang-edited', event
|
||||
|
||||
|
||||
navigateToAllThangs: ->
|
||||
Backbone.Mediator.publish 'level-thang-done-editing'
|
||||
|
||||
|
||||
toggleNameEdit: ->
|
||||
link = @$el.find '#thang-name-link'
|
||||
wasEditing = link.find('input:visible').length
|
||||
span = link.find('span')
|
||||
input = link.find('input')
|
||||
if wasEditing then span.text(input.val()) else input.val(span.text())
|
||||
link.find('span, input').toggleClass('hide')
|
||||
link.find('span, input').toggle()
|
||||
input.select() unless wasEditing
|
||||
@thangData.id = span.text()
|
||||
@saveThang()
|
||||
|
||||
|
||||
toggleTypeEdit: ->
|
||||
link = @$el.find '#thang-type-link'
|
||||
wasEditing = link.find('input:visible').length
|
||||
span = link.find('span')
|
||||
input = link.find('input')
|
||||
span.text(input.val()) if wasEditing
|
||||
link.find('span, input').toggleClass('hide')
|
||||
span.text(input.val()) if wasEditing
|
||||
link.find('span, input').toggle()
|
||||
input.select() unless wasEditing
|
||||
thangTypeName = input.val()
|
||||
thangType = _.find @supermodel.getModels(ThangType), (m) -> m.get('name') is thangTypeName
|
||||
if thangType and wasEditing
|
||||
@thangData.thangType = thangType.get('original')
|
||||
@saveThang()
|
||||
|
||||
|
||||
onComponentsChanged: (components) =>
|
||||
@thangData.components = components
|
||||
@saveThang()
|
||||
|
|
|
@ -72,7 +72,7 @@ module.exports = class ThangsTabView extends View
|
|||
@render() # do it again but without the loading screen
|
||||
@onLevelLoaded level: @level if @level
|
||||
|
||||
getRenderData: (context={}) =>
|
||||
getRenderData: (context={}) ->
|
||||
context = super(context)
|
||||
thangTypes = (thangType.attributes for thangType in @supermodel.getModels(ThangType))
|
||||
thangTypes = _.uniq thangTypes, false, 'original'
|
||||
|
@ -151,9 +151,9 @@ module.exports = class ThangsTabView extends View
|
|||
@surface.camera.zoomTo({x:262, y:-164}, 1.66, 0)
|
||||
|
||||
destroy: ->
|
||||
super()
|
||||
@selectAddThangType null
|
||||
@surface.destroy()
|
||||
super()
|
||||
|
||||
onViewSwitched: (e) ->
|
||||
@selectAddThang()
|
||||
|
@ -374,7 +374,7 @@ module.exports = class ThangsTabView extends View
|
|||
thangData = @thangsTreema.get "id=#{e.thangID}"
|
||||
@editThangView = new LevelThangEditView thangData: thangData, supermodel: @supermodel, level: @level, world: @world
|
||||
@insertSubView @editThangView
|
||||
@$el.find('.thangs-column').addClass('hide')
|
||||
@$el.find('.thangs-column').hide()
|
||||
Backbone.Mediator.publish 'level:view-switched', e
|
||||
|
||||
onLevelThangEdited: (e) ->
|
||||
|
@ -383,7 +383,7 @@ module.exports = class ThangsTabView extends View
|
|||
|
||||
onLevelThangDoneEditing: ->
|
||||
@removeSubView @editThangView
|
||||
@$el.find('.thangs-column').removeClass('hide')
|
||||
@$el.find('.thangs-column').show()
|
||||
|
||||
|
||||
class ThangsNode extends TreemaNode.nodeMap.array
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
CocoView = require 'views/kinds/CocoView'
|
||||
template = require 'templates/editor/thang/colors_tab'
|
||||
SpriteBuilder = require 'lib/sprites/SpriteBuilder'
|
||||
{hexToHSL} = require 'lib/utils'
|
||||
{hexToHSL} = require 'lib/utils'
|
||||
|
||||
module.exports = class ColorsTabView extends CocoView
|
||||
id: 'editor-thang-colors-tab-view'
|
||||
template: template
|
||||
className: 'tab-pane'
|
||||
|
||||
|
||||
offset: 0
|
||||
|
||||
|
||||
constructor: (@thangType, options) ->
|
||||
@thangType.once 'sync', @tryToBuild, @
|
||||
@thangType.schema().once 'sync', @tryToBuild, @
|
||||
@colorConfig = { hue: 0, saturation: 0.5, lightness: 0.5 }
|
||||
@spriteBuilder = new SpriteBuilder(@thangType)
|
||||
f = =>
|
||||
f = =>
|
||||
@offset++
|
||||
@updateMovieClip()
|
||||
@interval = setInterval f, 1000
|
||||
|
@ -27,21 +27,21 @@ module.exports = class ColorsTabView extends CocoView
|
|||
@initStage()
|
||||
@initSliders()
|
||||
@tryToBuild()
|
||||
|
||||
|
||||
# sliders
|
||||
|
||||
initSliders: ->
|
||||
@hueSlider = @initSlider $("#hue-slider", @$el), 0, @makeSliderCallback 'hue'
|
||||
@saturationSlider = @initSlider $("#saturation-slider", @$el), 50, @makeSliderCallback 'saturation'
|
||||
@lightnessSlider = @initSlider $("#lightness-slider", @$el), 50, @makeSliderCallback 'lightness'
|
||||
|
||||
|
||||
makeSliderCallback: (property) ->
|
||||
(e, result) =>
|
||||
@colorConfig[property] = result.value / 100
|
||||
@updateMovieClip()
|
||||
|
||||
|
||||
# movie clip
|
||||
|
||||
|
||||
initStage: ->
|
||||
canvas = @$el.find('#tinting-display')
|
||||
@stage = new createjs.Stage(canvas[0])
|
||||
|
@ -95,7 +95,7 @@ module.exports = class ColorsTabView extends CocoView
|
|||
aHSL = hexToHSL(a)
|
||||
bHSL = hexToHSL(b)
|
||||
if aHSL[0] > bHSL[0] then -1 else 1
|
||||
|
||||
|
||||
for color in colors
|
||||
button = $('<button></button>').addClass('btn')
|
||||
button.css('background', color)
|
||||
|
@ -130,23 +130,23 @@ module.exports = class ColorsTabView extends CocoView
|
|||
@thangType.set('colorGroups', @colorGroups.data)
|
||||
|
||||
onColorGroupSelected: (e, selected) =>
|
||||
@$el.find('#color-group-settings').toggleClass('hide', not selected.length)
|
||||
@$el.find('#color-group-settings').toggle selected.length > 0
|
||||
treema = @colorGroups.getLastSelectedTreema()
|
||||
return unless treema
|
||||
@currentColorGroupTreema = treema
|
||||
|
||||
|
||||
shapes = {}
|
||||
shapes[shape] = true for shape in treema.data
|
||||
|
||||
|
||||
colors = {}
|
||||
for key, shape of @thangType.get('raw')?.shapes or {}
|
||||
continue unless shape.fc?
|
||||
colors[shape.fc] = true if shapes[key]
|
||||
|
||||
|
||||
@buttons.find('button').removeClass('selected')
|
||||
@buttons.find('button').each (i, button) ->
|
||||
$(button).addClass('selected') if colors[$(button).val()]
|
||||
|
||||
|
||||
@updateMovieClip()
|
||||
|
||||
updateColorGroup: ->
|
||||
|
@ -155,7 +155,7 @@ module.exports = class ColorsTabView extends CocoView
|
|||
return unless $(button).hasClass('selected')
|
||||
window.button = button
|
||||
colors[$(button).val()] = true
|
||||
|
||||
|
||||
shapes = []
|
||||
for key, shape of @thangType.get('raw')?.shapes or {}
|
||||
continue unless shape.fc?
|
||||
|
|
|
@ -29,6 +29,7 @@ module.exports = class ThangTypeEditView extends View
|
|||
'change #real-upload-button': 'animationFileChosen'
|
||||
'change #animations-select': 'showAnimation'
|
||||
'click #marker-button': 'toggleDots'
|
||||
'click #end-button': 'endAnimation'
|
||||
|
||||
subscriptions:
|
||||
'save-new-version': 'saveNewThangType'
|
||||
|
@ -52,7 +53,7 @@ module.exports = class ThangTypeEditView extends View
|
|||
@files.fetch()
|
||||
@render()
|
||||
|
||||
getRenderData: (context={}) =>
|
||||
getRenderData: (context={}) ->
|
||||
context = super(context)
|
||||
context.thangType = @thangType
|
||||
context.animations = @getAnimationNames()
|
||||
|
@ -97,6 +98,7 @@ module.exports = class ThangTypeEditView extends View
|
|||
@stage = new createjs.Stage(canvas[0])
|
||||
canvasWidth = parseInt(canvas.attr('width'), 10)
|
||||
canvasHeight = parseInt(canvas.attr('height'), 10)
|
||||
@camera?.destroy()
|
||||
@camera = new Camera canvasWidth, canvasHeight
|
||||
|
||||
@torsoDot = @makeDot('blue')
|
||||
|
@ -129,6 +131,9 @@ module.exports = class ThangTypeEditView extends View
|
|||
@aboveHeadDot.y = CENTER.y + aboveHead.y * @scale
|
||||
@stage.addChild(@groundDot, @torsoDot, @mouthDot, @aboveHeadDot)
|
||||
|
||||
endAnimation: ->
|
||||
@currentSprite?.queueAction('idle')
|
||||
|
||||
updateGrid: ->
|
||||
grid = new createjs.Container()
|
||||
line = new createjs.Shape()
|
||||
|
@ -371,3 +376,7 @@ module.exports = class ThangTypeEditView extends View
|
|||
@grid.alpha = 1.0
|
||||
@showAnimation()
|
||||
@showingSelectedNode = false
|
||||
|
||||
destroy: ->
|
||||
@camera?.destroy()
|
||||
super()
|
||||
|
|
|
@ -2,6 +2,7 @@ View = require 'views/kinds/RootView'
|
|||
template = require 'templates/home'
|
||||
WizardSprite = require 'lib/surface/WizardSprite'
|
||||
ThangType = require 'models/ThangType'
|
||||
Simulator = require 'lib/simulator/Simulator'
|
||||
|
||||
module.exports = class HomeView extends View
|
||||
id: 'home-view'
|
||||
|
@ -10,6 +11,7 @@ module.exports = class HomeView extends View
|
|||
events:
|
||||
'mouseover #beginner-campaign': 'onMouseOverButton'
|
||||
'mouseout #beginner-campaign': 'onMouseOutButton'
|
||||
'click #simulate-button': 'onSimulateButtonClick'
|
||||
|
||||
getRenderData: ->
|
||||
c = super()
|
||||
|
@ -84,7 +86,7 @@ module.exports = class HomeView extends View
|
|||
@wizardSprite?.queueAction 'idle'
|
||||
|
||||
updateStage: =>
|
||||
@stage.update()
|
||||
@stage?.update()
|
||||
|
||||
willDisappear: ->
|
||||
super()
|
||||
|
@ -94,3 +96,11 @@ module.exports = class HomeView extends View
|
|||
didReappear: ->
|
||||
super()
|
||||
@turnOnStageUpdates()
|
||||
|
||||
destroy: ->
|
||||
@wizardSprite?.destroy()
|
||||
super()
|
||||
|
||||
onSimulateButtonClick: (e) =>
|
||||
simulator = new Simulator()
|
||||
simulator.fetchAndSimulateTask()
|
||||
|
|
|
@ -11,7 +11,7 @@ makeScopeName = -> "view-scope-#{classCount++}"
|
|||
module.exports = class CocoView extends Backbone.View
|
||||
startsLoading: false
|
||||
cache: true # signals to the router to keep this view around
|
||||
template: => ''
|
||||
template: -> ''
|
||||
|
||||
events:
|
||||
'click a': 'toggleModal'
|
||||
|
@ -36,11 +36,15 @@ module.exports = class CocoView extends Backbone.View
|
|||
super options
|
||||
|
||||
destroy: ->
|
||||
@destroyed = true
|
||||
@stopListening()
|
||||
@stopListeningToShortcuts()
|
||||
@undelegateEvents() # removes both events and subs
|
||||
view.destroy() for id, view of @subviews
|
||||
@modalClosed = null
|
||||
$('#modal-wrapper .modal').off 'hidden.bs.modal', @modalClosed
|
||||
@[key] = undefined for key, value of @
|
||||
@destroyed = true
|
||||
@destroy = ->
|
||||
|
||||
afterInsert: ->
|
||||
|
||||
|
@ -60,7 +64,7 @@ module.exports = class CocoView extends Backbone.View
|
|||
|
||||
# View Rendering
|
||||
|
||||
render: =>
|
||||
render: ->
|
||||
return @ unless me
|
||||
super()
|
||||
return @template if _.isString(@template)
|
||||
|
@ -70,7 +74,7 @@ module.exports = class CocoView extends Backbone.View
|
|||
@$el.i18n()
|
||||
@
|
||||
|
||||
getRenderData: (context) =>
|
||||
getRenderData: (context) ->
|
||||
context ?= {}
|
||||
context.isProduction = document.location.href.search(/codecombat.com/) isnt -1
|
||||
context.me = me
|
||||
|
@ -81,46 +85,40 @@ module.exports = class CocoView extends Backbone.View
|
|||
context
|
||||
|
||||
afterRender: ->
|
||||
@registerModalsWithin()
|
||||
|
||||
# Modals
|
||||
|
||||
toggleModal: (e) ->
|
||||
toggleModal: (e) ->
|
||||
if $(e.currentTarget).prop('target') is '_blank'
|
||||
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)
|
||||
return unless elem.data('toggle') is 'coco-modal'
|
||||
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)
|
||||
|
||||
registerModalsWithin: (e...) ->
|
||||
# TODO: Get rid of this part
|
||||
for modal in $('.modal', @$el)
|
||||
# console.warn 'Registered modal to get rid of...', modal
|
||||
$(modal).on('show.bs.modal', @clearModals)
|
||||
|
||||
openModalView: (modalView) ->
|
||||
return if @waitingModal # can only have one waiting at once
|
||||
if visibleModal
|
||||
waitingModal = modalView
|
||||
visibleModal.hide()
|
||||
return
|
||||
modalView.render()
|
||||
modalView.render()
|
||||
$('#modal-wrapper').empty().append modalView.el
|
||||
modalView.afterInsert()
|
||||
visibleModal = modalView
|
||||
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
|
||||
@getRootView().stopListeningToShortcuts(true)
|
||||
|
||||
modalClosed: =>
|
||||
modalClosed: =>
|
||||
visibleModal.willDisappear() if visibleModal
|
||||
visibleModal.destroy()
|
||||
visibleModal = null
|
||||
if waitingModal
|
||||
#$('#modal-wrapper .modal').off 'hidden.bs.modal', @modalClosed
|
||||
if waitingModal
|
||||
wm = waitingModal
|
||||
waitingModal = null
|
||||
@openModalView(wm)
|
||||
|
@ -128,16 +126,10 @@ module.exports = class CocoView extends Backbone.View
|
|||
@getRootView().listenToShortcuts(true)
|
||||
Backbone.Mediator.publish 'modal-closed'
|
||||
|
||||
clearModals: =>
|
||||
if visibleModal
|
||||
visibleModal.$el.addClass('hide')
|
||||
waitingModal = null
|
||||
@modalClosed()
|
||||
|
||||
# Loading RootViews
|
||||
|
||||
showLoading: ($el=@$el) ->
|
||||
$el.find('>').addClass('hide')
|
||||
$el.find('>').hide()
|
||||
$el.append($('<div class="loading-screen"></div>')
|
||||
.append('<h2>Loading</h2>')
|
||||
.append('<div class="progress progress-striped active loading"><div class="progress-bar"></div></div>'))
|
||||
|
@ -146,18 +138,20 @@ module.exports = class CocoView extends Backbone.View
|
|||
hideLoading: ->
|
||||
return unless @_lastLoading?
|
||||
@_lastLoading.find('.loading-screen').remove()
|
||||
@_lastLoading.find('>').removeClass('hide')
|
||||
@_lastLoading.find('>').show()
|
||||
@_lastLoading = null
|
||||
|
||||
# Loading ModalViews
|
||||
|
||||
enableModalInProgress: (modal) ->
|
||||
$('> div', modal).addClass('hide')
|
||||
$('.wait', modal).removeClass('hide')
|
||||
el = modal.find('.modal-content')
|
||||
el.find('> div', modal).hide()
|
||||
el.find('.wait', modal).show()
|
||||
|
||||
disableModalInProgress: (modal) ->
|
||||
$('> div', modal).removeClass('hide')
|
||||
$('.wait', modal).addClass('hide')
|
||||
el = modal.find('.modal-content')
|
||||
el.find('> div', modal).show()
|
||||
el.find('.wait', modal).hide()
|
||||
|
||||
# Subscriptions
|
||||
|
||||
|
@ -224,8 +218,8 @@ module.exports = class CocoView extends Backbone.View
|
|||
slider.on('slide',changeCallback)
|
||||
slider.on('slidechange',changeCallback)
|
||||
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
|
||||
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
|
||||
|
|
|
@ -16,7 +16,7 @@ module.exports = class ModalView extends CocoView
|
|||
@modalWidthPercent = options.modalWidthPercent if options.modalWidthPercent
|
||||
super options
|
||||
|
||||
getRenderData: (context={}) =>
|
||||
getRenderData: (context={}) ->
|
||||
context = super(context)
|
||||
context.closeButton = @closeButton
|
||||
context
|
||||
|
|
|
@ -34,7 +34,7 @@ module.exports = class SuperVersionsView extends View
|
|||
@startsLoading = false
|
||||
@render()
|
||||
|
||||
getRenderData: (context={}) =>
|
||||
getRenderData: (context={}) ->
|
||||
context = super(context)
|
||||
context.page = @page
|
||||
context.dataList = (m.attributes for m in @collection.models) if @collection
|
||||
|
|
|
@ -36,7 +36,6 @@ module.exports = class LoginModalView extends View
|
|||
loginAccount: (e) =>
|
||||
forms.clearFormAlerts(@$el)
|
||||
userObject = forms.formToObject @$el
|
||||
# res = validateUser(userObject)
|
||||
res = tv4.validateMultiple userObject, User.schema.attributes
|
||||
return forms.applyErrorsToForm(@$el, res.errors) unless res.valid
|
||||
@enableModalInProgress(@$el) # TODO: part of forms
|
||||
|
|
|
@ -76,5 +76,5 @@ module.exports = class WizardSettingsView extends View
|
|||
me.save()
|
||||
|
||||
destroy: ->
|
||||
super()
|
||||
@wizardSprite?.destroy()
|
||||
super()
|
||||
|
|
|
@ -21,11 +21,28 @@ class LeaderboardCollection extends CocoCollection
|
|||
super()
|
||||
options ?= {}
|
||||
@url = "/db/level/#{level.get('original')}.#{level.get('version').major}/leaderboard?#{$.param(options)}"
|
||||
#@url = "/db/level/#{level.get('original')}/leaderboard?#{$.param(options)}"
|
||||
|
||||
|
||||
module.exports = class LadderView extends RootView
|
||||
id: 'ladder-view'
|
||||
template: require 'templates/play/ladder'
|
||||
startsLoading: true
|
||||
|
||||
events:
|
||||
'click #simulate-button': 'onSimulateButtonClick'
|
||||
|
||||
onSimulateButtonClick: (e) ->
|
||||
submitIDs = _.pluck @leaderboards[@teams[0]].topPlayers.models, "id"
|
||||
for ID in submitIDs
|
||||
$.ajax
|
||||
url: '/queue/scoring'
|
||||
method: 'POST'
|
||||
data:
|
||||
session: ID
|
||||
alert "Simulating all games!"
|
||||
alert "(do not push more than once pls)"
|
||||
|
||||
|
||||
constructor: (options, levelID) ->
|
||||
super(options)
|
||||
|
@ -33,15 +50,16 @@ module.exports = class LadderView extends RootView
|
|||
@level.fetch()
|
||||
@level.once 'sync', @onLevelLoaded, @
|
||||
|
||||
@sessions = new LevelSessionsCollection(levelID)
|
||||
@sessions.fetch({})
|
||||
@sessions.once 'sync', @onMySessionsLoaded, @
|
||||
# @sessions = new LevelSessionsCollection(levelID)
|
||||
# @sessions.fetch({})
|
||||
# @sessions.once 'sync', @onMySessionsLoaded, @
|
||||
|
||||
onLevelLoaded: -> @startLoadingPhaseTwoMaybe()
|
||||
onMySessionsLoaded: -> @startLoadingPhaseTwoMaybe()
|
||||
onMySessionsLoaded: ->
|
||||
@startLoadingPhaseTwoMaybe()
|
||||
|
||||
startLoadingPhaseTwoMaybe: ->
|
||||
return unless @level.loaded and @sessions.loaded
|
||||
return unless @level.loaded # and @sessions.loaded
|
||||
@loadPhaseTwo()
|
||||
|
||||
loadPhaseTwo: ->
|
||||
|
@ -55,17 +73,19 @@ module.exports = class LadderView extends RootView
|
|||
@leaderboards = {}
|
||||
@challengers = {}
|
||||
for team in teams
|
||||
teamSession = _.find @sessions.models, (session) -> session.get('team') is team
|
||||
# teamSession = _.find @sessions.models, (session) -> session.get('team') is team
|
||||
teamSession = null
|
||||
console.log "Team session: #{JSON.stringify teamSession}"
|
||||
@leaderboards[team] = new LeaderboardData(@level, team, teamSession)
|
||||
@leaderboards[team].once 'sync', @onLeaderboardLoaded, @
|
||||
@challengers[team] = new ChallengersData(@level, team, teamSession)
|
||||
@challengers[team].once 'sync', @onChallengersLoaded, @
|
||||
# @challengers[team] = new ChallengersData(@level, team, teamSession)
|
||||
# @challengers[team].once 'sync', @onChallengersLoaded, @
|
||||
|
||||
onChallengersLoaded: -> @renderMaybe()
|
||||
onLeaderboardLoaded: -> @renderMaybe()
|
||||
|
||||
renderMaybe: ->
|
||||
loaders = _.values(@leaderboards).concat(_.values(@challengers))
|
||||
loaders = _.values(@leaderboards) # .concat(_.values(@challengers))
|
||||
return unless _.every loaders, (loader) -> loader.loaded
|
||||
@startsLoading = false
|
||||
@render()
|
||||
|
@ -78,13 +98,15 @@ module.exports = class LadderView extends RootView
|
|||
ctx.link = "/play/level/#{@level.get('name')}"
|
||||
ctx.teams = []
|
||||
for team in @teams or []
|
||||
otherTeam = if team is 'ogres' then 'humans' else 'ogres'
|
||||
ctx.teams.push({
|
||||
id: team
|
||||
name: _.string.titleize(team)
|
||||
leaderboard: @leaderboards[team]
|
||||
easyChallenger: @challengers[team].easyPlayer.models[0]
|
||||
mediumChallenger: @challengers[team].mediumPlayer.models[0]
|
||||
hardChallenger: @challengers[team].hardPlayer.models[0]
|
||||
otherTeam: otherTeam
|
||||
# easyChallenger: @challengers[team].easyPlayer.models[0]
|
||||
# mediumChallenger: @challengers[team].mediumPlayer.models[0]
|
||||
# hardChallenger: @challengers[team].hardPlayer.models[0]
|
||||
})
|
||||
ctx
|
||||
|
||||
|
@ -94,23 +116,28 @@ module.exports = class LadderView extends RootView
|
|||
|
||||
class LeaderboardData
|
||||
constructor: (@level, @team, @session) ->
|
||||
console.log 'creating leaderboard data', @level, @team, @session
|
||||
_.extend @, Backbone.Events
|
||||
@topPlayers = new LeaderboardCollection(@level, {order:-1, scoreOffset: HIGHEST_SCORE, team: @team, limit: if @session then 10 else 20})
|
||||
@topPlayers.fetch()
|
||||
@topPlayers.comparator = (model) ->
|
||||
return -model.get('totalScore')
|
||||
@topPlayers.sort()
|
||||
|
||||
@topPlayers.once 'sync', @leaderboardPartLoaded, @
|
||||
|
||||
if @session
|
||||
score = @session.get('score') or 25
|
||||
@playersAbove = new LeaderboardCollection(@level, {order:1, scoreOffset: score, limit: 4, team: @team})
|
||||
@playersAbove.fetch()
|
||||
@playersAbove.once 'sync', @leaderboardPartLoaded, @
|
||||
@playersBelow = new LeaderboardCollection(@level, {order:-1, scoreOffset: score, limit: 4, team: @team})
|
||||
@playersBelow.fetch()
|
||||
@playersBelow.once 'sync', @leaderboardPartLoaded, @
|
||||
# if @session
|
||||
# score = @session.get('totalScore') or 25
|
||||
# @playersAbove = new LeaderboardCollection(@level, {order:1, scoreOffset: score, limit: 4, team: @team})
|
||||
# @playersAbove.fetch()
|
||||
# @playersAbove.once 'sync', @leaderboardPartLoaded, @
|
||||
# @playersBelow = new LeaderboardCollection(@level, {order:-1, scoreOffset: score, limit: 4, team: @team})
|
||||
# @playersBelow.fetch()
|
||||
# @playersBelow.once 'sync', @leaderboardPartLoaded, @
|
||||
|
||||
leaderboardPartLoaded: ->
|
||||
if @session
|
||||
if @topPlayers.loaded and @playersAbove.loaded and @playersBelow.loaded
|
||||
if @topPlayers.loaded # and @playersAbove.loaded and @playersBelow.loaded
|
||||
@loaded = true
|
||||
@trigger 'sync'
|
||||
else
|
||||
|
@ -120,7 +147,7 @@ class LeaderboardData
|
|||
class ChallengersData
|
||||
constructor: (@level, @team, @session) ->
|
||||
_.extend @, Backbone.Events
|
||||
score = @session?.get('score') or 25
|
||||
score = @session?.get('totalScore') or 25
|
||||
@easyPlayer = new LeaderboardCollection(@level, {order:1, scoreOffset: score - 5, limit: 1, team: @team})
|
||||
@easyPlayer.fetch()
|
||||
@easyPlayer.once 'sync', @challengerLoaded, @
|
||||
|
|
|
@ -37,6 +37,7 @@ module.exports = class ControlBarView extends View
|
|||
setBus: (@bus) ->
|
||||
|
||||
onPlayerStatesChanged: (e) ->
|
||||
# TODO: this doesn't fire any more. Replacement?
|
||||
return unless @bus is e.bus
|
||||
numPlayers = _.keys(e.players).length
|
||||
return if numPlayers is @numPlayers
|
||||
|
@ -45,7 +46,7 @@ module.exports = class ControlBarView extends View
|
|||
text += " (#{numPlayers})" if numPlayers > 1
|
||||
$('#multiplayer-button', @$el).text(text)
|
||||
|
||||
getRenderData: (context={}) =>
|
||||
getRenderData: (context={}) ->
|
||||
super context
|
||||
context.worldName = @worldName
|
||||
context.multiplayerEnabled = @session.get('multiplayer')
|
||||
|
@ -56,7 +57,7 @@ module.exports = class ControlBarView extends View
|
|||
@openModalView(new DocsModal(options))
|
||||
|
||||
showMultiplayerModal: ->
|
||||
@openModalView(new MultiplayerModal(session: @session, playableTeams: @playableTeams))
|
||||
@openModalView(new MultiplayerModal(session: @session, playableTeams: @playableTeams, level: @level))
|
||||
|
||||
showRestartModal: ->
|
||||
@openModalView(new ReloadModal())
|
||||
|
|
|
@ -18,7 +18,7 @@ module.exports = class GoalsView extends View
|
|||
events:
|
||||
'click': 'toggleCollapse'
|
||||
|
||||
toggleCollapse: (e) =>
|
||||
toggleCollapse: (e) ->
|
||||
@$el.toggleClass('expanded').toggleClass('collapsed')
|
||||
|
||||
onNewGoalStates: (e) ->
|
||||
|
@ -28,6 +28,7 @@ module.exports = class GoalsView extends View
|
|||
for goal in e.goals
|
||||
state = e.goalStates[goal.id]
|
||||
continue if goal.hiddenGoal and state.status isnt 'failure'
|
||||
continue if goal.team and me.team isnt goal.team
|
||||
text = goal.i18n?[me.lang()]?.name ? goal.name
|
||||
if state.killed
|
||||
dead = _.filter(_.values(state.killed)).length
|
||||
|
@ -45,11 +46,11 @@ module.exports = class GoalsView extends View
|
|||
li.prepend($('<i></i>').addClass(stateIconMap[state.status]))
|
||||
list.append(li)
|
||||
goals.push goal
|
||||
if goals.length then @$el.removeClass('hide') else @$el.addClass('hide')
|
||||
@$el.removeClass('secret') if goals.length > 0
|
||||
|
||||
render: ->
|
||||
super()
|
||||
@$el.addClass('hide').addClass('expanded')
|
||||
@$el.addClass('secret').addClass('expanded')
|
||||
|
||||
onSetLetterbox: (e) ->
|
||||
if e.on then @$el.hide() else @$el.show()
|
||||
@$el.toggle not e.on
|
||||
|
|
22
app/views/play/level/gold_view.coffee
Normal file
22
app/views/play/level/gold_view.coffee
Normal file
|
@ -0,0 +1,22 @@
|
|||
View = require 'views/kinds/CocoView'
|
||||
template = require 'templates/play/level/gold'
|
||||
|
||||
module.exports = class GoldView extends View
|
||||
id: "gold-view"
|
||||
template: template
|
||||
|
||||
subscriptions:
|
||||
'surface:gold-changed': 'onGoldChanged'
|
||||
'level-set-letterbox': 'onSetLetterbox'
|
||||
|
||||
onGoldChanged: (e) ->
|
||||
@$el.show()
|
||||
goldEl = @$el.find('.gold-amount.team-' + e.team)
|
||||
unless goldEl.length
|
||||
teamEl = $("<h3 class='team-#{e.team}' title='Gold: #{e.team}'><img src='/images/level/prop_gold.png'> <div class='gold-amount team-#{e.team}'></div>")
|
||||
@$el.append(teamEl)
|
||||
goldEl = teamEl.find('.gold-amount.team-' + e.team)
|
||||
goldEl.text(e.gold)
|
||||
|
||||
onSetLetterbox: (e) ->
|
||||
@$el.toggle not e.on
|
|
@ -24,12 +24,15 @@ module.exports = class HUDView extends View
|
|||
'god:new-world-created': 'onNewWorld'
|
||||
|
||||
events:
|
||||
'click': -> Backbone.Mediator.publish 'focus-editor'
|
||||
'click': 'onClick'
|
||||
|
||||
afterRender: =>
|
||||
afterRender: ->
|
||||
super()
|
||||
@$el.addClass 'no-selection'
|
||||
|
||||
onClick: (e) ->
|
||||
Backbone.Mediator.publish 'focus-editor' unless $(e.target).parents('.thang-props').length
|
||||
|
||||
onFrameChanged: (e) ->
|
||||
@timeProgress = e.progress
|
||||
@update()
|
||||
|
@ -153,7 +156,7 @@ module.exports = class HUDView extends View
|
|||
@bubble.removeClass(@lastMood) if @lastMood
|
||||
@lastMood = mood
|
||||
@bubble.text('')
|
||||
group = $('<div class="enter hide"></div>')
|
||||
group = $('<div class="enter secret"></div>')
|
||||
@bubble.append(group)
|
||||
if responses
|
||||
@lastResponses = responses
|
||||
|
@ -176,7 +179,7 @@ module.exports = class HUDView extends View
|
|||
if @animator.done()
|
||||
clearInterval(@messageInterval)
|
||||
@messageInterval = null
|
||||
$('.enter', @bubble).removeClass("hide").css('opacity', 0.0).delay(500).animate({opacity:1.0}, 500, @animateEnterButton)
|
||||
$('.enter', @bubble).removeClass("secret").css('opacity', 0.0).delay(500).animate({opacity:1.0}, 500, @animateEnterButton)
|
||||
if @lastResponses
|
||||
buttons = $('.enter button')
|
||||
for response, i in @lastResponses
|
||||
|
@ -207,10 +210,10 @@ module.exports = class HUDView extends View
|
|||
|
||||
switchToDialogueElements: ->
|
||||
@dialogueMode = true
|
||||
$('.thang-elem', @$el).addClass('hide')
|
||||
@$el.find('.thang-canvas-wrapper').removeClass('hide')
|
||||
$('.thang-elem', @$el).addClass('secret')
|
||||
@$el.find('.thang-canvas-wrapper').removeClass('secret')
|
||||
$('.dialogue-area', @$el)
|
||||
.removeClass('hide')
|
||||
.removeClass('secret')
|
||||
.animate({opacity:1.0}, 200)
|
||||
$('.dialogue-bubble', @$el)
|
||||
.css('opacity', 0.0)
|
||||
|
@ -220,8 +223,8 @@ module.exports = class HUDView extends View
|
|||
|
||||
switchToThangElements: ->
|
||||
@dialogueMode = false
|
||||
$('.thang-elem', @$el).removeClass('hide')
|
||||
$('.dialogue-area', @$el).addClass('hide')
|
||||
$('.thang-elem', @$el).removeClass('secret')
|
||||
$('.dialogue-area', @$el).addClass('secret')
|
||||
|
||||
update: ->
|
||||
return unless @thang and not @speaker
|
||||
|
@ -235,7 +238,7 @@ module.exports = class HUDView extends View
|
|||
return null # included in the bar
|
||||
context =
|
||||
prop: prop
|
||||
hasIcon: prop in ["health", "pos", "target", "inventory"]
|
||||
hasIcon: prop in ["health", "pos", "target", "inventory", "gold"]
|
||||
hasBar: prop in ["health"]
|
||||
$(prop_template(context))
|
||||
|
||||
|
@ -321,7 +324,7 @@ module.exports = class HUDView extends View
|
|||
changed = true
|
||||
break
|
||||
return unless changed
|
||||
ael.toggleClass 'hidden', not timespans.length
|
||||
ael.toggleClass 'secret', not timespans.length
|
||||
@lastActionTimespans[action] = timespans
|
||||
timeline = ael.find('.action-timeline .timeline-wrapper').empty()
|
||||
lifespan = @thang.world.totalFrames / @thang.world.frameRate
|
||||
|
@ -333,5 +336,8 @@ module.exports = class HUDView extends View
|
|||
ael
|
||||
|
||||
destroy: ->
|
||||
super()
|
||||
@stage?.stopTalking()
|
||||
@addMoreMessage = null
|
||||
@animateEnterButton = null
|
||||
clearInterval(@messageInterval) if @messageInterval
|
||||
super()
|
||||
|
|
|
@ -18,19 +18,16 @@ module.exports = class LevelChatView extends View
|
|||
constructor: (options) ->
|
||||
@levelID = options.levelID
|
||||
@session = options.session
|
||||
@session.on 'change:multiplayer', @updateMultiplayerVisibility
|
||||
@session.on 'change:multiplayer', @updateMultiplayerVisibility, @
|
||||
@sessionID = options.sessionID
|
||||
@bus = LevelBus.get(@levelID, @sessionID)
|
||||
super()
|
||||
@regularlyClearOldMessages()
|
||||
@playNoise = _.debounce(@playNoise, 100)
|
||||
|
||||
updateMultiplayerVisibility: =>
|
||||
updateMultiplayerVisibility: ->
|
||||
return unless @$el?
|
||||
if @session.get('multiplayer')
|
||||
@$el.removeClass('hide')
|
||||
else
|
||||
@$el.addClass('hide')
|
||||
@$el.toggle Boolean @session.get('multiplayer')
|
||||
|
||||
afterRender: ->
|
||||
@chatTables = $('table', @$el)
|
||||
|
@ -47,8 +44,8 @@ module.exports = class LevelChatView extends View
|
|||
if new Date().getTime() - added > 60 * 1000
|
||||
row.fadeOut(1000, -> $(this).remove())
|
||||
|
||||
onNewMessage: (e) =>
|
||||
@$el.removeClass('hide') unless e.message.system
|
||||
onNewMessage: (e) ->
|
||||
@$el.show() unless e.message.system
|
||||
@addOne(e.message)
|
||||
@trimClosedPanel()
|
||||
@playNoise() if e.message.authorID isnt me.id
|
||||
|
@ -99,7 +96,7 @@ module.exports = class LevelChatView extends View
|
|||
break if rows.length - i <= limit
|
||||
row.remove()
|
||||
|
||||
onChatKeydown: (e) =>
|
||||
onChatKeydown: (e) ->
|
||||
if key.isPressed('enter')
|
||||
message = _.string.strip($(e.target).val())
|
||||
return false unless message
|
||||
|
@ -107,17 +104,11 @@ module.exports = class LevelChatView extends View
|
|||
$(e.target).val('')
|
||||
return false
|
||||
|
||||
onIconClick: =>
|
||||
openPanel = $('.open-chat-area', @$el)
|
||||
closedPanel = $('.closed-chat-area', @$el)
|
||||
onIconClick: ->
|
||||
@open = not @open
|
||||
if @open
|
||||
closedPanel.addClass('hide')
|
||||
openPanel.removeClass('hide')
|
||||
@scrollDown()
|
||||
else
|
||||
openPanel.addClass('hide')
|
||||
closedPanel.removeClass('hide')
|
||||
openPanel = $('.open-chat-area', @$el).toggle @open
|
||||
closedPanel = $('.closed-chat-area', @$el).toggle not @open
|
||||
@scrollDown() if @open
|
||||
if window.getSelection?
|
||||
sel = window.getSelection()
|
||||
sel.empty?()
|
||||
|
@ -130,6 +121,8 @@ module.exports = class LevelChatView extends View
|
|||
openPanel.scrollTop = openPanel.scrollHeight or 1000000
|
||||
|
||||
destroy: ->
|
||||
console.log('DESTROY CHAT', @levelID)
|
||||
super()
|
||||
key.deleteScope('level')
|
||||
@session.off 'change:multiplayer', @updateMultiplayerVisibility, @
|
||||
clearInterval @clearOldMessagesInterval if @clearOldMessagesInterval
|
||||
@clearOldMessages = null
|
||||
super()
|
||||
|
|
|
@ -9,11 +9,13 @@ module.exports = class MultiplayerModal extends View
|
|||
events:
|
||||
'click textarea': 'onClickLink'
|
||||
'change #multiplayer': 'updateLinkSection'
|
||||
'click #submit-session-button': 'submitSession'
|
||||
|
||||
constructor: (options) ->
|
||||
super(options)
|
||||
@session = options.session
|
||||
@session.on 'change:multiplayer', @updateLinkSection
|
||||
@level = options.level
|
||||
@session.on 'change:multiplayer', @updateLinkSection, @
|
||||
@playableTeams = options.playableTeams
|
||||
|
||||
getRenderData: ->
|
||||
|
@ -23,6 +25,7 @@ module.exports = class MultiplayerModal extends View
|
|||
@session.id)
|
||||
c.multiplayer = @session.get('multiplayer')
|
||||
c.playableTeams = @playableTeams
|
||||
c.ladderGame = @level?.get('name') is 'Project DotA' and not me.get('isAnonymous')
|
||||
c
|
||||
|
||||
afterRender: ->
|
||||
|
@ -32,12 +35,23 @@ module.exports = class MultiplayerModal extends View
|
|||
onClickLink: (e) ->
|
||||
e.target.select()
|
||||
|
||||
updateLinkSection: =>
|
||||
updateLinkSection: ->
|
||||
multiplayer = @$el.find('#multiplayer').prop('checked')
|
||||
la = @$el.find('#link-area')
|
||||
if multiplayer then la.show() else la.hide()
|
||||
la.toggle Boolean(multiplayer)
|
||||
true
|
||||
|
||||
submitSession: ->
|
||||
$.ajax('/queue/scoring', {
|
||||
method: 'POST'
|
||||
data:
|
||||
session: @session.id
|
||||
})
|
||||
|
||||
onHidden: ->
|
||||
multiplayer = Boolean(@$el.find('#multiplayer').prop('checked'))
|
||||
@session.set('multiplayer', multiplayer)
|
||||
|
||||
destroy: ->
|
||||
@session.off 'change:multiplayer', @updateLinkSection, @
|
||||
super()
|
||||
|
|
|
@ -100,8 +100,8 @@ module.exports = class VictoryModal extends View
|
|||
Backbone.Mediator.publish 'level:victory-hidden'
|
||||
|
||||
destroy: ->
|
||||
super()
|
||||
@saveReview() if @$el.find('.review textarea').val()
|
||||
super()
|
||||
|
||||
# rating, review
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue