diff --git a/app/Router.coffee b/app/Router.coffee index b826fcd89..cffcab045 100644 --- a/app/Router.coffee +++ b/app/Router.coffee @@ -14,7 +14,8 @@ module.exports = class CocoRouter extends Backbone.Router Backbone.Mediator.subscribe 'router:navigate', @onNavigate, @ 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') @@ -73,7 +74,8 @@ module.exports = class CocoRouter extends Backbone.Router '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': go('play/ladder/MainLadderView') 'play/level/:levelID': go('play/level/PlayLevelView') diff --git a/app/assets/images/pages/front/play_web.jpg b/app/assets/images/pages/front/play_web.jpg new file mode 100644 index 000000000..6ef8412da Binary files /dev/null and b/app/assets/images/pages/front/play_web.jpg differ diff --git a/app/assets/images/pages/play/easy_button.png b/app/assets/images/pages/play/ladder/easy_button.png similarity index 100% rename from app/assets/images/pages/play/easy_button.png rename to app/assets/images/pages/play/ladder/easy_button.png diff --git a/app/assets/images/pages/play/hard_button.png b/app/assets/images/pages/play/ladder/hard_button.png similarity index 100% rename from app/assets/images/pages/play/hard_button.png rename to app/assets/images/pages/play/ladder/hard_button.png diff --git a/app/assets/images/pages/play/medium_button.png b/app/assets/images/pages/play/ladder/medium_button.png similarity index 100% rename from app/assets/images/pages/play/medium_button.png rename to app/assets/images/pages/play/ladder/medium_button.png diff --git a/app/assets/images/pages/play/warmup_button.png b/app/assets/images/pages/play/ladder/warmup_button.png similarity index 100% rename from app/assets/images/pages/play/warmup_button.png rename to app/assets/images/pages/play/ladder/warmup_button.png diff --git a/app/assets/images/pages/play/map_forest.jpg b/app/assets/images/pages/play/map_forest.jpg new file mode 100644 index 000000000..51d50779c Binary files /dev/null and b/app/assets/images/pages/play/map_forest.jpg differ diff --git a/app/assets/images/pages/play/menu_icons.png b/app/assets/images/pages/play/menu_icons.png new file mode 100644 index 000000000..7c19bde65 Binary files /dev/null and b/app/assets/images/pages/play/menu_icons.png differ diff --git a/app/lib/LevelLoader.coffee b/app/lib/LevelLoader.coffee index ff7135f2d..df5b6b28d 100644 --- a/app/lib/LevelLoader.coffee +++ b/app/lib/LevelLoader.coffee @@ -202,7 +202,7 @@ module.exports = class LevelLoader extends CocoClass thangsToLoad = _.uniq( (t.spriteName for t in @world.thangs when t.exists) ) nameModelTuples = ([thangType.get('name'), thangType] for thangType in @thangNames.models) nameModelMap = _.zipObject nameModelTuples - @spriteSheetsToBuild = [] + @spriteSheetsToBuild ?= [] for thangTypeName in thangsToLoad thangType = nameModelMap[thangTypeName] @@ -230,7 +230,7 @@ module.exports = class LevelLoader extends CocoClass buildLoop: => someLeft = false - for spriteSheetResource, i in @spriteSheetsToBuild + for spriteSheetResource, i in @spriteSheetsToBuild ? [] continue if spriteSheetResource.spriteSheetKeys someLeft = true thangType = spriteSheetResource.thangType diff --git a/app/lib/surface/CocoSprite.coffee b/app/lib/surface/CocoSprite.coffee index d0a192515..f76caa2e4 100644 --- a/app/lib/surface/CocoSprite.coffee +++ b/app/lib/surface/CocoSprite.coffee @@ -79,6 +79,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass @handledDisplayEvents = {} @age = 0 @stillLoading = true + @setNameLabel @thang.id if @thang?.showsName and not @thang.health <= 0 if @thangType.isFullyLoaded() @setUpSprite() else @@ -488,6 +489,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass rotation = @getRotation() if relatedActions['111111111111'] # has grid-surrounding-wall-based actions if @wallGrid + @hadWallGrid = true action = '' tileSize = 4 [gx, gy] = [@thang.pos.x, @thang.pos.y] @@ -514,6 +516,8 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass break #console.log 'returning', matchedAction, 'for', @thang.id, 'at', gx, gy return relatedActions[matchedAction] + else if @hadWallGrid + return null else keys = _.keys relatedActions index = Math.max 0, Math.floor((179 + rotation) / 360 * keys.length) @@ -527,13 +531,15 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass relatedActions[direction] updateStats: -> + return unless @thang and @thang.health isnt @lastHealth + @lastHealth = @thang.health if bar = @healthBar - return if @thang.health is @lastHealth - @lastHealth = @thang.health healthPct = Math.max(@thang.health / @thang.maxHealth, 0) bar.scaleX = healthPct / bar.baseScale healthOffset = @getOffset 'aboveHead' [bar.x, bar.y] = [healthOffset.x - bar.width / 2, healthOffset.y] + if @thang.showsName + @setNameLabel(if @thang.health <= 0 then '' else @thang.id) configureMouse: -> @imageObject.cursor = 'pointer' if @thang?.isSelectable diff --git a/app/lib/surface/RegionChooser.coffee b/app/lib/surface/RegionChooser.coffee index c5147b25c..939782e0c 100644 --- a/app/lib/surface/RegionChooser.coffee +++ b/app/lib/surface/RegionChooser.coffee @@ -22,7 +22,7 @@ module.exports = class RegionChooser extends CocoClass onMouseMove: (e) => return unless @firstPoint @secondPoint = @options.camera.screenToWorld {x: e.stageX, y: e.stageY} - @restrictRegion() if @options.restrictRatio + @restrictRegion() if @options.restrictRatio or key.alt @updateShape() onMouseUp: (e) => diff --git a/app/lib/surface/SpriteBoss.coffee b/app/lib/surface/SpriteBoss.coffee index b994b5a39..481d1a6d1 100644 --- a/app/lib/surface/SpriteBoss.coffee +++ b/app/lib/surface/SpriteBoss.coffee @@ -186,11 +186,11 @@ module.exports = class SpriteBoss extends CocoClass sprite.update frameChanged for sprite in @spriteArray @updateSelection() @spriteLayers['Default'].updateLayerOrder() - @cache() + @cacheObstacles() adjustSpriteExistence: -> # Add anything new, remove anything old, update everything current - updateCache = false + updatedObstacles = [] itemsJustEquipped = [] for thang in @world.thangs when thang.exists and thang.pos itemsJustEquipped = itemsJustEquipped.concat @equipNewItems thang @@ -199,16 +199,16 @@ module.exports = class SpriteBoss extends CocoClass else sprite = @addThangToSprites(thang) 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() item.modifyStats() for item in itemsJustEquipped for thangID, sprite of @sprites missing = not (sprite.notOfThisWorld or @world.thangMap[thangID]?.exists) 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 @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 if @willSelectThang and @sprites[@willSelectThang[0]] @@ -229,25 +229,26 @@ module.exports = class SpriteBoss extends CocoClass itemsJustEquipped.push item return itemsJustEquipped - cache: (update=false) -> - return if @cached and not update + cacheObstacles: (updatedObstacles=null) -> + return if @cachedObstacles and not updatedObstacles 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) walls = (sprite.thang for sprite in wallSprites) @world.calculateBounds() 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.updateScale() wallSprite.updatePosition() #console.log @wallGrid.toString() @spriteLayers['Obstacle'].uncache() if @spriteLayers['Obstacle'].cacheID # might have changed sizes @spriteLayers['Obstacle'].cache() - # test performance of doing land layer, too, to see if it's faster -# @spriteLayers['Land'].uncache() if @spriteLayers['Land'].cacheID # might have changed sizes -# @spriteLayers['Land'].cache() - # I don't notice much difference - Scott - @cached = true + @cachedObstacles = true spriteFor: (thangID) -> @sprites[thangID] diff --git a/app/lib/surface/Surface.coffee b/app/lib/surface/Surface.coffee index 96e051fe1..2d8c45fb1 100644 --- a/app/lib/surface/Surface.coffee +++ b/app/lib/surface/Surface.coffee @@ -488,7 +488,7 @@ module.exports = Surface = class Surface extends CocoClass #- Canvas callbacks onResize: (e) => - return if @destroyed + return if @destroyed or @options.choosing oldWidth = parseInt @canvas.attr('width'), 10 oldHeight = parseInt @canvas.attr('height'), 10 aspectRatio = oldWidth / oldHeight diff --git a/app/lib/world/GoalManager.coffee b/app/lib/world/GoalManager.coffee index a758d9fc7..8f629989e 100644 --- a/app/lib/world/GoalManager.coffee +++ b/app/lib/world/GoalManager.coffee @@ -139,7 +139,7 @@ module.exports = class GoalManager extends CocoClass @initGoalState(state, [[], keepFrom.keepFromLocation?.who], 'arrived') @initGoalState(state, [goal.getToLocations?.who, goal.keepFromLocations?.who], 'arrived') @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 onThangDied: (e, frameNumber) -> @@ -169,23 +169,23 @@ module.exports = class GoalManager extends CocoClass onThangLeftMap: (e, frameNumber) -> 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.keepFromLeavingOffSides.who, goal.keepFromLeavingOffSides.sides, e.thang.id, e.side, frameNumber) if goal.keepFromLeavingOffSides? + @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, 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 unless thangID in who - @updateGoalState(goalID, thangID, 'left', frameNumber) + return unless thang.id in who or thang.team in who + @updateGoalState(goalID, thang.id, 'left', frameNumber) onThangCollectedItem: (e, frameNumber) -> 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.keepFromCollectingThangs.who, goal.keepFromCollectingThangs.targets, e.actor.id, e.item.id, frameNumber) if goal.keepFromCollectingThangs? + @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, 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 thangID in who - @updateGoalState(goalID, thangID, 'collected', frameNumber) + return unless thang.id in who or thang.team in who + @updateGoalState(goalID, itemID, 'collected', frameNumber) wrapUpGoalStates: (finalFrame) -> for goalID, state of @goalStates diff --git a/app/locale/en.coffee b/app/locale/en.coffee index 9ea0c254b..8d1cc3b63 100644 --- a/app/locale/en.coffee +++ b/app/locale/en.coffee @@ -131,6 +131,19 @@ spectate: "Spectate" players: "players" 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_us: "Contact CodeCombat" @@ -878,7 +891,6 @@ tutorial_play_first: "Play the Tutorial first." simple_ai: "Simple AI" warmup: "Warmup" - vs: "VS" friends_playing: "Friends Playing" log_in_for_friends: "Log in to play with your friends!" social_connect_blurb: "Connect and play against your friends!" diff --git a/app/models/CocoModel.coffee b/app/models/CocoModel.coffee index 5d1770c45..d59bd5dc6 100644 --- a/app/models/CocoModel.coffee +++ b/app/models/CocoModel.coffee @@ -62,7 +62,7 @@ class CocoModel extends Backbone.Model super(attribute) 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 @markToRevert() unless inFlux or @_revertAttributes or @project or options?.fromMerge res = super attributes, options @@ -125,8 +125,12 @@ class CocoModel extends Backbone.Model error(@, res) if error return unless @notyErrors errorMessage = "Error saving #{@get('name') ? @type()}" - console.error errorMessage, res.responseJSON - noty text: "#{errorMessage}: #{res.status} #{res.statusText}", layout: 'topCenter', type: 'error', killer: false, timeout: 10000 + 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 + catch notyError + console.warn "Couldn't even show noty error for", error, "because", notyError @trigger 'save', @ return super attrs, options diff --git a/app/models/Level.coffee b/app/models/Level.coffee index 6830d870b..a08e814af 100644 --- a/app/models/Level.coffee +++ b/app/models/Level.coffee @@ -170,14 +170,31 @@ module.exports = class Level extends CocoModel thang.components = sorted 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 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} + unless isPhysical + originalComponent = $.extend true, {}, component component.config ?= {} - TreemaUtils.populateDefaults(component.config, lc.configSchema, tv4) + TreemaUtils.populateDefaults(component.config, lc.configSchema ? {}, tv4) @lastType = 'component' @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) -> for system in levelSystems ? [] @@ -185,21 +202,6 @@ module.exports = class Level extends CocoModel TreemaUtils.populateDefaults(system.config, system.model.configSchema, tv4) @lastType = 'system' @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: -> width = 0 diff --git a/app/schemas/models/level.coffee b/app/schemas/models/level.coffee index 993eed92f..9e1577b22 100644 --- a/app/schemas/models/level.coffee +++ b/app/schemas/models/level.coffee @@ -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.') 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} - 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.'} 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.'} diff --git a/app/schemas/models/level_component.coffee b/app/schemas/models/level_component.coffee index 1bf647015..6a4b8fa72 100644 --- a/app/schemas/models/level_component.coffee +++ b/app/schemas/models/level_component.coffee @@ -102,7 +102,7 @@ DependencySchema = c.object { LevelComponentSchema = c.object { title: 'Component' description: 'A Component which can affect Thang behavior.' - required: ['system', 'name', 'description', 'code', 'dependencies', 'propertyDocumentation', 'codeLanguage'] + required: ['system', 'name', 'code'] default: system: 'ai' name: 'AttacksSelf' diff --git a/app/schemas/subscriptions/editor.coffee b/app/schemas/subscriptions/editor.coffee index db490f17c..2665438be 100644 --- a/app/schemas/subscriptions/editor.coffee +++ b/app/schemas/subscriptions/editor.coffee @@ -24,6 +24,10 @@ module.exports = 'editor:edit-level-thang': c.object {required: ['thangID']}, 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']}, thangData: {type: 'object'} oldPath: {type: 'string'} diff --git a/app/styles/front-view.sass b/app/styles/front-view.sass new file mode 100644 index 000000000..f0a4438a0 --- /dev/null +++ b/app/styles/front-view.sass @@ -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 diff --git a/app/styles/play.sass b/app/styles/play.sass index 1a85cd747..f2ba54a11 100644 --- a/app/styles/play.sass +++ b/app/styles/play.sass @@ -47,3 +47,17 @@ color: black 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 + diff --git a/app/styles/play/ladder/play_modal.sass b/app/styles/play/ladder/play_modal.sass index 1bed7b9d1..95805da4d 100644 --- a/app/styles/play/ladder/play_modal.sass +++ b/app/styles/play/ladder/play_modal.sass @@ -16,8 +16,7 @@ height: 100px overflow: hidden background: white - border: 1px solid #333 - border-radius: 5px + border-radius: 8px position: relative -webkit-transition: opacity 0.3s ease-in-out -moz-transition: opacity 0.3s ease-in-out @@ -46,15 +45,15 @@ .my-team-icon height: 60px position: relative - top: -10px - left: 10px + top: -3.5px + left: 13.5px z-index: 0 .opponent-team-icon height: 60px position: relative - top: 10px - right: 10px + top: 16.5px + right: 13.5px z-index: 0 float: right -moz-transform: scaleX(-1) @@ -78,19 +77,19 @@ z-index: 1 .name-label - border-bottom: 20px solid lightslategray - height: 0 + height: 20px width: 40% position: absolute bottom: 0 - color: black + color: white font-weight: bold + text-shadow: -1px -1px 0px black text-align: center z-index: 2 span position: relative - top: 1px + top: 4px .code-language position: absolute @@ -116,9 +115,6 @@ right: 3px .difficulty - border-top: 25px solid darkgray - border-left: 20px solid transparent - border-right: 20px solid transparent height: 0 width: 30% position: absolute @@ -131,17 +127,21 @@ span position: relative - top: -25px + top: 6px + + .play-option + background-image: url(/images/pages/play/ladder/warmup_button.png) - .easy-option .difficulty - border-top: 25px solid limegreen + .easy-option + background-image: url(/images/pages/play/ladder/easy_button.png) - .medium-option .difficulty - border-top: 25px solid darkorange + .medium-option + background-image: url(/images/pages/play/ladder/medium_button.png) - .hard-option .difficulty - border-top: 25px solid black - color: white + .hard-option + background-image: url(/images/pages/play/ladder/hard_button.png) + .difficulty + color: white .vs position: absolute diff --git a/app/styles/play/modal/play-account-modal.sass b/app/styles/play/modal/play-account-modal.sass new file mode 100644 index 000000000..b30d2d3a2 --- /dev/null +++ b/app/styles/play/modal/play-account-modal.sass @@ -0,0 +1,4 @@ +#play-account-modal + .account-view + color: black + diff --git a/app/styles/play/modal/play-achievements-modal.sass b/app/styles/play/modal/play-achievements-modal.sass new file mode 100644 index 000000000..5f3652962 --- /dev/null +++ b/app/styles/play/modal/play-achievements-modal.sass @@ -0,0 +1,4 @@ +#play-achievements-modal + .achievement-view + color: black + diff --git a/app/styles/play/modal/play-heroes-modal.sass b/app/styles/play/modal/play-heroes-modal.sass new file mode 100644 index 000000000..de555028a --- /dev/null +++ b/app/styles/play/modal/play-heroes-modal.sass @@ -0,0 +1,4 @@ +#play-heroes-modal + .hero-view + color: black + diff --git a/app/styles/play/modal/play-items-modal.sass b/app/styles/play/modal/play-items-modal.sass new file mode 100644 index 000000000..71c03ee4e --- /dev/null +++ b/app/styles/play/modal/play-items-modal.sass @@ -0,0 +1,8 @@ +#play-items-modal + .item-view + width: 420px + float: left + background-color: white + border-radius: 6px + margin: 5px + diff --git a/app/styles/play/modal/play-settings-modal.sass b/app/styles/play/modal/play-settings-modal.sass new file mode 100644 index 000000000..5e71053e5 --- /dev/null +++ b/app/styles/play/modal/play-settings-modal.sass @@ -0,0 +1,4 @@ +#play-settings-modal + .settings-view + color: black + diff --git a/app/styles/play/world-map-view.sass b/app/styles/play/world-map-view.sass new file mode 100644 index 000000000..94212d574 --- /dev/null +++ b/app/styles/play/world-map-view.sass @@ -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 diff --git a/app/templates/account/profile.jade b/app/templates/account/profile.jade index bbe9daba6..c18a85917 100644 --- a/app/templates/account/profile.jade +++ b/app/templates/account/profile.jade @@ -177,7 +177,7 @@ block content if !editing && !myProfile button#contact-candidate.btn.btn-large.btn-inverse.flat-button span(data-i18n="account_profile.contact") Contact - | #{profile.name.split(' ')[0]} + | #{(profile.name || user.get('name') || 'Anonymous').split(' ')[0]} if me.isAdmin() select#admin-contact.form-control for contact in adminContacts diff --git a/app/templates/editor/level/modal/world_select.jade b/app/templates/editor/level/modal/world_select.jade index fbc77f1df..3fe648e6a 100644 --- a/app/templates/editor/level/modal/world_select.jade +++ b/app/templates/editor/level/modal/world_select.jade @@ -24,9 +24,14 @@ block modal-body-content div.alert.alert-info strong Shift-drag | to select - div.alert.alert-info - strong Enter - | to confirm + if flexibleRegion + div.alert.alert-info + strong Alt-shift-drag + | to select ratio + else + div.alert.alert-info + strong Enter + | to confirm canvas(width=924, height=589) block modal-footer-content diff --git a/app/templates/front-view.jade b/app/templates/front-view.jade new file mode 100644 index 000000000..340235513 --- /dev/null +++ b/app/templates/front-view.jade @@ -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 diff --git a/app/templates/front.jade b/app/templates/front.jade new file mode 100644 index 000000000..cdd11c19f --- /dev/null +++ b/app/templates/front.jade @@ -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 diff --git a/app/templates/modal/modal_base.jade b/app/templates/modal/modal_base.jade index c8374033e..98f843662 100644 --- a/app/templates/modal/modal_base.jade +++ b/app/templates/modal/modal_base.jade @@ -6,14 +6,20 @@ if closeButton .button.close(type="button", data-dismiss="modal", aria-hidden="true") × block modal-header-content - h3 man bites God + if headerContent + h3!= headerContent + else + h3 man bites God block modal-body .modal-body block modal-body-content - 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/manrantb.jpg") + 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. + img(src="http://www.manbitesgod.com/images/picturecoupleb.jpg") + img(src="http://www.manbitesgod.com/images/manrantb.jpg") .modal-body.wait.secret block modal-body-wait-content diff --git a/app/templates/play/ladder/ladder.jade b/app/templates/play/ladder/ladder.jade index f031954ed..e55ee9137 100644 --- a/app/templates/play/ladder/ladder.jade +++ b/app/templates/play/ladder/ladder.jade @@ -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 | . + 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.column.col-md-2 @@ -86,7 +98,7 @@ block content if level.get('name') == 'Greed' li 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 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 | . - if level.get('name') == 'Greed' || level.get('name') == 'Criss-Cross!!!' + if level.get('name') == 'Greed' || level.get('name') == 'Criss-Cross' .tab-pane.well#winners h1(data-i18n="ladder.winners") Winners @@ -734,9 +746,15 @@ block content tr th(data-i18n="ladder_prizes.rank") Rank th Human - th Human wins/losses/ties + if level.get('name') == 'Greed' + th Human wins/losses/ties + else + th Human score th Ogre - th Ogre wins/losses/ties + if level.get('name') == 'Greed' + th Ogre wins/losses/ties + else + th Ogre score th Spectate tbody each human, index in winners.humans @@ -744,20 +762,28 @@ block content tr td= human.rank td= human.name - td - span.win= human.wins - | - - span.loss= human.losses - | - - span.tie= 377 - human.wins - human.losses + if level.get('name') == 'Greed' + td + span.win= human.wins + | - + span.loss= human.losses + | - + span.tie= 377 - human.wins - human.losses + else + td + span= Math.round(100 * human.score) if ogre td= ogre.name - td - span.win= ogre.wins - | - - span.loss= ogre.losses - | - - span.tie= 407 - ogre.wins - ogre.losses + if level.get('name') == 'Greed' + td + span.win= ogre.wins + | - + span.loss= ogre.losses + | - + span.tie= 407 - ogre.wins - ogre.losses + else + td + span= Math.round(100 * ogre.score) td a(href="/play/spectate/" + level.get('slug') + "?session-one=" + human.sessionID + "&session-two=" + ogre.sessionID) Watch the battle else diff --git a/app/templates/play/ladder/play_modal.jade b/app/templates/play/ladder/play_modal.jade index 22b4db84c..459d7ec9e 100644 --- a/app/templates/play/ladder/play_modal.jade +++ b/app/templates/play/ladder/play_modal.jade @@ -37,7 +37,6 @@ block modal-body-content //span.code-language(style="background-image: url(/images/common/code_languages/javascript_small.png)") div.difficulty span(data-i18n="ladder.warmup") Warmup - div(data-i18n="ladder.vs").vs VS if challengers.easy 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)") div.difficulty span(data-i18n="general.easy") Easy - div(data-i18n="ladder.vs").vs VS if challengers.medium 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)") div.difficulty span(data-i18n="general.medium") Medium - div(data-i18n="ladder.vs").vs VS if challengers.hard 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)") div.difficulty span(data-i18n="general.hard") Hard - div(data-i18n="ladder.vs").vs VS block modal-footer \ No newline at end of file diff --git a/app/templates/play/modal/play-account-modal.jade b/app/templates/play/modal/play-account-modal.jade new file mode 100644 index 000000000..a8d7f9800 --- /dev/null +++ b/app/templates/play/modal/play-account-modal.jade @@ -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 diff --git a/app/templates/play/modal/play-achievements-modal.jade b/app/templates/play/modal/play-achievements-modal.jade new file mode 100644 index 000000000..13d244310 --- /dev/null +++ b/app/templates/play/modal/play-achievements-modal.jade @@ -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 diff --git a/app/templates/play/modal/play-heroes-modal.jade b/app/templates/play/modal/play-heroes-modal.jade new file mode 100644 index 000000000..79643ef42 --- /dev/null +++ b/app/templates/play/modal/play-heroes-modal.jade @@ -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 diff --git a/app/templates/play/modal/play-items-modal.jade b/app/templates/play/modal/play-items-modal.jade new file mode 100644 index 000000000..172c500bc --- /dev/null +++ b/app/templates/play/modal/play-items-modal.jade @@ -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) diff --git a/app/templates/play/modal/play-settings-modal.jade b/app/templates/play/modal/play-settings-modal.jade new file mode 100644 index 000000000..ee37a395d --- /dev/null +++ b/app/templates/play/modal/play-settings-modal.jade @@ -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 diff --git a/app/templates/play/world-map-view.jade b/app/templates/play/world-map-view.jade new file mode 100644 index 000000000..bd35f17f3 --- /dev/null +++ b/app/templates/play/world-map-view.jade @@ -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") diff --git a/app/views/FrontView.coffee b/app/views/FrontView.coffee new file mode 100644 index 000000000..c137b4afd --- /dev/null +++ b/app/views/FrontView.coffee @@ -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 = ''' +

We are working on it!

+

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.

+ ''' + notImplementedModal = new ModalView headerContent: header, bodyContent: body + @openModalView notImplementedModal diff --git a/app/views/editor/level/components/NewLevelComponentModal.coffee b/app/views/editor/level/components/NewLevelComponentModal.coffee index a65b5c921..42db1c6e0 100644 --- a/app/views/editor/level/components/NewLevelComponentModal.coffee +++ b/app/views/editor/level/components/NewLevelComponentModal.coffee @@ -26,7 +26,7 @@ module.exports = class NewLevelComponentModal extends ModalView component = new LevelComponent() component.set 'system', system 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 res = component.save() return unless res diff --git a/app/views/editor/level/modals/WorldSelectModal.coffee b/app/views/editor/level/modals/WorldSelectModal.coffee index 38ba12fc6..95c949f7a 100644 --- a/app/views/editor/level/modals/WorldSelectModal.coffee +++ b/app/views/editor/level/modals/WorldSelectModal.coffee @@ -31,11 +31,11 @@ module.exports = class WorldSelectModal extends ModalView getRenderData: (c={}) => c = super(c) c.selectingPoint = @dataType is 'point' + c.flexibleRegion = @dataType is 'region' c afterInsert: -> super() - window.e = @$el @initSurface() # surface setup @@ -54,7 +54,6 @@ module.exports = class WorldSelectModal extends ModalView thangTypes: @supermodel.getModels(ThangType) showInvisible: true } - window.s = @surface @surface.playing = false @surface.setWorld @world @surface.camera.zoomTo({x: 262, y: -164}, 1.66, 0) diff --git a/app/views/editor/level/thangs/LevelThangEditView.coffee b/app/views/editor/level/thangs/LevelThangEditView.coffee index 635b6b9c4..d5debf33b 100644 --- a/app/views/editor/level/thangs/LevelThangEditView.coffee +++ b/app/views/editor/level/thangs/LevelThangEditView.coffee @@ -19,6 +19,8 @@ module.exports = class LevelThangEditView extends CocoView 'click #thang-type-link span': 'toggleTypeEdit' 'blur #thang-name-link input': 'toggleNameEdit' 'blur #thang-type-link input': 'toggleTypeEdit' + 'keydown #thang-name-link input': 'toggleNameEditIfReturn' + 'keydown #thang-type-link input': 'toggleTypeEditIfReturn' constructor: (options) -> options ?= {} @@ -27,6 +29,7 @@ module.exports = class LevelThangEditView extends CocoView @thangData = $.extend true, {}, options.thangData ? {} @level = options.level @oldPath = options.oldPath + @reportChanges = _.debounce @reportChanges, 1000 getRenderData: (context={}) -> context = super(context) @@ -83,5 +86,16 @@ module.exports = class LevelThangEditView extends CocoView if thangType and wasEditing @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) => @thangData.components = components + @reportChanges() + + reportChanges: => + return if @destroyed + Backbone.Mediator.publish 'editor:level-thang-edited', {thangData: $.extend(true, {}, @thangData), oldPath: @oldPath} diff --git a/app/views/editor/level/thangs/ThangsTabView.coffee b/app/views/editor/level/thangs/ThangsTabView.coffee index 03ee1d0a4..6a9b3376d 100644 --- a/app/views/editor/level/thangs/ThangsTabView.coffee +++ b/app/views/editor/level/thangs/ThangsTabView.coffee @@ -16,7 +16,7 @@ MOVE_MARGIN = 0.15 MOVE_SPEED = 13 # 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 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-out': 'onSurfaceMouseOut' 'editor:edit-level-thang': 'editThang' + 'editor:level-thang-edited': 'onLevelThangEdited' 'editor:level-thang-done-editing': 'onLevelThangDoneEditing' 'editor:view-switched': 'onViewSwitched' 'sprite:dragged': 'onSpriteDragged' 'sprite:mouse-up': 'onSpriteMouseUp' 'sprite:mouse-down': 'onSpriteMouseDown' 'sprite:double-clicked': 'onSpriteDoubleClicked' + 'surface:stage-mouse-down': 'onStageMouseDown' 'surface:stage-mouse-up': 'onStageMouseUp' 'editor:random-terrain-generated': 'onRandomTerrainGenerated' @@ -225,8 +227,17 @@ module.exports = class ThangsTabView extends CocoView # if e.originalEvent.nativeEvent.button == 2 # @onSpriteContextMenu e + onStageMouseDown: (e) -> + return unless @addThangSprite?.thangType.get('kind') is 'Wall' + @surface.camera.dragDisabled = true + @paintingWalls = true + 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() # If we click on the background, we need to add @addThangSprite, but not if onSpriteMouseUp will fire. @backgroundAddClickTimeout = _.defer => @onExtantThangSelected {} @@ -295,7 +306,12 @@ module.exports = class ThangsTabView extends CocoView @selectedExtantSprite?.setNameLabel? null unless @selectedExtantSprite is e.sprite @selectedExtantThang = e.thang @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 @selectAddThangType e.thang.spriteName, @selectedExtantThang else if @justAdded() @@ -389,6 +405,16 @@ module.exports = class ThangsTabView extends CocoView wop = @surface.camera.screenToWorld x: e.x, y: e.y wop.z = 0.5 @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 onSurfaceMouseOver: (e) -> @@ -427,6 +453,7 @@ module.exports = class ThangsTabView extends CocoView deleteSelectedExtantThang: (e) => return if $(e.target).hasClass 'treema-node' + return unless @selectedExtantThang thang = @getThangByID(@selectedExtantThang.id) @thangsTreema.delete(@pathForThang(thang)) 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) -> @removeSubView @editThangView @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 - @thangsTreema.delete e.oldPath + @thangsTreema.delete oldPath @populateFoldersForThang(newThang) @thangsTreema.set(@pathForThang(newThang), newThang) @hush = false @onThangsChanged() - @$el.find('>').show() preventDefaultContextMenu: (e) -> return unless $(e.target).closest('#canvas-wrapper').length diff --git a/app/views/game-menu/GameMenuModal.coffee b/app/views/game-menu/GameMenuModal.coffee index 042c1f9b5..ba9493ddb 100644 --- a/app/views/game-menu/GameMenuModal.coffee +++ b/app/views/game-menu/GameMenuModal.coffee @@ -41,6 +41,7 @@ module.exports = class GameMenuModal extends ModalView Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-tab-switch', volume: 1 onHidden: -> + super() subview.onHidden?() for subviewKey, subview of @subviews me.patch() Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-close', volume: 1 diff --git a/app/views/kinds/CocoView.coffee b/app/views/kinds/CocoView.coffee index 19aab42d2..7e9e1f29f 100644 --- a/app/views/kinds/CocoView.coffee +++ b/app/views/kinds/CocoView.coffee @@ -176,7 +176,7 @@ module.exports = class CocoView extends Backbone.View target = elem.data('target') Modal = require 'views/'+target e.stopPropagation() - @openModalView new Modal + @openModalView new Modal supermodel: @supermodal openModalView: (modalView, softly=false) -> return if waitingModal # can only have one waiting at once diff --git a/app/views/kinds/ModalView.coffee b/app/views/kinds/ModalView.coffee index 5fd527b9d..e3290c137 100644 --- a/app/views/kinds/ModalView.coffee +++ b/app/views/kinds/ModalView.coffee @@ -7,6 +7,7 @@ module.exports = class ModalView extends CocoView modalWidthPercent: null plain: false instant: false + template: require 'templates/modal/modal_base' events: 'click a': 'toggleModal' @@ -26,6 +27,8 @@ module.exports = class ModalView extends CocoView getRenderData: (context={}) -> context = super(context) context.closeButton = @closeButton + context.headerContent = @options.headerContent + context.bodyContent = @options.bodyContent context subscriptions: diff --git a/app/views/play/WorldMapView.coffee b/app/views/play/WorldMapView.coffee new file mode 100644 index 000000000..a73108f04 --- /dev/null +++ b/app/views/play/WorldMapView.coffee @@ -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 Artisan Wizards.', levels: playerCreated, color: "rgb(160, 160, 180)"} +] diff --git a/app/views/play/ladder/tournament_results.coffee b/app/views/play/ladder/tournament_results.coffee index 3cbe60305..6b4ae28fb 100644 --- a/app/views/play/ladder/tournament_results.coffee +++ b/app/views/play/ladder/tournament_results.coffee @@ -1,4 +1,4 @@ -module.exports = results = greed: {} +module.exports = results = greed: {}, 'criss-cross': {} results.greed.humans = [ {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: 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} +] diff --git a/app/views/play/level/LevelChatView.coffee b/app/views/play/level/LevelChatView.coffee index 8e71c2c24..4b334a8c1 100644 --- a/app/views/play/level/LevelChatView.coffee +++ b/app/views/play/level/LevelChatView.coffee @@ -27,7 +27,10 @@ module.exports = class LevelChatView extends CocoView updateMultiplayerVisibility: -> return unless @$el? - @$el.toggle Boolean @session.get('multiplayer') + try + @$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: -> @chatTables = $('table', @$el) diff --git a/app/views/play/modal/PlayAccountModal.coffee b/app/views/play/modal/PlayAccountModal.coffee new file mode 100644 index 000000000..0d577f077 --- /dev/null +++ b/app/views/play/modal/PlayAccountModal.coffee @@ -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 diff --git a/app/views/play/modal/PlayAchievementsModal.coffee b/app/views/play/modal/PlayAchievementsModal.coffee new file mode 100644 index 000000000..ca0940b88 --- /dev/null +++ b/app/views/play/modal/PlayAchievementsModal.coffee @@ -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 diff --git a/app/views/play/modal/PlayHeroesModal.coffee b/app/views/play/modal/PlayHeroesModal.coffee new file mode 100644 index 000000000..f9df489d6 --- /dev/null +++ b/app/views/play/modal/PlayHeroesModal.coffee @@ -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) diff --git a/app/views/play/modal/PlayItemsModal.coffee b/app/views/play/modal/PlayItemsModal.coffee new file mode 100644 index 000000000..816923dca --- /dev/null +++ b/app/views/play/modal/PlayItemsModal.coffee @@ -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) diff --git a/app/views/play/modal/PlaySettingsModal.coffee b/app/views/play/modal/PlaySettingsModal.coffee new file mode 100644 index 000000000..d859a765d --- /dev/null +++ b/app/views/play/modal/PlaySettingsModal.coffee @@ -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