mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-11-28 18:15:52 -05:00
Merge branch 'master' into production
This commit is contained in:
commit
bfeb1457a1
68 changed files with 7103 additions and 6920 deletions
Binary file not shown.
Before Width: | Height: | Size: 65 KiB After Width: | Height: | Size: 64 KiB |
BIN
app/assets/images/pages/play/modal/k-means.gif
Normal file
BIN
app/assets/images/pages/play/modal/k-means.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 MiB |
|
@ -130,7 +130,7 @@ module.exports = class Angel extends CocoClass
|
|||
eventType = if finished then 'god:new-world-created' else 'god:streaming-world-updated'
|
||||
if finished
|
||||
@shared.world = world
|
||||
Backbone.Mediator.publish eventType, world: world, firstWorld: @shared.firstWorld, goalStates: goalStates, team: me.team, firstChangedFrame: firstChangedFrame
|
||||
Backbone.Mediator.publish eventType, world: world, firstWorld: @shared.firstWorld, goalStates: goalStates, team: me.team, firstChangedFrame: firstChangedFrame, finished: finished
|
||||
if finished
|
||||
for scriptNote in @shared.world.scriptNotes
|
||||
Backbone.Mediator.publish scriptNote.channel, scriptNote.event
|
||||
|
|
|
@ -123,7 +123,7 @@ class AudioPlayer extends CocoClass
|
|||
return if filename of cache
|
||||
name ?= filename
|
||||
# SoundJS flips out if you try to register the same file twice
|
||||
createjs.Sound.registerSound(filename, name, 1, true) # 1: 1 channel, true: should preload
|
||||
result = createjs.Sound.registerSound(filename, name, 1) # 1: 1 channel
|
||||
cache[filename] = new Media(name)
|
||||
|
||||
# PROGRESS CALLBACKS
|
||||
|
|
|
@ -91,7 +91,9 @@ module.exports = GPlusHandler = class GPlusHandler extends CocoClass
|
|||
@saveIfAllDone()
|
||||
|
||||
saveIfAllDone: =>
|
||||
console.debug 'Save if all done. Person loaded:', @personLoaded, 'and email loaded:', @emailLoaded
|
||||
return unless @personLoaded and @emailLoaded
|
||||
console.debug 'Email, gplusID:', me.get('email'), me.get('gplusID')
|
||||
return unless me.get('email') and me.get('gplusID')
|
||||
|
||||
Backbone.Mediator.publish 'auth:logging-in-with-gplus', {}
|
||||
|
@ -104,12 +106,16 @@ module.exports = GPlusHandler = class GPlusHandler extends CocoClass
|
|||
patch.email = me.get('email')
|
||||
wasAnonymous = me.get('anonymous')
|
||||
@trigger 'logging-into-codecombat'
|
||||
console.debug('Logging into GPlus.')
|
||||
me.save(patch, {
|
||||
patch: true
|
||||
type: 'PUT'
|
||||
error: backboneFailure,
|
||||
error: ->
|
||||
console.debug('Logging into GPlus fail.', arguments)
|
||||
backboneFailure(arguments...)
|
||||
url: "/db/user?gplusID=#{gplusID}&gplusAccessToken=#{@accessToken.access_token}"
|
||||
success: (model) ->
|
||||
console.debug('GPLus login success!')
|
||||
window.location.reload() if wasAnonymous and not model.get('anonymous')
|
||||
})
|
||||
|
||||
|
|
|
@ -211,10 +211,12 @@ module.exports = class LevelBus extends Bus
|
|||
@changedSessionProperties.state = true
|
||||
@reallySaveSession() # Make sure it saves right away; don't debounce it.
|
||||
|
||||
onNewGoalStates: ({goalStates}) ->
|
||||
onNewGoalStates: (e) ->
|
||||
# TODO: this log doesn't capture when null-status goals are being set during world streaming. Where can they be coming from?
|
||||
goalStates = e.goalStates
|
||||
return console.error("Somehow trying to save null goal states!", newGoalStates) if _.find(newGoalStates, (gs) -> not gs.status)
|
||||
|
||||
return unless e.overallStatus is 'success'
|
||||
newGoalStates = goalStates
|
||||
state = @session.get('state')
|
||||
oldGoalStates = state.goalStates or {}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
module.exports = LevelOptions =
|
||||
'dungeons-of-kithgard':
|
||||
disableSpaces: true
|
||||
hidesSubmitUntilRun: true
|
||||
hidesPlayButton: true
|
||||
hidesRunShortcut: true
|
||||
|
@ -9,7 +10,9 @@ module.exports = LevelOptions =
|
|||
hidesRealTimePlayback: true
|
||||
requiredGear: {feet: 'simple-boots'}
|
||||
restrictedGear: {feet: 'leather-boots'}
|
||||
requiredCode: ['moveRight']
|
||||
'gems-in-the-deep':
|
||||
disableSpaces: true
|
||||
hidesSubmitUntilRun: true
|
||||
hidesPlayButton: true
|
||||
hidesRunShortcut: true
|
||||
|
@ -20,6 +23,7 @@ module.exports = LevelOptions =
|
|||
requiredGear: {feet: 'simple-boots'}
|
||||
restrictedGear: {feet: 'leather-boots'}
|
||||
'shadow-guard':
|
||||
disableSpaces: true
|
||||
hidesSubmitUntilRun: true
|
||||
hidesPlayButton: true
|
||||
hidesRunShortcut: true
|
||||
|
@ -30,6 +34,7 @@ module.exports = LevelOptions =
|
|||
requiredGear: {feet: 'simple-boots'}
|
||||
restrictedGear: {feet: 'leather-boots', 'right-hand': 'simple-sword'}
|
||||
'kounter-kithwise':
|
||||
disableSpaces: true
|
||||
hidesPlayButton: true
|
||||
hidesRunShortcut: true
|
||||
hidesHUD: true
|
||||
|
@ -48,6 +53,7 @@ module.exports = LevelOptions =
|
|||
requiredGear: {feet: 'simple-boots'}
|
||||
restrictedGear: {feet: 'leather-boots', 'right-hand': 'simple-sword', 'programming-book': 'programmaticon-i'}
|
||||
'forgetful-gemsmith':
|
||||
disableSpaces: true
|
||||
hidesPlayButton: true
|
||||
hidesRunShortcut: true
|
||||
hidesHUD: true
|
||||
|
@ -57,6 +63,7 @@ module.exports = LevelOptions =
|
|||
requiredGear: {feet: 'simple-boots'}
|
||||
restrictedGear: {feet: 'leather-boots', 'programming-book': 'programmaticon-i'}
|
||||
'true-names':
|
||||
disableSpaces: true
|
||||
hidesPlayButton: true
|
||||
hidesRunShortcut: true
|
||||
hidesHUD: true
|
||||
|
@ -65,7 +72,10 @@ module.exports = LevelOptions =
|
|||
hidesRealTimePlayback: true
|
||||
requiredGear: {feet: 'simple-boots', 'right-hand': 'simple-sword', waist: 'leather-belt'}
|
||||
restrictedGear: {feet: 'leather-boots'}
|
||||
requiredCode: ['Brak']
|
||||
'favorable-odds':
|
||||
disableSpaces: true
|
||||
hidesPlayButton: true
|
||||
hidesRunShortcut: true
|
||||
hidesHUD: true
|
||||
hidesSay: true
|
||||
|
@ -74,6 +84,8 @@ module.exports = LevelOptions =
|
|||
requiredGear: {feet: 'simple-boots', 'right-hand': 'simple-sword'}
|
||||
restrictedGear: {feet: 'leather-boots'}
|
||||
'the-raised-sword':
|
||||
disableSpaces: true
|
||||
hidesPlayButton: true
|
||||
hidesRunShortcut: true
|
||||
hidesHUD: true
|
||||
hidesSay: true
|
||||
|
@ -89,6 +101,7 @@ module.exports = LevelOptions =
|
|||
hidesRealTimePlayback: true
|
||||
requiredGear: {feet: 'simple-boots', 'programming-book': 'programmaticon-i'}
|
||||
restrictedGear: {feet: 'leather-boots'}
|
||||
requiredCode: ['loop']
|
||||
'haunted-kithmaze':
|
||||
hidesRunShortcut: true
|
||||
hidesHUD: true
|
||||
|
@ -97,6 +110,7 @@ module.exports = LevelOptions =
|
|||
hidesRealTimePlayback: true
|
||||
requiredGear: {feet: 'simple-boots', 'programming-book': 'programmaticon-i'}
|
||||
restrictedGear: {feet: 'leather-boots'}
|
||||
requiredCode: ['loop']
|
||||
'descending-further':
|
||||
hidesHUD: true
|
||||
hidesSay: true
|
||||
|
@ -132,6 +146,8 @@ module.exports = LevelOptions =
|
|||
hidesRealTimePlayback: true
|
||||
requiredGear: {feet: 'simple-boots', 'right-hand': 'simple-sword', 'programming-book': 'programmaticon-i', eyes: 'crude-glasses', torso: 'leather-tunic'}
|
||||
restrictedGear: {feet: 'leather-boots'}
|
||||
requiredCode: ['findNearestEnemy']
|
||||
suspectCode: [{name: 'lone-find-nearest-enemy', pattern: /^[ ]*(self|this|@)?[:.]?findNearestEnemy()/m}]
|
||||
'lowly-kithmen':
|
||||
hidesHUD: true
|
||||
hidesSay: true
|
||||
|
@ -139,6 +155,8 @@ module.exports = LevelOptions =
|
|||
hidesRealTimePlayback: true
|
||||
requiredGear: {feet: 'simple-boots', 'right-hand': 'simple-sword', 'programming-book': 'programmaticon-i', eyes: 'crude-glasses', torso: 'leather-tunic'}
|
||||
restrictedGear: {feet: 'leather-boots'}
|
||||
requiredCode: ['findNearestEnemy']
|
||||
suspectCode: [{name: 'lone-find-nearest-enemy', pattern: /^[ ]*(self|this|@)?[:.]?findNearestEnemy()/m}]
|
||||
'closing-the-distance':
|
||||
hidesHUD: true
|
||||
hidesSay: true
|
||||
|
@ -146,6 +164,7 @@ module.exports = LevelOptions =
|
|||
hidesRealTimePlayback: true
|
||||
requiredGear: {feet: 'simple-boots', 'right-hand': 'simple-sword', torso: 'leather-tunic', eyes: 'crude-glasses'}
|
||||
restrictedGear: {feet: 'leather-boots'}
|
||||
suspectCode: [{name: 'lone-find-nearest-enemy', pattern: /^[ ]*(self|this|@)?[:.]?findNearestEnemy()/m}]
|
||||
'tactical-strike':
|
||||
hidesHUD: true
|
||||
hidesSay: true
|
||||
|
@ -153,12 +172,14 @@ module.exports = LevelOptions =
|
|||
hidesRealTimePlayback: true
|
||||
requiredGear: {feet: 'simple-boots', 'right-hand': 'simple-sword', torso: 'leather-tunic', eyes: 'crude-glasses'}
|
||||
restrictedGear: {feet: 'leather-boots'}
|
||||
suspectCode: [{name: 'lone-find-nearest-enemy', pattern: /^[ ]*(self|this|@)?[:.]?findNearestEnemy()/m}]
|
||||
'the-final-kithmaze':
|
||||
hidesHUD: true
|
||||
hidesSay: true
|
||||
hidesCodeToolbar: true
|
||||
hidesRealTimePlayback: true
|
||||
requiredGear: {feet: 'simple-boots', 'right-hand': 'simple-sword', torso: 'leather-tunic', 'programming-book': 'programmaticon-i', eyes: 'crude-glasses'}
|
||||
suspectCode: [{name: 'lone-find-nearest-enemy', pattern: /^[ ]*(self|this|@)?[:.]?findNearestEnemy()/m}]
|
||||
'the-gauntlet':
|
||||
hidesHUD: true
|
||||
hidesSay: true
|
||||
|
@ -166,6 +187,7 @@ module.exports = LevelOptions =
|
|||
hidesRealTimePlayback: true
|
||||
requiredGear: {feet: 'simple-boots', 'right-hand': 'simple-sword', torso: 'leather-tunic', 'programming-book': 'programmaticon-i', eyes: 'crude-glasses'}
|
||||
restrictedGear: {feet: 'leather-boots'}
|
||||
suspectCode: [{name: 'lone-find-nearest-enemy', pattern: /^[ ]*(self|this|@)?[:.]?findNearestEnemy()/m}]
|
||||
'kithgard-gates':
|
||||
hidesSay: true
|
||||
hidesCodeToolbar: true
|
||||
|
|
|
@ -6,6 +6,7 @@ module.exports = class CoordinateDisplay extends createjs.Container
|
|||
'surface:mouse-over': 'onMouseOver'
|
||||
'surface:stage-mouse-down': 'onMouseDown'
|
||||
'camera:zoom-updated': 'onZoomUpdated'
|
||||
'level:flag-color-selected': 'onFlagColorSelected'
|
||||
|
||||
constructor: (options) ->
|
||||
super()
|
||||
|
@ -60,6 +61,9 @@ module.exports = class CoordinateDisplay extends createjs.Container
|
|||
@hide()
|
||||
@show()
|
||||
|
||||
onFlagColorSelected: (e) ->
|
||||
@placingFlag = Boolean e.color
|
||||
|
||||
hide: ->
|
||||
return unless @label.parent
|
||||
@removeChild @label
|
||||
|
@ -154,6 +158,6 @@ module.exports = class CoordinateDisplay extends createjs.Container
|
|||
@y = sup.y
|
||||
@addChild @background
|
||||
@addChild @label
|
||||
@addChild @pointMarker
|
||||
@addChild @pointMarker unless @placingFlag
|
||||
@updateCache()
|
||||
Backbone.Mediator.publish 'surface:coordinates-shown', {}
|
||||
|
|
|
@ -491,6 +491,7 @@ module.exports = Lank = class Lank extends CocoClass
|
|||
bar = createProgressBar(healthColor)
|
||||
@options.floatingLayer.addCustomGraphic(key, bar, bar.bounds)
|
||||
|
||||
hadHealthBar = @healthBar
|
||||
@healthBar = new createjs.Sprite(@options.floatingLayer.spriteSheet)
|
||||
@healthBar.gotoAndStop(key)
|
||||
offset = @getOffset 'aboveHead'
|
||||
|
@ -499,6 +500,8 @@ module.exports = Lank = class Lank extends CocoClass
|
|||
@options.floatingLayer.addChild @healthBar
|
||||
@updateHealthBar()
|
||||
@lastHealth = null
|
||||
if not hadHealthBar
|
||||
@listenTo @options.floatingLayer, 'new-spritesheet', @addHealthBar
|
||||
|
||||
getActionProp: (prop, subProp, def=null) ->
|
||||
# Get a property or sub-property from an action, falling back to ThangType
|
||||
|
@ -664,7 +667,9 @@ module.exports = Lank = class Lank extends CocoClass
|
|||
updateLabels: ->
|
||||
return unless @thang
|
||||
blurb = if @thang.health <= 0 then null else @thang.sayMessage # Dead men tell no tales
|
||||
@addLabel 'say', Label.STYLE_SAY if blurb
|
||||
blurb = null if blurb in ['For Thoktar!', 'Bones!', 'Behead!', 'Destroy!', 'Die, humans!'] # Let's just hear, not see, these ones.
|
||||
labelStyle = if /Hero Placeholder/.test(@thang.id) then Label.STYLE_DIALOGUE else Label.STYLE_SAY
|
||||
@addLabel 'say', labelStyle if blurb
|
||||
if @labels.say?.setText blurb
|
||||
@notifySpeechUpdated blurb: blurb
|
||||
label.update() for name, label of @labels
|
||||
|
|
|
@ -13,7 +13,7 @@ module.exports = class SegmentedSprite extends createjs.SpriteContainer
|
|||
|
||||
constructor: (@spriteSheet, @thangType, @spriteSheetPrefix, @resolutionFactor=SPRITE_RESOLUTION_FACTOR) ->
|
||||
@spriteSheet.mcPool ?= {}
|
||||
@initialize(@spriteSheet)
|
||||
super(@spriteSheet)
|
||||
@addEventListener 'tick', @handleTick
|
||||
|
||||
destroy: ->
|
||||
|
|
|
@ -6,7 +6,7 @@ module.exports = class SingularSprite extends createjs.Sprite
|
|||
childMovieClips: null
|
||||
|
||||
constructor: (@spriteSheet, @thangType, @spriteSheetPrefix, @resolutionFactor=SPRITE_RESOLUTION_FACTOR) ->
|
||||
@initialize(@spriteSheet)
|
||||
super(@spriteSheet)
|
||||
|
||||
destroy: ->
|
||||
@removeAllEventListeners()
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
CocoClass = require 'lib/CocoClass'
|
||||
path = require './path'
|
||||
TrailMaster = require './TrailMaster'
|
||||
Dropper = require './Dropper'
|
||||
AudioPlayer = require 'lib/AudioPlayer'
|
||||
{me} = require 'lib/auth'
|
||||
|
@ -181,7 +181,6 @@ module.exports = Surface = class Surface extends CocoClass
|
|||
framesDropped = 0
|
||||
while true
|
||||
Dropper.tick()
|
||||
@trailmaster.tick() if @trailmaster
|
||||
# Skip some frame updates unless we're playing and not at end (or we haven't drawn much yet)
|
||||
frameAdvanced = (@playing and @currentFrame < lastFrame) or @totalFramesDrawn < 2
|
||||
if frameAdvanced and @playing
|
||||
|
@ -210,7 +209,6 @@ module.exports = Surface = class Surface extends CocoClass
|
|||
@updateState @currentFrame isnt oldFrame
|
||||
@drawCurrentFrame e
|
||||
@onFrameChanged()
|
||||
@updatePaths() if (@totalFramesDrawn % 4) is 0 or createjs.Ticker.getMeasuredFPS() > createjs.Ticker.getFPS() - 5
|
||||
Backbone.Mediator.publish('surface:ticked', {dt: 1 / @options.frameRate})
|
||||
mib = @webGLStage.mouseInBounds
|
||||
if @mouseInBounds isnt mib
|
||||
|
@ -260,7 +258,9 @@ module.exports = Surface = class Surface extends CocoClass
|
|||
createjs.Tween.removeTweens(@)
|
||||
@currentFrame = @scrubbingTo
|
||||
|
||||
@scrubbingTo = Math.min(Math.round(progress * (@world.frames.length - 1)), @world.frames.length - 1)
|
||||
@scrubbingTo = Math.round(progress * (@world.frames.length - 1))
|
||||
@scrubbingTo = Math.max @scrubbingTo, 1
|
||||
@scrubbingTo = Math.min @scrubbingTo, @world.frames.length - 1
|
||||
@scrubbingPlaybackSpeed = Math.sqrt(Math.abs(@scrubbingTo - @currentFrame) * @world.dt / (scrubDuration or 0.5))
|
||||
if scrubDuration
|
||||
t = createjs.Tween
|
||||
|
@ -314,10 +314,12 @@ module.exports = Surface = class Surface extends CocoClass
|
|||
if paused
|
||||
@surfacePauseTimeout = _.delay performToggle, 2000
|
||||
@lankBoss.stop()
|
||||
@trailmaster?.stop()
|
||||
@playbackOverScreen.show()
|
||||
else
|
||||
performToggle()
|
||||
@lankBoss.play()
|
||||
@trailmaster?.play()
|
||||
@playbackOverScreen.hide()
|
||||
|
||||
|
||||
|
@ -402,7 +404,7 @@ module.exports = Surface = class Surface extends CocoClass
|
|||
@playing = (e ? {}).playing ? true
|
||||
@setPlayingCalled = true
|
||||
if @playing and @currentFrame >= (@world.totalFrames - 5)
|
||||
@currentFrame = 0
|
||||
@currentFrame = 1 # Go back to the beginning (but not frame 0, that frame is weird)
|
||||
if @fastForwardingToFrame and not @playing
|
||||
@fastForwardingToFrame = null
|
||||
|
||||
|
@ -456,7 +458,11 @@ module.exports = Surface = class Surface extends CocoClass
|
|||
@fastForwardingSpeed = lag / intendedLag
|
||||
else
|
||||
@fastForwardingToFrame = @fastForwardingSpeed = null
|
||||
#console.log "on new world, lag", lag, "intended lag", intendedLag, "fastForwardingToFrame", @fastForwardingToFrame, "speed", @fastForwardingSpeed, "cause we are at", @world.age, "of", @world.frames.length * @world.dt
|
||||
# console.log "on new world, lag", lag, "intended lag", intendedLag, "fastForwardingToFrame", @fastForwardingToFrame, "speed", @fastForwardingSpeed, "cause we are at", @world.age, "of", @world.frames.length * @world.dt
|
||||
if event.finished
|
||||
@updatePaths()
|
||||
else
|
||||
@hidePaths()
|
||||
|
||||
onIdleChanged: (e) ->
|
||||
@setPaused e.idle unless @ended
|
||||
|
@ -540,10 +546,12 @@ module.exports = Surface = class Surface extends CocoClass
|
|||
|
||||
#- Camera focus on hero
|
||||
focusOnHero: ->
|
||||
hadHero = @heroLank
|
||||
@heroLank = @lankBoss.lankFor 'Hero Placeholder'
|
||||
if me.team is 'ogres'
|
||||
# TODO: do this for real
|
||||
@heroLank = @lankBoss.lankFor 'Hero Placeholder 1'
|
||||
@updatePaths() if not hadHero
|
||||
|
||||
#- Real-time playback
|
||||
|
||||
|
@ -576,22 +584,16 @@ module.exports = Surface = class Surface extends CocoClass
|
|||
|
||||
|
||||
|
||||
#- Paths - TODO: move to LankBoss? but only update on frame drawing instead of on every frame update?
|
||||
|
||||
updatePaths: ->
|
||||
return # TODO: Get paths working again with WebGL
|
||||
return unless @options.paths
|
||||
return if @casting
|
||||
return unless @options.paths and @heroLank
|
||||
return unless me.isAdmin() # TODO: Fix world thang points, targets, then remove this
|
||||
@hidePaths()
|
||||
selectedThang = @lankBoss.selectedLank?.thang
|
||||
return if @world.showPaths is 'never'
|
||||
return if @world.showPaths is 'paused' and @playing
|
||||
return if @world.showPaths is 'selected' and not selectedThang
|
||||
@trailmaster ?= new path.Trailmaster @camera
|
||||
selectedOnly = @playing and @world.showPaths is 'selected'
|
||||
@paths = @trailmaster.generatePaths @world, @getCurrentFrame(), selectedThang, @lankBoss.lanks, selectedOnly
|
||||
layerAdapter = @lankBoss.layerAdapters['Path']
|
||||
@trailmaster ?= new TrailMaster @camera, layerAdapter
|
||||
@paths = @trailmaster.generatePaths @world, @heroLank.thang
|
||||
@paths.name = 'paths'
|
||||
@lankBoss.layerAdapters['Path'].addChild @paths
|
||||
layerAdapter.addChild @paths
|
||||
|
||||
hidePaths: ->
|
||||
return if not @paths
|
||||
|
@ -699,6 +701,7 @@ module.exports = Surface = class Surface extends CocoClass
|
|||
@normalStage.clear()
|
||||
@webGLStage.clear()
|
||||
@musicPlayer?.destroy()
|
||||
@trailmaster?.destroy()
|
||||
@normalStage.removeAllChildren()
|
||||
@webGLStage.removeAllChildren()
|
||||
@webGLStage.removeEventListener 'stagemousemove', @onMouseMove
|
||||
|
|
122
app/lib/surface/TrailMaster.coffee
Normal file
122
app/lib/surface/TrailMaster.coffee
Normal file
|
@ -0,0 +1,122 @@
|
|||
PAST_PATH_ALPHA = 0.75
|
||||
PAST_PATH_WIDTH = 5
|
||||
FUTURE_PATH_ALPHA = 0.75
|
||||
FUTURE_PATH_WIDTH = 4
|
||||
TARGET_ALPHA = 1
|
||||
TARGET_WIDTH = 10
|
||||
FUTURE_PATH_INTERVAL_DIVISOR = 4
|
||||
PAST_PATH_INTERVAL_DIVISOR = 2
|
||||
|
||||
Camera = require './Camera'
|
||||
CocoClass = require 'lib/CocoClass'
|
||||
|
||||
module.exports = class TrailMaster extends CocoClass
|
||||
world: null
|
||||
|
||||
constructor: (@camera, @layerAdapter) ->
|
||||
super()
|
||||
@tweenedSprites = []
|
||||
@tweens = []
|
||||
@listenTo @layerAdapter, 'new-spritesheet', -> @generatePaths(@world, @thang)
|
||||
|
||||
generatePaths: (@world, @thang) ->
|
||||
return if @generatingPaths
|
||||
@generatingPaths = true
|
||||
@cleanUp()
|
||||
@createGraphics()
|
||||
pathDisplayObject = new createjs.SpriteContainer(@layerAdapter.spriteSheet)
|
||||
pathDisplayObject.mouseEnabled = pathDisplayObject.mouseChildren = false
|
||||
pathDisplayObject.addChild @createFuturePath()
|
||||
# pathDisplayObject.addChild @createPastPath() # Just made the animated path the full path... do we want to have past and future look different again?
|
||||
pathDisplayObject.addChild @createTargets()
|
||||
@generatingPaths = false
|
||||
return pathDisplayObject
|
||||
|
||||
cleanUp: ->
|
||||
createjs.Tween.removeTweens(sprite) for sprite in @tweenedSprites
|
||||
@tweenedSprites = []
|
||||
@tweens = []
|
||||
|
||||
createGraphics: ->
|
||||
@targetDotKey = @cachePathDot(TARGET_WIDTH, @colorForThang(@thang.team, TARGET_ALPHA), [0, 0, 0, 1])
|
||||
@pastDotKey = @cachePathDot(PAST_PATH_WIDTH, @colorForThang(@thang.team, PAST_PATH_ALPHA), [0, 0, 0, 1])
|
||||
@futureDotKey = @cachePathDot(FUTURE_PATH_WIDTH, [255, 255, 255, FUTURE_PATH_ALPHA], @colorForThang(@thang.team, 1))
|
||||
|
||||
cachePathDot: (width, fillColor, strokeColor) ->
|
||||
key = "path-dot-#{width}-#{fillColor}-#{strokeColor}"
|
||||
fillColor = createjs.Graphics.getRGB(fillColor...)
|
||||
strokeColor = createjs.Graphics.getRGB(strokeColor...)
|
||||
unless key in @layerAdapter.spriteSheet.getAnimations()
|
||||
circle = new createjs.Shape()
|
||||
radius = width/2
|
||||
circle.graphics.setStrokeStyle(width/5).beginFill(fillColor).beginStroke(strokeColor).drawCircle(0, 0, radius)
|
||||
@layerAdapter.addCustomGraphic(key, circle, [-radius*1.5, -radius*1.5, radius*3, radius*3])
|
||||
return key
|
||||
|
||||
colorForThang: (team, alpha=1.0) ->
|
||||
rgb = [0, 255, 0]
|
||||
rgb = [255, 0, 0] if team is 'humans'
|
||||
rgb = [0, 0, 255] if team is 'ogres'
|
||||
rgb.push(alpha)
|
||||
return rgb
|
||||
|
||||
createPastPath: ->
|
||||
return unless points = @world.pointsForThang @thang.id, @camera
|
||||
interval = Math.max(1, parseInt(@world.frameRate / PAST_PATH_INTERVAL_DIVISOR))
|
||||
params = { interval: interval, frameKey: @pastDotKey }
|
||||
return @createPath(points, params)
|
||||
|
||||
createFuturePath: ->
|
||||
return unless points = @world.pointsForThang @thang.id, @camera
|
||||
interval = Math.max(1, parseInt(@world.frameRate / FUTURE_PATH_INTERVAL_DIVISOR))
|
||||
params = { interval: interval, animate: true, frameKey: @futureDotKey }
|
||||
return @createPath(points, params)
|
||||
|
||||
createTargets: ->
|
||||
return unless @thang.allTargets
|
||||
container = new createjs.SpriteContainer(@layerAdapter.spriteSheet)
|
||||
for x, i in @thang.allTargets by 2
|
||||
y = @thang.allTargets[i + 1]
|
||||
sup = @camera.worldToSurface x: x, y: y
|
||||
sprite = new createjs.Sprite(@layerAdapter.spriteSheet)
|
||||
sprite.scaleX = sprite.scaleY = 1 / @layerAdapter.resolutionFactor
|
||||
sprite.scaleY *= @camera.y2x
|
||||
sprite.gotoAndStop(@targetDotKey)
|
||||
sprite.x = sup.x
|
||||
sprite.y = sup.y
|
||||
container.addChild(sprite)
|
||||
return container
|
||||
|
||||
createPath: (points, options={}) ->
|
||||
options = options or {}
|
||||
interval = options.interval or 8
|
||||
key = options.frameKey or @pastDotKey
|
||||
container = new createjs.SpriteContainer(@layerAdapter.spriteSheet)
|
||||
|
||||
for x, i in points by interval * 2
|
||||
y = points[i + 1]
|
||||
sprite = new createjs.Sprite(@layerAdapter.spriteSheet)
|
||||
sprite.scaleX = sprite.scaleY = 1 / @layerAdapter.resolutionFactor
|
||||
sprite.scaleY *= @camera.y2x
|
||||
sprite.gotoAndStop(key)
|
||||
sprite.x = x
|
||||
sprite.y = y
|
||||
container.addChild(sprite)
|
||||
if lastSprite and options.animate
|
||||
tween = createjs.Tween.get(lastSprite, {loop: true}).to({x:x, y:y}, 1000)
|
||||
@tweenedSprites.push lastSprite
|
||||
@tweens.push tween
|
||||
lastSprite = sprite
|
||||
|
||||
@logged = true
|
||||
container
|
||||
|
||||
play: ->
|
||||
tween.setPaused(false) for tween in @tweens
|
||||
|
||||
stop: ->
|
||||
tween.setPaused(true) for tween in @tweens
|
||||
|
||||
destroy: ->
|
||||
@cleanUp()
|
||||
super()
|
|
@ -1,289 +0,0 @@
|
|||
# paths before the current state taper out,
|
||||
# and have a different color than the future
|
||||
PAST_PATH_TAIL_BRIGHTNESS = 150
|
||||
PAST_PATH_TAIL_ALPHA = 0.3
|
||||
PAST_PATH_HEAD_BRIGHTNESS = 200
|
||||
PAST_PATH_HEAD_ALPHA = 0.75
|
||||
|
||||
PAST_PATH_HEAD_LENGTH = 50
|
||||
PAST_PATH_TAIL_WIDTH = 2
|
||||
PAST_PATH_HEAD_WIDTH = 2
|
||||
PAST_PATH_MAX_LENGTH = 200
|
||||
|
||||
# paths in the future are single color dotted lines
|
||||
FUT_PATH_BRIGHTNESS = 153
|
||||
FUT_PATH_ALPHA = 0.8
|
||||
FUT_PATH_HEAD_LENGTH = 0
|
||||
FUT_PATH_WIDTH = 1
|
||||
FUT_PATH_MAX_LENGTH = 2000
|
||||
|
||||
# selected paths are single color, and larger, more prominent
|
||||
# most other properties are the same as non-selected
|
||||
SELECTED_PATH_TAIL_BRIGHTNESS = 146
|
||||
SELECTED_PATH_TAIL_ALPHA = 0.5
|
||||
SELECTED_PATH_HEAD_BRIGHTNESS = 200
|
||||
SELECTED_PATH_HEAD_ALPHA = 1.0
|
||||
SELECTED_PAST_PATH_MAX_LENGTH = 2000
|
||||
|
||||
FUT_SELECTED_PATH_WIDTH = 3
|
||||
|
||||
# for sprites along the path
|
||||
CLONE_INTERVAL = 250 # distance between them, ignored for new actions
|
||||
CLONE_SCALE = 1.0
|
||||
CLONE_ALPHA = 0.4
|
||||
|
||||
# path defaults
|
||||
PATH_DOT_LENGTH = 3
|
||||
PATH_SEGMENT_LENGTH = 15 # should be > PATH_DOT_LENGTH
|
||||
|
||||
Camera = require './Camera'
|
||||
|
||||
module.exports.Trailmaster = class Trailmaster
|
||||
paths: null # dictionary of thang ids to containers for their paths
|
||||
selectedPath: null # container of path selected
|
||||
pathDisplayObject: null
|
||||
world: null
|
||||
clock: 0
|
||||
|
||||
constructor: (@camera) ->
|
||||
|
||||
tick: ->
|
||||
@clock += 1
|
||||
|
||||
generatePaths: (@world, @currentFrame, @selectedThang, @sprites, @selectedOnly) ->
|
||||
@paths = {}
|
||||
@pathDisplayObject = new createjs.Container()
|
||||
@pathDisplayObject.mouseEnabled = @pathDisplayObject.mouseChildren = false
|
||||
for thang in world.thangs
|
||||
continue unless thang.isSelectable
|
||||
continue unless thang.isMovable
|
||||
continue if @selectedOnly and thang isnt @selectedThang
|
||||
path = @createPathForThang thang
|
||||
continue if not path
|
||||
@pathDisplayObject.addChild path
|
||||
@paths[thang.id] = path
|
||||
@pathDisplayObject
|
||||
|
||||
createPathForThang: (thang) ->
|
||||
container = new createjs.Container()
|
||||
|
||||
path = @createPastPathForThang(thang)
|
||||
container.addChild(path) if path
|
||||
|
||||
path = @createFuturePathForThang(thang)
|
||||
container.addChild(path) if path
|
||||
|
||||
targets = @createTargetsForThang(thang)
|
||||
container.addChild(targets) if targets
|
||||
|
||||
if thang is @selectedThang
|
||||
sprites = @spritesForThang(thang)
|
||||
for sprite in sprites
|
||||
container.addChild(sprite)
|
||||
|
||||
container
|
||||
|
||||
createPastPathForThang: (thang) ->
|
||||
maxLength = if thang is @selectedThang then SELECTED_PAST_PATH_MAX_LENGTH else PAST_PATH_MAX_LENGTH
|
||||
start = Math.max(@currentFrame - maxLength, 0)
|
||||
start = 0 if thang isnt @selectedThang
|
||||
resolution = if thang is @selectedThang then 4 else 12
|
||||
return unless points = @world.pointsForThang thang.id, start, @currentFrame, @camera, resolution
|
||||
params =
|
||||
tailWidth: PAST_PATH_TAIL_WIDTH
|
||||
headWidth: PAST_PATH_HEAD_WIDTH
|
||||
headLength: PAST_PATH_HEAD_LENGTH
|
||||
if thang is @selectedThang
|
||||
params['tailColor'] = colorForThang(thang.team, SELECTED_PATH_TAIL_BRIGHTNESS, SELECTED_PATH_TAIL_ALPHA)
|
||||
params['headColor'] = colorForThang(thang.team, SELECTED_PATH_HEAD_BRIGHTNESS, SELECTED_PATH_HEAD_ALPHA)
|
||||
else
|
||||
params['tailColor'] = colorForThang(thang.team, PAST_PATH_TAIL_BRIGHTNESS, PAST_PATH_TAIL_ALPHA)
|
||||
params['headColor'] = colorForThang(thang.team, PAST_PATH_HEAD_BRIGHTNESS, PAST_PATH_HEAD_ALPHA)
|
||||
return createPath(points, params)
|
||||
|
||||
|
||||
createFuturePathForThang: (thang) ->
|
||||
resolution = 8
|
||||
return unless points = @world.pointsForThang thang.id, @currentFrame, @currentFrame + FUT_PATH_MAX_LENGTH, @camera, resolution
|
||||
if thang is @selectedThang
|
||||
color = colorForThang(thang.team, SELECTED_PATH_HEAD_BRIGHTNESS, SELECTED_PATH_HEAD_ALPHA)
|
||||
else
|
||||
color = colorForThang(thang.team, FUT_PATH_BRIGHTNESS, FUT_PATH_ALPHA)
|
||||
return createPath(points,
|
||||
tailColor: color
|
||||
tailWidth: if thang is @selectedThang then FUT_SELECTED_PATH_WIDTH else FUT_PATH_WIDTH
|
||||
headLength: FUT_PATH_HEAD_LENGTH
|
||||
dotted: true
|
||||
dotOffset: @clock
|
||||
)
|
||||
|
||||
createTargetsForThang: (thang) ->
|
||||
return unless thang.allTargets
|
||||
g = new createjs.Graphics()
|
||||
g.setStrokeStyle(0.5)
|
||||
g.beginStroke(createjs.Graphics.getRGB(0, 0, 0))
|
||||
color = colorForThang(thang.team)
|
||||
|
||||
i = 0
|
||||
while i < thang.allTargets.length
|
||||
g.beginStroke(createjs.Graphics.getRGB(0, 0, 0))
|
||||
g.beginFill(createjs.Graphics.getRGB(color...))
|
||||
sup = @camera.worldToSurface x: thang.allTargets[i], y: thang.allTargets[i + 1]
|
||||
g.drawEllipse(sup.x - 5, sup.y - 3, 10, 6)
|
||||
g.endStroke()
|
||||
|
||||
i += 2
|
||||
|
||||
s = new createjs.Shape(g)
|
||||
s.x = 0
|
||||
s.y = 0
|
||||
s
|
||||
|
||||
spritesForThang: (thang) ->
|
||||
i = 0
|
||||
sprites = []
|
||||
sprite = @sprites[thang.id]
|
||||
return sprites unless sprite?.actions
|
||||
lastPos = @camera.surfaceToWorld x: sprite.sprite.x, y: sprite.sprite.y
|
||||
minDistance = Math.pow(CLONE_INTERVAL * Camera.MPP, 2)
|
||||
actions = @world.actionsForThang(thang.id)
|
||||
lastAction = null
|
||||
|
||||
for action in actions
|
||||
continue if action.name in ['idle', 'move']
|
||||
frame = @world.frames[action.frame]
|
||||
frame.restoreStateForThang(thang)
|
||||
|
||||
if lastPos
|
||||
diff = Math.pow(lastPos.x - thang.pos.x, 2)
|
||||
diff += Math.pow(lastPos.y - thang.pos.y, 2)
|
||||
continue if diff < minDistance and action.name is lastAction
|
||||
|
||||
clone = sprite.sprite.clone()
|
||||
clonePos = @camera.worldToSurface thang.pos
|
||||
clone.x = clonePos.x
|
||||
clone.y = clonePos.y
|
||||
clone.alpha = CLONE_ALPHA
|
||||
clone.scaleX *= CLONE_SCALE
|
||||
clone.scaleY *= CLONE_SCALE
|
||||
if sprite.expandActions # old Sprite
|
||||
sprite.updateRotation(clone, sprite.data)
|
||||
animActions = sprite.expandActions(if thang.acts then thang.getActionName() else 'idle')
|
||||
sprite.applyActionsToSprites(animActions, [clone], true)
|
||||
animation = clone.spriteSheet.getAnimation(clone.currentAnimation)
|
||||
clone.currentAnimationFrame = Math.min(@clock % (animation.frames.length * 3), animation.frames.length - 1)
|
||||
else
|
||||
continue unless animation = sprite.actions[action.name]
|
||||
sprite.updateRotation clone
|
||||
animation = sprite.getActionDirection(animation) ? animation # no idea if this ever works
|
||||
clone.gotoAndStop animation.name
|
||||
# TODO: use action-specific framerate here?
|
||||
# clone.currentAnimationFrame = Math.min(@clock % (animation.frames.length * 3), animation.frames.length - 1)
|
||||
sprites.push(clone)
|
||||
lastPos = x: thang.pos.x, y: thang.pos.y
|
||||
lastAction = action.name
|
||||
|
||||
@world.frames[@currentFrame].restoreStateForThang(thang)
|
||||
sprites
|
||||
|
||||
createPath = (points, options={}, g=null) ->
|
||||
options = options or {}
|
||||
tailColor = options.tailColor ? options.headColor
|
||||
headColor = options.headColor ? options.tailColor
|
||||
oneColor = true
|
||||
oneColor = oneColor and headColor[i] is tailColor[i] for i in [0..4]
|
||||
maxLength = options.maxLength or 0
|
||||
tailWidth = options.tailWidth
|
||||
headWidth = options.headWidth
|
||||
oneWidth = headWidth is tailWidth
|
||||
headLength = options.headLength
|
||||
dotted = options.dotted
|
||||
dotOffset = if options.dotOffset? then options.dotOffset else 0
|
||||
|
||||
points = points.slice(-maxLength * 2) if maxLength isnt 0
|
||||
points = points.slice(((points.length / 2 + dotOffset) % PATH_SEGMENT_LENGTH) * 2) if dotOffset
|
||||
g = new createjs.Graphics() unless g
|
||||
return new createjs.Shape(g) if not points
|
||||
|
||||
g.setStrokeStyle(tailWidth)
|
||||
g.beginStroke(createjs.Graphics.getRGB(tailColor...))
|
||||
g.moveTo(points[0], points[1])
|
||||
|
||||
headStart = points.length - headLength
|
||||
[lastX, lastY] = [points[0], points[1]]
|
||||
|
||||
for x, i in points by 2
|
||||
continue if i is 0
|
||||
y = points[i + 1]
|
||||
if i >= headStart and not (oneColor and oneWidth)
|
||||
diff = (i - headStart) / headLength
|
||||
style = transition(tailWidth, headWidth, diff)
|
||||
color = colorTransition(tailColor, headColor, diff)
|
||||
g.setStrokeStyle(style)
|
||||
g.beginStroke(createjs.Graphics.getRGB(color...))
|
||||
g.moveTo(lastX, lastY) if lastX?
|
||||
|
||||
else if dotted
|
||||
|
||||
if false and i < 2
|
||||
# Test: footprints
|
||||
g.beginFill(createjs.Graphics.getRGB(tailColor...))
|
||||
xofs = x - lastX
|
||||
yofs = y - lastY
|
||||
theta = Math.atan2(yofs, xofs)
|
||||
[fdist, fwidth] = [4, 2]
|
||||
fside = if (i + dotOffset) % 4 is 0 then -1 else 1
|
||||
fx = [lastX + fside * fdist * (Math.cos(theta) * xofs - Math.sin(theta) * yofs)]
|
||||
fy = [lastY + fside * fdist * (Math.sin(theta) * xofs - Math.cos(theta) * yofs)]
|
||||
g.drawCircle(fx, fy, 2)
|
||||
|
||||
offset = ((i / 2) % PATH_SEGMENT_LENGTH)
|
||||
if offset >= PATH_DOT_LENGTH
|
||||
if offset is PATH_DOT_LENGTH
|
||||
g.endStroke()
|
||||
lastX = x
|
||||
lastY = y
|
||||
continue
|
||||
|
||||
else
|
||||
if offset is 0
|
||||
g.beginStroke(createjs.Graphics.getRGB(tailColor...))
|
||||
g.moveTo(lastX, lastY) if lastX?
|
||||
|
||||
g.lineTo(x, y)
|
||||
lastX = x
|
||||
lastY = y
|
||||
|
||||
g.endStroke()
|
||||
|
||||
s = new createjs.Shape(g)
|
||||
return s
|
||||
|
||||
colorTransition = (color1, color2, pct) ->
|
||||
return color1 if pct <= 0
|
||||
return color2 if pct >= 1
|
||||
|
||||
i = 0
|
||||
color = []
|
||||
while i < 4
|
||||
val = transition(color1[i], color2[i], pct)
|
||||
val = Math.floor(val) if i isnt 3
|
||||
color.push(val)
|
||||
i += 1
|
||||
color
|
||||
|
||||
transition = (num1, num2, pct) ->
|
||||
return num1 if pct <= 0
|
||||
return num2 if pct >= 1
|
||||
num1 + (num2 - num1) * pct
|
||||
|
||||
colorForThang = (team, brightness=100, alpha=1.0) =>
|
||||
# multipliers should sum to 3.0
|
||||
multipliers = [2.0, 0.5, 0.5] if team is 'humans'
|
||||
multipliers = [0.5, 0.5, 2.0] if team is 'ogres'
|
||||
multipliers = [2.0, 0.5, 0.5] if not multipliers
|
||||
color = _.map(multipliers, (m) -> return parseInt(m * brightness))
|
||||
color.push(alpha)
|
||||
return color
|
||||
|
||||
module.exports.createPath = createPath
|
|
@ -472,7 +472,11 @@ module.exports = class World
|
|||
|
||||
perf.t1 = now()
|
||||
if w.thangs.length
|
||||
for thangConfig in o.thangs when not w.thangMap[thangConfig.id]
|
||||
for thangConfig in o.thangs
|
||||
if thang = w.thangMap[thangConfig.id]
|
||||
for prop, val of thangConfig.finalState
|
||||
thang[prop] = val
|
||||
else
|
||||
w.thangs.push thang = Thang.deserialize(thangConfig, w, classMap, level.levelComponents)
|
||||
w.setThang thang
|
||||
else
|
||||
|
@ -551,7 +555,7 @@ module.exports = class World
|
|||
freeMemoryAfterEachSerialization: ->
|
||||
@frames[i] = null for frame, i in @frames when i < @frames.length - 1
|
||||
|
||||
pointsForThang: (thangID, frameStart=0, frameEnd=null, camera=null, resolution=4) ->
|
||||
pointsForThang: (thangID, camera=null) ->
|
||||
# Optimized
|
||||
@pointsForThangCache ?= {}
|
||||
cacheKey = thangID
|
||||
|
@ -570,16 +574,7 @@ module.exports = class World
|
|||
allPoints.reverse()
|
||||
@pointsForThangCache[cacheKey] = allPoints
|
||||
|
||||
points = []
|
||||
[lastX, lastY] = [null, null]
|
||||
for frameIndex in [Math.floor(frameStart / resolution) ... Math.ceil(frameEnd / resolution)]
|
||||
x = allPoints[frameIndex * 2 * resolution]
|
||||
y = allPoints[frameIndex * 2 * resolution + 1]
|
||||
continue if x is lastX and y is lastY
|
||||
lastX = x
|
||||
lastY = y
|
||||
points.push x, y
|
||||
points
|
||||
return allPoints
|
||||
|
||||
actionsForThang: (thangID, keepIdle=false) ->
|
||||
# Optimized
|
||||
|
|
|
@ -49,13 +49,13 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
|
|||
play_as: "扮演" # Ladder page
|
||||
spectate: "旁观他人的游戏" # Ladder page
|
||||
players: "玩家" # Hover over a level on /play
|
||||
hours_played: "已经玩过的时间" # Hover over a level on /play
|
||||
hours_played: "游戏时长" # Hover over a level on /play
|
||||
items: "道具" # Tooltip on item shop button from /play
|
||||
unlock: "解锁" # For purchasing items and heroes
|
||||
confirm: "确认"
|
||||
owned: "已拥有" # For items you own
|
||||
locked: "需解锁"
|
||||
available: "可用"
|
||||
available: "可用" # Available
|
||||
skills_granted: "获得技能" # Property documentation details
|
||||
heroes: "英雄" # Tooltip on hero shop button from /play
|
||||
achievements: "成就" # Tooltip on achievement list button from /play
|
||||
|
@ -64,14 +64,14 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
|
|||
next: "下一步" # Go from choose hero to choose inventory before playing a level
|
||||
change_hero: "重新选择英雄" # Go back from choose inventory to choose hero
|
||||
choose_inventory: "装备道具"
|
||||
buy_gems: "购买宝石"
|
||||
buy_gems: "购买宝石" # Buy Gems
|
||||
older_campaigns: "旧的战役"
|
||||
anonymous: "匿名玩家"
|
||||
level_difficulty: "难度:"
|
||||
campaign_beginner: "新手作战"
|
||||
awaiting_levels_adventurer_prefix: "我们每周开放五个关卡"
|
||||
# awaiting_levels_adventurer: "Sign up as an Adventurer"
|
||||
awaiting_levels_adventurer_suffix: "做第一个玩新关卡的人"
|
||||
awaiting_levels_adventurer: "注册成为冒险家" #"Sign up as an Adventurer"
|
||||
awaiting_levels_adventurer_suffix: "来优先尝试新关卡" #to be the first to play new levels."
|
||||
choose_your_level: "选择关卡" # The rest of this section is the old play view at /play-old and isn't very important.
|
||||
adventurer_prefix: "你可以选择以下任意关卡,或者讨论以上的关卡。到"
|
||||
adventurer_forum: "冒险者论坛"
|
||||
|
@ -86,8 +86,8 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
|
|||
campaign_player_created_description: "……在这里你可以与你的小伙伴的创造力战斗 <a href=\"/contribute#artisan\">技术指导</a>."
|
||||
campaign_classic_algorithms: "经典算法"
|
||||
campaign_classic_algorithms_description: "... 你可以在此学习到计算机科学中最常用的算法"
|
||||
# campaign_forest: "Forest Campaign"
|
||||
# campaign_dungeon: "Dungeon Campaign"
|
||||
campaign_forest: "森林战役" #Forest Campaign"
|
||||
campaign_dungeon: "地牢战役" #Dungeon Campaign"
|
||||
|
||||
login:
|
||||
sign_up: "注册"
|
||||
|
@ -95,10 +95,10 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
|
|||
logging_in: "正在登录"
|
||||
log_out: "登出"
|
||||
recover: "找回账户"
|
||||
authenticate_gplus: "通过G+授权"
|
||||
load_profile: "载入G+资料"
|
||||
load_email: "载入G+ Email"
|
||||
finishing: "完成"
|
||||
authenticate_gplus: "使用 G+ 授权"#Authenticate G+"
|
||||
load_profile: "载入 G+ 档案" # Load G+ Profile"
|
||||
load_email: "载入 G+ 电子邮件" #Load G+ Email"
|
||||
finishing: "完成" #Finishing"
|
||||
|
||||
signup:
|
||||
create_account_title: "创建一个账户来保存进度"
|
||||
|
@ -118,8 +118,8 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
|
|||
recovery_sent: "找回账户邮件已发送."
|
||||
|
||||
items:
|
||||
primary: "主要的"
|
||||
secondary: "次要的"
|
||||
primary: "右手"#"Primary"
|
||||
secondary: "左手"#Secondary"
|
||||
armor: "盔甲"
|
||||
accessories: "配饰"
|
||||
misc: "辅助道具"
|
||||
|
@ -172,7 +172,7 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
|
|||
medium: "中等"
|
||||
hard: "困难"
|
||||
player: "玩家"
|
||||
# player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard
|
||||
player_level: "等级" # Like player level 5, not like level: Dungeons of Kithgard
|
||||
|
||||
units:
|
||||
second: "秒"
|
||||
|
@ -193,7 +193,7 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
|
|||
play_level:
|
||||
done: "完成"
|
||||
home: "主页" # Not used any more, will be removed soon.
|
||||
# level: "Level" # Like "Level: Dungeons of Kithgard"
|
||||
level: "等级" # Like "Level: Dungeons of Kithgard"
|
||||
skip: "跳过"
|
||||
game_menu: "游戏菜单"
|
||||
guide: "指南"
|
||||
|
|
|
@ -51,6 +51,8 @@ class CocoModel extends Backbone.Model
|
|||
@jqxhr = null
|
||||
@loadFromBackup()
|
||||
|
||||
getCreationDate: -> new Date(parseInt(@id.slice(0,8), 16)*1000)
|
||||
|
||||
getNormalizedURL: -> "#{@urlRoot}/#{@id}"
|
||||
|
||||
attributesWithDefaults: undefined
|
||||
|
|
|
@ -250,11 +250,13 @@ module.exports = class ThangType extends CocoModel
|
|||
stage.update()
|
||||
stage.startTalking = ->
|
||||
sprite.gotoAndPlay 'portrait'
|
||||
return # TODO: causes infinite recursion in new EaselJS
|
||||
return if @tick
|
||||
@tick = (e) => @update(e)
|
||||
createjs.Ticker.addEventListener 'tick', @tick
|
||||
stage.stopTalking = ->
|
||||
sprite.gotoAndStop 'portrait'
|
||||
return # TODO: just breaks in new EaselJS
|
||||
@update()
|
||||
createjs.Ticker.removeEventListener 'tick', @tick
|
||||
@tick = null
|
||||
|
|
|
@ -24,6 +24,7 @@ worldUpdatedEventSchema = c.object {required: ['world', 'firstWorld', 'goalState
|
|||
goalStates: goalStatesSchema
|
||||
team: {type: 'string'}
|
||||
firstChangedFrame: {type: 'integer', minimum: 0}
|
||||
finished: {type: 'boolean'}
|
||||
|
||||
module.exports =
|
||||
'god:user-code-problem': c.object {required: ['problem']},
|
||||
|
|
|
@ -35,7 +35,7 @@ module.exports =
|
|||
|
||||
'tome:toggle-spell-list': c.object {title: 'Toggle Spell List', description: 'Published when you toggle the dropdown for a thang\'s spells'}
|
||||
|
||||
'tome:reload-code': c.object {title: 'Reload Code', description: 'Published when you reset a spell to its original source', required: ['spell']},
|
||||
'tome:reload-code': c.object {title: 'Reload Code', description: 'Published when you reset a spell to its original source', required: []},
|
||||
spell: {type: 'object'}
|
||||
|
||||
'tome:palette-cleared': c.object {title: 'Palette Cleared', description: 'Published when the spell palette is about to be cleared and recreated.'},
|
||||
|
@ -122,6 +122,10 @@ module.exports =
|
|||
'tome:required-code-fragment-deleted': c.object {title: 'Required Code Fragment Deleted', description: 'Published when a required code fragment is deleted from the sample code.', required: ['codeFragment']},
|
||||
codeFragment: {type: 'string'}
|
||||
|
||||
'tome:suspect-code-fragment-added': c.object {title: 'Suspect Code Fragment Added', description: 'Published when a suspect code fragment is added to the sample code.', required: ['codeFragment']},
|
||||
codeFragment: {type: 'string'}
|
||||
codeLanguage: {type: 'string'}
|
||||
|
||||
'tome:winnability-updated': c.object {title: 'Winnability Updated', description: 'When we think we can now win (or can no longer win), we may want to emphasize the submit button versus the run button (or vice versa), so this fires when we get new goal states (even preloaded goal states) suggesting success or failure change.', required: ['winnable']},
|
||||
winnable: {type: 'boolean'}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ module.exports =
|
|||
thang: {type: 'object'}
|
||||
killer: {type: 'object'}
|
||||
killerHealth: {type: ['number', 'undefined']}
|
||||
maxHealth: {type: 'number'}
|
||||
|
||||
'world:thang-touched-goal': c.object {required: ['actor', 'touched']},
|
||||
replacedNoteChain: {type: 'array'}
|
||||
|
|
|
@ -21,6 +21,26 @@
|
|||
left: -3px
|
||||
|
||||
|
||||
//- Close modal button
|
||||
|
||||
#close-modal
|
||||
position: absolute
|
||||
left: 769px
|
||||
top: -5px
|
||||
width: 60px
|
||||
height: 60px
|
||||
color: white
|
||||
text-align: center
|
||||
font-size: 30px
|
||||
padding-top: 17px
|
||||
cursor: pointer
|
||||
z-index: 2
|
||||
@include rotate(-3deg)
|
||||
|
||||
&:hover
|
||||
color: yellow
|
||||
|
||||
|
||||
//- Nav bar
|
||||
|
||||
#game-menu-nav
|
||||
|
|
|
@ -106,11 +106,11 @@ $itemSlotGridHeight: 70px
|
|||
// opacity: 0.5
|
||||
|
||||
&.selected
|
||||
.placeholder, img.item
|
||||
.placeholder, .item
|
||||
border-color: rgb(81,153,236)
|
||||
background-color: rgb(81,153,236)
|
||||
@include box-shadow(0 0 10px rgb(81,153,236))
|
||||
img.item
|
||||
.item
|
||||
background: rgb(81,153,236)
|
||||
|
||||
&.should-equip
|
||||
|
@ -269,7 +269,7 @@ $itemSlotGridHeight: 70px
|
|||
.placeholder
|
||||
background-position: (-2 * $itemSlotInnerWidth) 0px
|
||||
|
||||
img.item
|
||||
.item
|
||||
position: absolute
|
||||
left: -2px
|
||||
top: -2px
|
||||
|
@ -299,7 +299,7 @@ $itemSlotGridHeight: 70px
|
|||
|
||||
//- Available equipment
|
||||
|
||||
&.Warrior #unequipped img.item:not(.Warrior), &.Ranger #unequipped img.item:not(.Ranger), &.Wizard #unequipped img.item:not(.Wizard)
|
||||
&.Warrior #unequipped .item:not(.Warrior), &.Ranger #unequipped .item:not(.Ranger), &.Wizard #unequipped .item:not(.Wizard)
|
||||
// Our code hides and shows (modifies display), but we can be invisible this other way.
|
||||
visibility: hidden
|
||||
position: absolute
|
||||
|
@ -313,7 +313,14 @@ $itemSlotGridHeight: 70px
|
|||
padding: 9px 0 9px 9px
|
||||
|
||||
#double-click-hint
|
||||
margin: 20px 50px 70px 0
|
||||
margin: 20px 40px 60px 0
|
||||
border: 3px solid #8585f4
|
||||
padding: 5px
|
||||
font-weight: bold
|
||||
.glyphicon
|
||||
margin-right: 5px
|
||||
position: relative
|
||||
top: 2px
|
||||
|
||||
h4
|
||||
clear: both
|
||||
|
@ -323,14 +330,28 @@ $itemSlotGridHeight: 70px
|
|||
text-transform: uppercase
|
||||
font-weight: bold
|
||||
|
||||
img.item
|
||||
.item
|
||||
float: left
|
||||
border: 1px solid black
|
||||
margin: 3px
|
||||
padding: 1px
|
||||
position: relative
|
||||
width: 60px
|
||||
height: 60px
|
||||
cursor: pointer
|
||||
|
||||
img
|
||||
width: 56px
|
||||
height: 56px
|
||||
display: block
|
||||
|
||||
button
|
||||
width: 100%
|
||||
height: 17px
|
||||
border: 1px solid rgb(46,46,46)
|
||||
background: white
|
||||
font-size: 11px
|
||||
border-radius: 1px
|
||||
padding: 0
|
||||
|
||||
|
||||
&.active
|
||||
background-color: rgb(81,153,236)
|
||||
|
@ -417,6 +438,9 @@ $itemSlotGridHeight: 70px
|
|||
#selected-item-unlock-button
|
||||
left: 646px
|
||||
|
||||
.unequippable
|
||||
display: none
|
||||
|
||||
|
||||
//- Equip/unequip/extra
|
||||
|
||||
|
@ -437,3 +461,7 @@ $itemSlotGridHeight: 70px
|
|||
border: 3px solid rgb(46,46,46)
|
||||
background: white
|
||||
font-size: 16px
|
||||
|
||||
#equip-item-viewed
|
||||
background: rgb(84,128,44)
|
||||
color: white
|
||||
|
|
|
@ -43,6 +43,13 @@ $level-resize-transition-time: 0.5s
|
|||
visibility: hidden
|
||||
#gold-view
|
||||
right: 1%
|
||||
@include box-shadow(-1px 1px 10px cyan)
|
||||
.team-gold
|
||||
font-size: 2vw
|
||||
line-height: 2vw
|
||||
img
|
||||
width: 1.8vw
|
||||
heighT: 1.8vw
|
||||
#control-bar-view .title
|
||||
left: 20%
|
||||
width: 60%
|
||||
|
|
|
@ -12,11 +12,13 @@
|
|||
padding: 19px 0px 2px 25px
|
||||
z-index: 3
|
||||
font-size: 14px
|
||||
min-width: 230px
|
||||
|
||||
&.brighter
|
||||
font-size: 18px
|
||||
font-size: 1.4vw
|
||||
border-width: 0.91vw 1.22vw 3.10vw 0.91vw
|
||||
min-width: 23vw
|
||||
|
||||
.goals-status
|
||||
margin: 5px 0 0 0
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
z-index: 6
|
||||
@include transition(box-shadow .2s linear)
|
||||
@include user-select(none)
|
||||
padding: 4px
|
||||
padding: 0.4vw
|
||||
background: transparent url(/images/level/gold_background.png) no-repeat
|
||||
background-size: 100% 100%
|
||||
border-radius: 4px
|
||||
|
@ -18,9 +18,9 @@
|
|||
box-shadow: 2px 2px 2px black
|
||||
|
||||
.team-gold
|
||||
font-size: 18px
|
||||
font-size: 1.4vw
|
||||
line-height: 1.4vw
|
||||
margin: 0
|
||||
line-height: 20px
|
||||
color: hsla(205,0%,51%,1)
|
||||
display: inline-block
|
||||
padding: 0px 4px
|
||||
|
@ -35,11 +35,12 @@
|
|||
color: hsla(116,80%,51%,1)
|
||||
|
||||
img
|
||||
width: 16px
|
||||
height: 16px
|
||||
width: 1.2vw
|
||||
height: 1.2vw
|
||||
border-radius: 2px
|
||||
padding: 1px
|
||||
margin-top: -1px
|
||||
padding: 0.1vw
|
||||
margin-top: -0.2vw
|
||||
margin-right: 0.1vw
|
||||
|
||||
.gold-amount
|
||||
display: inline-block
|
||||
|
|
|
@ -55,7 +55,7 @@
|
|||
line-height: 20px
|
||||
|
||||
strong
|
||||
color: #09B057
|
||||
color: #FFCCAA
|
||||
|
||||
.hud-hint
|
||||
font-weight: normal
|
||||
|
|
|
@ -99,7 +99,7 @@
|
|||
@include gradient-striped()
|
||||
outline: 2px outset #0099ff
|
||||
@include box-shadow(1px 1px 4px black)
|
||||
&.playback-ended .executing
|
||||
&.playback-ended .executing, &.user-code-problem .executing
|
||||
background-color: rgba(50, 255, 80, 0.65)
|
||||
outline: 0
|
||||
@include box-shadow(0 0 0px black)
|
||||
|
@ -125,11 +125,12 @@
|
|||
// TODO: Pulses too quickly during playback
|
||||
@include animation(pulseRedBackground 1s infinite)
|
||||
|
||||
&:not(.playback-ended)
|
||||
&:not(.playback-ended):not(.user-code-problem)
|
||||
.executing:not(.ace_gutter-cell)
|
||||
background-size: 40px 40px
|
||||
@include animation(progress-bar-stripes 0.5s linear infinite)
|
||||
|
||||
&:not(.user-code-problem)
|
||||
.ace_gutter-cell.executing:not(.ace_error):not(.ace_warning):not(.ace_info):after
|
||||
|
||||
// Experimenting with a larger executing-line-pointer
|
||||
|
@ -148,12 +149,6 @@
|
|||
-webkit-font-smoothing: antialiased
|
||||
-moz-osx-font-smoothing: grayscale
|
||||
|
||||
|
||||
//display: block
|
||||
//margin-left: 1px
|
||||
//background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2hpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYxIDY0LjE0MDk0OSwgMjAxMC8xMi8wNy0xMDo1NzowMSAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowMjgwMTE3NDA3MjA2ODExOEE2REU4Q0M1MTM1MkIxRiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpBQjVEQUNDMzQ4RUIxMUUxOEVGRUUyNzFENDM3RDVFMCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpBQjVEQUNDMjQ4RUIxMUUxOEVGRUUyNzFENDM3RDVFMCIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1LjEgTWFjaW50b3NoIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QTU1MjE3RDIzMTIwNjgxMThEQkI4NTlBMjQ1QTEwOTUiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MDI4MDExNzQwNzIwNjgxMThBNkRFOENDNTEzNTJCMUYiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz7SazaGAAAAiElEQVR42mL8//8/AzUBEwOVweA3kAWboI2jCyhgDwBx4ZH9ey5Qy4UOQHweaHg/EAtQ08sFUIMDqBmGCkC8HmgoCCtQM1ICoK5toGYsg8KzHmjo+UGbDj8AcSMwORkSnQ7xgA3QtPmApISNBTyAGrSBGl6eAMSGxBhGyIVkZT3G0fKQYgAQYACL+C2ZM6PC7AAAAABJRU5ErkJggg==)
|
||||
//background-position: 0px center
|
||||
|
||||
.ace_gutter-cell.executed:not(.ace_error):not(.ace_warning):not(.ace_info)
|
||||
margin-left: 1px
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAANhJREFUeNpi/P//PwM1ARMDlcHgMrA428MAiANQBEFhSA4uynIXAOJ+dHFKXDgfiDdSxctAbzYAqQ+9U3ccQJdjIcMwByCVD8SGFEcK0DAFILUeiCcCXfeAIgOBhglADfsAxBNwqSPFy/1AbADEiUDXfSApHQJdcx+I9yPxE4AUCB8AGrYAn62M6HkZ6rX3UG4jEG8A4vNQviO2mMXrQqh3GqHcemi4gcACQobhixRQoMNiUQEaEY1k52WoKwuRhHAmE6KTDdCADdDwu4AvmRCMlOFfwAIEGAD4On+N4aXlhgAAAABJRU5ErkJggg==)
|
||||
|
|
|
@ -71,7 +71,7 @@
|
|||
.property-entry-item-group
|
||||
display: inline-block
|
||||
min-height: 38px
|
||||
max-width: 212px
|
||||
width: 212px
|
||||
@include flexbox()
|
||||
@include flex-wrap()
|
||||
@include flex-center()
|
||||
|
@ -93,3 +93,8 @@
|
|||
width: 174px
|
||||
width: -webkit-calc(100% - 38px)
|
||||
width: calc(100% - 38px)
|
||||
|
||||
@media only screen and (max-width: 1100px)
|
||||
#spell-palette-view
|
||||
// Make sure we have enough room for at least two columns
|
||||
padding-left: 12px
|
||||
|
|
|
@ -24,6 +24,10 @@
|
|||
&.pinned
|
||||
background-color: darken(#FFFFFF, 25%)
|
||||
|
||||
&.single-entry
|
||||
height: 38px
|
||||
line-height: 38px
|
||||
|
||||
// Originally pulled these colors from the most relevant textmate-theme classes, but then fudged them a lot.
|
||||
//&.function
|
||||
// color: black
|
||||
|
@ -80,6 +84,7 @@
|
|||
|
||||
h1:not(.not-code), h2:not(.not-code), h3:not(.not-code), h4:not(.not-code), h5:not(.not-code), h6:not(.not-code)
|
||||
font-family: Menlo, Monaco, Consolas, "Courier New", monospace
|
||||
font-variant: normal
|
||||
|
||||
.popover-title
|
||||
background-color: transparent
|
||||
|
|
|
@ -21,8 +21,12 @@
|
|||
position: absolute
|
||||
left: 860px
|
||||
top: 126px
|
||||
width: 330px
|
||||
width: 353px
|
||||
height: 449px
|
||||
|
||||
.nano-content
|
||||
right: 24px
|
||||
|
||||
//background: rgba(100,100,100,0.5)
|
||||
|
||||
#item-container
|
||||
|
|
|
@ -1,4 +1,82 @@
|
|||
#play-achievements-modal
|
||||
.achievement-view
|
||||
color: black
|
||||
|
||||
.modal-header
|
||||
padding-bottom: 20px
|
||||
|
||||
img.icon
|
||||
float: left
|
||||
width: 40px
|
||||
margin-right: 10px
|
||||
|
||||
|
||||
//- Unachieved Panels
|
||||
|
||||
.panel
|
||||
margin: 5px 0
|
||||
position: relative
|
||||
border: 2px solid rgb(75,75,75)
|
||||
padding: 2px
|
||||
|
||||
h3
|
||||
margin: 0 0 0 50px
|
||||
color: rgb(75,75,75)
|
||||
|
||||
p
|
||||
margin: 0 0 0 50px
|
||||
color: rgb(75,75,75)
|
||||
|
||||
.panel-body
|
||||
padding: 5px 150px 5px 5px
|
||||
border: 2px solid rgb(150,150,150)
|
||||
|
||||
.created
|
||||
position: absolute
|
||||
right: 10px
|
||||
top: 5px
|
||||
color: rgb(75,75,75)
|
||||
font-size: 12px
|
||||
|
||||
|
||||
//- Achieved Panels
|
||||
|
||||
.panel.earned
|
||||
background: rgb(50,40,33)
|
||||
border: 5px solid rgb(26,21,17)
|
||||
padding: 0
|
||||
|
||||
h3
|
||||
color: white
|
||||
|
||||
p
|
||||
color: rgb(203,170,148)
|
||||
|
||||
.panel-body
|
||||
border: 2px solid rgb(75,62,51)
|
||||
|
||||
.created
|
||||
color: rgb(255,189,68)
|
||||
|
||||
|
||||
//- Rewards
|
||||
|
||||
.rewards
|
||||
position: absolute
|
||||
right: .2em
|
||||
//bottom: 10px
|
||||
top: 29px
|
||||
|
||||
.label
|
||||
font-size: 18px
|
||||
margin-left: 5px
|
||||
color: rgb(50,40,33)
|
||||
background: rgb(203,170,148)
|
||||
|
||||
.gems
|
||||
background: #94ccc7
|
||||
|
||||
.worth
|
||||
background: #d8c488
|
||||
|
||||
img
|
||||
width: 12px
|
||||
height: 12px
|
|
@ -339,11 +339,13 @@
|
|||
.nano-content
|
||||
padding-left: 20px
|
||||
|
||||
#item-details-view
|
||||
|
||||
#item-title
|
||||
left: 698px
|
||||
|
||||
#item-details-body
|
||||
left: 648px
|
||||
|
||||
#selected-item-unlock-button
|
||||
#selected-item-unlock-button, .unequippable
|
||||
left: 645px
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
.modal-dialog
|
||||
.modal-content
|
||||
img(src="/images/pages/play/modal/game-menu-background.png")#game-menu-background
|
||||
img(src="/images/pages/play/modal/game-menu-background.png", draggable="false")#game-menu-background
|
||||
|
||||
div#close-modal
|
||||
span.glyphicon.glyphicon-remove
|
||||
|
||||
ul#game-menu-nav.nav.nav-pills.nav-stacked
|
||||
li
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
.modal-dialog
|
||||
.modal-content
|
||||
img(src="/images/pages/play/modal/inventory-background.png")#play-items-modal-narrow-bg
|
||||
img(src="/images/pages/play/modal/inventory-background.png", draggable="false")#play-items-modal-narrow-bg
|
||||
|
||||
div#gems-count-container
|
||||
span#gems-count.big-font= gems
|
||||
|
@ -25,21 +25,27 @@
|
|||
if itemGroups.availableItems.models.length
|
||||
h4#available-description(data-i18n="inventory.available_item")
|
||||
for item in itemGroups.availableItems.models
|
||||
img.item(src=item.getPortraitURL(), class=item.classes, data-item-id=item.id)
|
||||
.item.available(class=item.classes, data-item-id=item.id)
|
||||
img(src=item.getPortraitURL())
|
||||
button.btn.equip-item(data-i18n="inventory.equip")
|
||||
.clearfix
|
||||
|
||||
#double-click-hint.alert.alert-info.secret(data-i18n="inventory.should_equip")
|
||||
#double-click-hint.alert.alert-info
|
||||
span.glyphicon.glyphicon-info-sign.spr
|
||||
span(data-i18n="inventory.should_equip")
|
||||
|
||||
if itemGroups.restrictedItems.models.length
|
||||
h4#restricted-description(data-i18n="inventory.restricted_title")
|
||||
for item in itemGroups.restrictedItems.models
|
||||
img.item(src=item.getPortraitURL(), class=item.classes, data-item-id=item.id)
|
||||
.item(class=item.classes, data-item-id=item.id)
|
||||
img(src=item.getPortraitURL(), draggable="false")
|
||||
.clearfix
|
||||
|
||||
if itemGroups.lockedItems.models.length
|
||||
h4#locked-description(data-i18n="play.locked")
|
||||
for item in itemGroups.lockedItems.models
|
||||
img.item(src=item.getPortraitURL(), class=item.classes, data-item-id=item.id)
|
||||
.item(class=item.classes, data-item-id=item.id)
|
||||
img(src=item.getPortraitURL(), draggable="false")
|
||||
.clearfix
|
||||
|
||||
#item-details-view
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#player-avatar-container(title="Click to change your avatar")
|
||||
if !me.get('photoURL')
|
||||
.editable-icon.glyphicon.glyphicon-pencil
|
||||
img.profile-photo(src=me.getPhotoURL(230))
|
||||
img.profile-photo(src=me.getPhotoURL(230), draggable="false")
|
||||
.form-group
|
||||
input#player-name.profile-caption(name="playerName", type="text", value=me.get('name', true))
|
||||
|
||||
|
@ -32,7 +32,7 @@
|
|||
span.help-block(data-i18n="options.autorun_description") Control automatic code execution.
|
||||
|
||||
|
||||
img.hr(src="/images/pages/play/modal/hr.png")
|
||||
img.hr(src="/images/pages/play/modal/hr.png", draggable="false")
|
||||
|
||||
h3(data-i18n="options.editor_config_title") Editor Configuration
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
.thang-avatar-wrapper(class="team-" + (thang.team || "neutral"))
|
||||
//canvas(width=100, height=100, title=thang.id + " - " + thang.team)
|
||||
//- var title = thang.id + " - " + thang.team + (thang.type ? ' - type: "' + thang.type + '"' : '')
|
||||
img.avatar(src=avatarURL)
|
||||
img.avatar-frame(src="/images/level/thang_avatar_frame.png")
|
||||
img.avatar(src=avatarURL, draggable="false")
|
||||
img.avatar-frame(src="/images/level/thang_avatar_frame.png", draggable="false")
|
||||
.badge.problems
|
||||
.badge.shared-thangs
|
||||
if includeName
|
||||
|
|
|
@ -3,7 +3,6 @@ h3.problem-alert-title(data-i18n="play_level.problem_alert_title") Fix Your Code
|
|||
if hint
|
||||
span.problem-title!= hint
|
||||
br
|
||||
br
|
||||
span.problem-subtitle!= message
|
||||
else
|
||||
span.problem-title!= message
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
img(src="/images/level/code_palette_wood_background.png").code-palette-background
|
||||
img(src="/images/level/code_palette_wood_background.png", draggable="false").code-palette-background
|
||||
span.code-palette-background
|
||||
if entryGroupSlugs
|
||||
// Non-hero; group by entry groups, or maybe nothing.
|
||||
|
@ -15,3 +15,4 @@ else
|
|||
// Hero; group by items, no tabs.
|
||||
h4(data-i18n="play_level.tome_your_skills")
|
||||
.properties
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
h4
|
||||
span= doc.shortName
|
||||
span.prop-name= doc.shortName
|
||||
| -
|
||||
code.prop-type= doc.type == 'function' && doc.owner == 'this' ? 'method' : doc.type
|
||||
if doc.type != 'function'
|
||||
|
|
|
@ -7,16 +7,16 @@
|
|||
.nano
|
||||
.nano-content
|
||||
#item-container
|
||||
img.item-img(src=item.getPortraitURL())
|
||||
img.item-shadow(src=item.getPortraitURL())
|
||||
img.item-img(src=item.getPortraitURL(), draggable="false")
|
||||
img.item-shadow(src=item.getPortraitURL(), draggable="false")
|
||||
|
||||
img.hr(src="/images/pages/play/modal/hr.png")
|
||||
img.hr(src="/images/pages/play/modal/hr.png", draggable="false")
|
||||
|
||||
for stat in stats
|
||||
div.stat-row.big-font
|
||||
div.stat-label= stat.name
|
||||
div.stat= stat.display
|
||||
img.hr(src="/images/pages/play/modal/hr.png" class=stat.isLast ? "" : "faded")
|
||||
img.hr(src="/images/pages/play/modal/hr.png", class=stat.isLast ? "" : "faded", draggable="false")
|
||||
|
||||
if props.length
|
||||
#skills
|
||||
|
@ -33,14 +33,12 @@
|
|||
p Only admins will see it for now.
|
||||
|
||||
if item && !item.owned
|
||||
if item.equippable
|
||||
if item.unequippable
|
||||
// Temp, while we only have Warriors: prevent them from buying non-Warrior stuff
|
||||
button.btn.big-font.disabled.unequippable #{item.get('heroClass')} Only
|
||||
else
|
||||
button#selected-item-unlock-button.btn.big-font.unlock-button(disabled=!item.affordable, data-item-id=item.id)
|
||||
span(data-i18n="play.unlock") Unlock
|
||||
span.spl= '('+item.get('gems')
|
||||
img(src="/images/common/gem.png")
|
||||
img(src="/images/common/gem.png", draggable="false")
|
||||
span )
|
||||
else
|
||||
// Temp, while we only have Warriors: prevent them from buying non-Warrior stuff
|
||||
button.btn.big-font.disabled.unequippable #{item.get('heroClass')} Only
|
||||
|
||||
|
|
@ -4,4 +4,29 @@ block modal-header-content
|
|||
h3(data-i18n="play.achievements") Achievements
|
||||
|
||||
block modal-body-content
|
||||
p TODO: show all dem achievements
|
||||
for achievement in achievements
|
||||
.panel(class=achievement.earned ? 'earned' : '')
|
||||
.panel-body
|
||||
img.icon(src=achievement.getImageURL(), draggable="false")
|
||||
h3= achievement.name
|
||||
p= achievement.description
|
||||
|
||||
if achievement.earnedDate
|
||||
.created=moment(achievement.earnedDate).fromNow()
|
||||
else
|
||||
.created(data-i18n="user.status_unfinished")
|
||||
|
||||
.rewards
|
||||
- rewards = achievement.get('rewards')
|
||||
- rewards = { gems: 100 }
|
||||
if rewards && rewards.gems
|
||||
span.gems.label.label-default
|
||||
span= rewards.gems
|
||||
img.gem(src="/images/common/gem.png", draggable="false")
|
||||
|
||||
- worth = achievement.get('worth')
|
||||
if worth
|
||||
span.worth.label.label-default
|
||||
span #{worth}xp
|
||||
// maybe add more icons/numbers for items, heroes, levels, xp?
|
||||
block modal-footer
|
|
@ -1,7 +1,7 @@
|
|||
.modal-dialog
|
||||
.modal-content
|
||||
|
||||
img(src="/images/pages/play/modal/heroes-background.png")#play-heroes-background
|
||||
img(src="/images/pages/play/modal/heroes-background.png", draggable="false")#play-heroes-background
|
||||
|
||||
h1(data-i18n="choose_hero.choose_hero")
|
||||
|
||||
|
@ -15,13 +15,13 @@
|
|||
li(data-hero-id=hero.get('original'), title=hero.name, data-slide-to=index, data-target="#hero-carousel", class="hero-indicator hero-index-" + index + (hero.locked ? " locked" : ""))
|
||||
.hero-avatar
|
||||
if hero.locked
|
||||
img.lock-indicator(src="/images/pages/game-menu/lock.png")
|
||||
img.lock-indicator(src="/images/pages/game-menu/lock.png", draggable="false")
|
||||
.carousel-inner
|
||||
for hero in heroes
|
||||
div(class="item hero-item" + (hero.locked ? " locked" : ""), data-hero-id=hero.get('original'))
|
||||
canvas.hero-canvas
|
||||
.hero-feature-image
|
||||
img
|
||||
img(draggable="false")
|
||||
.hero-stats
|
||||
h2= hero.name
|
||||
.hero-description= hero.description
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
.modal-dialog
|
||||
.modal-content
|
||||
img(src="/images/pages/play/modal/items-background.png")#play-items-modal-bg
|
||||
img(src="/images/pages/play/modal/items-background-narrow.png")#play-items-modal-narrow-bg
|
||||
img(src="/images/pages/play/modal/items-background.png", draggable="false")#play-items-modal-bg
|
||||
img(src="/images/pages/play/modal/items-background-narrow.png", draggable="false")#play-items-modal-narrow-bg
|
||||
|
||||
h1.big-font(data-i18n="play.items")
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
|||
for category, index in itemCategories
|
||||
li(class=index ? "" : "active")
|
||||
a.one-line(href="#item-category-" + category, data-toggle="tab")
|
||||
img.tab-icon(src="/images/pages/play/modal/item-icon-"+category+".png")
|
||||
img.tab-icon(src="/images/pages/play/modal/item-icon-"+category+".png", draggable="false")
|
||||
span.big-font= itemCategoryNames[index]
|
||||
|
||||
|
||||
|
@ -31,15 +31,15 @@
|
|||
if item.silhouetted && !item.owned
|
||||
span.glyphicon.glyphicon-lock.bolder
|
||||
span.glyphicon.glyphicon-lock
|
||||
img.item-silhouette(src=item.getPortraitURL())
|
||||
img.item-silhouette(src=item.getPortraitURL(), draggable="false")
|
||||
if item.level
|
||||
.required-level
|
||||
div(data-i18n="general.player_level")
|
||||
div= item.level
|
||||
else
|
||||
strong.big-font= item.name
|
||||
img.item-img(src=item.getPortraitURL())
|
||||
img.item-shadow(src=item.getPortraitURL())
|
||||
img.item-img(src=item.getPortraitURL(), draggable="false")
|
||||
img.item-shadow(src=item.getPortraitURL(), draggable="false")
|
||||
|
||||
if item.owned
|
||||
span.big-font.owned(data-i18n="play.owned")
|
||||
|
@ -47,7 +47,7 @@
|
|||
span.big-font.locked(data-i18n="play.locked")
|
||||
else
|
||||
span.cost
|
||||
img(src="/images/common/gem.png")
|
||||
img(src="/images/common/gem.png", draggable="false")
|
||||
span.big-font= item.get('gems')
|
||||
if item.equippable
|
||||
// Temp, while we only have Warriors: prevent them from buying non-Warrior stuff
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
.gradient.vertical-gradient.right-gradient
|
||||
.gradient.horizontal-gradient.bottom-gradient
|
||||
.gradient.vertical-gradient.left-gradient
|
||||
img.map-background(src="/images/pages/play/map_" + mapType + ".jpg", alt="")
|
||||
img.map-background(src="/images/pages/play/map_" + mapType + ".jpg", alt="", draggable="false")
|
||||
|
||||
- var seenNext = nextLevel;
|
||||
each level in campaign.levels
|
||||
|
@ -43,10 +43,10 @@
|
|||
.game-controls.header-font
|
||||
button.btn.items(data-toggle='coco-modal', data-target='play/modal/PlayItemsModal', data-i18n="[title]play.items")
|
||||
button.btn.heroes(data-toggle='coco-modal', data-target='play/modal/PlayHeroesModal', data-i18n="[title]play.heroes")
|
||||
button.btn.achievements(data-toggle='coco-modal', data-target='play/modal/PlayAchievementsModal', data-i18n="[title]play.achievements")
|
||||
if me.get('anonymous') === false
|
||||
button.btn.gems(data-toggle='coco-modal', data-target='play/modal/BuyGemsModal', data-i18n="[title]play.buy_gems")
|
||||
if me.isAdmin()
|
||||
button.btn.achievements(data-toggle='coco-modal', data-target='play/modal/PlayAchievementsModal', data-i18n="[title]play.achievements")
|
||||
button.btn.account(data-toggle='coco-modal', data-target='play/modal/PlayAccountModal', data-i18n="[title]play.account")
|
||||
button.btn.settings(data-toggle='coco-modal', data-target='play/modal/PlaySettingsModal', data-i18n="[title]play.settings")
|
||||
else if me.get('anonymous', true)
|
||||
|
|
|
@ -17,6 +17,7 @@ module.exports = class GameMenuModal extends ModalView
|
|||
'change input.select': 'onSelectionChanged'
|
||||
'shown.bs.tab #game-menu-nav a': 'onTabShown'
|
||||
'click #change-hero-tab': -> @trigger 'change-hero'
|
||||
'click #close-modal': 'hide'
|
||||
|
||||
constructor: (options) ->
|
||||
super options
|
||||
|
|
|
@ -20,9 +20,10 @@ module.exports = class InventoryModal extends ModalView
|
|||
|
||||
events:
|
||||
'click .item-slot': 'onItemSlotClick'
|
||||
'click #unequipped img.item': 'onUnequippedItemClick'
|
||||
'doubletap #unequipped img.item': 'onUnequippedItemDoubleClick'
|
||||
'doubletap .item-slot img.item': 'onEquippedItemDoubleClick'
|
||||
'click #unequipped .item': 'onUnequippedItemClick'
|
||||
'doubletap #unequipped .item': 'onUnequippedItemDoubleClick'
|
||||
'doubletap .item-slot .item': 'onEquippedItemDoubleClick'
|
||||
'click button.equip-item': 'onClickEquipItemButton'
|
||||
'shown.bs.modal': 'onShown'
|
||||
'click #choose-hero-button': 'onClickChooseHero'
|
||||
'click #play-level-button': 'onClickPlayLevel'
|
||||
|
@ -83,9 +84,11 @@ module.exports = class InventoryModal extends ModalView
|
|||
|
||||
# sort into one of the four groups
|
||||
locked = not (item.get('original') in me.items())
|
||||
locked = false if me.get('slug') is 'nick'
|
||||
#locked = false if me.get('slug') is 'nick'
|
||||
|
||||
if locked and item.get('slug') isnt 'simple-boots'
|
||||
if not item.getFrontFacingStats().props.length and not _.size(item.getFrontFacingStats().stats) and not locked # Temp: while there are placeholder items
|
||||
null # Don't put into a collection
|
||||
else if locked and item.get('slug') isnt 'simple-boots'
|
||||
@itemGroups.lockedItems.add(item)
|
||||
item.classes.push 'locked'
|
||||
item.classes.push 'silhouette' if item.isSilhouettedItem()
|
||||
|
@ -126,6 +129,7 @@ module.exports = class InventoryModal extends ModalView
|
|||
@insertSubView(@itemDetailsView)
|
||||
@requireLevelEquipment()
|
||||
@$el.find('.nano').nanoScroller({alwaysVisible: true})
|
||||
@onSelectionChanged()
|
||||
|
||||
afterInsert: ->
|
||||
super()
|
||||
|
@ -137,7 +141,7 @@ module.exports = class InventoryModal extends ModalView
|
|||
#- Draggable logic
|
||||
|
||||
setUpDraggableEventsForAvailableEquipment: ->
|
||||
for availableItemEl in @$el.find('#unequipped img.item')
|
||||
for availableItemEl in @$el.find('#unequipped .item')
|
||||
availableItemEl = $(availableItemEl)
|
||||
continue if availableItemEl.hasClass('locked') or availableItemEl.hasClass('restricted')
|
||||
dragHelper = availableItemEl.clone().addClass('draggable-item')
|
||||
|
@ -201,12 +205,12 @@ module.exports = class InventoryModal extends ModalView
|
|||
|
||||
onUnequippedItemClick: (e) ->
|
||||
return if @justDoubleClicked
|
||||
itemEl = $(e.target).closest('img.item')
|
||||
itemEl = $(e.target).closest('.item')
|
||||
@selectUnequippedItem(itemEl)
|
||||
|
||||
onUnequippedItemDoubleClick: (e) ->
|
||||
item = $(e.target).closest('img.item')
|
||||
return if item.hasClass('locked') or item.hasClass('restricted')
|
||||
itemEl = $(e.target).closest('.item')
|
||||
return if itemEl.hasClass('locked') or itemEl.hasClass('restricted')
|
||||
@equipSelectedItem()
|
||||
@justDoubleClicked = true
|
||||
_.defer => @justDoubleClicked = false
|
||||
|
@ -215,6 +219,11 @@ module.exports = class InventoryModal extends ModalView
|
|||
onClickEquipItemViewed: -> @equipSelectedItem()
|
||||
onClickUnequipItemViewed: -> @unequipSelectedItem()
|
||||
|
||||
onClickEquipItemButton: (e) ->
|
||||
itemEl = $(e.target).closest('.item')
|
||||
@selectUnequippedItem(itemEl)
|
||||
@equipSelectedItem()
|
||||
|
||||
onUnlockButtonClicked: (e) ->
|
||||
button = $(e.target).closest('button')
|
||||
if button.hasClass('confirm')
|
||||
|
@ -249,7 +258,7 @@ module.exports = class InventoryModal extends ModalView
|
|||
selectItemSlot: (slotEl) ->
|
||||
@clearSelection()
|
||||
slotEl.addClass('selected')
|
||||
selectedSlotItemID = slotEl.find('img.item').data('item-id')
|
||||
selectedSlotItemID = slotEl.find('.item').data('item-id')
|
||||
item = @items.get(selectedSlotItemID)
|
||||
if item then @showItemDetails(item, 'unequip')
|
||||
@onSelectionChanged()
|
||||
|
@ -270,7 +279,7 @@ module.exports = class InventoryModal extends ModalView
|
|||
selectedItemEl.effect('transfer', to: slotEl, duration: 500, easing: 'easeOutCubic')
|
||||
unequipped = @unequipItemFromSlot(slotEl)
|
||||
selectedItemEl.addClass('equipped')
|
||||
slotEl.append(selectedItemEl.clone())
|
||||
slotEl.append(selectedItemEl.find('img').clone().addClass('item').data('item-id', selectedItem.id))
|
||||
@clearSelection()
|
||||
@showItemDetails(selectedItem, 'unequip')
|
||||
slotEl.addClass('selected')
|
||||
|
@ -302,17 +311,17 @@ module.exports = class InventoryModal extends ModalView
|
|||
@hideItemDetails()
|
||||
|
||||
unequipItemFromSlot: (slotEl) ->
|
||||
itemEl = slotEl.find('img.item')
|
||||
itemEl = slotEl.find('.item')
|
||||
itemIDToUnequip = itemEl.data('item-id')
|
||||
return unless itemIDToUnequip
|
||||
itemEl.remove()
|
||||
@$el.find("#unequipped img.item[data-item-id=#{itemIDToUnequip}]").removeClass('equipped')
|
||||
@$el.find("#unequipped .item[data-item-id=#{itemIDToUnequip}]").removeClass('equipped')
|
||||
|
||||
deselectAllSlots: ->
|
||||
@$el.find('#equipped .item-slot.selected').removeClass('selected')
|
||||
|
||||
deselectAllUnequippedItems: ->
|
||||
@$el.find('#unequipped img.item').removeClass('active')
|
||||
@$el.find('#unequipped .item').removeClass('active')
|
||||
|
||||
getSlot: (name) ->
|
||||
@$el.find(".item-slot[data-slot=#{name}]")
|
||||
|
@ -321,9 +330,13 @@ module.exports = class InventoryModal extends ModalView
|
|||
@$el.find('#equipped .item-slot.selected')
|
||||
|
||||
getSelectedUnequippedItem: ->
|
||||
@$el.find('#unequipped img.item.active')
|
||||
@$el.find('#unequipped .item.active')
|
||||
|
||||
onSelectionChanged: ->
|
||||
heroClass = @selectedHero?.get('heroClass')
|
||||
itemsCanBeEquipped = @$el.find('#unequipped .item.available:not(.equipped)').filter('.'+heroClass).length
|
||||
toShow = @$el.find('#double-click-hint, #available-description')
|
||||
if itemsCanBeEquipped then toShow.removeClass('secret') else toShow.addClass('secret')
|
||||
@delegateEvents()
|
||||
|
||||
|
||||
|
@ -340,7 +353,7 @@ module.exports = class InventoryModal extends ModalView
|
|||
config = {}
|
||||
for slot in @$el.find('.item-slot')
|
||||
slotName = $(slot).data('slot')
|
||||
slotItemID = $(slot).find('img.item').data('item-id')
|
||||
slotItemID = $(slot).find('.item').data('item-id')
|
||||
continue unless slotItemID
|
||||
item = _.find @items.models, {id:slotItemID}
|
||||
config[slotName] = item.get('original')
|
||||
|
@ -387,7 +400,6 @@ module.exports = class InventoryModal extends ModalView
|
|||
@highlightElement availableSlotSelector, delay: 500, sides: ['right'], rotation: Math.PI / 2
|
||||
@$el.find(availableSlotSelector).addClass 'should-equip'
|
||||
@$el.find("#equipped div[data-slot='#{slot}']").addClass 'should-equip'
|
||||
@$el.find('#double-click-hint').removeClass('secret')
|
||||
@remainingRequiredEquipment.push slot: slot, item: gear[item]
|
||||
if hadRequired and not @remainingRequiredEquipment.length
|
||||
@endHighlight()
|
||||
|
|
|
@ -126,8 +126,6 @@ module.exports = class AuthModal extends ModalView
|
|||
step.done = false for step in @gplusAuthSteps
|
||||
handler = application.gplusHandler
|
||||
|
||||
@renderGPlusAuthChecklist()
|
||||
|
||||
@listenToOnce handler, 'logged-in', ->
|
||||
@gplusAuthSteps[0].done = true
|
||||
@renderGPlusAuthChecklist()
|
||||
|
|
|
@ -102,7 +102,7 @@ module.exports = class WorldMapView extends RootView
|
|||
context.isIPadApp = application.isIPadApp
|
||||
context.mapType = _.string.slugify @terrain
|
||||
context.nextLevel = @nextLevel
|
||||
context.forestIsAvailable = '541b67f71ccc8eaae19f3c62' in (me.get('earned')?.levels or [])
|
||||
context.forestIsAvailable = @startedForestLevel or '541b67f71ccc8eaae19f3c62' in (me.get('earned')?.levels or [])
|
||||
context
|
||||
|
||||
afterRender: ->
|
||||
|
@ -131,8 +131,10 @@ module.exports = class WorldMapView extends RootView
|
|||
@openModalView authModal
|
||||
|
||||
onSessionsLoaded: (e) ->
|
||||
forestLevels = (f.id for f in forest)
|
||||
for session in @sessions.models
|
||||
@levelStatusMap[session.get('levelID')] = if session.get('state')?.complete then 'complete' else 'started'
|
||||
@startedForestLevel = true if session.get('levelID') in forestLevels
|
||||
if @nextLevel and @levelStatusMap[@nextLevel] is 'complete'
|
||||
@nextLevel = null
|
||||
@render()
|
||||
|
@ -771,3 +773,4 @@ if me.getKithmazeGroup() is 'the-first-kithmaze'
|
|||
_.remove dungeon, id: 'haunted-kithmaze'
|
||||
else
|
||||
_.remove dungeon, id: 'the-first-kithmaze'
|
||||
_.find(dungeon, id: 'the-raised-sword').nextLevels.continue = 'haunted-kithmaze'
|
||||
|
|
|
@ -79,7 +79,7 @@ module.exports = class LevelHUDView extends CocoView
|
|||
wrapper.removeClass (i, css) -> (css.match(/\bteam-\S+/g) or []).join ' '
|
||||
wrapper.addClass "team-#{team}"
|
||||
if thangType.get('raster')
|
||||
wrapper.empty().append($('<img />').addClass('avatar').attr('src', '/file/'+thangType.get('raster')))
|
||||
wrapper.empty().append($('<img draggable="false"/>').addClass('avatar').attr('src', '/file/'+thangType.get('raster')))
|
||||
else
|
||||
return unless stage = thangType.getPortraitStage options, 100
|
||||
newCanvas = $(stage.canvas).addClass('thang-canvas avatar')
|
||||
|
@ -87,7 +87,7 @@ module.exports = class LevelHUDView extends CocoView
|
|||
stage.update()
|
||||
@stage?.stopTalking()
|
||||
@stage = stage
|
||||
wrapper.append($('<img />').addClass('avatar-frame').attr('src', '/images/level/thang_avatar_frame.png'))
|
||||
wrapper.append($('<img draggable="false" />').addClass('avatar-frame').attr('src', '/images/level/thang_avatar_frame.png'))
|
||||
|
||||
onThangBeganTalking: (e) ->
|
||||
return unless @stage and @thang is e.thang
|
||||
|
|
|
@ -210,6 +210,7 @@ module.exports = class LevelPlaybackView extends CocoView
|
|||
onProgressHover: (e, offsetX) ->
|
||||
timeRatio = @$progressScrubber.width() / @totalTime
|
||||
offsetX ?= e.clientX - $(e.target).closest('#timeProgress').offset().left
|
||||
offsetX = Math.max 0, offsetX
|
||||
@newTime = offsetX / timeRatio
|
||||
@updatePopupContent()
|
||||
@timePopup?.onHover e
|
||||
|
|
|
@ -6,4 +6,10 @@ module.exports = class ReloadLevelModal extends ModalView
|
|||
template: template
|
||||
|
||||
events:
|
||||
'click #restart-level-confirm-button': -> Backbone.Mediator.publish 'level:restart', {}
|
||||
'click #restart-level-confirm-button': 'onClickRestart'
|
||||
|
||||
onClickRestart: (e) ->
|
||||
if key.shift
|
||||
Backbone.Mediator.publish 'level:restart', {}
|
||||
else
|
||||
Backbone.Mediator.publish 'tome:reload-code', {}
|
||||
|
|
|
@ -34,8 +34,18 @@ module.exports = class ProblemAlertView extends CocoView
|
|||
getRenderData: (context={}) ->
|
||||
context = super context
|
||||
if @problem?
|
||||
format = (s) -> s?.replace(/</g, '<').replace(/>/g, '>').replace(/\n/g, '<br>')
|
||||
context.message = format @problem.aetherProblem.message
|
||||
format = (s) -> marked(s.replace(/</g, '<').replace(/>/g, '>')) if s?
|
||||
message = @problem.aetherProblem.message
|
||||
# Add time to problem message if hint is for a missing null check
|
||||
# NOTE: This may need to be updated with Aether error hint changes
|
||||
if @problem.aetherProblem.hint? and /(?:null|undefined)/.test @problem.aetherProblem.hint
|
||||
age = @problem.aetherProblem.userInfo?.age
|
||||
if age?
|
||||
if /^Line \d+:/.test message
|
||||
message = message.replace /^(Line \d+)/, "$1, time #{age.toFixed(1)}"
|
||||
else
|
||||
message = "Time #{age.toFixed(1)}: #{message}"
|
||||
context.message = format message
|
||||
context.hint = format @problem.aetherProblem.hint
|
||||
context
|
||||
|
||||
|
|
|
@ -51,10 +51,20 @@ module.exports = class SpellPaletteView extends CocoView
|
|||
@entryGroupElements = {}
|
||||
for group, entries of @entryGroups
|
||||
@entryGroupElements[group] = itemGroup = $('<div class="property-entry-item-group"></div>').appendTo @$el.find('.properties')
|
||||
itemGroup.append $('<img class="item-image"></img>').attr('src', entries[0].options.item.getPortraitURL()).css('top', Math.max(0, 19 * (entries.length - 2) / 2) + 2) if entries[0].options.item?.getPortraitURL
|
||||
for entry in entries
|
||||
if entries[0].options.item?.getPortraitURL
|
||||
itemImage = $('<img class="item-image" draggable=false></img>').attr('src', entries[0].options.item.getPortraitURL()).css('top', Math.max(0, 19 * (entries.length - 2) / 2) + 2)
|
||||
itemGroup.append itemImage
|
||||
firstEntry = entries[0]
|
||||
do (firstEntry) ->
|
||||
itemImage.on "mouseenter", (e) -> firstEntry.onMouseEnter e
|
||||
itemImage.on "mouseleave", (e) -> firstEntry.onMouseLeave e
|
||||
for entry, entryIndex in entries
|
||||
itemGroup.append entry.el
|
||||
entry.render() # Render after appending so that we can access parent container for popover
|
||||
if entries.length is 1
|
||||
entry.$el.addClass 'single-entry'
|
||||
if entryIndex is 0
|
||||
entry.$el.addClass 'first-entry'
|
||||
@$el.addClass 'hero'
|
||||
@updateMaxHeight() unless application.isIPadApp
|
||||
|
||||
|
|
|
@ -189,7 +189,7 @@ module.exports = class SpellView extends CocoView
|
|||
bindKey: {win: 'Ctrl-Shift-M', mac: 'Command-Shift-M|Ctrl-Shift-M'}
|
||||
exec: -> Backbone.Mediator.publish 'tome:toggle-maximize', {}
|
||||
addCommand
|
||||
# TODO: Restrict to beginner campaign levels
|
||||
# TODO: Restrict to beginner campaign levels, possibly with a CampaignOptions similar to LevelOptions
|
||||
name: 'enter-skip-delimiters'
|
||||
bindKey: 'Enter|Return'
|
||||
exec: =>
|
||||
|
@ -202,6 +202,10 @@ module.exports = class SpellView extends CocoView
|
|||
newRange.setEnd newRange.end.row, newRange.end.column + delimMatch[1].length
|
||||
@aceSession.selection.setSelectionRange newRange
|
||||
@ace.execCommand 'insertstring', '\n'
|
||||
addCommand
|
||||
name: 'disable-spaces'
|
||||
bindKey: 'Space'
|
||||
exec: => @ace.execCommand 'insertstring', ' ' unless LevelOptions[@options.level.get('slug')]?.disableSpaces
|
||||
|
||||
fillACE: ->
|
||||
@ace.setValue @spell.source
|
||||
|
@ -390,7 +394,7 @@ module.exports = class SpellView extends CocoView
|
|||
@focus() if cast
|
||||
|
||||
onCodeReload: (e) ->
|
||||
return unless e.spell is @spell
|
||||
return unless e.spell is @spell or not e.spell
|
||||
@reloadCode true
|
||||
@ace.clearSelection()
|
||||
_.delay (=> @ace?.clearSelection()), 500 # Make double sure this gets done (saw some timing issues?)
|
||||
|
@ -446,7 +450,8 @@ module.exports = class SpellView extends CocoView
|
|||
_.throttle @updateLines, 500
|
||||
_.throttle @hideProblemAlert, 500
|
||||
]
|
||||
onSignificantChange.push _.debounce @checkRequiredCode, 1500 if requiredCodePerLevel[@options.level.get('slug')]
|
||||
onSignificantChange.push _.debounce @checkRequiredCode, 750 if LevelOptions[@options.level.get('slug')]?.requiredCode
|
||||
onSignificantChange.push _.debounce @checkSuspectCode, 750 if LevelOptions[@options.level.get('slug')]?.suspectCode
|
||||
@onCodeChangeMetaHandler = =>
|
||||
return if @eventsSuppressed
|
||||
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'code-change', volume: 0.5
|
||||
|
@ -877,13 +882,26 @@ module.exports = class SpellView extends CocoView
|
|||
checkRequiredCode: =>
|
||||
return if @destroyed
|
||||
source = @getSource().replace @singleLineCommentRegex(), ''
|
||||
for requiredCodeFragment in requiredCodePerLevel[@options.level.get('slug')]
|
||||
requiredCodeFragments = LevelOptions[@options.level.get('slug')].requiredCode
|
||||
for requiredCodeFragment in requiredCodeFragments
|
||||
# Could make this obey regular expressions like suspectCode if needed
|
||||
if source.indexOf(requiredCodeFragment) is -1
|
||||
@warnedCodeFragments ?= {}
|
||||
unless @warnedCodeFragments[requiredCodeFragment]
|
||||
Backbone.Mediator.publish 'tome:required-code-fragment-deleted', codeFragment: requiredCodeFragment
|
||||
@warnedCodeFragments[requiredCodeFragment] = true
|
||||
|
||||
checkSuspectCode: =>
|
||||
return if @destroyed
|
||||
source = @getSource().replace @singleLineCommentRegex(), ''
|
||||
suspectCodeFragments = LevelOptions[@options.level.get('slug')].suspectCode
|
||||
for suspectCodeFragment in suspectCodeFragments
|
||||
if suspectCodeFragment.pattern.test source
|
||||
@warnedCodeFragments ?= {}
|
||||
unless @warnedCodeFragments[suspectCodeFragment.name]
|
||||
Backbone.Mediator.publish 'tome:suspect-code-fragment-added', codeFragment: suspectCodeFragment.name, codeLanguage: @spell.language
|
||||
@warnedCodeFragments[suspectCodeFragment] = true
|
||||
|
||||
destroy: ->
|
||||
$(@ace?.container).find('.ace_gutter').off 'click', '.ace_error, .ace_warning, .ace_info', @onAnnotationClick
|
||||
$(@ace?.container).find('.ace_gutter').off 'click', @onGutterClick
|
||||
|
@ -896,11 +914,3 @@ module.exports = class SpellView extends CocoView
|
|||
@debugView?.destroy()
|
||||
$(window).off 'resize', @onWindowResize
|
||||
super()
|
||||
|
||||
|
||||
requiredCodePerLevel =
|
||||
'dungeons-of-kithgard': ['moveRight']
|
||||
'true-names': ['Brak']
|
||||
'the-first-kithmaze': ['loop']
|
||||
'haunted-kithmaze': ['loop']
|
||||
'lowly-kithmen': ['findNearestEnemy']
|
||||
|
|
|
@ -44,7 +44,6 @@ module.exports = class ItemDetailsView extends CocoView
|
|||
@listenToOnce docs, 'sync', @onDocsLoaded
|
||||
|
||||
@render()
|
||||
@$el.find('.nano:visible').nanoScroller()
|
||||
|
||||
onDocsLoaded: (levelComponents) ->
|
||||
for component in levelComponents.models
|
||||
|
@ -55,6 +54,10 @@ module.exports = class ItemDetailsView extends CocoView
|
|||
@propDocs[propDoc.name] = propDoc
|
||||
@render()
|
||||
|
||||
afterRender: ->
|
||||
super()
|
||||
@$el.find('.nano:visible').nanoScroller({alwaysVisible: true})
|
||||
|
||||
getRenderData: ->
|
||||
c = super()
|
||||
c.item = @item
|
||||
|
|
|
@ -2,27 +2,92 @@ ModalView = require 'views/kinds/ModalView'
|
|||
template = require 'templates/play/modal/play-achievements-modal'
|
||||
CocoCollection = require 'collections/CocoCollection'
|
||||
Achievement = require 'models/Achievement'
|
||||
#AchievementView = require 'views/game-menu/AchievementView'
|
||||
EarnedAchievement = require 'models/EarnedAchievement'
|
||||
|
||||
utils = require 'lib/utils'
|
||||
|
||||
PAGE_SIZE = 200
|
||||
|
||||
module.exports = class PlayAchievementsModal extends ModalView
|
||||
className: 'modal fade play-modal'
|
||||
template: template
|
||||
modalWidthPercent: 90
|
||||
id: 'play-achievements-modal'
|
||||
#instant: true
|
||||
plain: true
|
||||
|
||||
#events:
|
||||
# 'change input.select': 'onSelectionChanged'
|
||||
earnedMap: {}
|
||||
|
||||
constructor: (options) ->
|
||||
super options
|
||||
#@achievements = new CocoCollection([], {model: Achievement})
|
||||
#@achievements.url = '/db/thang.type?view=achievements&project=name,description,components,original,rasterIcon'
|
||||
#@supermodel.loadCollection(@achievements, 'achievements')
|
||||
@achievements = new Backbone.Collection()
|
||||
earnedMap = {}
|
||||
|
||||
achievementsFetcher = new CocoCollection([], {url: '/db/achievement', model: Achievement})
|
||||
achievementsFetcher.setProjection([
|
||||
'name'
|
||||
'description'
|
||||
'icon'
|
||||
'worth'
|
||||
'i18n'
|
||||
'rewards'
|
||||
'collection'
|
||||
])
|
||||
|
||||
earnedAchievementsFetcher = new CocoCollection([], {url: '/db/earned_achievement', model: EarnedAchievement})
|
||||
earnedAchievementsFetcher.setProjection([ 'achievement' ])
|
||||
|
||||
achievementsFetcher.skip = 0
|
||||
achievementsFetcher.fetch({data: {skip: 0, limit: PAGE_SIZE}})
|
||||
earnedAchievementsFetcher.skip = 0
|
||||
earnedAchievementsFetcher.fetch({data: {skip: 0, limit: PAGE_SIZE}})
|
||||
|
||||
@listenTo achievementsFetcher, 'sync', @onAchievementsLoaded
|
||||
@listenTo earnedAchievementsFetcher, 'sync', @onEarnedAchievementsLoaded
|
||||
|
||||
@supermodel.loadCollection(achievementsFetcher, 'achievement')
|
||||
@supermodel.loadCollection(earnedAchievementsFetcher, 'achievement')
|
||||
|
||||
@onEverythingLoaded = _.after(2, @onEverythingLoaded)
|
||||
|
||||
onAchievementsLoaded: (fetcher) ->
|
||||
needMore = fetcher.models.length is PAGE_SIZE
|
||||
@achievements.add(fetcher.models)
|
||||
if needMore
|
||||
fetcher.skip += PAGE_SIZE
|
||||
fetcher.fetch({data: {skip: fetcher.skip, limit: PAGE_SIZE}})
|
||||
else
|
||||
@stopListening(fetcher)
|
||||
@onEverythingLoaded()
|
||||
|
||||
onEarnedAchievementsLoaded: (fetcher) ->
|
||||
needMore = fetcher.models.length is PAGE_SIZE
|
||||
for earned in fetcher.models
|
||||
@earnedMap[earned.get('achievement')] = earned
|
||||
if needMore
|
||||
fetcher.skip += PAGE_SIZE
|
||||
fetcher.fetch({data: {skip: fetcher.skip, limit: PAGE_SIZE}})
|
||||
else
|
||||
@stopListening(fetcher)
|
||||
@onEverythingLoaded()
|
||||
|
||||
onEverythingLoaded: =>
|
||||
@achievements.set(@achievements.filter((m) -> m.get('collection') isnt 'level.sessions'))
|
||||
for achievement in @achievements.models
|
||||
if earned = @earnedMap[achievement.id]
|
||||
achievement.earned = earned
|
||||
achievement.earnedDate = earned.getCreationDate()
|
||||
achievement.earnedDate ?= ''
|
||||
@achievements.comparator = (m) -> m.earnedDate
|
||||
@achievements.sort()
|
||||
@achievements.set(@achievements.models.reverse())
|
||||
for achievement in @achievements.models
|
||||
achievement.name = utils.i18n achievement.attributes, 'name'
|
||||
achievement.description = utils.i18n achievement.attributes, 'description'
|
||||
@render()
|
||||
|
||||
getRenderData: (context={}) ->
|
||||
context = super(context)
|
||||
#context.achievements = @achievements.models
|
||||
context.achievements = @achievements.models
|
||||
context
|
||||
|
||||
afterRender: ->
|
||||
|
|
|
@ -89,7 +89,7 @@ module.exports = class PlayItemsModal extends ModalView
|
|||
model.affordable = cost <= gemsOwned
|
||||
model.silhouetted = not model.owned and model.isSilhouettedItem()
|
||||
model.level = model.levelRequiredForItem() if model.get('tier')?
|
||||
model.equippable = 'Warrior' in model.getAllowedHeroClasses() # Temp: while there are no wizards/rangers
|
||||
model.unequippable = not ('Warrior' in model.getAllowedHeroClasses()) # Temp: while there are no wizards/rangers
|
||||
model.comingSoon = not model.getFrontFacingStats().props.length and not _.size model.getFrontFacingStats().stats and not model.owned # Temp: while there are placeholder items
|
||||
@idToItem[model.id] = model
|
||||
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
cd ~/Desktop/coco
|
||||
cp ~/Desktop/treema/treema.js ./vendor/scripts/
|
||||
cp ~/Desktop/treema/treema.css ./vendor/styles/
|
|
@ -2,12 +2,6 @@ mongoose = require 'mongoose'
|
|||
jsonschema = require '../../app/schemas/models/earned_achievement'
|
||||
|
||||
EarnedAchievementSchema = new mongoose.Schema({
|
||||
created:
|
||||
type: Date
|
||||
default: Date.now
|
||||
changed:
|
||||
type: Date
|
||||
default: Date.now
|
||||
notified:
|
||||
type: Boolean
|
||||
default: false
|
||||
|
|
|
@ -16,7 +16,26 @@ class EarnedAchievementHandler extends Handler
|
|||
|
||||
get: (req, res) ->
|
||||
return @getByAchievementIDs(req, res) if req.query.view is 'get-by-achievement-ids'
|
||||
super(arguments...)
|
||||
query = { user: req.user._id+''}
|
||||
|
||||
projection = {}
|
||||
if req.query.project
|
||||
projection[field] = 1 for field in req.query.project.split(',')
|
||||
|
||||
q = EarnedAchievement.find(query, projection)
|
||||
|
||||
skip = parseInt(req.query.skip)
|
||||
if skip? and skip < 1000000
|
||||
q.skip(skip)
|
||||
|
||||
limit = parseInt(req.query.limit)
|
||||
if limit? and limit < 1000
|
||||
q.limit(limit)
|
||||
|
||||
q.exec (err, documents) =>
|
||||
return @sendDatabaseError(res, err) if err
|
||||
documents = (@formatEntity(req, doc) for doc in documents)
|
||||
@sendSuccess(res, documents)
|
||||
|
||||
getByAchievementIDs: (req, res) ->
|
||||
query = { user: req.user._id+''}
|
||||
|
|
|
@ -129,6 +129,10 @@ module.exports = class Handler
|
|||
term = req.query.term
|
||||
matchedObjects = []
|
||||
filters = if @modelClass.schema.uses_coco_versions or @modelClass.schema.uses_coco_permissions then [filter: {index: true}] else [filter: {}]
|
||||
|
||||
skip = parseInt(req.query.skip)
|
||||
limit = parseInt(req.query.limit)
|
||||
|
||||
if @modelClass.schema.uses_coco_permissions and req.user
|
||||
filters.push {filter: {index: req.user.get('id')}}
|
||||
projection = null
|
||||
|
@ -158,7 +162,14 @@ module.exports = class Handler
|
|||
else
|
||||
args = [filter.filter]
|
||||
args.push projection if projection
|
||||
@modelClass.find(args...).limit(FETCH_LIMIT).exec callback
|
||||
q = @modelClass.find(args...)
|
||||
if skip? and skip < 1000000
|
||||
q.skip(skip)
|
||||
if limit? and limit < FETCH_LIMIT
|
||||
q.limit(limit)
|
||||
else
|
||||
q.limit(FETCH_LIMIT)
|
||||
q.exec callback
|
||||
# if it's not a text search but the user is an admin, let him try stuff anyway
|
||||
else if req.user?.isAdmin()
|
||||
# admins can send any sort of query down the wire
|
||||
|
|
43
vendor/scripts/SpriteContainer.js
vendored
43
vendor/scripts/SpriteContainer.js
vendored
|
@ -30,8 +30,10 @@
|
|||
this.createjs = this.createjs||{};
|
||||
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
|
||||
/**
|
||||
* A SpriteContainer is a nestable display list that enables aggressively optimized rendering of bitmap content.
|
||||
* In order to accomplish these optimizations, SpriteContainer enforces a few restrictions on its content.
|
||||
*
|
||||
|
@ -41,6 +43,7 @@ this.createjs = this.createjs||{};
|
|||
* - all children (with the exception of DOMElement) MUST use the same spriteSheet.
|
||||
*
|
||||
* <h4>Example</h4>
|
||||
*
|
||||
* var data = {
|
||||
* images: ["sprites.jpg"],
|
||||
* frames: {width:50, height:50},
|
||||
|
@ -58,43 +61,24 @@ this.createjs = this.createjs||{};
|
|||
* @constructor
|
||||
* @param {SpriteSheet} [spriteSheet] The spriteSheet to use for this SpriteContainer and its children.
|
||||
**/
|
||||
var SpriteContainer = function(spriteSheet) {
|
||||
this.initialize(spriteSheet);
|
||||
};
|
||||
var p = SpriteContainer.prototype = new createjs.Container();
|
||||
function SpriteContainer(spriteSheet) {
|
||||
this.Container_constructor();
|
||||
|
||||
// public properties:
|
||||
|
||||
// public properties:
|
||||
/**
|
||||
* The SpriteSheet that this container enforces use of.
|
||||
* @property spriteSheet
|
||||
* @type {SpriteSheet}
|
||||
* @readonly
|
||||
**/
|
||||
p.spriteSheet = null;
|
||||
|
||||
// constructor:
|
||||
|
||||
/**
|
||||
* @property Container_initialize
|
||||
* @type Function
|
||||
* @private
|
||||
**/
|
||||
p.Container_initialize = p.initialize;
|
||||
|
||||
/**
|
||||
* Initialization method.
|
||||
* @method initialize
|
||||
* @param {SpriteSheet} spriteSheet Optional. The spriteSheet to use for this SpriteContainer and its children.
|
||||
* @protected
|
||||
*/
|
||||
p.initialize = function(spriteSheet) {
|
||||
this.Container_initialize();
|
||||
this.spriteSheet = spriteSheet;
|
||||
};
|
||||
}
|
||||
var p = createjs.extend(SpriteContainer, createjs.Container);
|
||||
|
||||
|
||||
|
||||
// public methods:
|
||||
|
||||
/**
|
||||
* Adds a child to the top of the display list.
|
||||
* Only children of type SpriteContainer, Sprite, Bitmap, BitmapText, or DOMElement are allowed.
|
||||
|
@ -163,7 +147,7 @@ var p = SpriteContainer.prototype = new createjs.Container();
|
|||
}
|
||||
if (child._spritestage_compatibility <= 4) {
|
||||
var spriteSheet = child.spriteSheet;
|
||||
if ((!spriteSheet || !spriteSheet._images || spriteSheet._images.length > 1) || (this.spritesheet && spritesheet !== spritesheet)) {
|
||||
if ((!spriteSheet || !spriteSheet._images || spriteSheet._images.length > 1) || (this.spriteSheet && this.spriteSheet !== spriteSheet)) {
|
||||
console && console.log("Error: A child's spriteSheet must be equal to its parent spriteSheet and only use one image. [" + child.toString() + "]");
|
||||
return child;
|
||||
}
|
||||
|
@ -184,5 +168,6 @@ var p = SpriteContainer.prototype = new createjs.Container();
|
|||
return "[SpriteContainer (name="+ this.name +")]";
|
||||
};
|
||||
|
||||
createjs.SpriteContainer = SpriteContainer;
|
||||
|
||||
createjs.SpriteContainer = createjs.promote(SpriteContainer, "Container");
|
||||
}());
|
454
vendor/scripts/SpriteStage.js
vendored
454
vendor/scripts/SpriteStage.js
vendored
|
@ -36,14 +36,17 @@ this.createjs = this.createjs||{};
|
|||
(function() {
|
||||
"use strict";
|
||||
|
||||
// Set which classes are compatible with SpriteStage.
|
||||
// The order is important!!! If it's changed/appended, make sure that any logic that
|
||||
// checks _spritestage_compatibility accounts for it!
|
||||
[createjs.SpriteContainer, createjs.Sprite, createjs.BitmapText, createjs.Bitmap, createjs.DOMElement].forEach(function(_class, index) {
|
||||
_class.prototype._spritestage_compatibility = index + 1;
|
||||
});
|
||||
|
||||
/**
|
||||
// Set which classes are compatible with SpriteStage.
|
||||
// The order is important!!! If it's changed/appended, make sure that any logic that
|
||||
// checks _spritestage_compatibility accounts for it!
|
||||
[createjs.SpriteContainer, createjs.Sprite, createjs.BitmapText, createjs.Bitmap, createjs.DOMElement].forEach(function(_class, index) {
|
||||
_class.prototype._spritestage_compatibility = index + 1;
|
||||
});
|
||||
|
||||
|
||||
// constructor:
|
||||
/**
|
||||
* A sprite stage is the root level {{#crossLink "Container"}}{{/crossLink}} for an aggressively optimized display list. Each time its {{#crossLink "Stage/tick"}}{{/crossLink}}
|
||||
* method is called, it will render its display list to its target canvas. WebGL content is fully compatible with the existing Context2D renderer.
|
||||
* On devices or browsers that don't support WebGL, content will automatically be rendered via canvas 2D.
|
||||
|
@ -77,13 +80,193 @@ this.createjs = this.createjs||{};
|
|||
* @param {Boolean} preserveDrawingBuffer If true, the canvas is NOT auto-cleared by WebGL (spec discourages true). Useful if you want to use p.autoClear = false.
|
||||
* @param {Boolean} antialias Specifies whether or not the browser's WebGL implementation should try to perform antialiasing.
|
||||
**/
|
||||
var SpriteStage = function(canvas, preserveDrawingBuffer, antialias) {
|
||||
this.initialize(canvas, preserveDrawingBuffer, antialias);
|
||||
};
|
||||
var p = SpriteStage.prototype = new createjs.Stage();
|
||||
function SpriteStage(canvas, preserveDrawingBuffer, antialias) {
|
||||
this.Stage_constructor(canvas);
|
||||
|
||||
// static properties:
|
||||
|
||||
// private properties:
|
||||
/**
|
||||
* Specifies whether or not the canvas is auto-cleared by WebGL. Spec discourages true.
|
||||
* If true, the canvas is NOT auto-cleared by WebGL. Value is ignored if `_alphaEnabled` is false.
|
||||
* Useful if you want to use `autoClear = false`.
|
||||
* @property _preserveDrawingBuffer
|
||||
* @protected
|
||||
* @type {Boolean}
|
||||
* @default false
|
||||
**/
|
||||
this._preserveDrawingBuffer = preserveDrawingBuffer||false;
|
||||
|
||||
/**
|
||||
* Specifies whether or not the browser's WebGL implementation should try to perform antialiasing.
|
||||
* @property _antialias
|
||||
* @protected
|
||||
* @type {Boolean}
|
||||
* @default false
|
||||
**/
|
||||
this._antialias = antialias||false;
|
||||
|
||||
/**
|
||||
* The width of the canvas element.
|
||||
* @property _viewportWidth
|
||||
* @protected
|
||||
* @type {Number}
|
||||
* @default 0
|
||||
**/
|
||||
this._viewportWidth = 0;
|
||||
|
||||
/**
|
||||
* The height of the canvas element.
|
||||
* @property _viewportHeight
|
||||
* @protected
|
||||
* @type {Number}
|
||||
* @default 0
|
||||
**/
|
||||
this._viewportHeight = 0;
|
||||
|
||||
/**
|
||||
* A 2D projection matrix used to convert WebGL's clipspace into normal pixels.
|
||||
* @property _projectionMatrix
|
||||
* @protected
|
||||
* @type {Float32Array}
|
||||
* @default null
|
||||
**/
|
||||
this._projectionMatrix = null;
|
||||
|
||||
/**
|
||||
* The current WebGL canvas context.
|
||||
* @property _webGLContext
|
||||
* @protected
|
||||
* @type {WebGLRenderingContext}
|
||||
* @default null
|
||||
**/
|
||||
this._webGLContext = null;
|
||||
|
||||
/**
|
||||
* Indicates whether or not an error has been detected when dealing with WebGL.
|
||||
* If the is true, the behavior should be to use Canvas 2D rendering instead.
|
||||
* @property _webGLErrorDetected
|
||||
* @protected
|
||||
* @type {Boolean}
|
||||
* @default false
|
||||
**/
|
||||
this._webGLErrorDetected = false;
|
||||
|
||||
/**
|
||||
* The color to use when the WebGL canvas has been cleared.
|
||||
* @property _clearColor
|
||||
* @protected
|
||||
* @type {Object}
|
||||
* @default null
|
||||
**/
|
||||
this._clearColor = null;
|
||||
|
||||
/**
|
||||
* The maximum number of textures WebGL can work with per draw call.
|
||||
* @property _maxTexturesPerDraw
|
||||
* @protected
|
||||
* @type {Number}
|
||||
* @default 1
|
||||
**/
|
||||
this._maxTexturesPerDraw = 1; // TODO: this is currently unused.
|
||||
|
||||
/**
|
||||
* The maximum total number of boxes points that can be defined per draw call.
|
||||
* @property _maxBoxesPointsPerDraw
|
||||
* @protected
|
||||
* @type {Number}
|
||||
* @default null
|
||||
**/
|
||||
this._maxBoxesPointsPerDraw = null;
|
||||
|
||||
/**
|
||||
* The maximum number of boxes (sprites) that can be drawn in one draw call.
|
||||
* @property _maxBoxesPerDraw
|
||||
* @protected
|
||||
* @type {Number}
|
||||
* @default null
|
||||
**/
|
||||
this._maxBoxesPerDraw = null;
|
||||
|
||||
/**
|
||||
* The maximum number of indices that can be drawn in one draw call.
|
||||
* @property _maxIndicesPerDraw
|
||||
* @protected
|
||||
* @type {Number}
|
||||
* @default null
|
||||
**/
|
||||
this._maxIndicesPerDraw = null;
|
||||
|
||||
/**
|
||||
* The shader program used to draw everything.
|
||||
* @property _shaderProgram
|
||||
* @protected
|
||||
* @type {WebGLProgram}
|
||||
* @default null
|
||||
**/
|
||||
this._shaderProgram = null;
|
||||
|
||||
/**
|
||||
* The vertices data for the current draw call.
|
||||
* @property _vertices
|
||||
* @protected
|
||||
* @type {Float32Array}
|
||||
* @default null
|
||||
**/
|
||||
this._vertices = null;
|
||||
|
||||
/**
|
||||
* The buffer that contains all the vertices data.
|
||||
* @property _verticesBuffer
|
||||
* @protected
|
||||
* @type {WebGLBuffer}
|
||||
* @default null
|
||||
**/
|
||||
this._verticesBuffer = null;
|
||||
|
||||
/**
|
||||
* The indices to the vertices defined in this._vertices.
|
||||
* @property _indices
|
||||
* @protected
|
||||
* @type {Uint16Array}
|
||||
* @default null
|
||||
**/
|
||||
this._indices = null;
|
||||
|
||||
/**
|
||||
* The buffer that contains all the indices data.
|
||||
* @property _indicesBuffer
|
||||
* @protected
|
||||
* @type {WebGLBuffer}
|
||||
* @default null
|
||||
**/
|
||||
this._indicesBuffer = null;
|
||||
|
||||
/**
|
||||
* The current box index being defined for drawing.
|
||||
* @property _currentBoxIndex
|
||||
* @protected
|
||||
* @type {Number}
|
||||
* @default -1
|
||||
**/
|
||||
this._currentBoxIndex = -1;
|
||||
|
||||
/**
|
||||
* The current texture that will be used to draw into the GPU.
|
||||
* @property _drawTexture
|
||||
* @protected
|
||||
* @type {WebGLTexture}
|
||||
* @default null
|
||||
**/
|
||||
this._drawTexture = null;
|
||||
|
||||
|
||||
// setup:
|
||||
this._initializeWebGL();
|
||||
}
|
||||
var p = createjs.extend(SpriteStage, createjs.Stage);
|
||||
|
||||
|
||||
// constants:
|
||||
/**
|
||||
* The number of properties defined per vertex in p._verticesBuffer.
|
||||
* x, y, textureU, textureV, alpha
|
||||
|
@ -150,6 +333,7 @@ var p = SpriteStage.prototype = new createjs.Stage();
|
|||
**/
|
||||
SpriteStage.MAX_BOXES_POINTS_INCREMENT = SpriteStage.MAX_INDEX_SIZE / 4;
|
||||
|
||||
|
||||
// getter / setters:
|
||||
/**
|
||||
* Indicates whether WebGL is being used for rendering. For example, this would be false if WebGL is not
|
||||
|
@ -168,209 +352,8 @@ var p = SpriteStage.prototype = new createjs.Stage();
|
|||
});
|
||||
} catch (e) {} // TODO: use Log
|
||||
|
||||
// private properties:
|
||||
|
||||
/**
|
||||
* Specifies whether or not the canvas is auto-cleared by WebGL. Spec discourages true.
|
||||
* If true, the canvas is NOT auto-cleared by WebGL. Value is ignored if p._alphaEnabled is false.
|
||||
* Useful if you want to use p.autoClear = false.
|
||||
* @property _preserveDrawingBuffer
|
||||
* @protected
|
||||
* @type {Boolean}
|
||||
* @default false
|
||||
**/
|
||||
p._preserveDrawingBuffer = false;
|
||||
|
||||
/**
|
||||
* Specifies whether or not the browser's WebGL implementation should try to perform antialiasing.
|
||||
* @property _antialias
|
||||
* @protected
|
||||
* @type {Boolean}
|
||||
* @default false
|
||||
**/
|
||||
p._antialias = false;
|
||||
|
||||
/**
|
||||
* The width of the canvas element.
|
||||
* @property _viewportWidth
|
||||
* @protected
|
||||
* @type {Number}
|
||||
* @default 0
|
||||
**/
|
||||
p._viewportWidth = 0;
|
||||
|
||||
/**
|
||||
* The height of the canvas element.
|
||||
* @property _viewportHeight
|
||||
* @protected
|
||||
* @type {Number}
|
||||
* @default 0
|
||||
**/
|
||||
p._viewportHeight = 0;
|
||||
|
||||
/**
|
||||
* A 2D projection matrix used to convert WebGL's clipspace into normal pixels.
|
||||
* @property _projectionMatrix
|
||||
* @protected
|
||||
* @type {Float32Array}
|
||||
* @default null
|
||||
**/
|
||||
p._projectionMatrix = null;
|
||||
|
||||
/**
|
||||
* The current WebGL canvas context.
|
||||
* @property _webGLContext
|
||||
* @protected
|
||||
* @type {WebGLRenderingContext}
|
||||
* @default null
|
||||
**/
|
||||
p._webGLContext = null;
|
||||
|
||||
/**
|
||||
* Indicates whether or not an error has been detected when dealing with WebGL.
|
||||
* If the is true, the behavior should be to use Canvas 2D rendering instead.
|
||||
* @property _webGLErrorDetected
|
||||
* @protected
|
||||
* @type {Boolean}
|
||||
* @default false
|
||||
**/
|
||||
p._webGLErrorDetected = false;
|
||||
|
||||
/**
|
||||
* The color to use when the WebGL canvas has been cleared.
|
||||
* @property _clearColor
|
||||
* @protected
|
||||
* @type {Object}
|
||||
* @default null
|
||||
**/
|
||||
p._clearColor = null;
|
||||
|
||||
/**
|
||||
* The maximum number of textures WebGL can work with per draw call.
|
||||
* @property _maxTexturesPerDraw
|
||||
* @protected
|
||||
* @type {Number}
|
||||
* @default 1
|
||||
**/
|
||||
p._maxTexturesPerDraw = 1;
|
||||
|
||||
/**
|
||||
* The maximum total number of boxes points that can be defined per draw call.
|
||||
* @property _maxBoxesPointsPerDraw
|
||||
* @protected
|
||||
* @type {Number}
|
||||
* @default null
|
||||
**/
|
||||
p._maxBoxesPointsPerDraw = null;
|
||||
|
||||
/**
|
||||
* The maximum number of boxes (sprites) that can be drawn in one draw call.
|
||||
* @property _maxBoxesPerDraw
|
||||
* @protected
|
||||
* @type {Number}
|
||||
* @default null
|
||||
**/
|
||||
p._maxBoxesPerDraw = null;
|
||||
|
||||
/**
|
||||
* The maximum number of indices that can be drawn in one draw call.
|
||||
* @property _maxIndicesPerDraw
|
||||
* @protected
|
||||
* @type {Number}
|
||||
* @default null
|
||||
**/
|
||||
p._maxIndicesPerDraw = null;
|
||||
|
||||
/**
|
||||
* The shader program used to draw everything.
|
||||
* @property _shaderProgram
|
||||
* @protected
|
||||
* @type {WebGLProgram}
|
||||
* @default null
|
||||
**/
|
||||
p._shaderProgram = null;
|
||||
|
||||
/**
|
||||
* The vertices data for the current draw call.
|
||||
* @property _vertices
|
||||
* @protected
|
||||
* @type {Float32Array}
|
||||
* @default null
|
||||
**/
|
||||
p._vertices = null;
|
||||
|
||||
/**
|
||||
* The buffer that contains all the vertices data.
|
||||
* @property _verticesBuffer
|
||||
* @protected
|
||||
* @type {WebGLBuffer}
|
||||
* @default null
|
||||
**/
|
||||
p._verticesBuffer = null;
|
||||
|
||||
/**
|
||||
* The indices to the vertices defined in p._vertices.
|
||||
* @property _indices
|
||||
* @protected
|
||||
* @type {Uint16Array}
|
||||
* @default null
|
||||
**/
|
||||
p._indices = null;
|
||||
|
||||
/**
|
||||
* The buffer that contains all the indices data.
|
||||
* @property _indicesBuffer
|
||||
* @protected
|
||||
* @type {WebGLBuffer}
|
||||
* @default null
|
||||
**/
|
||||
p._indicesBuffer = null;
|
||||
|
||||
/**
|
||||
* The current box index being defined for drawing.
|
||||
* @property _currentBoxIndex
|
||||
* @protected
|
||||
* @type {Number}
|
||||
* @default -1
|
||||
**/
|
||||
p._currentBoxIndex = -1;
|
||||
|
||||
/**
|
||||
* The current texture that will be used to draw into the GPU.
|
||||
* @property _drawTexture
|
||||
* @protected
|
||||
* @type {WebGLTexture}
|
||||
* @default null
|
||||
**/
|
||||
p._drawTexture = null;
|
||||
|
||||
// constructor:
|
||||
|
||||
/**
|
||||
* @property Stage_initialize
|
||||
* @type Function
|
||||
* @private
|
||||
**/
|
||||
p.Stage_initialize = p.initialize;
|
||||
|
||||
/**
|
||||
* Initialization method.
|
||||
* @method initialize
|
||||
* @param {HTMLCanvasElement | String | Object} canvas A canvas object, or the string id of a canvas object in the current document.
|
||||
* @param {Boolean} preserveDrawingBuffer If true, the canvas is NOT auto-cleared by WebGL (spec discourages true). Useful if you want to use p.autoClear = false.
|
||||
* @param {Boolean} antialias Specifies whether or not the browser's WebGL implementation should try to perform antialiasing.
|
||||
* @protected
|
||||
**/
|
||||
p.initialize = function(canvas, preserveDrawingBuffer, antialias) {
|
||||
this._preserveDrawingBuffer = preserveDrawingBuffer !== undefined ? preserveDrawingBuffer : this._preserveDrawingBuffer;
|
||||
this._antialias = antialias !== undefined ? antialias : this._antialias;
|
||||
|
||||
this.Stage_initialize(canvas);
|
||||
this._initializeWebGL();
|
||||
};
|
||||
|
||||
// public methods:
|
||||
|
||||
/**
|
||||
* Adds a child to the top of the display list.
|
||||
* Only children of type SpriteContainer, Sprite, Bitmap, BitmapText, or DOMElement are allowed.
|
||||
|
@ -403,6 +386,7 @@ var p = SpriteStage.prototype = new createjs.Stage();
|
|||
* Children also MUST have either an image or spriteSheet defined on them (unless it's a DOMElement).
|
||||
*
|
||||
* <h4>Example</h4>
|
||||
*
|
||||
* addChildAt(child1, index);
|
||||
*
|
||||
* You can also add multiple children, such as:
|
||||
|
@ -432,7 +416,6 @@ var p = SpriteStage.prototype = new createjs.Stage();
|
|||
if (child._spritestage_compatibility >= 1) {
|
||||
// The child is compatible with SpriteStage.
|
||||
} else {
|
||||
console.trace();
|
||||
console && console.log("Error: You can only add children of type SpriteContainer, Sprite, Bitmap, BitmapText, or DOMElement. [" + child.toString() + "]");
|
||||
return child;
|
||||
}
|
||||
|
@ -447,32 +430,10 @@ var p = SpriteStage.prototype = new createjs.Stage();
|
|||
return child;
|
||||
};
|
||||
|
||||
/**
|
||||
* Each time the update method is called, the stage will tick all descendants (see: {{#crossLink "DisplayObject/tick"}}{{/crossLink}})
|
||||
* and then render the display list to the canvas using WebGL. If WebGL is not supported in the browser, it will default to a 2D context.
|
||||
*
|
||||
* Any parameters passed to `update()` will be passed on to any
|
||||
* {{#crossLink "DisplayObject/tick:event"}}{{/crossLink}} event handlers.
|
||||
*
|
||||
* Some time-based features in EaselJS (for example {{#crossLink "Sprite/framerate"}}{{/crossLink}} require that
|
||||
* a tick event object (or equivalent) be passed as the first parameter to update(). For example:
|
||||
*
|
||||
* Ticker.addEventListener("tick", handleTick);
|
||||
* function handleTick(evtObj) {
|
||||
* // do some work here, then update the stage, passing through the event object:
|
||||
* myStage.update(evtObj);
|
||||
* }
|
||||
*
|
||||
* @method update
|
||||
* @param {*} [params]* Params to include when ticking descendants. The first param should usually be a tick event.
|
||||
**/
|
||||
p.update = function(params) {
|
||||
/** docced in super class **/
|
||||
p.update = function(props) {
|
||||
if (!this.canvas) { return; }
|
||||
if (this.tickOnUpdate) {
|
||||
this.dispatchEvent("tickstart"); // TODO: make cancellable?
|
||||
this._tick((arguments.length ? arguments : null));
|
||||
this.dispatchEvent("tickend");
|
||||
}
|
||||
if (this.tickOnUpdate) { this.tick(props); }
|
||||
this.dispatchEvent("drawstart"); // TODO: make cancellable?
|
||||
if (this.autoClear) { this.clear(); }
|
||||
var ctx = this._setWebGLContext();
|
||||
|
@ -508,13 +469,6 @@ var p = SpriteStage.prototype = new createjs.Stage();
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @property Stage_draw
|
||||
* @type {Function}
|
||||
* @private
|
||||
**/
|
||||
p.Stage_draw = p.draw;
|
||||
|
||||
/**
|
||||
* Draws the stage into the specified context (using WebGL) ignoring its visible, alpha, shadow, and transform.
|
||||
* If WebGL is not supported in the browser, it will default to a 2D context.
|
||||
|
@ -881,11 +835,10 @@ var p = SpriteStage.prototype = new createjs.Stage();
|
|||
for (var i = 0, l = kids.length; i < l; i++) {
|
||||
kid = kids[i];
|
||||
if (!kid.isVisible()) { continue; }
|
||||
mtx = kid._matrix;
|
||||
mtx = kid._props.matrix;
|
||||
|
||||
// Get the kid's global matrix (relative to the stage):
|
||||
mtx = (parentMVMatrix ? mtx.copy(parentMVMatrix) : mtx.identity())
|
||||
.appendTransform(kid.x, kid.y, kid.scaleX, kid.scaleY, kid.rotation, kid.skewX, kid.skewY, kid.regX, kid.regY);
|
||||
mtx = (parentMVMatrix ? mtx.copy(parentMVMatrix) : mtx.identity()).prependTransform(kid.x, kid.y, kid.scaleX, kid.scaleY, kid.rotation, kid.skewX, kid.skewY, kid.regX, kid.regY);
|
||||
|
||||
// Set default texture coordinates:
|
||||
var uStart = 0, uEnd = 1,
|
||||
|
@ -1027,5 +980,6 @@ var p = SpriteStage.prototype = new createjs.Stage();
|
|||
this._drawTexture = null;
|
||||
};
|
||||
|
||||
createjs.SpriteStage = SpriteStage;
|
||||
|
||||
createjs.SpriteStage = createjs.promote(SpriteStage, "Stage");
|
||||
}());
|
||||
|
|
7707
vendor/scripts/easeljs-NEXT.combined.js
vendored
7707
vendor/scripts/easeljs-NEXT.combined.js
vendored
File diff suppressed because it is too large
Load diff
149
vendor/scripts/preloadjs-NEXT.combined.js
vendored
149
vendor/scripts/preloadjs-NEXT.combined.js
vendored
|
@ -27,7 +27,7 @@ this.createjs = this.createjs||{};
|
|||
* @type String
|
||||
* @static
|
||||
**/
|
||||
s.buildDate = /*date*/"Thu, 06 Mar 2014 22:58:10 GMT"; // injected by build process
|
||||
s.buildDate = /*date*/"Wed, 22 Oct 2014 16:11:35 GMT"; // injected by build process
|
||||
|
||||
})();
|
||||
/*
|
||||
|
@ -91,6 +91,7 @@ var Event = function(type, bubbles, cancelable) {
|
|||
this.initialize(type, bubbles, cancelable);
|
||||
};
|
||||
var p = Event.prototype;
|
||||
Event.prototype.constructor = Event;
|
||||
|
||||
// events:
|
||||
|
||||
|
@ -375,6 +376,7 @@ var EventDispatcher = function() {
|
|||
/* this.initialize(); */ // not needed.
|
||||
};
|
||||
var p = EventDispatcher.prototype;
|
||||
EventDispatcher.prototype.constructor = EventDispatcher;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -579,19 +581,19 @@ var p = EventDispatcher.prototype;
|
|||
* @param {Object | String | Event} eventObj An object with a "type" property, or a string type.
|
||||
* While a generic object will work, it is recommended to use a CreateJS Event instance. If a string is used,
|
||||
* dispatchEvent will construct an Event instance with the specified type.
|
||||
* @param {Object} [target] The object to use as the target property of the event object. This will default to the
|
||||
* dispatching object. <b>This parameter is deprecated and will be removed.</b>
|
||||
* @return {Boolean} Returns the value of eventObj.defaultPrevented.
|
||||
**/
|
||||
p.dispatchEvent = function(eventObj, target) {
|
||||
p.dispatchEvent = function(eventObj) {
|
||||
if (typeof eventObj == "string") {
|
||||
// won't bubble, so skip everything if there's no listeners:
|
||||
var listeners = this._listeners;
|
||||
if (!listeners || !listeners[eventObj]) { return false; }
|
||||
eventObj = new createjs.Event(eventObj);
|
||||
} else if (eventObj.target && eventObj.clone) {
|
||||
// redispatching an active event object, so clone it:
|
||||
eventObj = eventObj.clone();
|
||||
}
|
||||
// TODO: deprecated. Target param is deprecated, only use case is MouseEvent/mousemove, remove.
|
||||
eventObj.target = target||this;
|
||||
try { eventObj.target = this; } catch (e) {} // try/catch allows redispatching of native events
|
||||
|
||||
if (!eventObj.bubbles || !this.parent) {
|
||||
this._dispatchEvent(eventObj, 2);
|
||||
|
@ -663,8 +665,8 @@ var p = EventDispatcher.prototype;
|
|||
if (eventObj && listeners) {
|
||||
var arr = listeners[eventObj.type];
|
||||
if (!arr||!(l=arr.length)) { return; }
|
||||
eventObj.currentTarget = this;
|
||||
eventObj.eventPhase = eventPhase;
|
||||
try { eventObj.currentTarget = this; } catch (e) {}
|
||||
try { eventObj.eventPhase = eventPhase; } catch (e) {}
|
||||
eventObj.removed = false;
|
||||
arr = arr.slice(); // to avoid issues with items being removed or added during the dispatch
|
||||
for (var i=0; i<l && !eventObj.immediatePropagationStopped; i++) {
|
||||
|
@ -904,29 +906,37 @@ this.createjs = this.createjs||{};
|
|||
this.init();
|
||||
};
|
||||
|
||||
AbstractLoader.prototype = new createjs.EventDispatcher(); //TODO: TEST!
|
||||
var p = AbstractLoader.prototype;
|
||||
var p = AbstractLoader.prototype = new createjs.EventDispatcher();
|
||||
AbstractLoader.prototype.constructor = AbstractLoader;
|
||||
var s = AbstractLoader;
|
||||
|
||||
/**
|
||||
* The RegExp pattern to use to parse file URIs. This supports simple file names, as well as full domain URIs with
|
||||
* query strings. The resulting match is: protocol:$1 domain:$2 relativePath:$3 path:$4 file:$5 extension:$6 query:$7.
|
||||
* @property FILE_PATTERN
|
||||
* @type {RegExp}
|
||||
* The Regular Expression used to test file URLS for an absolute path.
|
||||
* @property ABSOLUTE_PATH
|
||||
* @static
|
||||
* @protected
|
||||
* @type {RegExp}
|
||||
* @since 0.4.2
|
||||
*/
|
||||
s.FILE_PATTERN = /^(?:(\w+:)\/{2}(\w+(?:\.\w+)*\/?)|(.{0,2}\/{1}))?([/.]*?(?:[^?]+)?\/)?((?:[^/?]+)\.(\w+))(?:\?(\S+)?)?$/;
|
||||
s.ABSOLUTE_PATT = /^(?:\w+:)?\/{2}/i;
|
||||
|
||||
/**
|
||||
* The RegExp pattern to use to parse path URIs. This supports protocols, relative files, and paths. The resulting
|
||||
* match is: protocol:$1 relativePath:$2 path$3.
|
||||
* @property PATH_PATTERN
|
||||
* @type {RegExp}
|
||||
* The Regular Expression used to test file URLS for an absolute path.
|
||||
* @property RELATIVE_PATH
|
||||
* @static
|
||||
* @protected
|
||||
* @type {RegExp}
|
||||
* @since 0.4.2
|
||||
*/
|
||||
s.PATH_PATTERN = /^(?:(\w+:)\/{2})|(.{0,2}\/{1})?([/.]*?(?:[^?]+)?\/?)?$/;
|
||||
s.RELATIVE_PATT = (/^[./]*?\//i);
|
||||
|
||||
/**
|
||||
* The Regular Expression used to test file URLS for an extension. Note that URIs must already have the query string
|
||||
* removed.
|
||||
* @property EXTENSION_PATT
|
||||
* @static
|
||||
* @type {RegExp}
|
||||
* @since 0.4.2
|
||||
*/
|
||||
s.EXTENSION_PATT = /\/?[^/]+\.(\w{1,5})$/i;
|
||||
|
||||
/**
|
||||
* If the loader has completed loading. This provides a quick check, but also ensures that the different approaches
|
||||
|
@ -1164,29 +1174,49 @@ this.createjs = this.createjs||{};
|
|||
};
|
||||
|
||||
/**
|
||||
* Parse a file URI using the {{#crossLink "AbstractLoader/FILE_PATTERN:property"}}{{/crossLink}} RegExp pattern.
|
||||
* @method _parseURI
|
||||
* @param {String} path The file path to parse.
|
||||
* @return {Array} The matched file contents. Please see the FILE_PATTERN property for details on the return value.
|
||||
* This will return null if it does not match.
|
||||
* @protected
|
||||
* Parse a file path to determine the information we need to work with it. Currently, PreloadJS needs to know:
|
||||
* <ul>
|
||||
* <li>If the path is absolute. Absolute paths start with a protocol (such as `http://`, `file://`, or
|
||||
* `//networkPath`)</li>
|
||||
* <li>If the path is relative. Relative paths start with `../` or `/path` (or similar)</li>
|
||||
* <li>The file extension. This is determined by the filename with an extension. Query strings are dropped, and
|
||||
* the file path is expected to follow the format `name.ext`.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <strong>Note:</strong> This has changed from earlier versions, which used a single, complicated Regular Expression, which
|
||||
* was difficult to maintain, and over-aggressive in determining all file properties. It has been simplified to
|
||||
* only pull out what it needs.
|
||||
* @param path
|
||||
* @returns {Object} An Object with an `absolute` and `relative` Boolean, as well as an optional 'extension` String
|
||||
* property, which is the lowercase extension.
|
||||
* @private
|
||||
*/
|
||||
p._parseURI = function(path) {
|
||||
if (!path) { return null; }
|
||||
return path.match(s.FILE_PATTERN);
|
||||
};
|
||||
var info = { absolute: false, relative:false };
|
||||
if (path == null) { return info; };
|
||||
|
||||
/**
|
||||
* Parse a file URI using the {{#crossLink "AbstractLoader/PATH_PATTERN"}}{{/crossLink}} RegExp pattern.
|
||||
* @method _parsePath
|
||||
* @param {String} path The file path to parse.
|
||||
* @return {Array} The matched path contents. Please see the PATH_PATTERN property for details on the return value.
|
||||
* This will return null if it does not match.
|
||||
* @protected
|
||||
*/
|
||||
p._parsePath = function(path) {
|
||||
if (!path) { return null; }
|
||||
return path.match(s.PATH_PATTERN);
|
||||
// Drop the query string
|
||||
var queryIndex = path.indexOf("?");
|
||||
if (queryIndex > -1) {
|
||||
path = path.substr(0,queryIndex);
|
||||
}
|
||||
|
||||
// Absolute
|
||||
var match;
|
||||
if (s.ABSOLUTE_PATT.test(path)) {
|
||||
info.absolute = true;
|
||||
|
||||
// Relative
|
||||
} else if (s.RELATIVE_PATT.test(path)) {
|
||||
info.relative = true;
|
||||
}
|
||||
|
||||
// Extension
|
||||
if (match = path.match(s.EXTENSION_PATT)) {
|
||||
info.extension = match[1].toLowerCase();
|
||||
}
|
||||
return info;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1541,6 +1571,7 @@ TODO: WINDOWS ISSUES
|
|||
};
|
||||
|
||||
var p = LoadQueue.prototype = new createjs.AbstractLoader();
|
||||
LoadQueue.prototype.constructor = LoadQueue;
|
||||
var s = LoadQueue;
|
||||
|
||||
/**
|
||||
|
@ -2078,7 +2109,7 @@ TODO: WINDOWS ISSUES
|
|||
for (var n in this._loadItemsById) {
|
||||
this._disposeItem(this._loadItemsById[n]);
|
||||
}
|
||||
this.init(this.useXHR);
|
||||
this.init(this.useXHR, this._basePath, this._crossOrigin);
|
||||
|
||||
// Remove specific items
|
||||
} else {
|
||||
|
@ -2593,7 +2624,7 @@ TODO: WINDOWS ISSUES
|
|||
|
||||
// Determine Extension, etc.
|
||||
var match = this._parseURI(item.src);
|
||||
if (match != null) { item.ext = match[6]; }
|
||||
if (match.extension) { item.ext = match.extension; }
|
||||
if (item.type == null) {
|
||||
item.type = this._getTypeByExtension(item.ext);
|
||||
}
|
||||
|
@ -2602,13 +2633,13 @@ TODO: WINDOWS ISSUES
|
|||
var bp = ""; // Store the generated basePath
|
||||
var useBasePath = basePath || this._basePath;
|
||||
var autoId = item.src;
|
||||
if (match && match[1] == null && match[3] == null) {
|
||||
if (!match.absolute && !match.relative) {
|
||||
if (path) {
|
||||
bp = path;
|
||||
var pathMatch = this._parsePath(path);
|
||||
var pathMatch = this._parseURI(path);
|
||||
autoId = path + autoId;
|
||||
// Also append basePath
|
||||
if (useBasePath != null && pathMatch && pathMatch[1] == null && pathMatch[2] == null) {
|
||||
if (useBasePath != null && !pathMatch.absolute && !pathMatch.relative) {
|
||||
bp = useBasePath + bp;
|
||||
}
|
||||
} else if (useBasePath != null) {
|
||||
|
@ -2666,8 +2697,8 @@ TODO: WINDOWS ISSUES
|
|||
|
||||
// Update the extension in case the type changed:
|
||||
match = this._parseURI(item.src);
|
||||
if (match != null && match[6] != null) {
|
||||
item.ext = match[6].toLowerCase();
|
||||
if (match.extension != null) {
|
||||
item.ext = match.extension;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3252,6 +3283,7 @@ this.createjs = this.createjs||{};
|
|||
};
|
||||
|
||||
var p = TagLoader.prototype = new createjs.AbstractLoader();
|
||||
TagLoader.prototype.constructor = TagLoader;
|
||||
|
||||
// Protected
|
||||
|
||||
|
@ -3396,7 +3428,16 @@ this.createjs = this.createjs||{};
|
|||
item.type == createjs.LoadQueue.CSS) {
|
||||
this._startTagVisibility = tag.style.visibility;
|
||||
tag.style.visibility = "hidden";
|
||||
(document.body || document.getElementsByTagName("body")[0]).appendChild(tag);
|
||||
var node = document.body || document.getElementsByTagName("body")[0];
|
||||
if (node == null) {
|
||||
if (item.type == createjs.LoadQueue.SVG) {
|
||||
this._handleSVGError();
|
||||
return;
|
||||
} else {
|
||||
node = document.head || document.getElementsByTagName("head");
|
||||
}
|
||||
}
|
||||
node.appendChild(tag);
|
||||
}
|
||||
|
||||
// Note: Previous versions didn't seem to work when we called load() for OGG tags in Firefox. Seems fixed in 15.0.1
|
||||
|
@ -3405,6 +3446,13 @@ this.createjs = this.createjs||{};
|
|||
}
|
||||
};
|
||||
|
||||
p._handleSVGError = function() {
|
||||
this._clean();
|
||||
var event = new createjs.Event("error");
|
||||
event.text = "SVG_NO_BODY";
|
||||
this._sendError(event);
|
||||
};
|
||||
|
||||
p._handleJSONPLoad = function(data) {
|
||||
this._jsonResult = data;
|
||||
};
|
||||
|
@ -3488,7 +3536,7 @@ this.createjs = this.createjs||{};
|
|||
// case createjs.LoadQueue.CSS:
|
||||
//LM: We may need to remove CSS tags loaded using a LINK
|
||||
tag.style.visibility = this._startTagVisibility;
|
||||
(document.body || document.getElementsByTagName("body")[0]).removeChild(tag);
|
||||
tag.parentNode && tag.parentNode.contains(tag) && tag.parentNode.removeChild(tag);
|
||||
break;
|
||||
default:
|
||||
}
|
||||
|
@ -3618,6 +3666,7 @@ this.createjs = this.createjs || {};
|
|||
];
|
||||
|
||||
var p = XHRLoader.prototype = new createjs.AbstractLoader();
|
||||
XHRLoader.prototype.constructor = XHRLoader;
|
||||
|
||||
//Protected
|
||||
/**
|
||||
|
|
1408
vendor/scripts/soundjs-NEXT.combined.js
vendored
1408
vendor/scripts/soundjs-NEXT.combined.js
vendored
File diff suppressed because it is too large
Load diff
673
vendor/scripts/tweenjs-NEXT.combined.js
vendored
673
vendor/scripts/tweenjs-NEXT.combined.js
vendored
|
@ -59,6 +59,7 @@ var Event = function(type, bubbles, cancelable) {
|
|||
this.initialize(type, bubbles, cancelable);
|
||||
};
|
||||
var p = Event.prototype;
|
||||
Event.prototype.constructor = Event;
|
||||
|
||||
// events:
|
||||
|
||||
|
@ -343,6 +344,7 @@ var EventDispatcher = function() {
|
|||
/* this.initialize(); */ // not needed.
|
||||
};
|
||||
var p = EventDispatcher.prototype;
|
||||
EventDispatcher.prototype.constructor = EventDispatcher;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -547,19 +549,19 @@ var p = EventDispatcher.prototype;
|
|||
* @param {Object | String | Event} eventObj An object with a "type" property, or a string type.
|
||||
* While a generic object will work, it is recommended to use a CreateJS Event instance. If a string is used,
|
||||
* dispatchEvent will construct an Event instance with the specified type.
|
||||
* @param {Object} [target] The object to use as the target property of the event object. This will default to the
|
||||
* dispatching object. <b>This parameter is deprecated and will be removed.</b>
|
||||
* @return {Boolean} Returns the value of eventObj.defaultPrevented.
|
||||
**/
|
||||
p.dispatchEvent = function(eventObj, target) {
|
||||
p.dispatchEvent = function(eventObj) {
|
||||
if (typeof eventObj == "string") {
|
||||
// won't bubble, so skip everything if there's no listeners:
|
||||
var listeners = this._listeners;
|
||||
if (!listeners || !listeners[eventObj]) { return false; }
|
||||
eventObj = new createjs.Event(eventObj);
|
||||
} else if (eventObj.target && eventObj.clone) {
|
||||
// redispatching an active event object, so clone it:
|
||||
eventObj = eventObj.clone();
|
||||
}
|
||||
// TODO: deprecated. Target param is deprecated, only use case is MouseEvent/mousemove, remove.
|
||||
eventObj.target = target||this;
|
||||
try { eventObj.target = this; } catch (e) {} // try/catch allows redispatching of native events
|
||||
|
||||
if (!eventObj.bubbles || !this.parent) {
|
||||
this._dispatchEvent(eventObj, 2);
|
||||
|
@ -631,8 +633,8 @@ var p = EventDispatcher.prototype;
|
|||
if (eventObj && listeners) {
|
||||
var arr = listeners[eventObj.type];
|
||||
if (!arr||!(l=arr.length)) { return; }
|
||||
eventObj.currentTarget = this;
|
||||
eventObj.eventPhase = eventPhase;
|
||||
try { eventObj.currentTarget = this; } catch (e) {}
|
||||
try { eventObj.eventPhase = eventPhase; } catch (e) {}
|
||||
eventObj.removed = false;
|
||||
arr = arr.slice(); // to avoid issues with items being removed or added during the dispatch
|
||||
for (var i=0; i<l && !eventObj.immediatePropagationStopped; i++) {
|
||||
|
@ -651,6 +653,613 @@ var p = EventDispatcher.prototype;
|
|||
createjs.EventDispatcher = EventDispatcher;
|
||||
}());
|
||||
/*
|
||||
* Ticker
|
||||
* Visit http://createjs.com/ for documentation, updates and examples.
|
||||
*
|
||||
* Copyright (c) 2010 gskinner.com, inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @module CreateJS
|
||||
*/
|
||||
|
||||
// namespace:
|
||||
this.createjs = this.createjs||{};
|
||||
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
|
||||
// constructor:
|
||||
/**
|
||||
* The Ticker provides a centralized tick or heartbeat broadcast at a set interval. Listeners can subscribe to the tick
|
||||
* event to be notified when a set time interval has elapsed.
|
||||
*
|
||||
* Note that the interval that the tick event is called is a target interval, and may be broadcast at a slower interval
|
||||
* during times of high CPU load. The Ticker class uses a static interface (ex. <code>Ticker.getPaused()</code>) and
|
||||
* should not be instantiated.
|
||||
*
|
||||
* <h4>Example</h4>
|
||||
*
|
||||
* createjs.Ticker.addEventListener("tick", handleTick);
|
||||
* function handleTick(event) {
|
||||
* // Actions carried out each frame
|
||||
* if (!event.paused) {
|
||||
* // Actions carried out when the Ticker is not paused.
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* To update a stage every tick, the {{#crossLink "Stage"}}{{/crossLink}} instance can also be used as a listener, as
|
||||
* it will automatically update when it receives a tick event:
|
||||
*
|
||||
* createjs.Ticker.addEventListener("tick", stage);
|
||||
*
|
||||
* @class Ticker
|
||||
* @uses EventDispatcher
|
||||
* @static
|
||||
**/
|
||||
function Ticker() {
|
||||
throw "Ticker cannot be instantiated.";
|
||||
}
|
||||
|
||||
|
||||
// constants:
|
||||
/**
|
||||
* In this mode, Ticker uses the requestAnimationFrame API, but attempts to synch the ticks to target framerate. It
|
||||
* uses a simple heuristic that compares the time of the RAF return to the target time for the current frame and
|
||||
* dispatches the tick when the time is within a certain threshold.
|
||||
*
|
||||
* This mode has a higher variance for time between frames than TIMEOUT, but does not require that content be time
|
||||
* based as with RAF while gaining the benefits of that API (screen synch, background throttling).
|
||||
*
|
||||
* Variance is usually lowest for framerates that are a divisor of the RAF frequency. This is usually 60, so
|
||||
* framerates of 10, 12, 15, 20, and 30 work well.
|
||||
*
|
||||
* Falls back on TIMEOUT if the requestAnimationFrame API is not supported.
|
||||
* @property RAF_SYNCHED
|
||||
* @static
|
||||
* @type {String}
|
||||
* @default "synched"
|
||||
* @readonly
|
||||
**/
|
||||
Ticker.RAF_SYNCHED = "synched";
|
||||
|
||||
/**
|
||||
* In this mode, Ticker passes through the requestAnimationFrame heartbeat, ignoring the target framerate completely.
|
||||
* Because requestAnimationFrame frequency is not deterministic, any content using this mode should be time based.
|
||||
* You can leverage {{#crossLink "Ticker/getTime"}}{{/crossLink}} and the tick event object's "delta" properties
|
||||
* to make this easier.
|
||||
*
|
||||
* Falls back on TIMEOUT if the requestAnimationFrame API is not supported.
|
||||
* @property RAF
|
||||
* @static
|
||||
* @type {String}
|
||||
* @default "raf"
|
||||
* @readonly
|
||||
**/
|
||||
Ticker.RAF = "raf";
|
||||
|
||||
/**
|
||||
* In this mode, Ticker uses the setTimeout API. This provides predictable, adaptive frame timing, but does not
|
||||
* provide the benefits of requestAnimationFrame (screen synch, background throttling).
|
||||
* @property TIMEOUT
|
||||
* @static
|
||||
* @type {String}
|
||||
* @default "timer"
|
||||
* @readonly
|
||||
**/
|
||||
Ticker.TIMEOUT = "timeout";
|
||||
|
||||
|
||||
// static events:
|
||||
/**
|
||||
* Dispatched each tick. The event will be dispatched to each listener even when the Ticker has been paused using
|
||||
* {{#crossLink "Ticker/setPaused"}}{{/crossLink}}.
|
||||
*
|
||||
* <h4>Example</h4>
|
||||
*
|
||||
* createjs.Ticker.addEventListener("tick", handleTick);
|
||||
* function handleTick(event) {
|
||||
* console.log("Paused:", event.paused, event.delta);
|
||||
* }
|
||||
*
|
||||
* @event tick
|
||||
* @param {Object} target The object that dispatched the event.
|
||||
* @param {String} type The event type.
|
||||
* @param {Boolean} paused Indicates whether the ticker is currently paused.
|
||||
* @param {Number} delta The time elapsed in ms since the last tick.
|
||||
* @param {Number} time The total time in ms since Ticker was initialized.
|
||||
* @param {Number} runTime The total time in ms that Ticker was not paused since it was initialized. For example,
|
||||
* you could determine the amount of time that the Ticker has been paused since initialization with time-runTime.
|
||||
* @since 0.6.0
|
||||
*/
|
||||
|
||||
|
||||
// public static properties:
|
||||
/**
|
||||
* Deprecated in favour of {{#crossLink "Ticker/timingMode"}}{{/crossLink}}, and will be removed in a future version. If true, timingMode will
|
||||
* use {{#crossLink "Ticker/RAF_SYNCHED"}}{{/crossLink}} by default.
|
||||
* @deprecated Deprecated in favour of {{#crossLink "Ticker/timingMode"}}{{/crossLink}}.
|
||||
* @property useRAF
|
||||
* @static
|
||||
* @type {Boolean}
|
||||
* @default false
|
||||
**/
|
||||
Ticker.useRAF = false;
|
||||
|
||||
/**
|
||||
* Specifies the timing api (setTimeout or requestAnimationFrame) and mode to use. See
|
||||
* {{#crossLink "Ticker/TIMEOUT"}}{{/crossLink}}, {{#crossLink "Ticker/RAF"}}{{/crossLink}}, and
|
||||
* {{#crossLink "Ticker/RAF_SYNCHED"}}{{/crossLink}} for mode details.
|
||||
* @property timingMode
|
||||
* @static
|
||||
* @type {String}
|
||||
* @default Ticker.TIMEOUT
|
||||
**/
|
||||
Ticker.timingMode = null;
|
||||
|
||||
/**
|
||||
* Specifies a maximum value for the delta property in the tick event object. This is useful when building time
|
||||
* based animations and systems to prevent issues caused by large time gaps caused by background tabs, system sleep,
|
||||
* alert dialogs, or other blocking routines. Double the expected frame duration is often an effective value
|
||||
* (ex. maxDelta=50 when running at 40fps).
|
||||
*
|
||||
* This does not impact any other values (ex. time, runTime, etc), so you may experience issues if you enable maxDelta
|
||||
* when using both delta and other values.
|
||||
*
|
||||
* If 0, there is no maximum.
|
||||
* @property maxDelta
|
||||
* @static
|
||||
* @type {number}
|
||||
* @default 0
|
||||
*/
|
||||
Ticker.maxDelta = 0;
|
||||
|
||||
|
||||
// mix-ins:
|
||||
// EventDispatcher methods:
|
||||
Ticker.removeEventListener = null;
|
||||
Ticker.removeAllEventListeners = null;
|
||||
Ticker.dispatchEvent = null;
|
||||
Ticker.hasEventListener = null;
|
||||
Ticker._listeners = null;
|
||||
createjs.EventDispatcher.initialize(Ticker); // inject EventDispatcher methods.
|
||||
Ticker._addEventListener = Ticker.addEventListener;
|
||||
Ticker.addEventListener = function() {
|
||||
!Ticker._inited&&Ticker.init();
|
||||
return Ticker._addEventListener.apply(Ticker, arguments);
|
||||
};
|
||||
|
||||
|
||||
// private static properties:
|
||||
/**
|
||||
* @property _paused
|
||||
* @type {Boolean}
|
||||
* @protected
|
||||
**/
|
||||
Ticker._paused = false;
|
||||
|
||||
/**
|
||||
* @property _inited
|
||||
* @type {Boolean}
|
||||
* @protected
|
||||
**/
|
||||
Ticker._inited = false;
|
||||
|
||||
/**
|
||||
* @property _startTime
|
||||
* @type {Number}
|
||||
* @protected
|
||||
**/
|
||||
Ticker._startTime = 0;
|
||||
|
||||
/**
|
||||
* @property _pausedTime
|
||||
* @type {Number}
|
||||
* @protected
|
||||
**/
|
||||
Ticker._pausedTime=0;
|
||||
|
||||
/**
|
||||
* The number of ticks that have passed
|
||||
* @property _ticks
|
||||
* @type {Number}
|
||||
* @protected
|
||||
**/
|
||||
Ticker._ticks = 0;
|
||||
|
||||
/**
|
||||
* The number of ticks that have passed while Ticker has been paused
|
||||
* @property _pausedTicks
|
||||
* @type {Number}
|
||||
* @protected
|
||||
**/
|
||||
Ticker._pausedTicks = 0;
|
||||
|
||||
/**
|
||||
* @property _interval
|
||||
* @type {Number}
|
||||
* @protected
|
||||
**/
|
||||
Ticker._interval = 50;
|
||||
|
||||
/**
|
||||
* @property _lastTime
|
||||
* @type {Number}
|
||||
* @protected
|
||||
**/
|
||||
Ticker._lastTime = 0;
|
||||
|
||||
/**
|
||||
* @property _times
|
||||
* @type {Array}
|
||||
* @protected
|
||||
**/
|
||||
Ticker._times = null;
|
||||
|
||||
/**
|
||||
* @property _tickTimes
|
||||
* @type {Array}
|
||||
* @protected
|
||||
**/
|
||||
Ticker._tickTimes = null;
|
||||
|
||||
/**
|
||||
* Stores the timeout or requestAnimationFrame id.
|
||||
* @property _timerId
|
||||
* @type {Number}
|
||||
* @protected
|
||||
**/
|
||||
Ticker._timerId = null;
|
||||
|
||||
/**
|
||||
* True if currently using requestAnimationFrame, false if using setTimeout.
|
||||
* @property _raf
|
||||
* @type {Boolean}
|
||||
* @protected
|
||||
**/
|
||||
Ticker._raf = true;
|
||||
|
||||
|
||||
// public static methods:
|
||||
/**
|
||||
* Starts the tick. This is called automatically when the first listener is added.
|
||||
* @method init
|
||||
* @static
|
||||
**/
|
||||
Ticker.init = function() {
|
||||
if (Ticker._inited) { return; }
|
||||
Ticker._inited = true;
|
||||
Ticker._times = [];
|
||||
Ticker._tickTimes = [];
|
||||
Ticker._startTime = Ticker._getTime();
|
||||
Ticker._times.push(Ticker._lastTime = 0);
|
||||
Ticker.setInterval(Ticker._interval);
|
||||
};
|
||||
|
||||
/**
|
||||
* Stops the Ticker and removes all listeners. Use init() to restart the Ticker.
|
||||
* @method reset
|
||||
* @static
|
||||
**/
|
||||
Ticker.reset = function() {
|
||||
if (Ticker._raf) {
|
||||
var f = window.cancelAnimationFrame || window.webkitCancelAnimationFrame || window.mozCancelAnimationFrame || window.oCancelAnimationFrame || window.msCancelAnimationFrame;
|
||||
f&&f(Ticker._timerId);
|
||||
} else {
|
||||
clearTimeout(Ticker._timerId);
|
||||
}
|
||||
Ticker.removeAllEventListeners("tick");
|
||||
Ticker._timerId = null;
|
||||
Ticker._inited = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the target time (in milliseconds) between ticks. Default is 50 (20 FPS).
|
||||
*
|
||||
* Note actual time between ticks may be more than requested depending on CPU load.
|
||||
* @method setInterval
|
||||
* @static
|
||||
* @param {Number} interval Time in milliseconds between ticks. Default value is 50.
|
||||
**/
|
||||
Ticker.setInterval = function(interval) {
|
||||
Ticker._interval = interval;
|
||||
if (!Ticker._inited) { return; }
|
||||
Ticker._setupTick();
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the current target time between ticks, as set with {{#crossLink "Ticker/setInterval"}}{{/crossLink}}.
|
||||
* @method getInterval
|
||||
* @static
|
||||
* @return {Number} The current target interval in milliseconds between tick events.
|
||||
**/
|
||||
Ticker.getInterval = function() {
|
||||
return Ticker._interval;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the target frame rate in frames per second (FPS). For example, with an interval of 40, <code>getFPS()</code>
|
||||
* will return 25 (1000ms per second divided by 40 ms per tick = 25fps).
|
||||
* @method setFPS
|
||||
* @static
|
||||
* @param {Number} value Target number of ticks broadcast per second.
|
||||
**/
|
||||
Ticker.setFPS = function(value) {
|
||||
Ticker.setInterval(1000/value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the target frame rate in frames per second (FPS). For example, with an interval of 40, <code>getFPS()</code>
|
||||
* will return 25 (1000ms per second divided by 40 ms per tick = 25fps).
|
||||
* @method getFPS
|
||||
* @static
|
||||
* @return {Number} The current target number of frames / ticks broadcast per second.
|
||||
**/
|
||||
Ticker.getFPS = function() {
|
||||
return 1000/Ticker._interval;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the average time spent within a tick. This can vary significantly from the value provided by getMeasuredFPS
|
||||
* because it only measures the time spent within the tick execution stack.
|
||||
*
|
||||
* Example 1: With a target FPS of 20, getMeasuredFPS() returns 20fps, which indicates an average of 50ms between
|
||||
* the end of one tick and the end of the next. However, getMeasuredTickTime() returns 15ms. This indicates that
|
||||
* there may be up to 35ms of "idle" time between the end of one tick and the start of the next.
|
||||
*
|
||||
* Example 2: With a target FPS of 30, getFPS() returns 10fps, which indicates an average of 100ms between the end of
|
||||
* one tick and the end of the next. However, getMeasuredTickTime() returns 20ms. This would indicate that something
|
||||
* other than the tick is using ~80ms (another script, DOM rendering, etc).
|
||||
* @method getMeasuredTickTime
|
||||
* @static
|
||||
* @param {Number} [ticks] The number of previous ticks over which to measure the average time spent in a tick.
|
||||
* Defaults to the number of ticks per second. To get only the last tick's time, pass in 1.
|
||||
* @return {Number} The average time spent in a tick in milliseconds.
|
||||
**/
|
||||
Ticker.getMeasuredTickTime = function(ticks) {
|
||||
var ttl=0, times=Ticker._tickTimes;
|
||||
if (!times || times.length < 1) { return -1; }
|
||||
|
||||
// by default, calculate average for the past ~1 second:
|
||||
ticks = Math.min(times.length, ticks||(Ticker.getFPS()|0));
|
||||
for (var i=0; i<ticks; i++) { ttl += times[i]; }
|
||||
return ttl/ticks;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the actual frames / ticks per second.
|
||||
* @method getMeasuredFPS
|
||||
* @static
|
||||
* @param {Number} [ticks] The number of previous ticks over which to measure the actual frames / ticks per second.
|
||||
* Defaults to the number of ticks per second.
|
||||
* @return {Number} The actual frames / ticks per second. Depending on performance, this may differ
|
||||
* from the target frames per second.
|
||||
**/
|
||||
Ticker.getMeasuredFPS = function(ticks) {
|
||||
var times = Ticker._times;
|
||||
if (!times || times.length < 2) { return -1; }
|
||||
|
||||
// by default, calculate fps for the past ~1 second:
|
||||
ticks = Math.min(times.length-1, ticks||(Ticker.getFPS()|0));
|
||||
return 1000/((times[0]-times[ticks])/ticks);
|
||||
};
|
||||
|
||||
/**
|
||||
* Changes the "paused" state of the Ticker, which can be retrieved by the {{#crossLink "Ticker/getPaused"}}{{/crossLink}}
|
||||
* method, and is passed as the "paused" property of the <code>tick</code> event. When the ticker is paused, all
|
||||
* listeners will still receive a tick event, but the <code>paused</code> property will be false.
|
||||
*
|
||||
* Note that in EaselJS v0.5.0 and earlier, "pauseable" listeners would <strong>not</strong> receive the tick
|
||||
* callback when Ticker was paused. This is no longer the case.
|
||||
*
|
||||
* <h4>Example</h4>
|
||||
*
|
||||
* createjs.Ticker.addEventListener("tick", handleTick);
|
||||
* createjs.Ticker.setPaused(true);
|
||||
* function handleTick(event) {
|
||||
* console.log("Paused:", event.paused, createjs.Ticker.getPaused());
|
||||
* }
|
||||
*
|
||||
* @method setPaused
|
||||
* @static
|
||||
* @param {Boolean} value Indicates whether to pause (true) or unpause (false) Ticker.
|
||||
**/
|
||||
Ticker.setPaused = function(value) {
|
||||
Ticker._paused = value;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a boolean indicating whether Ticker is currently paused, as set with {{#crossLink "Ticker/setPaused"}}{{/crossLink}}.
|
||||
* When the ticker is paused, all listeners will still receive a tick event, but this value will be false.
|
||||
*
|
||||
* Note that in EaselJS v0.5.0 and earlier, "pauseable" listeners would <strong>not</strong> receive the tick
|
||||
* callback when Ticker was paused. This is no longer the case.
|
||||
*
|
||||
* <h4>Example</h4>
|
||||
*
|
||||
* createjs.Ticker.addEventListener("tick", handleTick);
|
||||
* createjs.Ticker.setPaused(true);
|
||||
* function handleTick(event) {
|
||||
* console.log("Paused:", createjs.Ticker.getPaused());
|
||||
* }
|
||||
*
|
||||
* @method getPaused
|
||||
* @static
|
||||
* @return {Boolean} Whether the Ticker is currently paused.
|
||||
**/
|
||||
Ticker.getPaused = function() {
|
||||
return Ticker._paused;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the number of milliseconds that have elapsed since Ticker was initialized via {{#crossLink "Ticker/init"}}.
|
||||
* Returns -1 if Ticker has not been initialized. For example, you could use
|
||||
* this in a time synchronized animation to determine the exact amount of time that has elapsed.
|
||||
* @method getTime
|
||||
* @static
|
||||
* @param {Boolean} [runTime=false] If true only time elapsed while Ticker was not paused will be returned.
|
||||
* If false, the value returned will be total time elapsed since the first tick event listener was added.
|
||||
* @return {Number} Number of milliseconds that have elapsed since Ticker was initialized or -1.
|
||||
**/
|
||||
Ticker.getTime = function(runTime) {
|
||||
return Ticker._startTime ? Ticker._getTime() - Ticker._startTime - (runTime ? Ticker._pausedTime : 0) : -1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Similar to getTime(), but returns the time included with the current (or most recent) tick event object.
|
||||
* @method getEventTime
|
||||
* @param runTime {Boolean} [runTime=false] If true, the runTime property will be returned instead of time.
|
||||
* @returns {number} The time or runTime property from the most recent tick event or -1.
|
||||
*/
|
||||
Ticker.getEventTime = function(runTime) {
|
||||
return Ticker._startTime ? (Ticker._lastTime || Ticker._startTime) - (runTime ? Ticker._pausedTime : 0) : -1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the number of ticks that have been broadcast by Ticker.
|
||||
* @method getTicks
|
||||
* @static
|
||||
* @param {Boolean} pauseable Indicates whether to include ticks that would have been broadcast
|
||||
* while Ticker was paused. If true only tick events broadcast while Ticker is not paused will be returned.
|
||||
* If false, tick events that would have been broadcast while Ticker was paused will be included in the return
|
||||
* value. The default value is false.
|
||||
* @return {Number} of ticks that have been broadcast.
|
||||
**/
|
||||
Ticker.getTicks = function(pauseable) {
|
||||
return Ticker._ticks - (pauseable ?Ticker._pausedTicks : 0);
|
||||
};
|
||||
|
||||
|
||||
// private static methods:
|
||||
/**
|
||||
* @method _handleSynch
|
||||
* @static
|
||||
* @protected
|
||||
**/
|
||||
Ticker._handleSynch = function() {
|
||||
Ticker._timerId = null;
|
||||
Ticker._setupTick();
|
||||
|
||||
// run if enough time has elapsed, with a little bit of flexibility to be early:
|
||||
if (Ticker._getTime() - Ticker._lastTime >= (Ticker._interval-1)*0.97) {
|
||||
Ticker._tick();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @method _handleRAF
|
||||
* @static
|
||||
* @protected
|
||||
**/
|
||||
Ticker._handleRAF = function() {
|
||||
Ticker._timerId = null;
|
||||
Ticker._setupTick();
|
||||
Ticker._tick();
|
||||
};
|
||||
|
||||
/**
|
||||
* @method _handleTimeout
|
||||
* @static
|
||||
* @protected
|
||||
**/
|
||||
Ticker._handleTimeout = function() {
|
||||
Ticker._timerId = null;
|
||||
Ticker._setupTick();
|
||||
Ticker._tick();
|
||||
};
|
||||
|
||||
/**
|
||||
* @method _setupTick
|
||||
* @static
|
||||
* @protected
|
||||
**/
|
||||
Ticker._setupTick = function() {
|
||||
if (Ticker._timerId != null) { return; } // avoid duplicates
|
||||
|
||||
var mode = Ticker.timingMode||(Ticker.useRAF&&Ticker.RAF_SYNCHED);
|
||||
if (mode == Ticker.RAF_SYNCHED || mode == Ticker.RAF) {
|
||||
var f = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame;
|
||||
if (f) {
|
||||
Ticker._timerId = f(mode == Ticker.RAF ? Ticker._handleRAF : Ticker._handleSynch);
|
||||
Ticker._raf = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
Ticker._raf = false;
|
||||
Ticker._timerId = setTimeout(Ticker._handleTimeout, Ticker._interval);
|
||||
};
|
||||
|
||||
/**
|
||||
* @method _tick
|
||||
* @static
|
||||
* @protected
|
||||
**/
|
||||
Ticker._tick = function() {
|
||||
var time = Ticker._getTime();
|
||||
var adjTime = time-Ticker._startTime;
|
||||
var elapsedTime = time-Ticker._lastTime;
|
||||
var paused = Ticker._paused;
|
||||
|
||||
Ticker._ticks++;
|
||||
if (paused) {
|
||||
Ticker._pausedTicks++;
|
||||
Ticker._pausedTime += elapsedTime;
|
||||
}
|
||||
Ticker._lastTime = time;
|
||||
|
||||
if (Ticker.hasEventListener("tick")) {
|
||||
var event = new createjs.Event("tick");
|
||||
var maxDelta = Ticker.maxDelta;
|
||||
event.delta = (maxDelta && elapsedTime > maxDelta) ? maxDelta : elapsedTime;
|
||||
event.paused = paused;
|
||||
event.time = adjTime;
|
||||
event.runTime = adjTime-Ticker._pausedTime;
|
||||
Ticker.dispatchEvent(event);
|
||||
}
|
||||
|
||||
Ticker._tickTimes.unshift(Ticker._getTime()-time);
|
||||
while (Ticker._tickTimes.length > 100) { Ticker._tickTimes.pop(); }
|
||||
|
||||
Ticker._times.unshift(adjTime);
|
||||
while (Ticker._times.length > 100) { Ticker._times.pop(); }
|
||||
};
|
||||
|
||||
/**
|
||||
* @method _getTime
|
||||
* @static
|
||||
* @protected
|
||||
**/
|
||||
var now = window.performance && (performance.now || performance.mozNow || performance.msNow || performance.oNow || performance.webkitNow);
|
||||
Ticker._getTime = function() {
|
||||
return (now&&now.call(performance))||(new Date().getTime());
|
||||
};
|
||||
|
||||
|
||||
createjs.Ticker = Ticker;
|
||||
}());
|
||||
/*
|
||||
* Tween
|
||||
* Visit http://createjs.com/ for documentation, updates and examples.
|
||||
*
|
||||
|
@ -710,11 +1319,6 @@ createjs.EventDispatcher = EventDispatcher;
|
|||
* //Tween complete
|
||||
* }
|
||||
*
|
||||
* <h4>Required Support<h4>
|
||||
* Tweenjs requires a ticker function, which is included in <a href="http://www.easeljs.com">EaselJS</a>.
|
||||
* If you are not using EaselJS, you must build your own ticker function that calls {{#crossLink "Tween/tick"}}{{/crossLink}}
|
||||
* on the tweens.
|
||||
*
|
||||
* <h4>Browser Support</h4>
|
||||
* TweenJS will work in all browsers.
|
||||
*
|
||||
|
@ -764,7 +1368,7 @@ this.createjs = this.createjs||{};
|
|||
* All properties default to false. Supported props are:<UL>
|
||||
* <LI> loop: sets the loop property on this tween.</LI>
|
||||
* <LI> useTicks: uses ticks for all durations instead of milliseconds.</LI>
|
||||
* <LI> ignoreGlobalPause: sets the ignoreGlobalPause property on this tween.</LI>
|
||||
* <LI> ignoreGlobalPause: sets the {{#crossLink "Tween/ignoreGlobalPause:property"}}{{/crossLink}} property on this tween.</LI>
|
||||
* <LI> override: if true, `Tween.removeTweens(target)` will be called to remove any other tweens with the same target.
|
||||
* <LI> paused: indicates whether to start the tween paused.</LI>
|
||||
* <LI> position: indicates the initial position for this tween.</LI>
|
||||
|
@ -779,6 +1383,7 @@ var Tween = function(target, props, pluginData) {
|
|||
this.initialize(target, props, pluginData);
|
||||
};
|
||||
var p = Tween.prototype = new createjs.EventDispatcher();
|
||||
Tween.prototype.constructor = Tween;
|
||||
|
||||
// static interface:
|
||||
/**
|
||||
|
@ -843,7 +1448,7 @@ var p = Tween.prototype = new createjs.EventDispatcher();
|
|||
* All properties default to false. Supported props are:<UL>
|
||||
* <LI> loop: sets the loop property on this tween.</LI>
|
||||
* <LI> useTicks: uses ticks for all durations instead of milliseconds.</LI>
|
||||
* <LI> ignoreGlobalPause: sets the ignoreGlobalPause property on this tween.</LI>
|
||||
* <LI> ignoreGlobalPause: sets the {{#crossLink "Tween/ignoreGlobalPause:property"}}{{/crossLink}} property on this tween.</LI>
|
||||
* <LI> override: if true, Tween.removeTweens(target) will be called to remove any other tweens with the same target.
|
||||
* <LI> paused: indicates whether to start the tween paused.</LI>
|
||||
* <LI> position: indicates the initial position for this tween.</LI>
|
||||
|
@ -863,15 +1468,13 @@ var p = Tween.prototype = new createjs.EventDispatcher();
|
|||
};
|
||||
|
||||
/**
|
||||
* Advances all tweens. This typically uses the Ticker class (available in the EaselJS library), but you can call it
|
||||
* Advances all tweens. This typically uses the {{#crossLink "Ticker"}}{{/crossLink}} class, but you can call it
|
||||
* manually if you prefer to use your own "heartbeat" implementation.
|
||||
*
|
||||
* Note: Currently, EaselJS must be included <em>before</em> TweenJS to ensure Ticker exists during initialization.
|
||||
* @method tick
|
||||
* @param {Number} delta The change in time in milliseconds since the last tick. Required unless all tweens have
|
||||
* <code>useTicks</code> set to true.
|
||||
* @param {Boolean} paused Indicates whether a global pause is in effect. Tweens with <code>ignoreGlobalPause</code>
|
||||
* will ignore this, but all others will pause if this is true.
|
||||
* @param {Boolean} paused Indicates whether a global pause is in effect. Tweens with {{#crossLink "Tween/ignoreGlobalPause:property"}}{{/crossLink}}
|
||||
* will ignore this, but all others will pause if this is `true`.
|
||||
* @static
|
||||
*/
|
||||
Tween.tick = function(delta, paused) {
|
||||
|
@ -887,7 +1490,8 @@ var p = Tween.prototype = new createjs.EventDispatcher();
|
|||
* Handle events that result from Tween being used as an event handler. This is included to allow Tween to handle
|
||||
* tick events from <code>createjs.Ticker</code>. No other events are handled in Tween.
|
||||
* @method handleEvent
|
||||
* @param {Object} event An event object passed in by the EventDispatcher. Will usually be of type "tick".
|
||||
* @param {Object} event An event object passed in by the {{#crossLink "EventDispatcher"}}{{/crossLink}}. Will
|
||||
* usually be of type "tick".
|
||||
* @private
|
||||
* @static
|
||||
* @since 0.4.2
|
||||
|
@ -909,9 +1513,10 @@ var p = Tween.prototype = new createjs.EventDispatcher();
|
|||
if (!target.tweenjs_count) { return; }
|
||||
var tweens = Tween._tweens;
|
||||
for (var i=tweens.length-1; i>=0; i--) {
|
||||
if (tweens[i]._target == target) {
|
||||
tweens[i]._paused = true;
|
||||
tweens.splice(i,1);
|
||||
var tween = tweens[i];
|
||||
if (tween._target == target) {
|
||||
tween._paused = true;
|
||||
tweens.splice(i, 1);
|
||||
}
|
||||
}
|
||||
target.tweenjs_count = 0;
|
||||
|
@ -927,7 +1532,7 @@ var p = Tween.prototype = new createjs.EventDispatcher();
|
|||
var tweens = Tween._tweens;
|
||||
for (var i= 0, l=tweens.length; i<l; i++) {
|
||||
var tween = tweens[i];
|
||||
tween.paused = true;
|
||||
tween._paused = true;
|
||||
tween.target.tweenjs_count = 0;
|
||||
}
|
||||
tweens.length = 0;
|
||||
|
@ -947,8 +1552,8 @@ var p = Tween.prototype = new createjs.EventDispatcher();
|
|||
};
|
||||
|
||||
/**
|
||||
* Installs a plugin, which can modify how certain properties are handled when tweened. See the CSSPlugin for an
|
||||
* example of how to write TweenJS plugins.
|
||||
* Installs a plugin, which can modify how certain properties are handled when tweened. See the {{#crossLink "CSSPlugin"}}{{/crossLink}}
|
||||
* for an example of how to write TweenJS plugins.
|
||||
* @method installPlugin
|
||||
* @static
|
||||
* @param {Object} plugin The plugin class to install
|
||||
|
@ -1000,9 +1605,10 @@ var p = Tween.prototype = new createjs.EventDispatcher();
|
|||
|
||||
// public properties:
|
||||
/**
|
||||
* Causes this tween to continue playing when a global pause is active. For example, if TweenJS is using Ticker,
|
||||
* then setting this to true (the default) will cause this tween to be paused when <code>Ticker.setPaused(true)</code> is called.
|
||||
* See Tween.tick() for more info. Can be set via the props param.
|
||||
* Causes this tween to continue playing when a global pause is active. For example, if TweenJS is using {{#crossLink "Ticker"}}{{/crossLink}},
|
||||
* then setting this to true (the default) will cause this tween to be paused when <code>Ticker.setPaused(true)</code>
|
||||
* is called. See the Tween {{#crossLink "Tween/tick"}}{{/crossLink}} method for more info. Can be set via the props
|
||||
* parameter.
|
||||
* @property ignoreGlobalPause
|
||||
* @type Boolean
|
||||
* @default false
|
||||
|
@ -1360,7 +1966,8 @@ var p = Tween.prototype = new createjs.EventDispatcher();
|
|||
|
||||
/**
|
||||
* Advances this tween by the specified amount of time in milliseconds (or ticks if <code>useTicks</code> is true).
|
||||
* This is normally called automatically by the Tween engine (via <code>Tween.tick</code>), but is exposed for advanced uses.
|
||||
* This is normally called automatically by the Tween engine (via <code>Tween.tick</code>), but is exposed for
|
||||
* advanced uses.
|
||||
* @method tick
|
||||
* @param {Number} delta The time to advance in milliseconds (or ticks if <code>useTicks</code> is true).
|
||||
*/
|
||||
|
@ -1376,6 +1983,7 @@ var p = Tween.prototype = new createjs.EventDispatcher();
|
|||
* @return {Tween} This tween instance (for chaining calls)
|
||||
*/
|
||||
p.setPaused = function(value) {
|
||||
if (this._paused === !!value) { return this; }
|
||||
this._paused = !!value;
|
||||
Tween._register(this, !value);
|
||||
return this;
|
||||
|
@ -1630,6 +2238,7 @@ var Timeline = function(tweens, labels, props) {
|
|||
this.initialize(tweens, labels, props);
|
||||
};
|
||||
var p = Timeline.prototype = new createjs.EventDispatcher();
|
||||
Timeline.prototype.constructor = Timeline;
|
||||
|
||||
// public properties:
|
||||
|
||||
|
@ -1953,7 +2562,7 @@ var p = Timeline.prototype = new createjs.EventDispatcher();
|
|||
* @param {String|Number} positionOrLabel A numeric position value or label string.
|
||||
**/
|
||||
p.resolve = function(positionOrLabel) {
|
||||
var pos = parseFloat(positionOrLabel);
|
||||
var pos = Number(positionOrLabel);
|
||||
if (isNaN(pos)) { pos = this._labels[positionOrLabel]; }
|
||||
return pos;
|
||||
};
|
||||
|
@ -2755,6 +3364,6 @@ this.createjs = this.createjs || {};
|
|||
* @type String
|
||||
* @static
|
||||
**/
|
||||
s.buildDate = /*date*/"Thu, 12 Dec 2013 23:37:07 GMT"; // injected by build process
|
||||
s.buildDate = /*date*/"Fri, 24 Oct 2014 16:09:53 GMT"; // injected by build process
|
||||
|
||||
})();
|
||||
|
|
Loading…
Reference in a new issue