mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2025-03-13 22:49:51 -04:00
Merge branch 'master' of https://github.com/codecombat/codecombat
This commit is contained in:
commit
eeff462051
10 changed files with 465 additions and 55 deletions
|
@ -44,8 +44,13 @@ Application = initialize: ->
|
|||
}, (t) =>
|
||||
@router = new Router()
|
||||
@router.subscribe()
|
||||
Object.freeze this if typeof Object.freeze is 'function'
|
||||
@router = Router
|
||||
@idleTracker = new Idle
|
||||
onAway: => @userIsIdle = true
|
||||
onAwayBack: => @userIsIdle = false
|
||||
onHidden: => @userIsIdle = true
|
||||
onVisible: => @userIsIdle = false
|
||||
awayTimeout: 5 * 60 * 1000
|
||||
@idleTracker.start()
|
||||
|
||||
module.exports = Application
|
||||
window.application = Application
|
||||
|
|
|
@ -8,7 +8,7 @@ module.exports = nativeDescription: "Nederlands", englishDescription: "Dutch", t
|
|||
delay_1_sec: "1 seconde"
|
||||
delay_3_sec: "3 secondes"
|
||||
delay_5_sec: "5 secondes"
|
||||
manual: "Handmatig"
|
||||
manual: "Handleiding"
|
||||
fork: "Fork"
|
||||
play: "Spelen"
|
||||
|
||||
|
@ -103,7 +103,7 @@ module.exports = nativeDescription: "Nederlands", englishDescription: "Dutch", t
|
|||
|
||||
wizard_settings:
|
||||
title: "Tovenaar instellingen"
|
||||
customize_avatar: "Bewerk jouw avatar"
|
||||
customize_avatar: "Bewerk je avatar"
|
||||
clothes: "Kleren"
|
||||
trim: "Trim"
|
||||
cloud: "Wolk"
|
||||
|
@ -357,7 +357,7 @@ module.exports = nativeDescription: "Nederlands", englishDescription: "Dutch", t
|
|||
|
||||
contribute:
|
||||
page_title: "Bijdragen"
|
||||
character_classes_title: "Karakter Klassen"
|
||||
character_classes_title: "Karakterklassen"
|
||||
introduction_desc_intro: "We hebben hoge verwachtingen over CodeCombat."
|
||||
introduction_desc_pref: "We willen zijn waar programmeurs van alle niveaus komen om te leren en samen te spelen, anderen introduceren aan de wondere wereld van code, en de beste delen van de gemeenschap te reflecteren. We kunnen en willen dit niet alleen doen; wat projecten zoals GitHub, Stack Overflow en Linux groots en succesvol maken, zijn de mensen die deze software gebruiken en verbeteren. Daartoe, "
|
||||
introduction_desc_github_url: "CodeCombat is volledig open source"
|
||||
|
@ -422,7 +422,7 @@ module.exports = nativeDescription: "Nederlands", englishDescription: "Dutch", t
|
|||
diplomat_launch_url: "release in oktober"
|
||||
diplomat_introduction_suf: "dan is het wel dat er een significante interesse is in CodeCombat in andere landen, vooral Brazilië! We zijn een corps aan vertalers aan het creëren dat ijverig de ene set woorden in een andere omzet om CodeCombat zo toegankelijk te maken als mogelijk in heel de wereld. Als jij het leuk vindt glimpsen op te vangen van aankomende content en deze levels zo snel mogelijk naar je landgenoten te krijgen, dan is dit de klasse voor jou."
|
||||
diplomat_attribute_1: "Vloeiend Engels en de taal waar naar je wilt vertalen kunnen spreken. Wanneer je moeilijke ideeën wilt overbrengen, is het belangrijk beide goed te kunnen!"
|
||||
diplomat_join_pref_github: "Vind jouw taal haar locale bestand "
|
||||
diplomat_join_pref_github: "Vind van jouw taal het locale bestand "
|
||||
diplomat_github_url: "op GitHub"
|
||||
diplomat_join_suf_github: ", edit het online, en submit een pull request. Daarnaast kun je hieronder aanvinken als je up-to-date wilt worden gehouden met nieuwe internationalisatie-ontwikkelingen."
|
||||
more_about_diplomat: "Leer meer over het worden van een geweldige Diplomaat"
|
||||
|
|
152
app/styles/play/spectate.sass
Normal file
152
app/styles/play/spectate.sass
Normal file
|
@ -0,0 +1,152 @@
|
|||
@import "app/styles/bootstrap/mixins"
|
||||
@import "app/styles/mixins"
|
||||
|
||||
#spectate-level-view
|
||||
margin: 0 auto
|
||||
@include user-select(none)
|
||||
|
||||
.level-content
|
||||
position: relative
|
||||
|
||||
#canvas-wrapper
|
||||
width: 55%
|
||||
position: relative
|
||||
|
||||
canvas#surface
|
||||
background-color: #ddd
|
||||
width: 100%
|
||||
display: block
|
||||
z-index: 1
|
||||
|
||||
|
||||
//max-width: 1680px // guideline, but for now let's let it stretch out
|
||||
min-width: 1024px
|
||||
position: relative
|
||||
|
||||
#code-area
|
||||
@include box-sizing(border-box)
|
||||
padding: 10px 1%
|
||||
width: 45%
|
||||
background: transparent url(/images/level/wood_texture.png)
|
||||
background-size: 100% 100%
|
||||
position: absolute
|
||||
right: 0
|
||||
top: 0px
|
||||
bottom: 0
|
||||
|
||||
#pointer
|
||||
position: absolute
|
||||
left: 0
|
||||
top: 0
|
||||
height: 100px
|
||||
opacity: 0.0
|
||||
pointer-events: none
|
||||
z-index: 10
|
||||
|
||||
// Level Docs
|
||||
.ui-effects-transfer
|
||||
border: 2px dotted gray
|
||||
|
||||
.modal
|
||||
img
|
||||
float: right
|
||||
|
||||
img.diagram
|
||||
float: none
|
||||
|
||||
#multiplayer-join-link
|
||||
font-size: 12px
|
||||
|
||||
#level-done-button
|
||||
position: absolute
|
||||
right: 46%
|
||||
top: 43px
|
||||
@include box-shadow(4px 4px 15px black)
|
||||
|
||||
// Custom Buttons
|
||||
.btn.banner
|
||||
@include banner-button(#FFF, #333)
|
||||
@include box-shadow(2px 2px 2px rgba(0, 0, 0, 0.5))
|
||||
border: 1px solid black
|
||||
text-shadow: none
|
||||
|
||||
$buttonConfig: 'primary' #6CA8EA, 'info' #71AACC, 'success' #90B236, 'warning' #CD6800, 'danger' #B43C20, 'inverse' #3A537F
|
||||
@each $tuple in $buttonConfig
|
||||
&.btn-#{nth($tuple, 1)}
|
||||
@include banner-button(nth($tuple, 2), #FFF)
|
||||
|
||||
.footer .footer-link-text a
|
||||
@include opacity(0.75)
|
||||
@include transition(opacity .10s linear)
|
||||
|
||||
&:hover, &:active
|
||||
@include opacity(1)
|
||||
|
||||
$GI: 0.5 // gradient intensity; can tweak this 0-1
|
||||
|
||||
.gradient
|
||||
position: absolute
|
||||
z-index: 10
|
||||
|
||||
#code-area-gradient
|
||||
top: 0px
|
||||
width: 3px
|
||||
background: linear-gradient(to right, rgba(0,0,0,0) 0%,rgba(0,0,0,$GI) 100%)
|
||||
left: -3px
|
||||
bottom: 0
|
||||
|
||||
#hud-top-gradient
|
||||
top: -32px
|
||||
background: linear-gradient(to bottom, rgba(0,0,0,0) 0%,rgba(0,0,0,0.8*$GI) 100%)
|
||||
left: 0
|
||||
right: 0
|
||||
bottom: 0
|
||||
height: 3px
|
||||
|
||||
#canvas-left-gradient
|
||||
left: 0px
|
||||
width: 5px
|
||||
background: linear-gradient(to left, rgba(0,0,0,0) 0%,rgba(0,0,0,0.8*$GI) 100%)
|
||||
bottom: -30px
|
||||
top: 0
|
||||
|
||||
#canvas-top-gradient
|
||||
top: 0
|
||||
height: 5px
|
||||
left: 0
|
||||
right: 0
|
||||
background: linear-gradient(to top, rgba(0,0,0,0) 0%,rgba(0,0,0,0.8*$GI) 100%)
|
||||
|
||||
#hud-left-gradient
|
||||
background: linear-gradient(to right, rgba(0,0,0,$GI) 0%,rgba(0,0,0,0) 100%)
|
||||
left: 0
|
||||
top: 0
|
||||
height: 100%
|
||||
width: 2%
|
||||
|
||||
#hud-right-gradient
|
||||
background: linear-gradient(to right, rgba(0,0,0,0) 0%,rgba(0,0,0,$GI) 100%)
|
||||
right: 0
|
||||
position: absolute
|
||||
top: 0
|
||||
height: 100%
|
||||
width: 2%
|
||||
|
||||
.footer
|
||||
@media screen and (min-aspect-ratio: 17/10)
|
||||
display: none
|
||||
|
||||
&:not(:hover)
|
||||
@include opacity(0.6)
|
||||
|
||||
.hour-of-code-explanation
|
||||
margin-top: 5px
|
||||
color: white
|
||||
font-size: 12px
|
||||
|
||||
&:not(:hover)
|
||||
@include opacity(0.75)
|
||||
|
||||
a
|
||||
color: white
|
||||
text-decoration: underline
|
|
@ -73,7 +73,7 @@ block content
|
|||
li German - Dirk, faabsen, HiroP0, Anon
|
||||
li Thai - Kamolchanok Jittrepit
|
||||
li Vietnamese - An Nguyen Hoang Thien
|
||||
li Dutch - Glen De Cauwsemaecker
|
||||
li Dutch - Glen De Cauwsemaecker, Guido Zuidhof, Ruben Vereecken
|
||||
li Greek - Stergios
|
||||
li Latin American Spanish - Jesús Ruppel, Matthew Burt, Mariano Luzza
|
||||
li Spain Spanish - Matthew Burt, DanielRodriguezRivero, Anon
|
||||
|
|
|
@ -24,7 +24,7 @@ div#columns.row
|
|||
if team.chartData
|
||||
tr
|
||||
th(colspan=4, style="color: #{team.primaryColor}")
|
||||
img(src="https://chart.googleapis.com/chart?chs=450x125&cht=lxy&chco=#{team.chartColor}&chtt=Score%3A+#{team.currentScore}&chts=#{team.chartColor},16,c&chf=a,s,000000FF&chls=2&chm=o,#{team.chartColor},0,4&chd=t:#{team.chartData}")
|
||||
img(src="https://chart.googleapis.com/chart?chs=450x125&cht=lxy&chco=#{team.chartColor}&chtt=Score%3A+#{team.currentScore}&chts=#{team.chartColor},16,r&chf=a,s,000000FF&chls=2&chm=o,#{team.chartColor},0,4&chd=t:#{team.chartData}")
|
||||
|
||||
tr
|
||||
th Result
|
||||
|
|
|
@ -1,16 +1,22 @@
|
|||
.level-content
|
||||
#control-bar-view
|
||||
|
||||
#canvas-wrapper
|
||||
canvas(width=924, height=589)#surface
|
||||
canvas(width=1848, height=1178)#surface
|
||||
#canvas-left-gradient.gradient
|
||||
#canvas-top-gradient.gradient
|
||||
#goals-view.hide
|
||||
#gold-view.hide.expanded
|
||||
#gold-view.secret.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
|
||||
a(title='Send CodeCombat a message', tabindex=-1, data-toggle="coco-modal", data-target="modal/contact", data-i18n="nav.contact") Contact
|
||||
if explainHourOfCode
|
||||
// Does not show up unless lang is en-US.
|
||||
div.hour-of-code-explanation
|
||||
| The 'Hour of Code' is a nationwide initiative by
|
||||
a(href="http://csedweek.org") Computer Science Education Week
|
||||
| and
|
||||
a(href="http://code.org") Code.org
|
||||
| to introduce millions of students to one hour of computer science and computer programming.
|
|
@ -4,6 +4,7 @@ Simulator = require 'lib/simulator/Simulator'
|
|||
LevelSession = require 'models/LevelSession'
|
||||
CocoCollection = require 'models/CocoCollection'
|
||||
{teamDataFromLevel} = require './ladder/utils'
|
||||
application = require 'application'
|
||||
|
||||
LadderTabView = require './ladder/ladder_tab'
|
||||
MyMatchesTabView = require './ladder/my_matches_tab'
|
||||
|
@ -74,12 +75,11 @@ module.exports = class LadderView extends RootView
|
|||
@sessions.fetch({"success": @refreshViews})
|
||||
|
||||
refreshViews: =>
|
||||
return if @destroyed
|
||||
return if @destroyed or application.userIsIdle
|
||||
@ladderTab.refreshLadder()
|
||||
@myMatchesTab.refreshMatches()
|
||||
console.log "refreshed views!"
|
||||
|
||||
|
||||
# Simulations
|
||||
|
||||
onSimulateAllButtonClick: (e) ->
|
||||
|
|
|
@ -5,7 +5,6 @@ ThangType = require 'models/ThangType'
|
|||
|
||||
# temp hard coded data
|
||||
World = require 'lib/world/world'
|
||||
docs = require 'lib/world/docs'
|
||||
|
||||
# tools
|
||||
Surface = require 'lib/surface/Surface'
|
||||
|
@ -17,7 +16,9 @@ LevelLoader = require 'lib/LevelLoader'
|
|||
LevelSession = require 'models/LevelSession'
|
||||
Level = require 'models/Level'
|
||||
LevelComponent = require 'models/LevelComponent'
|
||||
Article = require 'models/Article'
|
||||
Camera = require 'lib/surface/Camera'
|
||||
AudioPlayer = require 'lib/AudioPlayer'
|
||||
|
||||
# subviews
|
||||
TomeView = require './level/tome/tome_view'
|
||||
|
@ -34,8 +35,6 @@ LoadingScreen = require 'lib/LoadingScreen'
|
|||
|
||||
PROFILE_ME = false
|
||||
|
||||
PlayLevelView = require './level_view'
|
||||
|
||||
module.exports = class SpectateLevelView extends View
|
||||
id: 'spectate-level-view'
|
||||
template: template
|
||||
|
@ -46,6 +45,8 @@ module.exports = class SpectateLevelView extends View
|
|||
|
||||
subscriptions:
|
||||
'level-set-volume': (e) -> createjs.Sound.setVolume(e.volume)
|
||||
'level-show-victory': 'onShowVictory'
|
||||
'restart-level': 'onRestartLevel'
|
||||
'level-highlight-dom': 'onHighlightDom'
|
||||
'end-level-highlight-dom': 'onEndHighlight'
|
||||
'level-focus-dom': 'onFocusDom'
|
||||
|
@ -53,33 +54,33 @@ module.exports = class SpectateLevelView extends View
|
|||
'level-enable-controls': 'onEnableControls'
|
||||
'god:new-world-created': 'onNewWorld'
|
||||
'god:infinite-loop': 'onInfiniteLoop'
|
||||
'level-reload-from-data': 'onLevelReloadFromData'
|
||||
'play-next-level': 'onPlayNextLevel'
|
||||
'edit-wizard-settings': 'showWizardSettingsModal'
|
||||
'surface:world-set-up': 'onSurfaceSetUpNewWorld'
|
||||
'level:session-will-save': 'onSessionWillSave'
|
||||
'level:set-team': 'setTeam'
|
||||
'god:new-world-created': 'loadSoundsForWorld'
|
||||
|
||||
events:
|
||||
'click #level-done-button': 'onDonePressed'
|
||||
|
||||
shortcuts:
|
||||
'ctrl+s': 'onCtrlS'
|
||||
|
||||
constructor: (options, @levelID) ->
|
||||
console.profile?() if PROFILE_ME
|
||||
super options
|
||||
console.log @levelID
|
||||
|
||||
@ogreSessionID = @getQueryVariable 'ogres'
|
||||
@humanSessionID = @getQueryVariable 'humans'
|
||||
|
||||
@sessionID = @getQueryVariable 'session'
|
||||
|
||||
$(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>')
|
||||
|
||||
@supermodel.once 'error', @onLevelLoadError
|
||||
|
||||
@load()
|
||||
|
||||
|
||||
onLevelLoadError: (e) =>
|
||||
application.router.navigate "/play?not_found=#{@levelID}", {trigger: true}
|
||||
|
||||
setLevel: (@level, @supermodel) ->
|
||||
@god?.level = @level.serialize @supermodel
|
||||
|
@ -91,7 +92,8 @@ module.exports = class SpectateLevelView extends View
|
|||
|
||||
load: ->
|
||||
@levelLoader = new LevelLoader supermodel: @supermodel, levelID: @levelID, sessionID: @sessionID, opponentSessionID: @getQueryVariable('opponent'), team: @getQueryVariable("team")
|
||||
@levelLoader.once 'loaded-all', @onLevelLoaderLoaded
|
||||
@levelLoader.once 'loaded-all', @onLevelLoaderLoaded, @
|
||||
@levelLoader.on 'progress', @onLevelLoaderProgressChanged, @
|
||||
@god = new God()
|
||||
|
||||
getRenderData: ->
|
||||
|
@ -103,30 +105,83 @@ module.exports = class SpectateLevelView extends View
|
|||
window.onPlayLevelViewLoaded? @ # still a hack
|
||||
@loadingScreen = new LoadingScreen(@$el.find('canvas')[0])
|
||||
@loadingScreen.show()
|
||||
@$el.find('#level-done-button').hide()
|
||||
super()
|
||||
|
||||
onLevelLoaderLoaded: =>
|
||||
#needs editing
|
||||
@session = @levelLoader.session
|
||||
@world = @levelLoader.world
|
||||
@level = @levelLoader.level
|
||||
@levelLoader.destroy()
|
||||
@levelLoader = null
|
||||
onLevelLoaderProgressChanged: ->
|
||||
return if @seenDocs
|
||||
return unless showFrequency = @levelLoader.level.get('showGuide')
|
||||
session = @levelLoader.session
|
||||
diff = new Date().getTime() - new Date(session.get('created')).getTime()
|
||||
return if showFrequency is 'first-time' and diff > (5 * 60 * 1000)
|
||||
return unless @levelLoader.level.loaded
|
||||
articles = @levelLoader.supermodel.getModels Article
|
||||
for article in articles
|
||||
return unless article.loaded
|
||||
@showGuide()
|
||||
|
||||
showGuide: ->
|
||||
@seenDocs = true
|
||||
DocsModal = require './level/modal/docs_modal'
|
||||
options = {docs: @levelLoader.level.get('documentation'), supermodel: @supermodel}
|
||||
@openModalView(new DocsModal(options), true)
|
||||
Backbone.Mediator.subscribeOnce 'modal-closed', @onLevelLoaderLoaded, @
|
||||
return true
|
||||
|
||||
onLevelLoaderLoaded: ->
|
||||
return unless @levelLoader.progress() is 1 # double check, since closing the guide may trigger this early
|
||||
# Save latest level played in local storage
|
||||
if window.currentModal and not window.currentModal.destroyed
|
||||
@loadingScreen.showReady()
|
||||
return Backbone.Mediator.subscribeOnce 'modal-closed', @onLevelLoaderLoaded, @
|
||||
|
||||
localStorage["lastLevel"] = @levelID if localStorage?
|
||||
@grabLevelLoaderData()
|
||||
team = @getQueryVariable("team") ? @world.teamForPlayer(0)
|
||||
@loadOpponentTeam(team)
|
||||
@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)
|
||||
@setTeam team
|
||||
@initSurface()
|
||||
@initGoalManager()
|
||||
@initScriptManager()
|
||||
@insertSubviews()
|
||||
@insertSubviews ladderGame: @otherSession?
|
||||
@initVolume()
|
||||
@session.on 'change:multiplayer', @onMultiplayerChanged, @
|
||||
@originalSessionState = _.cloneDeep(@session.get('state'))
|
||||
@register()
|
||||
@controlBar.setBus(@bus)
|
||||
@surface.showLevel()
|
||||
if @otherSession
|
||||
# TODO: colorize name and cloud by team, colorize wizard by user's color config
|
||||
@surface.createOpponentWizard id: @otherSession.get('creator'), name: @otherSession.get('creatorName'), team: @otherSession.get('team')
|
||||
|
||||
grabLevelLoaderData: ->
|
||||
@session = @levelLoader.session
|
||||
@world = @levelLoader.world
|
||||
@level = @levelLoader.level
|
||||
@otherSession = @levelLoader.opponentSession
|
||||
@levelLoader.destroy()
|
||||
@levelLoader = null
|
||||
|
||||
loadOpponentTeam: (myTeam) ->
|
||||
opponentSpells = []
|
||||
for spellTeam, spells of @session.get('teamSpells') ? @otherSession?.get('teamSpells') ? {}
|
||||
continue if spellTeam is myTeam or not myTeam
|
||||
opponentSpells = opponentSpells.concat spells
|
||||
|
||||
opponentCode = @otherSession?.get('submittedCode') or {}
|
||||
myCode = @session.get('code') or {}
|
||||
for spell in opponentSpells
|
||||
[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.
|
||||
@session.set 'multiplayer', false
|
||||
|
||||
|
||||
onSupermodelLoadedOne: =>
|
||||
@modelsLoaded ?= 0
|
||||
|
@ -142,38 +197,66 @@ module.exports = class SpectateLevelView extends View
|
|||
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
|
||||
insertSubviews: (subviewOptions) ->
|
||||
@insertSubView @tome = new TomeView levelID: @levelID, session: @session, thangs: @world.thangs, supermodel: @supermodel, ladderGame: subviewOptions.ladderGame
|
||||
@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}
|
||||
@controlBar = @insertSubView new ControlBarView {worldName: worldName, session: @session, level: @level, supermodel: @supermodel, playableTeams: @world.playableTeams, ladderGame: subviewOptions.ladderGame}
|
||||
#Backbone.Mediator.publish('level-set-debug', debug: true) if me.displayName() is 'Nick!'
|
||||
|
||||
afterInsert: ->
|
||||
super()
|
||||
@showWizardSettingsModal() if not me.get('name')
|
||||
|
||||
# callbacks
|
||||
|
||||
onCtrlS: (e) ->
|
||||
e.preventDefault()
|
||||
|
||||
onLevelReloadFromData: (e) ->
|
||||
isReload = Boolean @world
|
||||
@setLevel e.level, e.supermodel
|
||||
if isReload
|
||||
@scriptManager.setScripts(e.level.get('scripts'))
|
||||
Backbone.Mediator.publish 'tome:cast-spell' # a bit hacky
|
||||
|
||||
onWindowResize: (s...) ->
|
||||
$('#pointer').css('opacity', 0.0)
|
||||
|
||||
onDisableControls: (e) =>
|
||||
onDisableControls: (e) ->
|
||||
return if e.controls and not ('level' in e.controls)
|
||||
@shortcutsEnabled = false
|
||||
@wasFocusedOn = document.activeElement
|
||||
$('body').focus()
|
||||
|
||||
onEnableControls: (e) =>
|
||||
onEnableControls: (e) ->
|
||||
return if e.controls? and not ('level' in e.controls)
|
||||
@shortcutsEnabled = true
|
||||
$(@wasFocusedOn).focus() if @wasFocusedOn
|
||||
@wasFocusedOn = null
|
||||
|
||||
onDonePressed: => @showVictory()
|
||||
onDonePressed: -> @showVictory()
|
||||
|
||||
onShowVictory: (e) ->
|
||||
$('#level-done-button').show()
|
||||
@showVictory() if e.showModal
|
||||
setTimeout(@preloadNextLevel, 3000)
|
||||
|
||||
showVictory: ->
|
||||
options = {level: @level, supermodel: @supermodel, session:@session}
|
||||
docs = new VictoryModal(options)
|
||||
@openModalView(docs)
|
||||
window.tracker?.trackEvent 'Saw Victory', level: @world.name, label: @world.name
|
||||
|
||||
onRestartLevel: ->
|
||||
@tome.reloadAllCode()
|
||||
Backbone.Mediator.publish 'level:restarted'
|
||||
$('#level-done-button', @$el).hide()
|
||||
window.tracker?.trackEvent 'Confirmed Restart', level: @world.name, label: @world.name
|
||||
|
||||
onNewWorld: (e) ->
|
||||
@world = e.world
|
||||
|
@ -183,13 +266,21 @@ module.exports = class SpectateLevelView extends View
|
|||
@openModalView new InfiniteLoopModal()
|
||||
window.tracker?.trackEvent 'Saw Initial Infinite Loop', level: @world.name, label: @world.name
|
||||
|
||||
onPlayNextLevel: ->
|
||||
nextLevel = @getNextLevel()
|
||||
nextLevelID = nextLevel.get('slug') or nextLevel.id
|
||||
url = "/play/level/#{nextLevelID}"
|
||||
Backbone.Mediator.publish 'router:navigate', {
|
||||
route: url,
|
||||
viewClass: PlayLevelView,
|
||||
viewArgs: [{supermodel:@supermodel}, nextLevelID]}
|
||||
|
||||
getNextLevel: ->
|
||||
nextLevelOriginal = @level.get('nextLevel')?.original
|
||||
levels = @supermodel.getModels(Level)
|
||||
return l for l in levels when l.get('original') is nextLevelOriginal
|
||||
|
||||
onHighlightDom: (e) =>
|
||||
onHighlightDom: (e) ->
|
||||
if e.delay
|
||||
delay = e.delay
|
||||
delete e.delay
|
||||
|
@ -243,19 +334,25 @@ module.exports = class SpectateLevelView extends View
|
|||
), 1)
|
||||
|
||||
|
||||
animatePointer: =>
|
||||
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()
|
||||
onFocusDom: (e) -> $(e.selector).focus()
|
||||
|
||||
onEndHighlight: =>
|
||||
onEndHighlight: ->
|
||||
$('#pointer').css('opacity', 0.0)
|
||||
clearInterval(@pointerInterval)
|
||||
|
||||
onMultiplayerChanged: (e) ->
|
||||
if @session.get('multiplayer')
|
||||
@bus.connect()
|
||||
else
|
||||
@bus.removeFirebaseData =>
|
||||
@bus.disconnect()
|
||||
|
||||
# initialization
|
||||
|
||||
|
@ -273,7 +370,7 @@ module.exports = class SpectateLevelView extends View
|
|||
@surface.camera.zoomTo({x:0, y:0}, 0.1, 0)
|
||||
|
||||
initGoalManager: ->
|
||||
@goalManager = new GoalManager(@world)
|
||||
@goalManager = new GoalManager(@world, @level.get('goals'))
|
||||
@god.goalManager = @goalManager
|
||||
|
||||
initScriptManager: ->
|
||||
|
@ -297,11 +394,18 @@ module.exports = class SpectateLevelView extends View
|
|||
if state.playing?
|
||||
Backbone.Mediator.publish 'level-set-playing', { playing: state.playing }
|
||||
|
||||
preloadNextLevel: =>
|
||||
# TODO: Loading models in the middle of gameplay causes stuttering. Most of the improvement in loading time is simply from passing the supermodel from this level to the next, but if we can find a way to populate the level early without it being noticeable, that would be even better.
|
||||
# return if @destroyed
|
||||
# return if @preloaded
|
||||
# nextLevel = @getNextLevel()
|
||||
# @supermodel.populateModel nextLevel
|
||||
# @preloaded = true
|
||||
|
||||
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) ->
|
||||
|
@ -319,7 +423,20 @@ module.exports = class SpectateLevelView extends View
|
|||
me.team = team
|
||||
Backbone.Mediator.publish 'level:team-set', team: team
|
||||
|
||||
# Dynamic sound loading
|
||||
|
||||
loadSoundsForWorld: (e) ->
|
||||
return if @headless
|
||||
world = e.world
|
||||
thangTypes = @supermodel.getModels(ThangType)
|
||||
for [spriteName, message] in world.thangDialogueSounds()
|
||||
continue unless thangType = _.find thangTypes, (m) -> m.get('name') is spriteName
|
||||
continue unless sound = AudioPlayer.soundForDialogue message, thangType.get('soundTriggers')
|
||||
AudioPlayer.preloadSoundReference sound
|
||||
|
||||
destroy: ->
|
||||
@supermodel?.off 'error', @onLevelLoadError
|
||||
@levelLoader?.off 'loaded-all', @onLevelLoaderLoaded
|
||||
@levelLoader?.destroy()
|
||||
@surface?.destroy()
|
||||
@god?.destroy()
|
||||
|
@ -327,10 +444,14 @@ module.exports = class SpectateLevelView extends View
|
|||
@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, @
|
||||
@session?.off 'change:multiplayer', @onMultiplayerChanged, @
|
||||
@onLevelLoadError = null
|
||||
@onLevelLoaderLoaded = null
|
||||
@onSupermodelLoadedOne = null
|
||||
@preloadNextLevel = null
|
||||
@saveScreenshot = null
|
||||
super()
|
||||
|
|
|
@ -76,7 +76,7 @@ sendLadderUpdateEmail = (session, daysAgo) ->
|
|||
# (We could look at strongest/weakest, but we'd have to fetch everyone, or denormalize more.)
|
||||
matches = _.filter session.matches, (match) -> match.date >= (new Date() - 86400 * 1000 * daysAgo)
|
||||
defeats = _.filter matches, (match) -> match.metrics.rank is 1 and match.opponents[0].metrics.rank is 0
|
||||
victories = _.filter matches, (match) -> match.metrics.rank is 0
|
||||
victories = _.filter matches, (match) -> match.metrics.rank is 0 and match.opponents[0].metrics.rank is 1
|
||||
defeat = _.last defeats
|
||||
victory = _.last victories
|
||||
|
||||
|
|
126
vendor/scripts/idle.js
vendored
Normal file
126
vendor/scripts/idle.js
vendored
Normal file
|
@ -0,0 +1,126 @@
|
|||
// https://github.com/shawnmclean/Idle.js
|
||||
(function() {
|
||||
"use strict";
|
||||
var Idle;
|
||||
|
||||
Idle = {};
|
||||
|
||||
Idle = (function() {
|
||||
Idle.isAway = false;
|
||||
|
||||
Idle.awayTimeout = 3000;
|
||||
|
||||
Idle.awayTimestamp = 0;
|
||||
|
||||
Idle.awayTimer = null;
|
||||
|
||||
Idle.onAway = null;
|
||||
|
||||
Idle.onAwayBack = null;
|
||||
|
||||
Idle.onVisible = null;
|
||||
|
||||
Idle.onHidden = null;
|
||||
|
||||
function Idle(options) {
|
||||
var activeMethod, activity;
|
||||
|
||||
if (options) {
|
||||
this.awayTimeout = parseInt(options.awayTimeout, 10);
|
||||
this.onAway = options.onAway;
|
||||
this.onAwayBack = options.onAwayBack;
|
||||
this.onVisible = options.onVisible;
|
||||
this.onHidden = options.onHidden;
|
||||
}
|
||||
activity = this;
|
||||
activeMethod = function() {
|
||||
return activity.onActive();
|
||||
};
|
||||
window.onclick = activeMethod;
|
||||
window.onmousemove = activeMethod;
|
||||
window.onmouseenter = activeMethod;
|
||||
window.onkeydown = activeMethod;
|
||||
window.onscroll = activeMethod;
|
||||
window.onmousewheel = activeMethod;
|
||||
document.addEventListener("visibilitychange", (function() {
|
||||
return activity.handleVisibilityChange();
|
||||
}), false);
|
||||
document.addEventListener("webkitvisibilitychange", (function() {
|
||||
return activity.handleVisibilityChange();
|
||||
}), false);
|
||||
document.addEventListener("msvisibilitychange", (function() {
|
||||
return activity.handleVisibilityChange();
|
||||
}), false);
|
||||
}
|
||||
|
||||
Idle.prototype.onActive = function() {
|
||||
this.awayTimestamp = new Date().getTime() + this.awayTimeout;
|
||||
if (this.isAway) {
|
||||
if (this.onAwayBack) {
|
||||
this.onAwayBack();
|
||||
}
|
||||
this.start();
|
||||
}
|
||||
this.isAway = false;
|
||||
return true;
|
||||
};
|
||||
|
||||
Idle.prototype.start = function() {
|
||||
var activity;
|
||||
|
||||
this.awayTimestamp = new Date().getTime() + this.awayTimeout;
|
||||
if (this.awayTimer !== null) {
|
||||
clearTimeout(this.awayTimer);
|
||||
}
|
||||
activity = this;
|
||||
this.awayTimer = setTimeout((function() {
|
||||
return activity.checkAway();
|
||||
}), this.awayTimeout + 100);
|
||||
return this;
|
||||
};
|
||||
|
||||
Idle.prototype.setAwayTimeout = function(ms) {
|
||||
this.awayTimeout = parseInt(ms, 10);
|
||||
return this;
|
||||
};
|
||||
|
||||
Idle.prototype.checkAway = function() {
|
||||
var activity, t;
|
||||
|
||||
t = new Date().getTime();
|
||||
if (t < this.awayTimestamp) {
|
||||
this.isAway = false;
|
||||
activity = this;
|
||||
this.awayTimer = setTimeout((function() {
|
||||
return activity.checkAway();
|
||||
}), this.awayTimestamp - t + 100);
|
||||
return;
|
||||
}
|
||||
if (this.awayTimer !== null) {
|
||||
clearTimeout(this.awayTimer);
|
||||
}
|
||||
this.isAway = true;
|
||||
if (this.onAway) {
|
||||
return this.onAway();
|
||||
}
|
||||
};
|
||||
|
||||
Idle.prototype.handleVisibilityChange = function() {
|
||||
if (document.hidden || document.msHidden || document.webkitHidden) {
|
||||
if (this.onHidden) {
|
||||
return this.onHidden();
|
||||
}
|
||||
} else {
|
||||
if (this.onVisible) {
|
||||
return this.onVisible();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return Idle;
|
||||
|
||||
})();
|
||||
|
||||
window.Idle = Idle;
|
||||
|
||||
}).call(this);
|
Loading…
Reference in a new issue