Merge branch 'master' into production

This commit is contained in:
Nick Winter 2014-09-17 22:37:44 -07:00
commit dca6eb6f11
58 changed files with 1490 additions and 116 deletions

View file

@ -14,7 +14,8 @@ module.exports = class CocoRouter extends Backbone.Router
Backbone.Mediator.subscribe 'router:navigate', @onNavigate, @ Backbone.Mediator.subscribe 'router:navigate', @onNavigate, @
routes: routes:
'': go('HomeView') '': go('HomeView') # This will go somewhere deprecated when FrontView is done.
'front': go('FrontView') # This will become '' when it is done.
'about': go('AboutView') 'about': go('AboutView')
@ -73,7 +74,8 @@ module.exports = class CocoRouter extends Backbone.Router
'multiplayer': go('MultiplayerView') 'multiplayer': go('MultiplayerView')
'play': go('play/MainPlayView') 'play': go('play/MainPlayView') # This will become 'play-old' or something.
'play-hero': go('play/WorldMapView') # This will become 'play' when it is done.
'play/ladder/:levelID': go('play/ladder/LadderView') 'play/ladder/:levelID': go('play/ladder/LadderView')
'play/ladder': go('play/ladder/MainLadderView') 'play/ladder': go('play/ladder/MainLadderView')
'play/level/:levelID': go('play/level/PlayLevelView') 'play/level/:levelID': go('play/level/PlayLevelView')

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

View file

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View file

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View file

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View file

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

View file

@ -202,7 +202,7 @@ module.exports = class LevelLoader extends CocoClass
thangsToLoad = _.uniq( (t.spriteName for t in @world.thangs when t.exists) ) thangsToLoad = _.uniq( (t.spriteName for t in @world.thangs when t.exists) )
nameModelTuples = ([thangType.get('name'), thangType] for thangType in @thangNames.models) nameModelTuples = ([thangType.get('name'), thangType] for thangType in @thangNames.models)
nameModelMap = _.zipObject nameModelTuples nameModelMap = _.zipObject nameModelTuples
@spriteSheetsToBuild = [] @spriteSheetsToBuild ?= []
for thangTypeName in thangsToLoad for thangTypeName in thangsToLoad
thangType = nameModelMap[thangTypeName] thangType = nameModelMap[thangTypeName]
@ -230,7 +230,7 @@ module.exports = class LevelLoader extends CocoClass
buildLoop: => buildLoop: =>
someLeft = false someLeft = false
for spriteSheetResource, i in @spriteSheetsToBuild for spriteSheetResource, i in @spriteSheetsToBuild ? []
continue if spriteSheetResource.spriteSheetKeys continue if spriteSheetResource.spriteSheetKeys
someLeft = true someLeft = true
thangType = spriteSheetResource.thangType thangType = spriteSheetResource.thangType

View file

@ -79,6 +79,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
@handledDisplayEvents = {} @handledDisplayEvents = {}
@age = 0 @age = 0
@stillLoading = true @stillLoading = true
@setNameLabel @thang.id if @thang?.showsName and not @thang.health <= 0
if @thangType.isFullyLoaded() if @thangType.isFullyLoaded()
@setUpSprite() @setUpSprite()
else else
@ -488,6 +489,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
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
@hadWallGrid = true
action = '' action = ''
tileSize = 4 tileSize = 4
[gx, gy] = [@thang.pos.x, @thang.pos.y] [gx, gy] = [@thang.pos.x, @thang.pos.y]
@ -514,6 +516,8 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
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 if @hadWallGrid
return null
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)
@ -527,13 +531,15 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
relatedActions[direction] relatedActions[direction]
updateStats: -> updateStats: ->
if bar = @healthBar return unless @thang and @thang.health isnt @lastHealth
return if @thang.health is @lastHealth
@lastHealth = @thang.health @lastHealth = @thang.health
if bar = @healthBar
healthPct = Math.max(@thang.health / @thang.maxHealth, 0) healthPct = Math.max(@thang.health / @thang.maxHealth, 0)
bar.scaleX = healthPct / bar.baseScale bar.scaleX = healthPct / bar.baseScale
healthOffset = @getOffset 'aboveHead' healthOffset = @getOffset 'aboveHead'
[bar.x, bar.y] = [healthOffset.x - bar.width / 2, healthOffset.y] [bar.x, bar.y] = [healthOffset.x - bar.width / 2, healthOffset.y]
if @thang.showsName
@setNameLabel(if @thang.health <= 0 then '' else @thang.id)
configureMouse: -> configureMouse: ->
@imageObject.cursor = 'pointer' if @thang?.isSelectable @imageObject.cursor = 'pointer' if @thang?.isSelectable

View file

@ -22,7 +22,7 @@ module.exports = class RegionChooser extends CocoClass
onMouseMove: (e) => onMouseMove: (e) =>
return unless @firstPoint return unless @firstPoint
@secondPoint = @options.camera.screenToWorld {x: e.stageX, y: e.stageY} @secondPoint = @options.camera.screenToWorld {x: e.stageX, y: e.stageY}
@restrictRegion() if @options.restrictRatio @restrictRegion() if @options.restrictRatio or key.alt
@updateShape() @updateShape()
onMouseUp: (e) => onMouseUp: (e) =>

View file

@ -186,11 +186,11 @@ module.exports = class SpriteBoss extends CocoClass
sprite.update frameChanged for sprite in @spriteArray sprite.update frameChanged for sprite in @spriteArray
@updateSelection() @updateSelection()
@spriteLayers['Default'].updateLayerOrder() @spriteLayers['Default'].updateLayerOrder()
@cache() @cacheObstacles()
adjustSpriteExistence: -> adjustSpriteExistence: ->
# Add anything new, remove anything old, update everything current # Add anything new, remove anything old, update everything current
updateCache = false updatedObstacles = []
itemsJustEquipped = [] itemsJustEquipped = []
for thang in @world.thangs when thang.exists and thang.pos for thang in @world.thangs when thang.exists and thang.pos
itemsJustEquipped = itemsJustEquipped.concat @equipNewItems thang itemsJustEquipped = itemsJustEquipped.concat @equipNewItems thang
@ -199,16 +199,16 @@ 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'] updatedObstacles.push sprite if sprite.imageObject.parent is @spriteLayers['Obstacle']
sprite.playSounds() sprite.playSounds()
item.modifyStats() for item in itemsJustEquipped item.modifyStats() for item in itemsJustEquipped
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)) updatedObstacles.push sprite if isObstacle and (missing or sprite.hasMoved)
sprite.hasMoved = false sprite.hasMoved = false
@removeSprite sprite if missing @removeSprite sprite if missing
@cache true if updateCache and @cached @cacheObstacles updatedObstacles if updatedObstacles.length and @cachedObstacles
# mainly for handling selecting thangs from session when the thang is not always in existence # mainly for handling selecting thangs from session when the thang is not always in existence
if @willSelectThang and @sprites[@willSelectThang[0]] if @willSelectThang and @sprites[@willSelectThang[0]]
@ -229,25 +229,26 @@ module.exports = class SpriteBoss extends CocoClass
itemsJustEquipped.push item itemsJustEquipped.push item
return itemsJustEquipped return itemsJustEquipped
cache: (update=false) -> cacheObstacles: (updatedObstacles=null) ->
return if @cached and not update return if @cachedObstacles and not updatedObstacles
wallSprites = (sprite for sprite in @spriteArray when sprite.thangType?.get('name').search(/(dungeon|indoor).wall/i) isnt -1) wallSprites = (sprite for sprite in @spriteArray when sprite.thangType?.get('name').search(/(dungeon|indoor).wall/i) isnt -1)
return if _.any (s.stillLoading for s in wallSprites) return if _.any (s.stillLoading for s in wallSprites)
walls = (sprite.thang for sprite in wallSprites) walls = (sprite.thang for sprite in wallSprites)
@world.calculateBounds() @world.calculateBounds()
wallGrid = new Grid walls, @world.size()... wallGrid = new Grid walls, @world.size()...
for wallSprite in wallSprites if updatedObstacles
possiblyUpdatedWallSprites = (sprite for sprite in wallSprites when _.find updatedObstacles, (w2) -> sprite is w2 or (Math.abs(sprite.thang.pos.x - w2.thang.pos.x) + Math.abs(sprite.thang.pos.y - w2.thang.pos.y)) <= 16)
else
possiblyUpdatedWallSprites = wallSprites
#console.log 'updating up to', possiblyUpdatedWallSprites.length, 'of', wallSprites.length, 'wall sprites from updatedObstacles', updatedObstacles
for wallSprite in possiblyUpdatedWallSprites
wallSprite.updateActionDirection wallGrid wallSprite.updateActionDirection wallGrid
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 @cachedObstacles = true
# @spriteLayers['Land'].uncache() if @spriteLayers['Land'].cacheID # might have changed sizes
# @spriteLayers['Land'].cache()
# I don't notice much difference - Scott
@cached = true
spriteFor: (thangID) -> @sprites[thangID] spriteFor: (thangID) -> @sprites[thangID]

View file

@ -488,7 +488,7 @@ module.exports = Surface = class Surface extends CocoClass
#- Canvas callbacks #- Canvas callbacks
onResize: (e) => onResize: (e) =>
return if @destroyed return if @destroyed or @options.choosing
oldWidth = parseInt @canvas.attr('width'), 10 oldWidth = parseInt @canvas.attr('width'), 10
oldHeight = parseInt @canvas.attr('height'), 10 oldHeight = parseInt @canvas.attr('height'), 10
aspectRatio = oldWidth / oldHeight aspectRatio = oldWidth / oldHeight

View file

@ -139,7 +139,7 @@ module.exports = class GoalManager extends CocoClass
@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?.targets, goal.keepFromCollectingThangs?.targets], 'collected')
@goalStates[goal.id] = state @goalStates[goal.id] = state
onThangDied: (e, frameNumber) -> onThangDied: (e, frameNumber) ->
@ -169,23 +169,23 @@ module.exports = class GoalManager extends CocoClass
onThangLeftMap: (e, frameNumber) -> onThangLeftMap: (e, frameNumber) ->
for goal in @goals ? [] for goal in @goals ? []
@checkLeft(goal.id, goal.leaveOffSides.who, goal.leaveOffSides.sides, e.thang.id, e.side, frameNumber) if goal.leaveOffSides? @checkLeft(goal.id, goal.leaveOffSides.who, goal.leaveOffSides.sides, e.thang, e.side, frameNumber) if goal.leaveOffSides?
@checkLeft(goal.id, goal.keepFromLeavingOffSides.who, goal.keepFromLeavingOffSides.sides, e.thang.id, e.side, frameNumber) if goal.keepFromLeavingOffSides? @checkLeft(goal.id, goal.keepFromLeavingOffSides.who, goal.keepFromLeavingOffSides.sides, e.thang, e.side, frameNumber) if goal.keepFromLeavingOffSides?
checkLeft: (goalID, who, sides, thangID, side, frameNumber) -> checkLeft: (goalID, who, sides, thang, side, frameNumber) ->
return if sides and side and not (side in sides) return if sides and side and not (side in sides)
return unless thangID in who return unless thang.id in who or thang.team in who
@updateGoalState(goalID, thangID, 'left', frameNumber) @updateGoalState(goalID, thang.id, 'left', frameNumber)
onThangCollectedItem: (e, frameNumber) -> onThangCollectedItem: (e, frameNumber) ->
for goal in @goals ? [] for goal in @goals ? []
@checkCollected(goal.id, goal.collectThangs.who, goal.collectThangs.targets, e.actor.id, e.item.id, frameNumber) if goal.collectThangs? @checkCollected(goal.id, goal.collectThangs.who, goal.collectThangs.targets, e.actor, e.item.id, frameNumber) if goal.collectThangs?
@checkCollected(goal.id, goal.keepFromCollectingThangs.who, goal.keepFromCollectingThangs.targets, e.actor.id, e.item.id, frameNumber) if goal.keepFromCollectingThangs? @checkCollected(goal.id, goal.keepFromCollectingThangs.who, goal.keepFromCollectingThangs.targets, e.actor, e.item.id, frameNumber) if goal.keepFromCollectingThangs?
checkCollected: (goalID, who, targets, thangID, itemID, frameNumber) -> checkCollected: (goalID, who, targets, thang, itemID, frameNumber) ->
return unless itemID in targets return unless itemID in targets
return unless thangID in who return unless thang.id in who or thang.team in who
@updateGoalState(goalID, thangID, 'collected', frameNumber) @updateGoalState(goalID, itemID, 'collected', frameNumber)
wrapUpGoalStates: (finalFrame) -> wrapUpGoalStates: (finalFrame) ->
for goalID, state of @goalStates for goalID, state of @goalStates

View file

@ -131,6 +131,19 @@
spectate: "Spectate" spectate: "Spectate"
players: "players" players: "players"
hours_played: "hours played" hours_played: "hours played"
items: "Items"
heroes: "Heroes"
achievements: "Achievements"
account: "Account"
settings: "Settings"
items:
armor: "Armor"
hands: "Hands"
accessories: "Accessories"
books: "Books"
minions: "Minions"
misc: "Misc"
contact: contact:
contact_us: "Contact CodeCombat" contact_us: "Contact CodeCombat"
@ -878,7 +891,6 @@
tutorial_play_first: "Play the Tutorial first." tutorial_play_first: "Play the Tutorial first."
simple_ai: "Simple AI" simple_ai: "Simple AI"
warmup: "Warmup" warmup: "Warmup"
vs: "VS"
friends_playing: "Friends Playing" friends_playing: "Friends Playing"
log_in_for_friends: "Log in to play with your friends!" log_in_for_friends: "Log in to play with your friends!"
social_connect_blurb: "Connect and play against your friends!" social_connect_blurb: "Connect and play against your friends!"

View file

@ -62,7 +62,7 @@ class CocoModel extends Backbone.Model
super(attribute) super(attribute)
set: (attributes, options) -> set: (attributes, options) ->
delete @attributesWithDefaults delete @attributesWithDefaults unless attributes is 'thangs' # unless attributes is 'thangs': performance optimization for Levels keeping their cache.
inFlux = @loading or not @loaded inFlux = @loading or not @loaded
@markToRevert() unless inFlux or @_revertAttributes or @project or options?.fromMerge @markToRevert() unless inFlux or @_revertAttributes or @project or options?.fromMerge
res = super attributes, options res = super attributes, options
@ -125,8 +125,12 @@ class CocoModel extends Backbone.Model
error(@, res) if error error(@, res) if error
return unless @notyErrors return unless @notyErrors
errorMessage = "Error saving #{@get('name') ? @type()}" errorMessage = "Error saving #{@get('name') ? @type()}"
console.error errorMessage, res.responseJSON console.log 'going to log an error message'
console.warn errorMessage, res.responseJSON
try
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
catch notyError
console.warn "Couldn't even show noty error for", error, "because", notyError
@trigger 'save', @ @trigger 'save', @
return super attrs, options return super attrs, options

View file

@ -170,14 +170,31 @@ module.exports = class Level extends CocoModel
thang.components = sorted thang.components = sorted
fillInDefaultComponentConfiguration: (thangs, levelComponents) -> fillInDefaultComponentConfiguration: (thangs, levelComponents) ->
# This is slow, so I inserted some optimizations to speed it up by caching the eventual defaults of commonly-used Components.
@defaultComponentConfigurations ?= {}
cached = 0
missed = 0
cachedConfigs = 0
for thang in thangs ? [] for thang in thangs ? []
for component in thang.components or [] for component in thang.components or []
isPhysical = component.original is LevelComponent.PhysicalID
if not isPhysical and defaultConfiguration = _.find @defaultComponentConfigurations[component.original], ((d) -> _.isEqual component, d.originalComponent)
component.config = defaultConfiguration.defaultedConfig
++cached
continue
continue unless lc = _.find levelComponents, {original: component.original} continue unless lc = _.find levelComponents, {original: component.original}
unless isPhysical
originalComponent = $.extend true, {}, component
component.config ?= {} component.config ?= {}
TreemaUtils.populateDefaults(component.config, lc.configSchema, tv4) TreemaUtils.populateDefaults(component.config, lc.configSchema ? {}, tv4)
@lastType = 'component' @lastType = 'component'
@lastOriginal = component.original @lastOriginal = component.original
@walkDefaults component.config, lc.configSchema.properties unless isPhysical
@defaultComponentConfigurations[component.original] ?= []
@defaultComponentConfigurations[component.original].push originalComponent: originalComponent, defaultedConfig: component.config
++cachedConfigs
++missed
#console.log 'cached', cached, 'missed', missed
fillInDefaultSystemConfiguration: (levelSystems) -> fillInDefaultSystemConfiguration: (levelSystems) ->
for system in levelSystems ? [] for system in levelSystems ? []
@ -185,21 +202,6 @@ module.exports = class Level extends CocoModel
TreemaUtils.populateDefaults(system.config, system.model.configSchema, tv4) TreemaUtils.populateDefaults(system.config, system.model.configSchema, tv4)
@lastType = 'system' @lastType = 'system'
@lastOriginal = system.model.name @lastOriginal = system.model.name
@walkDefaults system.config, system.model.configSchema.properties
walkDefaults: (config, properties) ->
# This function is redundant, but is the old implementation.
# Remove it and calls to it once we stop seeing these warnings.
return unless properties
for prop, schema of properties
if schema.default? and config[prop] is undefined
console.warn 'Setting default of', config, 'for', prop, 'to', schema.default, 'but this method is deprecated... check your config schema!', @lastType, @lastOriginal
config[prop] = schema.default
if schema.type is 'object' and config[prop]
@walkDefaults config[prop], schema.properties
else if schema.type is 'array' and config[prop]
for item in config[prop] or []
@walkDefaults item, schema.items
dimensions: -> dimensions: ->
width = 0 width = 0

View file

@ -104,7 +104,7 @@ NoteGroupSchema = c.object {title: 'Note Group', description: 'A group of notes
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.', format: 'radians'}
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.'}

View file

@ -102,7 +102,7 @@ DependencySchema = c.object {
LevelComponentSchema = c.object { LevelComponentSchema = c.object {
title: 'Component' title: 'Component'
description: 'A Component which can affect Thang behavior.' description: 'A Component which can affect Thang behavior.'
required: ['system', 'name', 'description', 'code', 'dependencies', 'propertyDocumentation', 'codeLanguage'] required: ['system', 'name', 'code']
default: default:
system: 'ai' system: 'ai'
name: 'AttacksSelf' name: 'AttacksSelf'

View file

@ -24,6 +24,10 @@ module.exports =
'editor:edit-level-thang': c.object {required: ['thangID']}, 'editor:edit-level-thang': c.object {required: ['thangID']},
thangID: {type: 'string'} thangID: {type: 'string'}
'editor:level-thang-edited': c.object {required: ['thangData', 'oldPath']},
thangData: {type: 'object'}
oldPath: {type: 'string'}
'editor:level-thang-done-editing': c.object {required: ['thangData', 'oldPath']}, 'editor:level-thang-done-editing': c.object {required: ['thangData', 'oldPath']},
thangData: {type: 'object'} thangData: {type: 'object'}
oldPath: {type: 'string'} oldPath: {type: 'string'}

View file

@ -0,0 +1,30 @@
@import "bootstrap/mixins"
@import "bootstrap/variables"
#front-view
h1
text-align: center
margin-top: 0
.platform-choices
a
text-align: center
.panel
@include transition(background-color 0.5s ease)
&:hover
text-decoration: none
.panel
background-color: rgb(230, 230, 255)
.platform-ios
img
transform: scaleY(-1)
@media only screen and (max-width: 800px)
#front-view
#site-slogan
font-size: 30px
margin-bottom: 30px

View file

@ -47,3 +47,17 @@
color: black color: black
text-shadow: 0 1px 0 white text-shadow: 0 1px 0 white
.modal.play-modal
.modal-header
border: 0
text-align: center
padding: 0
margin: 0 25px
h3
margin-bottom: 0
.modal-body
padding-top: 0

View file

@ -16,8 +16,7 @@
height: 100px height: 100px
overflow: hidden overflow: hidden
background: white background: white
border: 1px solid #333 border-radius: 8px
border-radius: 5px
position: relative position: relative
-webkit-transition: opacity 0.3s ease-in-out -webkit-transition: opacity 0.3s ease-in-out
-moz-transition: opacity 0.3s ease-in-out -moz-transition: opacity 0.3s ease-in-out
@ -46,15 +45,15 @@
.my-team-icon .my-team-icon
height: 60px height: 60px
position: relative position: relative
top: -10px top: -3.5px
left: 10px left: 13.5px
z-index: 0 z-index: 0
.opponent-team-icon .opponent-team-icon
height: 60px height: 60px
position: relative position: relative
top: 10px top: 16.5px
right: 10px right: 13.5px
z-index: 0 z-index: 0
float: right float: right
-moz-transform: scaleX(-1) -moz-transform: scaleX(-1)
@ -78,19 +77,19 @@
z-index: 1 z-index: 1
.name-label .name-label
border-bottom: 20px solid lightslategray height: 20px
height: 0
width: 40% width: 40%
position: absolute position: absolute
bottom: 0 bottom: 0
color: black color: white
font-weight: bold font-weight: bold
text-shadow: -1px -1px 0px black
text-align: center text-align: center
z-index: 2 z-index: 2
span span
position: relative position: relative
top: 1px top: 4px
.code-language .code-language
position: absolute position: absolute
@ -116,9 +115,6 @@
right: 3px right: 3px
.difficulty .difficulty
border-top: 25px solid darkgray
border-left: 20px solid transparent
border-right: 20px solid transparent
height: 0 height: 0
width: 30% width: 30%
position: absolute position: absolute
@ -131,16 +127,20 @@
span span
position: relative position: relative
top: -25px top: 6px
.easy-option .difficulty .play-option
border-top: 25px solid limegreen background-image: url(/images/pages/play/ladder/warmup_button.png)
.medium-option .difficulty .easy-option
border-top: 25px solid darkorange background-image: url(/images/pages/play/ladder/easy_button.png)
.hard-option .difficulty .medium-option
border-top: 25px solid black background-image: url(/images/pages/play/ladder/medium_button.png)
.hard-option
background-image: url(/images/pages/play/ladder/hard_button.png)
.difficulty
color: white color: white
.vs .vs

View file

@ -0,0 +1,4 @@
#play-account-modal
.account-view
color: black

View file

@ -0,0 +1,4 @@
#play-achievements-modal
.achievement-view
color: black

View file

@ -0,0 +1,4 @@
#play-heroes-modal
.hero-view
color: black

View file

@ -0,0 +1,8 @@
#play-items-modal
.item-view
width: 420px
float: left
background-color: white
border-radius: 6px
margin: 5px

View file

@ -0,0 +1,4 @@
#play-settings-modal
.settings-view
color: black

View file

@ -0,0 +1,151 @@
@import "app/styles/bootstrap/mixins"
@import "app/styles/bootstrap/variables"
$forestMapWidth: 2500
$forestMapHeight: 1536
$forestMapSeaBackground: #71bad0
$levelDotWidth: 2%
$levelDotHeight: $levelDotWidth * $forestMapWidth / $forestMapHeight
$levelDotZ: $levelDotHeight * 0.25
$levelDotHoverZ: $levelDotZ * 2
$levelDotShadowWidth: 0.8 * $levelDotWidth
$levelDotShadowHeight: 0.8 * $levelDotHeight
$levelClickRadius: 40px
$gameControlSize: 80px
$gameControlMargin: 40px
#world-map-view
width: 100%
height: 100%
background-color: $forestMapSeaBackground
.map
//background: white url("/images/pages/play/map_forest.jpg") no-repeat center center
position: relative
.map-background
width: 100%
height: 100%
.level, .level-shadow
position: absolute
border-radius: 100%
transform: scaleY(0.75)
.level
z-index: 2
width: $levelDotWidth
height: $levelDotHeight
margin-left: -0.5 * $levelDotWidth
margin-bottom: -$levelDotHeight / 3 + $levelDotZ
border: 2px groove white
@include transition(margin-bottom 0.5s ease)
&.disabled
opacity: 0.7
&.first
width: 2 * $levelDotWidth
height: 2 * $levelDotHeight
margin-left: -0.5 * 2 * $levelDotWidth
margin-bottom: -2 * $levelDotHeight / 3 + 2 * $levelDotZ
.level-shadow
z-index: 1
width: $levelDotShadowWidth
height: $levelDotShadowHeight
margin-left: -0.5 * $levelDotShadowWidth
margin-bottom: -$levelDotShadowHeight / 3
background-color: black
box-shadow: 0px 0px 10px black
@include opacity(0.75)
&.first
width: 2 * $levelDotShadowWidth
height: 2 * $levelDotShadowHeight
margin-left: -0.5 * 2 * $levelDotShadowWidth
margin-bottom: -2 * $levelDotShadowHeight / 3
.level:hover
margin-bottom: -$levelDotHeight / 3 + $levelDotHoverZ
.level
a
display: block
padding: $levelClickRadius
margin-left: -0.5 * $levelClickRadius
margin-top: -0.5 * $levelClickRadius
border-radius: $levelClickRadius
&.first a
padding: 2 * $levelClickRadius
margin-left: 2 * -0.5 * $levelClickRadius
margin-top: 2 * -0.5 * $levelClickRadius
border-radius: 2 * $levelClickRadius
.level-info-container
display: none
position: absolute
z-index: 3
padding: 10px 10px 30px 10px
border-image: url(/images/level/popover_background.png) 18 fill round
border-width: 15px
.level-image
float: left
margin-right: 20px
.level-info.complete h3:after
content: " - Complete!"
color: green
.level-info.started h3:after
content: " - Started"
color: desaturate(green, 50%)
.level-info
width: 330px
float: left
h3
margin-top: 0
margin-bottom: 0px
.level-description
color: black
text-shadow: 0 1px 0 white
.campaign-label
text-shadow: 0 1px 0 white
.game-controls
position: absolute
right: 1%
bottom: 1%
button.btn
&:not(:first-child)
margin-left: $gameControlMargin
width: $gameControlSize
height: $gameControlSize
background: url(/images/pages/play/menu_icons.png) no-repeat
background-size: cover
@include transition(0.5s ease)
box-shadow: 0 0 0 #bbf
border: 0
border-radius: 12px
&:hover
box-shadow: 0 0 12px #bbf
&:active
box-shadow: 0 0 20px white
&.heroes
background-position-x: -1 * $gameControlSize
&.achievements
background-position-x: -2 * $gameControlSize
&.account
background-position-x: -3 * $gameControlSize
&.settings
background-position-x: -4 * $gameControlSize

View file

@ -177,7 +177,7 @@ block content
if !editing && !myProfile if !editing && !myProfile
button#contact-candidate.btn.btn-large.btn-inverse.flat-button button#contact-candidate.btn.btn-large.btn-inverse.flat-button
span(data-i18n="account_profile.contact") Contact span(data-i18n="account_profile.contact") Contact
| #{profile.name.split(' ')[0]} | #{(profile.name || user.get('name') || 'Anonymous').split(' ')[0]}
if me.isAdmin() if me.isAdmin()
select#admin-contact.form-control select#admin-contact.form-control
for contact in adminContacts for contact in adminContacts

View file

@ -24,6 +24,11 @@ block modal-body-content
div.alert.alert-info div.alert.alert-info
strong Shift-drag strong Shift-drag
| to select | to select
if flexibleRegion
div.alert.alert-info
strong Alt-shift-drag
| to select ratio
else
div.alert.alert-info div.alert.alert-info
strong Enter strong Enter
| to confirm | to confirm

View file

@ -0,0 +1,28 @@
extends /templates/base
block content
.page-header
h1#site-slogan
span(data-i18n="home.slogan") Learn to Code by Playing a Game
small.spl.spr -
small free!
.row.platform-choices
.col-xs-6.platform-ios
a(href="#")
.panel.panel-default
.panel-body
h1 Play on iPad
img.img-responsive(src="/images/pages/front/play_web.jpg")
p.lead Get the app!
.col-xs-6.platform-web
a(href="/play-hero")
.panel.panel-default
.panel-body
h1 Play on web
img.img-responsive(src="/images/pages/front/play_web.jpg")
p.lead Play right now!
.clearfix

26
app/templates/front.jade Normal file
View file

@ -0,0 +1,26 @@
extends /templates/base
block content
.page-header
h1#site-slogan
span(data-i18n="home.slogan") Learn to Code by Playing a Game
small.spl.spr -
small free!
.row.platform-choices
.col-xs-6.platform-ios
a(href="#")
.panel.panel-default
.panel-body
h1 Play on iPad
em - get the app!
.col-xs-6.platform-web
a(href="/play")
.panel.panel-default
.panel-body
h1 Play on web
em - play right now!
.clearfix

View file

@ -6,11 +6,17 @@
if closeButton if closeButton
.button.close(type="button", data-dismiss="modal", aria-hidden="true") &times; .button.close(type="button", data-dismiss="modal", aria-hidden="true") &times;
block modal-header-content block modal-header-content
if headerContent
h3!= headerContent
else
h3 man bites God h3 man bites God
block modal-body block modal-body
.modal-body .modal-body
block modal-body-content block modal-body-content
if bodyContent
div!= bodyContent
else
p Man Bites God are the bad boys of the Melbourne live music and comedy scene. It is like being drowned in a bathtub of harmony. p Man Bites God are the bad boys of the Melbourne live music and comedy scene. It is like being drowned in a bathtub of harmony.
img(src="http://www.manbitesgod.com/images/picturecoupleb.jpg") img(src="http://www.manbitesgod.com/images/picturecoupleb.jpg")
img(src="http://www.manbitesgod.com/images/manrantb.jpg") img(src="http://www.manbitesgod.com/images/manrantb.jpg")

View file

@ -55,6 +55,18 @@ block content
| |
a(href="http://blog.codecombat.com/6-programming-languages-one-victor-codecombats-newest-tournament", data-i18n="ladder.tournament_blurb_blog") on our blog a(href="http://blog.codecombat.com/6-programming-languages-one-victor-codecombats-newest-tournament", data-i18n="ladder.tournament_blurb_blog") on our blog
| . | .
p
strong Tournament ended!
a(href="#winners") Behold the winners
| . Thanks for playing! You can
strong still play
| Criss-Cross and all of our other
a(href="/play/ladder") multiplayer arenas
| .
p
| Want to commiserate? Head over to
a(href="http://discourse.codecombat.com/") the forum
| and discuss your strategies, your triumphs, and your turmoils.
div#columns.row div#columns.row
div.column.col-md-2 div.column.col-md-2
@ -86,7 +98,7 @@ block content
if level.get('name') == 'Greed' if level.get('name') == 'Greed'
li li
a(href="#rules", data-toggle="tab", data-i18n="ladder.rules") Rules a(href="#rules", data-toggle="tab", data-i18n="ladder.rules") Rules
if level.get('name') == 'Greed' || (level.get('name') == 'Criss-Cross!!!') if level.get('name') == 'Greed' || (level.get('name') == 'Criss-Cross')
li li
a(href="#winners", data-toggle="tab", data-i18n="ladder.winners") Winners a(href="#winners", data-toggle="tab", data-i18n="ladder.winners") Winners
@ -725,7 +737,7 @@ block content
a(href="http://discourse.codecombat.com/") Discourse forum a(href="http://discourse.codecombat.com/") Discourse forum
| . | .
if level.get('name') == 'Greed' || level.get('name') == 'Criss-Cross!!!' if level.get('name') == 'Greed' || level.get('name') == 'Criss-Cross'
.tab-pane.well#winners .tab-pane.well#winners
h1(data-i18n="ladder.winners") Winners h1(data-i18n="ladder.winners") Winners
@ -734,9 +746,15 @@ block content
tr tr
th(data-i18n="ladder_prizes.rank") Rank th(data-i18n="ladder_prizes.rank") Rank
th Human th Human
if level.get('name') == 'Greed'
th Human wins/losses/ties th Human wins/losses/ties
else
th Human score
th Ogre th Ogre
if level.get('name') == 'Greed'
th Ogre wins/losses/ties th Ogre wins/losses/ties
else
th Ogre score
th Spectate th Spectate
tbody tbody
each human, index in winners.humans each human, index in winners.humans
@ -744,20 +762,28 @@ block content
tr tr
td= human.rank td= human.rank
td= human.name td= human.name
if level.get('name') == 'Greed'
td td
span.win= human.wins span.win= human.wins
| - | -
span.loss= human.losses span.loss= human.losses
| - | -
span.tie= 377 - human.wins - human.losses span.tie= 377 - human.wins - human.losses
else
td
span= Math.round(100 * human.score)
if ogre if ogre
td= ogre.name td= ogre.name
if level.get('name') == 'Greed'
td td
span.win= ogre.wins span.win= ogre.wins
| - | -
span.loss= ogre.losses span.loss= ogre.losses
| - | -
span.tie= 407 - ogre.wins - ogre.losses span.tie= 407 - ogre.wins - ogre.losses
else
td
span= Math.round(100 * ogre.score)
td td
a(href="/play/spectate/" + level.get('slug') + "?session-one=" + human.sessionID + "&session-two=" + ogre.sessionID) Watch the battle a(href="/play/spectate/" + level.get('slug') + "?session-one=" + human.sessionID + "&session-two=" + ogre.sessionID) Watch the battle
else else

View file

@ -37,7 +37,6 @@ block modal-body-content
//span.code-language(style="background-image: url(/images/common/code_languages/javascript_small.png)") //span.code-language(style="background-image: url(/images/common/code_languages/javascript_small.png)")
div.difficulty div.difficulty
span(data-i18n="ladder.warmup") Warmup span(data-i18n="ladder.warmup") Warmup
div(data-i18n="ladder.vs").vs VS
if challengers.easy if challengers.easy
a(href="/play/level/#{levelID}?team=#{teamID}&opponent=#{challengers.easy.sessionID}") a(href="/play/level/#{levelID}?team=#{teamID}&opponent=#{challengers.easy.sessionID}")
@ -54,7 +53,6 @@ block modal-body-content
span.code-language(style="background-image: url(/images/common/code_languages/" + challengers.easy.codeLanguage + "_small.png)") span.code-language(style="background-image: url(/images/common/code_languages/" + challengers.easy.codeLanguage + "_small.png)")
div.difficulty div.difficulty
span(data-i18n="general.easy") Easy span(data-i18n="general.easy") Easy
div(data-i18n="ladder.vs").vs VS
if challengers.medium if challengers.medium
a(href="/play/level/#{levelID}?team=#{teamID}&opponent=#{challengers.medium.sessionID}") a(href="/play/level/#{levelID}?team=#{teamID}&opponent=#{challengers.medium.sessionID}")
@ -71,7 +69,6 @@ block modal-body-content
span.code-language(style="background-image: url(/images/common/code_languages/" + challengers.medium.codeLanguage + "_small.png)") span.code-language(style="background-image: url(/images/common/code_languages/" + challengers.medium.codeLanguage + "_small.png)")
div.difficulty div.difficulty
span(data-i18n="general.medium") Medium span(data-i18n="general.medium") Medium
div(data-i18n="ladder.vs").vs VS
if challengers.hard if challengers.hard
a(href="/play/level/#{levelID}?team=#{teamID}&opponent=#{challengers.hard.sessionID}") a(href="/play/level/#{levelID}?team=#{teamID}&opponent=#{challengers.hard.sessionID}")
@ -88,6 +85,5 @@ block modal-body-content
span.code-language(style="background-image: url(/images/common/code_languages/" + challengers.hard.codeLanguage + "_small.png)") span.code-language(style="background-image: url(/images/common/code_languages/" + challengers.hard.codeLanguage + "_small.png)")
div.difficulty div.difficulty
span(data-i18n="general.hard") Hard span(data-i18n="general.hard") Hard
div(data-i18n="ladder.vs").vs VS
block modal-footer block modal-footer

View file

@ -0,0 +1,7 @@
extends /templates/modal/modal_base
block modal-header-content
h3(data-i18n="play.account") Account
block modal-body-content
p TODO: show all dem account

View file

@ -0,0 +1,7 @@
extends /templates/modal/modal_base
block modal-header-content
h3(data-i18n="play.achievements") Achievements
block modal-body-content
p TODO: show all dem achievements

View file

@ -0,0 +1,7 @@
extends /templates/modal/modal_base
block modal-header-content
h3(data-i18n="play.heroes") Heroes
block modal-body-content
p TODO: show all dem heroes

View file

@ -0,0 +1,17 @@
extends /templates/modal/modal_base
block modal-header-content
h3(data-i18n="play.items") Items
block modal-body-content
ul.nav.nav-tabs
for slotGroup, index in slotGroupsArray
li(class=index ? "" : "active")
a(href="#slot-group-" + slotGroup, data-toggle="tab")
span= slotGroupsNames[index]
.tab-content
for slotGroup, index in slotGroupsArray
.tab-pane(id="slot-group-" + slotGroup, class=index ? "" : "active")
h3 buy some #{slotGroupsNames[index]} yo:
for item in slotGroups[slotGroup]
.replace-me(data-item-id=item.id)

View file

@ -0,0 +1,7 @@
extends /templates/modal/modal_base
block modal-header-content
h3(data-i18n="play.settings") Settings
block modal-body-content
p TODO: show all dem settings

View file

@ -0,0 +1,34 @@
.map
img.map-background(src="/images/pages/play/map_forest.jpg", alt="")
each campaign in campaigns
each level in campaign.levels
div(style="left: #{level.x}%; bottom: #{level.y}%; background-color: #{campaign.color}", class="level" + (level.first ? " first" : "") + (level.disabled ? " disabled" : ""), data-level-id=level.id)
a(href=level.disabled ? "/play" : "/play/#{level.levelPath || 'level'}/#{level.id}", disabled=level.disabled, class=levelStatusMap[level.id] || "")
div(style="left: #{level.x}%; bottom: #{level.y}%", class="level-shadow" + (level.first ? " first" : ""))
.level-info-container(data-level-id=level.id)
if level.image
img.level-image(src="#{level.image}", alt="#{level.name}")
else
img.level-image(src="/images/generic-icon.png", alt="#{level.name}")
div(class="level-info " + (levelStatusMap[level.id] || ""))
h3= level.name + (level.disabled ? " (Coming soon!)" : "")
.level-description= level.description
span(data-i18n="play.level_difficulty") Difficulty:
each i in Array(level.difficulty)
i.icon-star
- var playCount = levelPlayCountMap[level.id]
if playCount
div
span.spr #{playCount.sessions}
span(data-i18n="play.players") players
span.spr , #{Math.round(playCount.playtime / 3600)}
span(data-i18n="play.hours_played") hours played
.campaign-label(style="color: #{campaign.color}")= campaign.name
.game-controls
button.btn.items(data-toggle='coco-modal', data-target='play/modal/PlayItemsModal', data-i18n="[title]play.items")
button.btn.heroes(data-toggle='coco-modal', data-target='play/modal/PlayHeroesModal', data-i18n="[title]play.heroes")
button.btn.achievements(data-toggle='coco-modal', data-target='play/modal/PlayAchievementsModal', data-i18n="[title]play.achievements")
button.btn.account(data-toggle='coco-modal', data-target='play/modal/PlayAccountModal', data-i18n="[title]play.account")
button.btn.settings(data-toggle='coco-modal', data-target='play/modal/PlaySettingsModal', data-i18n="[title]play.settings")

View file

@ -0,0 +1,34 @@
RootView = require 'views/kinds/RootView'
template = require 'templates/front-view'
{me} = require '/lib/auth'
ModalView = require 'views/kinds/ModalView'
module.exports = class FrontView extends RootView
id: 'front-view'
template: template
events:
'click .platform-ios a': 'onIOSClicked'
getRenderData: ->
c = super()
if $.browser
majorVersion = $.browser.versionNumber
c.isOldBrowser = true if $.browser.mozilla && majorVersion < 21
c.isOldBrowser = true if $.browser.chrome && majorVersion < 17
c.isOldBrowser = true if $.browser.safari && majorVersion < 6
else
console.warn 'no more jquery browser version...'
c
afterRender: ->
super()
onIOSClicked: (e) ->
header = 'Sorry, the iPad app isn\'t ready yet'
body = '''
<p class="lead">We are working on it!</p>
<p>For now, try playing on the web, and totally sign up (with emails enabled) so you can be the first to hear when it is ready.</p>
'''
notImplementedModal = new ModalView headerContent: header, bodyContent: body
@openModalView notImplementedModal

View file

@ -26,7 +26,7 @@ module.exports = class NewLevelComponentModal extends ModalView
component = new LevelComponent() component = new LevelComponent()
component.set 'system', system component.set 'system', system
component.set 'name', name component.set 'name', name
component.set 'code', component.get('code').replace(/AttacksSelf/g, name) component.set 'code', component.get('code', true).replace(/AttacksSelf/g, name)
component.set 'permissions', [{access: 'owner', target: me.id}] # Private until saved in a published Level component.set 'permissions', [{access: 'owner', target: me.id}] # Private until saved in a published Level
res = component.save() res = component.save()
return unless res return unless res

View file

@ -31,11 +31,11 @@ module.exports = class WorldSelectModal extends ModalView
getRenderData: (c={}) => getRenderData: (c={}) =>
c = super(c) c = super(c)
c.selectingPoint = @dataType is 'point' c.selectingPoint = @dataType is 'point'
c.flexibleRegion = @dataType is 'region'
c c
afterInsert: -> afterInsert: ->
super() super()
window.e = @$el
@initSurface() @initSurface()
# surface setup # surface setup
@ -54,7 +54,6 @@ module.exports = class WorldSelectModal extends ModalView
thangTypes: @supermodel.getModels(ThangType) thangTypes: @supermodel.getModels(ThangType)
showInvisible: true showInvisible: true
} }
window.s = @surface
@surface.playing = false @surface.playing = false
@surface.setWorld @world @surface.setWorld @world
@surface.camera.zoomTo({x: 262, y: -164}, 1.66, 0) @surface.camera.zoomTo({x: 262, y: -164}, 1.66, 0)

View file

@ -19,6 +19,8 @@ module.exports = class LevelThangEditView extends CocoView
'click #thang-type-link span': 'toggleTypeEdit' 'click #thang-type-link span': 'toggleTypeEdit'
'blur #thang-name-link input': 'toggleNameEdit' 'blur #thang-name-link input': 'toggleNameEdit'
'blur #thang-type-link input': 'toggleTypeEdit' 'blur #thang-type-link input': 'toggleTypeEdit'
'keydown #thang-name-link input': 'toggleNameEditIfReturn'
'keydown #thang-type-link input': 'toggleTypeEditIfReturn'
constructor: (options) -> constructor: (options) ->
options ?= {} options ?= {}
@ -27,6 +29,7 @@ module.exports = class LevelThangEditView extends CocoView
@thangData = $.extend true, {}, options.thangData ? {} @thangData = $.extend true, {}, options.thangData ? {}
@level = options.level @level = options.level
@oldPath = options.oldPath @oldPath = options.oldPath
@reportChanges = _.debounce @reportChanges, 1000
getRenderData: (context={}) -> getRenderData: (context={}) ->
context = super(context) context = super(context)
@ -83,5 +86,16 @@ module.exports = class LevelThangEditView extends CocoView
if thangType and wasEditing if thangType and wasEditing
@thangData.thangType = thangType.get('original') @thangData.thangType = thangType.get('original')
toggleNameEditIfReturn: (e) ->
@$el.find('#thang-name-link input').blur() if e.which is 13
toggleTypeEditIfReturn: (e) ->
@$el.find('#thang-type-link input').blur() if e.which is 13
onComponentsChanged: (components) => onComponentsChanged: (components) =>
@thangData.components = components @thangData.components = components
@reportChanges()
reportChanges: =>
return if @destroyed
Backbone.Mediator.publish 'editor:level-thang-edited', {thangData: $.extend(true, {}, @thangData), oldPath: @oldPath}

View file

@ -16,7 +16,7 @@ MOVE_MARGIN = 0.15
MOVE_SPEED = 13 MOVE_SPEED = 13
# Let us place these on top of other Thangs # Let us place these on top of other Thangs
overlappableThangTypeNames = ['Torch', 'Chains', 'Bird', 'Cloud 1', 'Cloud 2', 'Cloud 3', 'Waterfall', 'Obstacle'] overlappableThangTypeNames = ['Torch', 'Chains', 'Bird', 'Cloud 1', 'Cloud 2', 'Cloud 3', 'Waterfall', 'Obstacle', 'Electrowall']
class ThangTypeSearchCollection extends CocoCollection class ThangTypeSearchCollection extends CocoCollection
url: '/db/thang.type?project=original,name,version,slug,kind,components' url: '/db/thang.type?project=original,name,version,slug,kind,components'
@ -33,12 +33,14 @@ module.exports = class ThangsTabView extends CocoView
'surface:mouse-over': 'onSurfaceMouseOver' 'surface:mouse-over': 'onSurfaceMouseOver'
'surface:mouse-out': 'onSurfaceMouseOut' 'surface:mouse-out': 'onSurfaceMouseOut'
'editor:edit-level-thang': 'editThang' 'editor:edit-level-thang': 'editThang'
'editor:level-thang-edited': 'onLevelThangEdited'
'editor:level-thang-done-editing': 'onLevelThangDoneEditing' 'editor:level-thang-done-editing': 'onLevelThangDoneEditing'
'editor:view-switched': 'onViewSwitched' 'editor:view-switched': 'onViewSwitched'
'sprite:dragged': 'onSpriteDragged' 'sprite:dragged': 'onSpriteDragged'
'sprite:mouse-up': 'onSpriteMouseUp' 'sprite:mouse-up': 'onSpriteMouseUp'
'sprite:mouse-down': 'onSpriteMouseDown' 'sprite:mouse-down': 'onSpriteMouseDown'
'sprite:double-clicked': 'onSpriteDoubleClicked' 'sprite:double-clicked': 'onSpriteDoubleClicked'
'surface:stage-mouse-down': 'onStageMouseDown'
'surface:stage-mouse-up': 'onStageMouseUp' 'surface:stage-mouse-up': 'onStageMouseUp'
'editor:random-terrain-generated': 'onRandomTerrainGenerated' 'editor:random-terrain-generated': 'onRandomTerrainGenerated'
@ -225,8 +227,17 @@ module.exports = class ThangsTabView extends CocoView
# if e.originalEvent.nativeEvent.button == 2 # if e.originalEvent.nativeEvent.button == 2
# @onSpriteContextMenu e # @onSpriteContextMenu e
onStageMouseDown: (e) ->
return unless @addThangSprite?.thangType.get('kind') is 'Wall'
@surface.camera.dragDisabled = true
@paintingWalls = true
onStageMouseUp: (e) -> onStageMouseUp: (e) ->
if @addThangSprite if @paintingWalls
# We need to stop painting walls, but we may also stop in onExtantThangSelected.
_.defer =>
@paintingWalls = @paintedWalls = @surface.camera.dragDisabled = false
else if @addThangSprite
@surface.camera.lock() @surface.camera.lock()
# If we click on the background, we need to add @addThangSprite, but not if onSpriteMouseUp will fire. # If we click on the background, we need to add @addThangSprite, but not if onSpriteMouseUp will fire.
@backgroundAddClickTimeout = _.defer => @onExtantThangSelected {} @backgroundAddClickTimeout = _.defer => @onExtantThangSelected {}
@ -295,7 +306,12 @@ module.exports = class ThangsTabView extends CocoView
@selectedExtantSprite?.setNameLabel? null unless @selectedExtantSprite is e.sprite @selectedExtantSprite?.setNameLabel? null unless @selectedExtantSprite is e.sprite
@selectedExtantThang = e.thang @selectedExtantThang = e.thang
@selectedExtantSprite = e.sprite @selectedExtantSprite = e.sprite
if e.thang and (key.alt or key.meta) paintedAWall = @paintedWalls
@paintingWalls = @paintedWalls = @surface.camera.dragDisabled = false
if paintedAWall
# Skip adding a wall now, because we already dragged to add one
null
else if e.thang and (key.alt or key.meta)
# We alt-clicked, so create a clone addThang # We alt-clicked, so create a clone addThang
@selectAddThangType e.thang.spriteName, @selectedExtantThang @selectAddThangType e.thang.spriteName, @selectedExtantThang
else if @justAdded() else if @justAdded()
@ -389,6 +405,16 @@ module.exports = class ThangsTabView extends CocoView
wop = @surface.camera.screenToWorld x: e.x, y: e.y wop = @surface.camera.screenToWorld x: e.x, y: e.y
wop.z = 0.5 wop.z = 0.5
@adjustThangPos @addThangSprite, @addThangSprite.thang, wop @adjustThangPos @addThangSprite, @addThangSprite.thang, wop
if @paintingWalls
unless _.find @surface.spriteBoss.spriteArray, ((sprite) =>
sprite.thangType.get('kind') is 'Wall' and
Math.abs(sprite.thang.pos.x - @addThangSprite.thang.pos.x) < 2 and
Math.abs(sprite.thang.pos.y - @addThangSprite.thang.pos.y) < 2 and
sprite isnt @addThangSprite
)
@addThang @addThangType, @addThangSprite.thang.pos
@lastAddTime = new Date()
@paintedWalls = true
null null
onSurfaceMouseOver: (e) -> onSurfaceMouseOver: (e) ->
@ -427,6 +453,7 @@ module.exports = class ThangsTabView extends CocoView
deleteSelectedExtantThang: (e) => deleteSelectedExtantThang: (e) =>
return if $(e.target).hasClass 'treema-node' return if $(e.target).hasClass 'treema-node'
return unless @selectedExtantThang
thang = @getThangByID(@selectedExtantThang.id) thang = @getThangByID(@selectedExtantThang.id)
@thangsTreema.delete(@pathForThang(thang)) @thangsTreema.delete(@pathForThang(thang))
Thang.resetThangIDs() # TODO: find some way to do this when we delete from treema, too Thang.resetThangIDs() # TODO: find some way to do this when we delete from treema, too
@ -547,14 +574,19 @@ module.exports = class ThangsTabView extends CocoView
onLevelThangDoneEditing: (e) -> onLevelThangDoneEditing: (e) ->
@removeSubView @editThangView @removeSubView @editThangView
@editThangView = null @editThangView = null
newThang = e.thangData @updateEditedThang e.thangData, e.oldPath
@$el.find('>').show()
onLevelThangEdited: (e) ->
@updateEditedThang e.thangData, e.oldPath
updateEditedThang: (newThang, oldPath) ->
@hush = true @hush = true
@thangsTreema.delete e.oldPath @thangsTreema.delete oldPath
@populateFoldersForThang(newThang) @populateFoldersForThang(newThang)
@thangsTreema.set(@pathForThang(newThang), newThang) @thangsTreema.set(@pathForThang(newThang), newThang)
@hush = false @hush = false
@onThangsChanged() @onThangsChanged()
@$el.find('>').show()
preventDefaultContextMenu: (e) -> preventDefaultContextMenu: (e) ->
return unless $(e.target).closest('#canvas-wrapper').length return unless $(e.target).closest('#canvas-wrapper').length

View file

@ -41,6 +41,7 @@ module.exports = class GameMenuModal extends ModalView
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-tab-switch', volume: 1 Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-tab-switch', volume: 1
onHidden: -> onHidden: ->
super()
subview.onHidden?() for subviewKey, subview of @subviews subview.onHidden?() for subviewKey, subview of @subviews
me.patch() me.patch()
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-close', volume: 1 Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-close', volume: 1

View file

@ -176,7 +176,7 @@ module.exports = class CocoView extends Backbone.View
target = elem.data('target') target = elem.data('target')
Modal = require 'views/'+target Modal = require 'views/'+target
e.stopPropagation() e.stopPropagation()
@openModalView new Modal @openModalView new Modal supermodel: @supermodal
openModalView: (modalView, softly=false) -> openModalView: (modalView, softly=false) ->
return if waitingModal # can only have one waiting at once return if waitingModal # can only have one waiting at once

View file

@ -7,6 +7,7 @@ module.exports = class ModalView extends CocoView
modalWidthPercent: null modalWidthPercent: null
plain: false plain: false
instant: false instant: false
template: require 'templates/modal/modal_base'
events: events:
'click a': 'toggleModal' 'click a': 'toggleModal'
@ -26,6 +27,8 @@ module.exports = class ModalView extends CocoView
getRenderData: (context={}) -> getRenderData: (context={}) ->
context = super(context) context = super(context)
context.closeButton = @closeButton context.closeButton = @closeButton
context.headerContent = @options.headerContent
context.bodyContent = @options.bodyContent
context context
subscriptions: subscriptions:

View file

@ -0,0 +1,472 @@
RootView = require 'views/kinds/RootView'
template = require 'templates/play/world-map-view'
LevelSession = require 'models/LevelSession'
CocoCollection = require 'collections/CocoCollection'
AudioPlayer = require 'lib/AudioPlayer'
class LevelSessionsCollection extends CocoCollection
url: ''
model: LevelSession
constructor: (model) ->
super()
@url = "/db/user/#{me.id}/level.sessions?project=state.complete,levelID"
module.exports = class MainPlayView extends RootView
id: 'world-map-view'
template: template
events:
'click .map': 'onClickMap'
'click .game-controls button': 'onClickGameControl'
'mouseenter .level a': 'onMouseEnterLevel'
'mouseleave .level a': 'onMouseLeaveLevel'
'mousemove .map': 'onMouseMoveMap'
constructor: (options) ->
super options
@levelStatusMap = {}
@levelPlayCountMap = {}
@sessions = @supermodel.loadCollection(new LevelSessionsCollection(), 'your_sessions', null, 0).model
@listenToOnce @sessions, 'sync', @onSessionsLoaded
@getLevelPlayCounts()
$(window).on 'resize', @onWindowResize
getLevelPlayCounts: ->
success = (levelPlayCounts) =>
return if @destroyed
for level in levelPlayCounts
@levelPlayCountMap[level._id] = playtime: level.playtime, sessions: level.sessions
@render() if @supermodel.finished()
levelIDs = []
for campaign in campaigns
for level in campaign.levels
levelIDs.push level.id
levelPlayCountsRequest = @supermodel.addRequestResource 'play_counts', {
url: '/db/level/-/play_counts'
data: {ids: levelIDs}
method: 'POST'
success: success
}, 0
levelPlayCountsRequest.load()
getRenderData: (context={}) ->
context = super(context)
context.campaigns = campaigns
for campaign in context.campaigns
for level in campaign.levels
level.x ?= 10 + 80 * Math.random()
level.y ?= 10 + 80 * Math.random()
context.levelStatusMap = @levelStatusMap
context.levelPlayCountMap = @levelPlayCountMap
context
afterRender: ->
super()
@onWindowResize()
_.defer => @$el.find('.game-controls button').tooltip() # Have to defer or i18n doesn't take effect.
onSessionsLoaded: (e) ->
for session in @sessions.models
@levelStatusMap[session.get('levelID')] = if session.get('state')?.complete then 'complete' else 'started'
@render()
onClickMap: (e) ->
# Easy-ish way of figuring out coordinates for placing level dots.
x = e.offsetX / @$el.find('.map-background').width()
y = (1 - e.offsetY / @$el.find('.map-background').height())
console.log " x: #{(100 * x).toFixed(2)}\n y: #{(100 * y).toFixed(2)}\n"
onClickGameControl: (e) ->
onMouseEnterLevel: (e) ->
levelID = $(e.target).parents('.level').data('level-id')
@$levelInfo = @$el.find(".level-info-container[data-level-id=#{levelID}]").show()
@adjustLevelInfoPosition e
onMouseLeaveLevel: (e) ->
levelID = $(e.target).parents('.level').data('level-id')
@$el.find(".level-info-container[data-level-id='#{levelID}']").hide()
onMouseMoveMap: (e) ->
@adjustLevelInfoPosition e
adjustLevelInfoPosition: (e) ->
return unless @$levelInfo
@$map ?= @$el.find('.map')
mapOffset = @$map.offset()
mapX = e.pageX - mapOffset.left
mapY = e.pageY - mapOffset.top
margin = 20
width = @$levelInfo.outerWidth()
@$levelInfo.css('left', Math.min(Math.max(margin, mapX - width / 2), @$map.width() - width - margin))
height = @$levelInfo.outerHeight()
top = mapY - @$levelInfo.outerHeight() - 60
if top < 20
top = mapY + 60
@$levelInfo.css('top', top)
onWindowResize: (e) =>
forestMapWidth = 2401
forestMapHeight = 1536
aspectRatio = forestMapWidth / forestMapHeight
pageWidth = $(window).width()
pageHeight = $(window).height()
widthRatio = pageWidth / forestMapWidth
heightRatio = pageHeight / forestMapHeight
if widthRatio > heightRatio
resultingWidth = pageWidth
resultingHeight = resultingWidth / aspectRatio
else
resultingHeight = pageHeight
resultingWidth = resultingHeight * aspectRatio
resultingMarginX = (pageWidth - resultingWidth) / 2
resultingMarginY = (pageHeight - resultingHeight) / 2
@$el.find('.map').css(width: resultingWidth, height: resultingHeight, 'margin-left': resultingMarginX, 'margin-top': resultingMarginY)
tutorials = [
{
name: 'Rescue Mission'
difficulty: 1
id: 'rescue-mission'
image: '/file/db/level/52740644904ac0411700067c/rescue_mission_icon.png'
description: 'Tharin has been captured! Start here.'
first: true
x: 17.23
y: 36.94
}
{
name: 'Grab the Mushroom'
difficulty: 1
id: 'grab-the-mushroom'
image: '/file/db/level/529662dfe0df8f0000000007/grab_the_mushroom_icon.png'
description: 'Grab a powerup and smash a big ogre.'
x: 22.6
y: 35.1
}
{
name: 'Drink Me'
difficulty: 1
id: 'drink-me'
image: '/file/db/level/525dc5589a0765e496000006/drink_me_icon.png'
description: 'Drink up and slay two munchkins.'
x: 27.74
y: 35.17
}
{
name: 'Taunt the Guards'
difficulty: 1
id: 'taunt-the-guards'
image: '/file/db/level/5276c9bdcf83207a2801ff8f/taunt_icon.png'
description: 'Tharin, if clever, can escape with Phoebe.'
x: 32.7
y: 36.7
}
{
name: 'It\'s a Trap'
difficulty: 1
id: 'its-a-trap'
image: '/file/db/level/528aea2d7f37fc4e0700016b/its_a_trap_icon.png'
description: 'Organize a dungeon ambush with archers.'
x: 37.6
y: 40.0
}
{
name: 'Break the Prison'
difficulty: 1
id: 'break-the-prison'
image: '/file/db/level/5275272c69abdcb12401216e/break_the_prison_icon.png'
description: 'More comrades are imprisoned!'
x: 44.1
y: 39.5
}
{
name: 'Taunt'
difficulty: 1
id: 'taunt'
image: '/file/db/level/525f150306e1ab0962000018/taunt_icon.png'
description: 'Taunt the ogre to claim victory.'
x: 38.5
y: 44.1
}
{
name: 'Cowardly Taunt'
difficulty: 1
id: 'cowardly-taunt'
image: '/file/db/level/525abfd9b12777d78e000009/cowardly_taunt_icon.png'
description: 'Lure infuriated ogres to their doom.'
x: 39.2
y: 50.1
}
{
name: 'Commanding Followers'
difficulty: 1
id: 'commanding-followers'
image: '/file/db/level/525ef8ef06e1ab0962000003/commanding_followers_icon.png'
description: 'Lead allied soldiers into battle.'
x: 39.1
y: 54.7
}
{
name: 'Mobile Artillery'
difficulty: 1
id: 'mobile-artillery'
image: '/file/db/level/525085419851b83f4b000001/mobile_artillery_icon.png'
description: 'Blow ogres up!'
x: 39.5
y: 60.2
}
]
experienced = [
{
name: 'Hunter Triplets'
difficulty: 2
id: 'hunter-triplets'
image: '/file/db/level/526711d9add4f8965f000002/hunter_triplets_icon.png'
description: 'Three soldiers go ogre hunting.'
x: 51.76
y: 35.5
}
{
name: 'Emphasis on Aim'
difficulty: 2
id: 'emphasis-on-aim'
image: '/file/db/level/525f384d96cd77000000000f/munchkin_masher_icon.png'
description: 'Choose your targets carefully.'
x: 61.47
y: 33.46
}
{
name: 'Zone of Danger'
difficulty: 3
id: 'zone-of-danger'
image: '/file/db/level/526ae95c1e5cd30000000008/zone_of_danger_icon.png'
description: 'Target the ogres swarming into arrow range.'
x: 65.72
y: 26.72
}
{
name: 'Molotov Medic'
difficulty: 2
id: 'molotov-medic'
image: '/file/db/level/52602ecb026e8481e7000001/generic_1.png'
description: 'Tharin must play support in this dungeon battle.'
x: 70.95
y: 18.64
}
{
name: 'Gridmancer'
difficulty: 5
id: 'gridmancer'
image: '/file/db/level/52ae2460ef42c52f13000008/gridmancer_icon.png'
description: 'Super algorithm challenge level!'
x: 61.41
y: 17.22
}
]
arenas = [
{
name: 'Criss-Cross'
difficulty: 5
id: 'criss-cross'
image: '/file/db/level/528aea2d7f37fc4e0700016b/its_a_trap_icon.png'
description: 'Participate in a bidding war with opponents to reach the other side!'
levelPath: 'ladder'
x: 49.43
y: 21.48
}
{
name: 'Greed'
difficulty: 4
id: 'greed'
image: '/file/db/level/526fd3043c637ece50001bb2/the_herd_icon.png'
description: 'Liked Dungeon Arena and Gold Rush? Put them together in this economic arena!'
levelPath: 'ladder'
x: 45.00
y: 23.34
}
{
name: 'Dungeon Arena'
difficulty: 3
id: 'dungeon-arena'
image: '/file/db/level/526ae95c1e5cd30000000008/zone_of_danger_icon.png'
description: 'Play head-to-head against fellow Wizards in a dungeon melee!'
levelPath: 'ladder'
x: 36.82
y: 23.17
}
{
name: 'Gold Rush'
difficulty: 3
id: 'gold-rush'
image: '/file/db/level/52602ecb026e8481e7000001/generic_1.png'
description: 'Prove you are better at collecting gold than your opponent!'
levelPath: 'ladder'
x: 30.8
y: 16.87
}
{
name: 'Brawlwood'
difficulty: 4
id: 'brawlwood'
image: '/file/db/level/525ef8ef06e1ab0962000003/commanding_followers_icon.png'
description: 'Combat the armies of other Wizards in a strategic forest arena! (Fast computer required.)'
levelPath: 'ladder'
x: 41.93
y: 12.79
}
{
name: 'Sky Span (Testing)'
difficulty: 3
id: 'sky-span'
image: '/file/db/level/526ae95c1e5cd30000000008/zone_of_danger_icon.png'
description: 'Preview version of an upgraded Dungeon Arena. Help us with hero balance before release!'
levelPath: 'ladder'
x: 53.12
y: 11.37
}
]
classicAlgorithms = [
{
name: 'Bubble Sort Bootcamp Battle'
difficulty: 3
id: 'bubble-sort-bootcamp-battle'
image: '/file/db/level/525ef8ef06e1ab0962000003/commanding_followers_icon.png'
description: 'Write a bubble sort to organize your soldiers. - by Alexandru Caciulescu'
x: 26.37
y: 93.02
}
{
name: 'Ogres of Hanoi'
difficulty: 3
id: 'ogres-of-hanoi'
image: '/file/db/level/526fd3043c637ece50001bb2/the_herd_icon.png'
description: 'Transfer a stack of ogres while preserving their honor. - by Alexandru Caciulescu'
x: 32.39
y: 92.67
}
{
name: 'Danger! Minefield'
difficulty: 3
id: 'danger-minefield'
image: '/file/db/level/526bda3fe79aefde2a003e36/mobile_artillery_icon.png'
description: 'Learn how to find prime numbers while defusing mines! - by Alexandru Caciulescu'
x: 38.07
y: 92.76
}
{
name: 'K-means++ Cluster Wars'
difficulty: 4
id: 'k-means-cluster-wars'
image: '/file/db/level/525ef8ef06e1ab0962000003/commanding_followers_icon.png'
description: 'Learn cluster analysis while leading armies into battle! - by Alexandru Caciulescu'
x: 43.75
y: 90.36
}
{
name: 'Quicksort the Spiral'
difficulty: 3
id: 'quicksort-the-spiral'
image: '/file/db/level/525ef8ef06e1ab0962000003/commanding_followers_icon.png'
description: 'Learn Quicksort while sorting a spiral of ogres! - by Alexandru Caciulescu'
x: 48.97
y: 87.08
}
{
name: 'Minimax Tic-Tac-Toe'
difficulty: 4
id: 'minimax-tic-tac-toe'
image: '/file/db/level/525ef8ef06e1ab0962000003/commanding_followers_icon.png'
description: 'Learn how to make a game AI with the Minimax algorithm. - by Alexandru Caciulescu'
x: 55.96
y: 82.73
}
]
playerCreated = [
{
name: 'Extra Extrapolation'
difficulty: 2
id: 'extra-extrapolation'
image: '/file/db/level/526bda3fe79aefde2a003e36/mobile_artillery_icon.png'
description: 'Predict your target\'s position for deadly aim. - by Sootn'
x: 42.67
y: 67.98
}
{
name: 'The Right Route'
difficulty: 1
id: 'the-right-route'
image: '/file/db/level/526fd3043c637ece50001bb2/the_herd_icon.png'
description: 'Strike at the weak point in an array of enemies. - by Aftermath'
x: 47.38
y: 70.55
}
{
name: 'Sword Loop'
difficulty: 2
id: 'sword-loop'
image: '/file/db/level/525dc5589a0765e496000006/drink_me_icon.png'
description: 'Kill the ogres and save the peasants with for-loops. - by Prabh Simran Singh Baweja'
x: 52.66
y: 69.66
}
{
name: 'Coin Mania'
difficulty: 2
id: 'coin-mania'
image: '/file/db/level/529662dfe0df8f0000000007/grab_the_mushroom_icon.png'
description: 'Learn while-loops to grab coins and potions. - by Prabh Simran Singh Baweja'
x: 58.46
y: 66.38
}
{
name: 'Find the Spy'
difficulty: 2
id: 'find-the-spy'
image: '/file/db/level/526ae95c1e5cd30000000008/zone_of_danger_icon.png'
description: 'Identify the spies hidden among your soldiers - by Nathan Gossett'
x: 63.11
y: 62.74
}
{
name: 'Harvest Time'
difficulty: 2
id: 'harvest-time'
image: '/file/db/level/529662dfe0df8f0000000007/grab_the_mushroom_icon.png'
description: 'Collect a hundred mushrooms in just five lines of code - by Nathan Gossett'
x: 69.19
y: 60.61
}
{
name: 'Guide Everyone Home'
difficulty: 2
id: 'guide-everyone-home'
image: '/file/db/level/52740644904ac0411700067c/rescue_mission_icon.png'
description: 'Fetch the wizards teleporting into the area - by Nathan Gossett'
x: 77.54
y: 65.94
}
{
name: "Let's go Fly a Kite"
difficulty: 3
id: 'lets-go-fly-a-kite'
image: '/file/db/level/526711d9add4f8965f000002/hunter_triplets_icon.png'
description: 'There is a horde of ogres marching on your village. Stay out of reach and use your bow to take them out! - by Danny Whittaker'
x: 84.29
y: 61.23
}
]
campaigns = [
{id: 'beginner', name: 'Beginner Campaign', description: '... in which you learn the wizardry of programming.', levels: tutorials, color: "rgb(255, 80, 60)"}
{id: 'multiplayer', name: 'Multiplayer Arenas', description: '... in which you code head-to-head against other players.', levels: arenas, color: "rgb(80, 5, 60)"}
{id: 'dev', name: 'Random Harder Levels', description: '... in which you learn the interface while doing something a little harder.', levels: experienced, color: "rgb(80, 60, 255)"}
{id: 'classic' ,name: 'Classic Algorithms', description: '... in which you learn the most popular algorithms in Computer Science.', levels: classicAlgorithms, color: "rgb(110, 80, 120)"}
{id: 'player_created', name: 'Player-Created', description: '... in which you battle against the creativity of your fellow <a href=\"/contribute#artisan\">Artisan Wizards</a>.', levels: playerCreated, color: "rgb(160, 160, 180)"}
]

View file

@ -1,4 +1,4 @@
module.exports = results = greed: {} module.exports = results = greed: {}, 'criss-cross': {}
results.greed.humans = [ results.greed.humans = [
{team: 'humans', rank: 1, sessionID: '5381e3537585483905a829c1', name: 'Wizard Dude', playtime: 63184, wins: 363, losses: 0, score: 363} {team: 'humans', rank: 1, sessionID: '5381e3537585483905a829c1', name: 'Wizard Dude', playtime: 63184, wins: 363, losses: 0, score: 363}
@ -550,3 +550,199 @@ results.greed.ogres = [
{team: 'ogres', rank: 269, sessionID: '537b96b16c13497e05de82a8', name: 'Krris', playtime: 2618, wins: 0, losses: 379, score: -379} {team: 'ogres', rank: 269, sessionID: '537b96b16c13497e05de82a8', name: 'Krris', playtime: 2618, wins: 0, losses: 379, score: -379}
{team: 'ogres', rank: 270, sessionID: '537bc1b81db8c8ac063a3b7b', name: 'jpiasetz', playtime: 14565, wins: 0, losses: 380, score: -380} {team: 'ogres', rank: 270, sessionID: '537bc1b81db8c8ac063a3b7b', name: 'jpiasetz', playtime: 14565, wins: 0, losses: 380, score: -380}
] ]
results['criss-cross'].humans = [
{team: 'humans', rank: 1, sessionID: '53f63c00f7bc7336054f14f5', codeLanguage: 'javascript', name: 'mdz', score: 89.27983686}
{team: 'humans', rank: 2, sessionID: '53f5fdb054e4f234059ff58d', codeLanguage: 'javascript', name: 'Nafaya', score: 84.77623255}
{team: 'humans', rank: 3, sessionID: '53f4c7f454e4f234059e97a0', codeLanguage: 'javascript', name: 'nino48', score: 84.62650458}
{team: 'humans', rank: 4, sessionID: '53f66c3af7bc7336054f4b93', codeLanguage: 'javascript', name: 'Cracker', score: 82.04044463}
{team: 'humans', rank: 5, sessionID: '53f3b7751171a83405504419', codeLanguage: 'javascript', name: 'c11ris', score: 68.68840693}
{team: 'humans', rank: 6, sessionID: '5400515cff5d7535058f274e', codeLanguage: 'javascript', name: 'fireantik', score: 67.38125522}
{team: 'humans', rank: 7, sessionID: '53f6924ff7bc7336054f6007', codeLanguage: 'javascript', name: 'Alexei2', score: 66.05333713}
{team: 'humans', rank: 8, sessionID: '53f4c91c54e4f234059e9824', codeLanguage: 'javascript', name: 'Mortimer Blackwood', score: 65.94607122}
{team: 'humans', rank: 9, sessionID: '53a35c928dd4b200006b0573', codeLanguage: 'python', name: 'Nick', score: 63.06756959}
{team: 'humans', rank: 10, sessionID: '53f6c36ffda22e3305c025cb', codeLanguage: 'javascript', name: 'lichens', score: 60.19465423}
{team: 'humans', rank: 11, sessionID: '53f397aae7a7643005c042f7', codeLanguage: 'javascript', name: 'qwe', score: 60.03001559}
{team: 'humans', rank: 12, sessionID: '53f45a49fda22e3305bd9009', codeLanguage: 'javascript', name: 'paulgator', score: 59.8852267}
{team: 'humans', rank: 13, sessionID: '5406519efcbd8f33059c4ea8', codeLanguage: 'javascript', name: 'shadowfire', score: 59.61113289}
{team: 'humans', rank: 14, sessionID: '53ac4b7123798d3605a87f9f', codeLanguage: 'javascript', name: 'DollarAkshay', score: 56.10811426}
{team: 'humans', rank: 15, sessionID: '53f7d4dbbc7e716d14e80de6', codeLanguage: 'javascript', name: 'cholesky', score: 55.27757899}
{team: 'humans', rank: 16, sessionID: '53f39df01171a834055013df', codeLanguage: 'javascript', name: 'Wizard Dude', score: 55.22843626}
{team: 'humans', rank: 17, sessionID: '53b2ba14102a853605e48f39', codeLanguage: 'javascript', name: 'nemoyatpeace', score: 54.3862373}
{team: 'humans', rank: 18, sessionID: '53f6be67fda22e3305c02523', codeLanguage: 'javascript', name: 'Ibyoki', score: 54.28563847}
{team: 'humans', rank: 19, sessionID: '53f554c6d822c23505b7b48d', codeLanguage: 'javascript', name: 'Valiant', score: 53.70432642}
{team: 'humans', rank: 20, sessionID: '53ba3f1194abf748058eb864', codeLanguage: 'javascript', name: 'Jex', score: 51.53868217}
{team: 'humans', rank: 21, sessionID: '53f3956e7c1c003605e8ab74', codeLanguage: 'javascript', name: 'piThrower', score: 51.38189056}
{team: 'humans', rank: 22, sessionID: '53d02fbc7b4eee430a5f238a', codeLanguage: 'javascript', name: 'dcm', score: 51.079906}
{team: 'humans', rank: 23, sessionID: '53f5b34154e4f234059fcc34', codeLanguage: 'javascript', name: 'zbanana', score: 50.4276462}
{team: 'humans', rank: 24, sessionID: '53f401c2e7a7643005c1358d', codeLanguage: 'javascript', name: 'Jerigan', score: 49.98484298}
{team: 'humans', rank: 25, sessionID: '53fbcdd07c3dc73405009ca5', codeLanguage: 'javascript', name: 'ojannen', score: 49.70746541}
{team: 'humans', rank: 26, sessionID: '53f59a2fd822c23505b80aff', codeLanguage: 'javascript', name: 'gorunir', score: 48.68311327}
{team: 'humans', rank: 27, sessionID: '53f3a5b3e7a7643005c05d11', codeLanguage: 'javascript', name: 'Tilorian', score: 47.32564935}
{team: 'humans', rank: 28, sessionID: '53f38b90e7a7643005c0150d', codeLanguage: 'javascript', name: 'MyWizard', score: 46.616599}
{team: 'humans', rank: 29, sessionID: '53f3dbf3e7a7643005c0dd43', codeLanguage: 'javascript', name: 'Terebijoke', score: 45.40772291}
{team: 'humans', rank: 30, sessionID: '53c22de027d0244a057ecbd8', codeLanguage: 'javascript', name: 'JavaCaster1', score: 45.08075025}
{team: 'humans', rank: 31, sessionID: '53f8f7de67ab263605e41dc6', codeLanguage: 'javascript', name: 'Moojo', score: 43.91409757}
{team: 'humans', rank: 32, sessionID: '53f49f28d822c23505b6ccdb', codeLanguage: 'clojure', name: 'jChuck', score: 43.69648547}
{team: 'humans', rank: 33, sessionID: '53f3ecaa8165533205fb46a2', codeLanguage: 'javascript', name: 'MCS', score: 43.65500921}
{team: 'humans', rank: 34, sessionID: '53f51289fda22e3305beac6a', codeLanguage: 'javascript', name: 'anykey', score: 43.52308768}
{team: 'humans', rank: 35, sessionID: '53f3a645e7a7643005c05dd8', codeLanguage: 'javascript', name: 'Nazywam', score: 42.76690262}
{team: 'humans', rank: 36, sessionID: '53f3b7a6e7a7643005c08b17', codeLanguage: 'javascript', name: 'Lunt', score: 42.5117331}
{team: 'humans', rank: 37, sessionID: '53fb57b85cfd700d099b733e', codeLanguage: 'python', name: 'other019', score: 42.46817345}
{team: 'humans', rank: 38, sessionID: '53fb51c83baef834054b5f87', codeLanguage: 'python', name: 'KiPu', score: 42.37172766}
{team: 'humans', rank: 39, sessionID: '53f7b114c5baa2f41501231a', codeLanguage: 'javascript', name: 'Pop-up', score: 42.10472537}
{team: 'humans', rank: 40, sessionID: '53f3866b7c1c003605e886cf', codeLanguage: 'javascript', name: 'Plak87', score: 40.75472545}
{team: 'humans', rank: 41, sessionID: '53f4c876f7bc7336054d84cf', codeLanguage: 'javascript', name: 'chriswen', score: 40.2773747}
{team: 'humans', rank: 42, sessionID: '53fad79467ab263605e5daff', codeLanguage: 'python', name: 'JLeow00', score: 40.23592348}
{team: 'humans', rank: 43, sessionID: '53f3909e7c1c003605e8a089', codeLanguage: 'javascript', name: 'juckele', score: 39.71481956}
{team: 'humans', rank: 44, sessionID: '53f387abe7a7643005c01095', codeLanguage: 'python', name: 'parseval', score: 39.40974773}
{team: 'humans', rank: 45, sessionID: '53f387b47c1c003605e8878a', codeLanguage: 'javascript', name: 'NicRio', score: 39.06434886}
{team: 'humans', rank: 46, sessionID: '53f6377154e4f23405a05ce6', codeLanguage: 'javascript', name: 'Moji', score: 38.60381239}
{team: 'humans', rank: 47, sessionID: '54022fd6973a642e10f08679', codeLanguage: 'javascript', name: 'droidwarrior', score: 38.12258027}
{team: 'humans', rank: 48, sessionID: '53f47e87fda22e3305bdbc2d', codeLanguage: 'javascript', name: 'Santinell', score: 37.77333035}
{team: 'humans', rank: 49, sessionID: '53fcfddf10bddbd805b1606a', codeLanguage: 'javascript', name: '[i].Lashes', score: 37.10129461}
{team: 'humans', rank: 50, sessionID: '54034c2a0557f27b0c33577a', codeLanguage: 'javascript', name: 'Finwe', score: 36.33095077}
{team: 'humans', rank: 51, sessionID: '53f490dcd822c23505b6c346', codeLanguage: 'python', name: 'AlienHunter3010', score: 35.57859105}
{team: 'humans', rank: 52, sessionID: '53f3e780e7a7643005c0ecd5', codeLanguage: 'javascript', name: 'Plloi', score: 34.79515433}
{team: 'humans', rank: 53, sessionID: '53fe1d04450764220abe3ff4', codeLanguage: 'javascript', name: 'Ehrks', score: 34.79168316}
{team: 'humans', rank: 54, sessionID: '53f3927fe7a7643005c03438', codeLanguage: 'javascript', name: 'taz', score: 34.2703216}
{team: 'humans', rank: 55, sessionID: '53f37d707c1c003605e87b1a', codeLanguage: 'javascript', name: 'zero_degrees', score: 33.64927149}
{team: 'humans', rank: 56, sessionID: '53f47bbdd822c23505b6a40a', codeLanguage: 'javascript', name: 'liorst1', score: 33.60823861}
{team: 'humans', rank: 57, sessionID: '541413a75117a4d10e468fe5', codeLanguage: 'python', name: 'Ever', score: 33.5904028}
{team: 'humans', rank: 58, sessionID: '53f671acfda22e3305c009a2', codeLanguage: 'python', name: 'Gnu', score: 33.42381407}
{team: 'humans', rank: 59, sessionID: '53fdb707a1a2c92409fd3fae', codeLanguage: 'javascript', name: 'gyuri', score: 32.56147128}
{team: 'humans', rank: 60, sessionID: '53dee08059839b35051fb52f', codeLanguage: 'javascript', name: 'Lanner', score: 31.89927824}
{team: 'humans', rank: 61, sessionID: '53f566b3f7bc7336054e3f4f', codeLanguage: 'javascript', name: 'redWizzard', score: 31.71795432}
{team: 'humans', rank: 62, sessionID: '53f38d58e7a7643005c0205e', codeLanguage: 'javascript', name: 'Maixck', score: 31.13482773}
{team: 'humans', rank: 63, sessionID: '5416ada992fc490115ce300c', codeLanguage: 'javascript', name: 'DanielAwesome', score: 30.0249466}
{team: 'humans', rank: 64, sessionID: '53f3f08d1171a8340550b8e3', codeLanguage: 'python', name: 'Handsome2734', score: 29.61969526}
{team: 'humans', rank: 65, sessionID: '540d9481a6128791125a1765', codeLanguage: 'javascript', name: 'Thom VB', score: 29.59295282}
{team: 'humans', rank: 66, sessionID: '5415d931fed2785a13ccac3b', codeLanguage: 'python', name: 'RedPill', score: 28.6484878}
{team: 'humans', rank: 67, sessionID: '53f3969f7c1c003605e8aeda', codeLanguage: 'python', name: 'Icatpasdu', score: 28.59919383}
{team: 'humans', rank: 68, sessionID: '53f422abe7a7643005c17609', codeLanguage: 'python', name: 'akhtyamovpavel', score: 28.53107357}
{team: 'humans', rank: 69, sessionID: '53a341aeaa66a7afbe3d1175', codeLanguage: 'javascript', name: 'Scott', score: 28.13105798}
{team: 'humans', rank: 70, sessionID: '5408bee18d0b993805d6e2ed', codeLanguage: 'python', name: 'Juan13', score: 27.72431383}
{team: 'humans', rank: 71, sessionID: '53f3827c8165533205fa7e53', codeLanguage: 'coffeescript', name: 'Anomander', score: 27.48633158}
{team: 'humans', rank: 72, sessionID: '53f4e25cf7bc7336054dcb1e', codeLanguage: 'javascript', name: 'Eterm', score: 27.44590315}
{team: 'humans', rank: 73, sessionID: '53f3e09f1171a83405509857', codeLanguage: 'javascript', name: 'stuntman_fx', score: 27.31894123}
{team: 'humans', rank: 74, sessionID: '54022bcd0557f27b0c32a26a', codeLanguage: 'javascript', name: 'matom', score: 26.92817056}
{team: 'humans', rank: 75, sessionID: '540eaeae25c82d2b06e84e6e', codeLanguage: 'python', name: 'JamiePilgrim', score: 26.66085117}
{team: 'humans', rank: 76, sessionID: '53f39bef8165533205faaf1c', codeLanguage: 'python', name: 'Gybseaa', score: 25.67104323}
{team: 'humans', rank: 77, sessionID: '53f5266dfda22e3305bec4d4', codeLanguage: 'python', name: 'sdetfaewtgswrf', score: 25.11200369}
{team: 'humans', rank: 78, sessionID: '53f3bade7c1c003605e912e8', codeLanguage: 'javascript', name: 'Tyler', score: 24.45104378}
{team: 'humans', rank: 79, sessionID: '54145e81e8c6b8ed0ec3c0de', codeLanguage: 'javascript', name: 'yuvi', score: 23.35851543}
{team: 'humans', rank: 80, sessionID: '53f37c861171a834054fd63f', codeLanguage: 'python', name: 'pbd', score: 21.95799255}
{team: 'humans', rank: 81, sessionID: '53f67052f7bc7336054f4d9f', codeLanguage: 'python', name: 'Napillo', score: 21.59848035}
{team: 'humans', rank: 82, sessionID: '53f5a34ed822c23505b80e62', codeLanguage: 'javascript', name: 'Sailor', score: 19.88903243}
{team: 'humans', rank: 83, sessionID: '53f60815fda22e3305bf8d14', codeLanguage: 'javascript', name: 'Gordon2012', score: 19.3703958}
{team: 'humans', rank: 84, sessionID: '53f3a49ce7a7643005c05c28', codeLanguage: 'clojure', name: 'Oze', score: 19.15746715}
{team: 'humans', rank: 85, sessionID: '53f3cee5e7a7643005c0cd04', codeLanguage: 'javascript', name: 'Frewdicket', score: 18.32983195}
{team: 'humans', rank: 86, sessionID: '53f3dfe07c1c003605e9560b', codeLanguage: 'python', name: 'Wizardioo', score: 17.24637409}
{team: 'humans', rank: 87, sessionID: '53f73786d822c23505b99bdc', codeLanguage: 'javascript', name: 'WOUN', score: 16.09512312}
{team: 'humans', rank: 88, sessionID: '53fb3c3567ab263605e64525', codeLanguage: 'javascript', name: 'Uyjen', score: 15.41176492}
{team: 'humans', rank: 89, sessionID: '5412232591cb5534055f9df1', codeLanguage: 'javascript', name: 'alltom', score: 14.93217724}
{team: 'humans', rank: 90, sessionID: '53a35877367e5794b2f5d430', codeLanguage: 'javascript', name: 'Michael S.', score: 14.69737904}
{team: 'humans', rank: 91, sessionID: '53f3959a7c1c003605e8ab90', codeLanguage: 'javascript', name: 'Xyphex', score: 14.60922258}
{team: 'humans', rank: 92, sessionID: '53fdfc25bfe0d7f308a6c2f8', codeLanguage: 'javascript', name: 'BEFOR', score: 14.09951331}
{team: 'humans', rank: 93, sessionID: '53f39293e7a7643005c03596', codeLanguage: 'python', name: 'melondonkey', score: 13.66123744}
{team: 'humans', rank: 94, sessionID: '53f5865efda22e3305bf2296', codeLanguage: 'python', name: 'hax0r00110', score: 13.27129663}
{team: 'humans', rank: 95, sessionID: '53fb87786c1ea23605924fc5', codeLanguage: 'lua', name: 'Joker78', score: 12.82530944}
{team: 'humans', rank: 96, sessionID: '54127ddcb2f1cc3605cf7454', codeLanguage: 'javascript', name: 'yakotaki', score: 12.80841814}
{team: 'humans', rank: 97, sessionID: '54172c214580e6bb21433b96', codeLanguage: 'javascript', name: 'VictorMGB', score: 11.79712559}
{team: 'humans', rank: 98, sessionID: '5410b0520e98527205cf23b8', codeLanguage: 'python', name: 'SliceNDiceSpud', score: 11.7339923}
{team: 'humans', rank: 99, sessionID: '53fd248cb14343450634feda', codeLanguage: 'python', name: 'Galvan', score: 11.2788073}
{team: 'humans', rank: 100, sessionID: '53ff3dcf919148a7074c9608', codeLanguage: 'clojure', name: 'jjjjj', score: 10.28409032}
{team: 'humans', rank: 101, sessionID: '53b4a6862082f23505b849f7', codeLanguage: 'javascript', name: 'Anonymous', score: 10.10879041}
{team: 'humans', rank: 102, sessionID: '5415de2dc6f5ec4e13326a22', codeLanguage: 'javascript', name: 'Anonymous', score: 10.08833109}
{team: 'humans', rank: 103, sessionID: '53f803b0bc7e716d14e82fde', codeLanguage: 'javascript', name: 'Aaryan', score: 10.0045068}
{team: 'humans', rank: 104, sessionID: '53f848714d9388fa15d1a457', codeLanguage: 'python', name: 'null000llun', score: 9.753414719}
{team: 'humans', rank: 105, sessionID: '53fc7b9a7c3dc7340500f09a', codeLanguage: 'javascript', name: 'Hackrylix', score: 9.704839891}
{team: 'humans', rank: 106, sessionID: '53f9aa35549ae742068f9f7e', codeLanguage: 'javascript', name: 'AlexTelon', score: 9.357260227}
{team: 'humans', rank: 107, sessionID: '53f72633fda22e3305c06b56', codeLanguage: 'javascript', name: 'eellson', score: 8.949914084}
{team: 'humans', rank: 108, sessionID: '54049a2011058b421307e0e1', codeLanguage: 'javascript', name: 'ugly loaf', score: 8.813606154}
{team: 'humans', rank: 109, sessionID: '53f98c823baef834054a0544', codeLanguage: 'javascript', name: 'Gamagori-sama', score: 8.52660922}
{team: 'humans', rank: 110, sessionID: '54019e1e49086dee0ddaf200', codeLanguage: 'javascript', name: 'akonand', score: 7.542416738}
{team: 'humans', rank: 111, sessionID: '53f39b918165533205faaed6', codeLanguage: 'python', name: 'roshan360', score: 6.740957014}
{team: 'humans', rank: 112, sessionID: '54046519e2b6e64a1832659d', codeLanguage: 'javascript', name: 'Val-per', score: 6.444447281}
{team: 'humans', rank: 113, sessionID: '53fe8cee95fba83305f4592d', codeLanguage: 'javascript', name: 'Bendalf0', score: 6.003282371}
{team: 'humans', rank: 114, sessionID: '53a39f1a573d6d3505b187de', codeLanguage: 'javascript', name: '┬─┬ノ( º _ ºノ)', score: 5.650008807}
{team: 'humans', rank: 115, sessionID: '540efacbba536e450767b2e8', codeLanguage: 'javascript', name: 'cole3', score: 5.314407805}
{team: 'humans', rank: 116, sessionID: '540fb13d098c622109a769e5', codeLanguage: 'python', name: 'Crunchy Meatball', score: 5.107994897}
{team: 'humans', rank: 117, sessionID: '53b36e22bb61aa3405d50f18', codeLanguage: 'javascript', name: 'sjarvie', score: 4.846878355}
]
results['criss-cross'].ogres = [
{team: 'ogres', rank: 1, sessionID: '5404bc1a2c6432b618fdbc41', codeLanguage: 'javascript', name: 'HighSea', score: 86.69028832}
{team: 'ogres', rank: 2, sessionID: '53f3ba2d1171a834055046f2', codeLanguage: 'javascript', name: 'matthewd', score: 82.94918281}
{team: 'ogres', rank: 3, sessionID: '53f397528165533205faabbf', codeLanguage: 'clojure', name: 'Driphter', score: 78.85897022}
{team: 'ogres', rank: 4, sessionID: '53f3ac981171a83405503789', codeLanguage: 'javascript', name: 'Forsaken', score: 78.63231082}
{team: 'ogres', rank: 5, sessionID: '53f4d91d54e4f234059ea96c', codeLanguage: 'javascript', name: 'Tehvudgaw', score: 76.79927673}
{team: 'ogres', rank: 6, sessionID: '53fe21d197d25d030aae69c8', codeLanguage: 'javascript', name: 'Blash', score: 74.05348884}
{team: 'ogres', rank: 7, sessionID: '53f8f7c967ab263605e41dbf', codeLanguage: 'javascript', name: 'Aldo', score: 73.47433531}
{team: 'ogres', rank: 8, sessionID: '53f94a206c1ea23605902730', codeLanguage: 'javascript', name: 'ThatOtherPerson', score: 69.82052272}
{team: 'ogres', rank: 9, sessionID: '54171cd74580e6bb2143312d', codeLanguage: 'javascript', name: 'Tech', score: 68.94907702}
{team: 'ogres', rank: 10, sessionID: '53f86f3cbc7e716d14e8a15c', codeLanguage: 'python', name: 'CookieMonster', score: 66.97949263}
{team: 'ogres', rank: 11, sessionID: '53f49a36d822c23505b6cb63', codeLanguage: 'python', name: 'odoacre', score: 65.53452876}
{team: 'ogres', rank: 12, sessionID: '53f6f84654e4f23405a1070e', codeLanguage: 'javascript', name: 'Arkhaix', score: 62.24714335}
{team: 'ogres', rank: 13, sessionID: '53f3b0257c1c003605e8ee11', codeLanguage: 'python', name: 'Buddy7', score: 61.63398791}
{team: 'ogres', rank: 14, sessionID: '53f39dce7c1c003605e8bdab', codeLanguage: 'javascript', name: 'CryZe', score: 61.00336334}
{team: 'ogres', rank: 15, sessionID: '53f38d7f7c1c003605e893b7', codeLanguage: 'javascript', name: 'Interimo', score: 57.77153143}
{team: 'ogres', rank: 16, sessionID: '53ced120f5ef3848053b9033', codeLanguage: 'javascript', name: 'chess', score: 55.44106174}
{team: 'ogres', rank: 17, sessionID: '53f39bde1171a83405500eab', codeLanguage: 'javascript', name: 'MyWizardlyName', score: 52.78486678}
{team: 'ogres', rank: 18, sessionID: '53f7e3e0b8a67b731487de15', codeLanguage: 'python', name: 'nineties', score: 52.48111694}
{team: 'ogres', rank: 19, sessionID: '540f6761098c622109a74a7d', codeLanguage: 'javascript', name: 'snake-345', score: 50.92666589}
{team: 'ogres', rank: 20, sessionID: '53f3f1468165533205fb63e1', codeLanguage: 'javascript', name: 'jmmk', score: 48.92922599}
{team: 'ogres', rank: 21, sessionID: '53f39896e7a7643005c0443d', codeLanguage: 'javascript', name: 'aggressiveFloor', score: 48.14095491}
{team: 'ogres', rank: 22, sessionID: '53f389d48165533205fa8599', codeLanguage: 'javascript', name: 'RustyDoorknobs', score: 47.26139895}
{team: 'ogres', rank: 23, sessionID: '53f44cfafda22e3305bd815c', codeLanguage: 'python', name: 'PugachAG', score: 47.04215583}
{team: 'ogres', rank: 24, sessionID: '53f3f12b1171a8340550c098', codeLanguage: 'python', name: 'lanzafame', score: 46.62859491}
{team: 'ogres', rank: 25, sessionID: '540725e9fedc2a55092aedf5', codeLanguage: 'python', name: 'Snooze', score: 44.8836671}
{team: 'ogres', rank: 26, sessionID: '53b36ac7bb61aa3405d50df3', codeLanguage: 'javascript', name: 'differentmatt', score: 44.48371038}
{team: 'ogres', rank: 27, sessionID: '53ffbc9c4fa75bc805dc8fd7', codeLanguage: 'javascript', name: 'kwksilver', score: 43.89457566}
{team: 'ogres', rank: 28, sessionID: '53f86ac64d9388fa15d1cb81', codeLanguage: 'javascript', name: 'Burhub', score: 43.15569365}
{team: 'ogres', rank: 29, sessionID: '5407b788e2007791053bb498', codeLanguage: 'javascript', name: 'SocioDude', score: 43.15346459}
{team: 'ogres', rank: 30, sessionID: '53f46bbb54e4f234059e118f', codeLanguage: 'javascript', name: 'Diruna', score: 43.14092027}
{team: 'ogres', rank: 31, sessionID: '53fad85d6c1ea2360591b7bc', codeLanguage: 'python', name: 'XY00', score: 42.82746659}
{team: 'ogres', rank: 32, sessionID: '53f4d3ef54e4f234059ea632', codeLanguage: 'javascript', name: 'Kefir', score: 42.12141876}
{team: 'ogres', rank: 33, sessionID: '5402249e42fb9c380ad1ee81', codeLanguage: 'python', name: 'mattmatt', score: 41.89852019}
{team: 'ogres', rank: 34, sessionID: '53f4c47e54e4f234059e95bb', codeLanguage: 'javascript', name: 'Trefader', score: 41.30414337}
{team: 'ogres', rank: 35, sessionID: '53fa70243baef834054ab75a', codeLanguage: 'javascript', name: 'haydennedyah', score: 41.22021639}
{team: 'ogres', rank: 36, sessionID: '53fdc936bfe0d7f308a6911c', codeLanguage: 'python', name: 'Luogbelnu', score: 40.96555677}
{team: 'ogres', rank: 37, sessionID: '53ffc7e4a1e10bbd05ae2e1b', codeLanguage: 'python', name: 'bob0the0mighty', score: 40.41223631}
{team: 'ogres', rank: 38, sessionID: '53f5d0f2fda22e3305bf67ed', codeLanguage: 'javascript', name: 'lifeline', score: 39.0406456}
{team: 'ogres', rank: 39, sessionID: '53f379ca7c1c003605e8793d', codeLanguage: 'coffeescript', name: 'Makaze', score: 38.88913774}
{team: 'ogres', rank: 40, sessionID: '53f39b577c1c003605e8b97a', codeLanguage: 'javascript', name: 'Braleigh', score: 38.19984465}
{team: 'ogres', rank: 41, sessionID: '53f4b841fda22e3305be136a', codeLanguage: 'python', name: 'qeinar', score: 38.13867335}
{team: 'ogres', rank: 42, sessionID: '53fbf5e7547f52350547a66f', codeLanguage: 'javascript', name: 'Ziroby', score: 37.48383317}
{team: 'ogres', rank: 43, sessionID: '54161f1dc6f5ec4e13328bd8', codeLanguage: 'javascript', name: 'MrCrepe', score: 37.3907791}
{team: 'ogres', rank: 44, sessionID: '53f4e5efd822c23505b74ecd', codeLanguage: 'javascript', name: 'Ryan Matte', score: 36.780337}
{team: 'ogres', rank: 45, sessionID: '541721b34580e6bb21433509', codeLanguage: 'javascript', name: 'Ryemane', score: 36.40478416}
{team: 'ogres', rank: 46, sessionID: '53f4f349d822c23505b758a0', codeLanguage: 'python', name: 'ChadM', score: 34.0284634}
{team: 'ogres', rank: 47, sessionID: '540d00a18b7031c212067cfe', codeLanguage: 'javascript', name: 'mch82', score: 33.45805275}
{team: 'ogres', rank: 48, sessionID: '53f37d318165533205fa7af0', codeLanguage: 'python', name: 'Mateo', score: 33.31751134}
{team: 'ogres', rank: 49, sessionID: '53f57005fda22e3305befc0d', codeLanguage: 'python', name: 'liewjianwei7', score: 32.86449352}
{team: 'ogres', rank: 50, sessionID: '540a44e6a8fb9e2908864a95', codeLanguage: 'python', name: 'NupTup', score: 31.54057431}
{team: 'ogres', rank: 51, sessionID: '53ff7b1e620719340501eb62', codeLanguage: 'javascript', name: 'korrident', score: 30.36103457}
{team: 'ogres', rank: 52, sessionID: '5413faed64e0ac4609bc0506', codeLanguage: 'javascript', name: 'Vhr', score: 29.57257834}
{team: 'ogres', rank: 53, sessionID: '53fe610581b81d3505175aa4', codeLanguage: 'javascript', name: 'Anonymous', score: 26.11692793}
{team: 'ogres', rank: 54, sessionID: '53f4320a1171a8340551357c', codeLanguage: 'python', name: 'igemon', score: 25.57886325}
{team: 'ogres', rank: 55, sessionID: '53f44441f7bc7336054cec4c', codeLanguage: 'python', name: 'Willybe', score: 25.31132405}
{team: 'ogres', rank: 56, sessionID: '54092dfb4236843405e684e6', codeLanguage: 'javascript', name: 'Noidificus', score: 25.24067489}
{team: 'ogres', rank: 57, sessionID: '5408aac5901c3f7007b60799', codeLanguage: 'javascript', name: 'Robert7', score: 25.15177276}
{team: 'ogres', rank: 58, sessionID: '53f5364a54e4f234059f3d3b', codeLanguage: 'javascript', name: 'Bage', score: 24.80093885}
{team: 'ogres', rank: 59, sessionID: '53fc1b22547f52350547be8a', codeLanguage: 'javascript', name: 'Christian S.', score: 24.51828741}
{team: 'ogres', rank: 60, sessionID: '53f3efd17c1c003605e97769', codeLanguage: 'javascript', name: 'Dodrithard', score: 24.25374686}
{team: 'ogres', rank: 61, sessionID: '53f4e550fda22e3305be7032', codeLanguage: 'javascript', name: 'Saikarsis', score: 24.03641464}
{team: 'ogres', rank: 62, sessionID: '53f64591d822c23505b8b536', codeLanguage: 'javascript', name: 'SurferIX', score: 22.50107203}
{team: 'ogres', rank: 63, sessionID: '53f89788bc7e716d14e8e536', codeLanguage: 'javascript', name: 'Ezrael', score: 22.06693778}
{team: 'ogres', rank: 64, sessionID: '53f39c91e7a7643005c04751', codeLanguage: 'javascript', name: 'ksj00', score: 16.40406633}
{team: 'ogres', rank: 65, sessionID: '53f396f41171a834055008e0', codeLanguage: 'javascript', name: 'no_login_found', score: 14.50374011}
{team: 'ogres', rank: 66, sessionID: '53f6ac1fd822c23505b91f29', codeLanguage: 'javascript', name: 'soccer66', score: 13.95564768}
{team: 'ogres', rank: 67, sessionID: '53f394978165533205faa44a', codeLanguage: 'javascript', name: 'skeltoac', score: 13.77314387}
{team: 'ogres', rank: 68, sessionID: '53fdeef0a1a2c92409fd7dd1', codeLanguage: 'javascript', name: 'Luke Lunsford', score: 12.31318938}
{team: 'ogres', rank: 69, sessionID: '53ed2e5c5d4d593305f9d006', codeLanguage: 'python', name: 'Sir Giroto', score: 11.90012214}
{team: 'ogres', rank: 70, sessionID: '53a3a012573d6d3505b1882b', codeLanguage: 'javascript', name: 'Quantanaray', score: 11.7169867}
{team: 'ogres', rank: 71, sessionID: '5414d693e4fb6fc61274e974', codeLanguage: 'javascript', name: 'XxDavidxX', score: 11.32103184}
{team: 'ogres', rank: 72, sessionID: '53f4847afda22e3305bdbea5', codeLanguage: 'javascript', name: 'CodeMinion', score: 9.706964015}
{team: 'ogres', rank: 73, sessionID: '53fba49c3baef834054b98c6', codeLanguage: 'javascript', name: 'Moises Banales', score: 8.991630973}
]

View file

@ -27,7 +27,10 @@ module.exports = class LevelChatView extends CocoView
updateMultiplayerVisibility: -> updateMultiplayerVisibility: ->
return unless @$el? return unless @$el?
try
@$el.toggle Boolean @session.get('multiplayer') @$el.toggle Boolean @session.get('multiplayer')
catch e
console.error "Couldn't toggle the style on the LevelChatView to #{Boolean @session.get('multiplayer')} because of an error:", e
afterRender: -> afterRender: ->
@chatTables = $('table', @$el) @chatTables = $('table', @$el)

View file

@ -0,0 +1,28 @@
ModalView = require 'views/kinds/ModalView'
template = require 'templates/play/modal/play-account-modal'
module.exports = class PlayAccountModal extends ModalView
className: 'modal fade play-modal'
template: template
modalWidthPercent: 90
id: 'play-account-modal'
#instant: true
#events:
# 'change input.select': 'onSelectionChanged'
constructor: (options) ->
super options
getRenderData: (context={}) ->
context = super(context)
context
afterRender: ->
super()
return unless @supermodel.finished()
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-open', volume: 1
onHidden: ->
super()
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-close', volume: 1

View file

@ -0,0 +1,35 @@
ModalView = require 'views/kinds/ModalView'
template = require 'templates/play/modal/play-achievements-modal'
CocoCollection = require 'collections/CocoCollection'
Achievement = require 'models/Achievement'
#AchievementView = require 'views/game-menu/AchievementView'
module.exports = class PlayAchievementsModal extends ModalView
className: 'modal fade play-modal'
template: template
modalWidthPercent: 90
id: 'play-achievements-modal'
#instant: true
#events:
# 'change input.select': 'onSelectionChanged'
constructor: (options) ->
super options
#@achievements = new CocoCollection([], {model: Achievement})
#@achievements.url = '/db/thang.type?view=achievements&project=name,description,components,original,rasterIcon'
#@supermodel.loadCollection(@achievements, 'achievements')
getRenderData: (context={}) ->
context = super(context)
#context.achievements = @achievements.models
context
afterRender: ->
super()
return unless @supermodel.finished()
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-open', volume: 1
onHidden: ->
super()
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-close', volume: 1

View file

@ -0,0 +1,47 @@
ModalView = require 'views/kinds/ModalView'
template = require 'templates/play/modal/play-heroes-modal'
CocoCollection = require 'collections/CocoCollection'
ThangType = require 'models/ThangType'
#HeroView = require 'views/game-menu/HeroView'
module.exports = class PlayHeroesModal extends ModalView
className: 'modal fade play-modal'
template: template
modalWidthPercent: 90
id: 'play-heroes-modal'
#instant: true
#events:
# 'change input.select': 'onSelectionChanged'
constructor: (options) ->
super options
@heroes = new CocoCollection([], {model: ThangType})
@heroes.url = '/db/thang.type?view=heroes&project=name,description,components,original,rasterIcon'
@supermodel.loadCollection(@heroes, 'heroes')
getRenderData: (context={}) ->
context = super(context)
context.heroes = @heroes.models
context
afterRender: ->
super()
return unless @supermodel.finished()
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-open', volume: 1
#@addHeroViews()
onHidden: ->
super()
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-close', volume: 1
#addHeroViews: ->
# keys = (hero.id for hero in @heroes.models)
# heroMap = _.zipObject keys, @heroes.models
# for heroStub in @$el.find('.replace-me')
# heroID = $(heroStub).data('hero-id')
# hero = heroMap[heroID]
# heroView = new HeroView({hero: hero, includes: {name: true, stats: true, props: true}})
# heroView.render()
# $(heroStub).replaceWith(heroView.$el)
# @registerSubView(heroView)

View file

@ -0,0 +1,66 @@
ModalView = require 'views/kinds/ModalView'
template = require 'templates/play/modal/play-items-modal'
CocoCollection = require 'collections/CocoCollection'
ThangType = require 'models/ThangType'
ItemView = require 'views/game-menu/ItemView'
module.exports = class PlayItemsModal extends ModalView
className: 'modal fade play-modal'
template: template
modalWidthPercent: 90
id: 'play-items-modal'
#instant: true
slotGroups:
armor: ['torso', 'head', 'gloves', 'feet']
hands: ['right-hand', 'left-hand']
accessories: ['eyes', 'neck', 'left-ring', 'right-ring', 'waist']
books: ['programming-book', 'spellbook']
minions: ['minion', 'pet']
misc: ['misc-0', 'misc-1', 'misc-2', 'misc-3', 'misc-4']
#events:
# 'change input.select': 'onSelectionChanged'
constructor: (options) ->
super options
@items = new CocoCollection([], {model: ThangType})
@items.url = '/db/thang.type?view=items&project=name,description,components,original,rasterIcon'
@supermodel.loadCollection(@items, 'items')
groupItems: ->
groups = {}
for item in @items.models
itemSlots = item.getAllowedSlots()
for group, groupSlots of @slotGroups
if _.find itemSlots, ((slot) -> slot in groupSlots)
groups[group] ?= []
groups[group].push item
groups
getRenderData: (context={}) ->
context = super(context)
context.slotGroups = @groupItems()
context.slotGroupsArray = _.keys context.slotGroups
context.slotGroupsNames = ($.i18n.t "items.#{slotGroup}" for slotGroup in context.slotGroupsArray)
context
afterRender: ->
super()
return unless @supermodel.finished()
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-open', volume: 1
@addItemViews()
onHidden: ->
super()
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-close', volume: 1
addItemViews: ->
keys = (item.id for item in @items.models)
itemMap = _.zipObject keys, @items.models
for itemStub in @$el.find('.replace-me')
itemID = $(itemStub).data('item-id')
item = itemMap[itemID]
itemView = new ItemView({item: item, includes: {name: true, stats: true, props: true}})
itemView.render()
$(itemStub).replaceWith(itemView.$el)
@registerSubView(itemView)

View file

@ -0,0 +1,28 @@
ModalView = require 'views/kinds/ModalView'
template = require 'templates/play/modal/play-settings-modal'
module.exports = class PlaySettingsModal extends ModalView
className: 'modal fade play-modal'
template: template
modalWidthPercent: 90
id: 'play-settings-modal'
#instant: true
#events:
# 'change input.select': 'onSelectionChanged'
constructor: (options) ->
super options
getRenderData: (context={}) ->
context = super(context)
context
afterRender: ->
super()
return unless @supermodel.finished()
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-open', volume: 1
onHidden: ->
super()
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-close', volume: 1