<commit_before>CocoClass = require 'lib/CocoClass'
{createProgressBar} = require './sprite_utils'
Camera = require './Camera'
Mark = require './Mark'
Label = require './Label'
AudioPlayer = require 'lib/AudioPlayer'
{me} = require 'lib/auth'

# 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
  ranges: null

  options:
    resolutionFactor: 4
    groundLayer: null
    textLayer: null
    floatingLayer: null
    frameRateFactor: 1  # TODO: use or lose?
    thang: null
    camera: null
    spriteSheetCache: null
    showInvisible: false

  possessed: false
  flipped: false
  flippedCount: 0
  originalScaleX: null
  originalScaleY: null
  actionQueue: null
  actions: null
  rotation: 0

  # 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:
    'level-sprite-dialogue': 'onDialogue'
    'level-sprite-clear-dialogue': 'onClearDialogue'
    'level-set-letterbox': 'onSetLetterbox'
    'surface:ticked': 'onSurfaceTicked'
    'level-sprite-move': 'onMove'

  constructor: (@thangType, options) ->
    super()
    @options = _.extend($.extend(true, {}, @options), options)
    @setThang @options.thang
    console.error @toString(), "has no ThangType!" unless @thangType

    # this is a stub, use @setImageObject to swap it out for something else later
    @imageObject = new createjs.Container 
    
    @actionQueue = []
    @marks = {}
    @labels = {}
    @ranges = []
    @handledAoEs = {}
    @age = 0
    @scaleFactor = @targetScaleFactor = 1
    if @thangType.isFullyLoaded()
      @setupSprite()
    else
      @stillLoading = true
      @thangType.fetch()
      @listenToOnce(@thangType, 'sync', @setupSprite)

  setupSprite: ->
    @stillLoading = false
    if @thangType.get('raster')
      @isRaster = true
      @setUpRasterImage()
      @actions = {}
    else
      @actions = @thangType.getActions()
      @buildFromSpriteSheet @buildSpriteSheet()
      @createMarks()

  setUpRasterImage: ->
    raster = @thangType.get('raster')
    image = new createjs.Bitmap('/file/'+raster)
    @setImageObject image
    $(image.image).one 'load', => @updateScale?()
    @configureMouse()
    @originalScaleX = image.scaleX
    @originalScaleY = image.scaleY
    @imageObject.sprite = @
    @imageObject.layerPriority = @thangType.get 'layerPriority'
    @imageObject.name = @thang?.spriteName or @thangType.get 'name'
    reg = @getOffset 'registration'
    @imageObject.regX = -reg.x
    @imageObject.regY = -reg.y
    @updateScale()

  destroy: ->
    mark.destroy() for name, mark of @marks
    label.destroy() for name, label of @labels
    @imageObject?.off 'animationend', @playNextAction
    clearInterval @effectInterval if @effectInterval
    super()

  toString: -> "<CocoSprite: #{@thang?.id}>"

  buildSpriteSheet: ->
    options = _.extend @options, @thang?.getSpriteOptions?() ? {}
    options.colorConfig = @options.colorConfig if @options.colorConfig
    options.async = false
    @thangType.getSpriteSheet options
    
  setImageObject: (newImageObject) ->
    if parent = @imageObject?.parent
      parent.removeChild @imageObject
      parent.addChild newImageObject
    @imageObject = newImageObject

  buildFromSpriteSheet: (spriteSheet) ->
    if spriteSheet
      sprite = new createjs.Sprite(spriteSheet)
    else
      sprite = new createjs.Shape()
    sprite.scaleX = sprite.scaleY = 1 / @options.resolutionFactor
    # temp, until these are re-exported with perspective
    if @options.camera and @thangType.get('name') in ['Dungeon Floor', 'Indoor Floor', 'Grass', 'Goal Trigger', 'Obstacle']
      sprite.scaleY *= @options.camera.y2x

    @setImageObject sprite<commit_msg>Fixed #1008<commit_after>CocoClass = require 'lib/CocoClass'
{createProgressBar} = require './sprite_utils'
Camera = require './Camera'
Mark = require './Mark'
Label = require './Label'
AudioPlayer = require 'lib/AudioPlayer'
{me} = require 'lib/auth'

# 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
  ranges: null

  options:
    resolutionFactor: 4
    groundLayer: null
    textLayer: null
    floatingLayer: null
    frameRateFactor: 1  # TODO: use or lose?
    thang: null
    camera: null
    spriteSheetCache: null
    showInvisible: false

  possessed: false
  flipped: false
  flippedCount: 0
  originalScaleX: null
  originalScaleY: null
  actionQueue: null
  actions: null
  rotation: 0

  # 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:
    'level-sprite-dialogue': 'onDialogue'
    'level-sprite-clear-dialogue': 'onClearDialogue'
    'level-set-letterbox': 'onSetLetterbox'
    'surface:ticked': 'onSurfaceTicked'
    'level-sprite-move': 'onMove'

  constructor: (@thangType, options) ->
    super()
    @options = _.extend($.extend(true, {}, @options), options)
    @setThang @options.thang
    console.error @toString(), "has no ThangType!" unless @thangType

    # this is a stub, use @setImageObject to swap it out for something else later
    @imageObject = new createjs.Container 
    
    @actionQueue = []
    @marks = {}
    @labels = {}
    @ranges = []
    @handledAoEs = {}
    @age = 0
    @scaleFactor = @targetScaleFactor = 1
    if @thangType.isFullyLoaded()
      @setupSprite()
    else
      @stillLoading = true
      @thangType.fetch()
      @listenToOnce(@thangType, 'sync', @setupSprite)

  setupSprite: ->
    @stillLoading = false
    if @thangType.get('raster')
      @isRaster = true
      @setUpRasterImage()
      @actions = {}
    else
      @actions = @thangType.getActions()
      @buildFromSpriteSheet @buildSpriteSheet()
      @createMarks()

  setUpRasterImage: ->
    raster = @thangType.get('raster')
    image = new createjs.Bitmap('/file/'+raster)
    @setImageObject image
    $(image.image).one 'load', => @updateScale?()
    @configureMouse()
    @originalScaleX = image.scaleX
    @originalScaleY = image.scaleY
    @imageObject.sprite = @
    @imageObject.layerPriority = @thangType.get 'layerPriority'
    @imageObject.name = @thang?.spriteName or @thangType.get 'name'
    reg = @getOffset 'registration'
    @imageObject.regX = -reg.x
    @imageObject.regY = -reg.y
    @updateScale()

  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
    super()

  toString: -> "<CocoSprite: #{@thang?.id}>"

  buildSpriteSheet: ->
    options = _.extend @options, @thang?.getSpriteOptions?() ? {}
    options.colorConfig = @options.colorConfig if @options.colorConfig
    options.async = false
    @thangType.getSpriteSheet options
    
  setImageObject: (newImageObject) ->
    if parent = @imageObject?.parent
      parent.removeChild @imageObject
      parent.addChild newImageObject
    @imageObject = newImageObject

  buildFromSpriteSheet: (spriteSheet) ->
    if spriteSheet
      sprite = new createjs.Sprite(spriteSheet)
    else
      sprite = new createjs.Shape()
    sprite.scaleX = sprite.scaleY = 1 / @options.resolutionFactor
    # temp, until these are re-exported with perspective
    if @options.camera and @thangType.get('name') in ['Dungeon Floor', 'Indoor Floor', 'Grass', 'Goal Trigger', 'Obstacle']
      sprite.scaleY *= @options.camera.y2x

    @setImageObject sprite
{'fraction_non_alphanumeric': 0.1153441626738343, 'fraction_numerical': 0.007245529975458689, 'mean_word_length': 3.429606625258799, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7128690', 'n_tokens_mistral': 2775, 'n_tokens_neox': 2705, 'n_words': 723}