mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-11-28 10:06:08 -05:00
Merge pull request #1 from codecombat/master
Fixes error "Type str doesn't support the buffer API"
This commit is contained in:
commit
312d880508
94 changed files with 519 additions and 169 deletions
|
@ -132,7 +132,7 @@ module.exports = class God
|
|||
angel.destroy() for angel in @angels
|
||||
@dead = true
|
||||
Backbone.Mediator.unsubscribe('tome:cast-spells', @onTomeCast, @)
|
||||
@goalManager.destroy()
|
||||
@goalManager?.destroy()
|
||||
@goalManager = null
|
||||
@fillWorkerPool = null
|
||||
@simulateWorld = null
|
||||
|
|
|
@ -87,6 +87,9 @@ module.exports = class LevelBus extends Bus
|
|||
# LevelSession object. Either break this off into a separate class
|
||||
# or have the LevelSession object listen for all these events itself.
|
||||
|
||||
setSpells: (spells) ->
|
||||
@onSpellCreated spell: spell for spellKey, spell of spells
|
||||
|
||||
onSpellChanged: (e) ->
|
||||
return unless @onPoint()
|
||||
code = @session.get('code')
|
||||
|
@ -102,6 +105,7 @@ module.exports = class LevelBus extends Bus
|
|||
onSpellCreated: (e) ->
|
||||
return unless @onPoint()
|
||||
spellTeam = e.spell.team
|
||||
@teamSpellMap ?= {}
|
||||
@teamSpellMap[spellTeam] ?= []
|
||||
|
||||
unless e.spell.spellKey in @teamSpellMap[spellTeam]
|
||||
|
@ -109,8 +113,8 @@ module.exports = class LevelBus extends Bus
|
|||
@changedSessionProperties.teamSpells = true
|
||||
@session.set({'teamSpells': @teamSpellMap})
|
||||
@saveSession()
|
||||
|
||||
|
||||
if spellTeam is me.team
|
||||
@onSpellChanged e # Save the new spell to the session, too.
|
||||
|
||||
onScriptStateChanged: (e) ->
|
||||
return unless @onPoint()
|
||||
|
@ -236,10 +240,3 @@ module.exports = class LevelBus extends Bus
|
|||
destroy: ->
|
||||
@session.off 'change:multiplayer', @onMultiplayerChanged, @
|
||||
super()
|
||||
|
||||
setTeamSpellMap: (spellMap) ->
|
||||
@teamSpellMap = spellMap
|
||||
console.log @teamSpellMap
|
||||
@changedSessionProperties.teamSpells = true
|
||||
@session.set({'teamSpells': @teamSpellMap})
|
||||
@saveSession()
|
||||
|
|
|
@ -93,9 +93,6 @@ module.exports = class LevelLoader extends CocoClass
|
|||
@supermodel.populateModel @level
|
||||
|
||||
onSupermodelError: ->
|
||||
msg = $.i18n.t('play_level.level_load_error',
|
||||
defaultValue: "Level could not be loaded.")
|
||||
$('body').append('<div class="alert">' + msg + '</div>')
|
||||
|
||||
onSupermodelLoadedOne: (e) ->
|
||||
@update()
|
||||
|
@ -113,16 +110,19 @@ module.exports = class LevelLoader extends CocoClass
|
|||
@updateCompleted = true
|
||||
|
||||
denormalizeSession: ->
|
||||
return if @session.get 'levelName'
|
||||
return if @sessionDenormalized
|
||||
patch =
|
||||
'levelName': @level.get('name')
|
||||
'levelID': @level.get('slug') or @level.id
|
||||
if me.id is @session.get 'creator'
|
||||
patch.creatorName = me.get('name')
|
||||
|
||||
@session.set key, value for key, value of patch
|
||||
tempSession = new LevelSession _id: @session.id
|
||||
tempSession.save(patch, {patch: true})
|
||||
for key, value of patch
|
||||
if @session.get(key) is value
|
||||
delete patch[key]
|
||||
unless _.isEmpty patch
|
||||
@session.set key, value for key, value of patch
|
||||
tempSession = new LevelSession _id: @session.id
|
||||
tempSession.save(patch, {patch: true})
|
||||
@sessionDenormalized = true
|
||||
|
||||
# World init
|
||||
|
|
|
@ -132,6 +132,15 @@ module.exports = ScriptManager = class ScriptManager extends CocoClass
|
|||
continue unless script.channel is channel
|
||||
continue if alreadyTriggered and not script.repeats
|
||||
continue if script.lastTriggered? and new Date().getTime() - script.lastTriggered < 1
|
||||
continue if script.neverRun
|
||||
|
||||
if script.notAfter
|
||||
for scriptID in script.notAfter
|
||||
if scriptID in @triggered
|
||||
script.neverRun = true
|
||||
break
|
||||
continue if script.neverRun
|
||||
|
||||
continue unless @scriptPrereqsSatisfied(script)
|
||||
continue unless scriptMatchesEventPrereqs(script, event)
|
||||
# everything passed!
|
||||
|
|
|
@ -33,7 +33,7 @@ module.exports = class SpritesScriptModule extends ScriptModule
|
|||
spriteSayNote: (sprite, script) ->
|
||||
return if @speakingSprites[sprite.id]
|
||||
responses = sprite.say.responses
|
||||
responses = [] unless script.skippable
|
||||
responses = [] unless script.skippable or responses
|
||||
for response in responses ? []
|
||||
response.text = response.i18n?[me.lang()]?.text ? response.text
|
||||
text = sprite.say.i18n?[me.lang()]?.text or sprite.say.text
|
||||
|
|
|
@ -64,7 +64,7 @@ module.exports = class Simulator
|
|||
|
||||
setupGoalManager: ->
|
||||
@god.goalManager = new GoalManager @world
|
||||
@god.goalManager.goals = @fetchGoalsFromWorldNoteChain()
|
||||
@god.goalManager.goals = @god.level.goals
|
||||
@god.goalManager.goalStates = @manuallyGenerateGoalStates()
|
||||
|
||||
commenceSimulationAndSetupCallback: ->
|
||||
|
@ -108,17 +108,22 @@ module.exports = class Simulator
|
|||
taskResults =
|
||||
taskID: @task.getTaskID()
|
||||
receiptHandle: @task.getReceiptHandle()
|
||||
originalSessionID: @task.getFirstSessionID()
|
||||
originalSessionRank: -1
|
||||
calculationTime: 500
|
||||
sessions: []
|
||||
|
||||
for session in @task.getSessions()
|
||||
|
||||
sessionResult =
|
||||
sessionID: session.sessionID
|
||||
submitDate: session.submitDate
|
||||
creator: session.creator
|
||||
metrics:
|
||||
rank: @calculateSessionRank session.sessionID, simulationResults.goalStates, @task.generateTeamToSessionMap()
|
||||
|
||||
if session.sessionID is taskResults.originalSessionID
|
||||
taskResults.originalSessionRank = sessionResult.metrics.rank
|
||||
taskResults.originalSessionTeam = session.team
|
||||
taskResults.sessions.push sessionResult
|
||||
|
||||
return taskResults
|
||||
|
@ -137,8 +142,6 @@ module.exports = class Simulator
|
|||
else
|
||||
return 1
|
||||
|
||||
fetchGoalsFromWorldNoteChain: -> return @god.goalManager.world.scripts[0].noteChain[0].goals.add
|
||||
|
||||
manuallyGenerateGoalStates: ->
|
||||
goalStates =
|
||||
"destroy-humans":
|
||||
|
@ -178,7 +181,6 @@ module.exports = class Simulator
|
|||
generateSpellKeyToSourceMapPropertiesFromThang: (thang) =>
|
||||
for component in thang.components
|
||||
continue unless @componentHasProgrammableMethods component
|
||||
|
||||
for methodName, method of component.config.programmableMethods
|
||||
spellKey = @generateSpellKeyFromThangIDAndMethodName thang.id, methodName
|
||||
|
||||
|
@ -187,9 +189,11 @@ module.exports = class Simulator
|
|||
@transpileSpell thang, spellKey, methodName
|
||||
|
||||
generateSpellKeyFromThangIDAndMethodName: (thang, methodName) ->
|
||||
spellKeyComponents = [thang.id, methodName]
|
||||
spellKeyComponents = [thang, methodName]
|
||||
spellKeyComponents[0] = _.string.slugify spellKeyComponents[0]
|
||||
spellKeyComponents.join '/'
|
||||
spellKey = spellKeyComponents.join '/'
|
||||
spellKey
|
||||
|
||||
|
||||
createSpellAndAssignName: (spellKey, spellName) ->
|
||||
@spells[spellKey] ?= {}
|
||||
|
@ -202,7 +206,7 @@ module.exports = class Simulator
|
|||
|
||||
transpileSpell: (thang, spellKey, methodName) ->
|
||||
slugifiedThangID = _.string.slugify thang.id
|
||||
source = @currentUserCodeMap[slugifiedThangID]?[methodName] ? ""
|
||||
source = @currentUserCodeMap[[slugifiedThangID,methodName].join '/'] ? ""
|
||||
@spells[spellKey].thangs[thang.id].aether.transpile source
|
||||
|
||||
createAether: (methodName, method) ->
|
||||
|
@ -220,7 +224,7 @@ module.exports = class Simulator
|
|||
#functionParameters: # TODOOOOO
|
||||
if methodName is 'hear'
|
||||
aetherOptions.functionParameters = ['speaker', 'message', 'data']
|
||||
console.log "creating aether with options", aetherOptions
|
||||
#console.log "creating aether with options", aetherOptions
|
||||
return new Aether aetherOptions
|
||||
|
||||
class SimulationTask
|
||||
|
@ -253,12 +257,18 @@ class SimulationTask
|
|||
|
||||
generateSpellKeyToSourceMap: ->
|
||||
spellKeyToSourceMap = {}
|
||||
|
||||
for session in @rawData.sessions
|
||||
teamSpells = session.teamSpells[session.team]
|
||||
_.merge spellKeyToSourceMap, _.pick(session.code, teamSpells)
|
||||
teamCode = {}
|
||||
for thangName, thangSpells of session.code
|
||||
for spellName, spell of thangSpells
|
||||
fullSpellName = [thangName,spellName].join '/'
|
||||
if _.contains(teamSpells, fullSpellName)
|
||||
teamCode[fullSpellName]=spell
|
||||
|
||||
_.merge spellKeyToSourceMap, teamCode
|
||||
commonSpells = session.teamSpells["common"]
|
||||
_.merge spellKeyToSourceMap, _.pick(session.code, commonSpells) if commonSpells?
|
||||
|
||||
|
||||
spellKeyToSourceMap
|
||||
|
|
|
@ -40,6 +40,8 @@ module.exports = class Camera extends CocoClass
|
|||
'camera-zoom-out': 'onZoomOut'
|
||||
'surface:mouse-scrolled': 'onMouseScrolled'
|
||||
'level:restarted': 'onLevelRestarted'
|
||||
'sprite:mouse-down': 'onMouseDown'
|
||||
'sprite:dragged': 'onMouseDragged'
|
||||
|
||||
# TODO: Fix tests to not use mainLayer
|
||||
constructor: (@canvasWidth, @canvasHeight, angle=Math.asin(0.75), hFOV=d2r(30)) ->
|
||||
|
@ -164,6 +166,21 @@ module.exports = class Camera extends CocoClass
|
|||
target = @target
|
||||
@zoomTo target, newZoom, 0
|
||||
|
||||
onMouseDown: (e) ->
|
||||
return if @dragDisabled
|
||||
@lastPos = {x: e.originalEvent.rawX, y: e.originalEvent.rawY}
|
||||
|
||||
onMouseDragged: (e) ->
|
||||
return if @dragDisabled
|
||||
target = @boundTarget(@target, @zoom)
|
||||
newPos = {
|
||||
x: target.x + (@lastPos.x - e.originalEvent.rawX) / @zoom
|
||||
y: target.y + (@lastPos.y - e.originalEvent.rawY) / @zoom
|
||||
}
|
||||
@zoomTo newPos, @zoom, 0
|
||||
@lastPos = {x: e.originalEvent.rawX, y: e.originalEvent.rawY}
|
||||
Backbone.Mediator.publish 'camera:dragged'
|
||||
|
||||
onLevelRestarted: ->
|
||||
@setBounds(@firstBounds, false)
|
||||
|
||||
|
|
|
@ -55,7 +55,7 @@ module.exports = class CoordinateDisplay extends createjs.Container
|
|||
@label.regY = height / 2
|
||||
sup = @camera.worldToSurface @lastPos
|
||||
@x = sup.x
|
||||
@y = sup.y
|
||||
@y = sup.y - 7
|
||||
@addChild @label
|
||||
@cache -width / 2, -height / 2, width, height
|
||||
Backbone.Mediator.publish 'surface:coordinates-shown', {}
|
||||
|
|
|
@ -22,6 +22,7 @@ module.exports = IndieSprite = class IndieSprite extends CocoSprite
|
|||
thang.width = thang.height = thang.depth = 4
|
||||
thang.pos = pos ? @defaultPos()
|
||||
thang.pos.z = thang.depth / 2
|
||||
thang.shape = 'ellipsoid'
|
||||
thang.rotation = 0
|
||||
thang.action = 'idle'
|
||||
thang.setAction = (action) -> thang.action = action
|
||||
|
|
40
app/lib/surface/PlaybackOverScreen.coffee
Normal file
40
app/lib/surface/PlaybackOverScreen.coffee
Normal file
|
@ -0,0 +1,40 @@
|
|||
CocoClass = require 'lib/CocoClass'
|
||||
|
||||
module.exports = class PlaybackoverScreen extends CocoClass
|
||||
constructor: (options) ->
|
||||
super()
|
||||
options ?= {}
|
||||
@camera = options.camera
|
||||
@layer = options.layer
|
||||
console.error @toString(), "needs a camera." unless @camera
|
||||
console.error @toString(), "needs a layer." unless @layer
|
||||
@build()
|
||||
|
||||
toString: -> "<PlaybackoverScreen>"
|
||||
|
||||
build: ->
|
||||
@dimLayer = new createjs.Container()
|
||||
@dimLayer.mouseEnabled = @dimLayer.mouseChildren = false
|
||||
@dimLayer.layerIndex = -12
|
||||
@dimLayer.addChild @dimScreen = new createjs.Shape()
|
||||
@dimScreen.graphics.beginFill("rgba(0,0,0,0.4)").rect 0, 0, @camera.canvasWidth, @camera.canvasHeight
|
||||
@dimLayer.cache 0, 0, @camera.canvasWidth, @camera.canvasHeight
|
||||
@dimLayer.alpha = 0
|
||||
@layer.addChild @dimLayer
|
||||
|
||||
show: ->
|
||||
console.log 'show playback over screen', @showing
|
||||
return if @showing
|
||||
@showing = true
|
||||
|
||||
@dimLayer.alpha = 0
|
||||
createjs.Tween.removeTweens @dimLayer
|
||||
createjs.Tween.get(@dimLayer).to({alpha:1}, 500)
|
||||
|
||||
hide: ->
|
||||
console.log 'hide playback over screen', @showing
|
||||
return unless @showing
|
||||
@showing = false
|
||||
|
||||
createjs.Tween.removeTweens @dimLayer
|
||||
createjs.Tween.get(@dimLayer).to({alpha:0}, 500)
|
|
@ -5,6 +5,7 @@ module.exports = class PointChooser extends CocoClass
|
|||
super()
|
||||
@buildShape()
|
||||
@options.stage.addEventListener 'stagemousedown', @onMouseDown
|
||||
@options.camera.dragDisabled = true
|
||||
|
||||
destroy: ->
|
||||
@options.stage.removeEventListener 'stagemousedown', @onMouseDown
|
||||
|
|
|
@ -7,6 +7,7 @@ module.exports = class RegionChooser extends CocoClass
|
|||
@options.stage.addEventListener 'stagemousedown', @onMouseDown
|
||||
@options.stage.addEventListener 'stagemousemove', @onMouseMove
|
||||
@options.stage.addEventListener 'stagemouseup', @onMouseUp
|
||||
@options.camera.dragDisabled = true
|
||||
|
||||
destroy: ->
|
||||
@options.stage.removeEventListener 'stagemousedown', @onMouseDown
|
||||
|
|
|
@ -13,7 +13,7 @@ module.exports = class SpriteBoss extends CocoClass
|
|||
'bus:player-left': 'onPlayerLeft'
|
||||
'level-set-debug': 'onSetDebug'
|
||||
'level-highlight-sprites': 'onHighlightSprites'
|
||||
'sprite:mouse-down': 'onSpriteMouseDown'
|
||||
'sprite:mouse-up': 'onSpriteMouseUp'
|
||||
'surface:stage-mouse-down': 'onStageMouseDown'
|
||||
'level-select-sprite': 'onSelectSprite'
|
||||
'level-suppress-selection-sounds': 'onSuppressSelectionSounds'
|
||||
|
@ -21,9 +21,11 @@ module.exports = class SpriteBoss extends CocoClass
|
|||
'level:restarted': 'onLevelRestarted'
|
||||
'god:new-world-created': 'onNewWorld'
|
||||
'tome:cast-spells': 'onCastSpells'
|
||||
'camera:dragged': 'onCameraDragged'
|
||||
|
||||
constructor: (@options) ->
|
||||
super()
|
||||
@dragged = 0
|
||||
@options ?= {}
|
||||
@camera = @options.camera
|
||||
@surfaceLayer = @options.surfaceLayer
|
||||
|
@ -183,6 +185,11 @@ module.exports = class SpriteBoss extends CocoClass
|
|||
sprite.hasMoved = false
|
||||
@removeSprite sprite if missing
|
||||
@cache true if updateCache and @cached
|
||||
|
||||
# mainly for handling selecting thangs from session when the thang is not always in existence
|
||||
if @willSelectThang and @sprites[@willSelectThang[0]]
|
||||
@selectThang @willSelectThang...
|
||||
@willSelectThang = null
|
||||
|
||||
cache: (update=false) ->
|
||||
return if @cached and not update
|
||||
|
@ -206,11 +213,16 @@ module.exports = class SpriteBoss extends CocoClass
|
|||
|
||||
onNewWorld: (e) ->
|
||||
@world = @options.world = e.world
|
||||
@play()
|
||||
|
||||
onCastSpells: -> @stop()
|
||||
|
||||
play: ->
|
||||
sprite.imageObject.play() for thangID, sprite of @sprites
|
||||
@selectionMark?.play()
|
||||
@targetMark?.play()
|
||||
|
||||
onCastSpells: ->
|
||||
|
||||
stop: ->
|
||||
sprite.imageObject.stop() for thangID, sprite of @sprites
|
||||
@selectionMark?.stop()
|
||||
@targetMark?.stop()
|
||||
|
@ -226,8 +238,13 @@ module.exports = class SpriteBoss extends CocoClass
|
|||
onSelectSprite: (e) ->
|
||||
@selectThang e.thangID, e.spellName
|
||||
|
||||
onSpriteMouseDown: (e) ->
|
||||
onCameraDragged: ->
|
||||
@dragged += 1
|
||||
|
||||
onSpriteMouseUp: (e) ->
|
||||
return if key.shift and @options.choosing
|
||||
return @dragged = 0 if @dragged > 3
|
||||
@dragged = 0
|
||||
sprite = if e.sprite?.thang?.isSelectable then e.sprite else null
|
||||
@selectSprite e, sprite
|
||||
|
||||
|
@ -236,6 +253,7 @@ module.exports = class SpriteBoss extends CocoClass
|
|||
@selectSprite e if e.onBackground
|
||||
|
||||
selectThang: (thangID, spellName=null) ->
|
||||
return @willSelectThang = [thangID, spellName] unless @sprites[thangID]
|
||||
@selectSprite null, @sprites[thangID], spellName
|
||||
|
||||
selectSprite: (e, sprite=null, spellName=null) ->
|
||||
|
|
|
@ -9,6 +9,7 @@ Layer = require './Layer'
|
|||
Letterbox = require './Letterbox'
|
||||
Dimmer = require './Dimmer'
|
||||
CastingScreen = require './CastingScreen'
|
||||
PlaybackOverScreen = require './PlaybackOverScreen'
|
||||
DebugDisplay = require './DebugDisplay'
|
||||
CoordinateDisplay = require './CoordinateDisplay'
|
||||
SpriteBoss = require './SpriteBoss'
|
||||
|
@ -90,6 +91,7 @@ module.exports = Surface = class Surface extends CocoClass
|
|||
@chooser?.destroy()
|
||||
@dimmer?.destroy()
|
||||
@castingScreen?.destroy()
|
||||
@playbackOverScreen?.destroy()
|
||||
@stage.clear()
|
||||
@musicPlayer?.destroy()
|
||||
@stage.removeAllChildren()
|
||||
|
@ -179,7 +181,7 @@ module.exports = Surface = class Surface extends CocoClass
|
|||
container.addChild shape
|
||||
|
||||
setProgress: (progress, scrubDuration=500) ->
|
||||
progress = Math.max(Math.min(progress, 0.99), 0.0)
|
||||
progress = Math.max(Math.min(progress, 1), 0.0)
|
||||
|
||||
@scrubbing = true
|
||||
onTweenEnd = =>
|
||||
|
@ -193,7 +195,7 @@ module.exports = Surface = class Surface extends CocoClass
|
|||
createjs.Tween.removeTweens(@)
|
||||
@currentFrame = @scrubbingTo
|
||||
|
||||
@scrubbingTo = Math.floor(progress * @world.totalFrames)
|
||||
@scrubbingTo = Math.min(Math.floor(progress * @world.totalFrames), @world.totalFrames)
|
||||
@scrubbingPlaybackSpeed = Math.sqrt(Math.abs(@scrubbingTo - @currentFrame) * @world.dt / (scrubDuration or 0.5))
|
||||
if scrubDuration
|
||||
t = createjs.Tween
|
||||
|
@ -298,6 +300,18 @@ module.exports = Surface = class Surface extends CocoClass
|
|||
frame: @currentFrame
|
||||
world: @world
|
||||
)
|
||||
|
||||
if @lastFrame < @world.totalFrames and @currentFrame >= @world.totalFrames
|
||||
@spriteBoss.stop()
|
||||
@playbackOverScreen.show()
|
||||
@ended = true
|
||||
Backbone.Mediator.publish 'surface:playback-ended'
|
||||
else if @currentFrame < @world.totalFrames and @ended
|
||||
@spriteBoss.play()
|
||||
@playbackOverScreen.hide()
|
||||
@ended = false
|
||||
Backbone.Mediator.publish 'surface:playback-restarted'
|
||||
|
||||
@lastFrame = @currentFrame
|
||||
|
||||
onCastSpells: (event) ->
|
||||
|
@ -311,7 +325,10 @@ module.exports = Surface = class Surface extends CocoClass
|
|||
onNewWorld: (event) ->
|
||||
return unless event.world.name is @world.name
|
||||
@casting = false
|
||||
Backbone.Mediator.publish 'level-set-playing', { playing: @wasPlayingWhenCastingBegan }
|
||||
|
||||
# This has a tendency to break scripts that are waiting for playback to change when the level is loaded
|
||||
# so only run it after the first world is created.
|
||||
Backbone.Mediator.publish 'level-set-playing', { playing: @wasPlayingWhenCastingBegan } unless event.firstWorld
|
||||
|
||||
fastForwardTo = null
|
||||
if @playing
|
||||
|
@ -350,6 +367,7 @@ module.exports = Surface = class Surface extends CocoClass
|
|||
@screenLayer.addChild new Letterbox canvasWidth: canvasWidth, canvasHeight: canvasHeight
|
||||
@spriteBoss = new SpriteBoss camera: @camera, surfaceLayer: @surfaceLayer, surfaceTextLayer: @surfaceTextLayer, world: @world, thangTypes: @options.thangTypes, choosing: @options.choosing, navigateToSelection: @options.navigateToSelection, showInvisible: @options.showInvisible
|
||||
@castingScreen ?= new CastingScreen camera: @camera, layer: @screenLayer
|
||||
@playbackOverScreen ?= new PlaybackOverScreen camera: @camera, layer: @screenLayer
|
||||
@stage.enableMouseOver(10)
|
||||
@stage.addEventListener 'stagemousemove', @onMouseMove
|
||||
@stage.addEventListener 'stagemousedown', @onMouseDown
|
||||
|
|
|
@ -37,7 +37,7 @@ module.exports = class WizardSprite extends IndieSprite
|
|||
makeIndieThang: (thangType, thangID, pos) ->
|
||||
thang = super thangType, thangID, pos
|
||||
thang.isSelectable = false
|
||||
thang.bobHeight = 1.5
|
||||
thang.bobHeight = 0.75
|
||||
thang.bobTime = 2
|
||||
thang.pos.z += thang.bobHeight
|
||||
thang
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi
|
|||
# gravatar_profile_link: "Full Gravatar Profile"
|
||||
|
||||
# play_level:
|
||||
# level_load_error: "Level could not be loaded."
|
||||
# level_load_error: "Level could not be loaded: "
|
||||
# done: "Done"
|
||||
# grid: "Grid"
|
||||
# customize_wizard: "Customize Wizard"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "български език", englishDescri
|
|||
# gravatar_profile_link: "Full Gravatar Profile"
|
||||
|
||||
play_level:
|
||||
level_load_error: "Нивото не може да бъде заредено."
|
||||
level_load_error: "Нивото не може да бъде заредено: "
|
||||
done: "Готово"
|
||||
# grid: "Grid"
|
||||
# customize_wizard: "Customize Wizard"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "čeština", englishDescription: "Czech", tr
|
|||
gravatar_profile_link: "Účet Gravatar"
|
||||
|
||||
play_level:
|
||||
level_load_error: "Úroveň se nepodařilo otevřít."
|
||||
level_load_error: "Úroveň se nepodařilo otevřít: "
|
||||
done: "Hotovo"
|
||||
grid: "Mřížka"
|
||||
customize_wizard: "Upravit Kouzelníka"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans
|
|||
gravatar_profile_link: "Fuld Gravatar Profil"
|
||||
|
||||
play_level:
|
||||
level_load_error: "Banen kunne ikke indlæses."
|
||||
level_load_error: "Banen kunne ikke indlæses: "
|
||||
done: "Færdig"
|
||||
grid: "Gitter"
|
||||
customize_wizard: "Tilpas troldmand"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "Deutsch", englishDescription: "German", tra
|
|||
gravatar_profile_link: "Gravatar Profil"
|
||||
|
||||
play_level:
|
||||
level_load_error: "Level konnte nicht geladen werden."
|
||||
level_load_error: "Level konnte nicht geladen werden: "
|
||||
done: "Fertig"
|
||||
grid: "Raster"
|
||||
customize_wizard: "Bearbeite den Zauberer"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "ελληνικά", englishDescription: "Gre
|
|||
gravatar_profile_link: "Πλήρη προφίλ \"Gravatar\""
|
||||
|
||||
play_level:
|
||||
level_load_error: "Το επίπεδο δεν μπόρεσε να φορτωθεί"
|
||||
level_load_error: "Το επίπεδο δεν μπόρεσε να φορτωθεί: "
|
||||
done: "Έτοιμο"
|
||||
grid: "Πλέγμα"
|
||||
customize_wizard: "Προσαρμόστε τον Μάγο"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English
|
|||
# gravatar_profile_link: "Full Gravatar Profile"
|
||||
|
||||
# play_level:
|
||||
# level_load_error: "Level could not be loaded."
|
||||
# level_load_error: "Level could not be loaded: "
|
||||
# done: "Done"
|
||||
# grid: "Grid"
|
||||
# customize_wizard: "Customize Wizard"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English
|
|||
# gravatar_profile_link: "Full Gravatar Profile"
|
||||
|
||||
# play_level:
|
||||
# level_load_error: "Level could not be loaded."
|
||||
# level_load_error: "Level could not be loaded: "
|
||||
# done: "Done"
|
||||
# grid: "Grid"
|
||||
# customize_wizard: "Customize Wizard"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English
|
|||
# gravatar_profile_link: "Full Gravatar Profile"
|
||||
|
||||
# play_level:
|
||||
# level_load_error: "Level could not be loaded."
|
||||
# level_load_error: "Level could not be loaded: "
|
||||
# done: "Done"
|
||||
# grid: "Grid"
|
||||
# customize_wizard: "Customize Wizard"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "English", englishDescription: "English", tr
|
|||
gravatar_profile_link: "Full Gravatar Profile"
|
||||
|
||||
play_level:
|
||||
level_load_error: "Level could not be loaded."
|
||||
level_load_error: "Level could not be loaded: "
|
||||
done: "Done"
|
||||
grid: "Grid"
|
||||
customize_wizard: "Customize Wizard"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "español (América Latina)", englishDescrip
|
|||
gravatar_profile_link: "Perfil Gravatar Completo"
|
||||
|
||||
play_level:
|
||||
level_load_error: "El nivel no puede ser cargado."
|
||||
level_load_error: "El nivel no puede ser cargado: "
|
||||
done: "Listo"
|
||||
grid: "Cuadricula"
|
||||
customize_wizard: "Personalizar Hechicero"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis
|
|||
gravatar_profile_link: "Perfil de Gravatar completo"
|
||||
|
||||
play_level:
|
||||
level_load_error: "No se pudo cargar el nivel."
|
||||
level_load_error: "No se pudo cargar el nivel: "
|
||||
done: "Hecho"
|
||||
grid: "Cuadrícrula"
|
||||
customize_wizard: "Personalizar Mago"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "español", englishDescription: "Spanish", t
|
|||
gravatar_profile_link: "Prefil de Gravatar completo"
|
||||
|
||||
play_level:
|
||||
level_load_error: "No se pudo cargar el nivel."
|
||||
level_load_error: "No se pudo cargar el nivel: "
|
||||
done: "Hecho"
|
||||
grid: "Cuadrícrula"
|
||||
customize_wizard: "Personalizar Mago"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian",
|
|||
# gravatar_profile_link: "Full Gravatar Profile"
|
||||
|
||||
# play_level:
|
||||
# level_load_error: "Level could not be loaded."
|
||||
# level_load_error: "Level could not be loaded: "
|
||||
# done: "Done"
|
||||
# grid: "Grid"
|
||||
# customize_wizard: "Customize Wizard"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "suomi", englishDescription: "Finnish", tran
|
|||
# gravatar_profile_link: "Full Gravatar Profile"
|
||||
|
||||
# play_level:
|
||||
# level_load_error: "Level could not be loaded."
|
||||
# level_load_error: "Level could not be loaded: "
|
||||
# done: "Done"
|
||||
# grid: "Grid"
|
||||
# customize_wizard: "Customize Wizard"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
|
|||
gravatar_profile_link: "Profil Gravatar complet"
|
||||
|
||||
play_level:
|
||||
level_load_error: "Le niveau ne peut pas être chargé."
|
||||
level_load_error: "Le niveau ne peut pas être chargé: "
|
||||
done: "Fait"
|
||||
grid: "Grille"
|
||||
customize_wizard: "Personnaliser le magicien"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "עברית", englishDescription: "Hebrew",
|
|||
# gravatar_profile_link: "Full Gravatar Profile"
|
||||
|
||||
# play_level:
|
||||
# level_load_error: "Level could not be loaded."
|
||||
# level_load_error: "Level could not be loaded: "
|
||||
# done: "Done"
|
||||
# grid: "Grid"
|
||||
# customize_wizard: "Customize Wizard"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe
|
|||
# gravatar_profile_link: "Full Gravatar Profile"
|
||||
|
||||
# play_level:
|
||||
# level_load_error: "Level could not be loaded."
|
||||
# level_load_error: "Level could not be loaded: "
|
||||
# done: "Done"
|
||||
# grid: "Grid"
|
||||
# customize_wizard: "Customize Wizard"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t
|
|||
# gravatar_profile_link: "Full Gravatar Profile"
|
||||
|
||||
play_level:
|
||||
level_load_error: "A pályát nem sikerült betölteni."
|
||||
level_load_error: "A pályát nem sikerült betölteni: "
|
||||
done: "Kész"
|
||||
grid: "Rács"
|
||||
customize_wizard: "Varázsló testreszabása"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind
|
|||
# gravatar_profile_link: "Full Gravatar Profile"
|
||||
|
||||
# play_level:
|
||||
# level_load_error: "Level could not be loaded."
|
||||
# level_load_error: "Level could not be loaded: "
|
||||
# done: "Done"
|
||||
# grid: "Grid"
|
||||
# customize_wizard: "Customize Wizard"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "italiano", englishDescription: "Italian", t
|
|||
gravatar_profile_link: "Profilo Gravatar completo"
|
||||
|
||||
play_level:
|
||||
level_load_error: "Il livello non può essere caricato."
|
||||
level_load_error: "Il livello non può essere caricato: "
|
||||
done: "Fatto"
|
||||
grid: "Griglia"
|
||||
customize_wizard: "Personalizza stregone"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese",
|
|||
# gravatar_profile_link: "Full Gravatar Profile"
|
||||
|
||||
play_level:
|
||||
level_load_error: "レベルがロード出来ませんでした。"
|
||||
level_load_error: "レベルがロード出来ませんでした: "
|
||||
done: "完了"
|
||||
grid: "グリッド"
|
||||
customize_wizard: "魔法使いの設定"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t
|
|||
# gravatar_profile_link: "Full Gravatar Profile"
|
||||
|
||||
# play_level:
|
||||
# level_load_error: "Level could not be loaded."
|
||||
# level_load_error: "Level could not be loaded: "
|
||||
# done: "Done"
|
||||
# grid: "Grid"
|
||||
# customize_wizard: "Customize Wizard"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English
|
|||
# gravatar_profile_link: "Full Gravatar Profile"
|
||||
|
||||
# play_level:
|
||||
# level_load_error: "Level could not be loaded."
|
||||
# level_load_error: "Level could not be loaded: "
|
||||
# done: "Done"
|
||||
# grid: "Grid"
|
||||
# customize_wizard: "Customize Wizard"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa
|
|||
# gravatar_profile_link: "Full Gravatar Profile"
|
||||
|
||||
# play_level:
|
||||
# level_load_error: "Level could not be loaded."
|
||||
# level_load_error: "Level could not be loaded: "
|
||||
# done: "Done"
|
||||
# grid: "Grid"
|
||||
# customize_wizard: "Customize Wizard"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "Norsk Bokmål", englishDescription: "Norweg
|
|||
gravatar_profile_link: "Full Gravatar Profil"
|
||||
|
||||
play_level:
|
||||
level_load_error: "Nivået kunne ikke bli lastet."
|
||||
level_load_error: "Nivået kunne ikke bli lastet: "
|
||||
done: "Ferdig"
|
||||
grid: "Grid"
|
||||
customize_wizard: "Spesiallag Trollmann"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "Nederlands", englishDescription: "Dutch", t
|
|||
gravatar_profile_link: "Volledig Gravatar Profiel"
|
||||
|
||||
play_level:
|
||||
level_load_error: "Level kon niet geladen worden."
|
||||
level_load_error: "Level kon niet geladen worden: "
|
||||
done: "Klaar"
|
||||
grid: "Raster"
|
||||
customize_wizard: "Pas Tovenaar aan"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English
|
|||
# gravatar_profile_link: "Full Gravatar Profile"
|
||||
|
||||
# play_level:
|
||||
# level_load_error: "Level could not be loaded."
|
||||
# level_load_error: "Level could not be loaded: "
|
||||
# done: "Done"
|
||||
# grid: "Grid"
|
||||
# customize_wizard: "Customize Wizard"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr
|
|||
gravatar_profile_link: "Full Gravatar Profil"
|
||||
|
||||
play_level:
|
||||
level_load_error: "Nivået kunne ikke bli lastet."
|
||||
level_load_error: "Nivået kunne ikke bli lastet: "
|
||||
done: "Ferdig"
|
||||
grid: "Grid"
|
||||
customize_wizard: "Spesiallag Trollmann"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish
|
|||
gravatar_profile_link: "Profil Gravatar"
|
||||
|
||||
play_level:
|
||||
level_load_error: "Nie udało się wczytać poziomu."
|
||||
level_load_error: "Nie udało się wczytać poziomu: "
|
||||
done: "Zrobione"
|
||||
grid: "Siatka"
|
||||
customize_wizard: "Spersonalizuj Czarodzieja"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "português do Brasil", englishDescription:
|
|||
gravatar_profile_link: "Perfil Completo do Gravatar"
|
||||
|
||||
play_level:
|
||||
level_load_error: "O estágio não pôde ser carregado."
|
||||
level_load_error: "O estágio não pôde ser carregado: "
|
||||
done: "Pronto"
|
||||
grid: "Grade"
|
||||
customize_wizard: "Personalize o feiticeiro"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "Português europeu", englishDescription: "P
|
|||
gravatar_profile_link: "Perfil Gravatar completo"
|
||||
|
||||
play_level:
|
||||
level_load_error: "O nível não pôde ser carregado."
|
||||
level_load_error: "O nível não pôde ser carregado: "
|
||||
done: "Concluir"
|
||||
grid: "Grelha"
|
||||
customize_wizard: "Personalizar Feiticeiro"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "português", englishDescription: "Portugues
|
|||
gravatar_profile_link: "Perfil Completo do Gravatar"
|
||||
|
||||
play_level:
|
||||
level_load_error: "O estágio não pôde ser carregado."
|
||||
level_load_error: "O estágio não pôde ser carregado: "
|
||||
done: "Pronto"
|
||||
grid: "Grade"
|
||||
customize_wizard: "Personalize o feiticeiro"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman
|
|||
# gravatar_profile_link: "Full Gravatar Profile"
|
||||
|
||||
# play_level:
|
||||
# level_load_error: "Level could not be loaded."
|
||||
# level_load_error: "Level could not be loaded: "
|
||||
# done: "Done"
|
||||
# grid: "Grid"
|
||||
# customize_wizard: "Customize Wizard"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi
|
|||
gravatar_profile_link: "Полный профиль на Gravatar"
|
||||
|
||||
play_level:
|
||||
level_load_error: "Уровень не может быть загружен."
|
||||
level_load_error: "Уровень не может быть загружен: "
|
||||
done: "Готово"
|
||||
grid: "Сетка"
|
||||
customize_wizard: "Настройки волшебника"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak",
|
|||
# gravatar_profile_link: "Full Gravatar Profile"
|
||||
|
||||
# play_level:
|
||||
# level_load_error: "Level could not be loaded."
|
||||
# level_load_error: "Level could not be loaded: "
|
||||
# done: "Done"
|
||||
# grid: "Grid"
|
||||
# customize_wizard: "Customize Wizard"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven
|
|||
# gravatar_profile_link: "Full Gravatar Profile"
|
||||
|
||||
# play_level:
|
||||
# level_load_error: "Level could not be loaded."
|
||||
# level_load_error: "Level could not be loaded: "
|
||||
# done: "Done"
|
||||
# grid: "Grid"
|
||||
# customize_wizard: "Customize Wizard"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian
|
|||
gravatar_profile_link: "Цео Граватар налог"
|
||||
|
||||
play_level:
|
||||
level_load_error: "Ниво није могао бити учитан."
|
||||
level_load_error: "Ниво није могао бити учитан: "
|
||||
done: "Урађено"
|
||||
grid: "Мрежа"
|
||||
customize_wizard: "Прилагоди Чаробњака"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr
|
|||
gravatar_profile_link: "Hela Gravatar profilen"
|
||||
|
||||
play_level:
|
||||
level_load_error: "Nivån kunde inte laddas."
|
||||
level_load_error: "Nivån kunde inte laddas: "
|
||||
done: "Klar"
|
||||
grid: "Rutnät"
|
||||
customize_wizard: "Finjustera Trollkarl"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra
|
|||
# gravatar_profile_link: "Full Gravatar Profile"
|
||||
|
||||
play_level:
|
||||
# level_load_error: "Level could not be loaded."
|
||||
# level_load_error: "Level could not be loaded: "
|
||||
done: "เสร็จสิ้น"
|
||||
# grid: "Grid"
|
||||
# customize_wizard: "Customize Wizard"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t
|
|||
gravatar_profile_link: "Tam Gravatar Profili"
|
||||
|
||||
play_level:
|
||||
level_load_error: "Seviye yüklenemedi"
|
||||
level_load_error: "Seviye yüklenemedi: "
|
||||
done: "Tamamdır"
|
||||
grid: "Harita Bölmeleri"
|
||||
customize_wizard: "Sihirbazı Düzenle"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "українська мова", englishDesc
|
|||
gravatar_profile_link: "Повний профіль Gravatar"
|
||||
|
||||
play_level:
|
||||
level_load_error: "Неможливо завантажити рівень."
|
||||
level_load_error: "Неможливо завантажити рівень: "
|
||||
done: "Готово"
|
||||
grid: "Решітка"
|
||||
customize_wizard: "Налаштування персонажа"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu",
|
|||
# gravatar_profile_link: "Full Gravatar Profile"
|
||||
|
||||
# play_level:
|
||||
# level_load_error: "Level could not be loaded."
|
||||
# level_load_error: "Level could not be loaded: "
|
||||
# done: "Done"
|
||||
# grid: "Grid"
|
||||
# customize_wizard: "Customize Wizard"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn
|
|||
# gravatar_profile_link: "Full Gravatar Profile"
|
||||
|
||||
# play_level:
|
||||
# level_load_error: "Level could not be loaded."
|
||||
# level_load_error: "Level could not be loaded: "
|
||||
# done: "Done"
|
||||
# grid: "Grid"
|
||||
# customize_wizard: "Customize Wizard"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
|
|||
gravatar_profile_link: "完善 Gravatar 资料"
|
||||
|
||||
play_level:
|
||||
level_load_error: "关卡不能载入。"
|
||||
level_load_error: "关卡不能载入: "
|
||||
done: "完成"
|
||||
grid: "格子"
|
||||
customize_wizard: "自定义向导"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "繁体中文", englishDescription: "Chinese
|
|||
gravatar_profile_link: "完善 Gravatar 資料"
|
||||
|
||||
play_level:
|
||||
level_load_error: "載入關卡時發生錯誤。"
|
||||
level_load_error: "載入關卡時發生錯誤: "
|
||||
done: "完成"
|
||||
grid: "格子"
|
||||
customize_wizard: "自定義巫師"
|
||||
|
|
|
@ -158,7 +158,7 @@ module.exports = nativeDescription: "中文", englishDescription: "Chinese", tra
|
|||
# gravatar_profile_link: "Full Gravatar Profile"
|
||||
|
||||
# play_level:
|
||||
# level_load_error: "Level could not be loaded."
|
||||
# level_load_error: "Level could not be loaded: "
|
||||
# done: "Done"
|
||||
# grid: "Grid"
|
||||
# customize_wizard: "Customize Wizard"
|
||||
|
|
|
@ -38,3 +38,6 @@
|
|||
color: black
|
||||
text-shadow: 0 1px 0 white
|
||||
|
||||
.alert-warning h2
|
||||
color: black
|
||||
text-align: center
|
|
@ -3,6 +3,10 @@
|
|||
left: 10px
|
||||
top: 42px
|
||||
background-color: rgba(200,200,200,0.8)
|
||||
|
||||
&.brighter
|
||||
background-color: rgba(200,200,200,1.0)
|
||||
|
||||
border: black
|
||||
padding: 5px 7px 5px 5px
|
||||
box-sizing: border-box
|
||||
|
|
2
app/styles/play/level/modal/docs.sass
Normal file
2
app/styles/play/level/modal/docs.sass
Normal file
|
@ -0,0 +1,2 @@
|
|||
#docs-modal .modal-dialog
|
||||
width: 800px
|
|
@ -2,6 +2,18 @@ extends /templates/base
|
|||
|
||||
block content
|
||||
|
||||
h3 Espionage mode
|
||||
h5 Please enter the email/username of the person you want to spy on
|
||||
.form
|
||||
.form-group
|
||||
label.control-label Email
|
||||
input#user-email
|
||||
.form-group
|
||||
label.control-label Username
|
||||
input#user-username
|
||||
|
||||
button.btn.btn-primary.btn-large#enter-espionage-mode 007
|
||||
|
||||
h3(data-i18n="admin.av_title") Admin Views
|
||||
|
||||
h4(data-i18n="admin.av_entities_sub_title") Entities
|
||||
|
|
|
@ -2,6 +2,12 @@ extends /templates/base
|
|||
|
||||
block content
|
||||
|
||||
if notFound
|
||||
div(class="alert alert-warning")
|
||||
h2
|
||||
span(data-i18n="play_level.level_load_error") Level could not be loaded:
|
||||
| #{notFound}
|
||||
|
||||
h1(data-i18n="play.choose_your_level") Choose Your Level
|
||||
p
|
||||
span(data-i18n="play.adventurer_prefix") You can jump to any level below, or discuss the levels on
|
||||
|
|
|
@ -7,7 +7,7 @@ block content
|
|||
!{description}
|
||||
|
||||
if !me.get('anonymous')
|
||||
a(href="http://www.youtube.com/watch?v=IFvfZiJGDsw&list=HL1392928835&feature=mh_lolz").intro-button.btn.btn-primary.btn-lg Watch the Video
|
||||
//a(href="http://www.youtube.com/watch?v=IFvfZiJGDsw&list=HL1392928835&feature=mh_lolz").intro-button.btn.btn-primary.btn-lg Watch the Video
|
||||
|
||||
a(href="/play/level/ladder-tutorial").intro-button.btn.btn-primary.btn-lg Play the Tutorial
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
h4.home
|
||||
|
||||
a(href="/")
|
||||
a(href=homeLink || "/")
|
||||
i.icon-home.icon-white
|
||||
span(data-i18n="play_level.home") Home
|
||||
|
||||
h4.title #{worldName}
|
||||
|
||||
button.btn.btn-xs.btn-inverse.banner#docs-button(title="Show level instructions", data-i18n="play_level.guide") Guide
|
||||
button.btn.btn-xs.btn-success.banner#docs-button(title="Show level instructions", data-i18n="play_level.guide") Guide
|
||||
|
||||
if ladderGame
|
||||
button.btn.btn-xs.btn-inverse.banner#multiplayer-button(title="Leaderboard", data-i18n="play_level.leaderboard") Leaderboard
|
||||
|
|
|
@ -38,12 +38,13 @@ class LiveEditingMarkup extends TreemaNode.nodeMap.ace
|
|||
url: InkBlob.url
|
||||
filename: InkBlob.filename
|
||||
mimetype: InkBlob.mimetype
|
||||
description: ''
|
||||
createdFor: []
|
||||
path: @settings.filePath
|
||||
|
||||
@uploadingPath = [@settings.filePath, InkBlob.filename].join('/')
|
||||
$.ajax('/file', { type: 'POST', data: body, success: @onFileUploaded })
|
||||
|
||||
onFileUploaded: (e) =>
|
||||
@editor.insert "![#{e.metadata.name}](/file/#{e._id})"
|
||||
@editor.insert "![#{e.metadata.name}](/file/#{@uploadingPath})"
|
||||
|
||||
onEditorChange: =>
|
||||
@saveChanges()
|
||||
|
|
|
@ -1,6 +1,35 @@
|
|||
{backboneFailure, genericFailure} = require 'lib/errors'
|
||||
View = require 'views/kinds/RootView'
|
||||
template = require 'templates/admin'
|
||||
storage = require 'lib/storage'
|
||||
|
||||
module.exports = class AdminView extends View
|
||||
id: "admin-view"
|
||||
template: template
|
||||
|
||||
events:
|
||||
'click #enter-espionage-mode': 'enterEspionageMode'
|
||||
|
||||
enterEspionageMode: ->
|
||||
userEmail = $("#user-email").val().toLowerCase()
|
||||
username = $("#user-username").val().toLowerCase()
|
||||
|
||||
userIdentifier = userEmail || username
|
||||
postData =
|
||||
usernameLower: username
|
||||
emailLower: userEmail
|
||||
|
||||
$.ajax
|
||||
type: "POST",
|
||||
url: "/auth/spy"
|
||||
data: postData
|
||||
success: @espionageSuccess
|
||||
error: @espionageFailure
|
||||
|
||||
espionageSuccess: (model) ->
|
||||
storage.save('whoami',model)
|
||||
window.location.reload()
|
||||
espionageFailure: (jqxhr, status,error)->
|
||||
console.log "There was an error entering espionage mode: #{error}"
|
||||
|
||||
|
|
@ -35,6 +35,7 @@ module.exports = class ArticleEditView extends View
|
|||
data = $.extend(true, {}, @article.attributes)
|
||||
options =
|
||||
data: data
|
||||
filePath: "db/thang.type/#{@article.get('original')}"
|
||||
schema: Article.schema.attributes
|
||||
callbacks:
|
||||
change: @pushChangesToPreview
|
||||
|
|
|
@ -144,6 +144,7 @@ module.exports = class ThangsTabView extends View
|
|||
@surface.playing = false
|
||||
@surface.setWorld @world
|
||||
@surface.camera.zoomTo({x:262, y:-164}, 1.66, 0)
|
||||
@surface.camera.dragDisabled = true
|
||||
|
||||
destroy: ->
|
||||
@selectAddThangType null
|
||||
|
|
|
@ -47,12 +47,17 @@ module.exports = class ControlBarView extends View
|
|||
text += " (#{numPlayers})" if numPlayers > 1
|
||||
$('#multiplayer-button', @$el).text(text)
|
||||
|
||||
getRenderData: (context={}) ->
|
||||
super context
|
||||
context.worldName = @worldName
|
||||
context.multiplayerEnabled = @session.get('multiplayer')
|
||||
context.ladderGame = @ladderGame
|
||||
context
|
||||
getRenderData: (c={}) ->
|
||||
super c
|
||||
c.worldName = @worldName
|
||||
c.multiplayerEnabled = @session.get('multiplayer')
|
||||
c.ladderGame = @ladderGame
|
||||
c.homeLink = "/"
|
||||
levelID = @level.get('slug')
|
||||
if levelID in ["project-dota", "brawlwood", "ladder-tutorial"]
|
||||
levelID = 'project-dota' if levelID is 'ladder-tutorial'
|
||||
c.homeLink = "/play/ladder/" + levelID
|
||||
c
|
||||
|
||||
showGuideModal: ->
|
||||
options = {docs: @level.get('documentation'), supermodel: @supermodel}
|
||||
|
|
|
@ -14,6 +14,8 @@ module.exports = class GoalsView extends View
|
|||
subscriptions:
|
||||
'goal-manager:new-goal-states': 'onNewGoalStates'
|
||||
'level-set-letterbox': 'onSetLetterbox'
|
||||
'surface:playback-restarted': 'onSurfacePlaybackRestarted'
|
||||
'surface:playback-ended': 'onSurfacePlaybackEnded'
|
||||
|
||||
events:
|
||||
'click': 'toggleCollapse'
|
||||
|
@ -48,6 +50,12 @@ module.exports = class GoalsView extends View
|
|||
goals.push goal
|
||||
@$el.removeClass('secret') if goals.length > 0
|
||||
|
||||
onSurfacePlaybackRestarted: ->
|
||||
@$el.removeClass 'brighter'
|
||||
|
||||
onSurfacePlaybackEnded: ->
|
||||
@$el.addClass 'brighter'
|
||||
|
||||
render: ->
|
||||
super()
|
||||
@$el.addClass('secret').addClass('expanded')
|
||||
|
|
|
@ -66,8 +66,9 @@ module.exports = class HUDView extends View
|
|||
@clearSpeaker()
|
||||
|
||||
onNewWorld: (e) ->
|
||||
hadThang = @thang
|
||||
@thang = e.world.thangMap[@thang.id] if @thang
|
||||
if not @thang
|
||||
if hadThang and not @thang
|
||||
@setThang null, null
|
||||
|
||||
setThang: (thang, thangType) ->
|
||||
|
@ -269,7 +270,7 @@ module.exports = class HUDView extends View
|
|||
if prop is "rotation"
|
||||
return (val * 180 / Math.PI).toFixed(0) + "˚"
|
||||
if typeof val is 'number'
|
||||
if Math.round(val) == val then return val.toFixed(0) # int
|
||||
if Math.round(val) == val or prop is 'gold' then return val.toFixed(0) # int
|
||||
if -10 < val < 10 then return val.toFixed(2)
|
||||
if -100 < val < 100 then return val.toFixed(1)
|
||||
return val.toFixed(0)
|
||||
|
@ -341,4 +342,5 @@ module.exports = class HUDView extends View
|
|||
@addMoreMessage = null
|
||||
@animateEnterButton = null
|
||||
clearInterval(@messageInterval) if @messageInterval
|
||||
clearTimeout @hintNextSelectionTimeout if @hintNextSelectionTimeout
|
||||
super()
|
||||
|
|
|
@ -6,6 +6,7 @@ Article = require 'models/Article'
|
|||
|
||||
module.exports = class DocsModal extends View
|
||||
template: template
|
||||
id: 'docs-modal'
|
||||
|
||||
shortcuts:
|
||||
'enter': 'hide'
|
||||
|
|
|
@ -82,11 +82,20 @@ module.exports = class DebugView extends View
|
|||
else
|
||||
@$el.hide()
|
||||
if @variableChain?.length is 2
|
||||
Backbone.Mediator.publish 'tome:spell-debug-property-hovered', property: @variableChain[1], owner: @variableChain[0]
|
||||
clearTimeout @hoveredPropertyTimeout if @hoveredPropertyTimeout
|
||||
@hoveredPropertyTimeout = _.delay @notifyPropertyHovered, 500
|
||||
else
|
||||
Backbone.Mediator.publish 'tome:spell-debug-property-hovered', property: null
|
||||
@notifyPropertyHovered()
|
||||
@updateMarker()
|
||||
|
||||
notifyPropertyHovered: =>
|
||||
clearTimeout @hoveredPropertyTimeout if @hoveredPropertyTimeout
|
||||
@hoveredPropertyTimeout = null
|
||||
oldHoveredProperty = @hoveredProperty
|
||||
@hoveredProperty = if @variableChain?.length is 2 then owner: @variableChain[0], property: @variableChain[1] else {}
|
||||
unless _.isEqual oldHoveredProperty, @hoveredProperty
|
||||
Backbone.Mediator.publish 'tome:spell-debug-property-hovered', @hoveredProperty
|
||||
|
||||
updateMarker: ->
|
||||
if @marker
|
||||
@ace.getSession().removeMarker @marker
|
||||
|
|
|
@ -97,8 +97,7 @@ module.exports = class PlayLevelView extends View
|
|||
localStorage["lastLevel"] = @levelID
|
||||
|
||||
onLevelLoadError: (e) =>
|
||||
msg = $.i18n.t('play_level.level_load_error', defaultValue: "Level could not be loaded.")
|
||||
@$el.html('<div class="alert">' + msg + '</div>')
|
||||
application.router.navigate "/play?not_found=#{@levelID}", {trigger: true}
|
||||
|
||||
setLevel: (@level, @supermodel) ->
|
||||
@god?.level = @level.serialize @supermodel
|
||||
|
@ -136,7 +135,7 @@ module.exports = class PlayLevelView extends View
|
|||
team = @getQueryVariable("team") ? @world.teamForPlayer(0)
|
||||
|
||||
opponentSpells = []
|
||||
for spellTeam, spells of @session.get('teamSpells') or {}
|
||||
for spellTeam, spells of @session.get('teamSpells') ? otherSession?.get('teamSpells') ? {}
|
||||
continue if spellTeam is team or not team
|
||||
opponentSpells = opponentSpells.concat spells
|
||||
|
||||
|
@ -144,8 +143,10 @@ module.exports = class PlayLevelView extends View
|
|||
opponentCode = otherSession?.get('submittedCode') or {}
|
||||
myCode = @session.get('code') or {}
|
||||
for spell in opponentSpells
|
||||
c = opponentCode[spell]
|
||||
if c then myCode[spell] = c else delete myCode[spell]
|
||||
[thang, spell] = spell.split '/'
|
||||
c = opponentCode[thang]?[spell]
|
||||
myCode[thang] ?= {}
|
||||
if c then myCode[thang][spell] = c else delete myCode[thang][spell]
|
||||
@session.set('code', myCode)
|
||||
if @session.get('multiplayer') and otherSession?
|
||||
# For now, ladderGame will disallow multiplayer, because session code combining doesn't play nice yet.
|
||||
|
@ -393,7 +394,7 @@ module.exports = class PlayLevelView extends View
|
|||
register: ->
|
||||
@bus = LevelBus.get(@levelID, @session.id)
|
||||
@bus.setSession(@session)
|
||||
@bus.setTeamSpellMap @tome.teamSpellMap
|
||||
@bus.setSpells @tome.spells
|
||||
@bus.connect() if @session.get('multiplayer')
|
||||
|
||||
onSessionWillSave: (e) ->
|
||||
|
@ -423,7 +424,7 @@ module.exports = class PlayLevelView extends View
|
|||
AudioPlayer.preloadSoundReference sound
|
||||
|
||||
destroy: ->
|
||||
@supermodel.off 'error', @onLevelLoadError
|
||||
@supermodel?.off 'error', @onLevelLoadError
|
||||
@levelLoader?.off 'loaded-all', @onLevelLoaderLoaded
|
||||
@levelLoader?.destroy()
|
||||
@surface?.destroy()
|
||||
|
@ -436,7 +437,7 @@ module.exports = class PlayLevelView extends View
|
|||
@bus?.destroy()
|
||||
#@instance.save() unless @instance.loading
|
||||
console.profileEnd?() if PROFILE_ME
|
||||
@session.off 'change:multiplayer', @onMultiplayerChanged, @
|
||||
@session?.off 'change:multiplayer', @onMultiplayerChanged, @
|
||||
@onLevelLoadError = null
|
||||
@onLevelLoaderLoaded = null
|
||||
@onSupermodelLoadedOne = null
|
||||
|
|
|
@ -8,7 +8,7 @@ module.exports = class PlayView extends View
|
|||
getRenderData: (context={}) ->
|
||||
context = super(context)
|
||||
context.home = true
|
||||
|
||||
context.notFound = @getQueryVariable 'not_found'
|
||||
tutorials = [
|
||||
{
|
||||
name: 'Rescue Mission'
|
||||
|
|
|
@ -62,7 +62,6 @@
|
|||
"sendwithus": "2.0.x",
|
||||
"aws-sdk":"~2.0.0",
|
||||
"bayesian-battle":"0.0.x",
|
||||
"hiredis":"",
|
||||
"redis": ""
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -77,7 +76,7 @@
|
|||
"clean-css-brunch": "> 1.0 < 1.8",
|
||||
"auto-reload-brunch": "> 1.0 < 1.8",
|
||||
"brunch": "~1.7.4",
|
||||
"jasmine-node": "1.12.x",
|
||||
"jasmine-node": "1.13.x",
|
||||
"nodemon": "0.7.5",
|
||||
"marked": "0.2.x",
|
||||
"telepath-brunch": "https://github.com/nwinter/telepath-brunch/tarball/master",
|
||||
|
|
|
@ -16,7 +16,7 @@ module.exports = class Handler
|
|||
# subclasses should override these methods
|
||||
hasAccess: (req) -> true
|
||||
hasAccessToDocument: (req, document, method=null) ->
|
||||
return true if req.user.isAdmin()
|
||||
return true if req.user?.isAdmin()
|
||||
if @modelClass.schema.uses_coco_permissions
|
||||
return document.hasPermissionsForMethod(req.user, method or req.method)
|
||||
return true
|
||||
|
@ -32,7 +32,7 @@ module.exports = class Handler
|
|||
# can only edit permissions if this is a brand new property,
|
||||
# or you are an owner of the old one
|
||||
isOwner = document.getAccessForUserObjectId(req.user._id) is 'owner'
|
||||
if isBrandNew or isOwner or req.user.isAdmin()
|
||||
if isBrandNew or isOwner or req.user?.isAdmin()
|
||||
props.push 'permissions'
|
||||
|
||||
if @modelClass.schema.uses_coco_versions
|
||||
|
@ -57,7 +57,7 @@ module.exports = class Handler
|
|||
# generic handlers
|
||||
get: (req, res) ->
|
||||
# by default, ordinary users never get unfettered access to the database
|
||||
return @sendUnauthorizedError(res) unless req.user.isAdmin()
|
||||
return @sendUnauthorizedError(res) unless req.user?.isAdmin()
|
||||
|
||||
# admins can send any sort of query down the wire, though
|
||||
conditions = JSON.parse(req.query.conditions || '[]')
|
||||
|
@ -97,7 +97,7 @@ module.exports = class Handler
|
|||
term = req.query.term
|
||||
matchedObjects = []
|
||||
filters = [{filter: {index: true}}]
|
||||
if @modelClass.schema.uses_coco_permissions
|
||||
if @modelClass.schema.uses_coco_permissions and req.user
|
||||
filters.push {filter: {index: req.user.get('id')}}
|
||||
for filter in filters
|
||||
callback = (err, results) =>
|
||||
|
|
|
@ -39,6 +39,7 @@ LevelHandler = class LevelHandler extends Handler
|
|||
callback err, level
|
||||
|
||||
getSession: (req, res, id) ->
|
||||
return @sendNotFoundError(res) unless req.user
|
||||
@fetchLevelByIDAndHandleErrors id, req, res, (err, level) =>
|
||||
sessionQuery =
|
||||
level:
|
||||
|
@ -150,6 +151,7 @@ LevelHandler = class LevelHandler extends Handler
|
|||
req.query.limit = parseInt(req.query.limit) ? 20
|
||||
|
||||
getFeedback: (req, res, id) ->
|
||||
return @sendNotFoundError(res) unless req.user
|
||||
@fetchLevelByIDAndHandleErrors id, req, res, (err, level) =>
|
||||
feedbackQuery =
|
||||
creator: mongoose.Types.ObjectId(req.user.id.toString())
|
||||
|
|
|
@ -147,7 +147,9 @@ ScriptSchema = c.object {
|
|||
eventPrereqs: c.array {title: "Event Checks", description: "Logical checks on the event for this script to trigger.", format:'event-prereqs'}, EventPrereqSchema
|
||||
repeats: {title: "Repeats", description: "Whether this script can trigger more than once during a level.", type: 'boolean', "default": false}
|
||||
scriptPrereqs: c.array {title: "Happens After", description: "Scripts that need to fire first."},
|
||||
c.shortString(title: "ID", description: "A unique ID of a script that must have triggered before the parent script can trigger.")
|
||||
c.shortString(title: "ID", description: "A unique ID of a script.")
|
||||
notAfter: c.array {title: "Not After", description: "Do not run this script if any of these scripts have run."},
|
||||
c.shortString(title: "ID", description: "A unique ID of a script.")
|
||||
noteChain: c.array {title: "Actions", description: "A list of things that happen when this script triggers."}, NoteGroupSchema
|
||||
|
||||
LevelThangSchema = c.object {
|
||||
|
|
|
@ -139,6 +139,13 @@ _.extend LevelSessionSchema.properties,
|
|||
|
||||
submittedCode:
|
||||
type: 'object'
|
||||
|
||||
numberOfWinsAndTies:
|
||||
type: 'number'
|
||||
default: 0
|
||||
numberOfLosses:
|
||||
type: 'number'
|
||||
default: 0
|
||||
|
||||
matches:
|
||||
type: 'array'
|
||||
|
|
|
@ -12,7 +12,7 @@ TaskLog = require './task/ScoringTask'
|
|||
bayes = new (require 'bayesian-battle')()
|
||||
|
||||
scoringTaskQueue = undefined
|
||||
scoringTaskTimeoutInSeconds = 120
|
||||
scoringTaskTimeoutInSeconds = 180
|
||||
|
||||
|
||||
module.exports.setup = (app) -> connectToScoringQueue()
|
||||
|
@ -24,24 +24,27 @@ connectToScoringQueue = ->
|
|||
scoringTaskQueue = data
|
||||
log.info "Connected to scoring task queue!"
|
||||
|
||||
module.exports.addPairwiseTaskToQueue = (req, res) ->
|
||||
module.exports.addPairwiseTaskToQueueFromRequest = (req, res) ->
|
||||
taskPair = req.body.sessions
|
||||
#unless isUserAdmin req then return errors.forbidden res, "You do not have the permissions to submit that game to the leaderboard"
|
||||
#fetch both sessions
|
||||
addPairwiseTaskToQueue req.body.sessions (err, success) ->
|
||||
if err? then return errors.serverError res, "There was an error adding pairwise tasks: #{err}"
|
||||
sendResponseObject req, res, {"message":"All task pairs were succesfully sent to the queue"}
|
||||
|
||||
|
||||
addPairwiseTaskToQueue = (taskPair, cb) ->
|
||||
LevelSession.findOne(_id:taskPair[0]).lean().exec (err, firstSession) =>
|
||||
if err? then return errors.serverError res, "There was an error fetching the first session in the pair"
|
||||
if err? then return cb err, false
|
||||
LevelSession.find(_id:taskPair[1]).exec (err, secondSession) =>
|
||||
if err? then return errors.serverError res, "There was an error fetching the second session"
|
||||
if err? then return cb err, false
|
||||
try
|
||||
taskPairs = generateTaskPairs(secondSession, firstSession)
|
||||
catch e
|
||||
if e then return errors.serverError res, "There was an error generating the task pairs"
|
||||
|
||||
sendEachTaskPairToTheQueue taskPairs, (taskPairError) ->
|
||||
if taskPairError? then return errors.serverError res, "There was an error sending the task pairs to the queue"
|
||||
if e then return cb e, false
|
||||
|
||||
sendResponseObject req, res, {"message":"All task pairs were succesfully sent to the queue"}
|
||||
|
||||
sendEachTaskPairToTheQueue taskPairs, (taskPairError) ->
|
||||
if taskPairError? then return cb taskPairError,false
|
||||
cb null, true
|
||||
|
||||
|
||||
module.exports.createNewTask = (req, res) ->
|
||||
requestSessionID = req.body.session
|
||||
|
@ -56,8 +59,8 @@ module.exports.createNewTask = (req, res) ->
|
|||
|
||||
updateSessionToSubmit sessionToSubmit, (err, data) ->
|
||||
if err? then return errors.serverError res, "There was an error updating the session"
|
||||
|
||||
fetchSessionsToRankAgainst (err, sessionsToRankAgainst) ->
|
||||
opposingTeam = calculateOpposingTeam(sessionToSubmit.team)
|
||||
fetchInitialSessionsToRankAgainst opposingTeam, (err, sessionsToRankAgainst) ->
|
||||
if err? then return errors.serverError res, "There was an error fetching the sessions to rank against"
|
||||
|
||||
taskPairs = generateTaskPairs(sessionsToRankAgainst, sessionToSubmit)
|
||||
|
@ -114,9 +117,102 @@ module.exports.processTaskResult = (req, res) ->
|
|||
|
||||
addMatchToSessions clientResponseObject, newScoresObject, (err, data) ->
|
||||
if err? then return errors.serverError res, "There was an error updating the sessions with the match! #{JSON.stringify err}"
|
||||
console.log "Sending response object"
|
||||
sendResponseObject req, res, {"message":"The scores were updated successfully!"}
|
||||
|
||||
originalSessionID = clientResponseObject.originalSessionID
|
||||
originalSessionTeam = clientResponseObject.originalSessionTeam
|
||||
originalSessionRank = parseInt clientResponseObject.originalSessionRank
|
||||
|
||||
determineIfSessionShouldContinueAndUpdateLog originalSessionID, originalSessionRank, (err, sessionShouldContinue) ->
|
||||
if err? then return errors.serverError res, "There was an error determining if the session should continue, #{err}"
|
||||
|
||||
if sessionShouldContinue
|
||||
opposingTeam = calculateOpposingTeam(originalSessionTeam)
|
||||
opponentID = _.pull(_.keys(newScoresObject), originalSessionID)
|
||||
sessionNewScore = newScoresObject[originalSessionID].totalScore
|
||||
opponentNewScore = newScoresObject[opponentID].totalScore
|
||||
findNearestBetterSessionID sessionNewScore, opponentNewScore, opponentID ,opposingTeam, (err, opponentSessionID) ->
|
||||
if err? then return errors.serverError res, "There was an error finding the nearest sessionID!"
|
||||
unless opponentSessionID then return sendResponseObject req, res, {"message":"There were no more games to rank(game is at top!"}
|
||||
|
||||
addPairwiseTaskToQueue [originalSessionID, opponentSessionID], (err, success) ->
|
||||
if err? then return errors.serverError res, "There was an error sending the pairwise tasks to the queue!"
|
||||
sendResponseObject req, res, {"message":"The scores were updated successfully and more games were sent to the queue!"}
|
||||
else
|
||||
console.log "Player lost, achieved rank #{originalSessionRank}"
|
||||
sendResponseObject req, res, {"message":"The scores were updated successfully, person lost so no more games are being inserted!"}
|
||||
|
||||
|
||||
determineIfSessionShouldContinueAndUpdateLog = (sessionID, sessionRank, cb) ->
|
||||
queryParameters =
|
||||
_id: sessionID
|
||||
|
||||
updateParameters =
|
||||
"$inc": {}
|
||||
|
||||
if sessionRank is 0
|
||||
updateParameters["$inc"] = {numberOfWinsAndTies: 1}
|
||||
else
|
||||
updateParameters["$inc"] = {numberOfLosses: 1}
|
||||
|
||||
LevelSession.findOneAndUpdate queryParameters, updateParameters,{select: 'numberOfWinsAndTies numberOfLosses'}, (err, updatedSession) ->
|
||||
if err? then return cb err, updatedSession
|
||||
updatedSession = updatedSession.toObject()
|
||||
|
||||
totalNumberOfGamesPlayed = updatedSession.numberOfWinsAndTies + updatedSession.numberOfLosses
|
||||
if totalNumberOfGamesPlayed < 5
|
||||
console.log "Number of games played is less than 5, continuing..."
|
||||
cb null, true
|
||||
else if totalNumberOfGamesPlayed > 15
|
||||
console.log "Too many games played, ending..."
|
||||
cb null, false
|
||||
else
|
||||
ratio = (updatedSession.numberOfLosses - 5) / (totalNumberOfGamesPlayed)
|
||||
if ratio > 0.66
|
||||
cb null, false
|
||||
console.log "Ratio(#{ratio}) is bad, ending simulation"
|
||||
else
|
||||
console.log "Ratio(#{ratio}) is good, so continuing simulations"
|
||||
cb null, true
|
||||
|
||||
|
||||
findNearestBetterSessionID = (sessionTotalScore, opponentSessionTotalScore, opponentSessionID, opposingTeam, cb) ->
|
||||
queryParameters =
|
||||
totalScore:
|
||||
$gt:opponentSessionTotalScore + 0.5
|
||||
_id:
|
||||
$ne: opponentSessionID
|
||||
levelID: "project-dota"
|
||||
submitted: true
|
||||
submittedCode:
|
||||
$exists: true
|
||||
team: opposingTeam
|
||||
|
||||
limitNumber = 1
|
||||
|
||||
sortParameters =
|
||||
totalScore: 1
|
||||
|
||||
selectString = '_id totalScore'
|
||||
|
||||
query = LevelSession.findOne(queryParameters)
|
||||
.sort(sortParameters)
|
||||
.limit(limitNumber)
|
||||
.select(selectString)
|
||||
.lean()
|
||||
|
||||
console.log "Finding session with score near #{opponentSessionTotalScore}"
|
||||
query.exec (err, session) ->
|
||||
if err? then return cb err, session
|
||||
unless session then return cb err, null
|
||||
console.log "Found session with score #{session.totalScore}"
|
||||
cb err, session._id
|
||||
|
||||
calculateOpposingTeam = (sessionTeam) ->
|
||||
teams = ['ogres','humans']
|
||||
opposingTeams = _.pull teams, sessionTeam
|
||||
return opposingTeams[0]
|
||||
|
||||
|
||||
validatePermissions = (req, sessionID, callback) ->
|
||||
if isUserAnonymous req then return callback null, false
|
||||
if isUserAdmin req then return callback null, true
|
||||
|
@ -177,15 +273,30 @@ updateSessionToSubmit = (sessionToUpdate, callback) ->
|
|||
meanStrength: 25
|
||||
standardDeviation: 25/3
|
||||
totalScore: 10
|
||||
numberOfWinsAndTies: 0
|
||||
numberOfLosses: 0
|
||||
LevelSession.update {_id: sessionToUpdate._id}, sessionUpdateObject, callback
|
||||
|
||||
fetchSessionsToRankAgainst = (callback) ->
|
||||
submittedSessionsQuery =
|
||||
fetchInitialSessionsToRankAgainst = (opposingTeam, callback) ->
|
||||
console.log "Fetching sessions to rank against for opposing team #{opposingTeam}"
|
||||
findParameters =
|
||||
levelID: "project-dota"
|
||||
submitted: true
|
||||
submittedCode:
|
||||
$exists: true
|
||||
LevelSession.find submittedSessionsQuery, callback
|
||||
team: opposingTeam
|
||||
|
||||
sortParameters =
|
||||
totalScore: 1
|
||||
|
||||
limitNumber = 1
|
||||
|
||||
query = LevelSession.find(findParameters)
|
||||
.sort(sortParameters)
|
||||
.limit(limitNumber)
|
||||
|
||||
|
||||
query.exec callback
|
||||
|
||||
generateTaskPairs = (submittedSessions, sessionToScore) ->
|
||||
taskPairs = []
|
||||
|
|
|
@ -28,7 +28,30 @@ module.exports.setup = (app) ->
|
|||
return done(null, user)
|
||||
)
|
||||
))
|
||||
|
||||
app.post '/auth/spy', (req, res, next) ->
|
||||
if req?.user?.isAdmin()
|
||||
|
||||
username = req.body.usernameLower
|
||||
emailLower = req.body.emailLower
|
||||
if emailLower
|
||||
query = {"emailLower":emailLower}
|
||||
else if username
|
||||
query = {"nameLower":username}
|
||||
else
|
||||
return errors.badInput res, "You need to supply one of emailLower or username"
|
||||
|
||||
User.findOne query, (err, user) ->
|
||||
if err? then return errors.serverError res, "There was an error finding the specified user"
|
||||
|
||||
unless user then return errors.badInput res, "The specified user couldn't be found"
|
||||
|
||||
req.logIn user, (err) ->
|
||||
if err? then return errors.serverError res, "There was an error logging in with the specified"
|
||||
res.send(UserHandler.formatEntity(req, user))
|
||||
return res.end()
|
||||
else
|
||||
return errors.unauthorized res, "You must be an admin to enter espionage mode"
|
||||
|
||||
app.post('/auth/login', (req, res, next) ->
|
||||
authentication.authenticate('local', (err, user, info) ->
|
||||
return next(err) if err
|
||||
|
|
|
@ -4,6 +4,7 @@ mail = require '../commons/mail'
|
|||
|
||||
module.exports.setup = (app) ->
|
||||
app.post '/contact', (req, res) ->
|
||||
return res.end() unless req.user
|
||||
log.info "Sending mail from #{req.body.email} saying #{req.body.message}"
|
||||
if config.isProduction
|
||||
options = createMailOptions req.body.email, req.body.message, req.user
|
||||
|
|
|
@ -11,6 +11,7 @@ module.exports.setup = (app) ->
|
|||
parts = module.split('/')
|
||||
module = parts[0]
|
||||
return getSchema(req, res, module) if parts[1] is 'schema'
|
||||
return errors.unauthorized(res, 'Must have an identity to do anything with the db.') unless req.user
|
||||
|
||||
try
|
||||
moduleName = module.replace '.', '_'
|
||||
|
|
|
@ -69,7 +69,7 @@ postFileSchema =
|
|||
required: ['filename', 'mimetype', 'path']
|
||||
|
||||
filePost = (req, res) ->
|
||||
return errors.forbidden(res) unless req.user.isAdmin()
|
||||
return errors.forbidden(res) unless req.user?.isAdmin()
|
||||
options = req.body
|
||||
tv4 = require('tv4').tv4
|
||||
valid = tv4.validate(options, postFileSchema)
|
||||
|
|
|
@ -31,7 +31,7 @@ UserHandler = class UserHandler extends Handler
|
|||
return null unless document?
|
||||
obj = document.toObject()
|
||||
delete obj[prop] for prop in serverProperties
|
||||
includePrivates = req.user and (req.user.isAdmin() or req.user._id.equals(document._id))
|
||||
includePrivates = req.user and (req.user?.isAdmin() or req.user?._id.equals(document._id))
|
||||
delete obj[prop] for prop in privateProperties unless includePrivates
|
||||
|
||||
# emailHash is used by gravatar
|
||||
|
@ -105,7 +105,7 @@ UserHandler = class UserHandler extends Handler
|
|||
]
|
||||
|
||||
getById: (req, res, id) ->
|
||||
if req.user and req.user._id.equals(id)
|
||||
if req.user?._id.equals(id)
|
||||
return @sendSuccess(res, @formatEntity(req, req.user))
|
||||
super(req, res, id)
|
||||
|
||||
|
@ -132,14 +132,15 @@ UserHandler = class UserHandler extends Handler
|
|||
|
||||
post: (req, res) ->
|
||||
return @sendBadInputError(res, 'No input.') if _.isEmpty(req.body)
|
||||
return @sendBadInputError(res, 'Must have an anonymous user to post with.') unless req.user
|
||||
return @sendBadInputError(res, 'Existing users cannot create new ones.') unless req.user.get('anonymous')
|
||||
req.body._id = req.user._id if req.user.get('anonymous')
|
||||
@put(req, res)
|
||||
|
||||
hasAccessToDocument: (req, document) ->
|
||||
if req.route.method in ['put', 'post', 'patch']
|
||||
return true if req.user.isAdmin()
|
||||
return req.user._id.equals(document._id)
|
||||
return true if req.user?.isAdmin()
|
||||
return req.user?._id.equals(document._id)
|
||||
return true
|
||||
|
||||
getByRelationship: (req, res, args...) ->
|
||||
|
@ -149,6 +150,7 @@ UserHandler = class UserHandler extends Handler
|
|||
return @sendNotFoundError(res)
|
||||
|
||||
agreeToCLA: (req, res) ->
|
||||
return @sendUnauthorizedError(res) unless req.user
|
||||
doc =
|
||||
user: req.user._id+''
|
||||
email: req.user.get 'email'
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
# import this at the top of every file so we're not juggling connections
|
||||
# and common libraries are available
|
||||
|
||||
console.log 'IT BEGINS'
|
||||
|
||||
|
||||
GLOBAL._ = require('lodash')
|
||||
_.str = require('underscore.string')
|
||||
_.mixin(_.str.exports())
|
||||
|
@ -71,20 +74,22 @@ unittest.getUser = (email, password, done, force) ->
|
|||
return done(unittest.users[email]) if unittest.users[email] and not force
|
||||
request = require 'request'
|
||||
request.post getURL('/auth/logout'), ->
|
||||
req = request.post(getURL('/db/user'), (err, response, body) ->
|
||||
throw err if err
|
||||
User.findOne({email:email}).exec((err, user) ->
|
||||
if password is '80yqxpb38j'
|
||||
user.set('permissions', [ 'admin' ])
|
||||
user.save (err) ->
|
||||
request.get getURL('/auth/whoami'), ->
|
||||
req = request.post(getURL('/db/user'), (err, response, body) ->
|
||||
throw err if err
|
||||
User.findOne({email:email}).exec((err, user) ->
|
||||
if password is '80yqxpb38j'
|
||||
user.set('permissions', [ 'admin' ])
|
||||
user.save (err) ->
|
||||
wrapUpGetUser(email, user, done)
|
||||
else
|
||||
wrapUpGetUser(email, user, done)
|
||||
else
|
||||
wrapUpGetUser(email, user, done)
|
||||
)
|
||||
)
|
||||
)
|
||||
form = req.form()
|
||||
form.append('email', email)
|
||||
form.append('password', password)
|
||||
form = req.form()
|
||||
form.append('email', email)
|
||||
form.append('password', password)
|
||||
|
||||
|
||||
wrapUpGetUser = (email, user, done) ->
|
||||
unittest.users[email] = user
|
||||
|
|
|
@ -17,8 +17,9 @@ describe '/auth/login', ->
|
|||
|
||||
it 'clears Users first', (done) ->
|
||||
User.remove {}, (err) ->
|
||||
throw err if err
|
||||
done()
|
||||
request.get getURL('/auth/whoami'), ->
|
||||
throw err if err
|
||||
done()
|
||||
|
||||
it 'finds no user', (done) ->
|
||||
req = request.post(urlLogin, (error, response) ->
|
||||
|
@ -92,9 +93,10 @@ describe '/auth/reset', ->
|
|||
form = req.form()
|
||||
form.append('email', 'unknow')
|
||||
|
||||
it 'reset user password', (done) ->
|
||||
it 'resets user password', (done) ->
|
||||
req = request.post(urlReset, (error, response) ->
|
||||
expect(response).toBeDefined()
|
||||
console.log 'status code is', response.statusCode
|
||||
expect(response.statusCode).toBe(200)
|
||||
expect(response.body).toBeDefined()
|
||||
passwordReset = response.body
|
||||
|
|
|
@ -54,18 +54,16 @@ describe 'POST /db/user', ->
|
|||
|
||||
describe 'PUT /db/user', ->
|
||||
|
||||
it 'denies requests without any data', (done) ->
|
||||
req = request.post getURL('/auth/logout'),
|
||||
(err, res) ->
|
||||
expect(res.statusCode).toBe(200)
|
||||
req = request.put getURL(urlUser),
|
||||
(err, res) ->
|
||||
expect(res.statusCode).toBe(422)
|
||||
expect(res.body).toBe('No input.')
|
||||
done()
|
||||
|
||||
it 'logs in as normal joe', (done) ->
|
||||
loginJoe -> done()
|
||||
request.post getURL('/auth/logout'),
|
||||
loginJoe -> done()
|
||||
|
||||
it 'denies requests without any data', (done) ->
|
||||
request.put getURL(urlUser),
|
||||
(err, res) ->
|
||||
expect(res.statusCode).toBe(422)
|
||||
expect(res.body).toBe('No input.')
|
||||
done()
|
||||
|
||||
it 'denies requests to edit someone who is not joe', (done) ->
|
||||
unittest.getAdmin (admin) ->
|
||||
|
|
Loading…
Reference in a new issue