Added WebGL paths.
This commit is contained in:
parent
6ebc9e8c28
commit
aab51178e2
4 changed files with 122 additions and 316 deletions
app/lib
|
@ -1,5 +1,5 @@
|
||||||
CocoClass = require 'lib/CocoClass'
|
CocoClass = require 'lib/CocoClass'
|
||||||
path = require './path'
|
TrailMaster = require './TrailMaster'
|
||||||
Dropper = require './Dropper'
|
Dropper = require './Dropper'
|
||||||
AudioPlayer = require 'lib/AudioPlayer'
|
AudioPlayer = require 'lib/AudioPlayer'
|
||||||
{me} = require 'lib/auth'
|
{me} = require 'lib/auth'
|
||||||
|
@ -181,7 +181,6 @@ module.exports = Surface = class Surface extends CocoClass
|
||||||
framesDropped = 0
|
framesDropped = 0
|
||||||
while true
|
while true
|
||||||
Dropper.tick()
|
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)
|
# 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
|
frameAdvanced = (@playing and @currentFrame < lastFrame) or @totalFramesDrawn < 2
|
||||||
if frameAdvanced and @playing
|
if frameAdvanced and @playing
|
||||||
|
@ -210,7 +209,6 @@ module.exports = Surface = class Surface extends CocoClass
|
||||||
@updateState @currentFrame isnt oldFrame
|
@updateState @currentFrame isnt oldFrame
|
||||||
@drawCurrentFrame e
|
@drawCurrentFrame e
|
||||||
@onFrameChanged()
|
@onFrameChanged()
|
||||||
@updatePaths() if (@totalFramesDrawn % 4) is 0 or createjs.Ticker.getMeasuredFPS() > createjs.Ticker.getFPS() - 5
|
|
||||||
Backbone.Mediator.publish('surface:ticked', {dt: 1 / @options.frameRate})
|
Backbone.Mediator.publish('surface:ticked', {dt: 1 / @options.frameRate})
|
||||||
mib = @webGLStage.mouseInBounds
|
mib = @webGLStage.mouseInBounds
|
||||||
if @mouseInBounds isnt mib
|
if @mouseInBounds isnt mib
|
||||||
|
@ -456,7 +454,8 @@ module.exports = Surface = class Surface extends CocoClass
|
||||||
@fastForwardingSpeed = lag / intendedLag
|
@fastForwardingSpeed = lag / intendedLag
|
||||||
else
|
else
|
||||||
@fastForwardingToFrame = @fastForwardingSpeed = null
|
@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
|
||||||
|
@updatePaths()
|
||||||
|
|
||||||
onIdleChanged: (e) ->
|
onIdleChanged: (e) ->
|
||||||
@setPaused e.idle unless @ended
|
@setPaused e.idle unless @ended
|
||||||
|
@ -540,10 +539,12 @@ module.exports = Surface = class Surface extends CocoClass
|
||||||
|
|
||||||
#- Camera focus on hero
|
#- Camera focus on hero
|
||||||
focusOnHero: ->
|
focusOnHero: ->
|
||||||
|
hadHero = @heroLank
|
||||||
@heroLank = @lankBoss.lankFor 'Hero Placeholder'
|
@heroLank = @lankBoss.lankFor 'Hero Placeholder'
|
||||||
if me.team is 'ogres'
|
if me.team is 'ogres'
|
||||||
# TODO: do this for real
|
# TODO: do this for real
|
||||||
@heroLank = @lankBoss.lankFor 'Hero Placeholder 1'
|
@heroLank = @lankBoss.lankFor 'Hero Placeholder 1'
|
||||||
|
@updatePaths() if not hadHero
|
||||||
|
|
||||||
#- Real-time playback
|
#- Real-time playback
|
||||||
|
|
||||||
|
@ -576,22 +577,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: ->
|
updatePaths: ->
|
||||||
return # TODO: Get paths working again with WebGL
|
return unless @options.paths and @heroLank
|
||||||
return unless @options.paths
|
return unless me.isAdmin() # TODO: Fix world thang points, targets, then remove this
|
||||||
return if @casting
|
|
||||||
@hidePaths()
|
@hidePaths()
|
||||||
selectedThang = @lankBoss.selectedLank?.thang
|
|
||||||
return if @world.showPaths is 'never'
|
return if @world.showPaths is 'never'
|
||||||
return if @world.showPaths is 'paused' and @playing
|
layerAdapter = @lankBoss.layerAdapters['Path']
|
||||||
return if @world.showPaths is 'selected' and not selectedThang
|
@trailmaster ?= new TrailMaster @camera, layerAdapter
|
||||||
@trailmaster ?= new path.Trailmaster @camera
|
@paths = @trailmaster.generatePaths @world, @heroLank.thang
|
||||||
selectedOnly = @playing and @world.showPaths is 'selected'
|
|
||||||
@paths = @trailmaster.generatePaths @world, @getCurrentFrame(), selectedThang, @lankBoss.lanks, selectedOnly
|
|
||||||
@paths.name = 'paths'
|
@paths.name = 'paths'
|
||||||
@lankBoss.layerAdapters['Path'].addChild @paths
|
layerAdapter.addChild @paths
|
||||||
|
|
||||||
hidePaths: ->
|
hidePaths: ->
|
||||||
return if not @paths
|
return if not @paths
|
||||||
|
@ -699,6 +694,7 @@ module.exports = Surface = class Surface extends CocoClass
|
||||||
@normalStage.clear()
|
@normalStage.clear()
|
||||||
@webGLStage.clear()
|
@webGLStage.clear()
|
||||||
@musicPlayer?.destroy()
|
@musicPlayer?.destroy()
|
||||||
|
@trailmaster?.destroy()
|
||||||
@normalStage.removeAllChildren()
|
@normalStage.removeAllChildren()
|
||||||
@webGLStage.removeAllChildren()
|
@webGLStage.removeAllChildren()
|
||||||
@webGLStage.removeEventListener 'stagemousemove', @onMouseMove
|
@webGLStage.removeEventListener 'stagemousemove', @onMouseMove
|
||||||
|
|
108
app/lib/surface/TrailMaster.coffee
Normal file
108
app/lib/surface/TrailMaster.coffee
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
PAST_PATH_ALPHA = 0.75
|
||||||
|
PAST_PATH_WIDTH = 5
|
||||||
|
FUTURE_PATH_ALPHA = 0.4
|
||||||
|
FUTURE_PATH_WIDTH = 2
|
||||||
|
|
||||||
|
Camera = require './Camera'
|
||||||
|
CocoClass = require 'lib/CocoClass'
|
||||||
|
|
||||||
|
module.exports = class TrailMaster extends CocoClass
|
||||||
|
paths: null # dictionary of thang ids to containers for their paths
|
||||||
|
pathDisplayObject: null
|
||||||
|
world: null
|
||||||
|
|
||||||
|
constructor: (@camera, @layerAdapter) ->
|
||||||
|
super()
|
||||||
|
@tweenedSprites = []
|
||||||
|
@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 = []
|
||||||
|
|
||||||
|
createGraphics: ->
|
||||||
|
color = @colorForThang(@thang.team, PAST_PATH_ALPHA)
|
||||||
|
@targetDotKey = @cachePathDot(10, color)
|
||||||
|
@pastDotKey = @cachePathDot(PAST_PATH_WIDTH, color)
|
||||||
|
@futureDotKey = @cachePathDot(FUTURE_PATH_WIDTH, @colorForThang(@thang.team, FUTURE_PATH_ALPHA))
|
||||||
|
|
||||||
|
cachePathDot: (width, color) ->
|
||||||
|
key = "path-dot-#{width}-#{color}"
|
||||||
|
color = createjs.Graphics.getRGB(color...)
|
||||||
|
unless key in @layerAdapter.spriteSheet.getAnimations()
|
||||||
|
circle = new createjs.Shape()
|
||||||
|
radius = width/2
|
||||||
|
circle.graphics.setStrokeStyle(1).beginFill(color).beginStroke('#000000').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
|
||||||
|
params = { interval: 8, frameKey: @pastDotKey }
|
||||||
|
return @createPath(points, params)
|
||||||
|
|
||||||
|
createFuturePath: ->
|
||||||
|
return unless points = @world.pointsForThang @thang.id, @camera
|
||||||
|
interval = Math.max(1, parseInt(@world.frameRate / 4))
|
||||||
|
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.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.gotoAndStop(key)
|
||||||
|
sprite.x = x
|
||||||
|
sprite.y = y
|
||||||
|
container.addChild(sprite)
|
||||||
|
if lastSprite and options.animate
|
||||||
|
createjs.Tween.get(lastSprite, {loop: true}).to({x:x, y:y}, 1000)
|
||||||
|
@tweenedSprites.push lastSprite
|
||||||
|
lastSprite = sprite
|
||||||
|
|
||||||
|
@logged = true
|
||||||
|
container
|
||||||
|
|
||||||
|
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
|
|
|
@ -551,7 +551,7 @@ module.exports = class World
|
||||||
freeMemoryAfterEachSerialization: ->
|
freeMemoryAfterEachSerialization: ->
|
||||||
@frames[i] = null for frame, i in @frames when i < @frames.length - 1
|
@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
|
# Optimized
|
||||||
@pointsForThangCache ?= {}
|
@pointsForThangCache ?= {}
|
||||||
cacheKey = thangID
|
cacheKey = thangID
|
||||||
|
@ -570,16 +570,7 @@ module.exports = class World
|
||||||
allPoints.reverse()
|
allPoints.reverse()
|
||||||
@pointsForThangCache[cacheKey] = allPoints
|
@pointsForThangCache[cacheKey] = allPoints
|
||||||
|
|
||||||
points = []
|
return allPoints
|
||||||
[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
|
|
||||||
|
|
||||||
actionsForThang: (thangID, keepIdle=false) ->
|
actionsForThang: (thangID, keepIdle=false) ->
|
||||||
# Optimized
|
# Optimized
|
||||||
|
|
Reference in a new issue