mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-11-28 01:55:38 -05:00
Merge branch 'master' of https://github.com/codecombat/codecombat
This commit is contained in:
commit
2b742da9e4
5 changed files with 375 additions and 6 deletions
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
|
336
app/views/play/spectate_view.coffee
Normal file
336
app/views/play/spectate_view.coffee
Normal file
|
@ -0,0 +1,336 @@
|
|||
View = require 'views/kinds/RootView'
|
||||
template = require 'templates/play/spectate'
|
||||
{me} = require('lib/auth')
|
||||
ThangType = require 'models/ThangType'
|
||||
|
||||
# temp hard coded data
|
||||
World = require 'lib/world/world'
|
||||
docs = require 'lib/world/docs'
|
||||
|
||||
# tools
|
||||
Surface = require 'lib/surface/Surface'
|
||||
God = require 'lib/God'
|
||||
GoalManager = require 'lib/world/GoalManager'
|
||||
ScriptManager = require 'lib/scripts/ScriptManager'
|
||||
LevelBus = require('lib/LevelBus')
|
||||
LevelLoader = require 'lib/LevelLoader'
|
||||
LevelSession = require 'models/LevelSession'
|
||||
Level = require 'models/Level'
|
||||
LevelComponent = require 'models/LevelComponent'
|
||||
Camera = require 'lib/surface/Camera'
|
||||
|
||||
# subviews
|
||||
TomeView = require './level/tome/tome_view'
|
||||
ChatView = require './level/level_chat_view'
|
||||
HUDView = require './level/hud_view'
|
||||
ControlBarView = require './level/control_bar_view'
|
||||
PlaybackView = require './level/playback_view'
|
||||
GoalsView = require './level/goals_view'
|
||||
GoldView = require './level/gold_view'
|
||||
VictoryModal = require './level/modal/victory_modal'
|
||||
InfiniteLoopModal = require './level/modal/infinite_loop_modal'
|
||||
|
||||
LoadingScreen = require 'lib/LoadingScreen'
|
||||
|
||||
PROFILE_ME = false
|
||||
|
||||
PlayLevelView = require './level_view'
|
||||
|
||||
module.exports = class SpectateLevelView extends View
|
||||
id: 'spectate-level-view'
|
||||
template: template
|
||||
cache: false
|
||||
shortcutsEnabled: true
|
||||
startsLoading: true
|
||||
isEditorPreview: false
|
||||
|
||||
subscriptions:
|
||||
'level-set-volume': (e) -> createjs.Sound.setVolume(e.volume)
|
||||
'level-highlight-dom': 'onHighlightDom'
|
||||
'end-level-highlight-dom': 'onEndHighlight'
|
||||
'level-focus-dom': 'onFocusDom'
|
||||
'level-disable-controls': 'onDisableControls'
|
||||
'level-enable-controls': 'onEnableControls'
|
||||
'god:new-world-created': 'onNewWorld'
|
||||
'god:infinite-loop': 'onInfiniteLoop'
|
||||
'edit-wizard-settings': 'showWizardSettingsModal'
|
||||
'surface:world-set-up': 'onSurfaceSetUpNewWorld'
|
||||
'level:session-will-save': 'onSessionWillSave'
|
||||
'level:set-team': 'setTeam'
|
||||
|
||||
events:
|
||||
'click #level-done-button': 'onDonePressed'
|
||||
|
||||
|
||||
constructor: (options, @levelID) ->
|
||||
console.profile?() if PROFILE_ME
|
||||
super options
|
||||
console.log @levelID
|
||||
|
||||
@ogreSessionID = @getQueryVariable 'ogres'
|
||||
@humanSessionID = @getQueryVariable 'humans'
|
||||
|
||||
|
||||
$(window).on('resize', @onWindowResize)
|
||||
@supermodel.once 'error', =>
|
||||
msg = $.i18n.t('play_level.level_load_error', defaultValue: "Level could not be loaded.")
|
||||
@$el.html('<div class="alert">' + msg + '</div>')
|
||||
|
||||
|
||||
@load()
|
||||
|
||||
|
||||
|
||||
setLevel: (@level, @supermodel) ->
|
||||
@god?.level = @level.serialize @supermodel
|
||||
if @world
|
||||
serializedLevel = @level.serialize(@supermodel)
|
||||
@world.loadFromLevel serializedLevel, false
|
||||
else
|
||||
@load()
|
||||
|
||||
load: ->
|
||||
@levelLoader = new LevelLoader(@levelID, @supermodel, @sessionID)
|
||||
@levelLoader.once 'loaded-all', @onLevelLoaderLoaded
|
||||
@god = new God()
|
||||
|
||||
getRenderData: ->
|
||||
c = super()
|
||||
c.world = @world
|
||||
c
|
||||
|
||||
afterRender: ->
|
||||
window.onPlayLevelViewLoaded? @ # still a hack
|
||||
@loadingScreen = new LoadingScreen(@$el.find('canvas')[0])
|
||||
@loadingScreen.show()
|
||||
super()
|
||||
|
||||
onLevelLoaderLoaded: =>
|
||||
#needs editing
|
||||
@session = @levelLoader.session
|
||||
@world = @levelLoader.world
|
||||
@level = @levelLoader.level
|
||||
@levelLoader.destroy()
|
||||
@levelLoader = null
|
||||
@loadingScreen.destroy()
|
||||
@god.level = @level.serialize @supermodel
|
||||
@god.worldClassMap = @world.classMap
|
||||
#@setTeam @world.teamForPlayer _.size @session.get 'players' # TODO: players aren't initialized yet?
|
||||
@setTeam @getQueryVariable("team") ? @world.teamForPlayer(0)
|
||||
@initSurface()
|
||||
@initGoalManager()
|
||||
@initScriptManager()
|
||||
@insertSubviews()
|
||||
@initVolume()
|
||||
@session.on 'change:multiplayer', @onMultiplayerChanged, @
|
||||
@originalSessionState = _.cloneDeep(@session.get('state'))
|
||||
@register()
|
||||
@controlBar.setBus(@bus)
|
||||
@surface.showLevel()
|
||||
|
||||
onSupermodelLoadedOne: =>
|
||||
@modelsLoaded ?= 0
|
||||
@modelsLoaded += 1
|
||||
@updateInitString()
|
||||
|
||||
updateInitString: ->
|
||||
return if @surface
|
||||
@modelsLoaded ?= 0
|
||||
canvas = @$el.find('#surface')[0]
|
||||
ctx = canvas.getContext('2d')
|
||||
ctx.font="20px Georgia"
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height)
|
||||
ctx.fillText("Loaded #{@modelsLoaded} thingies",50,50)
|
||||
|
||||
insertSubviews: ->
|
||||
#needs editing
|
||||
@insertSubView @tome = new TomeView levelID: @levelID, session: @session, thangs: @world.thangs, supermodel: @supermodel
|
||||
@insertSubView new PlaybackView {}
|
||||
@insertSubView new GoalsView {}
|
||||
@insertSubView new GoldView {}
|
||||
@insertSubView new HUDView {}
|
||||
@insertSubView new ChatView levelID: @levelID, sessionID: @session.id, session: @session
|
||||
worldName = @level.get('i18n')?[me.lang()]?.name ? @level.get('name')
|
||||
@controlBar = @insertSubView new ControlBarView {worldName: worldName, session: @session, level: @level, supermodel: @supermodel, playableTeams: @world.playableTeams}
|
||||
#Backbone.Mediator.publish('level-set-debug', debug: true) if me.displayName() is 'Nick!'
|
||||
|
||||
afterInsert: ->
|
||||
super()
|
||||
|
||||
|
||||
onWindowResize: (s...) ->
|
||||
$('#pointer').css('opacity', 0.0)
|
||||
|
||||
onDisableControls: (e) =>
|
||||
return if e.controls and not ('level' in e.controls)
|
||||
@shortcutsEnabled = false
|
||||
@wasFocusedOn = document.activeElement
|
||||
$('body').focus()
|
||||
|
||||
onEnableControls: (e) =>
|
||||
return if e.controls? and not ('level' in e.controls)
|
||||
@shortcutsEnabled = true
|
||||
$(@wasFocusedOn).focus() if @wasFocusedOn
|
||||
@wasFocusedOn = null
|
||||
|
||||
onDonePressed: => @showVictory()
|
||||
|
||||
onNewWorld: (e) ->
|
||||
@world = e.world
|
||||
|
||||
onInfiniteLoop: (e) ->
|
||||
return unless e.firstWorld
|
||||
@openModalView new InfiniteLoopModal()
|
||||
window.tracker?.trackEvent 'Saw Initial Infinite Loop', level: @world.name, label: @world.name
|
||||
|
||||
|
||||
getNextLevel: ->
|
||||
nextLevelOriginal = @level.get('nextLevel')?.original
|
||||
levels = @supermodel.getModels(Level)
|
||||
return l for l in levels when l.get('original') is nextLevelOriginal
|
||||
|
||||
onHighlightDom: (e) =>
|
||||
if e.delay
|
||||
delay = e.delay
|
||||
delete e.delay
|
||||
@pointerInterval = _.delay((=> @onHighlightDom e), delay)
|
||||
return
|
||||
@addPointer()
|
||||
selector = e.selector + ':visible'
|
||||
dom = $(selector)
|
||||
return if parseFloat(dom.css('opacity')) is 0.0
|
||||
offset = dom.offset()
|
||||
return if not offset
|
||||
target_left = offset.left + dom.outerWidth() * 0.5
|
||||
target_top = offset.top + dom.outerHeight() * 0.5
|
||||
body = $('#level-view')
|
||||
|
||||
if e.sides
|
||||
if 'left' in e.sides then target_left = offset.left
|
||||
if 'right' in e.sides then target_left = offset.left + dom.outerWidth()
|
||||
if 'top' in e.sides then target_top = offset.top
|
||||
if 'bottom' in e.sides then target_top = offset.top + dom.outerHeight()
|
||||
else
|
||||
# aim to hit the side if the target is entirely on one side of the screen
|
||||
if offset.left > body.outerWidth()*0.5
|
||||
target_left = offset.left
|
||||
else if offset.left + dom.outerWidth() < body.outerWidth()*0.5
|
||||
target_left = offset.left + dom.outerWidth()
|
||||
|
||||
# aim to hit the bottom or top if the target is entirely on the top or bottom of the screen
|
||||
if offset.top > body.outerWidth()*0.5
|
||||
target_top = offset.top
|
||||
else if offset.top + dom.outerHeight() < body.outerHeight()*0.5
|
||||
target_top = offset.top + dom.outerHeight()
|
||||
|
||||
if e.offset
|
||||
target_left += e.offset.x
|
||||
target_top += e.offset.y
|
||||
|
||||
@pointerRadialDistance = -47 # - Math.sqrt(Math.pow(dom.outerHeight()*0.5, 2), Math.pow(dom.outerWidth()*0.5))
|
||||
@pointerRotation = e.rotation ? Math.atan2(body.outerWidth()*0.5 - target_left, target_top - body.outerHeight()*0.5)
|
||||
pointer = $('#pointer')
|
||||
pointer
|
||||
.css('opacity', 1.0)
|
||||
.css('transition', 'none')
|
||||
.css('transform', "rotate(#{@pointerRotation}rad) translate(-3px, #{@pointerRadialDistance}px)")
|
||||
.css('top', target_top - 50)
|
||||
.css('left', target_left - 50)
|
||||
setTimeout((=>
|
||||
@animatePointer()
|
||||
clearInterval(@pointerInterval)
|
||||
@pointerInterval = setInterval(@animatePointer, 1200)
|
||||
), 1)
|
||||
|
||||
|
||||
animatePointer: =>
|
||||
pointer = $('#pointer')
|
||||
pointer.css('transition', 'all 0.6s ease-out')
|
||||
pointer.css('transform', "rotate(#{@pointerRotation}rad) translate(-3px, #{@pointerRadialDistance-50}px)")
|
||||
setTimeout((=>
|
||||
pointer.css('transform', "rotate(#{@pointerRotation}rad) translate(-3px, #{@pointerRadialDistance}px)").css('transition', 'all 0.4s ease-in')), 800)
|
||||
|
||||
onFocusDom: (e) => $(e.selector).focus()
|
||||
|
||||
onEndHighlight: =>
|
||||
$('#pointer').css('opacity', 0.0)
|
||||
clearInterval(@pointerInterval)
|
||||
|
||||
|
||||
# initialization
|
||||
|
||||
addPointer: ->
|
||||
p = $('#pointer')
|
||||
return if p.length
|
||||
@$el.append($('<img src="/images/level/pointer.png" id="pointer">'))
|
||||
|
||||
initSurface: ->
|
||||
surfaceCanvas = $('canvas#surface', @$el)
|
||||
@surface = new Surface(@world, surfaceCanvas, thangTypes: @supermodel.getModels(ThangType), playJingle: not @isEditorPreview)
|
||||
worldBounds = @world.getBounds()
|
||||
bounds = [{x:worldBounds.left, y:worldBounds.top}, {x:worldBounds.right, y:worldBounds.bottom}]
|
||||
@surface.camera.setBounds(bounds)
|
||||
@surface.camera.zoomTo({x:0, y:0}, 0.1, 0)
|
||||
|
||||
initGoalManager: ->
|
||||
@goalManager = new GoalManager(@world)
|
||||
@god.goalManager = @goalManager
|
||||
|
||||
initScriptManager: ->
|
||||
@scriptManager = new ScriptManager({scripts: @world.scripts or [], view:@, session: @session})
|
||||
@scriptManager.loadFromSession()
|
||||
|
||||
initVolume: ->
|
||||
volume = me.get('volume')
|
||||
volume = 1.0 unless volume?
|
||||
Backbone.Mediator.publish 'level-set-volume', volume: volume
|
||||
|
||||
onSurfaceSetUpNewWorld: ->
|
||||
return if @alreadyLoadedState
|
||||
@alreadyLoadedState = true
|
||||
state = @originalSessionState
|
||||
if state.frame
|
||||
Backbone.Mediator.publish 'level-set-time', { time: 0, frameOffset: state.frame }
|
||||
if state.selected
|
||||
# TODO: Should also restore selected spell here by saving spellName
|
||||
Backbone.Mediator.publish 'level-select-sprite', { thangID: state.selected, spellName: null }
|
||||
if state.playing?
|
||||
Backbone.Mediator.publish 'level-set-playing', { playing: state.playing }
|
||||
|
||||
|
||||
register: ->
|
||||
@bus = LevelBus.get(@levelID, @session.id)
|
||||
@bus.setSession(@session)
|
||||
@bus.setTeamSpellMap @tome.teamSpellMap
|
||||
@bus.connect() if @session.get('multiplayer')
|
||||
|
||||
onSessionWillSave: (e) ->
|
||||
# Something interesting has happened, so (at a lower frequency), we'll save a screenshot.
|
||||
@saveScreenshot e.session
|
||||
|
||||
# Throttled
|
||||
saveScreenshot: (session) =>
|
||||
return unless screenshot = @surface?.screenshot()
|
||||
session.save {screenshot: screenshot}, {patch: true}
|
||||
|
||||
setTeam: (team) ->
|
||||
team = team?.team unless _.isString team
|
||||
team ?= 'humans'
|
||||
me.team = team
|
||||
Backbone.Mediator.publish 'level:team-set', team: team
|
||||
|
||||
destroy: ->
|
||||
super()
|
||||
@levelLoader?.destroy()
|
||||
@surface?.destroy()
|
||||
@god?.destroy()
|
||||
@goalManager?.destroy()
|
||||
@scriptManager?.destroy()
|
||||
$(window).off('resize', @onWindowResize)
|
||||
delete window.world # not sure where this is set, but this is one way to clean it up
|
||||
|
||||
clearInterval(@pointerInterval)
|
||||
@bus?.destroy()
|
||||
#@instance.save() unless @instance.loading
|
||||
console.profileEnd?() if PROFILE_ME
|
||||
@session.off 'change:multiplayer', @onMultiplayerChanged, @
|
|
@ -7,7 +7,7 @@ class LevelSessionHandler extends Handler
|
|||
modelClass: LevelSession
|
||||
editableProperties: ['multiplayer', 'players', 'code', 'completed', 'state',
|
||||
'levelName', 'creatorName', 'levelID', 'screenshot',
|
||||
'chat', 'teamSpells']
|
||||
'chat', 'teamSpells','submitted']
|
||||
|
||||
getByRelationship: (req, res, args...) ->
|
||||
return @sendNotFoundError(res) unless args.length is 2 and args[1] is 'active'
|
||||
|
|
|
@ -67,7 +67,7 @@ _.extend LevelSessionSchema.properties,
|
|||
meanStrength: {type: 'number', default: 25}
|
||||
standardDeviation: {type:'number', default:25/3, minimum: 0}
|
||||
totalScore: {type: 'number', default: 10}
|
||||
|
||||
submitted: {type: 'boolean', default: false, index:true}
|
||||
|
||||
c.extendBasicProperties LevelSessionSchema, 'level.session'
|
||||
c.extendPermissionsProperties LevelSessionSchema, 'level.session'
|
||||
|
|
|
@ -29,13 +29,30 @@ throwScoringQueueRegistrationError = (error) ->
|
|||
throw new Error "There was an error registering the scoring queue."
|
||||
|
||||
module.exports.createNewTask = (req, res) ->
|
||||
scoringTaskQueue.sendMessage req.body, 0, (err, data) ->
|
||||
return errors.badInput res, "There was an error creating the message, reason: #{err}" if err?
|
||||
return errors.badInput res, "The session ID is invalid" unless typeof req.body.session is "string"
|
||||
LevelSession.findOne { "_id": req.body.session}, (err, sessionToScore) ->
|
||||
return errors.serverError res, "There was an error finding the given session." if err?
|
||||
|
||||
res.send data
|
||||
res.end()
|
||||
sessionToScore.submitted = true
|
||||
LevelSession.update { "_id": req.body.session}, {"submitted":true}, (err, data) ->
|
||||
return errors.serverError res, "There was an error saving the submitted bool of the session." if err?
|
||||
LevelSession.find { "levelID": "project-dota", "submitted": true}, (err, submittedSessions) ->
|
||||
taskPairs = []
|
||||
for session in submittedSessions
|
||||
if String(session._id) isnt req.body.session
|
||||
taskPairs.push [req.body.session,String session._id]
|
||||
async.each taskPairs, sendTaskPairToQueue, (taskPairError) ->
|
||||
return errors.serverError res, "There was an error sending the task pairs to the queue" if taskPairError?
|
||||
sendResponseObject req, res, {"message":"All task pairs were succesfully sent to the queue"}
|
||||
|
||||
|
||||
sendTaskPairToQueue = (taskPair, callback) ->
|
||||
taskObject =
|
||||
sessions: taskPair
|
||||
|
||||
scoringTaskQueue.sendMessage taskObject, 0, (err,data) ->
|
||||
callback err,data
|
||||
|
||||
module.exports.dispatchTaskToConsumer = (req, res) ->
|
||||
userID = getUserIDFromRequest req,res
|
||||
return errors.forbidden res, "You need to be logged in to simulate games" if isUserAnonymous req
|
||||
|
|
Loading…
Reference in a new issue