2014-01-03 13:32:13 -05:00
|
|
|
CocoClass = require 'lib/CocoClass'
|
|
|
|
{createProgressBar} = require './sprite_utils'
|
|
|
|
Camera = require './Camera'
|
|
|
|
Mark = require './Mark'
|
|
|
|
Label = require './Label'
|
|
|
|
AudioPlayer = require 'lib/AudioPlayer'
|
2014-02-11 15:02:27 -05:00
|
|
|
{me} = require 'lib/auth'
|
2014-01-03 13:32:13 -05:00
|
|
|
|
|
|
|
# We'll get rid of this once level's teams actually have colors
|
|
|
|
healthColors =
|
|
|
|
ogres: [64, 128, 212]
|
|
|
|
humans: [255, 0, 0]
|
|
|
|
neutral: [64, 212, 128]
|
|
|
|
|
|
|
|
# Sprite: EaselJS-based view/controller for Thang model
|
|
|
|
module.exports = CocoSprite = class CocoSprite extends CocoClass
|
|
|
|
thangType: null # ThangType instance
|
|
|
|
|
|
|
|
imageObject: null
|
|
|
|
|
|
|
|
healthBar: null
|
|
|
|
marks: null
|
|
|
|
labels: null
|
2014-03-18 08:09:16 -04:00
|
|
|
ranges: null
|
2014-01-03 13:32:13 -05:00
|
|
|
|
|
|
|
options:
|
|
|
|
groundLayer: null
|
|
|
|
textLayer: null
|
|
|
|
floatingLayer: null
|
|
|
|
thang: null
|
|
|
|
camera: null
|
|
|
|
showInvisible: false
|
|
|
|
|
2014-03-24 02:19:18 -04:00
|
|
|
possessed: false
|
2014-01-03 13:32:13 -05:00
|
|
|
flipped: false
|
|
|
|
flippedCount: 0
|
|
|
|
actionQueue: null
|
|
|
|
actions: null
|
|
|
|
rotation: 0
|
|
|
|
|
2014-05-19 15:56:13 -04:00
|
|
|
# Scale numbers
|
2014-07-10 17:22:08 -04:00
|
|
|
scaleFactorX: 1 # Current scale adjustment. This can change rapidly.
|
|
|
|
scaleFactorY: 1
|
|
|
|
targetScaleFactorX: 1 # What the scaleFactor is going toward during a tween.
|
|
|
|
targetScaleFactorY: 1
|
2014-05-19 15:56:13 -04:00
|
|
|
|
2014-01-03 13:32:13 -05:00
|
|
|
# ACTION STATE
|
|
|
|
# Actions have relations. If you say 'move', 'move_side' may play because of a direction
|
|
|
|
# relationship, and if you say 'cast', 'cast_begin' may happen first, or 'cast_end' after.
|
|
|
|
currentRootAction: null # action that, in general, is playing or will play
|
|
|
|
currentAction: null # related action that is right now playing
|
|
|
|
|
|
|
|
subscriptions:
|
2014-08-27 15:24:03 -04:00
|
|
|
'level:sprite-dialogue': 'onDialogue'
|
|
|
|
'level:sprite-clear-dialogue': 'onClearDialogue'
|
|
|
|
'level:set-letterbox': 'onSetLetterbox'
|
2014-01-05 01:05:03 -05:00
|
|
|
'surface:ticked': 'onSurfaceTicked'
|
2014-08-27 15:24:03 -04:00
|
|
|
'sprite:move': 'onMove'
|
2014-01-03 13:32:13 -05:00
|
|
|
|
|
|
|
constructor: (@thangType, options) ->
|
2014-01-30 19:36:36 -05:00
|
|
|
super()
|
2014-03-18 16:08:26 -04:00
|
|
|
@options = _.extend($.extend(true, {}, @options), options)
|
2014-01-03 13:32:13 -05:00
|
|
|
@setThang @options.thang
|
2014-09-26 15:17:46 -04:00
|
|
|
if @thang?
|
|
|
|
options = @thang?.getSpriteOptions?()
|
2014-09-28 13:33:24 -04:00
|
|
|
@options.colorConfig = options.colorConfig if options and options.colorConfig
|
2014-06-30 22:16:26 -04:00
|
|
|
console.error @toString(), 'has no ThangType!' unless @thangType
|
2014-05-14 18:59:56 -04:00
|
|
|
|
|
|
|
# this is a stub, use @setImageObject to swap it out for something else later
|
2014-05-15 23:49:10 -04:00
|
|
|
@imageObject = new createjs.Container
|
|
|
|
|
2014-01-03 13:32:13 -05:00
|
|
|
@actionQueue = []
|
|
|
|
@marks = {}
|
|
|
|
@labels = {}
|
2014-03-18 08:09:16 -04:00
|
|
|
@ranges = []
|
2014-05-17 14:28:21 -04:00
|
|
|
@handledDisplayEvents = {}
|
2014-01-31 13:21:32 -05:00
|
|
|
@age = 0
|
2014-05-20 13:46:52 -04:00
|
|
|
@stillLoading = true
|
2014-09-16 18:36:59 -04:00
|
|
|
if @thangType.isFullyLoaded() then @onThangTypeLoaded() else @listenToOnce(@thangType, 'sync', @onThangTypeLoaded)
|
|
|
|
|
|
|
|
toString: -> "<CocoSprite: #{@thang?.id}>"
|
|
|
|
|
|
|
|
onThangTypeLoaded: ->
|
|
|
|
@stillLoading = false
|
2014-05-15 23:49:10 -04:00
|
|
|
for trigger, sounds of @thangType.get('soundTriggers') or {} when trigger isnt 'say'
|
2014-09-23 00:50:48 -04:00
|
|
|
AudioPlayer.preloadSoundReference sound for sound in sounds when sound
|
2014-05-02 20:03:30 -04:00
|
|
|
if @thangType.get('raster')
|
2014-05-19 13:39:18 -04:00
|
|
|
@actions = {}
|
2014-05-02 20:03:30 -04:00
|
|
|
@isRaster = true
|
|
|
|
else
|
2014-09-16 18:36:59 -04:00
|
|
|
@actions = @thangType.getActions()
|
|
|
|
@createMarks()
|
|
|
|
|
2014-07-10 17:22:08 -04:00
|
|
|
@scaleFactorX = @thang.scaleFactorX if @thang?.scaleFactorX?
|
|
|
|
@scaleFactorX = @thang.scaleFactor if @thang?.scaleFactor?
|
|
|
|
@scaleFactorY = @thang.scaleFactorY if @thang?.scaleFactorY?
|
|
|
|
@scaleFactorY = @thang.scaleFactor if @thang?.scaleFactor?
|
2014-09-25 13:47:53 -04:00
|
|
|
@updateAction() unless @currentAction
|
2014-05-15 23:49:10 -04:00
|
|
|
|
2014-05-14 18:59:56 -04:00
|
|
|
setImageObject: (newImageObject) ->
|
2014-09-16 18:36:59 -04:00
|
|
|
if @imageObject
|
2014-09-17 18:47:25 -04:00
|
|
|
@imageObject.destroy?()
|
2014-09-16 18:36:59 -04:00
|
|
|
if parent = @imageObject.parent
|
|
|
|
parent.removeChild @imageObject
|
|
|
|
parent.addChild newImageObject
|
2014-09-17 18:47:25 -04:00
|
|
|
|
|
|
|
# get the cocosprite to update things
|
|
|
|
for prop in ['lastPos', 'currentRootAction']
|
|
|
|
delete @[prop]
|
|
|
|
|
2014-05-14 18:59:56 -04:00
|
|
|
@imageObject = newImageObject
|
2014-01-03 13:32:13 -05:00
|
|
|
@configureMouse()
|
2014-02-12 15:41:41 -05:00
|
|
|
@imageObject.on 'animationend', @playNextAction
|
2014-09-25 13:47:53 -04:00
|
|
|
@playAction(@currentAction) if @currentAction and not @stillLoading
|
2014-09-24 21:42:04 -04:00
|
|
|
@trigger 'new-image-object', @imageObject
|
2014-09-16 18:36:59 -04:00
|
|
|
|
2014-01-03 13:32:13 -05:00
|
|
|
##################################################
|
|
|
|
# QUEUEING AND PLAYING ACTIONS
|
|
|
|
|
|
|
|
queueAction: (action) ->
|
|
|
|
# The normal way to have an action play
|
|
|
|
action = @actions[action] if _.isString(action)
|
|
|
|
action ?= @actions.idle
|
|
|
|
@actionQueue = []
|
|
|
|
@actionQueue.push @currentRootAction.relatedActions.end if @currentRootAction?.relatedActions?.end
|
|
|
|
@actionQueue.push action.relatedActions.begin if action.relatedActions?.begin
|
|
|
|
@actionQueue.push action
|
2014-02-06 20:00:02 -05:00
|
|
|
if action.goesTo and nextAction = @actions[action.goesTo]
|
|
|
|
@actionQueue.push nextAction if nextAction
|
2014-01-03 13:32:13 -05:00
|
|
|
@currentRootAction = action
|
|
|
|
@playNextAction()
|
|
|
|
|
2014-01-31 13:21:32 -05:00
|
|
|
onSurfaceTicked: (e) -> @age += e.dt
|
2014-01-03 13:32:13 -05:00
|
|
|
|
2014-02-12 15:41:41 -05:00
|
|
|
playNextAction: =>
|
2014-06-30 22:16:26 -04:00
|
|
|
@playAction(@actionQueue.splice(0, 1)[0]) if @actionQueue.length
|
2014-01-03 13:32:13 -05:00
|
|
|
|
|
|
|
playAction: (action) ->
|
2014-05-02 20:03:30 -04:00
|
|
|
return if @isRaster
|
2014-01-03 13:32:13 -05:00
|
|
|
@currentAction = action
|
2014-02-05 17:57:05 -05:00
|
|
|
return @hide() unless action.animation or action.container or action.relatedActions
|
|
|
|
@show()
|
2014-01-03 13:32:13 -05:00
|
|
|
return @updateActionDirection() unless action.animation or action.container
|
2014-09-26 15:17:46 -04:00
|
|
|
return if @imageObject.placeholder
|
2014-06-30 22:16:26 -04:00
|
|
|
m = if action.container then 'gotoAndStop' else 'gotoAndPlay'
|
2014-09-16 18:36:59 -04:00
|
|
|
@imageObject[m]?(action.name)
|
2014-09-26 14:25:36 -04:00
|
|
|
@updateScale()
|
|
|
|
@updateRotation()
|
2014-01-03 13:32:13 -05:00
|
|
|
|
2014-02-05 17:57:05 -05:00
|
|
|
hide: ->
|
|
|
|
@hiding = true
|
|
|
|
@updateAlpha()
|
2014-02-11 12:28:26 -05:00
|
|
|
|
2014-02-05 17:57:05 -05:00
|
|
|
show: ->
|
|
|
|
@hiding = false
|
|
|
|
@updateAlpha()
|
2014-02-24 19:42:34 -05:00
|
|
|
|
2014-02-24 17:40:28 -05:00
|
|
|
stop: ->
|
|
|
|
@imageObject?.stop?()
|
|
|
|
mark.stop() for name, mark of @marks
|
2014-05-06 14:02:53 -04:00
|
|
|
@stopped = true
|
2014-02-24 19:42:34 -05:00
|
|
|
|
2014-02-24 17:40:28 -05:00
|
|
|
play: ->
|
|
|
|
@imageObject?.play?()
|
|
|
|
mark.play() for name, mark of @marks
|
2014-05-06 14:02:53 -04:00
|
|
|
@stopped = false
|
2014-02-05 17:57:05 -05:00
|
|
|
|
2014-03-12 12:10:36 -04:00
|
|
|
update: (frameChanged) ->
|
2014-01-03 13:32:13 -05:00
|
|
|
# Gets the sprite to reflect what the current state of the thangs and surface are
|
2014-01-20 20:20:04 -05:00
|
|
|
return if @stillLoading
|
2014-01-03 13:32:13 -05:00
|
|
|
@updatePosition()
|
2014-07-10 17:22:08 -04:00
|
|
|
frameChanged = frameChanged or @targetScaleFactorX isnt @scaleFactorX or @targetScaleFactorY isnt @scaleFactorY
|
2014-03-12 12:31:45 -04:00
|
|
|
if frameChanged
|
2014-05-17 14:28:21 -04:00
|
|
|
@handledDisplayEvents = {}
|
2014-04-29 19:04:22 -04:00
|
|
|
@updateScale() # must happen before rotation
|
2014-03-12 12:31:45 -04:00
|
|
|
@updateAlpha()
|
|
|
|
@updateRotation()
|
|
|
|
@updateAction()
|
|
|
|
@updateStats()
|
|
|
|
@updateGold()
|
2014-03-12 16:49:36 -04:00
|
|
|
@showAreaOfEffects()
|
2014-05-17 14:28:21 -04:00
|
|
|
@showTextEvents()
|
2014-05-14 18:59:56 -04:00
|
|
|
@updateHealthBar()
|
2014-01-03 13:32:13 -05:00
|
|
|
@updateMarks()
|
|
|
|
@updateLabels()
|
|
|
|
|
2014-03-12 16:49:36 -04:00
|
|
|
showAreaOfEffects: ->
|
2014-09-24 15:08:55 -04:00
|
|
|
return unless @thang?.currentEvents
|
|
|
|
for event in @thang.currentEvents
|
|
|
|
continue unless event.startsWith 'aoe-'
|
|
|
|
continue if @handledDisplayEvents[event]
|
|
|
|
@handledDisplayEvents[event] = true
|
|
|
|
args = JSON.parse(event[4...])
|
|
|
|
key = 'aoe-' + JSON.stringify(args[2..])
|
|
|
|
|
|
|
|
unless key in @options.groundLayer.spriteSheet.getAnimations()
|
|
|
|
args = JSON.parse(event[4...])
|
|
|
|
circle = new createjs.Shape()
|
|
|
|
radius = args[2] * Camera.PPM
|
|
|
|
if args.length is 4
|
|
|
|
circle.graphics.beginFill(args[3]).drawCircle(0, 0, radius)
|
|
|
|
else
|
|
|
|
startAngle = args[4]
|
|
|
|
endAngle = args[5]
|
|
|
|
circle.graphics.beginFill(args[3])
|
|
|
|
.lineTo(0, 0)
|
|
|
|
.lineTo(radius * Math.cos(startAngle), radius * Math.sin(startAngle))
|
|
|
|
.arc(0, 0, radius, startAngle, endAngle)
|
|
|
|
.lineTo(0, 0)
|
|
|
|
@options.groundLayer.addCustomGraphic(key, circle, [-radius, -radius, radius*2, radius*2])
|
|
|
|
|
|
|
|
circle = new createjs.Sprite(@options.groundLayer.spriteSheet)
|
|
|
|
circle.gotoAndStop(key)
|
|
|
|
pos = @options.camera.worldToSurface {x: args[0], y: args[1]}
|
|
|
|
circle.x = pos.x
|
|
|
|
circle.y = pos.y
|
|
|
|
resFactor = @options.groundLayer.resolutionFactor
|
|
|
|
circle.scaleY = @options.camera.y2x * 0.7 / resFactor
|
|
|
|
circle.scaleX = 0.7 / resFactor
|
|
|
|
circle.alpha = 0.2
|
|
|
|
@options.groundLayer.addChild circle
|
|
|
|
createjs.Tween.get(circle)
|
|
|
|
.to({alpha: 0.6, scaleY: @options.camera.y2x / resFactor, scaleX: 1 / resFactor}, 100, createjs.Ease.circOut)
|
|
|
|
.to({alpha: 0, scaleY: 0, scaleX: 0}, 700, createjs.Ease.circIn)
|
|
|
|
.call =>
|
|
|
|
return if @destroyed
|
|
|
|
@options.groundLayer.removeChild circle
|
|
|
|
delete @handledDisplayEvents[event]
|
2014-05-19 13:39:18 -04:00
|
|
|
|
2014-05-17 14:28:21 -04:00
|
|
|
showTextEvents: ->
|
2014-09-26 18:57:24 -04:00
|
|
|
return unless @thang?.currentEvents
|
|
|
|
for event in @thang.currentEvents
|
|
|
|
continue unless event.startsWith 'text-'
|
|
|
|
continue if @handledDisplayEvents[event]
|
|
|
|
@handledDisplayEvents[event] = true
|
|
|
|
options = JSON.parse(event[5...])
|
|
|
|
label = new createjs.Text options.text, "bold #{options.size or 16}px Arial", options.color or '#FFF'
|
|
|
|
shadowColor = {humans: '#F00', ogres: '#00F', neutral: '#0F0', common: '#0F0'}[@thang.team] ? '#000'
|
|
|
|
label.shadow = new createjs.Shadow shadowColor, 1, 1, 3
|
|
|
|
offset = @getOffset 'aboveHead'
|
|
|
|
[label.x, label.y] = [@imageObject.x + offset.x - label.getMeasuredWidth() / 2, @imageObject.y + offset.y]
|
|
|
|
@options.textLayer.addChild label
|
|
|
|
window.labels ?= []
|
|
|
|
window.labels.push label
|
|
|
|
label.alpha = 0
|
|
|
|
createjs.Tween.get(label)
|
|
|
|
.to({y: label.y-2, alpha: 1}, 200, createjs.Ease.linear)
|
|
|
|
.to({y: label.y-12}, 1000, createjs.Ease.linear)
|
|
|
|
.to({y: label.y-22, alpha: 0}, 1000, createjs.Ease.linear)
|
|
|
|
.call =>
|
|
|
|
return if @destroyed
|
|
|
|
@options.textLayer.removeChild label
|
2014-05-17 14:28:21 -04:00
|
|
|
|
2014-01-31 13:21:32 -05:00
|
|
|
getBobOffset: ->
|
|
|
|
return 0 unless @thang.bobHeight
|
2014-05-06 14:02:53 -04:00
|
|
|
return @lastBobOffset if @stopped
|
|
|
|
return @lastBobOffset = @thang.bobHeight * (1 + Math.sin(@age * Math.PI / @thang.bobTime))
|
2014-01-31 13:21:32 -05:00
|
|
|
|
2014-03-05 22:39:14 -05:00
|
|
|
getWorldPosition: ->
|
2014-03-24 02:19:18 -04:00
|
|
|
p1 = if @possessed then @shadow.pos else @thang.pos
|
2014-03-05 22:39:14 -05:00
|
|
|
if bobOffset = @getBobOffset()
|
|
|
|
p1 = p1.copy?() or _.clone(p1)
|
|
|
|
p1.z += bobOffset
|
|
|
|
x: p1.x, y: p1.y, z: if @thang.isLand then 0 else p1.z - @thang.depth / 2
|
|
|
|
|
2014-08-29 20:03:02 -04:00
|
|
|
updatePosition: (whileLoading=false) ->
|
|
|
|
return if @stillLoading and not whileLoading
|
2014-01-03 13:32:13 -05:00
|
|
|
return unless @thang?.pos and @options.camera?
|
2014-03-05 22:54:58 -05:00
|
|
|
wop = @getWorldPosition()
|
2014-01-03 13:32:13 -05:00
|
|
|
[p0, p1] = [@lastPos, @thang.pos]
|
2014-04-28 21:36:46 -04:00
|
|
|
return if p0 and p0.x is p1.x and p0.y is p1.y and p0.z is p1.z and not @options.camera.tweeningZoomTo and not @thang.bobHeight
|
2014-01-03 13:32:13 -05:00
|
|
|
sup = @options.camera.worldToSurface wop
|
2014-05-14 18:59:56 -04:00
|
|
|
[@imageObject.x, @imageObject.y] = [sup.x, sup.y]
|
2014-08-29 20:03:02 -04:00
|
|
|
@lastPos = p1.copy?() or _.clone(p1) unless whileLoading
|
2014-01-05 01:05:03 -05:00
|
|
|
@hasMoved = true
|
2014-08-24 19:09:06 -04:00
|
|
|
if @thangType.get('name') is 'Flag' and not @notOfThisWorld
|
2014-08-24 19:48:59 -04:00
|
|
|
# Let the pending flags know we're here (but not this call stack, they need to delete themselves, and we may be iterating sprites).
|
2014-08-24 19:09:06 -04:00
|
|
|
_.defer => Backbone.Mediator.publish 'surface:flag-appeared', sprite: @
|
2014-05-22 15:05:30 -04:00
|
|
|
|
2014-01-03 13:32:13 -05:00
|
|
|
updateScale: ->
|
2014-05-13 13:26:33 -04:00
|
|
|
return unless @imageObject
|
2014-01-03 13:32:13 -05:00
|
|
|
if @thangType.get('matchWorldDimensions') and @thang
|
|
|
|
if @thang.width isnt @lastThangWidth or @thang.height isnt @lastThangHeight
|
|
|
|
bounds = @imageObject.getBounds()
|
2014-05-14 15:28:47 -04:00
|
|
|
return unless bounds
|
2014-02-24 19:42:34 -05:00
|
|
|
@imageObject.scaleX = @thang.width * Camera.PPM / bounds.width
|
|
|
|
@imageObject.scaleY = @thang.height * Camera.PPM * @options.camera.y2x / bounds.height
|
2014-05-20 00:33:44 -04:00
|
|
|
@imageObject.regX = bounds.width / 2
|
|
|
|
@imageObject.regY = bounds.height / 2
|
2014-05-15 23:49:10 -04:00
|
|
|
|
2014-02-24 19:42:34 -05:00
|
|
|
unless @thang.spriteName is 'Beam'
|
|
|
|
@imageObject.scaleX *= @thangType.get('scale') ? 1
|
|
|
|
@imageObject.scaleY *= @thangType.get('scale') ? 1
|
2014-05-02 20:03:30 -04:00
|
|
|
[@lastThangWidth, @lastThangHeight] = [@thang.width, @thang.height]
|
2014-01-03 13:32:13 -05:00
|
|
|
return
|
2014-05-22 15:05:30 -04:00
|
|
|
|
2014-05-19 15:56:13 -04:00
|
|
|
scaleX = scaleY = 1
|
2014-05-22 15:05:30 -04:00
|
|
|
|
2014-03-30 16:33:21 -04:00
|
|
|
if @thangType.get('name') in ['Arrow', 'Spear']
|
2014-03-23 15:51:46 -04:00
|
|
|
# Scales the arrow so it appears longer when flying parallel to horizon.
|
|
|
|
# To do that, we convert angle to [0, 90] (mirroring half-planes twice), then make linear function out of it:
|
2014-03-23 17:15:06 -04:00
|
|
|
# (a - x) / a: equals 1 when x = 0, equals 0 when x = a, monotonous in between. That gives us some sort of
|
2014-03-23 15:51:46 -04:00
|
|
|
# degenerative multiplier.
|
2014-03-30 16:33:21 -04:00
|
|
|
# For our purposes, a = 90 - the direction straight upwards.
|
2014-03-23 15:51:46 -04:00
|
|
|
# Then we use r + (1 - r) * x function with r = 0.5, so that
|
|
|
|
# maximal scale equals 1 (when x is at it's maximum) and minimal scale is 0.5.
|
|
|
|
# Notice that the value of r is empirical.
|
2014-03-17 16:40:37 -04:00
|
|
|
angle = @getRotation()
|
|
|
|
angle = -angle if angle < 0
|
|
|
|
angle = 180 - angle if angle > 90
|
|
|
|
scaleX = 0.5 + 0.5 * (90 - angle) / 90
|
2014-05-15 23:49:10 -04:00
|
|
|
|
2014-06-30 22:16:26 -04:00
|
|
|
# console.error 'No thang for', @ unless @thang
|
2014-09-24 19:53:38 -04:00
|
|
|
@imageObject.scaleX = @imageObject.baseScaleX * @scaleFactorX * scaleX
|
|
|
|
@imageObject.scaleY = @imageObject.baseScaleY * @scaleFactorY * scaleY
|
2014-07-10 17:22:08 -04:00
|
|
|
|
|
|
|
newScaleFactorX = @thang?.scaleFactorX ? @thang?.scaleFactor ? 1
|
|
|
|
newScaleFactorY = @thang?.scaleFactorY ? @thang?.scaleFactor ? 1
|
|
|
|
if @thang and (newScaleFactorX isnt @targetScaleFactorX or newScaleFactorY isnt @targetScaleFactorY)
|
|
|
|
@targetScaleFactorX = newScaleFactorX
|
|
|
|
@targetScaleFactorY = newScaleFactorY
|
2014-03-16 22:27:48 -04:00
|
|
|
createjs.Tween.removeTweens(@)
|
2014-07-10 17:22:08 -04:00
|
|
|
createjs.Tween.get(@).to({scaleFactorX: @targetScaleFactorX, scaleFactorY: @targetScaleFactorY}, 2000, createjs.Ease.elasticOut)
|
2014-03-17 00:48:28 -04:00
|
|
|
|
2014-01-03 13:32:13 -05:00
|
|
|
updateAlpha: ->
|
2014-02-05 17:57:05 -05:00
|
|
|
@imageObject.alpha = if @hiding then 0 else 1
|
2014-01-03 13:32:13 -05:00
|
|
|
return unless @thang?.alpha?
|
2014-06-22 01:31:10 -04:00
|
|
|
return if @imageObject.alpha is @thang.alpha
|
2014-01-03 13:32:13 -05:00
|
|
|
@imageObject.alpha = @thang.alpha
|
|
|
|
if @options.showInvisible
|
2014-01-05 01:05:03 -05:00
|
|
|
@imageObject.alpha = Math.max 0.5, @imageObject.alpha
|
2014-06-22 01:31:10 -04:00
|
|
|
mark.updateAlpha @thang.alpha for name, mark of @marks
|
|
|
|
@healthBar?.alpha = @thang.alpha
|
2014-01-03 13:32:13 -05:00
|
|
|
|
|
|
|
updateRotation: (imageObject) ->
|
|
|
|
rotationType = @thangType.get('rotationType')
|
|
|
|
return if rotationType is 'fixed'
|
|
|
|
rotation = @getRotation()
|
2014-08-06 10:43:56 -04:00
|
|
|
if @thangType.get('name') in ['Arrow', 'Spear'] and @thang.velocity
|
2014-03-23 15:51:46 -04:00
|
|
|
# Rotates the arrow to see it arc based on velocity.z.
|
2014-03-30 16:33:21 -04:00
|
|
|
# Notice that rotation here does not affect thang's state - it is just the effect.
|
2014-03-23 15:51:46 -04:00
|
|
|
# Thang's rotation is always pointing where it is heading.
|
2014-03-28 18:23:12 -04:00
|
|
|
vz = @thang.velocity.z
|
|
|
|
if vz and speed = @thang.velocity.magnitude(true)
|
|
|
|
vx = @thang.velocity.x
|
|
|
|
heading = @thang.velocity.heading()
|
|
|
|
xFactor = Math.cos heading
|
|
|
|
zFactor = vz / Math.sqrt(vz * vz + vx * vx)
|
|
|
|
rotation -= xFactor * zFactor * 45
|
2014-01-03 13:32:13 -05:00
|
|
|
imageObject ?= @imageObject
|
2014-08-20 01:33:33 -04:00
|
|
|
return imageObject.rotation = rotation if rotationType is 'free' or not rotationType
|
2014-01-03 13:32:13 -05:00
|
|
|
@updateIsometricRotation(rotation, imageObject)
|
|
|
|
|
|
|
|
getRotation: ->
|
2014-03-24 02:19:18 -04:00
|
|
|
thang = if @possessed then @shadow else @thang
|
|
|
|
return @rotation if not thang?.rotation
|
|
|
|
rotation = thang?.rotation
|
2014-01-03 13:32:13 -05:00
|
|
|
rotation = (360 - (rotation * 180 / Math.PI) % 360) % 360
|
|
|
|
rotation -= 360 if rotation > 180
|
|
|
|
rotation
|
|
|
|
|
|
|
|
updateIsometricRotation: (rotation, imageObject) ->
|
2014-01-23 23:08:35 -05:00
|
|
|
return unless @currentAction
|
|
|
|
return if _.string.endsWith(@currentAction.name, 'back')
|
|
|
|
return if _.string.endsWith(@currentAction.name, 'fore')
|
2014-03-12 12:31:45 -04:00
|
|
|
imageObject.scaleX *= -1 if Math.abs(rotation) >= 90
|
2014-01-03 13:32:13 -05:00
|
|
|
|
|
|
|
##################################################
|
|
|
|
updateAction: ->
|
2014-09-25 13:47:53 -04:00
|
|
|
return if @isRaster or @actionLocked
|
2014-01-03 13:32:13 -05:00
|
|
|
action = @determineAction()
|
2014-03-15 14:31:12 -04:00
|
|
|
isDifferent = action isnt @currentRootAction or action is null
|
2014-03-05 15:53:11 -05:00
|
|
|
if not action and @thang?.actionActivated and not @stopLogging
|
2014-06-30 22:16:26 -04:00
|
|
|
console.error 'action is', action, 'for', @thang?.id, 'from', @currentRootAction, @thang.action, @thang.getActionName?()
|
2014-03-05 15:53:11 -05:00
|
|
|
@stopLogging = true
|
2014-04-22 13:39:15 -04:00
|
|
|
@queueAction(action) if action and (isDifferent or (@thang?.actionActivated and action.name isnt 'move'))
|
2014-01-03 13:32:13 -05:00
|
|
|
@updateActionDirection()
|
|
|
|
|
|
|
|
determineAction: ->
|
|
|
|
action = null
|
2014-03-24 02:19:18 -04:00
|
|
|
thang = if @possessed then @shadow else @thang
|
|
|
|
action = thang.action if thang?.acts
|
2014-01-03 13:32:13 -05:00
|
|
|
action ?= @currentRootAction.name if @currentRootAction?
|
|
|
|
action ?= 'idle'
|
2014-04-22 13:39:15 -04:00
|
|
|
unless @actions[action]?
|
|
|
|
@warnedFor ?= {}
|
|
|
|
console.warn 'Cannot show action', action, 'for', @thangType.get('name'), 'because it DNE' unless @warnedFor[action]
|
|
|
|
@warnedFor[action] = true
|
2014-04-29 18:31:18 -04:00
|
|
|
return if @action is 'idle' then null else 'idle'
|
2014-07-17 18:50:29 -04:00
|
|
|
#action = 'break' if @actions.break? and @thang?.erroredOut # This makes it looks like it's dead when it's not: bad in Brawlwood.
|
2014-03-24 02:19:18 -04:00
|
|
|
action = 'die' if @actions.die? and thang?.health? and thang.health <= 0
|
2014-01-03 13:32:13 -05:00
|
|
|
@actions[action]
|
|
|
|
|
|
|
|
updateActionDirection: (@wallGrid=null) ->
|
|
|
|
# wallGrid is only needed for wall grid face updates; should refactor if this works
|
|
|
|
return unless action = @getActionDirection()
|
|
|
|
@playAction(action) if action isnt @currentAction
|
2014-09-25 13:47:53 -04:00
|
|
|
|
|
|
|
lockAction: -> (@actionLocked=true)
|
2014-01-03 13:32:13 -05:00
|
|
|
|
|
|
|
getActionDirection: (rootAction=null) ->
|
|
|
|
rootAction ?= @currentRootAction
|
|
|
|
return null unless relatedActions = rootAction?.relatedActions ? {}
|
|
|
|
rotation = @getRotation()
|
2014-06-30 22:16:26 -04:00
|
|
|
if relatedActions['111111111111'] # has grid-surrounding-wall-based actions
|
2014-01-03 13:32:13 -05:00
|
|
|
if @wallGrid
|
2014-09-16 20:31:00 -04:00
|
|
|
@hadWallGrid = true
|
2014-01-03 13:32:13 -05:00
|
|
|
action = ''
|
|
|
|
tileSize = 4
|
|
|
|
[gx, gy] = [@thang.pos.x, @thang.pos.y]
|
|
|
|
for y in [gy + tileSize, gy, gy - tileSize, gy - tileSize * 2]
|
|
|
|
for x in [gx - tileSize, gx, gx + tileSize]
|
|
|
|
if x >= 0 and y >= 0 and x < @wallGrid.width and y < @wallGrid.height
|
|
|
|
wallThangs = @wallGrid.contents x, y
|
|
|
|
else
|
|
|
|
wallThangs = ['outside of the map yo']
|
|
|
|
if wallThangs.length is 0
|
|
|
|
if y is gy and x is gx
|
2014-06-30 22:16:26 -04:00
|
|
|
action += '1' # the center wall we're placing
|
2014-01-03 13:32:13 -05:00
|
|
|
else
|
2014-06-30 22:16:26 -04:00
|
|
|
action += '0'
|
2014-01-03 13:32:13 -05:00
|
|
|
else if wallThangs.length is 1
|
2014-06-30 22:16:26 -04:00
|
|
|
action += '1'
|
2014-01-03 13:32:13 -05:00
|
|
|
else
|
2014-06-30 22:16:26 -04:00
|
|
|
console.error 'Overlapping walls at', x, y, '...', wallThangs
|
|
|
|
action += '1'
|
2014-01-03 13:32:13 -05:00
|
|
|
matchedAction = '111111111111'
|
|
|
|
for relatedAction of relatedActions
|
|
|
|
if action.match(relatedAction.replace(/\?/g, '.'))
|
|
|
|
matchedAction = relatedAction
|
|
|
|
break
|
2014-06-30 22:16:26 -04:00
|
|
|
#console.log 'returning', matchedAction, 'for', @thang.id, 'at', gx, gy
|
2014-01-03 13:32:13 -05:00
|
|
|
return relatedActions[matchedAction]
|
2014-09-16 20:31:00 -04:00
|
|
|
else if @hadWallGrid
|
|
|
|
return null
|
2014-01-03 13:32:13 -05:00
|
|
|
else
|
|
|
|
keys = _.keys relatedActions
|
|
|
|
index = Math.max 0, Math.floor((179 + rotation) / 360 * keys.length)
|
2014-06-30 22:16:26 -04:00
|
|
|
#console.log 'Showing', relatedActions[keys[index]]
|
2014-01-03 13:32:13 -05:00
|
|
|
return relatedActions[keys[index]]
|
|
|
|
value = Math.abs(rotation)
|
|
|
|
direction = null
|
|
|
|
direction = 'side' if value <= 45 or value >= 135
|
|
|
|
direction = 'fore' if 135 > rotation > 45
|
|
|
|
direction = 'back' if -135 < rotation < -45
|
|
|
|
relatedActions[direction]
|
|
|
|
|
|
|
|
updateStats: ->
|
2014-09-17 00:43:03 -04:00
|
|
|
return unless @thang and @thang.health isnt @lastHealth
|
|
|
|
@lastHealth = @thang.health
|
2014-01-03 13:32:13 -05:00
|
|
|
if bar = @healthBar
|
|
|
|
healthPct = Math.max(@thang.health / @thang.maxHealth, 0)
|
2014-09-24 19:53:38 -04:00
|
|
|
bar.scaleX = healthPct / @options.floatingLayer.resolutionFactor
|
2014-09-17 00:43:03 -04:00
|
|
|
if @thang.showsName
|
|
|
|
@setNameLabel(if @thang.health <= 0 then '' else @thang.id)
|
2014-01-03 13:32:13 -05:00
|
|
|
|
|
|
|
configureMouse: ->
|
2014-05-14 18:59:56 -04:00
|
|
|
@imageObject.cursor = 'pointer' if @thang?.isSelectable
|
|
|
|
@imageObject.mouseEnabled = @imageObject.mouseChildren = false unless @thang?.isSelectable or @thang?.isLand
|
|
|
|
if @imageObject.mouseEnabled
|
|
|
|
@imageObject.on 'mousedown', @onMouseEvent, @, false, 'sprite:mouse-down'
|
|
|
|
@imageObject.on 'click', @onMouseEvent, @, false, 'sprite:clicked'
|
|
|
|
@imageObject.on 'dblclick', @onMouseEvent, @, false, 'sprite:double-clicked'
|
|
|
|
@imageObject.on 'pressmove', @onMouseEvent, @, false, 'sprite:dragged'
|
|
|
|
@imageObject.on 'pressup', @onMouseEvent, @, false, 'sprite:mouse-up'
|
2014-01-03 13:32:13 -05:00
|
|
|
|
|
|
|
onMouseEvent: (e, ourEventName) ->
|
2014-08-24 19:48:59 -04:00
|
|
|
return if @letterboxOn or not @imageObject
|
2014-05-16 18:33:49 -04:00
|
|
|
p = @imageObject
|
|
|
|
p = p.parent while p.parent
|
2014-08-27 15:24:03 -04:00
|
|
|
newEvent = sprite: @, thang: @thang, originalEvent: e, canvas: p.canvas
|
2014-05-16 18:33:49 -04:00
|
|
|
@trigger ourEventName, newEvent
|
|
|
|
Backbone.Mediator.publish ourEventName, newEvent
|
2014-01-03 13:32:13 -05:00
|
|
|
|
|
|
|
addHealthBar: ->
|
2014-09-24 19:53:38 -04:00
|
|
|
return unless @thang?.health? and 'health' in (@thang?.hudProperties ? []) and @options.floatingLayer
|
|
|
|
team = @thang?.team or 'neutral'
|
|
|
|
key = "#{team}-health-bar"
|
|
|
|
|
|
|
|
unless key in @options.floatingLayer.spriteSheet.getAnimations()
|
|
|
|
healthColor = healthColors[team]
|
|
|
|
bar = createProgressBar(healthColor)
|
|
|
|
@options.floatingLayer.addCustomGraphic(key, bar, bar.bounds)
|
|
|
|
|
|
|
|
@healthBar = new createjs.Sprite(@options.floatingLayer.spriteSheet)
|
|
|
|
@healthBar.gotoAndStop(key)
|
|
|
|
offset = @getOffset 'aboveHead'
|
|
|
|
@healthBar.scaleX = @healthBar.scaleY = 1 / @options.floatingLayer.resolutionFactor
|
|
|
|
@healthBar.name = 'health bar'
|
|
|
|
@options.floatingLayer.addChild @healthBar
|
|
|
|
@updateHealthBar()
|
|
|
|
@lastHealth = null
|
2014-01-03 13:32:13 -05:00
|
|
|
|
|
|
|
getActionProp: (prop, subProp, def=null) ->
|
|
|
|
# Get a property or sub-property from an action, falling back to ThangType
|
|
|
|
for val in [@currentAction?[prop], @thangType.get(prop)]
|
|
|
|
val = val[subProp] if val? and subProp
|
|
|
|
return val if val?
|
|
|
|
def
|
|
|
|
|
|
|
|
getOffset: (prop) ->
|
|
|
|
# Get the proper offset from either the current action or the ThangType
|
|
|
|
def = x: 0, y: {registration: 0, torso: -50, mouth: -60, aboveHead: -100}[prop]
|
|
|
|
pos = @getActionProp 'positions', prop, def
|
|
|
|
pos = x: pos.x, y: pos.y
|
2014-05-02 20:03:30 -04:00
|
|
|
if not @isRaster
|
|
|
|
scale = @getActionProp 'scale', null, 1
|
2014-09-25 17:03:44 -04:00
|
|
|
scale *= @imageObject.parent.resolutionFactor if prop is 'registration'
|
2014-05-15 23:49:10 -04:00
|
|
|
pos.x *= scale
|
2014-05-02 20:03:30 -04:00
|
|
|
pos.y *= scale
|
2014-03-06 20:21:24 -05:00
|
|
|
if @thang and prop isnt 'registration'
|
2014-07-10 17:22:08 -04:00
|
|
|
pos.x *= @thang.scaleFactorX ? @thang.scaleFactor ? 1
|
|
|
|
pos.y *= @thang.scaleFactorY ? @thang.scaleFactor ? 1
|
2014-04-24 17:23:15 -04:00
|
|
|
# We might need to do this, but I don't have a good test case yet. TODO: figure out.
|
|
|
|
#if prop isnt @registration
|
|
|
|
# pos.x *= if @getActionProp 'flipX' then -1 else 1
|
|
|
|
# pos.y *= if @getActionProp 'flipY' then -1 else 1
|
2014-01-03 13:32:13 -05:00
|
|
|
pos
|
|
|
|
|
2014-03-15 16:32:17 -04:00
|
|
|
createMarks: ->
|
2014-03-17 00:48:28 -04:00
|
|
|
return unless @options.camera
|
2014-03-15 16:32:17 -04:00
|
|
|
if @thang
|
2014-09-24 21:42:04 -04:00
|
|
|
# TODO: Add back ranges
|
|
|
|
# allProps = []
|
|
|
|
# allProps = allProps.concat (@thang.hudProperties ? [])
|
|
|
|
# allProps = allProps.concat (@thang.programmableProperties ? [])
|
|
|
|
# allProps = allProps.concat (@thang.moreProgrammableProperties ? [])
|
|
|
|
#
|
|
|
|
# for property in allProps
|
|
|
|
# if m = property.match /.*(Range|Distance|Radius)$/
|
|
|
|
# if @thang[m[0]]? and @thang[m[0]] < 9001
|
|
|
|
# @ranges.push
|
|
|
|
# name: m[0]
|
|
|
|
# radius: @thang[m[0]]
|
|
|
|
#
|
|
|
|
# @ranges = _.sortBy @ranges, 'radius'
|
|
|
|
# @ranges.reverse()
|
|
|
|
#
|
|
|
|
# @addMark range.name for range in @ranges
|
2014-03-15 16:32:17 -04:00
|
|
|
|
2014-09-24 21:42:04 -04:00
|
|
|
# TODO: add back bounds
|
|
|
|
# @addMark('bounds').toggle true if @thang?.drawsBounds
|
2014-03-15 16:32:17 -04:00
|
|
|
@addMark('shadow').toggle true unless @thangType.get('shadow') is 0
|
|
|
|
|
2014-01-03 13:32:13 -05:00
|
|
|
updateMarks: ->
|
|
|
|
return unless @options.camera
|
2014-03-06 18:52:09 -05:00
|
|
|
@addMark 'repair', null, 'repair' if @thang?.errorsOut
|
2014-01-03 13:32:13 -05:00
|
|
|
@marks.repair?.toggle @thang?.errorsOut
|
2014-03-15 16:32:17 -04:00
|
|
|
|
|
|
|
if @selected
|
2014-03-18 09:03:39 -04:00
|
|
|
@marks[range['name']].toggle true for range in @ranges
|
2014-03-15 16:32:17 -04:00
|
|
|
else
|
2014-03-18 09:03:39 -04:00
|
|
|
@marks[range['name']].toggle false for range in @ranges
|
2014-03-15 16:32:17 -04:00
|
|
|
|
2014-03-25 08:48:26 -04:00
|
|
|
if @thangType.get('name') in ['Arrow', 'Spear'] and @thang.action is 'die'
|
2014-05-23 15:04:42 -04:00
|
|
|
@marks.shadow?.hide()
|
2014-01-03 13:32:13 -05:00
|
|
|
mark.update() for name, mark of @marks
|
2014-07-23 11:59:42 -04:00
|
|
|
#@thang.effectNames = ['warcry', 'confuse', 'control', 'curse', 'fear', 'poison', 'paralyze', 'regen', 'sleep', 'slow', 'haste']
|
2014-03-06 20:13:16 -05:00
|
|
|
@updateEffectMarks() if @thang?.effectNames?.length or @previousEffectNames?.length
|
2014-03-06 19:32:13 -05:00
|
|
|
|
2014-03-06 18:52:09 -05:00
|
|
|
updateEffectMarks: ->
|
2014-09-24 21:42:04 -04:00
|
|
|
return if _.isEqual @thang.effectNames, @previousEffectNames
|
|
|
|
return if @stopped
|
|
|
|
for effect in @thang.effectNames
|
|
|
|
mark = @addMark effect, @options.floatingLayer, effect
|
|
|
|
mark.statusEffect = true
|
|
|
|
mark.toggle 'on'
|
|
|
|
mark.show()
|
|
|
|
|
|
|
|
if @previousEffectNames
|
|
|
|
for effect in @previousEffectNames
|
|
|
|
continue if effect in @thang.effectNames
|
|
|
|
mark = @marks[effect]
|
|
|
|
mark.toggle false
|
|
|
|
|
|
|
|
if @thang.effectNames.length > 1 and not @effectInterval
|
|
|
|
@rotateEffect()
|
|
|
|
@effectInterval = setInterval @rotateEffect, 1500
|
|
|
|
|
|
|
|
else if @effectInterval and @thang.effectNames.length <= 1
|
|
|
|
clearInterval @effectInterval
|
|
|
|
@effectInterval = null
|
|
|
|
|
|
|
|
@previousEffectNames = @thang.effectNames
|
2014-03-06 18:52:09 -05:00
|
|
|
|
|
|
|
rotateEffect: =>
|
|
|
|
effects = (m.name for m in _.values(@marks) when m.on and m.statusEffect and m.mark)
|
2014-03-06 20:25:40 -05:00
|
|
|
return unless effects.length
|
2014-03-06 18:52:09 -05:00
|
|
|
effects.sort()
|
|
|
|
@effectIndex ?= 0
|
|
|
|
@effectIndex = (@effectIndex + 1) % effects.length
|
|
|
|
@marks[effect].hide() for effect in effects
|
|
|
|
@marks[effects[@effectIndex]].show()
|
2014-01-03 13:32:13 -05:00
|
|
|
|
|
|
|
setHighlight: (to, delay) ->
|
2014-09-26 16:58:35 -04:00
|
|
|
@addMark 'highlight', @options.floatingLayer, 'highlight' if to
|
|
|
|
@marks.highlight?.highlightDelay = delay
|
|
|
|
@marks.highlight?.toggle to and not @dimmed
|
2014-01-03 13:32:13 -05:00
|
|
|
|
|
|
|
setDimmed: (@dimmed) ->
|
|
|
|
@marks.highlight?.toggle @marks.highlight.on and not @dimmed
|
|
|
|
|
|
|
|
setThang: (@thang) ->
|
|
|
|
@options.thang = @thang
|
|
|
|
|
|
|
|
setDebug: (debug) ->
|
2014-09-26 17:09:44 -04:00
|
|
|
return unless @thang?.collides and @options.camera?
|
|
|
|
@addMark 'debug', @options.floatingLayer if debug
|
|
|
|
@marks.debug?.toggle debug
|
2014-01-03 13:32:13 -05:00
|
|
|
|
|
|
|
addLabel: (name, style) ->
|
|
|
|
@labels[name] ?= new Label sprite: @, camera: @options.camera, layer: @options.textLayer, style: style
|
|
|
|
@labels[name]
|
|
|
|
|
|
|
|
addMark: (name, layer, thangType=null) ->
|
|
|
|
@marks[name] ?= new Mark name: name, sprite: @, camera: @options.camera, layer: layer ? @options.groundLayer, thangType: thangType
|
|
|
|
@marks[name]
|
|
|
|
|
|
|
|
notifySpeechUpdated: (e) ->
|
|
|
|
e = _.clone(e)
|
|
|
|
e.sprite = @
|
|
|
|
e.blurb ?= '...'
|
2014-01-09 14:04:22 -05:00
|
|
|
e.thang = @thang
|
2014-01-03 13:32:13 -05:00
|
|
|
Backbone.Mediator.publish 'sprite:speech-updated', e
|
|
|
|
|
|
|
|
isTalking: ->
|
|
|
|
Boolean @labels.dialogue?.text or @labels.say?.text
|
|
|
|
|
|
|
|
onDialogue: (e) ->
|
|
|
|
return unless @thang?.id is e.spriteID
|
|
|
|
label = @addLabel 'dialogue', Label.STYLE_DIALOGUE
|
|
|
|
label.setText e.blurb or '...'
|
|
|
|
sound = e.sound ? AudioPlayer.soundForDialogue e.message, @thangType.get 'soundTriggers'
|
2014-08-26 18:22:13 -04:00
|
|
|
@dialogueSoundInstance?.stop()
|
|
|
|
if @dialogueSoundInstance = @playSound sound, false
|
2014-08-27 15:24:03 -04:00
|
|
|
@dialogueSoundInstance.addEventListener 'complete', -> Backbone.Mediator.publish 'sprite:dialogue-sound-completed', {}
|
2014-01-03 13:32:13 -05:00
|
|
|
@notifySpeechUpdated e
|
|
|
|
|
|
|
|
onClearDialogue: (e) ->
|
|
|
|
@labels.dialogue?.setText null
|
2014-08-26 18:22:13 -04:00
|
|
|
@dialogueSoundInstance?.stop()
|
2014-01-03 13:32:13 -05:00
|
|
|
@notifySpeechUpdated {}
|
|
|
|
|
2014-08-27 15:24:03 -04:00
|
|
|
onSetLetterbox: (e) ->
|
|
|
|
@letterboxOn = e.on
|
|
|
|
|
2014-01-03 13:32:13 -05:00
|
|
|
setNameLabel: (name) ->
|
|
|
|
label = @addLabel 'name', Label.STYLE_NAME
|
|
|
|
label.setText name
|
|
|
|
|
|
|
|
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
|
|
|
|
if @labels.say?.setText blurb
|
|
|
|
@notifySpeechUpdated blurb: blurb
|
|
|
|
label.update() for name, label of @labels
|
|
|
|
|
2014-02-11 15:02:27 -05:00
|
|
|
updateGold: ->
|
|
|
|
# TODO: eventually this should be moved into some sort of team-based update
|
|
|
|
# rather than an each-thang-that-shows-gold-per-team thing.
|
2014-05-20 13:46:52 -04:00
|
|
|
return unless @thang
|
2014-02-11 15:02:27 -05:00
|
|
|
return if @thang.gold is @lastGold
|
|
|
|
gold = Math.floor @thang.gold
|
2014-04-27 23:11:11 -04:00
|
|
|
if @thang.world.age is 0
|
|
|
|
gold = @thang.world.initialTeamGold[@thang.team].gold
|
2014-02-11 15:02:27 -05:00
|
|
|
return if gold is @lastGold
|
|
|
|
@lastGold = gold
|
2014-04-27 18:33:16 -04:00
|
|
|
Backbone.Mediator.publish 'surface:gold-changed', {team: @thang.team, gold: gold, goldEarned: Math.floor(@thang.goldEarned)}
|
2014-02-11 15:02:27 -05:00
|
|
|
|
2014-01-03 13:32:13 -05:00
|
|
|
playSounds: (withDelay=true, volume=1.0) ->
|
|
|
|
for event in @thang.currentEvents ? []
|
|
|
|
@playSound event, withDelay, volume
|
2014-02-11 15:02:27 -05:00
|
|
|
if event is 'pay-bounty-gold' and @thang.bountyGold > 25 and @thang.team isnt me.team
|
2014-02-11 12:28:26 -05:00
|
|
|
AudioPlayer.playInterfaceSound 'coin_1', 0.25
|
2014-01-03 13:32:13 -05:00
|
|
|
if @thang.actionActivated and (action = @thang.getActionName()) isnt 'say'
|
|
|
|
@playSound action, withDelay, volume
|
2014-07-10 11:31:34 -04:00
|
|
|
if @thang.sayMessage and withDelay and not @thang.silent # don't play sayMessages while scrubbing, annoying
|
2014-01-03 13:32:13 -05:00
|
|
|
offsetFrames = Math.abs(@thang.sayStartTime - @thang.world.age) / @thang.world.dt
|
|
|
|
if offsetFrames <= 2 # or (not withDelay and offsetFrames < 30)
|
|
|
|
sound = AudioPlayer.soundForDialogue @thang.sayMessage, @thangType.get 'soundTriggers'
|
|
|
|
@playSound sound, false, volume
|
|
|
|
|
|
|
|
playSound: (sound, withDelay=true, volume=1.0) ->
|
|
|
|
if _.isString sound
|
|
|
|
sound = @thangType.get('soundTriggers')?[sound]
|
|
|
|
if _.isArray sound
|
|
|
|
sound = sound[Math.floor Math.random() * sound.length]
|
|
|
|
return null unless sound
|
|
|
|
delay = if withDelay and sound.delay then 1000 * sound.delay / createjs.Ticker.getFPS() else 0
|
|
|
|
name = AudioPlayer.nameForSoundReference sound
|
2014-08-26 18:22:13 -04:00
|
|
|
AudioPlayer.preloadSoundReference sound
|
2014-03-05 22:39:14 -05:00
|
|
|
instance = AudioPlayer.playSound name, volume, delay, @getWorldPosition()
|
2014-06-30 22:16:26 -04:00
|
|
|
#console.log @thang?.id, 'played sound', name, 'with delay', delay, 'volume', volume, 'and got sound instance', instance
|
2014-01-30 19:36:36 -05:00
|
|
|
instance
|
2014-03-24 02:19:18 -04:00
|
|
|
|
|
|
|
onMove: (e) ->
|
|
|
|
return unless e.spriteID is @thang?.id
|
|
|
|
pos = e.pos
|
|
|
|
if _.isArray pos
|
|
|
|
pos = new Vector pos...
|
|
|
|
else if _.isString pos
|
2014-06-30 22:16:26 -04:00
|
|
|
return console.warn 'Couldn\'t find target sprite', pos, 'from', @options.sprites unless pos of @options.sprites
|
2014-03-24 02:19:18 -04:00
|
|
|
target = @options.sprites[pos].thang
|
|
|
|
heading = Vector.subtract(target.pos, @thang.pos).normalize()
|
|
|
|
distance = @thang.pos.distance target.pos
|
|
|
|
offset = Math.max(target.width, target.height, 2) / 2 + 3
|
|
|
|
pos = Vector.add(@thang.pos, heading.multiply(distance - offset))
|
2014-08-27 15:24:03 -04:00
|
|
|
Backbone.Mediator.publish 'level:sprite-clear-dialogue', {}
|
2014-03-24 02:19:18 -04:00
|
|
|
@onClearDialogue()
|
|
|
|
args = [pos]
|
|
|
|
args.push(e.duration) if e.duration?
|
|
|
|
@move(args...)
|
|
|
|
|
|
|
|
move: (pos, duration=2000, endAnimation='idle') =>
|
|
|
|
@updateShadow()
|
|
|
|
if not duration
|
|
|
|
createjs.Tween.removeTweens(@shadow.pos) if @lastTween
|
|
|
|
@lastTween = null
|
|
|
|
z = @shadow.pos.z
|
|
|
|
@shadow.pos = pos
|
|
|
|
@shadow.pos.z = z
|
2014-05-02 20:03:30 -04:00
|
|
|
@imageObject.gotoAndPlay?(endAnimation)
|
2014-03-24 02:19:18 -04:00
|
|
|
return
|
|
|
|
|
|
|
|
@shadow.action = 'move'
|
|
|
|
@shadow.actionActivated = true
|
|
|
|
@pointToward(pos)
|
|
|
|
@possessed = true
|
|
|
|
@update true
|
|
|
|
|
|
|
|
ease = createjs.Ease.getPowInOut(2.2)
|
|
|
|
if @lastTween
|
|
|
|
ease = createjs.Ease.getPowOut(1.2)
|
|
|
|
createjs.Tween.removeTweens(@shadow.pos)
|
|
|
|
|
|
|
|
endFunc = =>
|
|
|
|
@lastTween = null
|
2014-05-20 15:22:24 -04:00
|
|
|
@imageObject.gotoAndPlay(endAnimation) unless @stillLoading
|
2014-03-24 02:19:18 -04:00
|
|
|
@shadow.action = 'idle'
|
|
|
|
@update true
|
|
|
|
@possessed = false
|
|
|
|
|
|
|
|
@lastTween = createjs.Tween
|
|
|
|
.get(@shadow.pos)
|
2014-06-30 22:16:26 -04:00
|
|
|
.to({x: pos.x, y: pos.y}, duration, ease)
|
2014-03-24 02:19:18 -04:00
|
|
|
.call(endFunc)
|
|
|
|
|
|
|
|
pointToward: (pos) ->
|
|
|
|
@shadow.rotation = Math.atan2(pos.y - @shadow.pos.y, pos.x - @shadow.pos.x)
|
|
|
|
if (@shadow.rotation * 180 / Math.PI) % 90 is 0
|
|
|
|
@shadow.rotation += 0.01
|
|
|
|
|
|
|
|
updateShadow: ->
|
|
|
|
@shadow = {} if not @shadow
|
|
|
|
@shadow.pos = @thang.pos
|
|
|
|
@shadow.rotation = @thang.rotation
|
|
|
|
@shadow.action = @thang.action
|
|
|
|
@shadow.actionActivated = @thang.actionActivated
|
2014-05-14 18:59:56 -04:00
|
|
|
|
|
|
|
updateHealthBar: ->
|
|
|
|
return unless @healthBar
|
2014-09-24 19:53:38 -04:00
|
|
|
bounds = @healthBar.getBounds()
|
|
|
|
offset = @getOffset 'aboveHead'
|
|
|
|
@healthBar.x = @imageObject.x - (-offset.x + bounds.width / 2 / @options.floatingLayer.resolutionFactor)
|
|
|
|
@healthBar.y = @imageObject.y - (-offset.y + bounds.height / 2 / @options.floatingLayer.resolutionFactor)
|
2014-05-19 15:56:13 -04:00
|
|
|
|
|
|
|
destroy: ->
|
|
|
|
mark.destroy() for name, mark of @marks
|
|
|
|
label.destroy() for name, label of @labels
|
|
|
|
p.removeChild @healthBar if p = @healthBar?.parent
|
|
|
|
@imageObject?.off 'animationend', @playNextAction
|
|
|
|
clearInterval @effectInterval if @effectInterval
|
2014-05-22 15:05:30 -04:00
|
|
|
super()
|