Merge branch 'master' into production

This commit is contained in:
Scott Erickson 2014-07-05 10:10:27 -07:00
commit 11a725e992
351 changed files with 4534 additions and 4681 deletions

View file

@ -27,7 +27,7 @@ elementAcceptsKeystrokes = (el) ->
type = el.type?.toLowerCase() type = el.type?.toLowerCase()
textInputTypes = ['text', 'password', 'file', 'number', 'search', 'url', 'tel', 'email', 'date', 'month', 'week', 'time', 'datetimelocal'] textInputTypes = ['text', 'password', 'file', 'number', 'search', 'url', 'tel', 'email', 'date', 'month', 'week', 'time', 'datetimelocal']
# not radio, checkbox, range, or color # not radio, checkbox, range, or color
return (tag is 'textarea' or (tag is 'input' and type in textInputTypes) or el.contentEditable in ["", "true"]) and not (el.readOnly or el.disabled) return (tag is 'textarea' or (tag is 'input' and type in textInputTypes) or el.contentEditable in ['', 'true']) and not (el.readOnly or el.disabled)
COMMON_FILES = ['/images/pages/base/modal_background.png', '/images/level/code_palette_background.png', '/images/level/popover_background.png', '/images/level/code_editor_background.png'] COMMON_FILES = ['/images/pages/base/modal_background.png', '/images/level/code_palette_background.png', '/images/level/popover_background.png', '/images/level/code_editor_background.png']
preload = (arrayOfImages) -> preload = (arrayOfImages) ->
@ -49,7 +49,7 @@ Application = initialize: ->
resStore: locale resStore: locale
#debug: true #debug: true
#sendMissing: true #sendMissing: true
#sendMissingTo: "current" #sendMissingTo: 'current'
#resPostPath: '/languages/add/__lng__/__ns__' #resPostPath: '/languages/add/__lng__/__ns__'
}, (t) => }, (t) =>
@router = new Router() @router = new Router()

View file

@ -3,9 +3,9 @@ File = require 'models/File'
module.exports = class ModelFiles extends CocoCollection module.exports = class ModelFiles extends CocoCollection
model: File model: File
constructor: (model) -> constructor: (model) ->
super() super()
url = model.constructor.prototype.urlRoot url = model.constructor.prototype.urlRoot
url += "/#{model.get('original') or model.id}/files" url += "/#{model.get('original') or model.id}/files"
@url = url @url = url

View file

@ -3,8 +3,7 @@ CocoCollection = require 'collections/CocoCollection'
module.exports = class PatchesCollection extends CocoCollection module.exports = class PatchesCollection extends CocoCollection
model: PatchModel model: PatchModel
initialize: (models, options, forModel, @status='pending') -> initialize: (models, options, forModel, @status='pending') ->
super(arguments...) super(arguments...)
@url = "#{forModel.urlRoot}/#{forModel.get('original')}/patches?status=#{@status}" @url = "#{forModel.urlRoot}/#{forModel.get('original')}/patches?status=#{@status}"

View file

@ -7,7 +7,7 @@ module.exports = class ThangNamesCollection extends CocoCollection
isCachable: false isCachable: false
constructor: (@ids) -> super() constructor: (@ids) -> super()
fetch: (options) -> fetch: (options) ->
options ?= {} options ?= {}
_.extend options, {type:'POST', data:{ids:@ids}} _.extend options, {type:'POST', data:{ids:@ids}}

View file

@ -22,7 +22,7 @@ init = ->
testing = path.startsWith '/test' testing = path.startsWith '/test'
demoing = path.startsWith '/demo' demoing = path.startsWith '/demo'
initializeServices() unless testing or demoing initializeServices() unless testing or demoing
# Set up Backbone.Mediator schemas # Set up Backbone.Mediator schemas
setUpDefinitions() setUpDefinitions()
setUpChannels() setUpChannels()
@ -35,10 +35,10 @@ init = ->
treemaExt.setup() treemaExt.setup()
$ -> init() $ -> init()
handleNormalUrls = -> handleNormalUrls = ->
# http://artsy.github.com/blog/2012/06/25/replacing-hashbang-routes-with-pushstate/ # http://artsy.github.com/blog/2012/06/25/replacing-hashbang-routes-with-pushstate/
$(document).on "click", "a[href^='/']", (event) -> $(document).on 'click', "a[href^='/']", (event) ->
href = $(event.currentTarget).attr('href') href = $(event.currentTarget).attr('href')

View file

@ -15,7 +15,7 @@ module.exports = class Angel extends CocoClass
constructor: (@shared) -> constructor: (@shared) ->
super() super()
@say 'Got my wings.' @say 'Got my wings.'
if window.navigator and (window.navigator.userAgent.search("MSIE") isnt -1 or window.navigator.appName is 'Microsoft Internet Explorer') if window.navigator and (window.navigator.userAgent.search('MSIE') isnt -1 or window.navigator.appName is 'Microsoft Internet Explorer')
@infiniteLoopIntervalDuration *= 10 # since it's so slow to serialize without transferable objects, we can't trust it @infiniteLoopIntervalDuration *= 10 # since it's so slow to serialize without transferable objects, we can't trust it
@infiniteLoopTimeoutDuration *= 10 @infiniteLoopTimeoutDuration *= 10
@abortTimeoutDuration *= 10 @abortTimeoutDuration *= 10
@ -40,7 +40,7 @@ module.exports = class Angel extends CocoClass
return if @destroyed return if @destroyed
clearTimeout @condemnTimeout clearTimeout @condemnTimeout
@condemnTimeout = _.delay @infinitelyLooped, @infiniteLoopTimeoutDuration @condemnTimeout = _.delay @infinitelyLooped, @infiniteLoopTimeoutDuration
@say "Let's give it", @infiniteLoopTimeoutDuration, "to not loop." @say 'Let\'s give it', @infiniteLoopTimeoutDuration, 'to not loop.'
@worker.postMessage func: 'reportIn' @worker.postMessage func: 'reportIn'
onWorkerMessage: (event) => onWorkerMessage: (event) =>
@ -58,7 +58,7 @@ module.exports = class Angel extends CocoClass
when 'start-load-frames' when 'start-load-frames'
clearTimeout @condemnTimeout clearTimeout @condemnTimeout
when 'report-in' when 'report-in'
@say "Worker reported in." @say 'Worker reported in.'
clearTimeout @condemnTimeout clearTimeout @condemnTimeout
when 'end-load-frames' when 'end-load-frames'
clearTimeout @condemnTimeout clearTimeout @condemnTimeout
@ -84,7 +84,7 @@ module.exports = class Angel extends CocoClass
when 'new-world' when 'new-world'
@beholdWorld event.data.serialized, event.data.goalStates @beholdWorld event.data.serialized, event.data.goalStates
when 'abort' when 'abort'
@say "Aborted.", event.data @say 'Aborted.', event.data
clearTimeout @abortTimeout clearTimeout @abortTimeout
@aborting = false @aborting = false
@running = false @running = false
@ -92,7 +92,7 @@ module.exports = class Angel extends CocoClass
@doWork() @doWork()
else else
@log "Received unsupported message:", event.data @log 'Received unsupported message:', event.data
beholdGoalStates: (goalStates) -> beholdGoalStates: (goalStates) ->
return if @aborting return if @aborting
@ -125,37 +125,37 @@ module.exports = class Angel extends CocoClass
@doWork() @doWork()
finalizePreload: -> finalizePreload: ->
@say "Finalize preload." @say 'Finalize preload.'
@worker.postMessage func: 'finalizePreload' @worker.postMessage func: 'finalizePreload'
infinitelyLooped: => infinitelyLooped: =>
@say "On infinitely looped! Aborting?", @aborting @say 'On infinitely looped! Aborting?', @aborting
return if @aborting return if @aborting
problem = type: "runtime", level: "error", id: "runtime_InfiniteLoop", message: "Code never finished. It's either really slow or has an infinite loop." problem = type: 'runtime', level: 'error', id: 'runtime_InfiniteLoop', message: 'Code never finished. It\'s either really slow or has an infinite loop.'
Backbone.Mediator.publish 'god:user-code-problem', problem: problem Backbone.Mediator.publish 'god:user-code-problem', problem: problem
Backbone.Mediator.publish 'god:infinite-loop', firstWorld: @shared.firstWorld Backbone.Mediator.publish 'god:infinite-loop', firstWorld: @shared.firstWorld
@fireWorker() @fireWorker()
doWork: -> doWork: ->
return if @aborting return if @aborting
return @say "Not initialized for work yet." unless @initialized return @say 'Not initialized for work yet.' unless @initialized
if @shared.workQueue.length if @shared.workQueue.length
@work = @shared.workQueue.shift() @work = @shared.workQueue.shift()
return _.defer @simulateSync, @work if @work.synchronous return _.defer @simulateSync, @work if @work.synchronous
@say "Running world..." @say 'Running world...'
@running = true @running = true
@shared.busyAngels.push @ @shared.busyAngels.push @
@worker.postMessage func: 'runWorld', args: @work @worker.postMessage func: 'runWorld', args: @work
clearTimeout @purgatoryTimer clearTimeout @purgatoryTimer
@say "Infinite loop timer started at interval of", @infiniteLoopIntervalDuration @say 'Infinite loop timer started at interval of', @infiniteLoopIntervalDuration
@purgatoryTimer = setInterval @testWorker, @infiniteLoopIntervalDuration @purgatoryTimer = setInterval @testWorker, @infiniteLoopIntervalDuration
else else
@say "No work to do." @say 'No work to do.'
@hireWorker() @hireWorker()
abort: -> abort: ->
return unless @worker and @running return unless @worker and @running
@say "Aborting..." @say 'Aborting...'
@running = false @running = false
@work = null @work = null
_.remove @shared.busyAngels, @ _.remove @shared.busyAngels, @
@ -172,14 +172,14 @@ module.exports = class Angel extends CocoClass
@worker = null @worker = null
clearTimeout @condemnTimeout clearTimeout @condemnTimeout
clearInterval @purgatoryTimer clearInterval @purgatoryTimer
@say "Fired worker." @say 'Fired worker.'
@initialized = false @initialized = false
@work = null @work = null
@hireWorker() if rehire @hireWorker() if rehire
hireWorker: -> hireWorker: ->
return if @worker return if @worker
@say "Hiring worker." @say 'Hiring worker.'
@worker = new Worker @shared.workerCode @worker = new Worker @shared.workerCode
@worker.addEventListener 'message', @onWorkerMessage @worker.addEventListener 'message', @onWorkerMessage
@worker.creationTime = new Date() @worker.creationTime = new Date()
@ -199,7 +199,7 @@ module.exports = class Angel extends CocoClass
testWorld.setGoalManager testGM testWorld.setGoalManager testGM
@doSimulateWorld work @doSimulateWorld work
console?.profileEnd?() if imitateIE9? console?.profileEnd?() if imitateIE9?
console.log "Construction:", (work.t1 - work.t0).toFixed(0), "ms. Simulation:", (work.t2 - work.t1).toFixed(0), "ms --", ((work.t2 - work.t1) / testWorld.frames.length).toFixed(3), "ms per frame, profiled." console.log 'Construction:', (work.t1 - work.t0).toFixed(0), 'ms. Simulation:', (work.t2 - work.t1).toFixed(0), 'ms --', ((work.t2 - work.t1) / testWorld.frames.length).toFixed(3), 'ms per frame, profiled.'
# If performance was really a priority in IE9, we would rework things to be able to skip this step. # If performance was really a priority in IE9, we would rework things to be able to skip this step.
goalStates = testGM?.getGoalStates() goalStates = testGM?.getGoalStates()
@ -212,7 +212,7 @@ module.exports = class Angel extends CocoClass
doSimulateWorld: (work) -> doSimulateWorld: (work) ->
work.t1 = now() work.t1 = now()
Math.random = work.testWorld.rand.randf # so user code is predictable Math.random = work.testWorld.rand.randf # so user code is predictable
Aether.replaceBuiltin("Math", Math) Aether.replaceBuiltin('Math', Math)
i = 0 i = 0
while i < work.testWorld.totalFrames while i < work.testWorld.totalFrames
frame = work.testWorld.getFrame i++ frame = work.testWorld.getFrame i++

View file

@ -6,17 +6,17 @@
# super() # super()
# @indexLists = [] # @indexLists = []
# @initClone() # @initClone()
# #
# initClone: () -> # initClone: () ->
# @target = AsyncCloner.cloneToDepth(@source, @depth) # @target = AsyncCloner.cloneToDepth(@source, @depth)
# @indexLists = [_.keys(@target)] if _.isObject @target # @indexLists = [_.keys(@target)] if _.isObject @target
# #
# @cloneToDepth: (value, depth) -> # @cloneToDepth: (value, depth) ->
# value = _.clone(value) # value = _.clone(value)
# return value unless depth and _.isObject value # return value unless depth and _.isObject value
# value[key] = @cloneToDepth(value[key], depth-1) for key in _.keys value # value[key] = @cloneToDepth(value[key], depth-1) for key in _.keys value
# value # value
# #
# clone: -> # clone: ->
# while @indexLists.length # while @indexLists.length
# #console.log 'Clone loop:', JSON.stringify @indexLists # #console.log 'Clone loop:', JSON.stringify @indexLists
@ -25,7 +25,7 @@
# @cloneOne() # @cloneOne()
# @moveIndexForwardOne() # @moveIndexForwardOne()
# break if @done() or @timeToSleep() # break if @done() or @timeToSleep()
# #
# moveIndexForward: -> # moveIndexForward: ->
# while @indexLists.length # while @indexLists.length
# nextValue = @getNextValue() # nextValue = @getNextValue()
@ -34,7 +34,7 @@
# # push a new list if it's a collection # # push a new list if it's a collection
# @indexLists.push _.keys(nextValue) # @indexLists.push _.keys(nextValue)
# continue # continue
# else # else
# break # we done, the next value needs to be deep cloned # break # we done, the next value needs to be deep cloned
# #console.log 'Skipping:', @getNextPath() # #console.log 'Skipping:', @getNextPath()
# @moveIndexForwardOne() # move past this value otherwise # @moveIndexForwardOne() # move past this value otherwise
@ -44,15 +44,15 @@
# value = @target # value = @target
# value = value[indexList[0]] for indexList in @indexLists # value = value[indexList[0]] for indexList in @indexLists
# value # value
# #
# getNextParent: -> # getNextParent: ->
# parent = @target # parent = @target
# parent = parent[indexList[0]] for indexList in @indexLists[...-1] # parent = parent[indexList[0]] for indexList in @indexLists[...-1]
# parent # parent
# #
# getNextPath: -> # getNextPath: ->
# (indexList[0] for indexList in @indexLists when indexList.length).join '.' # (indexList[0] for indexList in @indexLists when indexList.length).join '.'
# #
# moveIndexForwardOne: -> # moveIndexForwardOne: ->
# @indexLists[@indexLists.length-1].shift() # move the index forward one # @indexLists[@indexLists.length-1].shift() # move the index forward one
# # if we reached the end of an index list, trim down through all finished lists # # if we reached the end of an index list, trim down through all finished lists
@ -69,7 +69,7 @@
# #console.log 'Deep Cloned:', @getNextPath() # #console.log 'Deep Cloned:', @getNextPath()
# #
# done: -> not @indexLists.length # done: -> not @indexLists.length
# #
# timeToSleep: -> false # timeToSleep: -> false
@ -79,4 +79,4 @@
Clone that one, popping it off the list. Clone that one, popping it off the list.
If the last list is now empty, pop that list and every subsequent list if needed. If the last list is now empty, pop that list and every subsequent list if needed.
Check for doneness, or timeout. Check for doneness, or timeout.
### ###

View file

@ -1,11 +1,11 @@
CocoClass = require 'lib/CocoClass' CocoClass = require 'lib/CocoClass'
cache = {} cache = {}
{me} = require('lib/auth') {me} = require 'lib/auth'
# Top 20 obscene words (plus 'fiddlesticks') will trigger swearing Simlish with *beeps*. # Top 20 obscene words (plus 'fiddlesticks') will trigger swearing Simlish with *beeps*.
# Didn't like leaving so much profanity lying around in the source, so rot13'd. # Didn't like leaving so much profanity lying around in the source, so rot13'd.
rot13 = (s) -> s.replace /[A-z]/g, (c) -> String.fromCharCode c.charCodeAt(0) + (if c.toUpperCase() <= "M" then 13 else -13) rot13 = (s) -> s.replace /[A-z]/g, (c) -> String.fromCharCode c.charCodeAt(0) + (if c.toUpperCase() <= 'M' then 13 else -13)
swears = (rot13 s for s in ["nefrubyr", "nffubyr", "onfgneq", "ovgpu", "oybbql", "obyybpxf", "ohttre", "pbpx", "penc", "phag", "qnza", "qnea", "qvpx", "qbhpur", "snt", "shpx", "cvff", "chffl", "fuvg", "fyhg", "svqqyrfgvpxf"]) swears = (rot13 s for s in ['nefrubyr', 'nffubyr', 'onfgneq', 'ovgpu', 'oybbql', 'obyybpxf', 'ohttre', 'pbpx', 'penc', 'phag', 'qnza', 'qnea', 'qvpx', 'qbhpur', 'snt', 'shpx', 'cvff', 'chffl', 'fuvg', 'fyhg', 'svqqyrfgvpxf'])
createjs.Sound.registerPlugins([createjs.WebAudioPlugin, createjs.FlashPlugin, createjs.HTMLAudioPlugin]) createjs.Sound.registerPlugins([createjs.WebAudioPlugin, createjs.FlashPlugin, createjs.HTMLAudioPlugin])
@ -129,7 +129,7 @@ class AudioPlayer extends CocoClass
console.error 'Could not load sound', e console.error 'Could not load sound', e
notifyProgressChanged: -> notifyProgressChanged: ->
Backbone.Mediator.publish('audio-player:loaded', {sender:@}) Backbone.Mediator.publish('audio-player:loaded', {sender: @})
getStatus: (src) -> getStatus: (src) ->
return cache[src] or null return cache[src] or null

View file

@ -26,9 +26,9 @@ module.exports = Bus = class Bus extends CocoClass
@notifyStateChanges() @notifyStateChanges()
connect: -> connect: ->
Backbone.Mediator.publish 'bus:connecting', {bus:@} Backbone.Mediator.publish 'bus:connecting', {bus: @}
Firebase.goOnline() Firebase.goOnline()
@fireRef = new Firebase(Bus.fireHost + "/" + @docName) @fireRef = new Firebase(Bus.fireHost + '/' + @docName)
@fireRef.once 'value', @onFireOpen @fireRef.once 'value', @onFireOpen
onFireOpen: (snapshot) => onFireOpen: (snapshot) =>
@ -36,7 +36,7 @@ module.exports = Bus = class Bus extends CocoClass
console.log("Leaving '#{@docName}' because class has been destroyed.") console.log("Leaving '#{@docName}' because class has been destroyed.")
return return
@init() @init()
Backbone.Mediator.publish 'bus:connected', {bus:@} Backbone.Mediator.publish 'bus:connected', {bus: @}
disconnect: -> disconnect: ->
Firebase.goOffline() Firebase.goOffline()
@ -49,7 +49,7 @@ module.exports = Bus = class Bus extends CocoClass
@myConnection?.off() @myConnection?.off()
@myConnection = null @myConnection = null
@joined = false @joined = false
Backbone.Mediator.publish 'bus:disconnected', {bus:@} Backbone.Mediator.publish 'bus:disconnected', {bus: @}
init: -> init: ->
""" """
@ -59,7 +59,7 @@ module.exports = Bus = class Bus extends CocoClass
@firePlayersRef = @fireRef.child('players') @firePlayersRef = @fireRef.child('players')
@join() @join()
@listenForChanges() @listenForChanges()
@sendMessage("/me joined.", true) @sendMessage('/me joined.', true)
join: -> join: ->
@joined = true @joined = true
@ -75,13 +75,13 @@ module.exports = Bus = class Bus extends CocoClass
@firePlayersRef.on 'child_changed', @onPlayerChanged @firePlayersRef.on 'child_changed', @onPlayerChanged
onChatAdded: (snapshot) => onChatAdded: (snapshot) =>
Backbone.Mediator.publish('bus:new-message', {message:snapshot.val(), bus:@}) Backbone.Mediator.publish('bus:new-message', {message: snapshot.val(), bus: @})
onPlayerJoined: (snapshot) => onPlayerJoined: (snapshot) =>
player = snapshot.val() player = snapshot.val()
return unless player.connected return unless player.connected
@players[player.id] = player @players[player.id] = player
Backbone.Mediator.publish('bus:player-joined', {player:player, bus:@}) Backbone.Mediator.publish('bus:player-joined', {player: player, bus: @})
onPlayerLeft: (snapshot) => onPlayerLeft: (snapshot) =>
val = snapshot.val() val = snapshot.val()
@ -89,7 +89,7 @@ module.exports = Bus = class Bus extends CocoClass
player = @players[val.id] player = @players[val.id]
return unless player return unless player
delete @players[player.id] delete @players[player.id]
Backbone.Mediator.publish('bus:player-left', {player:player, bus:@}) Backbone.Mediator.publish('bus:player-left', {player: player, bus: @})
onPlayerChanged: (snapshot) => onPlayerChanged: (snapshot) =>
player = snapshot.val() player = snapshot.val()
@ -97,7 +97,7 @@ module.exports = Bus = class Bus extends CocoClass
@players[player.id] = player @players[player.id] = player
@onPlayerLeft(snapshot) if wasConnected and not player.connected @onPlayerLeft(snapshot) if wasConnected and not player.connected
@onPlayerJoined(snapshot) if player.connected and not wasConnected @onPlayerJoined(snapshot) if player.connected and not wasConnected
Backbone.Mediator.publish('bus:player-states-changed', {states:@players, bus:@}) Backbone.Mediator.publish('bus:player-states-changed', {states: @players, bus: @})
onMeSynced: => onMeSynced: =>
@myConnection?.child('name').set(me.get('name')) @myConnection?.child('name').set(me.get('name'))
@ -118,9 +118,9 @@ module.exports = Bus = class Bus extends CocoClass
sendMessage: (content, system=false) -> sendMessage: (content, system=false) ->
MAX_MESSAGE_LENGTH = 400 MAX_MESSAGE_LENGTH = 400
message = message =
content:content[... MAX_MESSAGE_LENGTH] content: content[... MAX_MESSAGE_LENGTH]
authorName:me.displayName() authorName: me.displayName()
authorID:me.id authorID: me.id
dateMade: new Date() dateMade: new Date()
message.system = system if system message.system = system if system
@fireChatRef.push(message) @fireChatRef.push(message)
@ -128,7 +128,7 @@ module.exports = Bus = class Bus extends CocoClass
# TEARDOWN # TEARDOWN
destroy: -> destroy: ->
@sendMessage("/me left.", true) if @joined @sendMessage('/me left.', true) if @joined
delete Bus.activeBuses[@docName] if @docName of Bus.activeBuses delete Bus.activeBuses[@docName] if @docName of Bus.activeBuses
@disconnect() @disconnect()
super() super()

View file

@ -9,7 +9,7 @@ module.exports = class CocoClass
@nicksUsed: {} @nicksUsed: {}
@remainingNicks: [] @remainingNicks: []
@nextNick: -> @nextNick: ->
return (@name or "CocoClass") + " " + classCount unless @nicks.length return (@name or 'CocoClass') + ' ' + classCount unless @nicks.length
@remainingNicks = if @remainingNicks.length then @remainingNicks else @nicks.slice() @remainingNicks = if @remainingNicks.length then @remainingNicks else @nicks.slice()
baseNick = @remainingNicks.splice(Math.floor(Math.random() * @remainingNicks.length), 1)[0] baseNick = @remainingNicks.splice(Math.floor(Math.random() * @remainingNicks.length), 1)[0]
i = 0 i = 0

View file

@ -44,7 +44,7 @@ module.exports = FacebookHandler = class FacebookHandler extends CocoClass
me.set('gender', r.gender) if r.gender me.set('gender', r.gender) if r.gender
me.set('email', r.email) if r.email me.set('email', r.email) if r.email
me.set('facebookID', r.id) if r.id me.set('facebookID', r.id) if r.id
Backbone.Mediator.publish('logging-in-with-facebook') Backbone.Mediator.publish('logging-in-with-facebook')
window.tracker?.trackEvent 'Facebook Login' window.tracker?.trackEvent 'Facebook Login'
window.tracker?.identify() window.tracker?.identify()

View file

@ -14,8 +14,8 @@ userPropsToSave =
fieldsToFetch = 'displayName,gender,image,name(familyName,givenName),id' fieldsToFetch = 'displayName,gender,image,name(familyName,givenName),id'
plusURL = '/plus/v1/people/me?fields='+fieldsToFetch plusURL = '/plus/v1/people/me?fields='+fieldsToFetch
revokeUrl = 'https://accounts.google.com/o/oauth2/revoke?token=' revokeUrl = 'https://accounts.google.com/o/oauth2/revoke?token='
clientID = "800329290710-j9sivplv2gpcdgkrsis9rff3o417mlfa.apps.googleusercontent.com" clientID = '800329290710-j9sivplv2gpcdgkrsis9rff3o417mlfa.apps.googleusercontent.com'
scope = "https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/userinfo.email" scope = 'https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/userinfo.email'
module.exports = GPlusHandler = class GPlusHandler extends CocoClass module.exports = GPlusHandler = class GPlusHandler extends CocoClass
constructor: -> constructor: ->
@ -32,7 +32,7 @@ module.exports = GPlusHandler = class GPlusHandler extends CocoClass
# We need to check the current state, given our access token # We need to check the current state, given our access token
gapi.auth.setToken 'token', @accessToken gapi.auth.setToken 'token', @accessToken
session_state = @accessToken.session_state session_state = @accessToken.session_state
gapi.auth.checkSessionState({client_id:clientID, session_state:session_state}, @onCheckedSessionState) gapi.auth.checkSessionState({client_id: clientID, session_state: session_state}, @onCheckedSessionState)
else else
# If we ran checkSessionState, it might return true, that the user is logged into Google, but has not authorized us # If we ran checkSessionState, it might return true, that the user is logged into Google, but has not authorized us
@loggedIn = false @loggedIn = false
@ -57,7 +57,7 @@ module.exports = GPlusHandler = class GPlusHandler extends CocoClass
# email and profile data loaded separately # email and profile data loaded separately
@responsesComplete = 0 @responsesComplete = 0
gapi.client.request(path:plusURL, callback:@onPersonEntityReceived) gapi.client.request(path: plusURL, callback: @onPersonEntityReceived)
gapi.client.load('oauth2', 'v2', => gapi.client.load('oauth2', 'v2', =>
gapi.client.oauth2.userinfo.get().execute(@onEmailReceived)) gapi.client.oauth2.userinfo.get().execute(@onEmailReceived))
@ -108,7 +108,7 @@ module.exports = GPlusHandler = class GPlusHandler extends CocoClass
loadFriends: (friendsCallback) -> loadFriends: (friendsCallback) ->
return friendsCallback() unless @loggedIn return friendsCallback() unless @loggedIn
expiresIn = if @accessToken then parseInt(@accessToken.expires_at) - new Date().getTime()/1000 else -1 expiresIn = if @accessToken then parseInt(@accessToken.expires_at) - new Date().getTime()/1000 else -1
onReauthorized = => gapi.client.request({path:'/plus/v1/people/me/people/visible', callback: friendsCallback}) onReauthorized = => gapi.client.request({path: '/plus/v1/people/me/people/visible', callback: friendsCallback})
if expiresIn < 0 if expiresIn < 0
# TODO: this tries to open a popup window, which might not ever finish or work, so the callback may never be called. # TODO: this tries to open a popup window, which might not ever finish or work, so the callback may never be called.
@reauthorize() @reauthorize()

View file

@ -8,7 +8,7 @@ CocoClass = require 'lib/CocoClass'
Angel = require 'lib/Angel' Angel = require 'lib/Angel'
module.exports = class God extends CocoClass module.exports = class God extends CocoClass
@nicks: ['Athena', 'Baldr', 'Crom', 'Dagr', 'Eris', 'Freyja', 'Great Gish', 'Hades', 'Ishtar', 'Janus', 'Khronos', 'Loki', 'Marduk', 'Negafook', 'Odin', 'Poseidon', 'Quetzalcoatl', 'Ra', 'Shiva', 'Thor', 'Umvelinqangi', 'Týr', 'Vishnu', 'Wepwawet', 'Xipe Totec', 'Yahweh', 'Zeus', '上帝', 'Tiamat', '盘古', 'Phoebe', 'Artemis', 'Osiris', "嫦娥", 'Anhur', 'Teshub', 'Enlil', 'Perkele', 'Chaos', 'Hera', 'Iris', 'Theia', 'Uranus', 'Stribog', 'Sabazios', 'Izanagi', 'Ao', 'Tāwhirimātea', 'Tengri', 'Inmar', 'Torngarsuk', 'Centzonhuitznahua', 'Hunab Ku', 'Apollo', 'Helios', 'Thoth', 'Hyperion', 'Alectrona', 'Eos', 'Mitra', 'Saranyu', 'Freyr', 'Koyash', 'Atropos', 'Clotho', 'Lachesis', 'Tyche', 'Skuld', 'Urðr', 'Verðandi', 'Camaxtli', 'Huhetotl', 'Set', 'Anu', 'Allah', 'Anshar', 'Hermes', 'Lugh', 'Brigit', 'Manannan Mac Lir', 'Persephone', 'Mercury', 'Venus', 'Mars', 'Azrael', 'He-Man', 'Anansi', 'Issek', 'Mog', 'Kos', 'Amaterasu Omikami', 'Raijin', 'Susanowo', 'Blind Io', 'The Lady', 'Offler', 'Ptah', 'Anubis', 'Ereshkigal', 'Nergal', 'Thanatos', 'Macaria', 'Angelos', 'Erebus', 'Hecate', 'Hel', 'Orcus', 'Ishtar-Deela Nakh', 'Prometheus', 'Hephaestos', 'Sekhmet', 'Ares', 'Enyo', 'Otrera', 'Pele', 'Hadúr', 'Hachiman', 'Dayisun Tngri', 'Ullr', 'Lua', 'Minerva'] @nicks: ['Athena', 'Baldr', 'Crom', 'Dagr', 'Eris', 'Freyja', 'Great Gish', 'Hades', 'Ishtar', 'Janus', 'Khronos', 'Loki', 'Marduk', 'Negafook', 'Odin', 'Poseidon', 'Quetzalcoatl', 'Ra', 'Shiva', 'Thor', 'Umvelinqangi', 'Týr', 'Vishnu', 'Wepwawet', 'Xipe Totec', 'Yahweh', 'Zeus', '上帝', 'Tiamat', '盘古', 'Phoebe', 'Artemis', 'Osiris', '嫦娥', 'Anhur', 'Teshub', 'Enlil', 'Perkele', 'Chaos', 'Hera', 'Iris', 'Theia', 'Uranus', 'Stribog', 'Sabazios', 'Izanagi', 'Ao', 'Tāwhirimātea', 'Tengri', 'Inmar', 'Torngarsuk', 'Centzonhuitznahua', 'Hunab Ku', 'Apollo', 'Helios', 'Thoth', 'Hyperion', 'Alectrona', 'Eos', 'Mitra', 'Saranyu', 'Freyr', 'Koyash', 'Atropos', 'Clotho', 'Lachesis', 'Tyche', 'Skuld', 'Urðr', 'Verðandi', 'Camaxtli', 'Huhetotl', 'Set', 'Anu', 'Allah', 'Anshar', 'Hermes', 'Lugh', 'Brigit', 'Manannan Mac Lir', 'Persephone', 'Mercury', 'Venus', 'Mars', 'Azrael', 'He-Man', 'Anansi', 'Issek', 'Mog', 'Kos', 'Amaterasu Omikami', 'Raijin', 'Susanowo', 'Blind Io', 'The Lady', 'Offler', 'Ptah', 'Anubis', 'Ereshkigal', 'Nergal', 'Thanatos', 'Macaria', 'Angelos', 'Erebus', 'Hecate', 'Hel', 'Orcus', 'Ishtar-Deela Nakh', 'Prometheus', 'Hephaestos', 'Sekhmet', 'Ares', 'Enyo', 'Otrera', 'Pele', 'Hadúr', 'Hachiman', 'Dayisun Tngri', 'Ullr', 'Lua', 'Minerva']
subscriptions: subscriptions:
'tome:cast-spells': 'onTomeCast' 'tome:cast-spells': 'onTomeCast'
@ -98,7 +98,7 @@ module.exports = class God extends CocoClass
retrieveValueFromFrame: (args) => retrieveValueFromFrame: (args) =>
return if @destroyed return if @destroyed
return unless args.thangID and args.spellID and args.variableChain return unless args.thangID and args.spellID and args.variableChain
return console.error "Tried to retrieve debug value with no currentUserCodeMap" unless @currentUserCodeMap return console.error 'Tried to retrieve debug value with no currentUserCodeMap' unless @currentUserCodeMap
@debugWorker ?= @createDebugWorker() @debugWorker ?= @createDebugWorker()
args.frame ?= @angelsShare.world.age / @angelsShare.world.dt args.frame ?= @angelsShare.world.age / @angelsShare.world.dt
@debugWorker.postMessage @debugWorker.postMessage

View file

@ -43,7 +43,7 @@ module.exports = class LevelBus extends Bus
incrementSessionPlaytime: => incrementSessionPlaytime: =>
if @playerIsIdle then return if @playerIsIdle then return
@changedSessionProperties.playtime = true @changedSessionProperties.playtime = true
@session.set("playtime",@session.get("playtime") + 1) @session.set('playtime', @session.get('playtime') + 1)
onPoint: -> onPoint: ->
return true unless @session?.get('multiplayer') return true unless @session?.get('multiplayer')
@ -123,7 +123,7 @@ module.exports = class LevelBus extends Bus
@changedSessionProperties.teamSpells = true @changedSessionProperties.teamSpells = true
@session.set({'teamSpells': @teamSpellMap}) @session.set({'teamSpells': @teamSpellMap})
@saveSession() @saveSession()
if spellTeam is me.team or spellTeam is "common" if spellTeam is me.team or spellTeam is 'common'
@onSpellChanged e # Save the new spell to the session, too. @onSpellChanged e # Save the new spell to the session, too.
onScriptStateChanged: (e) -> onScriptStateChanged: (e) ->
@ -233,7 +233,7 @@ module.exports = class LevelBus extends Bus
# since updates are coming fast and loose for session objects # since updates are coming fast and loose for session objects
# don't let what the server returns overwrite changes since the save began # don't let what the server returns overwrite changes since the save began
tempSession = new LevelSession _id:@session.id tempSession = new LevelSession _id: @session.id
tempSession.save(patch, {patch: true}) tempSession.save(patch, {patch: true})
destroy: -> destroy: ->

View file

@ -48,7 +48,7 @@ module.exports = class LevelLoader extends CocoClass
# Apparently the jingle, when it tries to play immediately during all this loading, you can't hear it. # Apparently the jingle, when it tries to play immediately during all this loading, you can't hear it.
# Add the timeout to fix this weird behavior. # Add the timeout to fix this weird behavior.
f = -> f = ->
jingles = ["ident_1", "ident_2"] jingles = ['ident_1', 'ident_2']
AudioPlayer.playInterfaceSound jingles[Math.floor Math.random() * jingles.length] AudioPlayer.playInterfaceSound jingles[Math.floor Math.random() * jingles.length]
setTimeout f, 500 setTimeout f, 500
@ -63,7 +63,7 @@ module.exports = class LevelLoader extends CocoClass
url += "?team=#{@team}" if @team url += "?team=#{@team}" if @team
session = new LevelSession().setURL url session = new LevelSession().setURL url
@sessionResource = @supermodel.loadModel(session, 'level_session', {cache:false}) @sessionResource = @supermodel.loadModel(session, 'level_session', {cache: false})
@session = @sessionResource.model @session = @sessionResource.model
@session.once 'sync', -> @url = -> '/db/level.session/' + @id @session.once 'sync', -> @url = -> '/db/level.session/' + @id
@ -151,7 +151,7 @@ module.exports = class LevelLoader extends CocoClass
continue if thangType.isFullyLoaded() continue if thangType.isFullyLoaded()
thangType.fetch() thangType.fetch()
thangType = @supermodel.loadModel(thangType, 'thang').model thangType = @supermodel.loadModel(thangType, 'thang').model
res = @supermodel.addSomethingResource "sprite_sheet", 5 res = @supermodel.addSomethingResource 'sprite_sheet', 5
res.thangType = thangType res.thangType = thangType
res.markLoading() res.markLoading()
@spriteSheetsToBuild.push res @spriteSheetsToBuild.push res
@ -246,7 +246,7 @@ module.exports = class LevelLoader extends CocoClass
break break
unless @teamConfigs unless @teamConfigs
# Hack: pulled from Alliance System code. TODO: put in just one place. # Hack: pulled from Alliance System code. TODO: put in just one place.
@teamConfigs = {"humans":{"superteam":"humans","color":{"hue":0,"saturation":0.75,"lightness":0.5},"playable":true},"ogres":{"superteam":"ogres","color":{"hue":0.66,"saturation":0.75,"lightness":0.5},"playable":false},"neutral":{"superteam":"neutral","color":{"hue":0.33,"saturation":0.75,"lightness":0.5}}} @teamConfigs = {'humans': {'superteam': 'humans', 'color': {'hue': 0, 'saturation': 0.75, 'lightness': 0.5}, 'playable': true}, 'ogres': {'superteam': 'ogres', 'color': {'hue': 0.66, 'saturation': 0.75, 'lightness': 0.5}, 'playable': false}, 'neutral': {'superteam': 'neutral', 'color': {'hue': 0.33, 'saturation': 0.75, 'lightness': 0.5}}}
@teamConfigs @teamConfigs
buildSpriteSheet: (thangType, options) -> buildSpriteSheet: (thangType, options) ->
@ -263,13 +263,13 @@ module.exports = class LevelLoader extends CocoClass
@world.levelSessionIDs = if @opponentSessionID then [@sessionID, @opponentSessionID] else [@sessionID] @world.levelSessionIDs = if @opponentSessionID then [@sessionID, @opponentSessionID] else [@sessionID]
serializedLevel = @level.serialize(@supermodel) serializedLevel = @level.serialize(@supermodel)
@world.loadFromLevel serializedLevel, false @world.loadFromLevel serializedLevel, false
console.log "World has been initialized from level loader." console.log 'World has been initialized from level loader.'
# Initial Sound Loading # Initial Sound Loading
loadAudio: -> loadAudio: ->
return if @headless return if @headless
AudioPlayer.preloadInterfaceSounds ["victory"] AudioPlayer.preloadInterfaceSounds ['victory']
loadLevelSounds: -> loadLevelSounds: ->
return if @headless return if @headless

View file

@ -12,20 +12,20 @@ module.exports = LinkedInHandler = class LinkedInHandler extends CocoClass
'linkedin-loaded': 'onLinkedInLoaded' 'linkedin-loaded': 'onLinkedInLoaded'
onLinkedInLoaded: (e) -> onLinkedInLoaded: (e) ->
IN.Event.on IN, "auth", @onLinkedInAuth IN.Event.on IN, 'auth', @onLinkedInAuth
onLinkedInAuth: (e) => console.log "Authorized with LinkedIn" onLinkedInAuth: (e) => console.log 'Authorized with LinkedIn'
constructEmployerAgreementObject: (cb) => constructEmployerAgreementObject: (cb) =>
IN.API.Profile("me") IN.API.Profile('me')
.fields(["positions","public-profile-url","id","first-name","last-name","email-address"]) .fields(['positions', 'public-profile-url', 'id', 'first-name', 'last-name', 'email-address'])
.error(cb) .error(cb)
.result (profiles) => .result (profiles) =>
cb null, profiles.values[0] cb null, profiles.values[0]
getProfileData: (cb) => getProfileData: (cb) =>
IN.API.Profile("me") IN.API.Profile('me')
.fields(["formatted-name","educations","skills","headline","summary","positions","public-profile-url"]) .fields(['formatted-name', 'educations', 'skills', 'headline', 'summary', 'positions', 'public-profile-url'])
.error(cb) .error(cb)
.result (profiles) => .result (profiles) =>
cb null, profiles.values[0] cb null, profiles.values[0]

View file

@ -7,9 +7,9 @@ class NameLoader extends CocoClass
toLoad = _.uniq (id for id in ids when not namesCache[id]) toLoad = _.uniq (id for id in ids when not namesCache[id])
return false unless toLoad.length return false unless toLoad.length
jqxhrOptions = { jqxhrOptions = {
url: '/db/user/x/names', url: '/db/user/x/names',
type:'POST', type: 'POST',
data:{ids:toLoad}, data: {ids: toLoad},
success: @loadedNames success: @loadedNames
} }
@ -17,7 +17,7 @@ class NameLoader extends CocoClass
loadedNames: (newNames) => loadedNames: (newNames) =>
_.extend namesCache, newNames _.extend namesCache, newNames
getName: (id) -> namesCache[id]?.name or id getName: (id) -> namesCache[id]?.name or id
module.exports = new NameLoader() module.exports = new NameLoader()

View file

@ -1,4 +1,4 @@
gplusClientID = "800329290710-j9sivplv2gpcdgkrsis9rff3o417mlfa.apps.googleusercontent.com" gplusClientID = '800329290710-j9sivplv2gpcdgkrsis9rff3o417mlfa.apps.googleusercontent.com'
go = (path) -> -> @routeDirectly path, arguments go = (path) -> -> @routeDirectly path, arguments
@ -75,16 +75,16 @@ module.exports = class CocoRouter extends Backbone.Router
gapi.plusone.go?() # Handles +1 button gapi.plusone.go?() # Handles +1 button
for gplusButton in $('.gplus-login-button') for gplusButton in $('.gplus-login-button')
params = { params = {
callback:"signinCallback", callback: 'signinCallback',
clientid:gplusClientID, clientid: gplusClientID,
cookiepolicy:"single_host_origin", cookiepolicy: 'single_host_origin',
scope:"https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/userinfo.email", scope: 'https://www.googleapis.com/auth/plus.login https://www.googleapis.com/auth/userinfo.email',
height: "short", height: 'short',
} }
if gapi.signin?.render if gapi.signin?.render
gapi.signin.render(gplusButton, params) gapi.signin.render(gplusButton, params)
else else
console.warn "Didn't have gapi.signin to render G+ login button. (DoNotTrackMe extension?)" console.warn 'Didn\'t have gapi.signin to render G+ login button. (DoNotTrackMe extension?)'
getViewFromCache: (route) -> getViewFromCache: (route) ->
if route of @cache if route of @cache
@ -106,7 +106,7 @@ module.exports = class CocoRouter extends Backbone.Router
getView: (route, suffix='_view') -> getView: (route, suffix='_view') ->
# iteratively breaks down the url pieces looking for the view # iteratively breaks down the url pieces looking for the view
# passing the broken off pieces as args. This way views like "resource/14394893" # passing the broken off pieces as args. This way views like 'resource/14394893'
# will get passed to the resource view with arg '14394893' # will get passed to the resource view with arg '14394893'
pieces = _.string.words(route, '/') pieces = _.string.words(route, '/')
split = Math.max(1, pieces.length-1) split = Math.max(1, pieces.length-1)

View file

@ -5,6 +5,6 @@ namesCache = {}
class SystemNameLoader extends CocoClass class SystemNameLoader extends CocoClass
getName: (id) -> namesCache[id]?.name getName: (id) -> namesCache[id]?.name
setName: (system) -> namesCache[system.get('original')] = {name:system.get('name')} setName: (system) -> namesCache[system.get('original')] = {name: system.get('name')}
module.exports = new SystemNameLoader() module.exports = new SystemNameLoader()

View file

@ -5,14 +5,14 @@ debugAnalytics = false
module.exports = class Tracker module.exports = class Tracker
constructor: -> constructor: ->
if window.tracker if window.tracker
console.error "Overwrote our Tracker!", window.tracker console.error 'Overwrote our Tracker!', window.tracker
window.tracker = @ window.tracker = @
@isProduction = document.location.href.search("codecombat.com") isnt -1 @isProduction = document.location.href.search('codecombat.com') isnt -1
@identify() @identify()
@updateOlark() @updateOlark()
identify: (traits) -> identify: (traits) ->
console.log "Would identify", traits if debugAnalytics console.log 'Would identify', traits if debugAnalytics
return unless me and @isProduction and analytics? return unless me and @isProduction and analytics?
# https://segment.io/docs/methods/identify # https://segment.io/docs/methods/identify
traits ?= {} traits ?= {}
@ -23,8 +23,8 @@ module.exports = class Tracker
updateOlark: -> updateOlark: ->
return unless me and olark? return unless me and olark?
olark 'api.chat.updateVisitorStatus', snippet: ["User ID: #{me.id}"] olark 'api.chat.updateVisitorStatus', snippet: ["User ID: #{me.id}"]
return if me.get("anonymous") return if me.get('anonymous')
olark 'api.visitor.updateEmailAddress', emailAddress: me.get("email") if me.get('email') olark 'api.visitor.updateEmailAddress', emailAddress: me.get('email') if me.get('email')
olark 'api.chat.updateVisitorNickname', snippet: me.displayName() olark 'api.chat.updateVisitorNickname', snippet: me.displayName()
updatePlayState: (level, session) -> updatePlayState: (level, session) ->
@ -41,13 +41,13 @@ module.exports = class Tracker
trackPageView: -> trackPageView: ->
return unless @isProduction and analytics? return unless @isProduction and analytics?
url = Backbone.history.getFragment() url = Backbone.history.getFragment()
console.log "Going to track visit for", "/#{url}" if debugAnalytics console.log 'Going to track visit for', "/#{url}" if debugAnalytics
analytics.pageview "/#{url}" analytics.pageview "/#{url}"
trackEvent: (event, properties, includeProviders=null) => trackEvent: (event, properties, includeProviders=null) =>
console.log "Would track analytics event:", event, properties if debugAnalytics console.log 'Would track analytics event:', event, properties if debugAnalytics
return unless me and @isProduction and analytics? return unless me and @isProduction and analytics?
console.log "Going to track analytics event:", event, properties if debugAnalytics console.log 'Going to track analytics event:', event, properties if debugAnalytics
properties = properties or {} properties = properties or {}
context = {} context = {}
if includeProviders if includeProviders
@ -60,5 +60,5 @@ module.exports = class Tracker
trackTiming: (duration, category, variable, label, samplePercentage=5) -> trackTiming: (duration, category, variable, label, samplePercentage=5) ->
# https://developers.google.com/analytics/devguides/collection/gajs/gaTrackingTiming # https://developers.google.com/analytics/devguides/collection/gajs/gaTrackingTiming
return console.warn "Duration #{duration} invalid for trackTiming call." unless duration >= 0 and duration < 60 * 60 * 1000 return console.warn "Duration #{duration} invalid for trackTiming call." unless duration >= 0 and duration < 60 * 60 * 1000
console.log "Would track timing event:", arguments if debugAnalytics console.log 'Would track timing event:', arguments if debugAnalytics
window._gaq?.push ['_trackTiming', category, variable, duration, label, samplePercentage] window._gaq?.push ['_trackTiming', category, variable, duration, label, samplePercentage]

View file

@ -12,13 +12,13 @@ init = ->
me.set 'testGroupNumber', Math.floor(Math.random() * 256) me.set 'testGroupNumber', Math.floor(Math.random() * 256)
me.patch() me.patch()
Backbone.listenTo(me, 'sync', Backbone.Mediator.publish('me:synced', {me:me})) Backbone.listenTo(me, 'sync', Backbone.Mediator.publish('me:synced', {me: me}))
module.exports.createUser = (userObject, failure=backboneFailure, nextURL=null) -> module.exports.createUser = (userObject, failure=backboneFailure, nextURL=null) ->
user = new User(userObject) user = new User(userObject)
user.notyErrors = false user.notyErrors = false
user.save({}, { user.save({}, {
error: (model,jqxhr,options) -> error: (model, jqxhr, options) ->
error = parseServerError(jqxhr.responseText) error = parseServerError(jqxhr.responseText)
property = error.property if error.property property = error.property if error.property
if jqxhr.status is 409 and property is 'name' if jqxhr.status is 409 and property is 'name'
@ -34,14 +34,14 @@ module.exports.createUserWithoutReload = (userObject, failure=backboneFailure) -
user.save({}, { user.save({}, {
error: failure error: failure
success: -> success: ->
Backbone.Mediator.publish("created-user-without-reload") Backbone.Mediator.publish('created-user-without-reload')
}) })
module.exports.loginUser = (userObject, failure=genericFailure) -> module.exports.loginUser = (userObject, failure=genericFailure) ->
jqxhr = $.post('/auth/login', jqxhr = $.post('/auth/login',
{ {
username:userObject.email, username: userObject.email,
password:userObject.password password: userObject.password
}, },
(model) -> window.location.reload() (model) -> window.location.reload()
) )

View file

@ -2,8 +2,8 @@ module.exports.sendContactMessage = (contactMessageObject, modal) ->
modal.find('.sending-indicator').show() modal.find('.sending-indicator').show()
jqxhr = $.post '/contact', contactMessageObject, (response) -> jqxhr = $.post '/contact', contactMessageObject, (response) ->
modal.find('.sending-indicator').hide() modal.find('.sending-indicator').hide()
modal.find('#contact-message').val("Thanks!") modal.find('#contact-message').val('Thanks!')
_.delay -> _.delay ->
modal.find('#contact-message').val("") modal.find('#contact-message').val('')
modal.modal 'hide' modal.modal 'hide'
, 1000 , 1000

View file

@ -1,23 +1,23 @@
SystemNameLoader = require 'lib/SystemNameLoader' SystemNameLoader = require 'lib/SystemNameLoader'
### ###
Good-to-knows: Good-to-knows:
dataPath: an array of keys that walks you up a JSON object that's being patched dataPath: an array of keys that walks you up a JSON object that's being patched
ex: ['scripts', 0, 'description'] ex: ['scripts', 0, 'description']
deltaPath: an array of keys that walks you up a JSON Diff Patch object. deltaPath: an array of keys that walks you up a JSON Diff Patch object.
ex: ['scripts', '_0', 'description'] ex: ['scripts', '_0', 'description']
### ###
module.exports.expandDelta = (delta, left, schema) -> module.exports.expandDelta = (delta, left, schema) ->
flattenedDeltas = flattenDelta(delta) flattenedDeltas = flattenDelta(delta)
(expandFlattenedDelta(fd, left, schema) for fd in flattenedDeltas) (expandFlattenedDelta(fd, left, schema) for fd in flattenedDeltas)
flattenDelta = (delta, dataPath=null, deltaPath=null) -> flattenDelta = (delta, dataPath=null, deltaPath=null) ->
# takes a single jsondiffpatch delta and returns an array of objects with # takes a single jsondiffpatch delta and returns an array of objects with
return [] unless delta return [] unless delta
dataPath ?= [] dataPath ?= []
deltaPath ?= [] deltaPath ?= []
return [{dataPath:dataPath, deltaPath: deltaPath, o:delta}] if _.isArray delta return [{dataPath: dataPath, deltaPath: deltaPath, o: delta}] if _.isArray delta
results = [] results = []
affectingArray = delta._t is 'a' affectingArray = delta._t is 'a'
@ -27,12 +27,12 @@ flattenDelta = (delta, dataPath=null, deltaPath=null) ->
results = results.concat flattenDelta( results = results.concat flattenDelta(
childDelta, dataPath.concat([dataIndex]), deltaPath.concat([deltaIndex])) childDelta, dataPath.concat([dataIndex]), deltaPath.concat([deltaIndex]))
results results
expandFlattenedDelta = (delta, left, schema) -> expandFlattenedDelta = (delta, left, schema) ->
# takes a single flattened delta and converts into an object that can be # takes a single flattened delta and converts into an object that can be
# easily formatted into something human readable. # easily formatted into something human readable.
delta.action = '???' delta.action = '???'
o = delta.o # the raw jsondiffpatch delta o = delta.o # the raw jsondiffpatch delta
@ -80,58 +80,72 @@ expandFlattenedDelta = (delta, left, schema) ->
humanPath.push humanKey humanPath.push humanKey
parentLeft = childLeft parentLeft = childLeft
parentSchema = childSchema parentSchema = childSchema
delta.humanPath = humanPath.join(' :: ') delta.humanPath = humanPath.join(' :: ')
delta.schema = childSchema delta.schema = childSchema
delta.left = childLeft delta.left = childLeft
delta.right = jsondiffpatch.patch childLeft, delta.o unless delta.action is 'moved-index' delta.right = jsondiffpatch.patch childLeft, delta.o unless delta.action is 'moved-index'
delta delta
module.exports.makeJSONDiffer = -> module.exports.makeJSONDiffer = ->
hasher = (obj) -> obj.name || obj.id || obj._id || JSON.stringify(_.keys(obj)) hasher = (obj) -> obj.name || obj.id || obj._id || JSON.stringify(_.keys(obj))
jsondiffpatch.create({objectHash:hasher}) jsondiffpatch.create({objectHash: hasher})
module.exports.getConflicts = (headDeltas, pendingDeltas) -> module.exports.getConflicts = (headDeltas, pendingDeltas) ->
# headDeltas and pendingDeltas should be lists of deltas returned by interpretDelta # headDeltas and pendingDeltas should be lists of deltas returned by expandDelta
# Returns a list of conflict objects with properties: # Returns a list of conflict objects with properties:
# headDelta # headDelta
# pendingDelta # pendingDelta
# The deltas that have conflicts also have conflict properties pointing to one another. # The deltas that have conflicts also have conflict properties pointing to one another.
headPathMap = groupDeltasByAffectingPaths(headDeltas) headPathMap = groupDeltasByAffectingPaths(headDeltas)
pendingPathMap = groupDeltasByAffectingPaths(pendingDeltas) pendingPathMap = groupDeltasByAffectingPaths(pendingDeltas)
paths = _.keys(headPathMap).concat(_.keys(pendingPathMap)) paths = _.keys(headPathMap).concat(_.keys(pendingPathMap))
# Here's my thinking: conflicts happen when one delta path is a substring of another delta path # Here's my thinking: conflicts happen when one delta path is a substring of another delta path
# So, sort paths from both deltas together, which will naturally make conflicts adjacent, # So, sort paths from both deltas together, which will naturally make conflicts adjacent,
# and if one is identified, one path is from the headDeltas, the other is from pendingDeltas # and if one is identified AND one path is from the headDeltas AND the other is from pendingDeltas
# This is all to avoid an O(nm) brute force search. # This is all to avoid an O(nm) brute force search.
conflicts = [] conflicts = []
paths.sort() paths.sort()
for path, i in paths for path, i in paths
continue if i + 1 is paths.length offset = 1
nextPath = paths[i+1] while i + offset < paths.length
if nextPath.startsWith path # Look at the neighbor
headDelta = (headPathMap[path] or headPathMap[nextPath])[0].delta nextPath = paths[i+offset]
pendingDelta = (pendingPathMap[path] or pendingPathMap[nextPath])[0].delta offset += 1
conflicts.push({headDelta:headDelta, pendingDelta:pendingDelta})
pendingDelta.conflict = headDelta # these stop being substrings of each other? Then conflict DNE
headDelta.conflict = pendingDelta if not (nextPath.startsWith path) then break
# check if these two are from the same group, but we still need to check for more beyond
unless headPathMap[path] or headPathMap[nextPath] then continue
unless pendingPathMap[path] or pendingPathMap[nextPath] then continue
# Okay, we found two deltas from different groups which conflict
for headMetaDelta in (headPathMap[path] or headPathMap[nextPath])
headDelta = headMetaDelta.delta
for pendingMetaDelta in (pendingPathMap[path] or pendingPathMap[nextPath])
pendingDelta = pendingMetaDelta.delta
conflicts.push({headDelta: headDelta, pendingDelta: pendingDelta})
pendingDelta.conflict = headDelta
headDelta.conflict = pendingDelta
return conflicts if conflicts.length return conflicts if conflicts.length
groupDeltasByAffectingPaths = (deltas) -> groupDeltasByAffectingPaths = (deltas) ->
metaDeltas = [] metaDeltas = []
for delta in deltas for delta in deltas
conflictPaths = [] conflictPaths = []
# We're being fairly liberal with what's a conflict, because the alternative is worse
if delta.action is 'moved-index' if delta.action is 'moved-index'
# every other action affects just the data path, but moved indexes affect a swath # If you moved items around in an array, mark the whole array as a gonner
indices = [delta.originalIndex, delta.destinationIndex] conflictPaths.push delta.dataPath.slice(0, delta.dataPath.length-1)
indices.sort() else if delta.action in ['deleted', 'added'] and _.isNumber(delta.dataPath[delta.dataPath.length-1])
for index in _.range(indices[0], indices[1]+1) # If you remove or add items in an array, mark the whole thing as a gonner
conflictPaths.push delta.dataPath.slice(0, delta.dataPath.length-1).concat(index) conflictPaths.push delta.dataPath.slice(0, delta.dataPath.length-1)
else else
conflictPaths.push delta.dataPath conflictPaths.push delta.dataPath
for path in conflictPaths for path in conflictPaths
@ -139,39 +153,21 @@ groupDeltasByAffectingPaths = (deltas) ->
delta: delta delta: delta
path: (item.toString() for item in path).join('/') path: (item.toString() for item in path).join('/')
} }
map = _.groupBy metaDeltas, 'path' map = _.groupBy metaDeltas, 'path'
return map
# Turns out there are cases where a single delta can include paths
# that 'conflict' with each other, ie one is a substring of the other
# because of moved indices. To handle this case, go through and prune
# out all deeper paths that conflict with more shallow paths, so
# getConflicts path checking works properly.
paths = _.keys(map)
return map unless paths.length
paths.sort()
prunedMap = {}
previousPath = paths[0]
for path, i in paths
continue if i is 0
continue if path.startsWith previousPath
prunedMap[path] = map[path]
previousPath = path
prunedMap
module.exports.pruneConflictsFromDelta = (delta, conflicts) -> module.exports.pruneConflictsFromDelta = (delta, conflicts) ->
expandedDeltas = (conflict.pendingDelta for conflict in conflicts) expandedDeltas = (conflict.pendingDelta for conflict in conflicts)
module.exports.pruneExpandedDeltasFromDelta delta, expandedDeltas module.exports.pruneExpandedDeltasFromDelta delta, expandedDeltas
module.exports.pruneExpandedDeltasFromDelta = (delta, expandedDeltas) -> module.exports.pruneExpandedDeltasFromDelta = (delta, expandedDeltas) ->
# the jsondiffpatch delta mustn't include any dangling nodes, # the jsondiffpatch delta mustn't include any dangling nodes,
# or else things will get removed which shouldn't be, or errors will occur # or else things will get removed which shouldn't be, or errors will occur
for expandedDelta in expandedDeltas for expandedDelta in expandedDeltas
prunePath delta, expandedDelta.deltaPath prunePath delta, expandedDelta.deltaPath
if _.isEmpty delta then undefined else delta if _.isEmpty delta then undefined else delta
prunePath = (delta, path) -> prunePath = (delta, path) ->
if path.length is 1 if path.length is 1
delete delta[path] delete delta[path]

View file

@ -1,16 +1,16 @@
errorModalTemplate = require('templates/modal/error') errorModalTemplate = require 'templates/modal/error'
{applyErrorsToForm} = require('lib/forms') {applyErrorsToForm} = require 'lib/forms'
module.exports.parseServerError = (text) -> module.exports.parseServerError = (text) ->
try try
error = JSON.parse(text) or {message:"Unknown error."} error = JSON.parse(text) or {message: 'Unknown error.'}
catch SyntaxError catch SyntaxError
error = {message:text or "Unknown error."} error = {message: text or 'Unknown error.'}
error = error[0] if _.isArray(error) error = error[0] if _.isArray(error)
error error
module.exports.genericFailure = (jqxhr) -> module.exports.genericFailure = (jqxhr) ->
Backbone.Mediator.publish('server-error', {response:jqxhr}) Backbone.Mediator.publish('server-error', {response: jqxhr})
return connectionFailure() if not jqxhr.status return connectionFailure() if not jqxhr.status
error = module.exports.parseServerError(jqxhr.responseText) error = module.exports.parseServerError(jqxhr.responseText)
@ -24,8 +24,8 @@ module.exports.genericFailure = (jqxhr) ->
existingForm.append($('<div class="alert alert-danger"></div>').text(error.message)) existingForm.append($('<div class="alert alert-danger"></div>').text(error.message))
else else
res = errorModalTemplate( res = errorModalTemplate(
status:jqxhr.status status: jqxhr.status
statusText:jqxhr.statusText statusText: jqxhr.statusText
message: message message: message
) )
showErrorModal(res) showErrorModal(res)
@ -36,7 +36,7 @@ module.exports.backboneFailure = (model, jqxhr, options) ->
module.exports.connectionFailure = connectionFailure = -> module.exports.connectionFailure = connectionFailure = ->
html = errorModalTemplate( html = errorModalTemplate(
status: 0 status: 0
statusText:'Connection Gone' statusText: 'Connection Gone'
message: 'No response from the CoCo servers, captain.' message: 'No response from the CoCo servers, captain.'
) )
showErrorModal(html) showErrorModal(html)

View file

@ -16,13 +16,13 @@ module.exports.applyErrorsToForm = (el, errors) ->
if error.dataPath if error.dataPath
prop = error.dataPath[1..] prop = error.dataPath[1..]
message = error.message message = error.message
else else
message = "#{error.property} #{error.message}." message = "#{error.property} #{error.message}."
message = message[0].toUpperCase() + message[1..] message = message[0].toUpperCase() + message[1..]
message = error.message if error.formatted message = error.message if error.formatted
prop = error.property prop = error.property
input = $("[name='#{prop}']", el) input = $("[name='#{prop}']", el)
if not input.length if not input.length
missingErrors.push(error) missingErrors.push(error)
@ -35,4 +35,4 @@ module.exports.applyErrorsToForm = (el, errors) ->
module.exports.clearFormAlerts = (el) -> module.exports.clearFormAlerts = (el) ->
$('.has-error', el).removeClass('has-error') $('.has-error', el).removeClass('has-error')
$('.alert.alert-danger', el).remove() $('.alert.alert-danger', el).remove()
el.find('.help-block.error-help-block').remove() el.find('.help-block.error-help-block').remove()

View file

@ -6,9 +6,9 @@ Filters.getPixels = (img) ->
c = @getCanvas(img.naturalWidth, img.naturalHeight) c = @getCanvas(img.naturalWidth, img.naturalHeight)
ctx = c.getContext('2d') ctx = c.getContext('2d')
ctx.drawImage(img, 0, 0) ctx.drawImage(img, 0, 0)
return ctx.getImageData(0,0,c.width,c.height) return ctx.getImageData(0, 0, c.width, c.height)
Filters.getCanvas = (w,h) -> Filters.getCanvas = (w, h) ->
c = document.createElement('canvas') c = document.createElement('canvas')
c.width = w c.width = w
c.height = h c.height = h
@ -34,7 +34,7 @@ module.exports.darkenImage = darkenImage = (img, pct=0.5) ->
return img.src = cachedValue if cachedValue return img.src = cachedValue if cachedValue
jqimg.data('original', img.src) unless jqimg.data('original') jqimg.data('original', img.src) unless jqimg.data('original')
if not (img.naturalWidth > 0 and img.naturalHeight > 0) if not (img.naturalWidth > 0 and img.naturalHeight > 0)
console.warn "Tried to darken image", img, "but it has natural dimensions", img.naturalWidth, img.naturalHeight console.warn 'Tried to darken image', img, 'but it has natural dimensions', img.naturalWidth, img.naturalHeight
return img return img
imageData = Filters.filterImage(Filters.brightness, img, pct) imageData = Filters.filterImage(Filters.brightness, img, pct)
c = Filters.getCanvas(img.naturalWidth, img.naturalHeight) c = Filters.getCanvas(img.naturalWidth, img.naturalHeight)

View file

@ -9,7 +9,7 @@ module.exports.getParentFolders = (subPath, urlPrefix='/test/') ->
url: urlPrefix + parts.join('/') url: urlPrefix + parts.join('/')
} }
paths paths
module.exports.parseImmediateChildren = (allChildren, subPath, baseRequirePath='test/app/', urlPrefix='/test/') -> module.exports.parseImmediateChildren = (allChildren, subPath, baseRequirePath='test/app/', urlPrefix='/test/') ->
return [] unless allChildren return [] unless allChildren
folders = {} folders = {}
@ -34,14 +34,14 @@ module.exports.parseImmediateChildren = (allChildren, subPath, baseRequirePath='
for name in _.keys(folders) for name in _.keys(folders)
children.push { children.push {
type:'folder', type: 'folder',
url: urlPrefix+name url: urlPrefix+name
name: name+'/' name: name+'/'
size: folders[name] size: folders[name]
} }
for name in _.keys(files) for name in _.keys(files)
children.push { children.push {
type:'file', type: 'file',
url: urlPrefix+name url: urlPrefix+name
name: name name: name
} }

View file

@ -15,8 +15,8 @@ module.exports = class DOMScriptModule extends ScriptModule
endNotes: -> endNotes: ->
notes = [] notes = []
notes.push({ 'channel': 'end-level-highlight-dom' }) if @noteGroup.dom.highlight? notes.push({'channel': 'end-level-highlight-dom'}) if @noteGroup.dom.highlight?
notes.push({ 'channel': 'level-enable-controls' }) if @noteGroup.dom.lock? notes.push({'channel': 'level-enable-controls'}) if @noteGroup.dom.lock?
return notes return notes
skipNotes: -> skipNotes: ->
@ -60,7 +60,7 @@ module.exports = class DOMScriptModule extends ScriptModule
lock = @noteGroup.dom.lock lock = @noteGroup.dom.lock
event.controls = lock if _.isArray lock # array: subset of controls event.controls = lock if _.isArray lock # array: subset of controls
channel = if lock then 'level-disable-controls' else 'level-enable-controls' channel = if lock then 'level-disable-controls' else 'level-enable-controls'
return { channel: channel, event: event } return {channel: channel, event: event}
letterboxNote: -> letterboxNote: ->
return { channel: 'level-set-letterbox', event: { on: @noteGroup.dom.letterbox } } return {channel: 'level-set-letterbox', event: {on: @noteGroup.dom.letterbox}}

View file

@ -76,10 +76,10 @@ module.exports = ScriptManager = class ScriptManager extends CocoClass
script.id = (idNum++).toString() unless script.id script.id = (idNum++).toString() unless script.id
callback = makeCallback(script.channel) # curry in the channel argument callback = makeCallback(script.channel) # curry in the channel argument
@addNewSubscription(script.channel, callback) @addNewSubscription(script.channel, callback)
beginTicking: -> beginTicking: ->
@tickInterval = setInterval @tick, 5000 @tickInterval = setInterval @tick, 5000
tick: => tick: =>
scriptStates = {} scriptStates = {}
now = new Date() now = new Date()
@ -87,7 +87,7 @@ module.exports = ScriptManager = class ScriptManager extends CocoClass
scriptStates[script.id] = scriptStates[script.id] =
timeSinceLastEnded: (if script.lastEnded then now - script.lastEnded else 0) / 1000 timeSinceLastEnded: (if script.lastEnded then now - script.lastEnded else 0) / 1000
timeSinceLastTriggered: (if script.lastTriggered then now - script.lastTriggered else 0) / 1000 timeSinceLastTriggered: (if script.lastTriggered then now - script.lastTriggered else 0) / 1000
stateEvent = stateEvent =
scriptRunning: @currentNoteGroup?.scriptID or '' scriptRunning: @currentNoteGroup?.scriptID or ''
noteGroupRunning: @currentNoteGroup?.name or '' noteGroupRunning: @currentNoteGroup?.name or ''
@ -123,7 +123,7 @@ module.exports = ScriptManager = class ScriptManager extends CocoClass
for scriptID in scriptsToSkip for scriptID in scriptsToSkip
script = _.find @scripts, {id: scriptID} script = _.find @scripts, {id: scriptID}
unless script unless script
console.warn "Couldn't find script for", scriptID, "from scripts", @scripts, "when restoring session scripts." console.warn 'Couldn\'t find script for', scriptID, 'from scripts', @scripts, 'when restoring session scripts.'
continue continue
continue if script.repeats # repeating scripts are not 'rerun' continue if script.repeats # repeating scripts are not 'rerun'
@triggered.push(scriptID) @triggered.push(scriptID)

View file

@ -1,31 +1,31 @@
CocoClass = require 'lib/CocoClass' CocoClass = require 'lib/CocoClass'
module.exports = class ScriptModule extends CocoClass module.exports = class ScriptModule extends CocoClass
scrubbingTime = 0 scrubbingTime = 0
movementTime = 0 movementTime = 0
constructor: (@noteGroup) -> constructor: (@noteGroup) ->
super() super()
if not @noteGroup.prepared if not @noteGroup.prepared
@analyzeNoteGroup(noteGroup) @analyzeNoteGroup(noteGroup)
@noteGroup.notes ?= [] @noteGroup.notes ?= []
@noteGroup.prepared = true @noteGroup.prepared = true
# subclass should overwrite these # subclass should overwrite these
@neededFor: -> false @neededFor: -> false
startNotes: -> [] startNotes: -> []
endNotes: -> [] endNotes: -> []
skipNotes: -> @endNotes() skipNotes: -> @endNotes()
# common logic # common logic
analyzeNoteGroup: -> analyzeNoteGroup: ->
# some notes need to happen after others. Calculate the delays # some notes need to happen after others. Calculate the delays
@movementTime = @calculateMovementMax(@noteGroup) @movementTime = @calculateMovementMax(@noteGroup)
@scrubbingTime = @noteGroup.playback?.scrub?.duration or 0 @scrubbingTime = @noteGroup.playback?.scrub?.duration or 0
calculateMovementMax: -> calculateMovementMax: ->
sums = {} sums = {}
for sprite in @noteGroup.sprites for sprite in @noteGroup.sprites
@ -36,4 +36,4 @@ module.exports = class ScriptModule extends CocoClass
Math.max(0, sums...) Math.max(0, sums...)
maybeApplyDelayToNote: (note) -> maybeApplyDelayToNote: (note) ->
note.delay = (@scrubbingTime + @movementTime) or 0 note.delay = (@scrubbingTime + @movementTime) or 0

View file

@ -26,7 +26,7 @@ module.exports = class SoundScriptModule extends ScriptModule
channel: 'level-suppress-selection-sounds' channel: 'level-suppress-selection-sounds'
event: {suppress: @noteGroup.sound.suppressSelectionSounds} event: {suppress: @noteGroup.sound.suppressSelectionSounds}
return note return note
addMusicNote: -> addMusicNote: ->
note = note =
channel: 'level-play-music' channel: 'level-play-music'

View file

@ -45,7 +45,7 @@ module.exports = class SpritesScriptModule extends ScriptModule
event: event:
message: text message: text
blurb: blurb blurb: blurb
mood: sprite.say.mood or "explain" mood: sprite.say.mood or 'explain'
responses: responses responses: responses
spriteID: sprite.id spriteID: sprite.id
sound: sound sound: sound

View file

@ -1,42 +1,42 @@
module.exports = initializeFacebook = -> module.exports = initializeFacebook = ->
# Additional JS functions here # Additional JS functions here
window.fbAsyncInit = -> window.fbAsyncInit = ->
Backbone.Mediator.publish "fbapi-loaded"
FB.init FB.init
appId: (if document.location.origin is "http://localhost:3000" then "607435142676437" else "148832601965463") # App ID appId: (if document.location.origin is 'http://localhost:3000' then '607435142676437' else '148832601965463') # App ID
channelUrl: document.location.origin + "/channel.html" # Channel File channelUrl: document.location.origin + '/channel.html' # Channel File
status: true # check login status status: true # check login status
cookie: true # enable cookies to allow the server to access the session cookie: true # enable cookies to allow the server to access the session
xfbml: true # parse XFBML xfbml: true # parse XFBML
Backbone.Mediator.publish 'fbapi-loaded'
# This is fired for any auth related change, such as login, logout or session refresh. # This is fired for any auth related change, such as login, logout or session refresh.
FB.Event.subscribe "auth.authResponseChange", (response) -> FB.Event.subscribe 'auth.authResponseChange', (response) ->
# Here we specify what we do with the response anytime this event occurs. # Here we specify what we do with the response anytime this event occurs.
if response.status is "connected" if response.status is 'connected'
# They have logged in to the app. # They have logged in to the app.
Backbone.Mediator.publish "facebook-logged-in", Backbone.Mediator.publish 'facebook-logged-in',
response: response response: response
else if response.status is "not_authorized" else if response.status is 'not_authorized'
# #
else else
# #
# Load the SDK asynchronously # Load the SDK asynchronously
((d) -> ((d) ->
js = undefined js = undefined
id = "facebook-jssdk" id = 'facebook-jssdk'
ref = d.getElementsByTagName("script")[0] ref = d.getElementsByTagName('script')[0]
return if d.getElementById(id) return if d.getElementById(id)
js = d.createElement("script") js = d.createElement('script')
js.id = id js.id = id
js.async = true js.async = true
js.src = "//connect.facebook.net/en_US/all.js" js.src = '//connect.facebook.net/en_US/all.js'
#js.src = "//connect.facebook.net/en_US/all/debug.js"; #js.src = '//connect.facebook.net/en_US/all/debug.js'
ref.parentNode.insertBefore js, ref ref.parentNode.insertBefore js, ref
return return
) document ) document

View file

@ -1,15 +1,15 @@
module.exports = initializeFilepicker = -> module.exports = initializeFilepicker = ->
((a) -> ((a) ->
return if window.filepicker return if window.filepicker
b = a.createElement("script") b = a.createElement('script')
b.type = "text/javascript" b.type = 'text/javascript'
b.async = not 0 b.async = not 0
b.src = ((if "https:" is a.location.protocol then "https:" else "http:")) + "//api.filepicker.io/v1/filepicker.js" b.src = ((if 'https:' is a.location.protocol then 'https:' else 'http:')) + '//api.filepicker.io/v1/filepicker.js'
c = a.getElementsByTagName("script")[0] c = a.getElementsByTagName('script')[0]
c.parentNode.insertBefore b, c c.parentNode.insertBefore b, c
d = {} d = {}
d._queue = [] d._queue = []
e = "pick,pickMultiple,pickAndStore,read,write,writeUrl,export,convert,store,storeUrl,remove,stat,setKey,constructWidget,makeDropPane".split(",") e = 'pick,pickMultiple,pickAndStore,read,write,writeUrl,export,convert,store,storeUrl,remove,stat,setKey,constructWidget,makeDropPane'.split(',')
f = (a, b) -> f = (a, b) ->
-> ->
b.push [ b.push [

View file

@ -1,16 +1,16 @@
module.exports = initializeGoogle = -> module.exports = initializeGoogle = ->
window.onGPlusLoaded = -> window.onGPlusLoaded = ->
Backbone.Mediator.publish "gapi-loaded" Backbone.Mediator.publish 'gapi-loaded'
return return
window.signinCallback = (authResult) -> window.signinCallback = (authResult) ->
Backbone.Mediator.publish "gplus-logged-in", authResult if authResult["access_token"] Backbone.Mediator.publish 'gplus-logged-in', authResult if authResult['access_token']
return return
(-> (->
po = document.createElement("script") po = document.createElement('script')
po.type = "text/javascript" po.type = 'text/javascript'
po.async = true po.async = true
po.src = "https://apis.google.com/js/client:plusone.js?onload=onGPlusLoaded" po.src = 'https://apis.google.com/js/client:plusone.js?onload=onGPlusLoaded'
s = document.getElementsByTagName("script")[0] s = document.getElementsByTagName('script')[0]
s.parentNode.insertBefore po, s s.parentNode.insertBefore po, s
return return
)() )()

View file

@ -1,6 +1,6 @@
module.exports = initializeLinkedIn = -> module.exports = initializeLinkedIn = ->
window.linkedInAsyncInit = -> window.linkedInAsyncInit = ->
console.log "Linkedin async init success!" console.log 'Linkedin async init success!'
Backbone.Mediator.publish 'linkedin-loaded' Backbone.Mediator.publish 'linkedin-loaded'
linkedInSnippet = linkedInSnippet =

View file

@ -2,9 +2,9 @@ module.exports = initializeOlark = ->
window.olark or ((c) -> #<![CDATA[ window.olark or ((c) -> #<![CDATA[
f = window f = window
d = document d = document
l = (if f.location.protocol is "https:" then "https:" else "http:") l = (if f.location.protocol is 'https:' then 'https:' else 'http:')
z = c.name z = c.name
r = "load" r = 'load'
nt = -> nt = ->
s = -> s = ->
a.P r a.P r
@ -19,7 +19,7 @@ module.exports = initializeOlark = ->
while q-- while q--
((n) -> ((n) ->
f[z][n] = -> f[z][n] = ->
f[z] "call", n, arguments f[z] 'call', n, arguments
return return
return return
@ -31,67 +31,67 @@ module.exports = initializeOlark = ->
a.p[u] = new Date - a.p[0] a.p[u] = new Date - a.p[0]
return return
(if f.addEventListener then f.addEventListener(r, s, false) else f.attachEvent("on" + r, s)) (if f.addEventListener then f.addEventListener(r, s, false) else f.attachEvent('on' + r, s))
ld = -> ld = ->
p = (hd) -> p = (hd) ->
hd = "head" hd = 'head'
[ [
"<" '<'
hd hd
"></" '></'
hd hd
"><" '><'
i i
" onl" + "oad=\"var d=" ' onl' + 'oad=\"var d='
g g
";d.getElementsByTagName('head')[0]." ";d.getElementsByTagName('head')[0]."
j j
"(d." '(d.'
h h
"('script'))." "('script'))."
k k
"='" "='"
l l
"//" '//'
a.l a.l
"'" "'"
"\"" '\"'
"></" '></'
i i
">" '>'
].join "" ].join ''
i = "body" i = 'body'
m = d[i] m = d[i]
return setTimeout(ld, 100) unless m return setTimeout(ld, 100) unless m
a.P 1 a.P 1
j = "appendChild" j = 'appendChild'
h = "createElement" h = 'createElement'
k = "src" k = 'src'
n = d[h]("div") n = d[h]('div')
v = n[j](d[h](z)) v = n[j](d[h](z))
b = d[h]("iframe") b = d[h]('iframe')
g = "document" g = 'document'
e = "domain" e = 'domain'
o = undefined o = undefined
n.style.display = "none" n.style.display = 'none'
m.insertBefore(n, m.firstChild).id = z m.insertBefore(n, m.firstChild).id = z
b.frameBorder = "0" b.frameBorder = '0'
b.id = z + "-loader" b.id = z + '-loader'
b.src = "javascript:false" if /MSIE[ ]+6/.test(navigator.userAgent) b.src = 'javascript:false' if /MSIE[ ]+6/.test(navigator.userAgent)
b.allowTransparency = "true" b.allowTransparency = 'true'
v[j] b v[j] b
try try
b.contentWindow[g].open() b.contentWindow[g].open()
catch w catch w
c[e] = d[e] c[e] = d[e]
o = "javascript:var d=" + g + ".open();d.domain='" + d.domain + "';" o = 'javascript:var d=' + g + ".open();d.domain='" + d.domain + "';"
b[k] = o + "void(0);" b[k] = o + 'void(0);'
try try
t = b.contentWindow[g] t = b.contentWindow[g]
t.write p() t.write p()
t.close() t.close()
catch x catch x
b[k] = o + "d.write(\"" + p().replace(/"/g, String.fromCharCode(92) + "\"") + "\");d.close();" b[k] = o + 'd.write(\"' + p().replace(/"/g, String.fromCharCode(92) + '\"') + '\");d.close();'
a.P 2 a.P 2
return return
@ -101,16 +101,15 @@ module.exports = initializeOlark = ->
nt() nt()
return return
)( )(
loader: "static.olark.com/jsclient/loader0.js" loader: 'static.olark.com/jsclient/loader0.js'
name: "olark" name: 'olark'
methods: [ methods: [
"configure" 'configure'
"extend" 'extend'
"declare" 'declare'
"identify" 'identify'
] ]
) )
# custom configuration goes here (www.olark.com/documentation) # custom configuration goes here (www.olark.com/documentation)
olark.identify "1451-787-10-5544" #]]> olark.identify '1451-787-10-5544' #]]>

View file

@ -2,18 +2,18 @@ module.exports = initializeSegmentio = ->
analytics = analytics or [] analytics = analytics or []
(-> (->
e = [ e = [
"identify" 'identify'
"track" 'track'
"trackLink" 'trackLink'
"trackForm" 'trackForm'
"trackClick" 'trackClick'
"trackSubmit" 'trackSubmit'
"page" 'page'
"pageview" 'pageview'
"ab" 'ab'
"alias" 'alias'
"ready" 'ready'
"group" 'group'
] ]
t = (e) -> t = (e) ->
-> ->
@ -28,14 +28,13 @@ module.exports = initializeSegmentio = ->
return return
)() )()
analytics.load = (e) -> analytics.load = (e) ->
t = document.createElement("script") t = document.createElement('script')
t.type = "text/javascript" t.type = 'text/javascript'
t.async = not 0 t.async = not 0
t.src = ((if "https:" is document.location.protocol then "https://" else "http://")) + "d2dq2ahtl5zl1z.cloudfront.net/analytics.js/v1/" + e + "/analytics.min.js" t.src = ((if 'https:' is document.location.protocol then 'https://' else 'http://')) + 'd2dq2ahtl5zl1z.cloudfront.net/analytics.js/v1/' + e + '/analytics.min.js'
n = document.getElementsByTagName("script")[0] n = document.getElementsByTagName('script')[0]
n.parentNode.insertBefore t, n n.parentNode.insertBefore t, n
return return
analytics.load 'jsjzx9n4d2'
analytics.load "jsjzx9n4d2"

View file

@ -2,11 +2,11 @@ module.exports = initializeTwitter = ->
((d, s, id) -> ((d, s, id) ->
js = undefined js = undefined
fjs = d.getElementsByTagName(s)[0] fjs = d.getElementsByTagName(s)[0]
p = (if /^http:/.test(d.location) then "http" else "https") p = (if /^http:/.test(d.location) then 'http' else 'https')
unless d.getElementById(id) unless d.getElementById(id)
js = d.createElement(s) js = d.createElement(s)
js.id = id js.id = id
js.src = p + "://platform.twitter.com/widgets.js" js.src = p + '://platform.twitter.com/widgets.js'
fjs.parentNode.insertBefore js, fjs fjs.parentNode.insertBefore js, fjs
return return
) document, "script", "twitter-wjs" ) document, 'script', 'twitter-wjs'

View file

@ -8,7 +8,6 @@ Aether.addGlobal 'Vector', require 'lib/world/vector'
Aether.addGlobal '_', _ Aether.addGlobal '_', _
module.exports = class Simulator extends CocoClass module.exports = class Simulator extends CocoClass
constructor: (@options) -> constructor: (@options) ->
@options ?= {} @options ?= {}
_.extend @, Backbone.Events _.extend @, Backbone.Events
@ -27,12 +26,12 @@ module.exports = class Simulator extends CocoClass
fetchAndSimulateOneGame: (humanGameID, ogresGameID) => fetchAndSimulateOneGame: (humanGameID, ogresGameID) =>
return if @destroyed return if @destroyed
$.ajax $.ajax
url: "/queue/scoring/getTwoGames" url: '/queue/scoring/getTwoGames'
type: "POST" type: 'POST'
parse: true parse: true
data: data:
"humansGameID": humanGameID 'humansGameID': humanGameID
"ogresGameID": ogresGameID 'ogresGameID': ogresGameID
error: (errorData) -> error: (errorData) ->
console.warn "There was an error fetching two games! #{JSON.stringify errorData}" console.warn "There was an error fetching two games! #{JSON.stringify errorData}"
success: (taskData) => success: (taskData) =>
@ -71,31 +70,31 @@ module.exports = class Simulator extends CocoClass
@god.createWorld @generateSpellsObject() @god.createWorld @generateSpellsObject()
handleSingleSimulationError: (error) -> handleSingleSimulationError: (error) ->
console.error "There was an error simulating a single game!", error console.error 'There was an error simulating a single game!', error
if @options.headlessClient and @options.simulateOnlyOneGame if @options.headlessClient and @options.simulateOnlyOneGame
console.log "GAMERESULT:tie" console.log 'GAMERESULT:tie'
process.exit(0) process.exit(0)
@cleanupAndSimulateAnotherTask() @cleanupAndSimulateAnotherTask()
handleSingleSimulationInfiniteLoop: -> handleSingleSimulationInfiniteLoop: ->
console.log "There was an infinite loop in the single game!" console.log 'There was an infinite loop in the single game!'
if @options.headlessClient and @options.simulateOnlyOneGame if @options.headlessClient and @options.simulateOnlyOneGame
console.log "GAMERESULT:tie" console.log 'GAMERESULT:tie'
process.exit(0) process.exit(0)
@cleanupAndSimulateAnotherTask() @cleanupAndSimulateAnotherTask()
processSingleGameResults: (simulationResults) -> processSingleGameResults: (simulationResults) ->
taskResults = @formTaskResultsObject simulationResults taskResults = @formTaskResultsObject simulationResults
console.log "Processing results:", taskResults console.log 'Processing results:', taskResults
humanSessionRank = taskResults.sessions[0].metrics.rank humanSessionRank = taskResults.sessions[0].metrics.rank
ogreSessionRank = taskResults.sessions[1].metrics.rank ogreSessionRank = taskResults.sessions[1].metrics.rank
if @options.headlessClient and @options.simulateOnlyOneGame if @options.headlessClient and @options.simulateOnlyOneGame
if humanSessionRank is ogreSessionRank if humanSessionRank is ogreSessionRank
console.log "GAMERESULT:tie" console.log 'GAMERESULT:tie'
else if humanSessionRank < ogreSessionRank else if humanSessionRank < ogreSessionRank
console.log "GAMERESULT:humans" console.log 'GAMERESULT:humans'
else if ogreSessionRank < humanSessionRank else if ogreSessionRank < humanSessionRank
console.log "GAMERESULT:ogres" console.log 'GAMERESULT:ogres'
process.exit(0) process.exit(0)
else else
@sendSingleGameBackToServer(taskResults) @sendSingleGameBackToServer(taskResults)
@ -104,32 +103,31 @@ module.exports = class Simulator extends CocoClass
@trigger 'statusUpdate', 'Simulation completed, sending results back to server!' @trigger 'statusUpdate', 'Simulation completed, sending results back to server!'
$.ajax $.ajax
url: "/queue/scoring/recordTwoGames" url: '/queue/scoring/recordTwoGames'
data: results data: results
type: "PUT" type: 'PUT'
parse: true parse: true
success: @handleTaskResultsTransferSuccess success: @handleTaskResultsTransferSuccess
error: @handleTaskResultsTransferError error: @handleTaskResultsTransferError
complete: @cleanupAndSimulateAnotherTask complete: @cleanupAndSimulateAnotherTask
fetchAndSimulateTask: => fetchAndSimulateTask: =>
return if @destroyed return if @destroyed
if @options.headlessClient if @options.headlessClient
if @dumpThisTime # The first heapdump would be useless to find leaks. if @dumpThisTime # The first heapdump would be useless to find leaks.
console.log "Writing snapshot." console.log 'Writing snapshot.'
@options.heapdump.writeSnapshot() @options.heapdump.writeSnapshot()
@dumpThisTime = true if @options.heapdump @dumpThisTime = true if @options.heapdump
if @options.testing if @options.testing
_.delay @setupSimulationAndLoadLevel, 0, @options.testFile, "Testing...", status: 400 _.delay @setupSimulationAndLoadLevel, 0, @options.testFile, 'Testing...', status: 400
return return
@trigger 'statusUpdate', 'Fetching simulation data!' @trigger 'statusUpdate', 'Fetching simulation data!'
$.ajax $.ajax
url: @taskURL url: @taskURL
type: "GET" type: 'GET'
parse: true parse: true
error: @handleFetchTaskError error: @handleFetchTaskError
success: @setupSimulationAndLoadLevel success: @setupSimulationAndLoadLevel
@ -139,13 +137,12 @@ module.exports = class Simulator extends CocoClass
@trigger 'statusUpdate', 'There was an error fetching games to simulate. Retrying in 10 seconds.' @trigger 'statusUpdate', 'There was an error fetching games to simulate. Retrying in 10 seconds.'
@simulateAnotherTaskAfterDelay() @simulateAnotherTaskAfterDelay()
handleNoGamesResponse: -> handleNoGamesResponse: ->
info = 'Finding game to simulate...' info = 'Finding game to simulate...'
console.log info console.log info
@trigger 'statusUpdate', info @trigger 'statusUpdate', info
@fetchAndSimulateOneGame() @fetchAndSimulateOneGame()
application.tracker?.trackEvent 'Simulator Result', label: "No Games", ['Google Analytics'] application.tracker?.trackEvent 'Simulator Result', label: 'No Games', ['Google Analytics']
simulateAnotherTaskAfterDelay: => simulateAnotherTaskAfterDelay: =>
console.log "Retrying in #{@retryDelayInSeconds}" console.log "Retrying in #{@retryDelayInSeconds}"
@ -184,7 +181,7 @@ module.exports = class Simulator extends CocoClass
try try
@commenceSimulationAndSetupCallback() @commenceSimulationAndSetupCallback()
catch err catch err
console.error "There was an error in simulation:", err, "-- trying again in #{@retryDelayInSeconds} seconds" console.error 'There was an error in simulation:', err, "-- trying again in #{@retryDelayInSeconds} seconds"
@simulateAnotherTaskAfterDelay() @simulateAnotherTaskAfterDelay()
assignWorldAndLevelFromLevelLoaderAndDestroyIt: -> assignWorldAndLevelFromLevelLoaderAndDestroyIt: ->
@ -205,11 +202,11 @@ module.exports = class Simulator extends CocoClass
Backbone.Mediator.subscribeOnce 'god:goals-calculated', @processResults, @ Backbone.Mediator.subscribeOnce 'god:goals-calculated', @processResults, @
@god.createWorld @generateSpellsObject() @god.createWorld @generateSpellsObject()
#Search for leaks, headless-client only. # Search for leaks, headless-client only.
if @options.headlessClient and @options.leakTest and not @memwatch? if @options.headlessClient and @options.leakTest and not @memwatch?
leakcount = 0 leakcount = 0
maxleakcount = 0 maxleakcount = 0
console.log "Setting leak callbacks." console.log 'Setting leak callbacks.'
@memwatch = require 'memwatch' @memwatch = require 'memwatch'
@memwatch.on 'leak', (info) => @memwatch.on 'leak', (info) =>
@ -220,17 +217,17 @@ module.exports = class Simulator extends CocoClass
@hd = new @memwatch.HeapDiff() @hd = new @memwatch.HeapDiff()
@memwatch.on 'stats', (stats) => @memwatch.on 'stats', (stats) =>
console.warn "stats callback: " + stats console.warn 'stats callback: ' + stats
diff = @hd.end() diff = @hd.end()
console.warn "HeapDiff:\n" + JSON.stringify(diff) console.warn "HeapDiff:\n" + JSON.stringify(diff)
if @options.exitOnLeak if @options.exitOnLeak
console.warn "Exiting because of Leak." console.warn 'Exiting because of Leak.'
process.exit() process.exit()
@hd = new @memwatch.HeapDiff() @hd = new @memwatch.HeapDiff()
onInfiniteLoop: -> onInfiniteLoop: ->
console.warn "Skipping infinitely looping game." console.warn 'Skipping infinitely looping game.'
@trigger 'statusUpdate', "Infinite loop detected; grabbing a new game in #{@retryDelayInSeconds} seconds." @trigger 'statusUpdate', "Infinite loop detected; grabbing a new game in #{@retryDelayInSeconds} seconds."
_.delay @cleanupAndSimulateAnotherTask, @retryDelayInSeconds * 1000 _.delay @cleanupAndSimulateAnotherTask, @retryDelayInSeconds * 1000
@ -244,16 +241,16 @@ module.exports = class Simulator extends CocoClass
sendResultsBackToServer: (results) -> sendResultsBackToServer: (results) ->
@trigger 'statusUpdate', 'Simulation completed, sending results back to server!' @trigger 'statusUpdate', 'Simulation completed, sending results back to server!'
console.log "Sending result back to server:" console.log 'Sending result back to server:'
console.log JSON.stringify results console.log JSON.stringify results
if @options.headlessClient and @options.testing if @options.headlessClient and @options.testing
return @fetchAndSimulateTask() return @fetchAndSimulateTask()
$.ajax $.ajax
url: "/queue/scoring" url: '/queue/scoring'
data: results data: results
type: "PUT" type: 'PUT'
parse: true parse: true
success: @handleTaskResultsTransferSuccess success: @handleTaskResultsTransferSuccess
error: @handleTaskResultsTransferError error: @handleTaskResultsTransferError
@ -263,12 +260,12 @@ module.exports = class Simulator extends CocoClass
return if @destroyed return if @destroyed
console.log "Task registration result: #{JSON.stringify result}" console.log "Task registration result: #{JSON.stringify result}"
@trigger 'statusUpdate', 'Results were successfully sent back to server!' @trigger 'statusUpdate', 'Results were successfully sent back to server!'
console.log "Simulated by you:", @simulatedByYou console.log 'Simulated by you:', @simulatedByYou
@simulatedByYou++ @simulatedByYou++
unless @options.headlessClient unless @options.headlessClient
simulatedBy = parseInt($('#simulated-by-you').text(), 10) + 1 simulatedBy = parseInt($('#simulated-by-you').text(), 10) + 1
$('#simulated-by-you').text(simulatedBy) $('#simulated-by-you').text(simulatedBy)
application.tracker?.trackEvent 'Simulator Result', label: "Success", ['Google Analytics'] application.tracker?.trackEvent 'Simulator Result', label: 'Success', ['Google Analytics']
handleTaskResultsTransferError: (error) => handleTaskResultsTransferError: (error) =>
return if @destroyed return if @destroyed
@ -316,11 +313,11 @@ module.exports = class Simulator extends CocoClass
humansWon = _.all humanGoals, {status: 'success'} humansWon = _.all humanGoals, {status: 'success'}
if ogresWon is humansWon if ogresWon is humansWon
return 0 return 0
else if ogresWon and teamSessionMap["ogres"] is sessionID else if ogresWon and teamSessionMap['ogres'] is sessionID
return 0 return 0
else if ogresWon and teamSessionMap["ogres"] isnt sessionID else if ogresWon and teamSessionMap['ogres'] isnt sessionID
return 1 return 1
else if humansWon and teamSessionMap["humans"] is sessionID else if humansWon and teamSessionMap['humans'] is sessionID
return 0 return 0
else else
return 1 return 1
@ -376,14 +373,13 @@ module.exports = class Simulator extends CocoClass
else else
spellSession = _.filter(@task.getSessions(), {team: spellTeam})[0] spellSession = _.filter(@task.getSessions(), {team: spellTeam})[0]
unless codeLanguage = spellSession?.submittedCodeLanguage unless codeLanguage = spellSession?.submittedCodeLanguage
console.warn "Session", spellSession.creatorName, spellSession.team, "didn't have submittedCodeLanguage, just:", spellSession console.warn 'Session', spellSession.creatorName, spellSession.team, 'didn\'t have submittedCodeLanguage, just:', spellSession
@spells[spellKey].thangs[thang.id].aether = @createAether @spells[spellKey].name, method, useProtectAPI, codeLanguage ? 'javascript' @spells[spellKey].thangs[thang.id].aether = @createAether @spells[spellKey].name, method, useProtectAPI, codeLanguage ? 'javascript'
transpileSpell: (thang, spellKey, methodName) -> transpileSpell: (thang, spellKey, methodName) ->
slugifiedThangID = _.string.slugify thang.id slugifiedThangID = _.string.slugify thang.id
generatedSpellKey = [slugifiedThangID,methodName].join '/' generatedSpellKey = [slugifiedThangID,methodName].join '/'
source = @currentUserCodeMap[generatedSpellKey] ? "" source = @currentUserCodeMap[generatedSpellKey] ? ''
aether = @spells[spellKey].thangs[thang.id].aether aether = @spells[spellKey].thangs[thang.id].aether
unless _.contains(@task.spellKeysToTranspile, generatedSpellKey) unless _.contains(@task.spellKeysToTranspile, generatedSpellKey)
aether.pure = source aether.pure = source
@ -399,22 +395,21 @@ module.exports = class Simulator extends CocoClass
functionName: methodName functionName: methodName
protectAPI: useProtectAPI protectAPI: useProtectAPI
includeFlow: false includeFlow: false
yieldConditionally: methodName is "plan" yieldConditionally: methodName is 'plan'
globals: ['Vector', '_'] globals: ['Vector', '_']
problems: problems:
jshint_W040: {level: "ignore"} jshint_W040: {level: 'ignore'}
jshint_W030: {level: "ignore"} # aether_NoEffect instead jshint_W030: {level: 'ignore'} # aether_NoEffect instead
aether_MissingThis: {level: 'error'} aether_MissingThis: {level: 'error'}
#functionParameters: # TODOOOOO #functionParameters: # TODOOOOO
executionLimit: 1 * 1000 * 1000 executionLimit: 1 * 1000 * 1000
language: codeLanguage language: codeLanguage
if methodName is 'hear' then aetherOptions.functionParameters = ['speaker', 'message', 'data'] if methodName is 'hear' then aetherOptions.functionParameters = ['speaker', 'message', 'data']
if methodName is 'makeBid' then aetherOptions.functionParameters = ['tileGroupLetter'] if methodName is 'makeBid' then aetherOptions.functionParameters = ['tileGroupLetter']
if methodName is "findCentroids" then aetherOptions.functionParameters = ["centroids"] if methodName is 'findCentroids' then aetherOptions.functionParameters = ['centroids']
#console.log "creating aether with options", aetherOptions #console.log 'creating aether with options', aetherOptions
return new Aether aetherOptions return new Aether aetherOptions
class SimulationTask class SimulationTask
constructor: (@rawData) -> constructor: (@rawData) ->
@spellKeyToTeamMap = {} @spellKeyToTeamMap = {}
@ -423,12 +418,12 @@ class SimulationTask
getLevelName: -> getLevelName: ->
levelName = @rawData.sessions?[0]?.levelID levelName = @rawData.sessions?[0]?.levelID
return levelName if levelName? return levelName if levelName?
@throwMalformedTaskError "The level name couldn't be deduced from the task." @throwMalformedTaskError 'The level name couldn\'t be deduced from the task.'
generateTeamToSessionMap: -> generateTeamToSessionMap: ->
teamSessionMap = {} teamSessionMap = {}
for session in @rawData.sessions for session in @rawData.sessions
@throwMalformedTaskError "Two players share the same team" if teamSessionMap[session.team]? @throwMalformedTaskError 'Two players share the same team' if teamSessionMap[session.team]?
teamSessionMap[session.team] = session.sessionID teamSessionMap[session.team] = session.sessionID
teamSessionMap teamSessionMap
@ -450,7 +445,6 @@ class SimulationTask
setWorld: (@world) -> setWorld: (@world) ->
generateSpellKeyToSourceMap: -> generateSpellKeyToSourceMap: ->
playerTeams = _.pluck @rawData.sessions, 'team' playerTeams = _.pluck @rawData.sessions, 'team'
spellKeyToSourceMap = {} spellKeyToSourceMap = {}
@ -469,7 +463,7 @@ class SimulationTask
for thangName, thangSpells of session.transpiledCode for thangName, thangSpells of session.transpiledCode
for spellName, spell of thangSpells for spellName, spell of thangSpells
fullSpellName = [thangName,spellName].join '/' fullSpellName = [thangName, spellName].join '/'
if _.contains(teamSpells, fullSpellName) if _.contains(teamSpells, fullSpellName)
teamCode[fullSpellName]=spell teamCode[fullSpellName]=spell

View file

@ -14,7 +14,7 @@ module.exports = class SpriteBuilder
buildMovieClip: (animationName, movieClipArgs...) -> buildMovieClip: (animationName, movieClipArgs...) ->
animData = @animationStore[animationName] animData = @animationStore[animationName]
unless animData unless animData
console.error "couldn't find animData from", @animationStore, "for", animationName console.error 'couldn\'t find animData from', @animationStore, 'for', animationName
return null return null
locals = {} locals = {}
_.extend locals, @buildMovieClipShapes(animData.shapes) _.extend locals, @buildMovieClipShapes(animData.shapes)
@ -111,7 +111,7 @@ module.exports = class SpriteBuilder
shape shape
buildContainerFromStore: (containerKey) -> buildContainerFromStore: (containerKey) ->
console.error "Yo we don't have no", containerKey unless containerKey console.error 'Yo we don\'t have no', containerKey unless containerKey
contData = @containerStore[containerKey] contData = @containerStore[containerKey]
cont = new createjs.Container() cont = new createjs.Container()
cont.initialize() cont.initialize()
@ -182,5 +182,4 @@ module.exports = class SpriteBuilder
continue if (not shape.fc?) or not(colors[shape.fc]) continue if (not shape.fc?) or not(colors[shape.fc])
@colorMap[shapeKey] = hslToHex(colors[shape.fc]) @colorMap[shapeKey] = hslToHex(colors[shape.fc])
sum = (nums) -> _.reduce(nums, (s, num) -> s + num) sum = (nums) -> _.reduce(nums, (s, num) -> s + num)

View file

@ -29,8 +29,8 @@ module.exports = class SpriteParser
parse: (source) -> parse: (source) ->
# Grab the library properties' width/height so we can subtract half of each from frame bounds # Grab the library properties' width/height so we can subtract half of each from frame bounds
properties = source.match(/.*lib\.properties = \{\n.*?width: (\d+),\n.*?height: (\d+)/im) properties = source.match(/.*lib\.properties = \{\n.*?width: (\d+),\n.*?height: (\d+)/im)
@width = parseInt(properties?[1] ? "0", 10) @width = parseInt(properties?[1] ? '0', 10)
@height = parseInt(properties?[2] ? "0", 10) @height = parseInt(properties?[2] ? '0', 10)
options = {loc: false, range: true} options = {loc: false, range: true}
ast = esprima.parse source, options ast = esprima.parse source, options
@ -60,7 +60,7 @@ module.exports = class SpriteParser
continue if gotIt continue if gotIt
for c in localContainers for c in localContainers
if c.bn is bn if c.bn is bn
instructions.push { t: c.t, gn: c.gn } instructions.push {t: c.t, gn: c.gn}
break break
@addContainer {c: instructions, b: container.bounds}, container.name @addContainer {c: instructions, b: container.bounds}, container.name
for movieClip in movieClips for movieClip in movieClips
@ -101,7 +101,7 @@ module.exports = class SpriteParser
if not shortKey? if not shortKey?
shortKey = name shortKey = name
if @thangType.containers[shortKey]? if @thangType.containers[shortKey]?
shortKey = @animationName + ":" + name shortKey = @animationName + ':' + name
@thangType.containers[shortKey] = container @thangType.containers[shortKey] = container
@containerLongKeys[longKey] = shortKey @containerLongKeys[longKey] = shortKey
@containerRenamings[name] = shortKey @containerRenamings[name] = shortKey
@ -115,7 +115,7 @@ module.exports = class SpriteParser
else else
shortKey = name shortKey = name
if @thangType.animations[shortKey]? if @thangType.animations[shortKey]?
shortKey = @animationName + ":" + name shortKey = @animationName + ':' + name
@thangType.animations[shortKey] = animation @thangType.animations[shortKey] = animation
@animationLongKeys[longKey] = shortKey @animationLongKeys[longKey] = shortKey
@animationRenamings[name] = shortKey @animationRenamings[name] = shortKey
@ -173,7 +173,7 @@ module.exports = class SpriteParser
frameBoundsRange = frameBoundsStatement.expression.right.range frameBoundsRange = frameBoundsStatement.expression.right.range
frameBoundsSource = @subSourceFromRange frameBoundsRange, source frameBoundsSource = @subSourceFromRange frameBoundsRange, source
if frameBoundsSource.search(/\[rect/) is -1 # some other statement; we don't have multiframe bounds if frameBoundsSource.search(/\[rect/) is -1 # some other statement; we don't have multiframe bounds
console.log "Didn't have multiframe bounds for this movie clip." console.log 'Didn\'t have multiframe bounds for this movie clip.'
frameBounds = [nominalBounds] frameBounds = [nominalBounds]
else else
lastRect = nominalBounds lastRect = nominalBounds
@ -204,10 +204,10 @@ module.exports = class SpriteParser
functionExpressions functionExpressions
### ###
this.shape_1.graphics.f("#605E4A").s().p("AAOD/IgOgaIAEhkIgmgdIgMgBIgPgFIgVgJQA1h9g8jXQAQAHAOASQAQAUAKAeQARAuAJBJQAHA/gBA5IAAADIACAfIAFARIACAGIAEAHIAHAHQAVAXAQAUQAUAaANAUIABACIgsgdIgggXIAAAnIABAwIgBgBg"); this.shape_1.graphics.f('#605E4A').s().p('AAOD/IgOgaIAEhkIgmgdIgMgBIgPgFIgVgJQA1h9g8jXQAQAHAOASQAQAUAKAeQARAuAJBJQAHA/gBA5IAAADIACAfIAFARIACAGIAEAHIAHAHQAVAXAQAUQAUAaANAUIABACIgsgdIgggXIAAAnIABAwIgBgBg');
this.shape_1.sett(23.2,30.1); this.shape_1.sett(23.2,30.1);
this.shape.graphics.f().s("#000000").ss(0.1,1,1).p("AAAAAQAAAAAAAA"); this.shape.graphics.f().s('#000000').ss(0.1,1,1).p('AAAAAQAAAAAAAA');
this.shape.sett(3.8,22.4); this.shape.sett(3.8,22.4);
### ###
@ -218,7 +218,7 @@ module.exports = class SpriteParser
return unless node.type is 'NewExpression' and node.callee.property.name is 'Graphics' return unless node.type is 'NewExpression' and node.callee.property.name is 'Graphics'
blockName = node.parent.parent.parent.id.name blockName = node.parent.parent.parent.id.name
graphicsString = node.parent.parent.arguments[0].value graphicsString = node.parent.parent.arguments[0].value
localGraphics.push({p:graphicsString, bn:blockName}) localGraphics.push {p:graphicsString, bn:blockName}
@walk block, null, gatherShapeDefinitions @walk block, null, gatherShapeDefinitions
return localGraphics return localGraphics
@ -233,7 +233,7 @@ module.exports = class SpriteParser
if not name if not name
name = node.parent?.parent?.id?.name name = node.parent?.parent?.id?.name
return unless name and name.indexOf('mask') is 0 and node.property?.name is 'Shape' return unless name and name.indexOf('mask') is 0 and node.property?.name is 'Shape'
shape = { bn: name, im: true } shape = {bn: name, im: true}
localShapes.push shape localShapes.push shape
return return
return unless name.search('shape') is 0 and node.object.property?.name is 'graphics' return unless name.search('shape') is 0 and node.object.property?.name is 'graphics'
@ -243,14 +243,14 @@ module.exports = class SpriteParser
linearGradientFill = @grabFunctionArguments linearGradientFillSource.replace(/.*?lf\(/, 'lf('), true linearGradientFill = @grabFunctionArguments linearGradientFillSource.replace(/.*?lf\(/, 'lf('), true
else else
fillColor = fillCall.arguments[0]?.value ? null fillColor = fillCall.arguments[0]?.value ? null
console.error "What is this?! Not a fill!" unless fillCall.callee.property.name is 'f' console.error 'What is this?! Not a fill!' unless fillCall.callee.property.name is 'f'
strokeCall = node.parent.parent.parent.parent strokeCall = node.parent.parent.parent.parent
if strokeCall.object.callee.property.name is 'ls' if strokeCall.object.callee.property.name is 'ls'
linearGradientStrokeSource = @subSourceFromRange strokeCall.parent.range, source linearGradientStrokeSource = @subSourceFromRange strokeCall.parent.range, source
linearGradientStroke = @grabFunctionArguments linearGradientStrokeSource.replace(/.*?ls\(/, 'ls(').replace(/\).ss\(.*/, ')'), true linearGradientStroke = @grabFunctionArguments linearGradientStrokeSource.replace(/.*?ls\(/, 'ls(').replace(/\).ss\(.*/, ')'), true
else else
strokeColor = strokeCall.object.arguments?[0]?.value ? null strokeColor = strokeCall.object.arguments?[0]?.value ? null
console.error "What is this?! Not a stroke!" unless strokeCall.object.callee.property.name is 's' console.error 'What is this?! Not a stroke!' unless strokeCall.object.callee.property.name is 's'
strokeStyle = null strokeStyle = null
graphicsStatement = strokeCall.parent graphicsStatement = strokeCall.parent
if strokeColor or linearGradientStroke if strokeColor or linearGradientStroke
@ -264,7 +264,7 @@ module.exports = class SpriteParser
drawEllipse = @grabFunctionArguments drawEllipseSource.replace(/.*?de\(/, 'de('), true drawEllipse = @grabFunctionArguments drawEllipseSource.replace(/.*?de\(/, 'de('), true
else else
path = graphicsStatement.arguments?[0]?.value ? null path = graphicsStatement.arguments?[0]?.value ? null
console.error "What is this?! Not a path!" unless graphicsStatement.callee.property.name is 'p' console.error 'What is this?! Not a path!' unless graphicsStatement.callee.property.name is 'p'
body = graphicsStatement.parent.parent.body body = graphicsStatement.parent.parent.body
graphicsStatementIndex = _.indexOf body, graphicsStatement.parent graphicsStatementIndex = _.indexOf body, graphicsStatement.parent
t = body[graphicsStatementIndex + 1].expression t = body[graphicsStatementIndex + 1].expression
@ -295,8 +295,8 @@ module.exports = class SpriteParser
shape.fc = fillColor if fillColor shape.fc = fillColor if fillColor
shape.lf = linearGradientFill if linearGradientFill shape.lf = linearGradientFill if linearGradientFill
shape.ls = linearGradientStroke if linearGradientStroke shape.ls = linearGradientStroke if linearGradientStroke
if name.search('shape') isnt -1 and shape.fc is "rgba(0,0,0,0.451)" and not shape.ss and not shape.sc if name.search('shape') isnt -1 and shape.fc is 'rgba(0,0,0,0.451)' and not shape.ss and not shape.sc
console.log "Skipping a shadow", name, shape, "because we're doing shadows separately now." console.log 'Skipping a shadow', name, shape, 'because we\'re doing shadows separately now.'
return return
shapeKeys.push shapeKey = @addShape shape shapeKeys.push shapeKey = @addShape shape
localShape = {bn: name, gn: shapeKey} localShape = {bn: name, gn: shapeKey}
@ -372,17 +372,17 @@ module.exports = class SpriteParser
return if name is 'get' and callExpressions.length # avoid Ease calls in the tweens return if name is 'get' and callExpressions.length # avoid Ease calls in the tweens
flattenedRanges = _.flatten [a.range for a in node.arguments] flattenedRanges = _.flatten [a.range for a in node.arguments]
range = [_.min(flattenedRanges), _.max(flattenedRanges)] range = [_.min(flattenedRanges), _.max(flattenedRanges)]
# Replace "this.<local>" references with just the "name" # Replace 'this.<local>' references with just the 'name'
argsSource = @subSourceFromRange(range, source) argsSource = @subSourceFromRange(range, source)
argsSource = argsSource.replace(/mask/g, 'this.mask') # so the mask thing will be handled correctly as a blockName in the next line argsSource = argsSource.replace(/mask/g, 'this.mask') # so the mask thing will be handled correctly as a blockName in the next line
argsSource = argsSource.replace(/this\.([a-z_0-9]+)/ig, '"$1"') # turns this.shape literal to "shape" string argsSource = argsSource.replace(/this\.([a-z_0-9]+)/ig, '"$1"') # turns this.shape literal to 'shape' string
argsSource = argsSource.replace(/cjs(.+)\)/, '"createjs$1)"') # turns cjs.Ease.get(0.5) argsSource = argsSource.replace(/cjs(.+)\)/, '"createjs$1)"') # turns cjs.Ease.get(0.5)
args = eval "[#{argsSource}]" args = eval "[#{argsSource}]"
shadowTween = args[0]?.search?('shape') is 0 and not _.find(localShapes, bn: args[0]) shadowTween = args[0]?.search?('shape') is 0 and not _.find(localShapes, bn: args[0])
shadowTween = shadowTween or args[0]?.state?[0]?.t?.search?("shape") is 0 and not _.find(localShapes, bn: args[0].state[0].t) shadowTween = shadowTween or args[0]?.state?[0]?.t?.search?('shape') is 0 and not _.find(localShapes, bn: args[0].state[0].t)
if shadowTween if shadowTween
console.log "Skipping tween", name, argsSource, args, "from localShapes", localShapes, "presumably because it's a shadow we skipped." console.log 'Skipping tween', name, argsSource, args, 'from localShapes', localShapes, 'presumably because it\'s a shadow we skipped.'
return return
callExpressions.push {n: name, a: args} callExpressions.push {n: name, a: args}
@walk node.parent.parent, null, gatherCallExpressions @walk node.parent.parent, null, gatherCallExpressions
@ -395,7 +395,7 @@ module.exports = class SpriteParser
block = block.expression.object.right.body block = block.expression.object.right.body
localArgs = [] localArgs = []
gatherAddChildCalls = (node) => gatherAddChildCalls = (node) =>
return unless node.type is "Identifier" and node.name is "addChild" return unless node.type is 'Identifier' and node.name is 'addChild'
args = node.parent.parent.arguments args = node.parent.parent.arguments
args = (arg.property.name for arg in args) args = (arg.property.name for arg in args)
localArgs.push arg for arg in args localArgs.push arg for arg in args
@ -427,18 +427,18 @@ var p; // shortcut to reference prototypes
// Layer 7 // Layer 7
this.shape = new cjs.Shape(); this.shape = new cjs.Shape();
this.shape.graphics.f("#4F6877").s().p("AgsAxQgSgVgB"); this.shape.graphics.f('#4F6877').s().p('AgsAxQgSgVgB');
this.shape.setTransform(283.1,146.1); this.shape.setTransform(283.1,146.1);
// Layer 7 2 // Layer 7 2
this.shape_1 = new cjs.Shape(); this.shape_1 = new cjs.Shape();
this.shape_1.graphics.f("rgba(255,255,255,0.4)").s().p("ArTs0QSMB7EbVGQhsBhiGBHQjg1IvVkhg"); this.shape_1.graphics.f('rgba(255,255,255,0.4)').s().p('ArTs0QSMB7EbVGQhsBhiGBHQjg1IvVkhg');
this.shape_1.setTransform(400.2,185.5); this.shape_1.setTransform(400.2,185.5);
this.timeline.addTween(cjs.Tween.get({}).to({state:[]}).to({state:[{t:this.shape}]},7).to({state:[]},2).wait(6)); this.timeline.addTween(cjs.Tween.get({}).to({state:[]}).to({state:[{t:this.shape}]},7).to({state:[]},2).wait(6));
// Wing // Wing
this.instance_9 = new lib.Wing_Animation("synched",0); this.instance_9 = new lib.Wing_Animation('synched',0);
this.instance_9.setTransform(313.9,145.6,1,1,0,0,0,49,-83.5); this.instance_9.setTransform(313.9,145.6,1,1,0,0,0,49,-83.5);
this.timeline.addTween(cjs.Tween.get(this.instance_9).to({y:128,startPosition:7},7).wait(1)); this.timeline.addTween(cjs.Tween.get(this.instance_9).to({y:128,startPosition:7},7).wait(1));
@ -455,11 +455,11 @@ p.nominalBounds = new cjs.Rectangle(7.1,48.9,528.7,431.1);
// Isolation Mode // Isolation Mode
this.shape = new cjs.Shape(); this.shape = new cjs.Shape();
this.shape.graphics.f("#1D2226").s().p("AgVAwQgUgdgN"); this.shape.graphics.f('#1D2226').s().p('AgVAwQgUgdgN');
this.shape.setTransform(75,25.8); this.shape.setTransform(75,25.8);
this.shape_1 = new cjs.Shape(); this.shape_1 = new cjs.Shape();
this.shape_1.graphics.f("#1D2226").s().p("AgnBXQACABAF"); this.shape_1.graphics.f('#1D2226').s().p('AgnBXQACABAF');
this.shape_1.setTransform(80.8,22); this.shape_1.setTransform(80.8,22);
this.addChild(this.shape_1,this.shape); this.addChild(this.shape_1,this.shape);
@ -471,15 +471,15 @@ p.nominalBounds = new cjs.Rectangle(5.8,0,87.9,85);
// Layer 1 // Layer 1
this.shape = new cjs.Shape(); this.shape = new cjs.Shape();
this.shape.graphics.f("#DBDDBC").s().p("Ag3BeQgCgRA"); this.shape.graphics.f('#DBDDBC').s().p('Ag3BeQgCgRA');
this.shape.setTransform(10.6,19.7,1.081,1.081); this.shape.setTransform(10.6,19.7,1.081,1.081);
this.shape_1 = new cjs.Shape(); this.shape_1 = new cjs.Shape();
this.shape_1.graphics.f("#1D2226").s().p("AB4CDQgGg"); this.shape_1.graphics.f('#1D2226').s().p('AB4CDQgGg');
this.shape_1.setTransform(19.9,17.6,1.081,1.081); this.shape_1.setTransform(19.9,17.6,1.081,1.081);
this.shape_2 = new cjs.Shape(); this.shape_2 = new cjs.Shape();
this.shape_2.graphics.f("#605E4A").s().p("AiECbQgRg"); this.shape_2.graphics.f('#605E4A').s().p('AiECbQgRg');
this.shape_2.setTransform(19.5,18.4,1.081,1.081); this.shape_2.setTransform(19.5,18.4,1.081,1.081);
this.addChild(this.shape_2,this.shape_1,this.shape); this.addChild(this.shape_2,this.shape_1,this.shape);

View file

@ -8,7 +8,7 @@ d2r = (degrees) -> degrees / 180 * Math.PI
MAX_ZOOM = 8 MAX_ZOOM = 8
MIN_ZOOM = 0.1 MIN_ZOOM = 0.1
DEFAULT_ZOOM = 2.0 DEFAULT_ZOOM = 2.0
DEFAULT_TARGET = {x:0, y:0} DEFAULT_TARGET = {x: 0, y: 0}
DEFAULT_TIME = 1000 DEFAULT_TIME = 1000
STANDARD_ZOOM_WIDTH = 924 STANDARD_ZOOM_WIDTH = 924
STANDARD_ZOOM_HEIGHT = 589 STANDARD_ZOOM_HEIGHT = 589
@ -78,7 +78,7 @@ module.exports = class Camera extends CocoClass
console.log "Restricted given horizontal field of view to #{r2d(hFOV)} to #{r2d(@hFOV)}." console.log "Restricted given horizontal field of view to #{r2d(hFOV)} to #{r2d(@hFOV)}."
@vFOV = 2 * Math.atan(Math.tan(@hFOV / 2) * @canvasHeight / @canvasWidth) @vFOV = 2 * Math.atan(Math.tan(@hFOV / 2) * @canvasHeight / @canvasWidth)
if @vFOV > Math.PI if @vFOV > Math.PI
console.log "Vertical field of view problem: expected canvas not to be taller than it is wide with high field of view." console.log 'Vertical field of view problem: expected canvas not to be taller than it is wide with high field of view.'
@vFOV = Math.PI - epsilon @vFOV = Math.PI - epsilon
calculateAxisConversionFactors: -> calculateAxisConversionFactors: ->
@ -210,9 +210,9 @@ module.exports = class Camera extends CocoClass
right = Math.max(worldBounds[0].x, worldBounds[1].x) right = Math.max(worldBounds[0].x, worldBounds[1].x)
bottom -= 1 if top is bottom bottom -= 1 if top is bottom
right += 1 if left is right right += 1 if left is right
p1 = @worldToSurface({x:left, y:top}) p1 = @worldToSurface({x: left, y: top})
p2 = @worldToSurface({x:right, y:bottom}) p2 = @worldToSurface({x: right, y: bottom})
{x:p1.x, y:p1.y, width:p2.x-p1.x, height:p2.y-p1.y} {x: p1.x, y: p1.y, width: p2.x-p1.x, height: p2.y-p1.y}
calculateMinMaxZoom: -> calculateMinMaxZoom: ->
# Zoom targets are always done in Surface coordinates. # Zoom targets are always done in Surface coordinates.
@ -314,6 +314,7 @@ module.exports = class Camera extends CocoClass
lock: -> lock: ->
@target = @currentTarget @target = @currentTarget
@locked = true @locked = true
unlock: -> unlock: ->
@locked = false @locked = false

View file

@ -25,6 +25,6 @@ module.exports = class CameraBorder extends createjs.Container
i = width i = width
while i while i
opacity = 3 * (1 - (i/width)) / width opacity = 3 * (1 - (i/width)) / width
@border.graphics.setStrokeStyle(i,"round").beginStroke("rgba(0,0,0,#{opacity})").drawRect(bounds.x, bounds.y, bounds.width, bounds.height) @border.graphics.setStrokeStyle(i, 'round').beginStroke("rgba(0,0,0,#{opacity})").drawRect(bounds.x, bounds.y, bounds.width, bounds.height)
i -= 1 i -= 1
@border.cache bounds.x, bounds.y, bounds.width, bounds.height @border.cache bounds.x, bounds.y, bounds.width, bounds.height

View file

@ -11,21 +11,21 @@ module.exports = class CastingScreen extends CocoClass
options ?= {} options ?= {}
@camera = options.camera @camera = options.camera
@layer = options.layer @layer = options.layer
console.error @toString(), "needs a camera." unless @camera console.error @toString(), 'needs a camera.' unless @camera
console.error @toString(), "needs a layer." unless @layer console.error @toString(), 'needs a layer.' unless @layer
@build() @build()
onCastingBegins: (e) -> @show() unless e.preload onCastingBegins: (e) -> @show() unless e.preload
onCastingEnds: (e) -> @hide() onCastingEnds: (e) -> @hide()
toString: -> "<CastingScreen>" toString: -> '<CastingScreen>'
build: -> build: ->
@dimLayer = new createjs.Container() @dimLayer = new createjs.Container()
@dimLayer.mouseEnabled = @dimLayer.mouseChildren = false @dimLayer.mouseEnabled = @dimLayer.mouseChildren = false
@dimLayer.layerIndex = -11 @dimLayer.layerIndex = -11
@dimLayer.addChild @dimScreen = new createjs.Shape() @dimLayer.addChild @dimScreen = new createjs.Shape()
@dimScreen.graphics.beginFill("rgba(0,0,0,0.5)").rect 0, 0, @camera.canvasWidth, @camera.canvasHeight @dimScreen.graphics.beginFill('rgba(0,0,0,0.5)').rect 0, 0, @camera.canvasWidth, @camera.canvasHeight
@dimLayer.alpha = 0 @dimLayer.alpha = 0
@layer.addChild @dimLayer @layer.addChild @dimLayer
@dimLayer.addChild @makeProgressBar() @dimLayer.addChild @makeProgressBar()
@ -34,7 +34,7 @@ module.exports = class CastingScreen extends CocoClass
onWorldLoadProgressChanged: (e) -> onWorldLoadProgressChanged: (e) ->
if new Date().getTime() - @t0 > 500 if new Date().getTime() - @t0 > 500
createjs.Tween.removeTweens @progressBar createjs.Tween.removeTweens @progressBar
createjs.Tween.get(@progressBar).to({scaleX:e.progress}, 200) createjs.Tween.get(@progressBar).to({scaleX: e.progress}, 200)
makeProgressBar: -> makeProgressBar: ->
BAR_PIXEL_HEIGHT = 3 BAR_PIXEL_HEIGHT = 3
@ -44,8 +44,8 @@ module.exports = class CastingScreen extends CocoClass
barY = 3 * (@camera.canvasHeight / 5) barY = 3 * (@camera.canvasHeight / 5)
g = new createjs.Graphics() g = new createjs.Graphics()
g.beginFill(createjs.Graphics.getRGB(255,255, 255)) g.beginFill(createjs.Graphics.getRGB(255, 255, 255))
g.drawRoundRect(0,0,pixelWidth, BAR_PIXEL_HEIGHT, 3) g.drawRoundRect(0, 0, pixelWidth, BAR_PIXEL_HEIGHT, 3)
@progressBar = new createjs.Shape(g) @progressBar = new createjs.Shape(g)
@progressBar.x = pixelMargin @progressBar.x = pixelMargin
@progressBar.y = barY @progressBar.y = barY
@ -54,7 +54,7 @@ module.exports = class CastingScreen extends CocoClass
makeCastingText: -> makeCastingText: ->
size = @camera.canvasHeight / 15 size = @camera.canvasHeight / 15
text = new createjs.Text("Casting", "#{size}px cursive", "#aaaaaa") text = new createjs.Text('Casting', "#{size}px cursive", '#aaaaaa')
text.regX = text.getMeasuredWidth() / 2 text.regX = text.getMeasuredWidth() / 2
text.regY = text.getMeasuredHeight() / 2 text.regY = text.getMeasuredHeight() / 2
text.x = @camera.canvasWidth / 2 text.x = @camera.canvasWidth / 2
@ -70,7 +70,7 @@ module.exports = class CastingScreen extends CocoClass
@progressBar.scaleX = 0 @progressBar.scaleX = 0
@dimLayer.alpha = 0 @dimLayer.alpha = 0
createjs.Tween.removeTweens @dimLayer createjs.Tween.removeTweens @dimLayer
createjs.Tween.get(@dimLayer).to({alpha:1}, 500) createjs.Tween.get(@dimLayer).to({alpha: 1}, 500)
hide: -> hide: ->
return unless @showing return unless @showing
@ -78,4 +78,4 @@ module.exports = class CastingScreen extends CocoClass
createjs.Tween.removeTweens @progressBar createjs.Tween.removeTweens @progressBar
createjs.Tween.removeTweens @dimLayer createjs.Tween.removeTweens @dimLayer
createjs.Tween.get(@dimLayer).to({alpha:0}, 500) createjs.Tween.get(@dimLayer).to({alpha: 0}, 500)

View file

@ -65,7 +65,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
super() super()
@options = _.extend($.extend(true, {}, @options), options) @options = _.extend($.extend(true, {}, @options), options)
@setThang @options.thang @setThang @options.thang
console.error @toString(), "has no ThangType!" unless @thangType console.error @toString(), 'has no ThangType!' unless @thangType
# this is a stub, use @setImageObject to swap it out for something else later # this is a stub, use @setImageObject to swap it out for something else later
@imageObject = new createjs.Container @imageObject = new createjs.Container
@ -94,7 +94,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
else else
result = @buildSpriteSheet() result = @buildSpriteSheet()
if _.isString result # async build if _.isString result # async build
@listenToOnce @thangType, 'build-complete', @setupSprite @listenToOnce @thangType, 'build-complete', @setupSprite
else else
@stillLoading = false @stillLoading = false
@actions = @thangType.getActions() @actions = @thangType.getActions()
@ -171,7 +171,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
onSurfaceTicked: (e) -> @age += e.dt onSurfaceTicked: (e) -> @age += e.dt
playNextAction: => playNextAction: =>
@playAction(@actionQueue.splice(0,1)[0]) if @actionQueue.length @playAction(@actionQueue.splice(0, 1)[0]) if @actionQueue.length
playAction: (action) -> playAction: (action) ->
return if @isRaster return if @isRaster
@ -180,7 +180,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
@show() @show()
@updateBaseScale() @updateBaseScale()
return @updateActionDirection() unless action.animation or action.container return @updateActionDirection() unless action.animation or action.container
m = if action.container then "gotoAndStop" else "gotoAndPlay" m = if action.container then 'gotoAndStop' else 'gotoAndPlay'
@imageObject.framerate = action.framerate or 20 @imageObject.framerate = action.framerate or 20
@imageObject[m] action.name @imageObject[m] action.name
reg = @getOffset 'registration' reg = @getOffset 'registration'
@ -235,7 +235,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
@handledDisplayEvents[event] = true @handledDisplayEvents[event] = true
args = JSON.parse(event[4...]) args = JSON.parse(event[4...])
pos = @options.camera.worldToSurface {x:args[0], y:args[1]} pos = @options.camera.worldToSurface {x: args[0], y: args[1]}
circle = new createjs.Shape() circle = new createjs.Shape()
circle.graphics.beginFill(args[3]).drawCircle(0, 0, args[2]*Camera.PPM) circle.graphics.beginFill(args[3]).drawCircle(0, 0, args[2]*Camera.PPM)
circle.x = pos.x circle.x = pos.x
@ -270,18 +270,17 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
window.labels.push label window.labels.push label
label.alpha = 0 label.alpha = 0
createjs.Tween.get(label) createjs.Tween.get(label)
.to({y:label.y-2, alpha:1}, 200, createjs.Ease.linear) .to({y: label.y-2, alpha: 1}, 200, createjs.Ease.linear)
.to({y:label.y-12}, 1000, createjs.Ease.linear) .to({y: label.y-12}, 1000, createjs.Ease.linear)
.to({y:label.y-22, alpha:0}, 1000, createjs.Ease.linear) .to({y: label.y-22, alpha: 0}, 1000, createjs.Ease.linear)
.call => .call =>
return if @destroyed return if @destroyed
@options.floatingLayer.removeChild label @options.floatingLayer.removeChild label
cache: -> cache: ->
bounds = @imageObject.getBounds() bounds = @imageObject.getBounds()
@imageObject.cache 0, 0, bounds.width, bounds.height @imageObject.cache 0, 0, bounds.width, bounds.height
#console.log "just cached", @thang.id, "which was at", @imageObject.x, @imageObject.y, bounds.width, bounds.height, "with scale", Math.max(@imageObject.scaleX, @imageObject.scaleY) #console.log 'just cached', @thang.id, 'which was at', @imageObject.x, @imageObject.y, bounds.width, bounds.height, 'with scale', Math.max(@imageObject.scaleX, @imageObject.scaleY)
getBobOffset: -> getBobOffset: ->
return 0 unless @thang.bobHeight return 0 unless @thang.bobHeight
@ -351,14 +350,14 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
angle = 180 - angle if angle > 90 angle = 180 - angle if angle > 90
scaleX = 0.5 + 0.5 * (90 - angle) / 90 scaleX = 0.5 + 0.5 * (90 - angle) / 90
# console.error "No thang for", @ unless @thang # console.error 'No thang for', @ unless @thang
# TODO: support using scaleFactorX/Y from the thang object # TODO: support using scaleFactorX/Y from the thang object
@imageObject.scaleX = @baseScaleX * @scaleFactor * scaleX @imageObject.scaleX = @baseScaleX * @scaleFactor * scaleX
@imageObject.scaleY = @baseScaleY * @scaleFactor * scaleY @imageObject.scaleY = @baseScaleY * @scaleFactor * scaleY
if @thang and (@thang.scaleFactor or 1) isnt @targetScaleFactor if @thang and (@thang.scaleFactor or 1) isnt @targetScaleFactor
createjs.Tween.removeTweens(@) createjs.Tween.removeTweens(@)
createjs.Tween.get(@).to({scaleFactor:@thang.scaleFactor or 1}, 2000, createjs.Ease.elasticOut) createjs.Tween.get(@).to({scaleFactor: @thang.scaleFactor or 1}, 2000, createjs.Ease.elasticOut)
@targetScaleFactor = @thang.scaleFactor or 1 @targetScaleFactor = @thang.scaleFactor or 1
updateAlpha: -> updateAlpha: ->
@ -410,7 +409,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
action = @determineAction() action = @determineAction()
isDifferent = action isnt @currentRootAction or action is null isDifferent = action isnt @currentRootAction or action is null
if not action and @thang?.actionActivated and not @stopLogging if not action and @thang?.actionActivated and not @stopLogging
console.error "action is", action, "for", @thang?.id, "from", @currentRootAction, @thang.action, @thang.getActionName?() console.error 'action is', action, 'for', @thang?.id, 'from', @currentRootAction, @thang.action, @thang.getActionName?()
@stopLogging = true @stopLogging = true
@queueAction(action) if action and (isDifferent or (@thang?.actionActivated and action.name isnt 'move')) @queueAction(action) if action and (isDifferent or (@thang?.actionActivated and action.name isnt 'move'))
@updateActionDirection() @updateActionDirection()
@ -439,7 +438,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
rootAction ?= @currentRootAction rootAction ?= @currentRootAction
return null unless relatedActions = rootAction?.relatedActions ? {} return null unless relatedActions = rootAction?.relatedActions ? {}
rotation = @getRotation() rotation = @getRotation()
if relatedActions["111111111111"] # has grid-surrounding-wall-based actions if relatedActions['111111111111'] # has grid-surrounding-wall-based actions
if @wallGrid if @wallGrid
action = '' action = ''
tileSize = 4 tileSize = 4
@ -452,25 +451,25 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
wallThangs = ['outside of the map yo'] wallThangs = ['outside of the map yo']
if wallThangs.length is 0 if wallThangs.length is 0
if y is gy and x is gx if y is gy and x is gx
action += "1" # the center wall we're placing action += '1' # the center wall we're placing
else else
action += "0" action += '0'
else if wallThangs.length is 1 else if wallThangs.length is 1
action += "1" action += '1'
else else
console.error "Overlapping walls at", x, y, "...", wallThangs console.error 'Overlapping walls at', x, y, '...', wallThangs
action += "1" action += '1'
matchedAction = '111111111111' matchedAction = '111111111111'
for relatedAction of relatedActions for relatedAction of relatedActions
if action.match(relatedAction.replace(/\?/g, '.')) if action.match(relatedAction.replace(/\?/g, '.'))
matchedAction = relatedAction matchedAction = relatedAction
break break
#console.log "returning", matchedAction, "for", @thang.id, "at", gx, gy #console.log 'returning', matchedAction, 'for', @thang.id, 'at', gx, gy
return relatedActions[matchedAction] return relatedActions[matchedAction]
else else
keys = _.keys relatedActions keys = _.keys relatedActions
index = Math.max 0, Math.floor((179 + rotation) / 360 * keys.length) index = Math.max 0, Math.floor((179 + rotation) / 360 * keys.length)
#console.log "Showing", relatedActions[keys[index]] #console.log 'Showing', relatedActions[keys[index]]
return relatedActions[keys[index]] return relatedActions[keys[index]]
value = Math.abs(rotation) value = Math.abs(rotation)
direction = null direction = null
@ -510,8 +509,8 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
Backbone.Mediator.publish ourEventName, newEvent Backbone.Mediator.publish ourEventName, newEvent
addHealthBar: -> addHealthBar: ->
return unless @thang?.health? and "health" in (@thang?.hudProperties ? []) and @options.floatingLayer return unless @thang?.health? and 'health' in (@thang?.hudProperties ? []) and @options.floatingLayer
healthColor = healthColors[@thang?.team] ? healthColors["neutral"] healthColor = healthColors[@thang?.team] ? healthColors['neutral']
healthOffset = @getOffset 'aboveHead' healthOffset = @getOffset 'aboveHead'
bar = @healthBar = createProgressBar(healthColor, healthOffset) bar = @healthBar = createProgressBar(healthColor, healthOffset)
bar.name = 'health bar' bar.name = 'health bar'
@ -660,7 +659,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
sound = e.sound ? AudioPlayer.soundForDialogue e.message, @thangType.get 'soundTriggers' sound = e.sound ? AudioPlayer.soundForDialogue e.message, @thangType.get 'soundTriggers'
@instance?.stop() @instance?.stop()
if @instance = @playSound sound, false if @instance = @playSound sound, false
@instance.addEventListener "complete", -> Backbone.Mediator.publish 'dialogue-sound-completed' @instance.addEventListener 'complete', -> Backbone.Mediator.publish 'dialogue-sound-completed'
@notifySpeechUpdated e @notifySpeechUpdated e
onClearDialogue: (e) -> onClearDialogue: (e) ->
@ -714,7 +713,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
delay = if withDelay and sound.delay then 1000 * sound.delay / createjs.Ticker.getFPS() else 0 delay = if withDelay and sound.delay then 1000 * sound.delay / createjs.Ticker.getFPS() else 0
name = AudioPlayer.nameForSoundReference sound name = AudioPlayer.nameForSoundReference sound
instance = AudioPlayer.playSound name, volume, delay, @getWorldPosition() instance = AudioPlayer.playSound name, volume, delay, @getWorldPosition()
#console.log @thang?.id, "played sound", name, "with delay", delay, "volume", volume, "and got sound instance", instance #console.log @thang?.id, 'played sound', name, 'with delay', delay, 'volume', volume, 'and got sound instance', instance
instance instance
onMove: (e) -> onMove: (e) ->
@ -723,7 +722,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
if _.isArray pos if _.isArray pos
pos = new Vector pos... pos = new Vector pos...
else if _.isString pos else if _.isString pos
return console.warn "Couldn't find target sprite", pos, "from", @options.sprites unless pos of @options.sprites return console.warn 'Couldn\'t find target sprite', pos, 'from', @options.sprites unless pos of @options.sprites
target = @options.sprites[pos].thang target = @options.sprites[pos].thang
heading = Vector.subtract(target.pos, @thang.pos).normalize() heading = Vector.subtract(target.pos, @thang.pos).normalize()
distance = @thang.pos.distance target.pos distance = @thang.pos.distance target.pos
@ -766,7 +765,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
@lastTween = createjs.Tween @lastTween = createjs.Tween
.get(@shadow.pos) .get(@shadow.pos)
.to({x:pos.x, y:pos.y}, duration, ease) .to({x: pos.x, y: pos.y}, duration, ease)
.call(endFunc) .call(endFunc)
pointToward: (pos) -> pointToward: (pos) ->

View file

@ -11,7 +11,7 @@ module.exports = class CoordinateDisplay extends createjs.Container
super() super()
@initialize() @initialize()
@camera = options.camera @camera = options.camera
console.error "CoordinateDisplay needs camera." unless @camera console.error 'CoordinateDisplay needs camera.' unless @camera
@build() @build()
@show = _.debounce @show, 125 @show = _.debounce @show, 125
Backbone.Mediator.subscribe(channel, @[func], @) for channel, func of @subscriptions Backbone.Mediator.subscribe(channel, @[func], @) for channel, func of @subscriptions
@ -24,10 +24,10 @@ module.exports = class CoordinateDisplay extends createjs.Container
build: -> build: ->
@mouseEnabled = @mouseChildren = false @mouseEnabled = @mouseChildren = false
@addChild @background = new createjs.Shape() @addChild @background = new createjs.Shape()
@addChild @label = new createjs.Text("", "bold 16px Arial", "#FFFFFF") @addChild @label = new createjs.Text('', 'bold 16px Arial', '#FFFFFF')
@label.name = 'Coordinate Display Text' @label.name = 'Coordinate Display Text'
@label.shadow = new createjs.Shadow("#000000", 1, 1, 0) @label.shadow = new createjs.Shadow('#000000', 1, 1, 0)
@background.name = "Coordinate Display Background" @background.name = 'Coordinate Display Background'
onMouseOver: (e) -> @mouseInBounds = true onMouseOver: (e) -> @mouseInBounds = true
onMouseOut: (e) -> @mouseInBounds = false onMouseOut: (e) -> @mouseInBounds = false
@ -71,8 +71,8 @@ module.exports = class CoordinateDisplay extends createjs.Container
@label.regY = @background.regY = height / 2 - margin @label.regY = @background.regY = height / 2 - margin
@background.graphics @background.graphics
.clear() .clear()
.beginFill("rgba(0, 0, 0, 0.4)") .beginFill('rgba(0,0,0,0.4)')
.beginStroke("rgba(0, 0, 0, 0.6)") .beginStroke('rgba(0,0,0,0.6)')
.setStrokeStyle(1) .setStrokeStyle(1)
.drawRoundRect(0, 0, width, height, radius) .drawRoundRect(0, 0, width, height, radius)
.endFill() .endFill()

View file

@ -8,7 +8,7 @@ module.exports = class DebugDisplay extends createjs.Container
@initialize() @initialize()
@canvasWidth = options.canvasWidth @canvasWidth = options.canvasWidth
@canvasHeight = options.canvasHeight @canvasHeight = options.canvasHeight
console.error "DebugDisplay needs canvasWidth/Height." unless @canvasWidth and @canvasHeight console.error 'DebugDisplay needs canvasWidth/Height.' unless @canvasWidth and @canvasHeight
@build() @build()
@onSetDebug debug: true @onSetDebug debug: true
Backbone.Mediator.subscribe(channel, @[func], @) for channel, func of @subscriptions Backbone.Mediator.subscribe(channel, @[func], @) for channel, func of @subscriptions
@ -25,7 +25,7 @@ module.exports = class DebugDisplay extends createjs.Container
build: -> build: ->
@mouseEnabled = @mouseChildren = false @mouseEnabled = @mouseChildren = false
@addChild @frameText = new createjs.Text "...", "20px Arial", "#FFF" @addChild @frameText = new createjs.Text '...', '20px Arial', '#FFF'
@frameText.name = 'frame text' @frameText.name = 'frame text'
@frameText.x = @canvasWidth - 50 @frameText.x = @canvasWidth - 50
@frameText.y = @canvasHeight - 25 @frameText.y = @canvasHeight - 25
@ -41,5 +41,5 @@ module.exports = class DebugDisplay extends createjs.Container
@lastFrameSecondStart = time @lastFrameSecondStart = time
@framesRenderedThisSecond = 0 @framesRenderedThisSecond = 0
@frameText.text = Math.round(currentFrame) + (if @fps? then " - " + @fps + ' fps' else '') @frameText.text = Math.round(currentFrame) + (if @fps? then ' - ' + @fps + ' fps' else '')
@frameText.x = @canvasWidth - @frameText.getMeasuredWidth() - 10 @frameText.x = @canvasWidth - @frameText.getMeasuredWidth() - 10

View file

@ -14,14 +14,14 @@ module.exports = class Dimmer extends CocoClass
options ?= {} options ?= {}
@camera = options.camera @camera = options.camera
@layer = options.layer @layer = options.layer
console.error @toString(), "needs a camera." unless @camera console.error @toString(), 'needs a camera.' unless @camera
console.error @toString(), "needs a layer." unless @layer console.error @toString(), 'needs a layer.' unless @layer
@build() @build()
@updateDimMask = _.throttle @updateDimMask, 10 @updateDimMask = _.throttle @updateDimMask, 10
@highlightedThangIDs = [] @highlightedThangIDs = []
@sprites = {} @sprites = {}
toString: -> "<Dimmer>" toString: -> '<Dimmer>'
build: -> build: ->
@dimLayer = new createjs.Container() @dimLayer = new createjs.Container()
@ -29,7 +29,7 @@ module.exports = class Dimmer extends CocoClass
@dimLayer.layerIndex = -10 @dimLayer.layerIndex = -10
@dimLayer.addChild @dimScreen = new createjs.Shape() @dimLayer.addChild @dimScreen = new createjs.Shape()
@dimLayer.addChild @dimMask = new createjs.Shape() @dimLayer.addChild @dimMask = new createjs.Shape()
@dimScreen.graphics.beginFill("rgba(0,0,0,0.5)").rect 0, 0, @camera.canvasWidth, @camera.canvasHeight @dimScreen.graphics.beginFill('rgba(0,0,0,0.5)').rect 0, 0, @camera.canvasWidth, @camera.canvasHeight
@dimMask.compositeOperation = 'destination-out' @dimMask.compositeOperation = 'destination-out'
@dimLayer.cache 0, 0, @camera.canvasWidth, @camera.canvasHeight @dimLayer.cache 0, 0, @camera.canvasWidth, @camera.canvasHeight
@ -70,6 +70,6 @@ module.exports = class Dimmer extends CocoClass
sup = x: sprite.imageObject.x, y: sprite.imageObject.y sup = x: sprite.imageObject.x, y: sprite.imageObject.y
cap = @camera.surfaceToCanvas sup cap = @camera.surfaceToCanvas sup
r = 50 * @camera.zoom # TODO: find better way to get the radius based on the sprite's size r = 50 * @camera.zoom # TODO: find better way to get the radius based on the sprite's size
@dimMask.graphics.beginRadialGradientFill(["rgba(0,0,0,1)", "rgba(0,0,0,0)"], [0.5, 1], cap.x, cap.y, 0, cap.x, cap.y, r).drawCircle(cap.x, cap.y, r) @dimMask.graphics.beginRadialGradientFill(['rgba(0,0,0,1)', 'rgba(0,0,0,0)'], [0.5, 1], cap.x, cap.y, 0, cap.x, cap.y, r).drawCircle(cap.x, cap.y, r)
@dimLayer.updateCache 0, 0, @camera.canvasWidth, @camera.canvasHeight @dimLayer.updateCache 0, 0, @camera.canvasWidth, @camera.canvasHeight

View file

@ -25,5 +25,4 @@ Dropper = class Dropper
drop: -> drop: ->
return @drop_counter > 0 return @drop_counter > 0
module.exports = new Dropper() module.exports = new Dropper()

View file

@ -1,9 +1,9 @@
CocoClass = require 'lib/CocoClass' CocoClass = require 'lib/CocoClass'
module.exports = class Label extends CocoClass module.exports = class Label extends CocoClass
@STYLE_DIALOGUE = "dialogue" # A speech bubble from a script @STYLE_DIALOGUE = 'dialogue' # A speech bubble from a script
@STYLE_SAY = "say" # A piece of text generated from the world @STYLE_SAY = 'say' # A piece of text generated from the world
@STYLE_NAME = "name" # A name like Scott set up for the Wizard @STYLE_NAME = 'name' # A name like Scott set up for the Wizard
# We might want to combine 'say' and 'name'; they're very similar # We might want to combine 'say' and 'name'; they're very similar
# Nick designed 'say' based off of Scott's 'name' back when they were using two systems # Nick designed 'say' based off of Scott's 'name' back when they were using two systems
@ -16,9 +16,9 @@ module.exports = class Label extends CocoClass
@camera = options.camera @camera = options.camera
@layer = options.layer @layer = options.layer
@style = options.style ? Label.STYLE_SAY @style = options.style ? Label.STYLE_SAY
console.error @toString(), "needs a sprite." unless @sprite console.error @toString(), 'needs a sprite.' unless @sprite
console.error @toString(), "needs a camera." unless @camera console.error @toString(), 'needs a camera.' unless @camera
console.error @toString(), "needs a layer." unless @layer console.error @toString(), 'needs a layer.' unless @layer
@setText options.text if options.text @setText options.text if options.text
destroy: -> destroy: ->
@ -58,15 +58,15 @@ module.exports = class Label extends CocoClass
st = {dialogue: 'D', say: 'S', name: 'N'}[@style] st = {dialogue: 'D', say: 'S', name: 'N'}[@style]
o.marginX = {D: 5, S: 6, N: 3}[st] o.marginX = {D: 5, S: 6, N: 3}[st]
o.marginY = {D: 6, S: 4, N: 3}[st] o.marginY = {D: 6, S: 4, N: 3}[st]
o.fontWeight = {D: "bold", S: "bold", N: "bold"}[st] o.fontWeight = {D: 'bold', S: 'bold', N: 'bold'}[st]
o.shadow = {D: false, S: true, N: true}[st] o.shadow = {D: false, S: true, N: true}[st]
o.shadowColor = {D: "#FFF", S: "#000", N: "#FFF"}[st] o.shadowColor = {D: '#FFF', S: '#000', N: '#FFF'}[st]
o.fontSize = {D: 25, S: 12, N: 12}[st] o.fontSize = {D: 25, S: 12, N: 12}[st]
fontFamily = {D: "Arial", S: "Arial", N: "Arial"}[st] fontFamily = {D: 'Arial', S: 'Arial', N: 'Arial'}[st]
o.fontDescriptor = "#{o.fontWeight} #{o.fontSize}px #{fontFamily}" o.fontDescriptor = "#{o.fontWeight} #{o.fontSize}px #{fontFamily}"
o.fontColor = {D: "#000", S: "#FFF", N: "#00a"}[st] o.fontColor = {D: '#000', S: '#FFF', N: '#00a'}[st]
o.backgroundFillColor = {D: "white", S: "rgba(0, 0, 0, 0.4)", N: "rgba(255, 255, 255, 0.5)"}[st] o.backgroundFillColor = {D: 'white', S: 'rgba(0,0,0,0.4)', N: 'rgba(255,255,255,0.5)'}[st]
o.backgroundStrokeColor = {D: "black", S: "rgba(0, 0, 0, .6)", N: "rgba(0, 0, 0, 0.0)"}[st] o.backgroundStrokeColor = {D: 'black', S: 'rgba(0,0,0,0.6)', N: 'rgba(0,0,0,0)'}[st]
o.backgroundStrokeStyle = {D: 2, S: 1, N: 1}[st] o.backgroundStrokeStyle = {D: 2, S: 1, N: 1}[st]
o.backgroundBorderRadius = {D: 10, S: 3, N: 3}[st] o.backgroundBorderRadius = {D: 10, S: 3, N: 3}[st]
o.layerPriority = {D: 10, S: 5, N: 5}[st] o.layerPriority = {D: 10, S: 5, N: 5}[st]
@ -169,7 +169,7 @@ module.exports = class Label extends CocoClass
textWidth = 0 textWidth = 0
for word in words for word in words
row.push(word) row.push(word)
text = new createjs.Text(_.string.join(' ', row...), fontDescriptor, "#000") text = new createjs.Text(_.string.join(' ', row...), fontDescriptor, '#000')
width = text.getMeasuredWidth() width = text.getMeasuredWidth()
if width > maxWidth if width > maxWidth
if row.length is 1 # one long word, truncate it if row.length is 1 # one long word, truncate it
@ -186,5 +186,5 @@ module.exports = class Label extends CocoClass
textWidth = Math.max(textWidth, width) textWidth = Math.max(textWidth, width)
rows.push(row) if row.length rows.push(row) if row.length
for row, i in rows for row, i in rows
rows[i] = _.string.join(" ", row...) rows[i] = _.string.join(' ', row...)
text: _.string.join("\n", rows...), textWidth: textWidth text: _.string.join("\n", rows...), textWidth: textWidth

View file

@ -18,10 +18,10 @@
### ###
module.exports = class Layer extends createjs.Container module.exports = class Layer extends createjs.Container
@TRANSFORM_CHILD = "child" # Layer transform is managed by its parents @TRANSFORM_CHILD = 'child' # Layer transform is managed by its parents
@TRANSFORM_SURFACE = "surface" # Layer moves/scales/zooms with the Surface of the World @TRANSFORM_SURFACE = 'surface' # Layer moves/scales/zooms with the Surface of the World
@TRANSFORM_SURFACE_TEXT = "surface_text" # Layer moves with the Surface but is size-independent @TRANSFORM_SURFACE_TEXT = 'surface_text' # Layer moves with the Surface but is size-independent
@TRANSFORM_SCREEN = "screen" # Layer stays fixed to the screen (different from child?) @TRANSFORM_SCREEN = 'screen' # Layer stays fixed to the screen (different from child?)
subscriptions: subscriptions:
'camera:zoom-updated': 'onZoomUpdated' 'camera:zoom-updated': 'onZoomUpdated'
@ -30,11 +30,11 @@ module.exports = class Layer extends createjs.Container
super() super()
@initialize() @initialize()
options ?= {} options ?= {}
@name = options.name ? "Unnamed" @name = options.name ? 'Unnamed'
@layerPriority = options.layerPriority ? 0 @layerPriority = options.layerPriority ? 0
@transformStyle = options.transform ? Layer.TRANSFORM_CHILD @transformStyle = options.transform ? Layer.TRANSFORM_CHILD
@camera = options.camera @camera = options.camera
console.error @toString(), "needs a camera." unless @camera console.error @toString(), 'needs a camera.' unless @camera
@updateLayerOrder = _.throttle @updateLayerOrder, 1000 / 30 # Don't call multiple times in one frame; 30 FPS is probably good enough @updateLayerOrder = _.throttle @updateLayerOrder, 1000 / 30 # Don't call multiple times in one frame; 30 FPS is probably good enough
Backbone.Mediator.subscribe(channel, @[func], @) for channel, func of @subscriptions Backbone.Mediator.subscribe(channel, @[func], @) for channel, func of @subscriptions
@ -60,7 +60,7 @@ module.exports = class Layer extends createjs.Container
child.scaleY *= @scaleY child.scaleY *= @scaleY
updateLayerOrder: => updateLayerOrder: =>
#console.log @, @toString(), "sorting children", _.clone @children if @name is 'Default' #console.log @, @toString(), 'sorting children', _.clone @children if @name is 'Default'
@sortChildren @layerOrderComparator @sortChildren @layerOrderComparator
layerOrderComparator: (a, b) -> layerOrderComparator: (a, b) ->

View file

@ -1,5 +1,4 @@
module.exports = class Letterbox extends createjs.Container module.exports = class Letterbox extends createjs.Container
subscriptions: subscriptions:
'level-set-letterbox': 'onSetLetterbox' 'level-set-letterbox': 'onSetLetterbox'
@ -8,7 +7,7 @@ module.exports = class Letterbox extends createjs.Container
@initialize() @initialize()
@canvasWidth = options.canvasWidth @canvasWidth = options.canvasWidth
@canvasHeight = options.canvasHeight @canvasHeight = options.canvasHeight
console.error "Letterbox needs canvasWidth/Height." unless @canvasWidth and @canvasHeight console.error 'Letterbox needs canvasWidth/Height.' unless @canvasWidth and @canvasHeight
@build() @build()
Backbone.Mediator.subscribe(channel, @[func], @) for channel, func of @subscriptions Backbone.Mediator.subscribe(channel, @[func], @) for channel, func of @subscriptions
@ -16,7 +15,7 @@ module.exports = class Letterbox extends createjs.Container
@mouseEnabled = @mouseChildren = false @mouseEnabled = @mouseChildren = false
@matteHeight = 0.10 * @canvasHeight @matteHeight = 0.10 * @canvasHeight
@upperMatte = new createjs.Shape() @upperMatte = new createjs.Shape()
@upperMatte.graphics.beginFill("black").rect(0, 0, @canvasWidth, @matteHeight) @upperMatte.graphics.beginFill('black').rect(0, 0, @canvasWidth, @matteHeight)
@lowerMatte = @upperMatte.clone() @lowerMatte = @upperMatte.clone()
@upperMatte.x = @lowerMatte.x = 0 @upperMatte.x = @lowerMatte.x = 0
@upperMatte.y = -@matteHeight @upperMatte.y = -@matteHeight

View file

@ -15,9 +15,9 @@ module.exports = class Mark extends CocoClass
@camera = options.camera @camera = options.camera
@layer = options.layer @layer = options.layer
@thangType = options.thangType @thangType = options.thangType
console.error @toString(), "needs a name." unless @name console.error @toString(), 'needs a name.' unless @name
console.error @toString(), "needs a camera." unless @camera console.error @toString(), 'needs a camera.' unless @camera
console.error @toString(), "needs a layer." unless @layer console.error @toString(), 'needs a layer.' unless @layer
@build() @build()
destroy: -> destroy: ->
@ -58,7 +58,7 @@ module.exports = class Mark extends CocoClass
else if @name is 'debug' then @buildDebug() else if @name is 'debug' then @buildDebug()
else if @name.match(/.+(Range|Distance|Radius)$/) then @buildRadius(@name) else if @name.match(/.+(Range|Distance|Radius)$/) then @buildRadius(@name)
else if @thangType then @buildSprite() else if @thangType then @buildSprite()
else console.error "Don't know how to build mark for", @name else console.error 'Don\'t know how to build mark for', @name
@mark?.mouseEnabled = false @mark?.mouseEnabled = false
@ @
@ -77,7 +77,7 @@ module.exports = class Mark extends CocoClass
shape.graphics.setStrokeStyle 5 shape.graphics.setStrokeStyle 5
shape.graphics.beginStroke color shape.graphics.beginStroke color
shape.graphics.beginFill color.replace('0.5', '0.25') shape.graphics.beginFill color.replace('0.5', '0.25')
if @sprite.thang.shape in ["ellipsoid", "disc"] if @sprite.thang.shape in ['ellipsoid', 'disc']
shape.drawEllipse 0, 0, w, h shape.drawEllipse 0, 0, w, h
else else
shape.graphics.drawRect -w / 2, -h / 2, w, h shape.graphics.drawRect -w / 2, -h / 2, w, h
@ -86,20 +86,20 @@ module.exports = class Mark extends CocoClass
@mark.addChild shape @mark.addChild shape
if @sprite.thang.drawsBoundsStyle is 'border-text' if @sprite.thang.drawsBoundsStyle is 'border-text'
text = new createjs.Text "" + @drawsBoundsIndex, "20px Arial", color.replace('0.5', '1') text = new createjs.Text '' + @drawsBoundsIndex, '20px Arial', color.replace('0.5', '1')
text.regX = text.getMeasuredWidth() / 2 text.regX = text.getMeasuredWidth() / 2
text.regY = text.getMeasuredHeight() / 2 text.regY = text.getMeasuredHeight() / 2
text.shadow = new createjs.Shadow("#000000", 1, 1, 0) text.shadow = new createjs.Shadow('#000000', 1, 1, 0)
@mark.addChild text @mark.addChild text
else if @sprite.thang.drawsBoundsStyle is 'corner-text' else if @sprite.thang.drawsBoundsStyle is 'corner-text'
return if @sprite.thang.world.age is 0 return if @sprite.thang.world.age is 0
letter = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[@drawsBoundsIndex % 26] letter = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[@drawsBoundsIndex % 26]
text = new createjs.Text letter, "14px Arial", "#333333" # color.replace('0.5', '1') text = new createjs.Text letter, '14px Arial', '#333333' # color.replace('0.5', '1')
text.x = -w / 2 + 2 text.x = -w / 2 + 2
text.y = -h / 2 + 2 text.y = -h / 2 + 2
@mark.addChild text @mark.addChild text
else else
console.warn @sprite.thang.id, "didn't know how to draw bounds style:", @sprite.thang.drawsBoundsStyle console.warn @sprite.thang.id, 'didn\'t know how to draw bounds style:', @sprite.thang.drawsBoundsStyle
if w > 0 and h > 0 if w > 0 and h > 0
@mark.cache -w / 2, -h / 2, w, h, 2 @mark.cache -w / 2, -h / 2, w, h, 2
@ -118,7 +118,7 @@ module.exports = class Mark extends CocoClass
height *= Camera.PPM * @camera.y2x # TODO: doesn't work with rotation height *= Camera.PPM * @camera.y2x # TODO: doesn't work with rotation
@mark = new createjs.Shape() @mark = new createjs.Shape()
@mark.mouseEnabled = false @mark.mouseEnabled = false
@mark.graphics.beginFill "rgba(0, 0, 0, #{alpha})" @mark.graphics.beginFill "rgba(0,0,0,#{alpha})"
if @sprite.thang.shape in ['ellipsoid', 'disc'] if @sprite.thang.shape in ['ellipsoid', 'disc']
@mark.graphics.drawEllipse 0, 0, width, height @mark.graphics.drawEllipse 0, 0, width, height
else else
@ -132,16 +132,16 @@ module.exports = class Mark extends CocoClass
buildRadius: (range) -> buildRadius: (range) ->
alpha = 0.15 alpha = 0.15
colors = colors =
voiceRange: "rgba(0, 145, 0, #{alpha})" voiceRange: "rgba(0,145,0,#{alpha})"
visualRange: "rgba(0, 0, 145, #{alpha})" visualRange: "rgba(0,0,145,#{alpha})"
attackRange: "rgba(145, 0, 0, #{alpha})" attackRange: "rgba(145,0,0,#{alpha})"
# Fallback colors which work on both dungeon and grass tiles # Fallback colors which work on both dungeon and grass tiles
extraColors = [ extraColors = [
"rgba(145, 0, 145, #{alpha})" "rgba(145,0,145,#{alpha})"
"rgba(0, 145, 145, #{alpha})" "rgba(0,145,145,#{alpha})"
"rgba(145, 105, 0, #{alpha})" "rgba(145,105,0,#{alpha})"
"rgba(225, 125, 0, #{alpha})" "rgba(225,125,0,#{alpha})"
] ]
# Find the index of this range, to find the next-smallest radius # Find the index of this range, to find the next-smallest radius
@ -179,7 +179,7 @@ module.exports = class Mark extends CocoClass
[w, h] = [Math.max(PX, @sprite.thang.width * Camera.PPM), Math.max(PX, @sprite.thang.height * Camera.PPM) * @camera.y2x] [w, h] = [Math.max(PX, @sprite.thang.width * Camera.PPM), Math.max(PX, @sprite.thang.height * Camera.PPM) * @camera.y2x]
@mark.alpha = 0.5 @mark.alpha = 0.5
@mark.graphics.beginFill '#abcdef' @mark.graphics.beginFill '#abcdef'
if @sprite.thang.shape in ["ellipsoid", "disc"] if @sprite.thang.shape in ['ellipsoid', 'disc']
[w, h] = [Math.max(PX, w, h), Math.max(PX, w, h)] [w, h] = [Math.max(PX, w, h), Math.max(PX, w, h)]
@mark.graphics.drawCircle 0, 0, w / 2 @mark.graphics.drawCircle 0, 0, w / 2
else else
@ -248,11 +248,11 @@ module.exports = class Mark extends CocoClass
if @name is 'shadow' if @name is 'shadow'
worldZ = @sprite.thang.pos.z - @sprite.thang.depth / 2 + @sprite.getBobOffset() worldZ = @sprite.thang.pos.z - @sprite.thang.depth / 2 + @sprite.getBobOffset()
@mark.alpha = @alpha * 0.451 / Math.sqrt(worldZ / 2 + 1) @mark.alpha = @alpha * 0.451 / Math.sqrt(worldZ / 2 + 1)
else if @name isnt "bounds" else if @name isnt 'bounds'
@mark.alpha = @alpha @mark.alpha = @alpha
updateRotation: -> updateRotation: ->
if @name is 'debug' or (@name is 'shadow' and @sprite.thang?.shape in ["rectangle", "box"]) if @name is 'debug' or (@name is 'shadow' and @sprite.thang?.shape in ['rectangle', 'box'])
@mark.rotation = @sprite.thang.rotation * 180 / Math.PI @mark.rotation = @sprite.thang.rotation * 180 / Math.PI
updateScale: -> updateScale: ->
@ -271,7 +271,7 @@ module.exports = class Mark extends CocoClass
@mark.scaleX = thang.scaleFactor ? thang.scaleFactorX ? 1 @mark.scaleX = thang.scaleFactor ? thang.scaleFactorX ? 1
@mark.scaleY = thang.scaleFactor ? thang.scaleFactorY ? 1 @mark.scaleY = thang.scaleFactor ? thang.scaleFactorY ? 1
return unless @name in ["selection", "target", "repair", "highlight"] return unless @name in ['selection', 'target', 'repair', 'highlight']
# scale these marks to 10m (100px). Adjust based on sprite size. # scale these marks to 10m (100px). Adjust based on sprite size.
factor = 0.3 # default size: 3m width, most commonly for target when pointing to a location factor = 0.3 # default size: 3m width, most commonly for target when pointing to a location

View file

@ -25,7 +25,7 @@ module.exports = class MusicPlayer extends CocoClass
if (not e.file) or src is @currentMusic?.src if (not e.file) or src is @currentMusic?.src
if e.play then @restartCurrentMusic() else @fadeOutCurrentMusic() if e.play then @restartCurrentMusic() else @fadeOutCurrentMusic()
return return
media = AudioPlayer.getStatus(src) media = AudioPlayer.getStatus(src)
if not media?.loaded if not media?.loaded
AudioPlayer.preloadSound(src) AudioPlayer.preloadSound(src)
@ -35,23 +35,23 @@ module.exports = class MusicPlayer extends CocoClass
@standingBy = null @standingBy = null
@fadeOutCurrentMusic() @fadeOutCurrentMusic()
@startNewMusic(src) if e.play @startNewMusic(src) if e.play
restartCurrentMusic: -> restartCurrentMusic: ->
return unless @currentMusic return unless @currentMusic
@currentMusic.play('none', 0, 0, -1, 0.3) @currentMusic.play('none', 0, 0, -1, 0.3)
@updateMusicVolume() @updateMusicVolume()
fadeOutCurrentMusic: -> fadeOutCurrentMusic: ->
return unless @currentMusic return unless @currentMusic
f = -> @stop() f = -> @stop()
createjs.Tween.get(@currentMusic).to({volume:0.0}, CROSSFADE_LENGTH).call(f) createjs.Tween.get(@currentMusic).to({volume: 0.0}, CROSSFADE_LENGTH).call(f)
startNewMusic: (src) -> startNewMusic: (src) ->
@currentMusic = createjs.Sound.play(src, 'none', 0, 0, -1, 0.3) if src @currentMusic = createjs.Sound.play(src, 'none', 0, 0, -1, 0.3) if src
return unless @currentMusic return unless @currentMusic
@currentMusic.volume = 0.0 @currentMusic.volume = 0.0
if me.get('music') if me.get('music')
createjs.Tween.get(@currentMusic).to({volume:1.0}, CROSSFADE_LENGTH) createjs.Tween.get(@currentMusic).to({volume: 1.0}, CROSSFADE_LENGTH)
onMusicSettingChanged: -> onMusicSettingChanged: ->
@updateMusicVolume() @updateMusicVolume()
@ -64,4 +64,3 @@ module.exports = class MusicPlayer extends CocoClass
destroy: -> destroy: ->
me.off 'change:music', @onMusicSettingChanged, @ me.off 'change:music', @onMusicSettingChanged, @
super() super()

View file

@ -6,18 +6,18 @@ module.exports = class PlaybackOverScreen extends CocoClass
options ?= {} options ?= {}
@camera = options.camera @camera = options.camera
@layer = options.layer @layer = options.layer
console.error @toString(), "needs a camera." unless @camera console.error @toString(), 'needs a camera.' unless @camera
console.error @toString(), "needs a layer." unless @layer console.error @toString(), 'needs a layer.' unless @layer
@build() @build()
toString: -> "<PlaybackOverScreen>" toString: -> '<PlaybackOverScreen>'
build: -> build: ->
@dimLayer = new createjs.Container() @dimLayer = new createjs.Container()
@dimLayer.mouseEnabled = @dimLayer.mouseChildren = false @dimLayer.mouseEnabled = @dimLayer.mouseChildren = false
@dimLayer.layerIndex = -12 @dimLayer.layerIndex = -12
@dimLayer.addChild @dimScreen = new createjs.Shape() @dimLayer.addChild @dimScreen = new createjs.Shape()
@dimScreen.graphics.beginFill("rgba(0,0,0,0.4)").rect 0, 0, @camera.canvasWidth, @camera.canvasHeight @dimScreen.graphics.beginFill('rgba(0,0,0,0.4)').rect 0, 0, @camera.canvasWidth, @camera.canvasHeight
@dimLayer.cache 0, 0, @camera.canvasWidth, @camera.canvasHeight @dimLayer.cache 0, 0, @camera.canvasWidth, @camera.canvasHeight
@dimLayer.alpha = 0 @dimLayer.alpha = 0
@layer.addChild @dimLayer @layer.addChild @dimLayer
@ -28,11 +28,11 @@ module.exports = class PlaybackOverScreen extends CocoClass
@dimLayer.alpha = 0 @dimLayer.alpha = 0
createjs.Tween.removeTweens @dimLayer createjs.Tween.removeTweens @dimLayer
createjs.Tween.get(@dimLayer).to({alpha:1}, 500) createjs.Tween.get(@dimLayer).to({alpha: 1}, 500)
hide: -> hide: ->
return unless @showing return unless @showing
@showing = false @showing = false
createjs.Tween.removeTweens @dimLayer createjs.Tween.removeTweens @dimLayer
createjs.Tween.get(@dimLayer).to({alpha:0}, 500) createjs.Tween.get(@dimLayer).to({alpha: 0}, 500)

View file

@ -19,12 +19,12 @@ module.exports = class PointChooser extends CocoClass
@shape = new createjs.Shape() @shape = new createjs.Shape()
@shape.alpha = 0.9 @shape.alpha = 0.9
@shape.mouseEnabled = false @shape.mouseEnabled = false
@shape.graphics.setStrokeStyle(1, "round").beginStroke("#000000").beginFill('#fedcba') @shape.graphics.setStrokeStyle(1, 'round').beginStroke('#000000').beginFill('#fedcba')
@shape.graphics.drawCircle(0, 0, 4).endFill() @shape.graphics.drawCircle(0, 0, 4).endFill()
@shape.layerIndex = 100 @shape.layerIndex = 100
onMouseDown: (e) => onMouseDown: (e) =>
console.log "got stagemousedown", e, key.shift console.log 'got stagemousedown', e, key.shift
return unless key.shift return unless key.shift
@setPoint @options.camera.screenToWorld {x: e.stageX, y: e.stageY} @setPoint @options.camera.screenToWorld {x: e.stageX, y: e.stageY}
Backbone.Mediator.publish 'choose-point', point: @point Backbone.Mediator.publish 'choose-point', point: @point

View file

@ -51,12 +51,12 @@ module.exports = class SpriteBoss extends CocoClass
createLayers: -> createLayers: ->
@spriteLayers = {} @spriteLayers = {}
for [name, priority] in [ for [name, priority] in [
["Land", -40] ['Land', -40]
["Ground", -30] ['Ground', -30]
["Obstacle", -20] ['Obstacle', -20]
["Path", -10] ['Path', -10]
["Default", 0] ['Default', 0]
["Floating", 10] ['Floating', 10]
] ]
@spriteLayers[name] = new Layer name: name, layerPriority: priority, transform: Layer.TRANSFORM_CHILD, camera: @camera @spriteLayers[name] = new Layer name: name, layerPriority: priority, transform: Layer.TRANSFORM_CHILD, camera: @camera
@surfaceLayer.addChild _.values(@spriteLayers)... @surfaceLayer.addChild _.values(@spriteLayers)...
@ -66,36 +66,36 @@ module.exports = class SpriteBoss extends CocoClass
# TODO: make better system # TODO: make better system
child.layerPriority = 0 if sprite?.thang?.isSelectable child.layerPriority = 0 if sprite?.thang?.isSelectable
child.layerPriority = -40 if sprite?.thang?.isLand child.layerPriority = -40 if sprite?.thang?.isLand
return @spriteLayers["Default"] unless child.layerPriority return @spriteLayers['Default'] unless child.layerPriority
layer = _.findLast @spriteLayers, (layer, name) -> layer = _.findLast @spriteLayers, (layer, name) ->
layer.layerPriority <= child.layerPriority layer.layerPriority <= child.layerPriority
#console.log "layer for", child, "is", (layer ? @spriteLayers["Default"]) #console.log 'layer for', child, 'is', (layer ? @spriteLayers['Default'])
layer ? @spriteLayers["Default"] layer ? @spriteLayers['Default']
addSprite: (sprite, id=null, layer=null) -> addSprite: (sprite, id=null, layer=null) ->
id ?= sprite.thang.id id ?= sprite.thang.id
console.error "Sprite collision! Already have:", id if @sprites[id] console.error 'Sprite collision! Already have:', id if @sprites[id]
@sprites[id] = sprite @sprites[id] = sprite
@spriteArray.push sprite @spriteArray.push sprite
layer ?= @spriteLayers["Obstacle"] if sprite.thang?.spriteName.search(/(dungeon|indoor).wall/i) isnt -1 layer ?= @spriteLayers['Obstacle'] if sprite.thang?.spriteName.search(/(dungeon|indoor).wall/i) isnt -1
layer ?= @layerForChild sprite.imageObject, sprite layer ?= @layerForChild sprite.imageObject, sprite
layer.addChild sprite.imageObject layer.addChild sprite.imageObject
layer.updateLayerOrder() layer.updateLayerOrder()
sprite sprite
createMarks: -> createMarks: ->
@targetMark = new Mark name: 'target', camera: @camera, layer: @spriteLayers["Ground"], thangType: 'target' @targetMark = new Mark name: 'target', camera: @camera, layer: @spriteLayers['Ground'], thangType: 'target'
@selectionMark = new Mark name: 'selection', camera: @camera, layer: @spriteLayers["Ground"], thangType: 'selection' @selectionMark = new Mark name: 'selection', camera: @camera, layer: @spriteLayers['Ground'], thangType: 'selection'
createSpriteOptions: (options) -> createSpriteOptions: (options) ->
_.extend options, camera: @camera, resolutionFactor: 4, groundLayer: @spriteLayers["Ground"], textLayer: @surfaceTextLayer, floatingLayer: @spriteLayers["Floating"], spriteSheetCache: @spriteSheetCache, showInvisible: @options.showInvisible _.extend options, camera: @camera, resolutionFactor: 4, groundLayer: @spriteLayers['Ground'], textLayer: @surfaceTextLayer, floatingLayer: @spriteLayers['Floating'], spriteSheetCache: @spriteSheetCache, showInvisible: @options.showInvisible
createIndieSprites: (indieSprites, withWizards) -> createIndieSprites: (indieSprites, withWizards) ->
unless @indieSprites unless @indieSprites
@indieSprites = [] @indieSprites = []
@indieSprites = (@createIndieSprite indieSprite for indieSprite in indieSprites) if indieSprites @indieSprites = (@createIndieSprite indieSprite for indieSprite in indieSprites) if indieSprites
if withWizards and not @selfWizardSprite if withWizards and not @selfWizardSprite
@selfWizardSprite = @createWizardSprite thangID: "My Wizard", isSelf: true, sprites: @sprites @selfWizardSprite = @createWizardSprite thangID: 'My Wizard', isSelf: true, sprites: @sprites
createIndieSprite: (indieSprite) -> createIndieSprite: (indieSprite) ->
unless thangType = @thangTypeFor indieSprite.thangType unless thangType = @thangTypeFor indieSprite.thangType
@ -107,16 +107,16 @@ module.exports = class SpriteBoss extends CocoClass
createOpponentWizard: (opponent) -> createOpponentWizard: (opponent) ->
# TODO: colorize name and cloud by team, colorize wizard by user's color config, level-specific wizard spawn points # TODO: colorize name and cloud by team, colorize wizard by user's color config, level-specific wizard spawn points
sprite = @createWizardSprite thangID: opponent.id, name: opponent.name sprite = @createWizardSprite thangID: opponent.id, name: opponent.name
if not opponent.levelSlug or opponent.levelSlug is "brawlwood" if not opponent.levelSlug or opponent.levelSlug is 'brawlwood'
sprite.targetPos = if opponent.team is 'ogres' then {x: 52, y: 52} else {x: 28, y: 28} sprite.targetPos = if opponent.team is 'ogres' then {x: 52, y: 52} else {x: 28, y: 28}
else if opponent.levelSlug is "dungeon-arena" else if opponent.levelSlug is 'dungeon-arena'
sprite.targetPos = if opponent.team is 'ogres' then {x:72, y: 39} else {x: 9, y:39} sprite.targetPos = if opponent.team is 'ogres' then {x: 72, y: 39} else {x: 9, y: 39}
else else
sprite.targetPos = if opponent.team is 'ogres' then {x:52, y: 28} else {x: 20, y:28} sprite.targetPos = if opponent.team is 'ogres' then {x: 52, y: 28} else {x: 20, y: 28}
createWizardSprite: (options) -> createWizardSprite: (options) ->
sprite = new WizardSprite @thangTypeFor("Wizard"), @createSpriteOptions(options) sprite = new WizardSprite @thangTypeFor('Wizard'), @createSpriteOptions(options)
@addSprite sprite, sprite.thang.id, @spriteLayers["Floating"] @addSprite sprite, sprite.thang.id, @spriteLayers['Floating']
onPlayerJoined: (e) -> onPlayerJoined: (e) ->
# Create another WizardSprite, unless this player is just me # Create another WizardSprite, unless this player is just me
@ -172,7 +172,7 @@ module.exports = class SpriteBoss extends CocoClass
@adjustSpriteExistence() if frameChanged @adjustSpriteExistence() if frameChanged
sprite.update frameChanged for sprite in @spriteArray sprite.update frameChanged for sprite in @spriteArray
@updateSelection() @updateSelection()
@spriteLayers["Default"].updateLayerOrder() @spriteLayers['Default'].updateLayerOrder()
@cache() @cache()
adjustSpriteExistence: -> adjustSpriteExistence: ->
@ -184,11 +184,11 @@ module.exports = class SpriteBoss extends CocoClass
else else
sprite = @addThangToSprites(thang) sprite = @addThangToSprites(thang)
Backbone.Mediator.publish 'surface:new-thang-added', thang:thang, sprite:sprite Backbone.Mediator.publish 'surface:new-thang-added', thang:thang, sprite:sprite
updateCache = updateCache or sprite.imageObject.parent is @spriteLayers["Obstacle"] updateCache = updateCache or sprite.imageObject.parent is @spriteLayers['Obstacle']
sprite.playSounds() sprite.playSounds()
for thangID, sprite of @sprites for thangID, sprite of @sprites
missing = not (sprite.notOfThisWorld or @world.thangMap[thangID]?.exists) missing = not (sprite.notOfThisWorld or @world.thangMap[thangID]?.exists)
isObstacle = sprite.imageObject.parent is @spriteLayers["Obstacle"] isObstacle = sprite.imageObject.parent is @spriteLayers['Obstacle']
updateCache = updateCache or (isObstacle and (missing or sprite.hasMoved)) updateCache = updateCache or (isObstacle and (missing or sprite.hasMoved))
sprite.hasMoved = false sprite.hasMoved = false
@removeSprite sprite if missing @removeSprite sprite if missing
@ -210,11 +210,11 @@ module.exports = class SpriteBoss extends CocoClass
wallSprite.updateScale() wallSprite.updateScale()
wallSprite.updatePosition() wallSprite.updatePosition()
#console.log @wallGrid.toString() #console.log @wallGrid.toString()
@spriteLayers["Obstacle"].uncache() if @spriteLayers["Obstacle"].cacheID # might have changed sizes @spriteLayers['Obstacle'].uncache() if @spriteLayers['Obstacle'].cacheID # might have changed sizes
@spriteLayers["Obstacle"].cache() @spriteLayers['Obstacle'].cache()
# test performance of doing land layer, too, to see if it's faster # test performance of doing land layer, too, to see if it's faster
# @spriteLayers["Land"].uncache() if @spriteLayers["Land"].cacheID # might have changed sizes # @spriteLayers['Land'].uncache() if @spriteLayers['Land'].cacheID # might have changed sizes
# @spriteLayers["Land"].cache() # @spriteLayers['Land'].cache()
# I don't notice much difference - Scott # I don't notice much difference - Scott
@cached = true @cached = true
@ -291,7 +291,6 @@ module.exports = class SpriteBoss extends CocoClass
instance.addEventListener 'complete', -> instance.addEventListener 'complete', ->
Backbone.Mediator.publish 'thang-finished-talking', thang: sprite?.thang Backbone.Mediator.publish 'thang-finished-talking', thang: sprite?.thang
# Marks # Marks
updateSelection: -> updateSelection: ->

View file

@ -90,7 +90,7 @@ module.exports = Surface = class Surface extends CocoClass
destroy: -> destroy: ->
@dead = true @dead = true
@camera?.destroy() @camera?.destroy()
createjs.Ticker.removeEventListener("tick", @tick) createjs.Ticker.removeEventListener('tick', @tick)
createjs.Sound.stop() createjs.Sound.stop()
layer.destroy() for layer in @layers layer.destroy() for layer in @layers
@spriteBoss.destroy() @spriteBoss.destroy()
@ -160,12 +160,12 @@ module.exports = Surface = class Surface extends CocoClass
addMeshRectanglesToContainer: (mesh, container) -> addMeshRectanglesToContainer: (mesh, container) ->
for rect in mesh for rect in mesh
shape = new createjs.Shape() shape = new createjs.Shape()
pos = @camera.worldToSurface {x:rect.x, y:rect.y} pos = @camera.worldToSurface {x: rect.x, y: rect.y}
dim = @camera.worldToSurface {x:rect.width, y:rect.height} dim = @camera.worldToSurface {x: rect.width, y: rect.height}
shape.graphics shape.graphics
.setStrokeStyle(3) .setStrokeStyle(3)
.beginFill('rgba(0, 0, 128, 0.3)') .beginFill('rgba(0,0,128,0.3)')
.beginStroke('rgba(0, 0, 128, 0.7)') .beginStroke('rgba(0,0,128,0.7)')
.drawRect(pos.x - dim.x/2, pos.y - dim.y/2, dim.x, dim.y) .drawRect(pos.x - dim.x/2, pos.y - dim.y/2, dim.x, dim.y)
container.addChild shape container.addChild shape
@ -181,7 +181,7 @@ module.exports = Surface = class Surface extends CocoClass
shape.graphics shape.graphics
.setStrokeStyle(1) .setStrokeStyle(1)
.moveTo(v1.x, v1.y) .moveTo(v1.x, v1.y)
.beginStroke('rgba(128, 0, 0, 0.4)') .beginStroke('rgba(128,0,0,0.4)')
.lineTo(v2.x, v2.y) .lineTo(v2.x, v2.y)
.endStroke() .endStroke()
container.addChild shape container.addChild shape
@ -206,7 +206,7 @@ module.exports = Surface = class Surface extends CocoClass
if scrubDuration if scrubDuration
t = createjs.Tween t = createjs.Tween
.get(@) .get(@)
.to({currentFrame:@scrubbingTo}, scrubDuration, createjs.Ease.sineInOut) .to({currentFrame: @scrubbingTo}, scrubDuration, createjs.Ease.sineInOut)
.call(onTweenEnd) .call(onTweenEnd)
t.addEventListener('change', @onFramesScrubbed) t.addEventListener('change', @onFramesScrubbed)
else else
@ -309,7 +309,7 @@ module.exports = Surface = class Surface extends CocoClass
return if @currentFrame is @lastFrame and not force return if @currentFrame is @lastFrame and not force
progress = @getProgress() progress = @getProgress()
Backbone.Mediator.publish('surface:frame-changed', Backbone.Mediator.publish('surface:frame-changed',
type: "frame-changed" type: 'frame-changed'
selectedThang: @spriteBoss.selectedSprite?.thang selectedThang: @spriteBoss.selectedSprite?.thang
progress: progress progress: progress
frame: @currentFrame frame: @currentFrame
@ -353,7 +353,7 @@ module.exports = Surface = class Surface extends CocoClass
@setPaused false if @ended @setPaused false if @ended
@casting = true @casting = true
@wasPlayingWhenCastingBegan = @playing @wasPlayingWhenCastingBegan = @playing
Backbone.Mediator.publish 'level-set-playing', { playing: false } Backbone.Mediator.publish 'level-set-playing', {playing: false}
@setPlayingCalled = false # don't overwrite playing settings if they changed by, say, scripts @setPlayingCalled = false # don't overwrite playing settings if they changed by, say, scripts
if @coordinateDisplay? if @coordinateDisplay?
@ -361,7 +361,7 @@ module.exports = Surface = class Surface extends CocoClass
@coordinateDisplay.destroy() @coordinateDisplay.destroy()
createjs.Tween.removeTweens(@surfaceLayer) createjs.Tween.removeTweens(@surfaceLayer)
createjs.Tween.get(@surfaceLayer).to({alpha:0.9}, 1000, createjs.Ease.getPowOut(4.0)) createjs.Tween.get(@surfaceLayer).to({alpha: 0.9}, 1000, createjs.Ease.getPowOut(4.0))
onNewWorld: (event) -> onNewWorld: (event) ->
return unless event.world.name is @world.name return unless event.world.name is @world.name
@ -373,7 +373,7 @@ module.exports = Surface = class Surface extends CocoClass
# This has a tendency to break scripts that are waiting for playback to change when the level is loaded # This has a tendency to break scripts that are waiting for playback to change when the level is loaded
# so only run it after the first world is created. # so only run it after the first world is created.
Backbone.Mediator.publish 'level-set-playing', { playing: @wasPlayingWhenCastingBegan } unless event.firstWorld or @setPlayingCalled Backbone.Mediator.publish 'level-set-playing', {playing: @wasPlayingWhenCastingBegan} unless event.firstWorld or @setPlayingCalled
fastForwardTo = null fastForwardTo = null
if @playing if @playing
@ -391,9 +391,9 @@ module.exports = Surface = class Surface extends CocoClass
@setProgress fastForwardToRatio, 1000 * fastForwardToTime / fastForwardSpeed @setProgress fastForwardToRatio, 1000 * fastForwardToTime / fastForwardSpeed
@fastForwarding = true @fastForwarding = true
createjs.Tween.get(@surfaceLayer) createjs.Tween.get(@surfaceLayer)
.to({alpha:0.0}, 50) .to({alpha: 0.0}, 50)
.call(f) .call(f)
.to({alpha:1.0}, 2000, createjs.Ease.getPowOut(2.0)) .to({alpha: 1.0}, 2000, createjs.Ease.getPowOut(2.0))
# initialization # initialization
@ -405,9 +405,9 @@ module.exports = Surface = class Surface extends CocoClass
@camera?.destroy() @camera?.destroy()
@camera = new Camera @canvas @camera = new Camera @canvas
AudioPlayer.camera = @camera AudioPlayer.camera = @camera
@layers.push @surfaceLayer = new Layer name: "Surface", layerPriority: 0, transform: Layer.TRANSFORM_SURFACE, camera: @camera @layers.push @surfaceLayer = new Layer name: 'Surface', layerPriority: 0, transform: Layer.TRANSFORM_SURFACE, camera: @camera
@layers.push @surfaceTextLayer = new Layer name: "Surface Text", layerPriority: 1, transform: Layer.TRANSFORM_SURFACE_TEXT, camera: @camera @layers.push @surfaceTextLayer = new Layer name: 'Surface Text', layerPriority: 1, transform: Layer.TRANSFORM_SURFACE_TEXT, camera: @camera
@layers.push @screenLayer = new Layer name: "Screen", layerPriority: 2, transform: Layer.TRANSFORM_SCREEN, camera: @camera @layers.push @screenLayer = new Layer name: 'Screen', layerPriority: 2, transform: Layer.TRANSFORM_SCREEN, camera: @camera
@stage.addChild @layers... @stage.addChild @layers...
@surfaceLayer.addChild @cameraBorder = new CameraBorder bounds: @camera.bounds @surfaceLayer.addChild @cameraBorder = new CameraBorder bounds: @camera.bounds
@screenLayer.addChild new Letterbox canvasWidth: canvasWidth, canvasHeight: canvasHeight @screenLayer.addChild new Letterbox canvasWidth: canvasWidth, canvasHeight: canvasHeight
@ -449,7 +449,7 @@ module.exports = Surface = class Surface extends CocoClass
@updateState true @updateState true
@drawCurrentFrame() @drawCurrentFrame()
@showGrid() if @options.grid # TODO: pay attention to world grid setting (which we only know when world simulates) @showGrid() if @options.grid # TODO: pay attention to world grid setting (which we only know when world simulates)
createjs.Ticker.addEventListener "tick", @tick createjs.Ticker.addEventListener 'tick', @tick
Backbone.Mediator.publish 'level:started' Backbone.Mediator.publish 'level:started'
createOpponentWizard: (opponent) -> createOpponentWizard: (opponent) ->
@ -469,10 +469,10 @@ module.exports = Surface = class Surface extends CocoClass
@gridLayer.z = 90019001 @gridLayer.z = 90019001
@gridLayer.mouseEnabled = false @gridLayer.mouseEnabled = false
@gridShape.alpha = 0.125 @gridShape.alpha = 0.125
@gridShape.graphics.beginStroke "blue" @gridShape.graphics.beginStroke 'blue'
gridSize = Math.round(@world.size()[0] / 20) gridSize = Math.round(@world.size()[0] / 20)
unless gridSize > 0.1 unless gridSize > 0.1
return console.error "Grid size is", gridSize, "so we can't draw a grid." return console.error 'Grid size is', gridSize, 'so we can\'t draw a grid.'
wopStart = x: 0, y: 0 wopStart = x: 0, y: 0
wopEnd = x: @world.size()[0], y: @world.size()[1] wopEnd = x: @world.size()[0], y: @world.size()[1]
supStart = @camera.worldToSurface wopStart supStart = @camera.worldToSurface wopStart
@ -481,7 +481,7 @@ module.exports = Surface = class Surface extends CocoClass
while wop.x < wopEnd.x while wop.x < wopEnd.x
sup = @camera.worldToSurface wop sup = @camera.worldToSurface wop
@gridShape.graphics.mt(sup.x, supStart.y).lt(sup.x, supEnd.y) @gridShape.graphics.mt(sup.x, supStart.y).lt(sup.x, supEnd.y)
t = new createjs.Text(wop.x.toFixed(0), "16px Arial", "blue") t = new createjs.Text(wop.x.toFixed(0), '16px Arial', 'blue')
t.x = sup.x - t.getMeasuredWidth() / 2 t.x = sup.x - t.getMeasuredWidth() / 2
t.y = supStart.y - 10 - t.getMeasuredHeight() / 2 t.y = supStart.y - 10 - t.getMeasuredHeight() / 2
t.alpha = 0.75 t.alpha = 0.75
@ -490,7 +490,7 @@ module.exports = Surface = class Surface extends CocoClass
while wop.y < wopEnd.y while wop.y < wopEnd.y
sup = @camera.worldToSurface wop sup = @camera.worldToSurface wop
@gridShape.graphics.mt(supStart.x, sup.y).lt(supEnd.x, sup.y) @gridShape.graphics.mt(supStart.x, sup.y).lt(supEnd.x, sup.y)
t = new createjs.Text(wop.y.toFixed(0), "16px Arial", "blue") t = new createjs.Text(wop.y.toFixed(0), '16px Arial', 'blue')
t.x = 10 - t.getMeasuredWidth() / 2 t.x = 10 - t.getMeasuredWidth() / 2
t.y = sup.y - t.getMeasuredHeight() / 2 t.y = sup.y - t.getMeasuredHeight() / 2
t.alpha = 0.75 t.alpha = 0.75
@ -596,7 +596,7 @@ module.exports = Surface = class Surface extends CocoClass
Backbone.Mediator.publish('surface:ticked', {dt: 1 / @options.frameRate}) Backbone.Mediator.publish('surface:ticked', {dt: 1 / @options.frameRate})
mib = @stage.mouseInBounds mib = @stage.mouseInBounds
if @mouseInBounds isnt mib if @mouseInBounds isnt mib
Backbone.Mediator.publish('surface:mouse-' + (if mib then "over" else "out"), {}) Backbone.Mediator.publish('surface:mouse-' + (if mib then 'over' else 'out'), {})
@mouseInBounds = mib @mouseInBounds = mib
restoreWorldState: -> restoreWorldState: ->
@ -631,10 +631,10 @@ module.exports = Surface = class Surface extends CocoClass
return if @world.showPaths is 'paused' and @playing return if @world.showPaths is 'paused' and @playing
return if @world.showPaths is 'selected' and not selectedThang return if @world.showPaths is 'selected' and not selectedThang
@trailmaster ?= new path.Trailmaster @camera @trailmaster ?= new path.Trailmaster @camera
selectedOnly = @playing and @world.showPaths is "selected" selectedOnly = @playing and @world.showPaths is 'selected'
@paths = @trailmaster.generatePaths @world, @getCurrentFrame(), selectedThang, @spriteBoss.sprites, selectedOnly @paths = @trailmaster.generatePaths @world, @getCurrentFrame(), selectedThang, @spriteBoss.sprites, selectedOnly
@paths.name = 'paths' @paths.name = 'paths'
@spriteBoss.spriteLayers["Path"].addChild @paths @spriteBoss.spriteLayers['Path'].addChild @paths
hidePaths: -> hidePaths: ->
return if not @paths return if not @paths
@ -647,8 +647,8 @@ module.exports = Surface = class Surface extends CocoClass
margin = (1 - 1 / zoom) / 2 margin = (1 - 1 / zoom) / 2
@stage.cache margin * w, margin * h, w / zoom, h / zoom, scale * zoom @stage.cache margin * w, margin * h, w / zoom, h / zoom, scale * zoom
imageData = @stage.cacheCanvas.toDataURL(format, quality) imageData = @stage.cacheCanvas.toDataURL(format, quality)
#console.log "Screenshot with scale", scale, "format", format, "quality", quality, "was", Math.floor(imageData.length / 1024), "kB" #console.log 'Screenshot with scale', scale, 'format', format, 'quality', quality, 'was', Math.floor(imageData.length / 1024), 'kB'
screenshot = document.createElement("img") screenshot = document.createElement('img')
screenshot.src = imageData screenshot.src = imageData
@stage.uncache() @stage.uncache()
imageData imageData

View file

@ -63,7 +63,7 @@ module.exports = class WizardSprite extends IndieSprite
continue unless state.wizard? continue unless state.wizard?
@setColorHue state.wizard.wizardColor1 @setColorHue state.wizard.wizardColor1
if targetID = state.wizard.targetSprite if targetID = state.wizard.targetSprite
return console.warn "Wizard Sprite couldn't find target sprite", targetID unless targetID of @options.sprites return console.warn 'Wizard Sprite couldn\'t find target sprite', targetID unless targetID of @options.sprites
@setTarget @options.sprites[targetID] @setTarget @options.sprites[targetID]
else else
@setTarget state.wizard.targetPos @setTarget state.wizard.targetPos
@ -129,7 +129,7 @@ module.exports = class WizardSprite extends IndieSprite
@targetPos = @boundWizard targetPos @targetPos = @boundWizard targetPos
@beginMoveTween(duration, isLinear) @beginMoveTween(duration, isLinear)
@shoveOtherWizards() @shoveOtherWizards()
Backbone.Mediator.publish('self-wizard:target-changed', {sender:@}) if @isSelf Backbone.Mediator.publish('self-wizard:target-changed', {sender: @}) if @isSelf
boundWizard: (target) -> boundWizard: (target) ->
# Passed an {x, y} in world coordinates, returns {x, y} within world bounds # Passed an {x, y} in world coordinates, returns {x, y} within world bounds
@ -168,7 +168,7 @@ module.exports = class WizardSprite extends IndieSprite
createjs.Tween createjs.Tween
.get(@) .get(@)
.to({tweenPercentage:0.0}, duration, ease) .to({tweenPercentage: 0.0}, duration, ease)
.call(@endMoveTween) .call(@endMoveTween)
@reachedTarget = false @reachedTarget = false
@update true @update true
@ -176,7 +176,7 @@ module.exports = class WizardSprite extends IndieSprite
shoveOtherWizards: (removeMe) -> shoveOtherWizards: (removeMe) ->
return unless @targetSprite return unless @targetSprite
allWizards = [] allWizards = []
Backbone.Mediator.publish('echo-all-wizard-sprites', {payload:allWizards}) Backbone.Mediator.publish('echo-all-wizard-sprites', {payload: allWizards})
allOfUs = (wizard for wizard in allWizards when wizard.targetSprite is @targetSprite) allOfUs = (wizard for wizard in allWizards when wizard.targetSprite is @targetSprite)
allOfUs = (wizard for wizard in allOfUs when wizard isnt @) if removeMe allOfUs = (wizard for wizard in allOfUs when wizard isnt @) if removeMe

View file

@ -121,12 +121,12 @@ module.exports.Trailmaster = class Trailmaster
return unless thang.allTargets return unless thang.allTargets
g = new createjs.Graphics() g = new createjs.Graphics()
g.setStrokeStyle(0.5) g.setStrokeStyle(0.5)
g.beginStroke(createjs.Graphics.getRGB(0,0,0)) g.beginStroke(createjs.Graphics.getRGB(0, 0, 0))
color = colorForThang(thang.team) color = colorForThang(thang.team)
i = 0 i = 0
while i < thang.allTargets.length while i < thang.allTargets.length
g.beginStroke(createjs.Graphics.getRGB(0,0,0)) g.beginStroke(createjs.Graphics.getRGB(0, 0, 0))
g.beginFill(createjs.Graphics.getRGB(color...)) g.beginFill(createjs.Graphics.getRGB(color...))
sup = @camera.worldToSurface x: thang.allTargets[i], y: thang.allTargets[i + 1] sup = @camera.worldToSurface x: thang.allTargets[i], y: thang.allTargets[i + 1]
g.drawEllipse(sup.x - 5, sup.y - 3, 10, 6) g.drawEllipse(sup.x - 5, sup.y - 3, 10, 6)
@ -168,7 +168,7 @@ module.exports.Trailmaster = class Trailmaster
clone.scaleY *= CLONE_SCALE clone.scaleY *= CLONE_SCALE
if sprite.expandActions # old Sprite if sprite.expandActions # old Sprite
sprite.updateRotation(clone, sprite.data) sprite.updateRotation(clone, sprite.data)
animActions = sprite.expandActions(if thang.acts then thang.getActionName() else "idle") animActions = sprite.expandActions(if thang.acts then thang.getActionName() else 'idle')
sprite.applyActionsToSprites(animActions, [clone], true) sprite.applyActionsToSprites(animActions, [clone], true)
animation = clone.spriteSheet.getAnimation(clone.currentAnimation) animation = clone.spriteSheet.getAnimation(clone.currentAnimation)
clone.currentAnimationFrame = Math.min(@clock % (animation.frames.length * 3), animation.frames.length - 1) clone.currentAnimationFrame = Math.min(@clock % (animation.frames.length * 3), animation.frames.length - 1)
@ -186,9 +186,6 @@ module.exports.Trailmaster = class Trailmaster
@world.frames[@currentFrame].restoreStateForThang(thang) @world.frames[@currentFrame].restoreStateForThang(thang)
sprites sprites
createPath = (points, options={}, g=null) -> createPath = (points, options={}, g=null) ->
options = options or {} options = options or {}
tailColor = options.tailColor ? options.headColor tailColor = options.tailColor ? options.headColor
@ -290,5 +287,3 @@ colorForThang = (team, brightness=100, alpha=1.0) =>
return color return color
module.exports.createPath = createPath module.exports.createPath = createPath

View file

@ -1,5 +1,5 @@
module.exports.clone = (obj) -> module.exports.clone = (obj) ->
return obj if obj is null or typeof (obj) isnt "object" return obj if obj is null or typeof (obj) isnt 'object'
temp = obj.constructor() temp = obj.constructor()
for key of obj for key of obj
temp[key] = module.exports.clone(obj[key]) temp[key] = module.exports.clone(obj[key])
@ -25,7 +25,7 @@ module.exports.normalizeFunc = (func_thing, object) ->
if _.isString(func_thing) if _.isString(func_thing)
func = object[func_thing] func = object[func_thing]
if not func if not func
console.error("Could not find method", func_thing, 'in object', @) console.error "Could not find method #{func_thing} in object #{@}"
return => null # always return a func, or Mediator will go boom return => null # always return a func, or Mediator will go boom
func_thing = func func_thing = func
return func_thing return func_thing
@ -36,7 +36,7 @@ module.exports.hexToHSL = (hex) ->
hexToR = (h) -> parseInt (cutHex(h)).substring(0, 2), 16 hexToR = (h) -> parseInt (cutHex(h)).substring(0, 2), 16
hexToG = (h) -> parseInt (cutHex(h)).substring(2, 4), 16 hexToG = (h) -> parseInt (cutHex(h)).substring(2, 4), 16
hexToB = (h) -> parseInt (cutHex(h)).substring(4, 6), 16 hexToB = (h) -> parseInt (cutHex(h)).substring(4, 6), 16
cutHex = (h) -> (if (h.charAt(0) is "#") then h.substring(1, 7) else h) cutHex = (h) -> (if (h.charAt(0) is '#') then h.substring(1, 7) else h)
module.exports.hslToHex = (hsl) -> module.exports.hslToHex = (hsl) ->
'#' + (toHex(n) for n in hslToRgb(hsl...)).join('') '#' + (toHex(n) for n in hslToRgb(hsl...)).join('')
@ -57,10 +57,10 @@ module.exports.i18n = (say, target, language=me.lang(), fallback='en') ->
if target of locale if target of locale
result = locale[target] result = locale[target]
else continue else continue
return result if localeName == language return result if localeName is language
generalResult = result if localeName == generalName generalResult = result if localeName is generalName
fallbackResult = result if localeName == fallback fallbackResult = result if localeName is fallback
fallforwardResult = result if localeName.indexOf(language) == 0 and not fallforwardResult? fallforwardResult = result if localeName.indexOf(language) is 0 and not fallforwardResult?
return generalResult if generalResult? return generalResult if generalResult?
return fallforwardResult if fallforwardResult? return fallforwardResult if fallforwardResult?

View file

@ -14,7 +14,7 @@ module.exports = class GoalManager extends CocoClass
# If you want weird goals or hybrid goals, make a custom goal. # If you want weird goals or hybrid goals, make a custom goal.
nextGoalID: 0 nextGoalID: 0
nicks: ["GoalManager"] nicks: ['GoalManager']
constructor: (@world, @initialGoals, @team) -> constructor: (@world, @initialGoals, @team) ->
super() super()
@ -134,9 +134,9 @@ module.exports = class GoalManager extends CocoClass
} }
@initGoalState(state, [goal.killThangs, goal.saveThangs], 'killed') @initGoalState(state, [goal.killThangs, goal.saveThangs], 'killed')
for getTo in goal.getAllToLocations ? [] for getTo in goal.getAllToLocations ? []
@initGoalState(state,[ getTo.getToLocation?.who , [] ], 'arrived') @initGoalState(state, [getTo.getToLocation?.who, []], 'arrived')
for keepFrom in goal.keepAllFromLocations ? [] for keepFrom in goal.keepAllFromLocations ? []
@initGoalState(state,[ [] , keepFrom.keepFromLocation?.who], 'arrived') @initGoalState(state, [[], keepFrom.keepFromLocation?.who], 'arrived')
@initGoalState(state, [goal.getToLocations?.who, goal.keepFromLocations?.who], 'arrived') @initGoalState(state, [goal.getToLocations?.who, goal.keepFromLocations?.who], 'arrived')
@initGoalState(state, [goal.leaveOffSides?.who, goal.keepFromLeavingOffSides?.who], 'left') @initGoalState(state, [goal.leaveOffSides?.who, goal.keepFromLeavingOffSides?.who], 'left')
@initGoalState(state, [goal.collectThangs?.who, goal.keepFromCollectingThangs?.who], 'collected') @initGoalState(state, [goal.collectThangs?.who, goal.keepFromCollectingThangs?.who], 'collected')
@ -229,8 +229,8 @@ module.exports = class GoalManager extends CocoClass
if overallStatus = @checkOverallStatus true if overallStatus = @checkOverallStatus true
matchedGoals = (_.find(@goals, {id: goalID}) for goalID, goalState of @goalStates when goalState.status is overallStatus) matchedGoals = (_.find(@goals, {id: goalID}) for goalID, goalState of @goalStates when goalState.status is overallStatus)
mostEagerGoal = _.min matchedGoals, 'worldEndsAfter' mostEagerGoal = _.min matchedGoals, 'worldEndsAfter'
victory = overallStatus is "success" victory = overallStatus is 'success'
tentative = overallStatus is "success" tentative = overallStatus is 'success'
@world.endWorld victory, mostEagerGoal.worldEndsAfter, tentative if mostEagerGoal isnt Infinity @world.endWorld victory, mostEagerGoal.worldEndsAfter, tentative if mostEagerGoal isnt Infinity
updateGoalState: (goalID, thangID, progressObjectName, frameNumber) -> updateGoalState: (goalID, thangID, progressObjectName, frameNumber) ->
@ -244,20 +244,20 @@ module.exports = class GoalManager extends CocoClass
if success if success
numNeeded = goal.howMany ? Math.max(1, _.size stateThangs) numNeeded = goal.howMany ? Math.max(1, _.size stateThangs)
else else
# saveThangs: by default we would want to save all the Thangs, which means that we would want none of them to be "done" # saveThangs: by default we would want to save all the Thangs, which means that we would want none of them to be 'done'
numNeeded = _.size(stateThangs) - Math.max((goal.howMany ? 1), _.size stateThangs) + 1 numNeeded = _.size(stateThangs) - Math.max((goal.howMany ? 1), _.size stateThangs) + 1
numDone = _.filter(stateThangs).length numDone = _.filter(stateThangs).length
#console.log "needed", numNeeded, "done", numDone, "of total", _.size(stateThangs), "with how many", goal.howMany, "and stateThangs", stateThangs #console.log 'needed', numNeeded, 'done', numDone, 'of total', _.size(stateThangs), 'with how many', goal.howMany, 'and stateThangs', stateThangs
return unless numDone >= numNeeded return unless numDone >= numNeeded
return if state.status and not success # already failed it; don't wipe keyframe return if state.status and not success # already failed it; don't wipe keyframe
state.status = if success then "success" else "failure" state.status = if success then 'success' else 'failure'
state.keyFrame = frameNumber state.keyFrame = frameNumber
#console.log goalID, "became", success, "on frame", frameNumber, "with overallStatus", @checkOverallStatus true #console.log goalID, 'became', success, 'on frame', frameNumber, 'with overallStatus', @checkOverallStatus true
if overallStatus = @checkOverallStatus true if overallStatus = @checkOverallStatus true
matchedGoals = (_.find(@goals, {id: goalID}) for goalID, goalState of @goalStates when goalState.status is overallStatus) matchedGoals = (_.find(@goals, {id: goalID}) for goalID, goalState of @goalStates when goalState.status is overallStatus)
mostEagerGoal = _.min matchedGoals, 'worldEndsAfter' mostEagerGoal = _.min matchedGoals, 'worldEndsAfter'
victory = overallStatus is "success" victory = overallStatus is 'success'
tentative = overallStatus is "success" tentative = overallStatus is 'success'
@world.endWorld victory, mostEagerGoal.worldEndsAfter, tentative if mostEagerGoal isnt Infinity @world.endWorld victory, mostEagerGoal.worldEndsAfter, tentative if mostEagerGoal isnt Infinity
goalIsPositive: (goalID) -> goalIsPositive: (goalID) ->

View file

@ -31,7 +31,7 @@ module.exports = class Grid
for y in @columns gy - height / 2, gy + height / 2 for y in @columns gy - height / 2, gy + height / 2
for x in @rows gx - width / 2, gx + width / 2 for x in @rows gx - width / 2, gx + width / 2
for thang in @grid[y][x] for thang in @grid[y][x]
thangs.push thang if thang.collides and not (thang in thangs) and thang.id isnt "Add Thang Phantom" thangs.push thang if thang.collides and not (thang in thangs) and thang.id isnt 'Add Thang Phantom'
thangs thangs
clampColumn: (y) -> clampColumn: (y) ->
@ -51,4 +51,4 @@ module.exports = class Grid
toString: -> toString: ->
upsideDown = _.clone @grid upsideDown = _.clone @grid
upsideDown.reverse() upsideDown.reverse()
(((if thangs.length then ("" + thangs.length) else " ") for thangs in row).join(" ") for row in upsideDown).join("\n") (((if thangs.length then ('' + thangs.length) else ' ') for thangs in row).join(' ') for row in upsideDown).join("\n")

View file

@ -1,7 +1,7 @@
componentKeywords = ['attach', 'constructor', 'validateArguments', 'toString', 'isComponent'] # Array is faster than object componentKeywords = ['attach', 'constructor', 'validateArguments', 'toString', 'isComponent'] # Array is faster than object
module.exports = class Component module.exports = class Component
@className: "Component" @className: 'Component'
isComponent: true isComponent: true
constructor: (config) -> constructor: (config) ->
for key, value of config for key, value of config
@ -9,7 +9,7 @@ module.exports = class Component
attach: (thang) -> attach: (thang) ->
# Optimize; this is much of the World constructor time # Optimize; this is much of the World constructor time
for key, value of @ when key not in componentKeywords and key[0] isnt "_" for key, value of @ when key not in componentKeywords and key[0] isnt '_'
oldValue = thang[key] oldValue = thang[key]
if typeof oldValue is 'function' if typeof oldValue is 'function'
thang.appendMethod key, value thang.appendMethod key, value

View file

@ -1,23 +1,23 @@
Vector = require './vector' Vector = require './vector'
module.exports.ArgumentError = class ArgumentError extends Error module.exports.ArgumentError = class ArgumentError extends Error
@className: "ArgumentError" @className: 'ArgumentError'
constructor: (@message, @functionName, @argumentName, @intendedType, @actualValue, @numArguments) -> constructor: (@message, @functionName, @argumentName, @intendedType, @actualValue, @numArguments) ->
super message super message
@name = "ArgumentError" @name = 'ArgumentError'
if Error.captureStackTrace? if Error.captureStackTrace?
Error.captureStackTrace @, @constructor Error.captureStackTrace @, @constructor
toString: -> toString: ->
s = "#{@functionName}" s = "#{@functionName}"
if @argumentName is "return" if @argumentName is 'return'
s += "'s return value" s += "'s return value"
else if @argumentName is "_excess" else if @argumentName is '_excess'
s += " takes only #{@numArguments} argument#{if @numArguments > 1 then 's' else ''}." s += " takes only #{@numArguments} argument#{if @numArguments > 1 then 's' else ''}."
else if @argumentName else if @argumentName
s += "'s argument #{@argumentName}" s += "'s argument #{@argumentName}"
else else
s += " takes no arguments." s += ' takes no arguments.'
actualType = typeof @actualValue actualType = typeof @actualValue
if not @actualValue? if not @actualValue?
@ -26,12 +26,12 @@ module.exports.ArgumentError = class ArgumentError extends Error
actualType = 'array' actualType = 'array'
typeMismatch = @intendedType and not @intendedType.match actualType typeMismatch = @intendedType and not @intendedType.match actualType
if typeMismatch if typeMismatch
v = "" v = ''
if actualType is 'string' if actualType is 'string'
v = "\"#{@actualValue}\"" v = "\"#{@actualValue}\""
else if actualType is "number" else if actualType is 'number'
if Math.round(@actualValue) is @actualValue then @actualValue else @actualValue.toFixed(2) if Math.round(@actualValue) is @actualValue then @actualValue else @actualValue.toFixed(2)
else if actualType is "boolean" else if actualType is 'boolean'
v = "#{@actualValue}" v = "#{@actualValue}"
else if (@actualValue? and @actualValue.id and @actualValue.trackedPropertiesKeys) else if (@actualValue? and @actualValue.id and @actualValue.trackedPropertiesKeys)
# (Don't import Thang, but determine whether it is Thang.) # (Don't import Thang, but determine whether it is Thang.)
@ -40,7 +40,7 @@ module.exports.ArgumentError = class ArgumentError extends Error
v = @actualValue.toString() v = @actualValue.toString()
showValue = showValue or @actualValue instanceof Vector showValue = showValue or @actualValue instanceof Vector
s += " should have type #{@intendedType}, but got #{actualType}#{if v then ': ' + v else ''}." s += " should have type #{@intendedType}, but got #{actualType}#{if v then ': ' + v else ''}."
else if @argumentName and @argumentName isnt "_excess" else if @argumentName and @argumentName isnt '_excess'
s += " has a problem." s += ' has a problem.'
s += '\n' + @message if @message s += '\n' + @message if @message
s s

View file

@ -1,375 +1,375 @@
module.exports.thangNames = thangNames = module.exports.thangNames = thangNames =
"Soldier M": [ 'Soldier M': [
"Duke" 'Duke'
"William" 'William'
"Lucas" 'Lucas'
"Marcus" 'Marcus'
"Robert" 'Robert'
"Gordon" 'Gordon'
"Kirin" 'Kirin'
"Theo" 'Theo'
"Roger" 'Roger'
"Roderick" 'Roderick'
"Samson" 'Samson'
"Silas" 'Silas'
"Richard" 'Richard'
"Max" 'Max'
"Jax" 'Jax'
"Dax" 'Dax'
"Mischa" 'Mischa'
"Ronald" 'Ronald'
"Tyrone" 'Tyrone'
"Thelonious" 'Thelonious'
"Miles" 'Miles'
"Bill" 'Bill'
"Kumar" 'Kumar'
"Ricardo" 'Ricardo'
"Maxwell" 'Maxwell'
"Jonah" 'Jonah'
"Leopold" 'Leopold'
"Phineas" 'Phineas'
"Ferb" 'Ferb'
"Felix" 'Felix'
"Ezra" 'Ezra'
"Lucian" 'Lucian'
"Augustus" 'Augustus'
"Ronan" 'Ronan'
"Pierce" 'Pierce'
"Harry" 'Harry'
"Hirium" 'Hirium'
"Hugo" 'Hugo'
"Cecil" 'Cecil'
"Barron" 'Barron'
"Huburt" 'Huburt'
"Sterling" 'Sterling'
"Alistair" 'Alistair'
"Cid" 'Cid'
"Remy" 'Remy'
"Stormy" 'Stormy'
"Halle" 'Halle'
"Sage" 'Sage'
"Ryan" 'Ryan'
"Bond" 'Bond'
] ]
"Soldier F": [ 'Soldier F': [
"Sarah" 'Sarah'
"Alexandra" 'Alexandra'
"Holly" 'Holly'
"Trinity" 'Trinity'
"Nikita" 'Nikita'
"Alana" 'Alana'
"Lana" 'Lana'
"Joan" 'Joan'
"Helga" 'Helga'
"Annie" 'Annie'
"Lukaz" 'Lukaz'
"Gorgin" 'Gorgin'
"Coco" 'Coco'
"Buffy" 'Buffy'
"Allankrita" 'Allankrita'
"Kay" 'Kay'
] ]
"Peasant M": [ 'Peasant M': [
"Yorik" 'Yorik'
"Hector" 'Hector'
"Thad" 'Thad'
"Victor" 'Victor'
"Lyle" 'Lyle'
"Charles" 'Charles'
"Yusef" 'Yusef'
"Hingle" 'Hingle'
"Azgot" 'Azgot'
"Piers" 'Piers'
"Carlton" 'Carlton'
"Hershell" 'Hershell'
"Gawain" 'Gawain'
"Durfkor" 'Durfkor'
"Paps" 'Paps'
"Hodor" 'Hodor'
] ]
"Peasant F": [ 'Peasant F': [
"Hilda" 'Hilda'
"Icey" 'Icey'
"Matilda" 'Matilda'
"Mertia" 'Mertia'
"Mary" 'Mary'
"Brandy" 'Brandy'
"Gwendolin" 'Gwendolin'
"Tabitha" 'Tabitha'
"Regan" 'Regan'
"Giselle" 'Giselle'
"Bernadette" 'Bernadette'
] ]
"Archer F": [ 'Archer F': [
"Phoebe" 'Phoebe'
"Mira" 'Mira'
"Agapi" 'Agapi'
"Cecily" 'Cecily'
"Tansy" 'Tansy'
"Ivy" 'Ivy'
"Gemma" 'Gemma'
"Keturah" 'Keturah'
"Korra" 'Korra'
"Kim" 'Kim'
"Odette" 'Odette'
"Orly" 'Orly'
"Mercedes" 'Mercedes'
"Rosaline" 'Rosaline'
"Vesper" 'Vesper'
"Beverly" 'Beverly'
"Natalie" 'Natalie'
"Clare" 'Clare'
"Rowan" 'Rowan'
"Omar" 'Omar'
"Alden" 'Alden'
"Cairn" 'Cairn'
"Jensen" 'Jensen'
"Yilitha" 'Yilitha'
"Mirana" 'Mirana'
"Lina" 'Lina'
"Luna" 'Luna'
"Alleria" 'Alleria'
"Vereesa" 'Vereesa'
"Beatrice" 'Beatrice'
] ]
"Archer M": [ 'Archer M': [
"Brian" 'Brian'
"Cole" 'Cole'
"Roman" 'Roman'
"Hunter" 'Hunter'
"Simon" 'Simon'
"Robin" 'Robin'
"Quinn" 'Quinn'
"Arty" 'Arty'
"Gimsley" 'Gimsley'
"Fidsdale" 'Fidsdale'
"Slyvos" 'Slyvos'
"Logos" 'Logos'
"Denin" 'Denin'
"Lycan" 'Lycan'
"Loco" 'Loco'
"Vican" 'Vican'
"Mars" 'Mars'
"Dev" 'Dev'
"Oliver" 'Oliver'
] ]
"Ogre Munchkin M": [ 'Ogre Munchkin M': [
"Brack" 'Brack'
"Gort" 'Gort'
"Weeb" 'Weeb'
"Nerph" 'Nerph'
"Kratt" 'Kratt'
"Smerk" 'Smerk'
"Raack" 'Raack'
"Dobo" 'Dobo'
"Draff" 'Draff'
"Zozo" 'Zozo'
"Kogpole" 'Kogpole'
"Leerer" 'Leerer'
"Skoggen" 'Skoggen'
"Treg" 'Treg'
"Goreball" 'Goreball'
"Gert" 'Gert'
"Thabt" 'Thabt'
"Snortt" 'Snortt'
"Kog" 'Kog'
"Ursa" 'Ursa'
"Ragtime" 'Ragtime'
] ]
"Ogre Munchkin F": [ 'Ogre Munchkin F': [
"Iyert" 'Iyert'
"Palt" 'Palt'
"Shmeal" 'Shmeal'
"Gurzunn" 'Gurzunn'
"Yugark" 'Yugark'
"Dosha" 'Dosha'
"Inski" 'Inski'
"Lacos" 'Lacos'
"Upfish" 'Upfish'
] ]
"Ogre Peon M": [ 'Ogre Peon M': [
"Durbo" 'Durbo'
"Kurger" 'Kurger'
"Mudwich" 'Mudwich'
"Ba Bo" 'Ba Bo'
"Zugger" 'Zugger'
"Toe Pod" 'Toe Pod'
] ]
"Ogre Peon F": [ 'Ogre Peon F': [
"Iblet" 'Iblet'
"Lorba" 'Lorba'
"Zzoya" 'Zzoya'
"Yamra" 'Yamra'
"Greeke" 'Greeke'
"Vapa" 'Vapa'
] ]
"Ogre M": [ 'Ogre M': [
"Krogg" 'Krogg'
"Dronck" 'Dronck'
"Trogdor" 'Trogdor'
"Kulgor" 'Kulgor'
"Skrungt" 'Skrungt'
"Mak Fod" 'Mak Fod'
"Trung" 'Trung'
"Axe Ox" 'Axe Ox'
"Vargutt" 'Vargutt'
"Grumus" 'Grumus'
"Gug" 'Gug'
"Tarlok" 'Tarlok'
"Gurulax" 'Gurulax'
"Mokrul" 'Mokrul'
"Polifemo" 'Polifemo'
"Muthyala" 'Muthyala'
"Saltporker" 'Saltporker'
] ]
"Ogre F": [ 'Ogre F': [
"Nareng" 'Nareng'
"Morthrug" 'Morthrug'
"Glonc" 'Glonc'
"Marghurk" 'Marghurk'
"Martha" 'Martha'
"Holkam" 'Holkam'
"Alkaz" 'Alkaz'
"Gar'ah" 'Gar\'ah'
"Mak'rah" 'Mak\'rah'
"Marnag" 'Marnag'
] ]
"Ogre Brawler": [ 'Ogre Brawler': [
"Grul'thock" 'Grul\'thock'
"Boz" 'Boz'
"Trod" 'Trod'
"Muul" 'Muul'
"Grumoll" 'Grumoll'
"Burobb" 'Burobb'
"Arelt" 'Arelt'
"Zagurk" 'Zagurk'
"Zeredd" 'Zeredd'
"Borgag" 'Borgag'
"Grognar" 'Grognar'
"Ironjaw" 'Ironjaw'
"Tuguro" 'Tuguro'
"York" 'York'
"Ork'han" 'Ork\'han'
"Roast Beefy" 'Roast Beefy'
"Haggar" 'Haggar'
] ]
"Ogre Fangrider": [ 'Ogre Fangrider': [
"Dreek" 'Dreek'
"Flarsho" 'Flarsho'
"Mizzy" 'Mizzy'
"Secka" 'Secka'
"Arizard" 'Arizard'
"Secka" 'Secka'
"Arizard" 'Arizard'
"Morzgret" 'Morzgret'
"Doralt" 'Doralt'
"Geggret" 'Geggret'
"Gurzthrot" 'Gurzthrot'
"Murgark" 'Murgark'
"Muttin" 'Muttin'
"Bortrok" 'Bortrok'
] ]
"Ogre Shaman": [ 'Ogre Shaman': [
"Sham'uk" 'Sham\'uk'
"Il'Du'duka" 'Il\'Du\'duka'
"Ahst'durante" 'Ahst\'durante'
"Poult" 'Poult'
"Aolian'Tak" 'Aolian\'Tak'
"Tuzell" 'Tuzell'
"Yamizeb" 'Yamizeb'
"Yerong" 'Yerong'
"Tuzang" 'Tuzang'
"Varreth" 'Varreth'
"Yugargen" 'Yugargen'
"Turann" 'Turann'
"Ugoki" 'Ugoki'
"Zulabar" 'Zulabar'
"Zo'Goroth" 'Zo\'Goroth'
"Mogadishu" 'Mogadishu'
"Nazgareth" 'Nazgareth'
"Gror" 'Gror'
"Grek" 'Grek'
"Gom" 'Gom'
"Gogg" 'Gogg'
"Ghuk" 'Ghuk'
"Makas" 'Makas'
"Drun" 'Drun'
] ]
"Ogre Thrower": [ 'Ogre Thrower': [
"Kyrgg" 'Kyrgg'
"Durnath" 'Durnath'
"Kraggan" 'Kraggan'
"Rasha" 'Rasha'
"Moza" 'Moza'
"Vujii" 'Vujii'
"Esha" 'Esha'
"Zara" 'Zara'
"Hamedi" 'Hamedi'
"Jinjin" 'Jinjin'
"Yetu" 'Yetu'
"Makas" 'Makas'
"Rakash" 'Rakash'
"Drumbaa" 'Drumbaa'
"Pinakin" 'Pinakin'
] ]
"Burl": [ 'Burl': [
"Borlit" 'Borlit'
"Burlosh" 'Burlosh'
"Dorf" 'Dorf'
] ]
"Griffin Rider": [ 'Griffin Rider': [
"Aeoldan" 'Aeoldan'
"Bestarius" 'Bestarius'
] ]
"Potion Master": [ 'Potion Master': [
"Snake" 'Snake'
"Amaranth" 'Amaranth'
"Zander" 'Zander'
"Arora" 'Arora'
"Curie" 'Curie'
"Clause" 'Clause'
"Vanders" 'Vanders'
] ]
"Librarian": [ 'Librarian': [
"Hushbaum" 'Hushbaum'
"Matilda" 'Matilda'
"Agnes" 'Agnes'
"Agathe" 'Agathe'
"Satish" 'Satish'
] ]
"Equestrian": [ 'Equestrian': [
"Reynaldo" 'Reynaldo'
"Ryder" 'Ryder'
"Thoron" 'Thoron'
"Mirial" 'Mirial'
"Neely" 'Neely'
] ]
"Knight": [ 'Knight': [
"Tharin" 'Tharin'
"Arthur" 'Arthur'
"Galahad" 'Galahad'
"Mace" 'Mace'
"Drake" 'Drake'
"Duran" 'Duran'
"Almeric" 'Almeric'
"Hunfray" 'Hunfray'
"Hank" 'Hank'
"Jeph" 'Jeph'
"Neville" 'Neville'
"Alphonse" 'Alphonse'
"Edward" 'Edward'
] ]
"Captain": [ 'Captain': [
"Anya" 'Anya'
"Brigette" 'Brigette'
"Sarre" 'Sarre'
"Katana" 'Katana'
"Lily" 'Lily'
"Isa" 'Isa'
"Dimia" 'Dimia'
"Jane" 'Jane'
"Lia" 'Lia'
"Hardcastle" 'Hardcastle'
"Leona" 'Leona'
] ]

View file

@ -1,6 +1,6 @@
# http://coffeescriptcookbook.com/chapters/math/generating-predictable-random-numbers # http://coffeescriptcookbook.com/chapters/math/generating-predictable-random-numbers
class Rand class Rand
@className: "Rand" @className: 'Rand'
# If created without a seed, uses current time as seed. # If created without a seed, uses current time as seed.
constructor: (@seed) -> constructor: (@seed) ->
# Knuth and Lewis' improvements to Park and Miller's LCPRNG # Knuth and Lewis' improvements to Park and Miller's LCPRNG

View file

@ -1,7 +1,7 @@
Vector = require './vector' Vector = require './vector'
class Rectangle class Rectangle
@className: "Rectangle" @className: 'Rectangle'
# Class methods for nondestructively operating # Class methods for nondestructively operating
for name in ['add', 'subtract', 'multiply', 'divide'] for name in ['add', 'subtract', 'multiply', 'divide']
do (name) -> do (name) ->

View file

@ -3,7 +3,7 @@
# Other Systems might be things like Attraction, EdgeBounce, EdgeWrap, and non-physics ones, too, like Rendering, Animation, ... # Other Systems might be things like Attraction, EdgeBounce, EdgeWrap, and non-physics ones, too, like Rendering, Animation, ...
module.exports = class System module.exports = class System
@className: "System" @className: 'System'
constructor: (@world, config) -> constructor: (@world, config) ->
# Unlike with Component, we don't automatically copy all our properties onto the World. # Unlike with Component, we don't automatically copy all our properties onto the World.
# Subclasses can copy select properties here if they like. # Subclasses can copy select properties here if they like.

View file

@ -1,16 +1,16 @@
# http://codingowl.com/readblog.php?blogid=124 # http://codingowl.com/readblog.php?blogid=124
module.exports.CollisionCategory = class CollisionCategory module.exports.CollisionCategory = class CollisionCategory
@className: "CollisionCategory" @className: 'CollisionCategory'
constructor: (name, @superteamIndex=null, @collisionSystem) -> constructor: (name, @superteamIndex=null, @collisionSystem) ->
# @superteamIndex is null for "none", "obstacles", and "dead". # @superteamIndex is null for 'none', 'obstacles', and 'dead'.
# It's 0 for "ground", "air", and "ground_and_air" units with no superteams. # It's 0 for 'ground', 'air', and 'ground_and_air' units with no superteams.
# It's 1, 2, or 3 for the superteams it gets after that. We can only have 16 collision categories. # It's 1, 2, or 3 for the superteams it gets after that. We can only have 16 collision categories.
@ground = name.search("ground") isnt -1 @ground = name.search('ground') isnt -1
@air = name.search("air") isnt -1 @air = name.search('air') isnt -1
@name = CollisionCategory.nameFor name, @superteamIndex @name = CollisionCategory.nameFor name, @superteamIndex
@superteamIndex ?= 0 if @ground or @air @superteamIndex ?= 0 if @ground or @air
@number = 1 << @collisionSystem.totalCategories++ @number = 1 << @collisionSystem.totalCategories++
if @collisionSystem.totalCategories > 16 then console.log "There should only be 16 collision categories!" if @collisionSystem.totalCategories > 16 then console.log 'There should only be 16 collision categories!'
@mask = 0 @mask = 0
@collisionSystem.allCategories[@name] = @ @collisionSystem.allCategories[@name] = @
for otherCatName, otherCat of @collisionSystem.allCategories for otherCatName, otherCat of @collisionSystem.allCategories
@ -19,32 +19,32 @@ module.exports.CollisionCategory = class CollisionCategory
otherCat.mask = otherCat.mask | @number otherCat.mask = otherCat.mask | @number
collidesWith: (cat) -> collidesWith: (cat) ->
# "none" collides with nothing # 'none' collides with nothing
return false if @name is "none" or cat.name is "none" return false if @name is 'none' or cat.name is 'none'
# "obstacles" collides with everything; could also try letting air units (but not ground_and_air) fly over these # 'obstacles' collides with everything; could also try letting air units (but not ground_and_air) fly over these
return true if cat.name is "obstacles" or @name is "obstacles" return true if cat.name is 'obstacles' or @name is 'obstacles'
# "dead" collides only with obstacles # 'dead' collides only with obstacles
return cat.name is "obstacles" if @name is "dead" return cat.name is 'obstacles' if @name is 'dead'
return @name is "obstacles" if cat.name is "dead" return @name is 'obstacles' if cat.name is 'dead'
# "ground_and_air_<team>" units don't hit ground or air units on their team (so missiles don't hit same team) # 'ground_and_air_<team>' units don't hit ground or air units on their team (so missiles don't hit same team)
sameTeam = @superteamIndex and cat.superteamIndex is @superteamIndex sameTeam = @superteamIndex and cat.superteamIndex is @superteamIndex
return false if sameTeam and @ground and @air return false if sameTeam and @ground and @air
# actually, "ground_and_air<team>" units don't hit any ground_and_air units (temp missile collision fix) # actually, 'ground_and_air<team>' units don't hit any ground_and_air units (temp missile collision fix)
return false if @ground and @air and cat.ground and cat.air return false if @ground and @air and cat.ground and cat.air
# "ground" collides with "ground" # 'ground' collides with 'ground'
return true if cat.ground and @ground return true if cat.ground and @ground
# "air" collides with "air" # 'air' collides with 'air'
return true if cat.air and @air return true if cat.air and @air
# doesn't collide (probably "ground" and "air") # doesn't collide (probably 'ground' and 'air')
false false
@nameFor: (name, superteamIndex=null) -> @nameFor: (name, superteamIndex=null) ->
return name unless name.match("ground") or name.match("air") return name unless name.match('ground') or name.match('air')
name + "_" + (superteamIndex or 0) name + '_' + (superteamIndex or 0)

View file

@ -131,7 +131,7 @@ module.exports = class Thang
source.original = chain.original.toString() source.original = chain.original.toString()
source.user = chain.user?.toString() source.user = chain.user?.toString()
else else
source.original = @[methodName]?.toString() ? "" source.original = @[methodName]?.toString() ? ''
source.original = Aether.getFunctionBody source.original source.original = Aether.getFunctionBody source.original
source source

View file

@ -8,15 +8,15 @@ else
bytesPerFloat = 4 bytesPerFloat = 4
module.exports = class ThangState module.exports = class ThangState
@className: "ThangState" @className: 'ThangState'
@trackedPropertyTypes: [ @trackedPropertyTypes: [
"boolean" 'boolean'
"number" 'number'
"string" 'string'
"array" # will turn everything into strings 'array' # will turn everything into strings
"object" # grrr 'object' # grrr
"Vector" 'Vector'
"Thang" # serialized as ids, like strings 'Thang' # serialized as ids, like strings
] ]
hasRestored: false hasRestored: false
@ -40,7 +40,7 @@ module.exports = class ThangState
unless type unless type
type = @trackedPropertyTypes[propIndex] type = @trackedPropertyTypes[propIndex]
storage = @trackedPropertyValues[propIndex] storage = @trackedPropertyValues[propIndex]
if type is "Vector" if type is 'Vector'
value = new Vector storage[3 * @frameIndex], storage[3 * @frameIndex + 1], storage[3 * @frameIndex + 2] value = new Vector storage[3 * @frameIndex], storage[3 * @frameIndex + 1], storage[3 * @frameIndex + 2]
else if type is 'string' else if type is 'string'
specialKey = storage[@frameIndex] specialKey = storage[@frameIndex]
@ -78,7 +78,7 @@ module.exports = class ThangState
type = @trackedPropertyTypes[propIndex] type = @trackedPropertyTypes[propIndex]
storage = @trackedPropertyValues[propIndex] storage = @trackedPropertyValues[propIndex]
props.push(@thang[prop] = @getStoredProp propIndex, type, storage) props.push(@thang[prop] = @getStoredProp propIndex, type, storage)
#console.log @frameIndex, @thang.id, prop, propIndex, type, storage, "got", @thang[prop] #console.log @frameIndex, @thang.id, prop, propIndex, type, storage, 'got', @thang[prop]
@props = props @props = props
@trackedPropertyTypes = @trackedPropertyValues = @specialKeysToValues = null # leave @trackedPropertyKeys for indexing @trackedPropertyTypes = @trackedPropertyValues = @specialKeysToValues = null # leave @trackedPropertyKeys for indexing
@hasRestored = true @hasRestored = true
@ -90,14 +90,14 @@ module.exports = class ThangState
restorePartial: (ratio) -> restorePartial: (ratio) ->
inverse = 1 - ratio inverse = 1 - ratio
for prop, propIndex in @trackedPropertyKeys when prop is "pos" or prop is "rotation" for prop, propIndex in @trackedPropertyKeys when prop is 'pos' or prop is 'rotation'
if @hasRestored if @hasRestored
value = @props[propIndex] value = @props[propIndex]
else else
type = @trackedPropertyTypes[propIndex] type = @trackedPropertyTypes[propIndex]
storage = @trackedPropertyValues[propIndex] storage = @trackedPropertyValues[propIndex]
value = @getStoredProp propIndex, type, storage value = @getStoredProp propIndex, type, storage
if prop is "pos" if prop is 'pos'
if @thang.teleport and @thang.pos.distanceSquared(value) > 900 if @thang.teleport and @thang.pos.distanceSquared(value) > 900
# Don't interpolate; it was probably a teleport. https://github.com/codecombat/codecombat/issues/738 # Don't interpolate; it was probably a teleport. https://github.com/codecombat/codecombat/issues/738
@thang.pos = value @thang.pos = value
@ -106,7 +106,7 @@ module.exports = class ThangState
@thang.pos.x = inverse * @thang.pos.x + ratio * value.x @thang.pos.x = inverse * @thang.pos.x + ratio * value.x
@thang.pos.y = inverse * @thang.pos.y + ratio * value.y @thang.pos.y = inverse * @thang.pos.y + ratio * value.y
@thang.pos.z = inverse * @thang.pos.z + ratio * value.z @thang.pos.z = inverse * @thang.pos.z + ratio * value.z
else if prop is "rotation" else if prop is 'rotation'
@thang.rotation = inverse * @thang.rotation + ratio * value @thang.rotation = inverse * @thang.rotation + ratio * value
@thang.partialState = true @thang.partialState = true
@ @
@ -119,7 +119,7 @@ module.exports = class ThangState
value = @props[originalPropIndex] value = @props[originalPropIndex]
if value if value
# undefined, null, false, 0 won't trigger in this serialization code scheme anyway, so we can't differentiate between them when deserializing # undefined, null, false, 0 won't trigger in this serialization code scheme anyway, so we can't differentiate between them when deserializing
if type is "Vector" if type is 'Vector'
storage[3 * frameIndex] = value.x storage[3 * frameIndex] = value.x
storage[3 * frameIndex + 1] = value.y storage[3 * frameIndex + 1] = value.y
storage[3 * frameIndex + 2] = value.z storage[3 * frameIndex + 2] = value.z
@ -157,7 +157,7 @@ module.exports = class ThangState
storage[frameIndex] = specialKey storage[frameIndex] = specialKey
else else
storage[frameIndex] = value storage[frameIndex] = value
#console.log @thang.id, "assigned prop", originalPropIndex, newPropIndex, value, type, "at", frameIndex, "to", storage[frameIndex] #console.log @thang.id, 'assigned prop', originalPropIndex, newPropIndex, value, type, 'at', frameIndex, 'to', storage[frameIndex]
null null
@deserialize: (world, frameIndex, thang, trackedPropertyKeys, trackedPropertyTypes, trackedPropertyValues, specialKeysToValues) -> @deserialize: (world, frameIndex, thang, trackedPropertyKeys, trackedPropertyTypes, trackedPropertyValues, specialKeysToValues) ->
@ -173,12 +173,12 @@ module.exports = class ThangState
@transferableBytesNeededForType: (type, nFrames) -> @transferableBytesNeededForType: (type, nFrames) ->
bytes = switch type bytes = switch type
when "boolean" then 1 when 'boolean' then 1
when "number" then bytesPerFloat when 'number' then bytesPerFloat
when "Vector" then bytesPerFloat * 3 when 'Vector' then bytesPerFloat * 3
when "string" then 4 when 'string' then 4
when "Thang" then 4 # turn them into strings of their ids when 'Thang' then 4 # turn them into strings of their ids
when "array" then 4 # turn them into strings and hope it doesn't explode? when 'array' then 4 # turn them into strings and hope it doesn't explode?
else 0 else 0
# We need to be a multiple of bytesPerFloat otherwise bigger-byte array (Float64Array, etc.) offsets won't work # We need to be a multiple of bytesPerFloat otherwise bigger-byte array (Float64Array, etc.) offsets won't work
# http://www.kirupa.com/forum/showthread.php?378737-Typed-Arrays-Y-U-No-offset-at-values-other-than-multiples-of-element-size # http://www.kirupa.com/forum/showthread.php?378737-Typed-Arrays-Y-U-No-offset-at-values-other-than-multiples-of-element-size
@ -187,17 +187,17 @@ module.exports = class ThangState
@createArrayForType: (type, nFrames, buffer, offset) -> @createArrayForType: (type, nFrames, buffer, offset) ->
bytes = @transferableBytesNeededForType type, nFrames bytes = @transferableBytesNeededForType type, nFrames
storage = switch type storage = switch type
when "boolean" when 'boolean'
new Uint8Array(buffer, offset, nFrames) new Uint8Array(buffer, offset, nFrames)
when "number" when 'number'
new FloatArrayType(buffer, offset, nFrames) new FloatArrayType(buffer, offset, nFrames)
when "Vector" when 'Vector'
new FloatArrayType(buffer, offset, nFrames * 3) new FloatArrayType(buffer, offset, nFrames * 3)
when "string" when 'string'
new Uint32Array(buffer, offset, nFrames) new Uint32Array(buffer, offset, nFrames)
when "Thang" when 'Thang'
new Uint32Array(buffer, offset, nFrames) new Uint32Array(buffer, offset, nFrames)
when "array" when 'array'
new Uint32Array(buffer, offset, nFrames) new Uint32Array(buffer, offset, nFrames)
else else
[] []

View file

@ -1,6 +1,6 @@
# https://github.com/hornairs/blog/blob/master/assets/coffeescripts/flocking/vector.coffee # https://github.com/hornairs/blog/blob/master/assets/coffeescripts/flocking/vector.coffee
class Vector class Vector
@className: "Vector" @className: 'Vector'
# Class methods for nondestructively operating # Class methods for nondestructively operating
for name in ['add', 'subtract', 'multiply', 'divide', 'limit', 'normalize'] for name in ['add', 'subtract', 'multiply', 'divide', 'limit', 'normalize']
do (name) -> do (name) ->

View file

@ -12,7 +12,7 @@ PROGRESS_UPDATE_INTERVAL = 200
DESERIALIZATION_INTERVAL = 20 DESERIALIZATION_INTERVAL = 20
module.exports = class World module.exports = class World
@className: "World" @className: 'World'
age: 0 age: 0
ended: false ended: false
preloading: false # Whether we are just preloading a world in case we soon cast it preloading: false # Whether we are just preloading a world in case we soon cast it
@ -63,12 +63,12 @@ module.exports = class World
@thangMap[thang.id] = thang @thangMap[thang.id] = thang
thangDialogueSounds: -> thangDialogueSounds: ->
if @frames.length < @totalFrames then throw new Error("World should be over before grabbing dialogue") if @frames.length < @totalFrames then throw new Error('World should be over before grabbing dialogue')
[sounds, seen] = [[], {}] [sounds, seen] = [[], {}]
for frame in @frames for frame in @frames
for thangID, state of frame.thangStateMap for thangID, state of frame.thangStateMap
continue unless state.thang.say and sayMessage = state.getStateForProp "sayMessage" continue unless state.thang.say and sayMessage = state.getStateForProp 'sayMessage'
soundKey = state.thang.spriteName + ":" + sayMessage soundKey = state.thang.spriteName + ':' + sayMessage
unless seen[soundKey] unless seen[soundKey]
sounds.push [state.thang.spriteName, sayMessage] sounds.push [state.thang.spriteName, sayMessage]
seen[soundKey] = true seen[soundKey] = true
@ -83,7 +83,7 @@ module.exports = class World
loadFrames: (loadedCallback, errorCallback, loadProgressCallback, skipDeferredLoading, loadUntilFrame) -> loadFrames: (loadedCallback, errorCallback, loadProgressCallback, skipDeferredLoading, loadUntilFrame) ->
return if @aborted return if @aborted
unless @thangs.length unless @thangs.length
console.log "Warning: loadFrames called on empty World (no thangs)." console.log 'Warning: loadFrames called on empty World (no thangs).'
t1 = now() t1 = now()
@t0 ?= t1 @t0 ?= t1
if loadUntilFrame if loadUntilFrame
@ -113,7 +113,7 @@ module.exports = class World
loadProgressCallback? i / @totalFrames unless @preloading loadProgressCallback? i / @totalFrames unless @preloading
t1 = t2 t1 = t2
if t2 - @t0 > 1000 if t2 - @t0 > 1000
console.log(' Loaded', i, 'of', @totalFrames, "(+" + (t2 - @t0).toFixed(0) + "ms)") console.log ' Loaded', i, 'of', @totalFrames, '(+' + (t2 - @t0).toFixed(0) + 'ms)'
@t0 = t2 @t0 = t2
continueFn = => continueFn = =>
return if @destroyed return if @destroyed
@ -155,7 +155,7 @@ module.exports = class World
for levelSystem in level.systems for levelSystem in level.systems
systemModel = levelSystem.model systemModel = levelSystem.model
config = levelSystem.config config = levelSystem.config
systemClass = @loadClassFromCode systemModel.js, systemModel.name, "system" systemClass = @loadClassFromCode systemModel.js, systemModel.name, 'system'
#console.log "using db system class ---\n", systemClass, "\n--- from code ---n", systemModel.js, "\n---" #console.log "using db system class ---\n", systemClass, "\n--- from code ---n", systemModel.js, "\n---"
system = new systemClass @, config system = new systemClass @, config
@addSystems system @addSystems system
@ -169,15 +169,15 @@ module.exports = class World
# Load new Thangs # Load new Thangs
toAdd = [] toAdd = []
for d in level.thangs for d in level.thangs
continue if d.thangType is "Interface" # ignore old Interface Thangs until we've migrated away continue if d.thangType is 'Interface' # ignore old Interface Thangs until we've migrated away
components = [] components = []
for component in d.components for component in d.components
componentModel = _.find level.levelComponents, (c) -> c.original is component.original and c.version.major is (component.majorVersion ? 0) componentModel = _.find level.levelComponents, (c) -> c.original is component.original and c.version.major is (component.majorVersion ? 0)
#console.log "found model", componentModel, "from", component, "for", d.id, "from existing components", level.levelComponents #console.log 'found model', componentModel, 'from', component, 'for', d.id, 'from existing components', level.levelComponents
componentClass = @loadClassFromCode componentModel.js, componentModel.name, "component" componentClass = @loadClassFromCode componentModel.js, componentModel.name, 'component'
components.push [componentClass, component.config] components.push [componentClass, component.config]
#console.log "---", d.id, "using db component class ---\n", componentClass, "\n--- from code ---\n", componentModel.js, '\n---' #console.log '---', d.id, "using db component class ---\n", componentClass, "\n--- from code ---\n", componentModel.js, '\n---'
#console.log "(found", componentModel, "for id", component.original, "from", level.levelComponents, ")" #console.log '(found', componentModel, 'for id', component.original, 'from', level.levelComponents, ')'
thangType = d.thangType thangType = d.thangType
thangTypeModel = _.find level.thangTypes, (t) -> t.original is thangType thangTypeModel = _.find level.thangTypes, (t) -> t.original is thangType
thangType = thangTypeModel.name if thangTypeModel thangType = thangTypeModel.name if thangTypeModel
@ -185,7 +185,7 @@ module.exports = class World
try try
thang.addComponents components... thang.addComponents components...
catch e catch e
console.error "couldn't load components for", d.thangType, d.id, "because", e, e.stack, e.stackTrace console.error 'couldn\'t load components for', d.thangType, d.id, 'because', e, e.stack, e.stackTrace
toAdd.push thang toAdd.push thang
@extraneousThangs = consolidateThangs toAdd if willSimulate # combine walls, for example; serialize the leftovers later @extraneousThangs = consolidateThangs toAdd if willSimulate # combine walls, for example; serialize the leftovers later
for thang in toAdd for thang in toAdd
@ -200,11 +200,11 @@ module.exports = class World
@scripts = [] @scripts = []
@addScripts level.scripts... @addScripts level.scripts...
loadClassFromCode: (js, name, kind="component") -> loadClassFromCode: (js, name, kind='component') ->
# Cache them based on source code so we don't have to worry about extra compilations # Cache them based on source code so we don't have to worry about extra compilations
@componentCodeClassMap ?= {} @componentCodeClassMap ?= {}
@systemCodeClassMap ?= {} @systemCodeClassMap ?= {}
map = if kind is "component" then @componentCodeClassMap else @systemCodeClassMap map = if kind is 'component' then @componentCodeClassMap else @systemCodeClassMap
c = map[js] c = map[js]
return c if c return c if c
c = map[js] = eval js c = map[js] = eval js
@ -285,7 +285,7 @@ module.exports = class World
serialize: -> serialize: ->
# Code hotspot; optimize it # Code hotspot; optimize it
if @frames.length < @totalFrames then throw new Error("World Should Be Over Before Serialization") if @frames.length < @totalFrames then throw new Error('World Should Be Over Before Serialization')
[transferableObjects, nontransferableObjects] = [0, 0] [transferableObjects, nontransferableObjects] = [0, 0]
o = {totalFrames: @totalFrames, maxTotalFrames: @maxTotalFrames, frameRate: @frameRate, dt: @dt, victory: @victory, userCodeMap: {}, trackedProperties: {}} o = {totalFrames: @totalFrames, maxTotalFrames: @maxTotalFrames, frameRate: @frameRate, dt: @dt, victory: @victory, userCodeMap: {}, trackedProperties: {}}
o.trackedProperties[prop] = @[prop] for prop in @trackedProperties or [] o.trackedProperties[prop] = @[prop] for prop in @trackedProperties or []
@ -364,20 +364,20 @@ module.exports = class World
flattened.push value flattened.push value
o.storageBuffer = flattened o.storageBuffer = flattened
#console.log "Allocating memory:", (t1 - t0).toFixed(0), "ms; assigning values:", (t2 - t1).toFixed(0), "ms, so", ((t2 - t1) / @frames.length).toFixed(3), "ms per frame" #console.log 'Allocating memory:', (t1 - t0).toFixed(0), 'ms; assigning values:', (t2 - t1).toFixed(0), 'ms, so', ((t2 - t1) / @frames.length).toFixed(3), 'ms per frame'
#console.log "Got", transferableObjects, "transferable objects and", nontransferableObjects, "nontransferable; stored", transferableStorageBytesNeeded, "bytes transferably" #console.log 'Got', transferableObjects, 'transferable objects and', nontransferableObjects, 'nontransferable; stored', transferableStorageBytesNeeded, 'bytes transferably'
o.thangs = (t.serialize() for t in @thangs.concat(@extraneousThangs ? [])) o.thangs = (t.serialize() for t in @thangs.concat(@extraneousThangs ? []))
o.scriptNotes = (sn.serialize() for sn in @scriptNotes) o.scriptNotes = (sn.serialize() for sn in @scriptNotes)
if o.scriptNotes.length > 200 if o.scriptNotes.length > 200
console.log "Whoa, serializing a lot of WorldScriptNotes here:", o.scriptNotes.length console.log 'Whoa, serializing a lot of WorldScriptNotes here:', o.scriptNotes.length
{serializedWorld: o, transferableObjects: [o.storageBuffer]} {serializedWorld: o, transferableObjects: [o.storageBuffer]}
@deserialize: (o, classMap, oldSerializedWorldFrames, finishedWorldCallback) -> @deserialize: (o, classMap, oldSerializedWorldFrames, finishedWorldCallback) ->
# Code hotspot; optimize it # Code hotspot; optimize it
#console.log "Deserializing", o, "length", JSON.stringify(o).length #console.log 'Deserializing', o, 'length', JSON.stringify(o).length
#console.log JSON.stringify(o) #console.log JSON.stringify(o)
#console.log "Got special keys and values:", o.specialValuesToKeys, o.specialKeysToValues #console.log 'Got special keys and values:', o.specialValuesToKeys, o.specialKeysToValues
perf = {} perf = {}
perf.t0 = now() perf.t0 = now()
w = new World o.userCodeMap, classMap w = new World o.userCodeMap, classMap
@ -424,13 +424,13 @@ module.exports = class World
w.ended = true w.ended = true
w.getFrame(w.totalFrames - 1).restoreState() w.getFrame(w.totalFrames - 1).restoreState()
perf.t5 = now() perf.t5 = now()
console.log "Deserialization:", (perf.t5 - perf.t0).toFixed(0) + "ms (" + ((perf.t5 - perf.t0) / w.frames.length).toFixed(3) + "ms per frame).", perf.batches, "batches." console.log 'Deserialization:', (perf.t5 - perf.t0).toFixed(0) + 'ms (' + ((perf.t5 - perf.t0) / w.frames.length).toFixed(3) + 'ms per frame).', perf.batches, 'batches.'
if false if false
console.log " Deserializing--constructing new World:", (perf.t1 - perf.t0).toFixed(2) + "ms" console.log ' Deserializing--constructing new World:', (perf.t1 - perf.t0).toFixed(2) + 'ms'
console.log " Deserializing--Thangs and ScriptNotes:", (perf.t2 - perf.t1).toFixed(2) + "ms" console.log ' Deserializing--Thangs and ScriptNotes:', (perf.t2 - perf.t1).toFixed(2) + 'ms'
console.log " Deserializing--reallocating memory:", (perf.t3 - perf.t2).toFixed(2) + "ms" console.log ' Deserializing--reallocating memory:', (perf.t3 - perf.t2).toFixed(2) + 'ms'
console.log " Deserializing--WorldFrames:", (perf.t4 - perf.t3).toFixed(2) + "ms" console.log ' Deserializing--WorldFrames:', (perf.t4 - perf.t3).toFixed(2) + 'ms'
console.log " Deserializing--restoring last WorldFrame:", (perf.t5 - perf.t4).toFixed(2) + "ms" console.log ' Deserializing--restoring last WorldFrame:', (perf.t5 - perf.t4).toFixed(2) + 'ms'
finishedWorldCallback w finishedWorldCallback w
findFirstChangedFrame: (oldWorld) -> findFirstChangedFrame: (oldWorld) ->
@ -440,9 +440,9 @@ module.exports = class World
break unless oldFrame and newFrame.hash is oldFrame.hash break unless oldFrame and newFrame.hash is oldFrame.hash
@firstChangedFrame = i @firstChangedFrame = i
if @frames[i] if @frames[i]
console.log "First changed frame is", @firstChangedFrame, "with hash", @frames[i].hash, "compared to", oldWorld.frames[i]?.hash console.log 'First changed frame is', @firstChangedFrame, 'with hash', @frames[i].hash, 'compared to', oldWorld.frames[i]?.hash
else else
console.log "No frames were changed out of all", @frames.length console.log 'No frames were changed out of all', @frames.length
@firstChangedFrame @firstChangedFrame
pointsForThang: (thangID, frameStart=0, frameEnd=null, camera=null, resolution=4) -> pointsForThang: (thangID, frameStart=0, frameEnd=null, camera=null, resolution=4) ->
@ -478,7 +478,7 @@ module.exports = class World
actionsForThang: (thangID, keepIdle=false) -> actionsForThang: (thangID, keepIdle=false) ->
# Optimized # Optimized
@actionsForThangCache ?= {} @actionsForThangCache ?= {}
cacheKey = thangID + "_" + Boolean(keepIdle) cacheKey = thangID + '_' + Boolean(keepIdle)
cached = @actionsForThangCache[cacheKey] cached = @actionsForThangCache[cacheKey]
return cached if cached return cached if cached
states = (frame.thangStateMap[thangID] for frame in @frames) states = (frame.thangStateMap[thangID] for frame in @frames)

View file

@ -1,10 +1,12 @@
ThangState = require './thang_state' ThangState = require './thang_state'
module.exports = class WorldFrame module.exports = class WorldFrame
@className: "WorldFrame" @className: 'WorldFrame'
constructor: (@world, @time=0) -> constructor: (@world, @time=0) ->
@thangStateMap = {} @thangStateMap = {}
@setState() if @world @setState() if @world
getNextFrame: -> getNextFrame: ->
# Optimized. Must be called while thangs are current at this frame. # Optimized. Must be called while thangs are current at this frame.
nextTime = @time + @world.dt nextTime = @time + @world.dt
@ -22,7 +24,7 @@ module.exports = class WorldFrame
thangState.restore() for thangID, thangState of @thangStateMap thangState.restore() for thangID, thangState of @thangStateMap
for thang in @world.thangs for thang in @world.thangs
if not @thangStateMap[thang.id] and not thang.stateless if not @thangStateMap[thang.id] and not thang.stateless
#console.log "Frame", @time, "restoring state for", thang.id, "and saying it don't exist" #console.log 'Frame', @time, 'restoring state for', thang.id, 'and saying it don\'t exist'
thang.exists = false thang.exists = false
restorePartialState: (ratio) -> restorePartialState: (ratio) ->
@ -33,22 +35,22 @@ module.exports = class WorldFrame
if not thangState if not thangState
if not thang.stateless if not thang.stateless
thang.exists = false thang.exists = false
#console.log "Frame", @time, "restoring state for", thang.id, "in particular and saying it don't exist" #console.log 'Frame', @time, 'restoring state for', thang.id, 'in particular and saying it don\'t exist'
return return
thangState.restore() thangState.restore()
clearEvents: -> thang.currentEvents = [] for thang in @world.thangs clearEvents: -> thang.currentEvents = [] for thang in @world.thangs
toString: -> toString: ->
map = ((' ' for x in [0 .. @world.width]) \ map = ((' ' for x in [0 .. @world.width]) \
for y in [0 .. @world.height]) for y in [0 .. @world.height])
symbols = ".ox@dfga[]/D" symbols = '.ox@dfga[]/D'
for thang, i in @world.thangs when thang.rectangle for thang, i in @world.thangs when thang.rectangle
rect = thang.rectangle().axisAlignedBoundingBox() rect = thang.rectangle().axisAlignedBoundingBox()
for y in [Math.floor(rect.y - rect.height / 2) ... Math.ceil(rect.y + rect.height / 2)] for y in [Math.floor(rect.y - rect.height / 2) ... Math.ceil(rect.y + rect.height / 2)]
for x in [Math.floor(rect.x - rect.width / 2) ... Math.ceil(rect.x + rect.width / 2)] for x in [Math.floor(rect.x - rect.width / 2) ... Math.ceil(rect.x + rect.width / 2)]
map[y][x] = symbols[i % symbols.length] if 0 <= y < @world.height and 0 <= x < @world.width map[y][x] = symbols[i % symbols.length] if 0 <= y < @world.height and 0 <= x < @world.width
@time + "\n" + (xs.join(' ') for xs in map).join('\n') + '\n' @time + '\n' + (xs.join(' ') for xs in map).join('\n') + '\n'
serialize: (frameIndex, trackedPropertiesThangIDs, trackedPropertiesPerThangIndices, trackedPropertiesPerThangTypes, trackedPropertiesPerThangValues, specialValuesToKeys, specialKeysToValues) -> serialize: (frameIndex, trackedPropertiesThangIDs, trackedPropertiesPerThangIndices, trackedPropertiesPerThangTypes, trackedPropertiesPerThangValues, specialValuesToKeys, specialKeysToValues) ->
# Optimize # Optimize

View file

@ -2,7 +2,7 @@
{scriptMatchesEventPrereqs} = require './script_event_prereqs' {scriptMatchesEventPrereqs} = require './script_event_prereqs'
module.exports = class WorldScriptNote module.exports = class WorldScriptNote
@className: "WorldScriptNote" @className: 'WorldScriptNote'
constructor: (script, @event, world) -> constructor: (script, @event, world) ->
return unless script? return unless script?
@invalid = true @invalid = true

View file

@ -78,17 +78,17 @@ module.exports.consolidateThangs = consolidateThangs = (thangs) ->
topmost = _.max structural, (t) -> t.pos.y + t.height / 2 topmost = _.max structural, (t) -> t.pos.y + t.height / 2
leftmost = _.min structural, (t) -> t.pos.x - t.width / 2 leftmost = _.min structural, (t) -> t.pos.x - t.width / 2
bottommost = _.min structural, (t) -> t.pos.y - t.height / 2 bottommost = _.min structural, (t) -> t.pos.y - t.height / 2
console.log "got rightmost", rightmost.id, "topmost", topmost.id, "lefmostmost", leftmost.id, "bottommost", bottommost.id, "out of", structural.length, "structural thangs" if debug console.log 'got rightmost', rightmost.id, 'topmost', topmost.id, 'lefmostmost', leftmost.id, 'bottommost', bottommost.id, 'out of', structural.length, 'structural thangs' if debug
left = Math.min 0, leftmost.pos.x - leftmost.width / 2 left = Math.min 0, leftmost.pos.x - leftmost.width / 2
bottom = Math.min 0, bottommost.pos.y - bottommost.height / 2 bottom = Math.min 0, bottommost.pos.y - bottommost.height / 2
if (left < 0) or (bottom < 0) if (left < 0) or (bottom < 0)
console.error "Negative structural Thangs aren't supported, sorry!" # TODO: largestRectangle, AI System, and anything else that accesses grid directly need updating to finish this console.error 'Negative structural Thangs aren\'t supported, sorry!' # TODO: largestRectangle, AI System, and anything else that accesses grid directly need updating to finish this
left = 0 left = 0
bottom = 0 bottom = 0
width = rightmost.pos.x + rightmost.width / 2 - left width = rightmost.pos.x + rightmost.width / 2 - left
height = topmost.pos.y + topmost.height / 2 - bottom height = topmost.pos.y + topmost.height / 2 - bottom
padding = 0 padding = 0
console.log "got max width", width, "height", height, "left", left, "bottom", bottom, "of thangs", thangs.length, "structural", structural.length if debug console.log 'got max width', width, 'height', height, 'left', left, 'bottom', bottom, 'of thangs', thangs.length, 'structural', structural.length if debug
grid = new Grid structural, width, height, padding, left, bottom grid = new Grid structural, width, height, padding, left, bottom
console.log grid.toString() if debug console.log grid.toString() if debug
@ -107,14 +107,14 @@ module.exports.consolidateThangs = consolidateThangs = (thangs) ->
grid.grid[y2][x2] = [] grid.grid[y2][x2] = []
console.log grid.toString() if debug console.log grid.toString() if debug
thang = structural[dissection.length] # grab one we already know is configured properly thang = structural[dissection.length] # grab one we already know is configured properly
console.error "Hmm, our dissection has more Thangs than the original structural Thangs?", dissection.length unless thang console.error 'Hmm, our dissection has more Thangs than the original structural Thangs?', dissection.length unless thang
thang.width = rect.width thang.width = rect.width
thang.height = rect.height thang.height = rect.height
thang.pos.x = rect.x thang.pos.x = rect.x
thang.pos.y = rect.y thang.pos.y = rect.y
thang.createBodyDef() thang.createBodyDef()
dissection.push thang dissection.push thang
console.log "Turned", structural.length, "structural Thangs into", dissection.length, "dissecting Thangs." console.log 'Turned', structural.length, 'structural Thangs into', dissection.length, 'dissecting Thangs.'
thangs.push dissection... thangs.push dissection...
structural[dissection.length ... structural.length] structural[dissection.length ... structural.length]
@ -133,7 +133,7 @@ module.exports.largestRectangle = largestRectangle = (grid, bottomY, leftX, want
break unless coveredRow break unless coveredRow
coveredRows.push coveredRow coveredRows.push coveredRow
shortestCoveredRow = Math.min(shortestCoveredRow, coveredRow) shortestCoveredRow = Math.min(shortestCoveredRow, coveredRow)
console.log "largestRectangle() for", bottomY, leftX, "got coveredRows", coveredRows if debug console.log 'largestRectangle() for', bottomY, leftX, 'got coveredRows', coveredRows if debug
[maxArea, maxAreaRows, maxAreaRowLength, shortestRow] = [0, 0, 0, 0] [maxArea, maxAreaRows, maxAreaRowLength, shortestRow] = [0, 0, 0, 0]
for rowLength, rowIndex in coveredRows for rowLength, rowIndex in coveredRows
shortestRow ||= rowLength shortestRow ||= rowLength
@ -143,7 +143,7 @@ module.exports.largestRectangle = largestRectangle = (grid, bottomY, leftX, want
maxAreaRowLength = shortestRow maxAreaRowLength = shortestRow
maxArea = area maxArea = area
shortestRow = Math.min(rowLength, shortestRow) shortestRow = Math.min(rowLength, shortestRow)
console.log "So largest rect has area", maxArea, "with", maxAreaRows, "rows of length", maxAreaRowLength if debug console.log 'So largest rect has area', maxArea, 'with', maxAreaRows, 'rows of length', maxAreaRowLength if debug
rect = new Rectangle leftX + maxAreaRowLength / 2, bottomY + maxAreaRows / 2, maxAreaRowLength, maxAreaRows rect = new Rectangle leftX + maxAreaRowLength / 2, bottomY + maxAreaRows / 2, maxAreaRowLength, maxAreaRows
console.log "That corresponds to a rectangle", rect.toString() if debug console.log 'That corresponds to a rectangle', rect.toString() if debug
rect rect

View file

@ -101,40 +101,40 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge
multiplayer: "Multiplayer" multiplayer: "Multiplayer"
for_developers: "Für Entwickler" for_developers: "Für Entwickler"
# play: play:
# choose_your_level: "Choose Your Level" choose_your_level: "Wähl dis Level us"
# adventurer_prefix: "You can jump to any level below, or discuss the levels on " adventurer_prefix: "Du chasch zu de untere Level zrugg goh oder die kommende Level diskutiere "
# adventurer_forum: "the Adventurer forum" adventurer_forum: "s Abentürer-Forum"
# adventurer_suffix: "." # adventurer_suffix: "."
# campaign_beginner: "Beginner Campaign" campaign_beginner: "Afängerkampagne"
# campaign_beginner_description: "... in which you learn the wizardry of programming." campaign_beginner_description: "... i dere du d Zauberkunst vom Programmiere lernsch."
# campaign_dev: "Random Harder Levels" campaign_dev: "Zuefälligi schwierigeri Level"
# campaign_dev_description: "... in which you learn the interface while doing something a little harder." campaign_dev_description: "... i dene du s Interface kenne lernsch, während du öppis chli Schwierigers machsch."
# campaign_multiplayer: "Multiplayer Arenas" campaign_multiplayer: "Multiplayer Arenas"
# campaign_multiplayer_description: "... in which you code head-to-head against other players." campaign_multiplayer_description: "... i dene du Chopf a Chopf geg anderi Spieler spielsch."
# campaign_player_created: "Player-Created" # campaign_player_created: "Player-Created"
# campaign_player_created_description: "... in which you battle against the creativity of your fellow <a href=\"/contribute#artisan\">Artisan Wizards</a>." # campaign_player_created_description: "... in which you battle against the creativity of your fellow <a href=\"/contribute#artisan\">Artisan Wizards</a>."
# level_difficulty: "Difficulty: " level_difficulty: "Schwierigkeit: "
# play_as: "Play As" play_as: "Spiel als"
# spectate: "Spectate" spectate: "Zueluege"
# contact: contact:
# contact_us: "Contact CodeCombat" contact_us: "CodeCombat kontaktiere"
# welcome: "Good to hear from you! Use this form to send us email. " welcome: "Mir ghöred gern vo dir! Benutz das Formular zum üs e E-Mail schicke."
# contribute_prefix: "If you're interested in contributing, check out our " contribute_prefix: "Wenn du dra interessiert bisch, mitzhelfe denn lueg doch mol verbii uf üsere"
# contribute_page: "contribute page" contribute_page: "Contribute Page"
# contribute_suffix: "!" # contribute_suffix: "!"
# forum_prefix: "For anything public, please try " forum_prefix: "Für öffentlichi Sache versuechs mol bi"
# forum_page: "our forum" forum_page: "üsem Forum"
# forum_suffix: " instead." forum_suffix: " stattdesse."
# send: "Send Feedback" send: "Feedback schicke"
# contact_candidate: "Contact Candidate" # contact_candidate: "Contact Candidate"
# recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns." # recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns."
diplomat_suggestion: diplomat_suggestion:
title: "Hilf, CodeCombat z übersetze!" title: "Hilf, CodeCombat z übersetze!"
sub_heading: "Mir bruuched dini Sprochfähigkeite." sub_heading: "Mir bruuched dini Sprochfähigkeite."
pitch_body: "We develop CodeCombat in English, but we already have players all over the world. Many of them want to play in Swiss German but don't speak English, so if you can speak both, please consider signing up to be a Diplomat and help translate both the CodeCombat website and all the levels into Swiss German." pitch_body: "Mir entwickled CodeCombat in Englisch, aber mir hend scho Spieler uf de ganze Welt. Vieli devo würed gern uf Schwiizerdütsch spiele, aber chönd kei Englisch. Wenn du beides chasch, denk doch mol drüber noh, dich bi üs als Diplomat izträge und z helfe, d CodeCombat Websiite und alli Level uf Schwiizerdütsch z übersetze."
missing_translations: "Until we can translate everything into Swiss German, you'll see generic German or English when Swiss German isn't available." missing_translations: "Until we can translate everything into Swiss German, you'll see generic German or English when Swiss German isn't available."
learn_more: "Lern meh drüber, en Diplomat zsii" learn_more: "Lern meh drüber, en Diplomat zsii"
subscribe_as_diplomat: "Abonnier als en Diplomat" subscribe_as_diplomat: "Abonnier als en Diplomat"
@ -142,18 +142,18 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge
wizard_settings: wizard_settings:
title: "Zaubereristellige" title: "Zaubereristellige"
customize_avatar: "Pass din Avatar ah" customize_avatar: "Pass din Avatar ah"
# active: "Active" active: "Aktiv"
color: "Farb" color: "Farb"
group: "Gruppe" group: "Gruppe"
clothes: "Chleider" clothes: "Chleider"
# trim: "Trim" trim: "Zueschniide"
cloud: "Wolke" cloud: "Wolke"
team: "Team" team: "Team"
spell: "Zauberspruch" spell: "Zauberspruch"
boots: "Stiefel" boots: "Stiefel"
# hue: "Hue" hue: "Färbig"
saturation: "Sättigung" saturation: "Sättigung"
# lightness: "Lightness" lightness: "Helligkeit"
account_settings: account_settings:
title: "Account Istellige" title: "Account Istellige"
@ -166,16 +166,16 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge
password_tab: "Passwort" password_tab: "Passwort"
emails_tab: "E-Mails" emails_tab: "E-Mails"
admin: "Admin" admin: "Admin"
# wizard_color: "Wizard Clothes Color" wizard_color: "Zaubererchleid Farb"
new_password: "Neus Passwort" new_password: "Neus Passwort"
new_password_verify: "Bestätige" new_password_verify: "Bestätige"
email_subscriptions: "E-Mail Abos" email_subscriptions: "E-Mail Abos"
email_announcements: "Akündigunge" email_announcements: "Akündigunge"
email_announcements_description: "Bechum Mails mit Neuigkeite und de neuste Entwicklige bi CodeCombat." email_announcements_description: "Bechum Mails mit Neuigkeite und de neuste Entwicklige bi CodeCombat."
email_notifications: "Benachrichtigunge" email_notifications: "Benachrichtigunge"
# email_notifications_summary: "Controls for personalized, automatic email notifications related to your CodeCombat activity." email_notifications_summary: "Istellige für personalisierti, automatischi E-Mail Notifikatione im Zemehang mit dine CodeCombat Aktivitäte"
# email_any_notes: "Any Notifications" email_any_notes: "Alli Notifikatione"
# email_any_notes_description: "Disable to stop all activity notification emails." email_any_notes_description: "Deaktiviere zum kei Aktivitäts-Notifikatione meh per E-Mail becho."
# email_recruit_notes: "Job Opportunities" # email_recruit_notes: "Job Opportunities"
# email_recruit_notes_description: "If you play really well, we may contact you about getting you a (better) job." # email_recruit_notes_description: "If you play really well, we may contact you about getting you a (better) job."
# contributor_emails: "Contributor Class Emails" # contributor_emails: "Contributor Class Emails"
@ -192,36 +192,35 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge
# sample_profile: "See a sample profile" # sample_profile: "See a sample profile"
# view_profile: "View Your Profile" # view_profile: "View Your Profile"
# account_profile: account_profile:
# settings: "Settings" settings: "Istellige"
# edit_profile: "Edit Profile" edit_profile: "Profil bearbeite"
# done_editing: "Done Editing" done_editing: "Fertig mit bearbeite"
# profile_for_prefix: "Profile for " profile_for_prefix: "Profilr "
# profile_for_suffix: "" # profile_for_suffix: ""
# featured: "Featured" # featured: "Featured"
# not_featured: "Not Featured" # not_featured: "Not Featured"
# looking_for: "Looking for:" # looking_for: "Looking for:"
# last_updated: "Last updated:" last_updated: "S letzte Update:"
# contact: "Contact" contact: "Kontakt"
# active: "Looking for interview offers now" # active: "Looking for interview offers now"
# inactive: "Not looking for offers right now" # inactive: "Not looking for offers right now"
# complete: "complete" complete: "komplett"
# next: "Next" next: "Nögst"
# next_city: "city?" next_city: "Stadt?"
# next_country: "pick your country." next_country: "wähl dis Land."
# next_name: "name?" next_name: "Name?"
# next_short_description: "write a short description." next_short_description: "schriibe e churzi Beschriibig."
# next_long_description: "describe your desired position." next_long_description: "beschriib dini Wunschstell."
# next_skills: "list at least five skills."
# next_work: "chronicle your work history." # next_work: "chronicle your work history."
# next_education: "recount your educational ordeals." # next_education: "recount your educational ordeals."
# next_projects: "show off up to three projects you've worked on." next_projects: "Zeig üs bis zu drü Projekt a dene du scho gschaffet hesch."
# next_links: "add any personal or social links." next_links: "füeg persönlichi oder Social Media Links ih."
# next_photo: "add an optional professional photo." # next_photo: "add an optional professional photo."
# next_active: "mark yourself open to offers to show up in searches." # next_active: "mark yourself open to offers to show up in searches."
# example_blog: "Blog" example_blog: "Blog"
# example_personal_site: "Personal Site" example_personal_site: "Eigeni Websiite"
# links_header: "Personal Links" links_header: "Eigeni Links"
# links_blurb: "Link any other sites or profiles you want to highlight, like your GitHub, your LinkedIn, or your blog." # links_blurb: "Link any other sites or profiles you want to highlight, like your GitHub, your LinkedIn, or your blog."
# links_name: "Link Name" # links_name: "Link Name"
# links_name_help: "What are you linking to?" # links_name_help: "What are you linking to?"
@ -324,39 +323,39 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge
# other_developers: "Other Developers" # other_developers: "Other Developers"
# inactive_developers: "Inactive Developers" # inactive_developers: "Inactive Developers"
# play_level: play_level:
# done: "Done" done: "Fertig"
# grid: "Grid" grid: "Gitter"
# customize_wizard: "Customize Wizard" customize_wizard: "Zauberer apasse"
# home: "Home" home: "Home"
# guide: "Guide" guide: "Aleitig"
# multiplayer: "Multiplayer" multiplayer: "Multiplayer"
# restart: "Restart" restart: "Neu starte"
# goals: "Goals" goals: "Ziel"
# success: "Success!" success: "Erfolg!"
# incomplete: "Incomplete" incomplete: "Unvollständig"
# timed_out: "Ran out of time" timed_out: "Ziit abglaufe"
# failing: "Failing" failing: "Fehler"
# action_timeline: "Action Timeline" action_timeline: "Aktionsziitleiste"
# click_to_select: "Click on a unit to select it." click_to_select: "Klick uf e Einheit zum sie uswähle."
# reload_title: "Reload All Code?" reload_title: "De ganze Code neu lade?"
# reload_really: "Are you sure you want to reload this level back to the beginning?" reload_really: "Bisch sicher du willsch level neu lade bis zrugg zum Afang?"
# reload_confirm: "Reload All" reload_confirm: "Alles neu lade"
# victory_title_prefix: "" # victory_title_prefix: ""
# victory_title_suffix: " Complete" victory_title_suffix: " Vollständig"
# victory_sign_up: "Sign Up to Save Progress" victory_sign_up: "Meld dich ah zum din Fortschritt speichere"
# victory_sign_up_poke: "Want to save your code? Create a free account!" victory_sign_up_poke: "Wötsch din Code speichere? Erstell gratis en Account!"
# victory_rate_the_level: "Rate the level: " victory_rate_the_level: "Bewerte das Level: "
# victory_return_to_ladder: "Return to Ladder" victory_return_to_ladder: "Zrugg zum letzte Level"
# victory_play_next_level: "Play Next Level" victory_play_next_level: "Spiel s nögste Level"
# victory_go_home: "Go Home" # victory_go_home: "Go Home"
# victory_review: "Tell us more!" victory_review: "Verzell üs meh!"
# victory_hour_of_code_done: "Are You Done?" victory_hour_of_code_done: "Bisch fertig?"
# victory_hour_of_code_done_yes: "Yes, I'm finished with my Hour of Code™!" victory_hour_of_code_done_yes: "Jo, ich bin fertig mit mim Hour of Code™!"
# multiplayer_title: "Multiplayer Settings" multiplayer_title: "Multiplayer Istellige"
# multiplayer_link_description: "Give this link to anyone to have them join you." multiplayer_link_description: "Gib de Link jedem, wo mit dir will spiele."
# multiplayer_hint_label: "Hint:" multiplayer_hint_label: "Hiiwis:"
# multiplayer_hint: " Click the link to select all, then press ⌘-C or Ctrl-C to copy the link." multiplayer_hint: " Klick uf de Link zum alles uswähle und druck ⌘-C or Ctrl-C zum de Link kopiere"
# multiplayer_coming_soon: "More multiplayer features to come!" # multiplayer_coming_soon: "More multiplayer features to come!"
# multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard." # multiplayer_sign_in_leaderboard: "Sign in or create an account and get your solution on the leaderboard."
# guide_title: "Guide" # guide_title: "Guide"

View file

@ -1,7 +1,7 @@
CocoModel = require('./CocoModel') CocoModel = require './CocoModel'
module.exports = class Article extends CocoModel module.exports = class Article extends CocoModel
@className: "Article" @className: 'Article'
@schema: require 'schemas/models/article' @schema: require 'schemas/models/article'
urlRoot: "/db/article" urlRoot: '/db/article'
saveBackups: true saveBackups: true

View file

@ -4,7 +4,7 @@ deltasLib = require 'lib/deltas'
NewAchievementCollection = require '../collections/NewAchievementCollection' NewAchievementCollection = require '../collections/NewAchievementCollection'
class CocoModel extends Backbone.Model class CocoModel extends Backbone.Model
idAttribute: "_id" idAttribute: '_id'
loaded: false loaded: false
loading: false loading: false
saveBackups: false saveBackups: false
@ -55,10 +55,12 @@ class CocoModel extends Backbone.Model
return unless @saveBackups return unless @saveBackups
existing = storage.load @id existing = storage.load @id
if existing if existing
@set(existing, {silent:true}) @set(existing, {silent: true})
CocoModel.backedUp[@id] = @ CocoModel.backedUp[@id] = @
saveBackup: -> saveBackup: -> @saveBackupNow()
saveBackupNow: ->
storage.save(@id, @attributes) storage.save(@id, @attributes)
CocoModel.backedUp[@id] = @ CocoModel.backedUp[@id] = @
@ -74,7 +76,7 @@ class CocoModel extends Backbone.Model
if errors?.length if errors?.length
console.debug "Validation failed for #{@constructor.className}: '#{@get('name') or @}'." console.debug "Validation failed for #{@constructor.className}: '#{@get('name') or @}'."
for error in errors for error in errors
console.debug "\t", error.dataPath, ":", error.message console.debug "\t", error.dataPath, ':', error.message
return errors return errors
save: (attrs, options) -> save: (attrs, options) ->
@ -84,7 +86,7 @@ class CocoModel extends Backbone.Model
success = options.success success = options.success
error = options.error error = options.error
options.success = (model, res) => options.success = (model, res) =>
@trigger "save:success", @ @trigger 'save:success', @
success(@, res) if success success(@, res) if success
@markToRevert() if @_revertAttributes @markToRevert() if @_revertAttributes
@clearBackup() @clearBackup()
@ -95,7 +97,7 @@ class CocoModel extends Backbone.Model
errorMessage = "Error saving #{@get('name') ? @type()}" errorMessage = "Error saving #{@get('name') ? @type()}"
console.error errorMessage, res.responseJSON console.error errorMessage, res.responseJSON
noty text: "#{errorMessage}: #{res.status} #{res.statusText}", layout: 'topCenter', type: 'error', killer: false, timeout: 10000 noty text: "#{errorMessage}: #{res.status} #{res.statusText}", layout: 'topCenter', type: 'error', killer: false, timeout: 10000
@trigger "save", @ @trigger 'save', @
return super attrs, options return super attrs, options
patch: (options) -> patch: (options) ->
@ -137,7 +139,6 @@ class CocoModel extends Backbone.Model
cloneNewMinorVersion: -> cloneNewMinorVersion: ->
newData = _.clone @attributes newData = _.clone @attributes
clone = new @constructor(newData) clone = new @constructor(newData)
clone clone
@ -152,20 +153,20 @@ class CocoModel extends Backbone.Model
false false
publish: -> publish: ->
if @isPublished() then throw new Error("Can't publish what's already-published. Can't kill what's already dead.") if @isPublished() then throw new Error('Can\'t publish what\'s already-published. Can\'t kill what\'s already dead.')
@set "permissions", (@get("permissions") or []).concat({access: 'read', target: 'public'}) @set 'permissions', (@get('permissions') or []).concat({access: 'read', target: 'public'})
addSchemaDefaults: -> addSchemaDefaults: ->
return if @addedSchemaDefaults return if @addedSchemaDefaults
@addedSchemaDefaults = true @addedSchemaDefaults = true
for prop, defaultValue of @constructor.schema.default or {} for prop, defaultValue of @constructor.schema.default or {}
continue if @get(prop)? continue if @get(prop)?
#console.log "setting", prop, "to", defaultValue, "from attributes.default" #console.log 'setting', prop, 'to', defaultValue, 'from attributes.default'
@set prop, defaultValue @set prop, defaultValue
for prop, sch of @constructor.schema.properties or {} for prop, sch of @constructor.schema.properties or {}
continue if @get(prop)? continue if @get(prop)?
continue if prop is 'emails' # hack, defaults are handled through User.coffee's email-specific methods. continue if prop is 'emails' # hack, defaults are handled through User.coffee's email-specific methods.
#console.log "setting", prop, "to", sch.default, "from sch.default" if sch.default? #console.log 'setting', prop, 'to', sch.default, 'from sch.default' if sch.default?
@set prop, sch.default if sch.default? @set prop, sch.default if sch.default?
if @loaded if @loaded
@loadFromBackup() @loadFromBackup()
@ -210,7 +211,7 @@ class CocoModel extends Backbone.Model
try try
jsondiffpatch.patch newAttributes, delta jsondiffpatch.patch newAttributes, delta
catch error catch error
console.error "Error applying delta", delta, "to attributes", newAttributes, error console.error 'Error applying delta\n', JSON.stringify(delta, null, '\t'), '\n\nto attributes\n\n', newAttributes
return false return false
@set newAttributes @set newAttributes
return true return true
@ -224,7 +225,7 @@ class CocoModel extends Backbone.Model
deltasLib.expandDelta(delta, @attributes, @schema()) deltasLib.expandDelta(delta, @attributes, @schema())
watch: (doWatch=true) -> watch: (doWatch=true) ->
$.ajax("#{@urlRoot}/#{@id}/watch", {type:'PUT', data:{on:doWatch}}) $.ajax("#{@urlRoot}/#{@id}/watch", {type: 'PUT', data: {on: doWatch}})
@watching = -> doWatch @watching = -> doWatch
watching: -> watching: ->
@ -254,9 +255,9 @@ class CocoModel extends Backbone.Model
@getReferencedModel: (data, schema) -> @getReferencedModel: (data, schema) ->
return null unless schema.links? return null unless schema.links?
linkObject = _.find schema.links, rel: "db" linkObject = _.find schema.links, rel: 'db'
return null unless linkObject return null unless linkObject
return null if linkObject.href.match("thang.type") and not @isObjectID(data) # Skip loading hardcoded Thang Types for now (TODO) return null if linkObject.href.match('thang.type') and not @isObjectID(data) # Skip loading hardcoded Thang Types for now (TODO)
# not fully extensible, but we can worry about that later # not fully extensible, but we can worry about that later
link = linkObject.href link = linkObject.href
@ -296,7 +297,6 @@ class CocoModel extends Backbone.Model
me.fetch (success: -> Backbone.Mediator.publish('achievements:new', collection)) unless _.isEmpty(collection.models) me.fetch (success: -> Backbone.Mediator.publish('achievements:new', collection)) unless _.isEmpty(collection.models)
) )
CocoModel.pollAchievements = _.debounce CocoModel.pollAchievements, 500 CocoModel.pollAchievements = _.debounce CocoModel.pollAchievements, 500
module.exports = CocoModel module.exports = CocoModel

View file

@ -1,6 +1,6 @@
CocoModel = require('./CocoModel') CocoModel = require './CocoModel'
module.exports = class File extends CocoModel module.exports = class File extends CocoModel
@className: "File" @className: 'File'
@schema: {} @schema: {}
urlRoot: "/db/file" urlRoot: '/db/file'

View file

@ -4,10 +4,10 @@ LevelSystem = require './LevelSystem'
ThangType = require './ThangType' ThangType = require './ThangType'
module.exports = class Level extends CocoModel module.exports = class Level extends CocoModel
@className: "Level" @className: 'Level'
@schema: require 'schemas/models/level' @schema: require 'schemas/models/level'
urlRoot: "/db/level" urlRoot: '/db/level'
serialize: (supermodel) -> serialize: (supermodel) ->
# o = _.cloneDeep @attributes # slow in level editor when there are hundreds of Thangs # o = _.cloneDeep @attributes # slow in level editor when there are hundreds of Thangs
o = $.extend true, {}, @attributes o = $.extend true, {}, @attributes
@ -23,7 +23,6 @@ module.exports = class Level extends CocoModel
@fillInDefaultSystemConfiguration o.systems @fillInDefaultSystemConfiguration o.systems
o.thangTypes = (original: tt.get('original'), name: tt.get('name') for tt in supermodel.getModels ThangType) o.thangTypes = (original: tt.get('original'), name: tt.get('name') for tt in supermodel.getModels ThangType)
o o
sortSystems: (levelSystems, systemModels) -> sortSystems: (levelSystems, systemModels) ->
@ -31,11 +30,11 @@ module.exports = class Level extends CocoModel
visit = (system) -> visit = (system) ->
return if system.original of originalsSeen return if system.original of originalsSeen
systemModel = _.find systemModels, {original: system.original} systemModel = _.find systemModels, {original: system.original}
console.error "Couldn't find model for original", system.original, "from", systemModels unless systemModel console.error 'Couldn\'t find model for original', system.original, 'from', systemModels unless systemModel
for d in systemModel.dependencies or [] for d in systemModel.dependencies or []
system2 = _.find levelSystems, {original: d.original} system2 = _.find levelSystems, {original: d.original}
visit system2 visit system2
#console.log "sorted systems adding", systemModel.name #console.log 'sorted systems adding', systemModel.name
sorted.push {model: systemModel, config: _.cloneDeep system.config} sorted.push {model: systemModel, config: _.cloneDeep system.config}
originalsSeen[system.original] = true originalsSeen[system.original] = true
visit system for system in levelSystems visit system for system in levelSystems
@ -54,21 +53,21 @@ module.exports = class Level extends CocoModel
visit = (c) -> visit = (c) ->
return if c in sorted return if c in sorted
lc = _.find levelComponents, {original: c.original} lc = _.find levelComponents, {original: c.original}
console.error thang.id, "couldn't find lc for", c unless lc console.error thang.id, 'couldn\'t find lc for', c unless lc
if lc.name is "Programmable" if lc.name is 'Programmable'
# Programmable always comes last # Programmable always comes last
visit c2 for c2 in _.without thang.components, c visit c2 for c2 in _.without thang.components, c
else else
for d in lc.dependencies or [] for d in lc.dependencies or []
c2 = _.find thang.components, {original: d.original} c2 = _.find thang.components, {original: d.original}
console.error thang.id, "couldn't find dependent Component", d.original, "from", lc.name unless c2 console.error thang.id, 'couldn\'t find dependent Component', d.original, 'from', lc.name unless c2
visit c2 visit c2
if lc.name is "Collides" if lc.name is 'Collides'
allied = _.find levelComponents, {name: "Allied"} allied = _.find levelComponents, {name: 'Allied'}
if allied if allied
collides = _.find(thang.components, {original: allied.original}) collides = _.find(thang.components, {original: allied.original})
visit collides if collides visit collides if collides
#console.log thang.id, "sorted comps adding", lc.name #console.log thang.id, 'sorted comps adding', lc.name
sorted.push c sorted.push c
for comp in thang.components for comp in thang.components
visit comp visit comp
@ -90,7 +89,7 @@ module.exports = class Level extends CocoModel
return unless properties return unless properties
for prop, schema of properties for prop, schema of properties
if schema.default? and config[prop] is undefined if schema.default? and config[prop] is undefined
#console.log "Setting default of", config, "for", prop, "to", schema.default #console.log 'Setting default of', config, 'for', prop, 'to', schema.default
config[prop] = schema.default config[prop] = schema.default
if schema.type is 'object' and config[prop] if schema.type is 'object' and config[prop]
@walkDefaults config[prop], schema.properties @walkDefaults config[prop], schema.properties
@ -107,4 +106,4 @@ module.exports = class Level extends CocoModel
continue unless c? continue unless c?
width = c.width if c.width? and c.width > width width = c.width if c.width? and c.width > width
height = c.height if c.height? and c.height > height height = c.height if c.height? and c.height > height
return {width:width, height:height} return {width: width, height: height}

View file

@ -1,9 +1,9 @@
CocoModel = require('./CocoModel') CocoModel = require './CocoModel'
module.exports = class LevelComponent extends CocoModel module.exports = class LevelComponent extends CocoModel
@className: "LevelComponent" @className: 'LevelComponent'
@schema: require 'schemas/models/level_component' @schema: require 'schemas/models/level_component'
urlRoot: "/db/level.component" urlRoot: '/db/level.component'
set: (key, val, options) -> set: (key, val, options) ->
if _.isObject key if _.isObject key
@ -20,11 +20,11 @@ module.exports = class LevelComponent extends CocoModel
compile: (code) -> compile: (code) ->
if @get('codeLanguage') and @get('codeLanguage') isnt 'coffeescript' if @get('codeLanguage') and @get('codeLanguage') isnt 'coffeescript'
return console.error("Can't compile", @get('codeLanguage'), "-- only CoffeeScript.", @) return console.error('Can\'t compile', @get('codeLanguage'), '-- only CoffeeScript.', @)
try try
js = CoffeeScript.compile(code, bare: true) js = CoffeeScript.compile(code, bare: true)
catch e catch e
#console.log "couldn't compile", code, "for", @get('name'), "because", e #console.log 'couldn\'t compile', code, 'for', @get('name'), 'because', e
js = @get 'js' js = @get 'js'
js js

View file

@ -1,6 +1,6 @@
CocoModel = require('./CocoModel') CocoModel = require './CocoModel'
module.exports = class LevelFeedback extends CocoModel module.exports = class LevelFeedback extends CocoModel
@className: "LevelFeedback" @className: 'LevelFeedback'
@schema: require 'schemas/models/level_feedback' @schema: require 'schemas/models/level_feedback'
urlRoot: "/db/level.feedback" urlRoot: '/db/level.feedback'

View file

@ -1,9 +1,9 @@
CocoModel = require('./CocoModel') CocoModel = require './CocoModel'
module.exports = class LevelSession extends CocoModel module.exports = class LevelSession extends CocoModel
@className: "LevelSession" @className: 'LevelSession'
@schema: require 'schemas/models/level_session' @schema: require 'schemas/models/level_session'
urlRoot: "/db/level.session" urlRoot: '/db/level.session'
initialize: -> initialize: ->
super() super()
@ -16,7 +16,7 @@ module.exports = class LevelSession extends CocoModel
permissions = @get 'permissions' permissions = @get 'permissions'
permissions = (p for p in permissions when p.target isnt 'public') permissions = (p for p in permissions when p.target isnt 'public')
if @get('multiplayer') if @get('multiplayer')
permissions.push {target:'public', access:'write'} permissions.push {target: 'public', access: 'write'}
@set 'permissions', permissions @set 'permissions', permissions
getSourceFor: (spellKey) -> getSourceFor: (spellKey) ->
@ -30,7 +30,7 @@ module.exports = class LevelSession extends CocoModel
return false unless c1 = @get('code') return false unless c1 = @get('code')
return false unless team = @get('team') return false unless team = @get('team')
return true unless c2 = @get('submittedCode') return true unless c2 = @get('submittedCode')
thangSpellArr = (s.split("/") for s in @get('teamSpells')[team]) thangSpellArr = (s.split('/') for s in @get('teamSpells')[team])
for item in thangSpellArr for item in thangSpellArr
thang = item[0] thang = item[0]
spell = item[1] spell = item[1]

View file

@ -1,10 +1,10 @@
CocoModel = require('./CocoModel') CocoModel = require './CocoModel'
SystemNameLoader = require('lib/SystemNameLoader') SystemNameLoader = require 'lib/SystemNameLoader'
module.exports = class LevelSystem extends CocoModel module.exports = class LevelSystem extends CocoModel
@className: "LevelSystem" @className: 'LevelSystem'
@schema: require 'schemas/models/level_system' @schema: require 'schemas/models/level_system'
urlRoot: "/db/level.system" urlRoot: '/db/level.system'
set: (key, val, options) -> set: (key, val, options) ->
if _.isObject key if _.isObject key
@ -22,11 +22,11 @@ module.exports = class LevelSystem extends CocoModel
compile: (code) -> compile: (code) ->
if @get('codeLanguage') and @get('codeLanguage') isnt 'coffeescript' if @get('codeLanguage') and @get('codeLanguage') isnt 'coffeescript'
return console.error("Can't compile", @get('codeLanguage'), "-- only CoffeeScript.", @) return console.error('Can\'t compile', @get('codeLanguage'), '-- only CoffeeScript.', @)
try try
js = CoffeeScript.compile(code, bare: true) js = CoffeeScript.compile(code, bare: true)
catch e catch e
#console.log "couldn't compile", code, "for", @get('name'), "because", e #console.log 'couldn\'t compile', code, 'for', @get('name'), 'because', e
js = @get 'js' js = @get 'js'
js js

View file

@ -1,12 +1,12 @@
CocoModel = require('./CocoModel') CocoModel = require './CocoModel'
module.exports = class PatchModel extends CocoModel module.exports = class PatchModel extends CocoModel
@className: "Patch" @className: 'Patch'
@schema: require 'schemas/models/patch' @schema: require 'schemas/models/patch'
urlRoot: "/db/patch" urlRoot: '/db/patch'
setStatus: (status) -> setStatus: (status) ->
PatchModel.setStatus @id, status PatchModel.setStatus @id, status
@setStatus: (id, status) -> @setStatus: (id, status) ->
$.ajax("/db/patch/#{id}/status", {type:"PUT", data: {status:status}}) $.ajax("/db/patch/#{id}/status", {type: 'PUT', data: {status: status}})

View file

@ -19,11 +19,11 @@ module.exports = class SuperModel extends Backbone.Model
report: -> report: ->
# Useful for debugging why a SuperModel never finishes loading. # Useful for debugging why a SuperModel never finishes loading.
console.info "SuperModel report ------------------------" console.info 'SuperModel report ------------------------'
console.info "#{_.values(@resources).length} resources." console.info "#{_.values(@resources).length} resources."
unfinished = [] unfinished = []
for resource in _.values(@resources) when resource for resource in _.values(@resources) when resource
console.info '\t', resource.name, "loaded", resource.isLoaded console.info "\t", resource.name, 'loaded', resource.isLoaded
unfinished.push resource unless resource.isLoaded unfinished.push resource unless resource.isLoaded
unfinished unfinished
@ -38,7 +38,6 @@ module.exports = class SuperModel extends Backbone.Model
res = @addModelResource(cachedModel, name, fetchOptions, value) res = @addModelResource(cachedModel, name, fetchOptions, value)
res.markLoading() res.markLoading()
return res return res
else else
@registerModel(model) @registerModel(model)
res = @addModelResource(model, name, fetchOptions, value) res = @addModelResource(model, name, fetchOptions, value)
@ -57,7 +56,6 @@ module.exports = class SuperModel extends Backbone.Model
res = @addModelResource(cachedCollection, name, fetchOptions, value) res = @addModelResource(cachedCollection, name, fetchOptions, value)
res.markLoading() res.markLoading()
return res return res
else else
@addCollection collection @addCollection collection
@listenToOnce collection, 'sync', (c) -> @listenToOnce collection, 'sync', (c) ->
@ -196,8 +194,6 @@ module.exports = class SuperModel extends Backbone.Model
getResource: (rid) -> getResource: (rid) ->
return @resources[rid] return @resources[rid]
class Resource extends Backbone.Model class Resource extends Backbone.Model
constructor: (name, value=1) -> constructor: (name, value=1) ->
@name = name @name = name
@ -230,8 +226,6 @@ class Resource extends Backbone.Model
load: -> @ load: -> @
class ModelResource extends Resource class ModelResource extends Resource
constructor: (modelOrCollection, name, fetchOptions, value)-> constructor: (modelOrCollection, name, fetchOptions, value)->
super(name, value) super(name, value)
@ -253,7 +247,6 @@ class ModelResource extends Resource
@jqxhr = null @jqxhr = null
@model.jqxhr = null @model.jqxhr = null
class RequestResource extends Resource class RequestResource extends Resource
constructor: (name, jqxhrOptions, value) -> constructor: (name, jqxhrOptions, value) ->
super(name, value) super(name, value)
@ -267,6 +260,4 @@ class RequestResource extends Resource
@jqxhr.fail => _.defer => @markFailed() @jqxhr.fail => _.defer => @markFailed()
@ @
class SomethingResource extends Resource class SomethingResource extends Resource

View file

@ -1,12 +1,12 @@
CocoModel = require('./CocoModel') CocoModel = require './CocoModel'
SpriteBuilder = require 'lib/sprites/SpriteBuilder' SpriteBuilder = require 'lib/sprites/SpriteBuilder'
buildQueue = [] buildQueue = []
module.exports = class ThangType extends CocoModel module.exports = class ThangType extends CocoModel
@className: "ThangType" @className: 'ThangType'
@schema: require 'schemas/models/thang_type' @schema: require 'schemas/models/thang_type'
urlRoot: "/db/thang.type" urlRoot: '/db/thang.type'
building: {} building: {}
initialize: -> initialize: ->
@ -27,7 +27,7 @@ module.exports = class ThangType extends CocoModel
@resetRawData() unless @get('raw') @resetRawData() unless @get('raw')
resetRawData: -> resetRawData: ->
@set('raw', {shapes:{}, containers:{}, animations:{}}) @set('raw', {shapes: {}, containers: {}, animations: {}})
resetSpriteSheetCache: -> resetSpriteSheetCache: ->
@buildActions() @buildActions()
@ -48,7 +48,7 @@ module.exports = class ThangType extends CocoModel
for name, action of @actions for name, action of @actions
action.name = name action.name = name
for relatedName, relatedAction of action.relatedActions ? {} for relatedName, relatedAction of action.relatedActions ? {}
relatedAction.name = action.name + "_" + relatedName relatedAction.name = action.name + '_' + relatedName
@actions[relatedAction.name] = relatedAction @actions[relatedAction.name] = relatedAction
@actions @actions
@ -114,12 +114,12 @@ module.exports = class ThangType extends CocoModel
mc = @vectorParser.buildMovieClip name mc = @vectorParser.buildMovieClip name
continue unless mc continue unless mc
@builder.addMovieClip mc, null, animation.scale * @options.resolutionFactor @builder.addMovieClip mc, null, animation.scale * @options.resolutionFactor
framesMap[animation.scale + "_" + name] = @builder._animations[name].frames framesMap[animation.scale + '_' + name] = @builder._animations[name].frames
for name, action of @actions when action.animation for name, action of @actions when action.animation
continue if name is 'portrait' continue if name is 'portrait'
scale = action.scale ? @get('scale') ? 1 scale = action.scale ? @get('scale') ? 1
frames = framesMap[scale + "_" + action.animation] frames = framesMap[scale + '_' + action.animation]
continue unless frames continue unless frames
frames = @mapFrames(action.frames, frames[0]) if action.frames? frames = @mapFrames(action.frames, frames[0]) if action.frames?
next = true next = true
@ -179,7 +179,7 @@ module.exports = class ThangType extends CocoModel
buildQueue[0]?.buildAsync() buildQueue[0]?.buildAsync()
@spriteSheets[key] = e.target.spriteSheet @spriteSheets[key] = e.target.spriteSheet
@building[key] = false @building[key] = false
@trigger 'build-complete', {key:key, thangType:@} @trigger 'build-complete', {key: key, thangType: @}
@vectorParser = null @vectorParser = null
logBuild: (startTime, async, portrait) -> logBuild: (startTime, async, portrait) ->
@ -249,14 +249,14 @@ module.exports = class ThangType extends CocoModel
path: "db/thang.type/#{@get('original')}" path: "db/thang.type/#{@get('original')}"
b64png: src b64png: src
force: 'true' force: 'true'
$.ajax('/file', { type: 'POST', data: body, success: callback or @onFileUploaded }) $.ajax('/file', {type: 'POST', data: body, success: callback or @onFileUploaded})
onFileUploaded: => onFileUploaded: =>
console.log 'Image uploaded' console.log 'Image uploaded'
@loadUniversalWizard: -> @loadUniversalWizard: ->
return @wizardType if @wizardType return @wizardType if @wizardType
wizOriginal = "52a00d55cf1818f2be00000b" wizOriginal = '52a00d55cf1818f2be00000b'
url = "/db/thang.type/#{wizOriginal}/version" url = "/db/thang.type/#{wizOriginal}/version"
@wizardType = new module.exports() @wizardType = new module.exports()
@wizardType.url = -> url @wizardType.url = -> url

View file

@ -1,11 +1,11 @@
GRAVATAR_URL = 'https://www.gravatar.com/' GRAVATAR_URL = 'https://www.gravatar.com/'
cache = {} cache = {}
CocoModel = require('./CocoModel') CocoModel = require './CocoModel'
module.exports = class User extends CocoModel module.exports = class User extends CocoModel
@className: "User" @className: 'User'
@schema: require 'schemas/models/user' @schema: require 'schemas/models/user'
urlRoot: "/db/user" urlRoot: '/db/user'
initialize: -> initialize: ->
super() super()
@ -16,24 +16,24 @@ module.exports = class User extends CocoModel
return 'admin' in permissions return 'admin' in permissions
displayName: -> displayName: ->
@get('name') or "Anoner" @get('name') or 'Anoner'
lang: -> lang: ->
@get('preferredLanguage') or "en-US" @get('preferredLanguage') or 'en-US'
getPhotoURL: (size=80, useJobProfilePhoto=false) -> getPhotoURL: (size=80, useJobProfilePhoto=false) ->
photoURL = if useJobProfilePhoto then @get('jobProfile')?.photoURL else null photoURL = if useJobProfilePhoto then @get('jobProfile')?.photoURL else null
photoURL ||= @get('photoURL') photoURL ||= @get('photoURL')
if photoURL if photoURL
prefix = if photoURL.search(/\?/) is -1 then "?" else "&" prefix = if photoURL.search(/\?/) is -1 then '?' else '&'
return "#{photoURL}#{prefix}s=#{size}" if photoURL.search('http') isnt -1 # legacy return "#{photoURL}#{prefix}s=#{size}" if photoURL.search('http') isnt -1 # legacy
return "/file/#{photoURL}#{prefix}s=#{size}" return "/file/#{photoURL}#{prefix}s=#{size}"
return "/db/user/#{@id}/avatar?s=#{size}" return "/db/user/#{@id}/avatar?s=#{size}"
@getByID = (id, properties, force) -> @getByID = (id, properties, force) ->
{me} = require('lib/auth') {me} = require 'lib/auth'
return me if me.id is id return me if me.id is id
user = cache[id] or new module.exports({_id:id}) user = cache[id] or new module.exports({_id: id})
if force or not cache[id] if force or not cache[id]
user.loading = true user.loading = true
user.fetch( user.fetch(
@ -44,18 +44,18 @@ module.exports = class User extends CocoModel
) )
cache[id] = user cache[id] = user
user user
getEnabledEmails: -> getEnabledEmails: ->
@migrateEmails() @migrateEmails()
emails = _.clone(@get('emails')) or {} emails = _.clone(@get('emails')) or {}
emails = _.defaults emails, @schema().properties.emails.default emails = _.defaults emails, @schema().properties.emails.default
(emailName for emailName, emailDoc of emails when emailDoc.enabled) (emailName for emailName, emailDoc of emails when emailDoc.enabled)
setEmailSubscription: (name, enabled) -> setEmailSubscription: (name, enabled) ->
newSubs = _.clone(@get('emails')) or {} newSubs = _.clone(@get('emails')) or {}
(newSubs[name] ?= {}).enabled = enabled (newSubs[name] ?= {}).enabled = enabled
@set 'emails', newSubs @set 'emails', newSubs
emailMap: emailMap:
announcement: 'generalNews' announcement: 'generalNews'
developer: 'archmageNews' developer: 'archmageNews'
@ -70,9 +70,9 @@ module.exports = class User extends CocoModel
return if @attributes.emails or not @attributes.emailSubscriptions return if @attributes.emails or not @attributes.emailSubscriptions
oldSubs = @get('emailSubscriptions') or [] oldSubs = @get('emailSubscriptions') or []
newSubs = {} newSubs = {}
newSubs[newSubName] = { enabled: oldSubName in oldSubs } for oldSubName, newSubName of @emailMap newSubs[newSubName] = {enabled: oldSubName in oldSubs} for oldSubName, newSubName of @emailMap
@set('emails', newSubs) @set('emails', newSubs)
isEmailSubscriptionEnabled: (name) -> (@get('emails') or {})[name]?.enabled isEmailSubscriptionEnabled: (name) -> (@get('emails') or {})[name]?.enabled
a = 5 a = 5

View file

@ -1,6 +1,6 @@
CocoModel = require('./CocoModel') CocoModel = require './CocoModel'
module.exports = class UserRemark extends CocoModel module.exports = class UserRemark extends CocoModel
@className: "UserRemark" @className: 'UserRemark'
@schema: require 'schemas/models/user_remark' @schema: require 'schemas/models/user_remark'
urlRoot: "/db/user.remark" urlRoot: '/db/user.remark'

View file

@ -1,14 +1,14 @@
module.exports = module.exports =
bus: bus:
title: "Bus" title: 'Bus'
id: "bus" id: 'bus'
$schema: "http://json-schema.org/draft-04/schema#" $schema: 'http://json-schema.org/draft-04/schema#'
description: "Bus" # TODO description: 'Bus' # TODO
type: "object" type: 'object'
properties: # TODO properties: # TODO
joined: joined:
type: ["boolean", "null"] type: ['boolean', 'null']
players: players:
type: "object" type: 'object'
required: ["joined", "players"] required: ['joined', 'players']
additionalProperties: true additionalProperties: true

View file

@ -1,12 +1,12 @@
module.exports = module.exports =
jQueryEvent: jQueryEvent:
title: "jQuery Event" title: 'jQuery Event'
id: "jQueryEvent" id: 'jQueryEvent'
$schema: "http://json-schema.org/draft-04/schema#" $schema: 'http://json-schema.org/draft-04/schema#'
description: "A standard jQuery Event" description: 'A standard jQuery Event'
type: "object" type: 'object'
properties: # TODO schema complete properties: # TODO schema complete
altKey: altKey:
type: "boolean" type: 'boolean'
required: [] required: []
additionalProperties: true additionalProperties: true

View file

@ -3,26 +3,24 @@ c = require './schemas'
languageCodeArrayRegex = c.generateLanguageCodeArrayRegex() languageCodeArrayRegex = c.generateLanguageCodeArrayRegex()
ExampleSchema = { ExampleSchema = {
title: "Example Schema", title: 'Example Schema',
description:"An example schema", description: 'An example schema',
type: "object", type: 'object',
properties: { properties: {
text: { text: {
title: "Text", title: 'Text',
description: "A short message to display in the dialogue area. Markdown okay.", description: 'A short message to display in the dialogue area. Markdown okay.',
type: "string", type: 'string',
maxLength: 400 maxLength: 400
}, },
i18n: {"$ref": "#/definitions/i18n"} i18n: {'$ref': '#/definitions/i18n'}
}, },
definitions: { definitions: {
i18n: { i18n: {
title: "i18n", title: 'i18n',
description: "The internationalization object", description: 'The internationalization object',
type: "object", type: 'object',
patternProperties: { patternProperties: {
languageCodeArrayRegex: { languageCodeArrayRegex: {
additionalProperties: false, additionalProperties: false,
@ -30,19 +28,18 @@ ExampleSchema = {
#put the translatable properties here #put the translatable properties here
#if it is possible to not include i18n with a reference #if it is possible to not include i18n with a reference
# to #/properties, you could just do # to #/properties, you could just do
properties: {"$ref":"#/properties"} properties: {'$ref': '#/properties'}
# text: {"$ref": "#/properties/text"} # text: {'$ref': '#/properties/text'}
} }
default: { default: {
title: "LanguageCode", title: 'LanguageCode',
description: "LanguageDescription" description: 'LanguageDescription'
} }
} }
} }
} }
}, }
} }
#define a i18n object type for each schema, then have the i18n have it's oneOf check against #define a i18n object type for each schema, then have the i18n have it's oneOf check against
#translatable schemas of that object #translatable schemas of that object

View file

@ -1,132 +1,132 @@
# The JSON Schema Core/Validation Meta-Schema, but with titles and descriptions added to make it easier to edit in Treema, and in CoffeeScript # The JSON Schema Core/Validation Meta-Schema, but with titles and descriptions added to make it easier to edit in Treema, and in CoffeeScript
module.exports = module.exports =
id: "metaschema" id: 'metaschema'
displayProperty: "title" displayProperty: 'title'
$schema: "http://json-schema.org/draft-04/schema#" $schema: 'http://json-schema.org/draft-04/schema#'
title: "Schema" title: 'Schema'
description: "Core schema meta-schema" description: 'Core schema meta-schema'
definitions: definitions:
schemaArray: schemaArray:
type: "array" type: 'array'
minItems: 1 minItems: 1
items: { $ref: "#" } items: {$ref: '#'}
title: "Array of Schemas" title: 'Array of Schemas'
"default": [{}] 'default': [{}]
positiveInteger: positiveInteger:
type: "integer" type: 'integer'
minimum: 0 minimum: 0
title: "Positive Integer" title: 'Positive Integer'
positiveIntegerDefault0: positiveIntegerDefault0:
allOf: [ { $ref: "#/definitions/positiveInteger" }, { "default": 0 } ] allOf: [{$ref: '#/definitions/positiveInteger'}, {'default': 0}]
simpleTypes: simpleTypes:
title: "Single Type" title: 'Single Type'
"enum": [ "array", "boolean", "integer", "null", "number", "object", "string" ] 'enum': ['array', 'boolean', 'integer', 'null', 'number', 'object', 'string']
stringArray: stringArray:
type: "array" type: 'array'
items: { type: "string" } items: {type: 'string'}
minItems: 1 minItems: 1
uniqueItems: true uniqueItems: true
title: "String Array" title: 'String Array'
"default": [''] 'default': ['']
type: "object" type: 'object'
properties: properties:
id: id:
type: "string" type: 'string'
format: "uri" format: 'uri'
$schema: $schema:
type: "string" type: 'string'
format: "uri" format: 'uri'
"default": "http://json-schema.org/draft-04/schema#" 'default': 'http://json-schema.org/draft-04/schema#'
title: title:
type: "string" type: 'string'
description: description:
type: "string" type: 'string'
"default": {} 'default': {}
multipleOf: multipleOf:
type: "number" type: 'number'
minimum: 0 minimum: 0
exclusiveMinimum: true exclusiveMinimum: true
maximum: maximum:
type: "number" type: 'number'
exclusiveMaximum: exclusiveMaximum:
type: "boolean" type: 'boolean'
"default": false 'default': false
minimum: minimum:
type: "number" type: 'number'
exclusiveMinimum: exclusiveMinimum:
type: "boolean" type: 'boolean'
"default": false 'default': false
maxLength: { $ref: "#/definitions/positiveInteger" } maxLength: {$ref: '#/definitions/positiveInteger'}
minLength: { $ref: "#/definitions/positiveIntegerDefault0" } minLength: {$ref: '#/definitions/positiveIntegerDefault0'}
pattern: pattern:
type: "string" type: 'string'
format: "regex" format: 'regex'
additionalItems: additionalItems:
anyOf: [ anyOf: [
{ type: "boolean", "default": false } {type: 'boolean', 'default': false}
{ $ref: "#" } {$ref: '#'}
] ]
items: items:
anyOf: [ anyOf: [
{ $ref: "#" } {$ref: '#'}
{ $ref: "#/definitions/schemaArray" } {$ref: '#/definitions/schemaArray'}
] ]
"default": {} 'default': {}
maxItems: { $ref: "#/definitions/positiveInteger" } maxItems: {$ref: '#/definitions/positiveInteger'}
minItems: { $ref: "#/definitions/positiveIntegerDefault0" } minItems: {$ref: '#/definitions/positiveIntegerDefault0'}
uniqueItems: uniqueItems:
type: "boolean" type: 'boolean'
"default": false 'default': false
maxProperties: { $ref: "#/definitions/positiveInteger" } maxProperties: {$ref: '#/definitions/positiveInteger'}
minProperties: { $ref: "#/definitions/positiveIntegerDefault0" } minProperties: {$ref: '#/definitions/positiveIntegerDefault0'}
required: { $ref: "#/definitions/stringArray" } required: {$ref: '#/definitions/stringArray'}
additionalProperties: additionalProperties:
anyOf: [ anyOf: [
{ type: "boolean", "default": true } {type: 'boolean', 'default': true}
{ $ref: "#" } {$ref: '#'}
] ]
"default": {} 'default': {}
definitions: definitions:
type: "object" type: 'object'
additionalProperties: { $ref: "#" } additionalProperties: {$ref: '#'}
"default": {} 'default': {}
properties: properties:
type: "object" type: 'object'
additionalProperties: { $ref: "#" } additionalProperties: {$ref: '#'}
"default": {} 'default': {}
patternProperties: patternProperties:
type: "object" type: 'object'
additionalProperties: { $ref: "#" } additionalProperties: {$ref: '#'}
"default": {} 'default': {}
dependencies: dependencies:
type: "object" type: 'object'
additionalProperties: additionalProperties:
anyOf: [ anyOf: [
{ $ref: "#" } {$ref: '#'}
{ $ref: "#/definitions/stringArray" } {$ref: '#/definitions/stringArray'}
] ]
"enum": 'enum':
type: "array" type: 'array'
minItems: 1 minItems: 1
uniqueItems: true uniqueItems: true
"default": [''] 'default': ['']
type: type:
anyOf: [ anyOf: [
{ $ref: "#/definitions/simpleTypes" } {$ref: '#/definitions/simpleTypes'}
{ {
type: "array" type: 'array'
items: { $ref: "#/definitions/simpleTypes" } items: {$ref: '#/definitions/simpleTypes'}
minItems: 1 minItems: 1
uniqueItems: true uniqueItems: true
title: "Array of Types" title: 'Array of Types'
"default": ['string'] 'default': ['string']
}] }]
allOf: { $ref: "#/definitions/schemaArray" } allOf: {$ref: '#/definitions/schemaArray'}
anyOf: { $ref: "#/definitions/schemaArray" } anyOf: {$ref: '#/definitions/schemaArray'}
oneOf: { $ref: "#/definitions/schemaArray" } oneOf: {$ref: '#/definitions/schemaArray'}
not: { $ref: "#" } not: {$ref: '#'}
dependencies: dependencies:
exclusiveMaximum: [ "maximum" ] exclusiveMaximum: ['maximum']
exclusiveMinimum: [ "minimum" ] exclusiveMinimum: ['minimum']
"default": {} 'default': {}

View file

@ -11,7 +11,7 @@ MongoQueryOperatorSchema =
'$in': type: 'array' '$in': type: 'array'
'$lt': type: 'number' '$lt': type: 'number'
'$lte': type: 'number' '$lte': type: 'number'
'$ne': type: [ 'number', 'string' ] '$ne': type: ['number', 'string']
'$nin': type: 'array' '$nin': type: 'array'
additionalProperties: true # TODO set to false when the schema's done additionalProperties: true # TODO set to false when the schema's done
@ -23,9 +23,9 @@ MongoFindQuerySchema =
#'^[-a-zA-Z0-9_]*$': #'^[-a-zA-Z0-9_]*$':
'^[-a-zA-Z0-9\.]*$': '^[-a-zA-Z0-9\.]*$':
oneOf: [ oneOf: [
#{ $ref: '#/definitions/' + MongoQueryOperatorSchema.id}, #{$ref: '#/definitions/' + MongoQueryOperatorSchema.id},
{ type: 'string' } {type: 'string'},
{ type: 'object' } {type: 'object'}
] ]
additionalProperties: true # TODO make Treema accept new pattern matched keys additionalProperties: true # TODO make Treema accept new pattern matched keys
definitions: {} definitions: {}
@ -41,12 +41,12 @@ _.extend(AchievementSchema.properties,
query: query:
#type:'object' #type:'object'
$ref: '#/definitions/' + MongoFindQuerySchema.id $ref: '#/definitions/' + MongoFindQuerySchema.id
worth: { type: 'number' } worth: {type: 'number'}
collection: { type: 'string' } collection: {type: 'string'}
description: { type: 'string' } description: {type: 'string'}
userField: { type: 'string' } userField: {type: 'string'}
related: c.objectId(description: 'Related entity') related: c.objectId(description: 'Related entity')
icon: { type: 'string', format: 'image-file', title: 'Icon' } icon: {type: 'string', format: 'image-file', title: 'Icon'}
proportionalTo: proportionalTo:
type: 'string' type: 'string'
description: 'For repeatables only. Denotes the field a repeatable achievement needs for its calculations' description: 'For repeatables only. Denotes the field a repeatable achievement needs for its calculations'

View file

@ -3,8 +3,8 @@ c = require './../schemas'
ArticleSchema = c.object() ArticleSchema = c.object()
c.extendNamedProperties ArticleSchema # name first c.extendNamedProperties ArticleSchema # name first
ArticleSchema.properties.body = { type: 'string', title: 'Content', format: 'markdown' } ArticleSchema.properties.body = {type: 'string', title: 'Content', format: 'markdown'}
ArticleSchema.properties.i18n = { type: 'object', title: 'i18n', format: 'i18n', props: ['name', 'body'] } ArticleSchema.properties.i18n = {type: 'object', title: 'i18n', format: 'i18n', props: ['name', 'body']}
c.extendBasicProperties ArticleSchema, 'article' c.extendBasicProperties ArticleSchema, 'article'
c.extendSearchableProperties ArticleSchema c.extendSearchableProperties ArticleSchema

View file

@ -9,7 +9,7 @@ module.exports =
[ [
{ {
rel: 'extra' rel: 'extra'
href: "/db/user/{($)}" href: '/db/user/{($)}'
} }
] ]
achievement: c.objectId achievement: c.objectId

View file

@ -3,242 +3,242 @@ ThangComponentSchema = require './thang_component'
SpecificArticleSchema = c.object() SpecificArticleSchema = c.object()
c.extendNamedProperties SpecificArticleSchema # name first c.extendNamedProperties SpecificArticleSchema # name first
SpecificArticleSchema.properties.body = { type: 'string', title: 'Content', description: "The body content of the article, in Markdown.", format: 'markdown' } SpecificArticleSchema.properties.body = {type: 'string', title: 'Content', description: 'The body content of the article, in Markdown.', format: 'markdown'}
SpecificArticleSchema.properties.i18n = {type: "object", format: 'i18n', props: ['name', 'body'], description: "Help translate this article"} SpecificArticleSchema.properties.i18n = {type: 'object', format: 'i18n', props: ['name', 'body'], description: 'Help translate this article'}
SpecificArticleSchema.displayProperty = 'name' SpecificArticleSchema.displayProperty = 'name'
side = {title: "Side", description: "A side.", type: 'string', 'enum': ['left', 'right', 'top', 'bottom']} side = {title: 'Side', description: 'A side.', type: 'string', 'enum': ['left', 'right', 'top', 'bottom']}
thang = {title: "Thang", description: "The name of a Thang.", type: 'string', maxLength: 30, format:'thang'} thang = {title: 'Thang', description: 'The name of a Thang.', type: 'string', maxLength: 30, format: 'thang'}
eventPrereqValueTypes = ["boolean", "integer", "number", "null", "string"] # not "object" or "array" eventPrereqValueTypes = ['boolean', 'integer', 'number', 'null', 'string'] # not 'object' or 'array'
EventPrereqSchema = c.object {title: "Event Prerequisite", format: 'event-prereq', description: "Script requires that the value of some property on the event triggering it to meet some prerequisite.", "default": {eventProps: []}, required: ["eventProps"]}, EventPrereqSchema = c.object {title: 'Event Prerequisite', format: 'event-prereq', description: 'Script requires that the value of some property on the event triggering it to meet some prerequisite.', 'default': {eventProps: []}, required: ['eventProps']},
eventProps: c.array {'default': ["thang"], format:'event-value-chain', maxItems: 10, title: "Event Property", description: 'A chain of keys in the event, like "thang.pos.x" to access event.thang.pos.x.'}, c.shortString(title: "Property", description: "A key in the event property key chain.") eventProps: c.array {'default': ['thang'], format: 'event-value-chain', maxItems: 10, title: 'Event Property', description: 'A chain of keys in the event, like "thang.pos.x" to access event.thang.pos.x.'}, c.shortString(title: 'Property', description: 'A key in the event property key chain.')
equalTo: c.object {type: eventPrereqValueTypes, title: "==", description: "Script requires the event's property chain value to be equal to this value."} equalTo: c.object {type: eventPrereqValueTypes, title: '==', description: 'Script requires the event\'s property chain value to be equal to this value.'}
notEqualTo: c.object {type: eventPrereqValueTypes, title: "!=", description: "Script requires the event's property chain value to *not* be equal to this value."} notEqualTo: c.object {type: eventPrereqValueTypes, title: '!=', description: 'Script requires the event\'s property chain value to *not* be equal to this value.'}
greaterThan: {type: 'number', title: ">", description: "Script requires the event's property chain value to be greater than this value."} greaterThan: {type: 'number', title: '>', description: 'Script requires the event\'s property chain value to be greater than this value.'}
greaterThanOrEqualTo: {type: 'number', title: ">=", description: "Script requires the event's property chain value to be greater or equal to this value."} greaterThanOrEqualTo: {type: 'number', title: '>=', description: 'Script requires the event\'s property chain value to be greater or equal to this value.'}
lessThan: {type: 'number', title: "<", description: "Script requires the event's property chain value to be less than this value."} lessThan: {type: 'number', title: '<', description: 'Script requires the event\'s property chain value to be less than this value.'}
lessThanOrEqualTo: {type: 'number', title: "<=", description: "Script requires the event's property chain value to be less than or equal to this value."} lessThanOrEqualTo: {type: 'number', title: '<=', description: 'Script requires the event\'s property chain value to be less than or equal to this value.'}
containingString: c.shortString(title: "Contains", description: "Script requires the event's property chain value to be a string containing this string.") containingString: c.shortString(title: 'Contains', description: 'Script requires the event\'s property chain value to be a string containing this string.')
notContainingString: c.shortString(title: "Does not contain", description: "Script requires the event's property chain value to *not* be a string containing this string.") notContainingString: c.shortString(title: 'Does not contain', description: 'Script requires the event\'s property chain value to *not* be a string containing this string.')
containingRegexp: c.shortString(title: "Contains Regexp", description: "Script requires the event's property chain value to be a string containing this regular expression.") containingRegexp: c.shortString(title: 'Contains Regexp', description: 'Script requires the event\'s property chain value to be a string containing this regular expression.')
notContainingRegexp: c.shortString(title: "Does not contain regexp", description: "Script requires the event's property chain value to *not* be a string containing this regular expression.") notContainingRegexp: c.shortString(title: 'Does not contain regexp', description: 'Script requires the event\'s property chain value to *not* be a string containing this regular expression.')
GoalSchema = c.object {title: "Goal", description: "A goal that the player can accomplish.", required: ["name", "id"]}, GoalSchema = c.object {title: 'Goal', description: 'A goal that the player can accomplish.', required: ['name', 'id']},
name: c.shortString(title: "Name", description: "Name of the goal that the player will see, like \"Defeat eighteen dragons\".") name: c.shortString(title: 'Name', description: 'Name of the goal that the player will see, like \"Defeat eighteen dragons\".')
i18n: {type: "object", format: 'i18n', props: ['name'], description: "Help translate this goal"} i18n: {type: 'object', format: 'i18n', props: ['name'], description: 'Help translate this goal'}
id: c.shortString(title: "ID", description: "Unique identifier for this goal, like \"defeat-dragons\".") # unique somehow? id: c.shortString(title: 'ID', description: 'Unique identifier for this goal, like \"defeat-dragons\".') # unique somehow?
worldEndsAfter: {title: 'World Ends After', description: "When included, ends the world this many seconds after this goal succeeds or fails.", type: 'number', minimum: 0, exclusiveMinimum: true, maximum: 300, default: 3} worldEndsAfter: {title: 'World Ends After', description: 'When included, ends the world this many seconds after this goal succeeds or fails.', type: 'number', minimum: 0, exclusiveMinimum: true, maximum: 300, default: 3}
howMany: {title: "How Many", description: "When included, require only this many of the listed goal targets instead of all of them.", type: 'integer', minimum: 1} howMany: {title: 'How Many', description: 'When included, require only this many of the listed goal targets instead of all of them.', type: 'integer', minimum: 1}
hiddenGoal: {title: "Hidden", description: "Hidden goals don't show up in the goals area for the player until they're failed. (Usually they're obvious, like 'don't die'.)", 'type': 'boolean', default: false} hiddenGoal: {title: 'Hidden', description: 'Hidden goals don\'t show up in the goals area for the player until they\'re failed. (Usually they\'re obvious, like "don\'t die".)', 'type': 'boolean', default: false}
team: c.shortString(title: 'Team', description: 'Name of the team this goal is for, if it is not for all of the playable teams.') team: c.shortString(title: 'Team', description: 'Name of the team this goal is for, if it is not for all of the playable teams.')
killThangs: c.array {title: "Kill Thangs", description: "A list of Thang IDs the player should kill, or team names.", uniqueItems: true, minItems: 1, "default": ["ogres"]}, thang killThangs: c.array {title: 'Kill Thangs', description: 'A list of Thang IDs the player should kill, or team names.', uniqueItems: true, minItems: 1, 'default': ['ogres']}, thang
saveThangs: c.array {title: "Save Thangs", description: "A list of Thang IDs the player should save, or team names", uniqueItems: true, minItems: 1, "default": ["humans"]}, thang saveThangs: c.array {title: 'Save Thangs', description: 'A list of Thang IDs the player should save, or team names', uniqueItems: true, minItems: 1, 'default': ['humans']}, thang
getToLocations: c.object {title: "Get To Locations", description: "Will be set off when any of the \"who\" touch any of the \"targets\" ", required: ["who", "targets"]}, getToLocations: c.object {title: 'Get To Locations', description: 'Will be set off when any of the \"who\" touch any of the \"targets\"', required: ['who', 'targets']},
who: c.array {title: "Who", description: "The Thangs who must get to the target locations.", minItems: 1}, thang who: c.array {title: 'Who', description: 'The Thangs who must get to the target locations.', minItems: 1}, thang
targets: c.array {title: "Targets", description: "The target locations to which the Thangs must get.", minItems: 1}, thang targets: c.array {title: 'Targets', description: 'The target locations to which the Thangs must get.', minItems: 1}, thang
getAllToLocations: c.array {title: "Get all to locations", description: "Similar to getToLocations but now a specific \"who\" can have a specific \"target\", also must be used with the HowMany property for desired effect",required: ["getToLocation"]}, getAllToLocations: c.array {title: 'Get all to locations', description: 'Similar to getToLocations but now a specific \"who\" can have a specific \"target\", also must be used with the HowMany property for desired effect', required: ['getToLocation']},
c.object {title: "", description: ""}, c.object {title: '', description: ''},
getToLocation: c.object {title: "Get To Locations", description: "TODO: explain", required: ["who", "targets"]}, getToLocation: c.object {title: 'Get To Locations', description: 'TODO: explain', required: ['who', 'targets']},
who: c.array {title: "Who", description: "The Thangs who must get to the target locations.", minItems: 1}, thang who: c.array {title: 'Who', description: 'The Thangs who must get to the target locations.', minItems: 1}, thang
targets: c.array {title: "Targets", description: "The target locations to which the Thangs must get.", minItems: 1}, thang targets: c.array {title: 'Targets', description: 'The target locations to which the Thangs must get.', minItems: 1}, thang
keepFromLocations: c.object {title: "Keep From Locations", description: "TODO: explain", required: ["who", "targets"]}, keepFromLocations: c.object {title: 'Keep From Locations', description: 'TODO: explain', required: ['who', 'targets']},
who: c.array {title: "Who", description: "The Thangs who must not get to the target locations.", minItems: 1}, thang who: c.array {title: 'Who', description: 'The Thangs who must not get to the target locations.', minItems: 1}, thang
targets: c.array {title: "Targets", description: "The target locations to which the Thangs must not get.", minItems: 1}, thang targets: c.array {title: 'Targets', description: 'The target locations to which the Thangs must not get.', minItems: 1}, thang
keepAllFromLocations: c.array {title: "Keep ALL From Locations", description: "Similar to keepFromLocations but now a specific \"who\" can have a specific \"target\", also must be used with the HowMany property for desired effect", required: ["keepFromLocation"]}, keepAllFromLocations: c.array {title: 'Keep ALL From Locations', description: 'Similar to keepFromLocations but now a specific \"who\" can have a specific \"target\", also must be used with the HowMany property for desired effect', required: ['keepFromLocation']},
c.object {title: "", description: ""}, c.object {title: '', description: ''},
keepFromLocation: c.object {title: "Keep From Locations", description: "TODO: explain", required: ["who", "targets"]}, keepFromLocation: c.object {title: 'Keep From Locations', description: 'TODO: explain', required: ['who', 'targets']},
who: c.array {title: "Who", description: "The Thangs who must not get to the target locations.", minItems: 1}, thang who: c.array {title: 'Who', description: 'The Thangs who must not get to the target locations.', minItems: 1}, thang
targets: c.array {title: "Targets", description: "The target locations to which the Thangs must not get.", minItems: 1}, thang targets: c.array {title: 'Targets', description: 'The target locations to which the Thangs must not get.', minItems: 1}, thang
leaveOffSides: c.object {title: "Leave Off Sides", description: "Sides of the level to get some Thangs to leave across.", required: ["who", "sides"]}, leaveOffSides: c.object {title: 'Leave Off Sides', description: 'Sides of the level to get some Thangs to leave across.', required: ['who', 'sides']},
who: c.array {title: "Who", description: "The Thangs which must leave off the sides of the level.", minItems: 1}, thang who: c.array {title: 'Who', description: 'The Thangs which must leave off the sides of the level.', minItems: 1}, thang
sides: c.array {title: "Sides", description: "The sides off which the Thangs must leave.", minItems: 1}, side sides: c.array {title: 'Sides', description: 'The sides off which the Thangs must leave.', minItems: 1}, side
keepFromLeavingOffSides: c.object {title: "Keep From Leaving Off Sides", description: "Sides of the level to keep some Thangs from leaving across.", required: ["who", "sides"]}, keepFromLeavingOffSides: c.object {title: 'Keep From Leaving Off Sides', description: 'Sides of the level to keep some Thangs from leaving across.', required: ['who', 'sides']},
who: c.array {title: "Who", description: "The Thangs which must not leave off the sides of the level.", minItems: 1}, thang who: c.array {title: 'Who', description: 'The Thangs which must not leave off the sides of the level.', minItems: 1}, thang
sides: side, {title: "Sides", description: "The sides off which the Thangs must not leave.", minItems: 1}, side sides: side, {title: 'Sides', description: 'The sides off which the Thangs must not leave.', minItems: 1}, side
collectThangs: c.object {title: "Collect", description: "Thangs that other Thangs must collect.", required: ["who", "targets"]}, collectThangs: c.object {title: 'Collect', description: 'Thangs that other Thangs must collect.', required: ['who', 'targets']},
who: c.array {title: "Who", description: "The Thangs which must collect the target items.", minItems: 1}, thang who: c.array {title: 'Who', description: 'The Thangs which must collect the target items.', minItems: 1}, thang
targets: c.array {title: "Targets", description: "The target items which the Thangs must collect.", minItems: 1}, thang targets: c.array {title: 'Targets', description: 'The target items which the Thangs must collect.', minItems: 1}, thang
keepFromCollectingThangs: c.object {title: "Keep From Collecting", description: "Thangs that the player must prevent other Thangs from collecting.", required: ["who", "targets"]}, keepFromCollectingThangs: c.object {title: 'Keep From Collecting', description: 'Thangs that the player must prevent other Thangs from collecting.', required: ['who', 'targets']},
who: c.array {title: "Who", description: "The Thangs which must not collect the target items.", minItems: 1}, thang who: c.array {title: 'Who', description: 'The Thangs which must not collect the target items.', minItems: 1}, thang
targets: c.array {title: "Targets", description: "The target items which the Thangs must not collect.", minItems: 1}, thang targets: c.array {title: 'Targets', description: 'The target items which the Thangs must not collect.', minItems: 1}, thang
ResponseSchema = c.object {title: "Dialogue Button", description: "A button to be shown to the user with the dialogue.", required: ["text"]}, ResponseSchema = c.object {title: 'Dialogue Button', description: 'A button to be shown to the user with the dialogue.', required: ['text']},
text: {title: "Title", description: "The text that will be on the button", "default": "Okay", type: 'string', maxLength: 30} text: {title: 'Title', description: 'The text that will be on the button', 'default': 'Okay', type: 'string', maxLength: 30}
channel: c.shortString(title: "Channel", format: 'event-channel', description: 'Channel that this event will be broadcast over, like "level-set-playing".') channel: c.shortString(title: 'Channel', format: 'event-channel', description: 'Channel that this event will be broadcast over, like "level-set-playing".')
event: {type: 'object', title: "Event", description: "Event that will be broadcast when this button is pressed, like {playing: true}."} event: {type: 'object', title: 'Event', description: 'Event that will be broadcast when this button is pressed, like {playing: true}.'}
buttonClass: c.shortString(title: "Button Class", description: 'CSS class that will be added to the button, like "btn-primary".') buttonClass: c.shortString(title: 'Button Class', description: 'CSS class that will be added to the button, like "btn-primary".')
i18n: {type: "object", format: 'i18n', props: ['text'], description: "Help translate this button"} i18n: {type: 'object', format: 'i18n', props: ['text'], description: 'Help translate this button'}
PointSchema = c.object {title: "Point", description: "An {x, y} coordinate point.", format: "point2d", required: ["x", "y"]}, PointSchema = c.object {title: 'Point', description: 'An {x, y} coordinate point.', format: 'point2d', required: ['x', 'y']},
x: {title: "x", description: "The x coordinate.", type: "number", "default": 15} x: {title: 'x', description: 'The x coordinate.', type: 'number', 'default': 15}
y: {title: "y", description: "The y coordinate.", type: "number", "default": 20} y: {title: 'y', description: 'The y coordinate.', type: 'number', 'default': 20}
SpriteCommandSchema = c.object {title: "Thang Command", description: "Make a target Thang move or say something, or select/deselect it.", required: ["id"], default: {id: "Captain Anya"}}, SpriteCommandSchema = c.object {title: 'Thang Command', description: 'Make a target Thang move or say something, or select/deselect it.', required: ['id'], default: {id: 'Captain Anya'}},
id: thang id: thang
select: {title: "Select", description: "Select or deselect this Thang.", type: 'boolean'} select: {title: 'Select', description: 'Select or deselect this Thang.', type: 'boolean'}
say: c.object {title: "Say", description: "Make this Thang say a message.", required: ["text"]}, say: c.object {title: 'Say', description: 'Make this Thang say a message.', required: ['text']},
blurb: c.shortString(title: "Blurb", description: "A very short message to display above this Thang's head. Plain text.", maxLength: 50) blurb: c.shortString(title: 'Blurb', description: 'A very short message to display above this Thang\'s head. Plain text.', maxLength: 50)
mood: c.shortString(title: "Mood", description: "The mood with which the Thang speaks.", "enum": ["explain", "debrief", "congrats", "attack", "joke", "tip", "alarm"], "default": "explain") mood: c.shortString(title: 'Mood', description: 'The mood with which the Thang speaks.', 'enum': ['explain', 'debrief', 'congrats', 'attack', 'joke', 'tip', 'alarm'], 'default': 'explain')
text: {title: "Text", description: "A short message to display in the dialogue area. Markdown okay.", type: "string", maxLength: 400} text: {title: 'Text', description: 'A short message to display in the dialogue area. Markdown okay.', type: 'string', maxLength: 400}
sound: c.object {title: "Sound", description: "A dialogue sound file to accompany the message.", required: ["mp3", "ogg"]}, sound: c.object {title: 'Sound', description: 'A dialogue sound file to accompany the message.', required: ['mp3', 'ogg']},
mp3: c.shortString(title: "MP3", format: 'sound-file') mp3: c.shortString(title: 'MP3', format: 'sound-file')
ogg: c.shortString(title: "OGG", format: 'sound-file') ogg: c.shortString(title: 'OGG', format: 'sound-file')
preload: {title: "Preload", description: "Whether to load this sound file before the level can begin (typically for the first dialogue of a level).", type: 'boolean', "default": false} preload: {title: 'Preload', description: 'Whether to load this sound file before the level can begin (typically for the first dialogue of a level).', type: 'boolean', 'default': false}
responses: c.array {title: "Buttons", description: "An array of buttons to include with the dialogue, with which the user can respond."}, ResponseSchema responses: c.array {title: 'Buttons', description: 'An array of buttons to include with the dialogue, with which the user can respond.'}, ResponseSchema
i18n: {type: "object", format: 'i18n', props: ['blurb', 'text'], description: "Help translate this message"} i18n: {type: 'object', format: 'i18n', props: ['blurb', 'text'], description: 'Help translate this message'}
move: c.object {title: "Move", description: "Tell the Thang to move.", required: ['target'], default: {target: {x: 20, y: 20}, duration: 500}}, move: c.object {title: 'Move', description: 'Tell the Thang to move.', required: ['target'], default: {target: {x: 20, y: 20}, duration: 500}},
target: _.extend _.cloneDeep(PointSchema), {title: 'Target', description: 'Target point to which the Thang will move.'} target: _.extend _.cloneDeep(PointSchema), {title: 'Target', description: 'Target point to which the Thang will move.'}
duration: {title: "Duration", description: "Number of milliseconds over which to move, or 0 for an instant move.", type: 'integer', minimum: 0, default: 500, format: 'milliseconds'} duration: {title: 'Duration', description: 'Number of milliseconds over which to move, or 0 for an instant move.', type: 'integer', minimum: 0, default: 500, format: 'milliseconds'}
NoteGroupSchema = c.object {title: "Note Group", description: "A group of notes that should be sent out as a result of this script triggering.", displayProperty: "name"}, NoteGroupSchema = c.object {title: 'Note Group', description: 'A group of notes that should be sent out as a result of this script triggering.', displayProperty: 'name'},
name: {title: "Name", description: "Short name describing the script, like \"Anya greets the player\", for your convenience.", type: "string"} name: {title: 'Name', description: 'Short name describing the script, like \"Anya greets the player\", for your convenience.', type: 'string'}
dom: c.object {title: "DOM", description: "Manipulate things in the play area DOM, outside of the level area canvas."}, dom: c.object {title: 'DOM', description: 'Manipulate things in the play area DOM, outside of the level area canvas.'},
focus: c.shortString(title: "Focus", description: "Set the window focus to this DOM selector string.") focus: c.shortString(title: 'Focus', description: 'Set the window focus to this DOM selector string.')
showVictory: { showVictory: {
title: "Show Victory", title: 'Show Victory',
description: "Show the done button and maybe also the victory modal.", description: 'Show the done button and maybe also the victory modal.',
enum: [true, 'Done Button', 'Done Button And Modal'] # deprecate true, same as 'done_button_and_modal' enum: [true, 'Done Button', 'Done Button And Modal'] # deprecate true, same as 'done_button_and_modal'
} }
highlight: c.object {title: "Highlight", description: "Highlight the target DOM selector string with a big arrow."}, highlight: c.object {title: 'Highlight', description: 'Highlight the target DOM selector string with a big arrow.'},
target: c.shortString(title: "Target", description: "Target highlight element DOM selector string.") target: c.shortString(title: 'Target', description: 'Target highlight element DOM selector string.')
delay: {type: 'integer', minimum: 0, title: "Delay", description: "Show the highlight after this many milliseconds. Doesn't affect the dim shade cutout highlight method."} delay: {type: 'integer', minimum: 0, title: 'Delay', description: 'Show the highlight after this many milliseconds. Doesn\'t affect the dim shade cutout highlight method.'}
offset: _.extend _.cloneDeep(PointSchema), {title: 'Offset', description: 'Pointing arrow tip offset in pixels from the default target.', format: null} offset: _.extend _.cloneDeep(PointSchema), {title: 'Offset', description: 'Pointing arrow tip offset in pixels from the default target.', format: null}
rotation: {type: 'number', minimum: 0, title: "Rotation", description: "Rotation of the pointing arrow, in radians. PI / 2 points left, PI points up, etc."} rotation: {type: 'number', minimum: 0, title: 'Rotation', description: 'Rotation of the pointing arrow, in radians. PI / 2 points left, PI points up, etc.'}
sides: c.array {title: "Sides", description: "Which sides of the target element to point at."}, {type: 'string', 'enum': ['left', 'right', 'top', 'bottom'], title: "Side", description: "A side of the target element to point at."} sides: c.array {title: 'Sides', description: 'Which sides of the target element to point at.'}, {type: 'string', 'enum': ['left', 'right', 'top', 'bottom'], title: 'Side', description: 'A side of the target element to point at.'}
lock: {title: "Lock", description: "Whether the interface should be locked so that the player's focus is on the script, or specific areas to lock.", type: ['boolean', 'array'], items: {type: 'string', enum: ['surface', 'editor', 'palette', 'hud', 'playback', 'playback-hover', 'level']}} lock: {title: 'Lock', description: 'Whether the interface should be locked so that the player\'s focus is on the script, or specific areas to lock.', type: ['boolean', 'array'], items: {type: 'string', enum: ['surface', 'editor', 'palette', 'hud', 'playback', 'playback-hover', 'level']}}
letterbox: {type: 'boolean', title: 'Letterbox', description:'Turn letterbox mode on or off. Disables surface and playback controls.'} letterbox: {type: 'boolean', title: 'Letterbox', description: 'Turn letterbox mode on or off. Disables surface and playback controls.'}
goals: c.object {title: "Goals (Old)", description: "Deprecated. Goals added here have no effect. Add goals in the level settings instead."}, goals: c.object {title: 'Goals (Old)', description: 'Deprecated. Goals added here have no effect. Add goals in the level settings instead.'},
add: c.array {title: "Add", description: "Deprecated. Goals added here have no effect. Add goals in the level settings instead."}, GoalSchema add: c.array {title: 'Add', description: 'Deprecated. Goals added here have no effect. Add goals in the level settings instead.'}, GoalSchema
remove: c.array {title: "Remove", description: "Deprecated. Goals removed here have no effect. Adjust goals in the level settings instead."}, GoalSchema remove: c.array {title: 'Remove', description: 'Deprecated. Goals removed here have no effect. Adjust goals in the level settings instead.'}, GoalSchema
playback: c.object {title: "Playback", description: "Control the playback of the level."}, playback: c.object {title: 'Playback', description: 'Control the playback of the level.'},
playing: {type: 'boolean', title: "Set Playing", description: "Set whether playback is playing or paused."} playing: {type: 'boolean', title: 'Set Playing', description: 'Set whether playback is playing or paused.'}
scrub: c.object {title: "Scrub", description: "Scrub the level playback time to a certain point.", default: {offset: 2, duration: 1000, toRatio: 0.5}}, scrub: c.object {title: 'Scrub', description: 'Scrub the level playback time to a certain point.', default: {offset: 2, duration: 1000, toRatio: 0.5}},
offset: {type: 'integer', title: "Offset", description: "Number of frames by which to adjust the scrub target time.", default: 2} offset: {type: 'integer', title: 'Offset', description: 'Number of frames by which to adjust the scrub target time.', default: 2}
duration: {type: 'integer', title: "Duration", description: "Number of milliseconds over which to scrub time.", minimum: 0, format: 'milliseconds'} duration: {type: 'integer', title: 'Duration', description: 'Number of milliseconds over which to scrub time.', minimum: 0, format: 'milliseconds'}
toRatio: {type: 'number', title: "To Progress Ratio", description: "Set playback time to a target playback progress ratio.", minimum: 0, maximum: 1} toRatio: {type: 'number', title: 'To Progress Ratio', description: 'Set playback time to a target playback progress ratio.', minimum: 0, maximum: 1}
toTime: {type: 'number', title: "To Time", description: "Set playback time to a target playback point, in seconds.", minimum: 0} toTime: {type: 'number', title: 'To Time', description: 'Set playback time to a target playback point, in seconds.', minimum: 0}
toGoal: c.shortString(title: "To Goal", description: "Set playback time to when this goal was achieved. (TODO: not implemented.)") toGoal: c.shortString(title: 'To Goal', description: 'Set playback time to when this goal was achieved. (TODO: not implemented.)')
script: c.object {title: "Script", description: "Extra configuration for this action group."}, script: c.object {title: 'Script', description: 'Extra configuration for this action group.'},
duration: {type: 'integer', minimum: 0, title: "Duration", description: "How long this script should last in milliseconds. 0 for indefinite.", format: 'milliseconds'} duration: {type: 'integer', minimum: 0, title: 'Duration', description: 'How long this script should last in milliseconds. 0 for indefinite.', format: 'milliseconds'}
skippable: {type: 'boolean', title: "Skippable", description: "Whether this script shouldn't bother firing when the player skips past all current scripts."} skippable: {type: 'boolean', title: 'Skippable', description: 'Whether this script shouldn\'t bother firing when the player skips past all current scripts.'}
beforeLoad: {type: 'boolean', title: "Before Load", description: "Whether this script should fire before the level is finished loading."} beforeLoad: {type: 'boolean', title: 'Before Load', description: 'Whether this script should fire before the level is finished loading.'}
sprites: c.array {title: "Sprites", description: "Commands to issue to Sprites on the Surface."}, SpriteCommandSchema sprites: c.array {title: 'Sprites', description: 'Commands to issue to Sprites on the Surface.'}, SpriteCommandSchema
surface: c.object {title: "Surface", description: "Commands to issue to the Surface itself."}, surface: c.object {title: 'Surface', description: 'Commands to issue to the Surface itself.'},
focus: c.object {title: "Camera", description: "Focus the camera on a specific point on the Surface.", format:'viewport'}, focus: c.object {title: 'Camera', description: 'Focus the camera on a specific point on the Surface.', format: 'viewport'},
target: {anyOf: [PointSchema, thang, {type: 'null'}], title: "Target", description: "Where to center the camera view.", default: {x:0, y:0}} target: {anyOf: [PointSchema, thang, {type: 'null'}], title: 'Target', description: 'Where to center the camera view.', default: {x: 0, y: 0}}
zoom: {type: 'number', minimum: 0, exclusiveMinimum: true, maximum: 64, title: "Zoom", description: "What zoom level to use."} zoom: {type: 'number', minimum: 0, exclusiveMinimum: true, maximum: 64, title: 'Zoom', description: 'What zoom level to use.'}
duration: {type:'number', minimum: 0, title: "Duration", description: "in ms"} duration: {type: 'number', minimum: 0, title: 'Duration', description: 'in ms'}
bounds: c.array {title:'Boundary', maxItems: 2, minItems: 2, default:[{x:0,y:0}, {x:46, y:39}], format: 'bounds'}, PointSchema bounds: c.array {title: 'Boundary', maxItems: 2, minItems: 2, default: [{x: 0, y: 0}, {x: 46, y: 39}], format: 'bounds'}, PointSchema
isNewDefault: {type:'boolean', format: 'hidden', title: "New Default", description: 'Set this as new default zoom once scripts end.'} # deprecated isNewDefault: {type: 'boolean', format: 'hidden', title: 'New Default', description: 'Set this as new default zoom once scripts end.'} # deprecated
highlight: c.object {title: "Highlight", description: "Highlight specific Sprites on the Surface."}, highlight: c.object {title: 'Highlight', description: 'Highlight specific Sprites on the Surface.'},
targets: c.array {title: "Targets", description: "Thang IDs of target Sprites to highlight."}, thang targets: c.array {title: 'Targets', description: 'Thang IDs of target Sprites to highlight.'}, thang
delay: {type: 'integer', minimum: 0, title: "Delay", description: "Delay in milliseconds before the highlight appears."} delay: {type: 'integer', minimum: 0, title: 'Delay', description: 'Delay in milliseconds before the highlight appears.'}
lockSelect: {type: 'boolean', title: "Lock Select", description: "Whether to lock Sprite selection so that the player can't select/deselect anything."} lockSelect: {type: 'boolean', title: 'Lock Select', description: 'Whether to lock Sprite selection so that the player can\'t select/deselect anything.'}
sound: c.object {title: "Sound", description: "Commands to control sound playback."}, sound: c.object {title: 'Sound', description: 'Commands to control sound playback.'},
suppressSelectionSounds: {type: "boolean", title: "Suppress Selection Sounds", description: "Whether to suppress selection sounds made from clicking on Thangs."} suppressSelectionSounds: {type: 'boolean', title: 'Suppress Selection Sounds', description: 'Whether to suppress selection sounds made from clicking on Thangs.'}
music: c.object { title: "Music", description: "Control music playing"}, music: c.object {title: 'Music', description: 'Control music playing'},
play: { title: "Play", type: "boolean" } play: {title: 'Play', type: 'boolean'}
file: c.shortString(title: "File", enum:['/music/music_level_1','/music/music_level_2','/music/music_level_3','/music/music_level_4','/music/music_level_5']) file: c.shortString(title: 'File', enum: ['/music/music_level_1', '/music/music_level_2', '/music/music_level_3', '/music/music_level_4', '/music/music_level_5'])
ScriptSchema = c.object { ScriptSchema = c.object {
title: "Script" title: 'Script'
description: 'A script fires off a chain of notes to interact with the game when a certain event triggers it.' description: 'A script fires off a chain of notes to interact with the game when a certain event triggers it.'
required: ["channel"] required: ['channel']
'default': {channel: "world:won", noteChain: []} 'default': {channel: 'world:won', noteChain: []}
}, },
id: c.shortString(title: "ID", description: "A unique ID that other scripts can rely on in their Happens After prereqs, for sequencing.") # uniqueness? id: c.shortString(title: 'ID', description: 'A unique ID that other scripts can rely on in their Happens After prereqs, for sequencing.') # uniqueness?
channel: c.shortString(title: "Event", format: 'event-channel', description: 'Event channel this script might trigger for, like "world:won".') channel: c.shortString(title: 'Event', format: 'event-channel', description: 'Event channel this script might trigger for, like "world:won".')
eventPrereqs: c.array {title: "Event Checks", description: "Logical checks on the event for this script to trigger.", format:'event-prereqs'}, EventPrereqSchema eventPrereqs: c.array {title: 'Event Checks', description: 'Logical checks on the event for this script to trigger.', format: 'event-prereqs'}, EventPrereqSchema
repeats: {title: "Repeats", description: "Whether this script can trigger more than once during a level.", enum: [true, false, 'session'], "default": false} repeats: {title: 'Repeats', description: 'Whether this script can trigger more than once during a level.', enum: [true, false, 'session'], 'default': false}
scriptPrereqs: c.array {title: "Happens After", description: "Scripts that need to fire first."}, scriptPrereqs: c.array {title: 'Happens After', description: 'Scripts that need to fire first.'},
c.shortString(title: "ID", description: "A unique ID of a script.") c.shortString(title: 'ID', description: 'A unique ID of a script.')
notAfter: c.array {title: "Not After", description: "Do not run this script if any of these scripts have run."}, notAfter: c.array {title: 'Not After', description: 'Do not run this script if any of these scripts have run.'},
c.shortString(title: "ID", description: "A unique ID of a script.") c.shortString(title: 'ID', description: 'A unique ID of a script.')
noteChain: c.array {title: "Actions", description: "A list of things that happen when this script triggers."}, NoteGroupSchema noteChain: c.array {title: 'Actions', description: 'A list of things that happen when this script triggers.'}, NoteGroupSchema
LevelThangSchema = c.object { LevelThangSchema = c.object {
title: "Thang", title: 'Thang',
description: "Thangs are any units, doodads, or abstract things that you use to build the level. (\"Thing\" was too confusing to say.)", description: 'Thangs are any units, doodads, or abstract things that you use to build the level. (\"Thing\" was too confusing to say.)',
format: "thang" format: 'thang'
required: ["id", "thangType", "components"] required: ['id', 'thangType', 'components']
'default': 'default':
id: "Boris" id: 'Boris'
thangType: "Soldier" thangType: 'Soldier'
components: [] components: []
}, },
id: thang # TODO: figure out if we can make this unique and how to set dynamic defaults id: thang # TODO: figure out if we can make this unique and how to set dynamic defaults
# TODO: split thangType into "original" and "majorVersion" like the rest for consistency # TODO: split thangType into 'original' and 'majorVersion' like the rest for consistency
thangType: c.objectId(links: [{rel: "db", href: "/db/thang.type/{($)}/version"}], title: "Thang Type", description: "A reference to the original Thang template being configured.", format: 'thang-type') thangType: c.objectId(links: [{rel: 'db', href: '/db/thang.type/{($)}/version'}], title: 'Thang Type', description: 'A reference to the original Thang template being configured.', format: 'thang-type')
components: c.array {title: "Components", description: "Thangs are configured by changing the Components attached to them.", uniqueItems: true, format: 'thang-components-array'}, ThangComponentSchema # TODO: uniqueness should be based on "original", not whole thing components: c.array {title: 'Components', description: 'Thangs are configured by changing the Components attached to them.', uniqueItems: true, format: 'thang-components-array'}, ThangComponentSchema # TODO: uniqueness should be based on 'original', not whole thing
LevelSystemSchema = c.object { LevelSystemSchema = c.object {
title: "System" title: 'System'
description: "Configuration for a System that this Level uses." description: 'Configuration for a System that this Level uses.'
format: 'level-system' format: 'level-system'
required: ['original', 'majorVersion'] required: ['original', 'majorVersion']
'default': 'default':
majorVersion: 0 majorVersion: 0
config: {} config: {}
links: [{rel: "db", href: "/db/level.system/{(original)}/version/{(majorVersion)}"}] links: [{rel: 'db', href: '/db/level.system/{(original)}/version/{(majorVersion)}'}]
}, },
original: c.objectId(title: "Original", description: "A reference to the original System being configured.", format: "hidden") original: c.objectId(title: 'Original', description: 'A reference to the original System being configured.', format: 'hidden')
config: c.object {title: "Configuration", description: "System-specific configuration properties.", additionalProperties: true, format: 'level-system-configuration'} config: c.object {title: 'Configuration', description: 'System-specific configuration properties.', additionalProperties: true, format: 'level-system-configuration'}
majorVersion: {title: "Major Version", description: "Which major version of the System is being used.", type: 'integer', minimum: 0, default: 0, format: "hidden"} majorVersion: {title: 'Major Version', description: 'Which major version of the System is being used.', type: 'integer', minimum: 0, default: 0, format: 'hidden'}
GeneralArticleSchema = c.object { GeneralArticleSchema = c.object {
title: "Article" title: 'Article'
description: "Reference to a general documentation article." description: 'Reference to a general documentation article.'
required: ['original'] required: ['original']
format: 'latest-version-reference' format: 'latest-version-reference'
'default': 'default':
original: null original: null
majorVersion: 0 majorVersion: 0
links: [{rel: "db", href: "/db/article/{(original)}/version/{(majorVersion)}"}] links: [{rel: 'db', href: '/db/article/{(original)}/version/{(majorVersion)}'}]
}, },
original: c.objectId(title: "Original", description: "A reference to the original Article.")#, format: "hidden") # hidden? original: c.objectId(title: 'Original', description: 'A reference to the original Article.')#, format: 'hidden') # hidden?
majorVersion: {title: "Major Version", description: "Which major version of the Article is being used.", type: 'integer', minimum: 0}#, format: "hidden"} # hidden? majorVersion: {title: 'Major Version', description: 'Which major version of the Article is being used.', type: 'integer', minimum: 0}#, format: 'hidden'} # hidden?
LevelSchema = c.object { LevelSchema = c.object {
title: "Level" title: 'Level'
description: "A spectacular level which will delight and educate its stalwart players with the sorcery of coding." description: 'A spectacular level which will delight and educate its stalwart players with the sorcery of coding.'
required: ["name", "description", "scripts", "thangs", "documentation"] required: ['name', 'description', 'scripts', 'thangs', 'documentation']
'default': 'default':
name: "Ineffable Wizardry" name: 'Ineffable Wizardry'
description: "This level is indescribably flarmy." description: 'This level is indescribably flarmy.'
documentation: {specificArticles: [], generalArticles: []} documentation: {specificArticles: [], generalArticles: []}
scripts: [] scripts: []
thangs: [] thangs: []
} }
c.extendNamedProperties LevelSchema # let's have the name be the first property c.extendNamedProperties LevelSchema # let's have the name be the first property
_.extend LevelSchema.properties, _.extend LevelSchema.properties,
description: {title: "Description", description: "A short explanation of what this level is about.", type: "string", maxLength: 65536, "default": "This level is indescribably flarmy!", format: 'markdown'} description: {title: 'Description', description: 'A short explanation of what this level is about.', type: 'string', maxLength: 65536, 'default': 'This level is indescribably flarmy!', format: 'markdown'}
documentation: c.object {title: "Documentation", description: "Documentation articles relating to this level.", required: ["specificArticles", "generalArticles"], 'default': {specificArticles: [], generalArticles: []}}, documentation: c.object {title: 'Documentation', description: 'Documentation articles relating to this level.', required: ['specificArticles', 'generalArticles'], 'default': {specificArticles: [], generalArticles: []}},
specificArticles: c.array {title: "Specific Articles", description: "Specific documentation articles that live only in this level.", uniqueItems: true, "default": []}, SpecificArticleSchema specificArticles: c.array {title: 'Specific Articles', description: 'Specific documentation articles that live only in this level.', uniqueItems: true, 'default': []}, SpecificArticleSchema
generalArticles: c.array {title: "General Articles", description: "General documentation articles that can be linked from multiple levels.", uniqueItems: true, "default": []}, GeneralArticleSchema generalArticles: c.array {title: 'General Articles', description: 'General documentation articles that can be linked from multiple levels.', uniqueItems: true, 'default': []}, GeneralArticleSchema
background: c.objectId({format: 'hidden'}) background: c.objectId({format: 'hidden'})
nextLevel: { nextLevel: {
type:'object', type: 'object',
links: [{rel: "extra", href: "/db/level/{($)}"}, {rel:'db', href: "/db/level/{(original)}/version/{(majorVersion)}"}], links: [{rel: 'extra', href: '/db/level/{($)}'}, {rel: 'db', href: '/db/level/{(original)}/version/{(majorVersion)}'}],
format: 'latest-version-reference', format: 'latest-version-reference',
title: "Next Level", title: 'Next Level',
description: "Reference to the next level players will play after beating this one." description: 'Reference to the next level players will play after beating this one.'
} }
scripts: c.array {title: "Scripts", description: "An array of scripts that trigger based on what the player does and affect things outside of the core level simulation.", "default": []}, ScriptSchema scripts: c.array {title: 'Scripts', description: 'An array of scripts that trigger based on what the player does and affect things outside of the core level simulation.', 'default': []}, ScriptSchema
thangs: c.array {title: "Thangs", description: "An array of Thangs that make up the level.", "default": []}, LevelThangSchema thangs: c.array {title: 'Thangs', description: 'An array of Thangs that make up the level.', 'default': []}, LevelThangSchema
systems: c.array {title: "Systems", description: "Levels are configured by changing the Systems attached to them.", uniqueItems: true, default: []}, LevelSystemSchema # TODO: uniqueness should be based on "original", not whole thing systems: c.array {title: 'Systems', description: 'Levels are configured by changing the Systems attached to them.', uniqueItems: true, default: []}, LevelSystemSchema # TODO: uniqueness should be based on 'original', not whole thing
victory: c.object {title: "Victory Screen", default: {}, properties: {'body': {type: 'string', format: 'markdown', title: 'Body Text', description: 'Inserted into the Victory Modal once this level is complete. Tell the player they did a good job and what they accomplished!'}, i18n: {type: "object", format: 'i18n', props: ['body'], description: "Help translate this victory message"}}} victory: c.object {title: 'Victory Screen', default: {}, properties: {'body': {type: 'string', format: 'markdown', title: 'Body Text', description: 'Inserted into the Victory Modal once this level is complete. Tell the player they did a good job and what they accomplished!'}, i18n: {type: 'object', format: 'i18n', props: ['body'], description: 'Help translate this victory message'}}}
i18n: {type: "object", format: 'i18n', props: ['name', 'description'], description: "Help translate this level"} i18n: {type: 'object', format: 'i18n', props: ['name', 'description'], description: 'Help translate this level'}
icon: { type: 'string', format: 'image-file', title: 'Icon' } icon: {type: 'string', format: 'image-file', title: 'Icon'}
goals: c.array {title: 'Goals', description: 'An array of goals which are visible to the player and can trigger scripts.'}, GoalSchema goals: c.array {title: 'Goals', description: 'An array of goals which are visible to the player and can trigger scripts.'}, GoalSchema
type: c.shortString(title: "Type", description: "What kind of level this is.", "enum": ['campaign', 'ladder', 'ladder-tutorial']) type: c.shortString(title: 'Type', description: 'What kind of level this is.', 'enum': ['campaign', 'ladder', 'ladder-tutorial'])
showsGuide: c.shortString(title: "Shows Guide", description: "If the guide is shown at the beginning of the level.", "enum": ['first-time', 'always']) showsGuide: c.shortString(title: 'Shows Guide', description: 'If the guide is shown at the beginning of the level.', 'enum': ['first-time', 'always'])
c.extendBasicProperties LevelSchema, 'level' c.extendBasicProperties LevelSchema, 'level'
c.extendSearchableProperties LevelSchema c.extendSearchableProperties LevelSchema

Some files were not shown because too many files have changed in this diff Show more