Merge branch 'feature/team-colors'

Conflicts:
	app/lib/world/world.coffee
	app/models/ThangType.coffee
This commit is contained in:
Scott Erickson 2014-01-16 10:14:21 -08:00
commit c5b9b103c5
14 changed files with 180 additions and 112 deletions

View file

@ -4,6 +4,7 @@ AudioPlayer = require 'lib/AudioPlayer'
LevelSession = require 'models/LevelSession' LevelSession = require 'models/LevelSession'
ThangType = require 'models/ThangType' ThangType = require 'models/ThangType'
app = require 'application' app = require 'application'
World = require 'lib/world/world'
# This is an initial stab at unifying loading and setup into a single place which can # This is an initial stab at unifying loading and setup into a single place which can
# monitor everything and keep a LoadingScreen visible overall progress. # monitor everything and keep a LoadingScreen visible overall progress.
@ -70,17 +71,17 @@ module.exports = class LevelLoader extends CocoClass
onSupermodelLoadedOne: (e) => onSupermodelLoadedOne: (e) =>
@notifyProgress() @notifyProgress()
if e.model.type() is 'ThangType' # if e.model.type() is 'ThangType'
thangType = e.model # thangType = e.model
options = {async: true} # options = {async: true}
if thangType.get('name') is 'Wizard' # if thangType.get('name') is 'Wizard'
options.colorConfig = me.get('wizard')?.colorConfig or {} # options.colorConfig = me.get('wizard')?.colorConfig or {}
building = thangType.buildSpriteSheet options # building = thangType.buildSpriteSheet options
if building # if building
@spriteSheetsToBuild += 1 # @spriteSheetsToBuild += 1
thangType.on 'build-complete', => # thangType.on 'build-complete', =>
@spriteSheetsBuilt += 1 # @spriteSheetsBuilt += 1
@notifyProgress() # @notifyProgress()
onSupermodelLoadedAll: => onSupermodelLoadedAll: =>
@trigger 'loaded-supermodel' @trigger 'loaded-supermodel'
@ -112,6 +113,49 @@ module.exports = class LevelLoader extends CocoClass
tempSession.save(patch, {patch: true}) tempSession.save(patch, {patch: true})
@sessionDenormalized = true @sessionDenormalized = true
# World init
initWorld: ->
return if @world
@world = new World @level.get('name')
serializedLevel = @level.serialize(@supermodel)
@world.loadFromLevel serializedLevel, false
# @setTeam @world.teamForPlayer 1 # We don't know which player we are; this will go away--temp TODO
@buildSpriteSheets()
buildSpriteSheets: ->
thangTypes = {}
thangTypes[tt.get('name')] = tt for tt in @supermodel.getModels(ThangType)
colorConfigs = @world.getTeamColors()
thangsProduced = {}
baseOptions = {resolutionFactor: 4, async: true}
for thang in @world.thangs
continue unless thang.spriteName
thangType = thangTypes[thang.spriteName]
options = thang.getSpriteOptions(colorConfigs)
options.async = true
thangsProduced[thang.spriteName] = true
@buildSpriteSheet(thangType, options)
for thangName, thangType of thangTypes
continue if thangsProduced[thangName]
thangType.spriteOptions = {resolutionFactor: 4, async: true}
@buildSpriteSheet(thangType, thangType.spriteOptions)
buildSpriteSheet: (thangType, options) ->
if thangType.get('name') is 'Wizard'
options.colorConfig = me.get('wizard')?.colorConfig or {}
building = thangType.buildSpriteSheet options
return unless building
console.log 'Building:', thangType.get('name'), options
@spriteSheetsToBuild += 1
thangType.on 'build-complete', =>
@spriteSheetsBuilt += 1
@notifyProgress()
# Initial Sound Loading # Initial Sound Loading
loadAudio: -> loadAudio: ->
@ -159,7 +203,9 @@ module.exports = class LevelLoader extends CocoClass
notifyProgress: -> notifyProgress: ->
Backbone.Mediator.publish 'level-loader:progress-changed', progress: @progress() Backbone.Mediator.publish 'level-loader:progress-changed', progress: @progress()
@trigger 'ready-to-init-world' if @allDone() @initWorld() if @allDone()
# @trigger 'ready-to-init-world' if @allDone()
@trigger 'loaded-all' if @progress() is 1
destroy: -> destroy: ->
@supermodel.off 'loaded-one', @onSupermodelLoadedOne @supermodel.off 'loaded-one', @onSupermodelLoadedOne

View file

@ -72,10 +72,11 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
toString: -> "<CocoSprite: #{@thang?.id}>" toString: -> "<CocoSprite: #{@thang?.id}>"
spriteSheetKey: -> buildSpriteSheet: ->
"#{@thangType.get('name')} - #{@options.resolutionFactor}" options = @thang?.getSpriteOptions() or {}
options.colorConfig = @options.colorConfig if @options.colorConfig
buildSpriteSheet: -> @thangType.getSpriteSheet @options options.async = false
@thangType.getSpriteSheet options
buildFromSpriteSheet: (spriteSheet) -> buildFromSpriteSheet: (spriteSheet) ->
if spriteSheet if spriteSheet

View file

@ -115,10 +115,9 @@ module.exports = class Mark extends CocoClass
buildSprite: -> buildSprite: ->
#console.log "building", @name, "with thangtype", @thangType #console.log "building", @name, "with thangtype", @thangType
options = resolutionFactor: 4
CocoSprite = require './CocoSprite' CocoSprite = require './CocoSprite'
markSprite = new CocoSprite @thangType, options markSprite = new CocoSprite @thangType, @thangType.spriteOptions
markSprite.queueAction "idle" markSprite.queueAction 'idle'
@mark = markSprite.displayObject @mark = markSprite.displayObject
update: (pos=null) -> update: (pos=null) ->

View file

@ -87,7 +87,7 @@ module.exports = class SpriteBoss extends CocoClass
@selectionMark = new Mark name: 'selection', camera: @camera, layer: @spriteLayers["Ground"], thangType: @thangTypeFor("Selection") @selectionMark = new Mark name: 'selection', camera: @camera, layer: @spriteLayers["Ground"], thangType: @thangTypeFor("Selection")
createSpriteOptions: (options) -> createSpriteOptions: (options) ->
_.extend options, camera: @camera, resolutionFactor: 4, groundLayer: @spriteLayers["Ground"], textLayer: @surfaceTextLayer, floatingLayer: @spriteLayers["Floating"], markThangTypes: @markThangTypes(), spriteSheetCache: @spriteSheetCache, showInvisible: @options.showInvisible _.extend options, camera: @camera, resolutionFactor: 4, groundLayer: @spriteLayers["Ground"], textLayer: @surfaceTextLayer, floatingLayer: @spriteLayers["Floating"], markThangTypes: @markThangTypes(), spriteSheetCache: @spriteSheetCache, showInvisible: @options.showInvisible, world: @world
createIndieSprites: (indieSprites, withWizards) -> createIndieSprites: (indieSprites, withWizards) ->
unless @indieSprites unless @indieSprites

View file

@ -157,3 +157,10 @@ module.exports = class Thang
# TODO: take some (but not all) of deserialize logic from ThangState to handle other types # TODO: take some (but not all) of deserialize logic from ThangState to handle other types
t[prop] = val t[prop] = val
t t
getSpriteOptions: ->
colorConfigs = @world?.getTeamColors() or {}
options = {}
if @team and colorConfigs[@team]
options.colorConfig = {team: colorConfigs[@team]}
options

View file

@ -252,9 +252,8 @@ module.exports = class World
# Code hotspot; optimize it # Code hotspot; optimize it
if @frames.length < @totalFrames then worldShouldBeOverBeforeSerialization if @frames.length < @totalFrames then worldShouldBeOverBeforeSerialization
[transferableObjects, nontransferableObjects] = [0, 0] [transferableObjects, nontransferableObjects] = [0, 0]
o = {name: @name, totalFrames: @totalFrames, maxTotalFrames: @maxTotalFrames, frameRate: @frameRate, dt: @dt, victory: @victory, userCodeMap: {}} o = {name: @name, totalFrames: @totalFrames, maxTotalFrames: @maxTotalFrames, frameRate: @frameRate, dt: @dt, victory: @victory, userCodeMap: {}, trackedProperties: {}}
o.trackedProperties[prop] = @[prop] for prop in @trackedProperties or []
o[prop] = @[prop] for prop in @trackedProperties or []
for thangID, methods of @userCodeMap for thangID, methods of @userCodeMap
serializedMethods = o.userCodeMap[thangID] = {} serializedMethods = o.userCodeMap[thangID] = {}
@ -348,7 +347,8 @@ module.exports = class World
perf.t0 = now() perf.t0 = now()
w = new World o.name, o.userCodeMap, classMap w = new World o.name, o.userCodeMap, classMap
[w.totalFrames, w.maxTotalFrames, w.frameRate, w.dt, w.scriptNotes, w.victory] = [o.totalFrames, o.maxTotalFrames, o.frameRate, o.dt, o.scriptNotes ? [], o.victory] [w.totalFrames, w.maxTotalFrames, w.frameRate, w.dt, w.scriptNotes, w.victory] = [o.totalFrames, o.maxTotalFrames, o.frameRate, o.dt, o.scriptNotes ? [], o.victory]
[w.showCoordinates, w.showGrid, w.showPaths, w.indieSprites] = [o.showCoordinates, o.showGrid, o.showPaths, o.indieSprites] w[prop] = val for prop, val of o.trackedProperties
perf.t1 = now() perf.t1 = now()
w.thangs = (Thang.deserialize(thang, w, classMap) for thang in o.thangs) w.thangs = (Thang.deserialize(thang, w, classMap) for thang in o.thangs)
w.setThang thang for thang in w.thangs w.setThang thang for thang in w.thangs
@ -457,3 +457,9 @@ module.exports = class World
lastAction = action lastAction = action
@actionsForThangCache[cacheKey] = actions @actionsForThangCache[cacheKey] = actions
return actions return actions
getTeamColors: ->
teamConfigs = @teamConfigs or {}
colorConfigs = {}
colorConfigs[teamName] = config.color for teamName, config of teamConfigs
colorConfigs

View file

@ -4,10 +4,11 @@ SpriteBuilder = require 'lib/sprites/SpriteBuilder'
module.exports = class ThangType extends CocoModel module.exports = class ThangType extends CocoModel
@className: "ThangType" @className: "ThangType"
urlRoot: "/db/thang.type" urlRoot: "/db/thang.type"
building: 0 building: {}
initialize: -> initialize: ->
super() super()
@building = {}
@setDefaults() @setDefaults()
@on 'sync', @setDefaults @on 'sync', @setDefaults
@spriteSheets = {} @spriteSheets = {}
@ -47,15 +48,18 @@ module.exports = class ThangType extends CocoModel
options options
buildSpriteSheet: (options) -> buildSpriteSheet: (options) ->
@options = @fillOptions options
key = @spriteSheetKey(@options)
return if @building[key]
@initBuild(options) @initBuild(options)
# @options.portraitOnly = true
@addGeneralFrames() unless @options.portraitOnly @addGeneralFrames() unless @options.portraitOnly
@addPortrait() @addPortrait()
@finishBuild() @building[key] = true
result = @finishBuild()
return result
initBuild: (options) -> initBuild: (options) ->
@buildActions() if not @actions @buildActions() if not @actions
@options = @fillOptions options
@vectorParser = new SpriteBuilder(@, options) @vectorParser = new SpriteBuilder(@, options)
@builder = new createjs.SpriteSheetBuilder() @builder = new createjs.SpriteSheetBuilder()
@builder.padding = 2 @builder.padding = 2
@ -131,8 +135,7 @@ module.exports = class ThangType extends CocoModel
@builder.buildAsync() @builder.buildAsync()
@builder.on 'complete', @onBuildSpriteSheetComplete, @, true, key @builder.on 'complete', @onBuildSpriteSheetComplete, @, true, key
return true return true
console.warn 'Building', @get('name'), 'and blocking the main thread.'
console.warn 'Building', @get('name'), 'and blocking the main thread. LevelLoader should have it built asynchronously instead.'
spriteSheet = @builder.build() spriteSheet = @builder.build()
@spriteSheets[key] = spriteSheet @spriteSheets[key] = spriteSheet
spriteSheet spriteSheet

View file

@ -35,22 +35,41 @@
font-size: 12px font-size: 12px
#wizard-settings-tab-view #wizard-settings-tab-view
.form-horizontal #color-settings
float: left float: left
width: 400px width: 600px
margin-left: 50px margin-left: 30px
canvas canvas
float: left float: left
border: 2px solid black border: 2px solid black
margin: 20px margin: 20px
.selector .color-group
position: relative clear: both
top: 10px padding-bottom: 10px
left: 10px
h2 input
margin-left: 10px
.form-horizontal .control-group
margin-bottom: 10px margin-bottom: 10px
border-bottom: 1px solid gray
.name-cell
float: left
width: 100px
padding-top: 2px
input
margin-right: 10px
position: relative
top: -3px
.checkbox-cell
float: left
width: 40px
.slider-cell
margin-bottom: 10px
float: left
width: 120px
.selector
width: 100px

View file

@ -1,41 +1,12 @@
@import "bootstrap/variables" @import "bootstrap/variables"
#editor-level-view #editor-level-view
.images .editor-column
margin: 30px 0 width: 33%
text-align: center
div
float: left
width: 25%
padding: 10px 40px
box-sizing: border-box box-sizing: border-box
img padding-right: 20px
width: 100% float: left
h3
text-decoration: underline
margin-bottom: 2px
#image-modal
.modal-body
max-height: 100%
height: 80%
width: 80%
margin-left: -40%
img
display: block
margin: 0 auto
max-height: 80%
#after-images
clear: both
ul#prereqs
margin: 20px 40px
#editor-links .btn
margin-right: 20px
margin-bottom: 30px
//.screencast-wrapper
// text-align: center
// background: $gray
// object
// display: inline-block

View file

@ -1,21 +1,22 @@
canvas#tinting-display(width=200, height=200).img-rounded canvas#tinting-display(width=200, height=200).img-rounded
.form-horizontal #color-settings
for group in colorGroups for group in colorGroups
.color-group(data-name=group.name) .color-group(data-name=group.name)
h2 div.name-cell
span(data-i18n=group.dasherized)= group.humanized
input(type='checkbox', checked=group.exists).color-group-checkbox input(type='checkbox', checked=group.exists).color-group-checkbox
.sliders span(data-i18n=group.dasherized)= group.humanized
.control-group div.sliders
label.control-label(for=group.humanized+"_hue", data-i18n="hue") Hue div.slider-cell
label(for=group.humanized+"_hue", data-i18n="hue") Hue
.controls .controls
.selector(id=group.humanized+"_hue", name=group.name+'.hue', data-key='hue') .selector(id=group.humanized+"_hue", name=group.name+'.hue', data-key='hue')
.control-group div.slider-cell
label.control-label(for=group.humanized+"_saturation", data-i18n="saturation") Saturation label(for=group.humanized+"_saturation", data-i18n="saturation") Saturation
.controls .controls
.selector(id=group.humanized+"_saturation", name=group.name+'.saturation', data-key='saturation') .selector(id=group.humanized+"_saturation", name=group.name+'.saturation', data-key='saturation')
.control-group div.slider-cell
label.control-label(for=group.humanized+"_lightness", data-i18n="lightness") Lightness label(for=group.humanized+"_lightness", data-i18n="lightness") Lightness
.controls .controls
.selector(id=group.humanized+"_lightness", name=group.name+'.lightness', data-key='lightness') .selector(id=group.humanized+"_lightness", name=group.name+'.lightness', data-key='lightness')
div.clearfix

View file

@ -49,7 +49,7 @@ module.exports = class WizardSettingsTabView extends RootView
@$el.find('.selector').each (i, slider) => @$el.find('.selector').each (i, slider) =>
[groupName, prop] = $(slider).attr('name').split('.') [groupName, prop] = $(slider).attr('name').split('.')
value = 100 * (wizardSettings.colorConfig[groupName]?[prop] or 0) value = 100 * (wizardSettings.colorConfig[groupName]?[prop] ? 0.5)
@initSlider $(slider), value, @onSliderChanged @initSlider $(slider), value, @onSliderChanged
@$el.find('.color-group').each (i, colorGroup) => @$el.find('.color-group').each (i, colorGroup) =>
@ -82,6 +82,8 @@ module.exports = class WizardSettingsTabView extends RootView
initStage: -> initStage: ->
@stage = new createjs.Stage(@$el.find('canvas')[0]) @stage = new createjs.Stage(@$el.find('canvas')[0])
@updateMovieClip() @updateMovieClip()
createjs.Ticker.setFPS 20
createjs.Ticker.addEventListener("tick", @stage)
updateMovieClip: -> updateMovieClip: ->
return unless @wizardThangType.loaded return unless @wizardThangType.loaded
@ -92,13 +94,16 @@ module.exports = class WizardSettingsTabView extends RootView
options = {colorConfig: wizardSettings.colorConfig} options = {colorConfig: wizardSettings.colorConfig}
@spriteBuilder.setOptions options @spriteBuilder.setOptions options
@spriteBuilder.buildColorMaps() @spriteBuilder.buildColorMaps()
portraitAction = @wizardThangType.get('actions')?.portrait castAction = @wizardThangType.get('actions')?.cast
return unless portraitAction?.animation return unless castAction?.animation
@movieClip = @spriteBuilder.buildMovieClip portraitAction.animation @movieClip = @spriteBuilder.buildMovieClip castAction.animation
@movieClip.scaleY = @movieClip.scaleX = 2 * (portraitAction.scale or 1) @movieClip.scaleY = @movieClip.scaleX = 1.7 * (castAction.scale or 1)
reg = portraitAction.positions?.registration reg = castAction.positions?.registration
if reg if reg
@movieClip.regX = reg.x @movieClip.regX = reg.x
@movieClip.regY = reg.y @movieClip.regY = reg.y
@stage.addChild @movieClip @stage.addChild @movieClip
@stage.update() @stage.update()
destroy: ->
@stage?.removeAllEventListeners()

View file

@ -78,7 +78,7 @@ module.exports = class HUDView extends View
if not @thang if not @thang
@hintNextSelectionTimeout = _.delay((=> @$el.find('.no-selection-message').slideDown('slow')), 10000) @hintNextSelectionTimeout = _.delay((=> @$el.find('.no-selection-message').slideDown('slow')), 10000)
return return
@createAvatar thangType @createAvatar thangType, @thang
@createProperties() @createProperties()
@createActions() @createActions()
@update() @update()
@ -88,7 +88,7 @@ module.exports = class HUDView extends View
return if speakerSprite is @speakerSprite return if speakerSprite is @speakerSprite
@speakerSprite = speakerSprite @speakerSprite = speakerSprite
@speaker = @speakerSprite.thang.id @speaker = @speakerSprite.thang.id
@createAvatar @speakerSprite.thangType @createAvatar @speakerSprite.thangType, @speakerSprite.thang
@$el.removeClass 'no-selection' @$el.removeClass 'no-selection'
@switchToDialogueElements() @switchToDialogueElements()
@ -102,8 +102,10 @@ module.exports = class HUDView extends View
@bubble = null @bubble = null
@update() @update()
createAvatar: (thangType) -> createAvatar: (thangType, thang) ->
stage = thangType.getPortraitStage() options = thang.getSpriteOptions() or {}
options.async = false
stage = thangType.getPortraitStage options
wrapper = @$el.find '.thang-canvas-wrapper' wrapper = @$el.find '.thang-canvas-wrapper'
newCanvas = $(stage.canvas).addClass('thang-canvas') newCanvas = $(stage.canvas).addClass('thang-canvas')
wrapper.empty().append(newCanvas) wrapper.empty().append(newCanvas)

View file

@ -21,7 +21,9 @@ module.exports = class ThangAvatarView extends View
thangs = @supermodel.getModels(ThangType) thangs = @supermodel.getModels(ThangType)
thangs = (t for t in thangs when t.get('name') is @thang.spriteName) thangs = (t for t in thangs when t.get('name') is @thang.spriteName)
thang = thangs[0] thang = thangs[0]
context.avatarURL = thang.getPortraitSource() options = @thang?.getSpriteOptions() or {}
options.async = false
context.avatarURL = thang.getPortraitSource(options)
context.includeName = @includeName context.includeName = @includeName
context context

View file

@ -71,9 +71,7 @@ module.exports = class PlayLevelView extends View
window.tracker?.trackEvent 'Hour of Code Begin', {} window.tracker?.trackEvent 'Hour of Code Begin', {}
@isEditorPreview = @getQueryVariable "dev" @isEditorPreview = @getQueryVariable "dev"
sessionID = @getQueryVariable "session" @sessionID = @getQueryVariable "session"
@levelLoader = new LevelLoader(@levelID, @supermodel, sessionID)
@levelLoader.once 'ready-to-init-world', @onReadyToInitWorld
$(window).on('resize', @onWindowResize) $(window).on('resize', @onWindowResize)
@supermodel.once 'error', => @supermodel.once 'error', =>
@ -81,9 +79,16 @@ module.exports = class PlayLevelView extends View
@$el.html('<div class="alert">' + msg + '</div>') @$el.html('<div class="alert">' + msg + '</div>')
@saveScreenshot = _.throttle @saveScreenshot, 30000 @saveScreenshot = _.throttle @saveScreenshot, 30000
@load() unless @isEditorPreview
setLevel: (@level, @supermodel) -> setLevel: (@level, @supermodel) ->
@god?.level = @level.serialize @supermodel @god?.level = @level.serialize @supermodel
@initWorld() @load()
load: ->
@levelLoader = new LevelLoader(@levelID, @supermodel, @sessionID)
@levelLoader.once 'ready-to-init-world', @onReadyToInitWorld
@levelLoader.once 'loaded-all', @onLevelLoaderLoaded
getRenderData: -> getRenderData: ->
c = super() c = super()
@ -96,11 +101,12 @@ module.exports = class PlayLevelView extends View
@loadingScreen.show() @loadingScreen.show()
super() super()
onReadyToInitWorld: => onLevelLoaderLoaded: =>
@session = @levelLoader.session @session = @levelLoader.session
@level = @levelLoader.level @level = @levelLoader.level
@world = @levelLoader.world
@loadingScreen.destroy() @loadingScreen.destroy()
@initWorld() # @initWorld()
@initSurface() @initSurface()
@initGod() @initGod()
@initGoalManager() @initGoalManager()