diff --git a/app/assets/images/pages/about/coco_comic.jpg b/app/assets/images/pages/about/coco_comic.jpg new file mode 100644 index 000000000..87ff27a54 Binary files /dev/null and b/app/assets/images/pages/about/coco_comic.jpg differ diff --git a/app/assets/images/pages/about/michael_small.png b/app/assets/images/pages/about/michael_small.png index e49de0534..84d56d4c3 100644 Binary files a/app/assets/images/pages/about/michael_small.png and b/app/assets/images/pages/about/michael_small.png differ diff --git a/app/assets/images/pages/about/placeholder.png b/app/assets/images/pages/about/placeholder.png new file mode 100644 index 000000000..997286b61 Binary files /dev/null and b/app/assets/images/pages/about/placeholder.png differ diff --git a/app/lib/surface/SpriteBoss.coffee b/app/lib/surface/SpriteBoss.coffee index 361e17318..b994b5a39 100644 --- a/app/lib/surface/SpriteBoss.coffee +++ b/app/lib/surface/SpriteBoss.coffee @@ -334,7 +334,7 @@ module.exports = class SpriteBoss extends CocoClass onFlagUpdated: (e) -> return unless e.active - pendingFlag = new FlagSprite @thangTypeFor('Flag'), @createSpriteOptions(thangID: 'Pending Flag ' + Math.random(), color: e.color, team: me.team, isCursor: false, pos: e.pos) + pendingFlag = new FlagSprite @thangTypeFor('Flag'), @createSpriteOptions(thangID: 'Pending Flag ' + Math.random(), color: e.color, team: e.team, isCursor: false, pos: e.pos) @addSprite pendingFlag, pendingFlag.thang.id, @spriteLayers['Floating'] @pendingFlags.push pendingFlag diff --git a/app/locale/en.coffee b/app/locale/en.coffee index a54115787..755ada82a 100644 --- a/app/locale/en.coffee +++ b/app/locale/en.coffee @@ -657,12 +657,16 @@ why_paragraph_4: "If you're going to get addicted to some game, get addicted to this one and become one of the wizards of the tech age." why_ending: "And hey, it's free. " why_ending_url: "Start wizarding now!" - george_description: "CEO, business guy, web designer, game designer, and champion of beginning programmers everywhere." - scott_description: "Programmer extraordinaire, software architect, kitchen wizard, and master of finances. Scott is the reasonable one." - nick_description: "Programming wizard, eccentric motivation mage, and upside-down experimenter. Nick can do anything and chooses to build CodeCombat." - jeremy_description: "Customer support mage, usability tester, and community organizer; you've probably already spoken with Jeremy." - michael_description: "Programmer, sys-admin, and undergrad technical wunderkind, Michael is the person keeping our servers online." - matt_description: "Bicyclist, Software Engineer, reader of heroic fantasy, connoisseur of peanut butter, sipper of coffee." + george_title: "CEO" + george_description: "Businesser" + scott_title: "Programmer" + scott_description: "Reasonable One" + nick_title: "Programmer" + nick_description: "Motivation Guru" + michael_title: "Programmer" + michael_description: "Sys Admin" + matt_title: "Programmer" + matt_description: "Bicyclist" legal: page_title: "Legal" diff --git a/app/schemas/models/achievement.coffee b/app/schemas/models/achievement.coffee index fe45e03b5..37690cadf 100644 --- a/app/schemas/models/achievement.coffee +++ b/app/schemas/models/achievement.coffee @@ -2,8 +2,7 @@ c = require './../schemas' # TODO add these: http://docs.mongodb.org/manual/reference/operator/query/ MongoQueryOperatorSchema = - title: 'MongoDB Query operator' - id: 'mongoQueryOperator' + title: 'Query Operator' type: 'object' properties: '$gt': type: 'number' @@ -13,26 +12,23 @@ MongoQueryOperatorSchema = '$lte': type: 'number' '$ne': type: ['number', 'string'] '$nin': type: 'array' - additionalProperties: true # TODO set to false when the schema's done + '$exists': type: 'boolean' + additionalProperties: false MongoFindQuerySchema = - title: 'MongoDB Query' - id: 'mongoFindQuery' + title: 'Query' type: 'object' patternProperties: - #'^[-a-zA-Z0-9_]*$': - '^[-a-zA-Z0-9\.]*$': - oneOf: [ - #{$ref: '#/definitions/' + MongoQueryOperatorSchema.id}, + '^[-a-zA-Z0-9.]*$': + anyOf: [ + {$ref: '#/definitions/mongoQueryOperator'}, {type: 'string'} {type: 'object'} {type: 'boolean'} ] - additionalProperties: true # TODO make Treema accept new pattern matched keys + additionalProperties: false definitions: {} -MongoFindQuerySchema.definitions[MongoQueryOperatorSchema.id] = MongoQueryOperatorSchema - AchievementSchema = c.object() c.extendNamedProperties AchievementSchema c.extendBasicProperties AchievementSchema, 'achievement' @@ -48,7 +44,7 @@ AchievementSchema.default = _.extend AchievementSchema.properties, query: #type:'object' - $ref: '#/definitions/' + MongoFindQuerySchema.id + $ref: '#/definitions/mongoFindQuery' worth: c.float collection: {type: 'string'} description: c.shortString() @@ -93,6 +89,7 @@ _.extend AchievementSchema, # Let's have these on the bottom additionalProperties: false AchievementSchema.definitions = {} -AchievementSchema.definitions[MongoFindQuerySchema.id] = MongoFindQuerySchema +AchievementSchema.definitions['mongoQueryOperator'] = MongoQueryOperatorSchema +AchievementSchema.definitions['mongoFindQuery'] = MongoFindQuerySchema module.exports = AchievementSchema diff --git a/app/schemas/models/level_component.coffee b/app/schemas/models/level_component.coffee index 352573ad0..1bf647015 100644 --- a/app/schemas/models/level_component.coffee +++ b/app/schemas/models/level_component.coffee @@ -148,10 +148,7 @@ _.extend LevelComponentSchema.properties, type: 'boolean' title: 'Official' description: 'Whether this is an official CodeCombat Component.' - searchStrings: { - type: 'array' - items: { type: 'string' } - } + searchStrings: {type: 'string'} c.extendBasicProperties LevelComponentSchema, 'level.component' c.extendSearchableProperties LevelComponentSchema diff --git a/app/schemas/subscriptions/multiplayer.coffee b/app/schemas/subscriptions/multiplayer.coffee index 5ad56607d..95bf1a325 100644 --- a/app/schemas/subscriptions/multiplayer.coffee +++ b/app/schemas/subscriptions/multiplayer.coffee @@ -5,10 +5,10 @@ module.exports = session: {type: 'object'} 'real-time-multiplayer:left-game': c.object {title: 'Multiplayer left game'} - + 'real-time-multiplayer:manual-cast': c.object {title: 'Multiplayer manual cast'} - + 'real-time-multiplayer:new-opponent-code': c.object {title: 'Multiplayer new opponent code', required: ['code', 'codeLanguage']}, code: {type: 'object'} codeLanguage: {type: 'string'} - + team: {type: 'string'} diff --git a/app/schemas/subscriptions/surface.coffee b/app/schemas/subscriptions/surface.coffee index 99063aae2..0ca5a6a92 100644 --- a/app/schemas/subscriptions/surface.coffee +++ b/app/schemas/subscriptions/surface.coffee @@ -180,5 +180,5 @@ module.exports = # /app/lib/surface 'surface:remove-selected-flag': c.object {} - 'surface:remove-flag': c.object {required: 'color'}, + 'surface:remove-flag': c.object {required: ['color']}, color: {type: 'string'} diff --git a/app/styles/about.sass b/app/styles/about.sass index 90cb3526c..ccb7787f7 100644 --- a/app/styles/about.sass +++ b/app/styles/about.sass @@ -18,6 +18,13 @@ &:hover background-color: rgba(200, 244, 255, 0.2) float: left - width: 100px - height: 100px - margin-top: 22px + width: 80px + height: 80px + margin: 0px 10px 22px 0px + + .team_name + margin-top: 0 + + .team_bio + width: 150px + float: left \ No newline at end of file diff --git a/app/styles/play/level/hud.sass b/app/styles/play/level/hud.sass index fd30f304f..0b9f3dc8f 100644 --- a/app/styles/play/level/hud.sass +++ b/app/styles/play/level/hud.sass @@ -121,7 +121,7 @@ background-position-x: -4 * $iconSize &.prop-label-icon-attackDamage background-position-x: -5 * $iconSize - &.prop-label-icon-attackRange + &.prop-label-icon-attackRange, &.prop-label-icon-attackNearbyEnemyRange background-position-x: -6 * $iconSize &.prop-label-icon-maxSpeed background-position-x: -7 * $iconSize diff --git a/app/templates/about.jade b/app/templates/about.jade index b9ffc3a57..fa31a820c 100644 --- a/app/templates/about.jade +++ b/app/templates/about.jade @@ -2,48 +2,17 @@ extends /templates/base block content + img(src="/images/pages/about/coco_comic.jpg") + .row .col-sm-6 - h2(data-i18n="about.who_is_codecombat") - | Who is CodeCombat? - - p - a(href="http://georgesaines.com/") George - span , - a(href="http://scotterickson.info/") Scott - span , - span(data-i18n="general.and") and - span - a(href="http://www.nickwinter.net") Nick - span - span(data-i18n="about.who_description_prefix") - | together started CodeCombat in 2013. We also created - a(href="http://www.skritter.com/") Skritter - span - span(data-i18n="about.who_description_suffix") - | in 2008, growing it to the #1 web and iOS application - | for learning to write Chinese and Japanese characters. - - p(data-i18n="about.who_description_ending") - | Now it's time to teach people to write code. - h2(data-i18n="about.why_codecombat") | Why CodeCombat? - - p(data-i18n="about.why_paragraph_1") - | When making Skritter, George didn't know how to program and was constantly - | frustrated by his inability to implement his ideas. Afterwards, - | he tried learning, but the lessons were too slow. His housemate, - | wanting to reskill and stop teaching, tried Codecademy, but "got bored." - | Each week another friend started Codecademy, then dropped off. - | We realized it was the same problem we'd solved with Skritter: - | people learning a skill via slow, intensive lessons when what they need is fast, - | extensive practice. We know how to fix that. p(data-i18n="about.why_paragraph_2") - | Need to learn to code? You don't need lessons. + | If you want to learn to program, you don't need lessons. | You need to write a lot of code and have a great time doing it. p @@ -69,11 +38,13 @@ block content | If you're going to get addicted to some game, | get addicted to this one and become one of the wizards of the tech age. - p.lead - span(data-i18n="about.why_ending") - | And hey, it's free. - a(href="/", data-i18n="about.why_ending_url") - | Start wizarding now! + h2 + | Bloggers/Press + + p + | Want to write about us? Feel free to download and use all of the resources included in our + a(href="https://s3.amazonaws.com/CodeCombatMisc/press_packet.zip") press packet. + | All logos and images may be used without contacting us directly. ul.col-sm-6.team-column @@ -81,87 +52,74 @@ block content ul.thumbnails li.row + + h2 Team - a(href="http://georgesaines.com") - img(src="/images/pages/about/george_small.png").img-thumbnail + img(src="/images/pages/about/george_small.png").img-thumbnail - .col-sm-8 + .team_bio - h3 + h4.team_name a(href="http://georgesaines.com") George Saines + p(data-i18n="about.george_title") + | CEO p(data-i18n="about.george_description") - | CEO, business guy, web designer, game designer, - | and champion of beginning programmers everywhere. + | Businesser - li.row - a(href="http://scotterickson.info") img(src="/images/pages/about/scott_small.png").img-thumbnail - .col-sm-8 + .team_bio - h3 + h4.team_name a(href="http://scotterickson.info") Scott Erickson + p(data-i18n="about.scott_title") + | Programmer p(data-i18n="about.scott_description") - | Programmer extraordinaire, software architect, - | kitchen wizard, and master of finances. - | Scott is the reasonable one. + | Reasonable one li.row a(href="http://www.nickwinter.net") img(src="/images/pages/about/nick_small.png").img-thumbnail - .col-sm-8 + .team_bio - h3 + h4.team_name a(href="http://www.nickwinter.net") Nick Winter + p(data-i18n="about.nick_title") + | Programmer p(data-i18n="about.nick_description") - | Programming wizard, eccentric motivation mage, - | and upside-down experimenter. - | Nick can do anything and chooses to build CodeCombat. - - li.row - + | Motivation Guru + a(href="http://michaelschmatz.com") img(src="/images/pages/about/michael_small.png").img-thumbnail - .col-sm-8 + .team_bio - h3 + h4.team_name a(href="http://michaelschmatz.com/") Michael Schmatz + p(data-i18n="about.michael_title") + | Programmer p(data-i18n="about.michael_description") - | Programmer, sys-admin, and undergrad technical wunderkind, - | Michael is the person keeping our servers online. + | Sys Admin li.row - img(src="/images/pages/about/jeremy_small.png").img-thumbnail + img(src="/images/pages/about/placeholder.png").img-thumbnail - .col-sm-8 + .team_bio - h3 Jeremy Arns - - p(data-i18n="about.jeremy_description") - | Customer support mage, usability tester, - | and community organizer; you've probably - | already spoken with Jeremy. - - li.row - - img(src="/images/pages/contribute/archmage.png").img-thumbnail - - .col-sm-8 - - h3 + h4.team_name a(href="http://www.mattlott.com/") Matt Lott + p(data-i18n="about.matt_title") + | Programmer p(data-i18n="about.matt_description") - | Bicyclist, Software Engineer, reader of heroic fantasy, connoisseur - | of peanut butter, sipper of coffee. + | Bicyclist diff --git a/app/views/editor/level/modals/NewAchievementModal.coffee b/app/views/editor/level/modals/NewAchievementModal.coffee index 69847e26b..565f91af2 100644 --- a/app/views/editor/level/modals/NewAchievementModal.coffee +++ b/app/views/editor/level/modals/NewAchievementModal.coffee @@ -28,22 +28,13 @@ module.exports = class NewAchievementModal extends NewModelModal createQuery: -> checked = @$el.find('[name=queryOptions]:checked') checkedValues = ($(check).val() for check in checked) - subQueries = [] + query = {} for id in checkedValues switch id when 'misc-level-completion' - subQueries.push state: complete: true - else # It's a goal - q = state: goalStates: {} - q.state.goalStates[id] = {} - q.state.goalStates[id].status = 'success' - subQueries.push q - unless subQueries.length - query = {} - else if subQueries.length is 1 - query = subQueries[0] - else - query = $or: subQueries + query['state.complete'] = true + else + query["state.goalStates.#{id}.status"] = 'success' query['level.original'] = @level.get 'original' query diff --git a/app/views/play/level/LevelFlagsView.coffee b/app/views/play/level/LevelFlagsView.coffee index 96200cb6e..971095ffe 100644 --- a/app/views/play/level/LevelFlagsView.coffee +++ b/app/views/play/level/LevelFlagsView.coffee @@ -3,6 +3,8 @@ template = require 'templates/play/level/level-flags-view' {me} = require 'lib/auth' RealTimeCollection = require 'collections/RealTimeCollection' +multiplayerFlagDelay = 0.5 # Long, static second delay for now; should be more than enough. + module.exports = class LevelFlagsView extends CocoView id: 'level-flags-view' template: template @@ -54,7 +56,9 @@ module.exports = class LevelFlagsView extends CocoView onStageMouseDown: (e) -> return unless @flagColor and @realTime pos = x: e.worldPos.x, y: e.worldPos.y - flag = player: me.id, team: me.team, color: @flagColor, pos: pos, time: @world.dt * @world.frames.length, active: true + delay = if @realTimeFlags then multiplayerFlagDelay else 0 + now = @world.dt * @world.frames.length + flag = player: me.id, team: me.team, color: @flagColor, pos: pos, time: now + delay, active: true @flags[@flagColor] = flag @flagHistory.push flag @realTimeFlags?.create flag @@ -67,7 +71,9 @@ module.exports = class LevelFlagsView extends CocoView onRemoveFlag: (e) -> delete @flags[e.color] - flag = player: me.id, team: me.team, color: e.color, time: @world.dt * @world.frames.length, active: false + delay = if @realTimeFlags then multiplayerFlagDelay else 0 + now = @world.dt * @world.frames.length + flag = player: me.id, team: me.team, color: e.color, time: now + delay, active: false @flagHistory.push flag Backbone.Mediator.publish 'level:flag-updated', flag #console.log e.color, 'deleted at time', flag.time @@ -79,6 +85,7 @@ module.exports = class LevelFlagsView extends CocoView onJoinedMultiplayerGame: (e) -> @realTimeFlags = new RealTimeCollection('multiplayer_level_sessions/' + e.session.id + '/flagHistory') @realTimeFlags.on 'add', @onRealTimeMultiplayerFlagAdded + @realTimeFlags.on 'remove', @onRealTimeMultiplayerFlagRemoved onLeftMultiplayerGame: (e) -> if @realTimeFlags @@ -98,3 +105,5 @@ module.exports = class LevelFlagsView extends CocoView active: e.get('active') @flagHistory.push flag Backbone.Mediator.publish 'level:flag-updated', flag + + onRealTimeMultiplayerFlagRemoved: (e) => diff --git a/app/views/play/level/LevelHUDView.coffee b/app/views/play/level/LevelHUDView.coffee index 3f5c7f0ba..97cd9cad0 100644 --- a/app/views/play/level/LevelHUDView.coffee +++ b/app/views/play/level/LevelHUDView.coffee @@ -265,7 +265,7 @@ module.exports = class LevelHUDView extends CocoView return null # included in the bar context = prop: prop - hasIcon: prop in ['health', 'pos', 'target', 'collectedThangIDs', 'gold', 'bountyGold', 'visualRange', 'attackDamage', 'attackRange', 'maxSpeed'] + hasIcon: prop in ['health', 'pos', 'target', 'collectedThangIDs', 'gold', 'bountyGold', 'visualRange', 'attackDamage', 'attackRange', 'maxSpeed', 'attackNearbyEnemyRange'] hasBar: prop in ['health'] $(prop_template(context)) diff --git a/app/views/play/level/PlayLevelView.coffee b/app/views/play/level/PlayLevelView.coffee index 0cc1f4399..5c4b49889 100644 --- a/app/views/play/level/PlayLevelView.coffee +++ b/app/views/play/level/PlayLevelView.coffee @@ -619,6 +619,7 @@ module.exports = class PlayLevelView extends RootView myPlayer.set 'code', @session.get('code') myPlayer.set 'codeLanguage', @session.get('codeLanguage') myPlayer.set 'state', 'submitted' + myPlayer.set 'team', me.team else console.error 'Did not find my player in onRealTimeMultiplayerCast' if opponentPlayer @@ -636,7 +637,7 @@ module.exports = class PlayLevelView extends RootView onOpponentSubmitted: (opponentPlayer, myPlayer) => # Save opponent's code - Backbone.Mediator.publish 'real-time-multiplayer:new-opponent-code', {codeLanguage: opponentPlayer.get('codeLanguage'), code: opponentPlayer.get('code')} + Backbone.Mediator.publish 'real-time-multiplayer:new-opponent-code', {codeLanguage: opponentPlayer.get('codeLanguage'), code: opponentPlayer.get('code'), team: opponentPlayer.get('team')} # I'm ready to rumble myPlayer.set 'state', 'ready' if opponentPlayer.get('state') is 'ready' diff --git a/app/views/play/level/tome/Spell.coffee b/app/views/play/level/tome/Spell.coffee index 940715f45..0b74cf6f7 100644 --- a/app/views/play/level/tome/Spell.coffee +++ b/app/views/play/level/tome/Spell.coffee @@ -164,11 +164,11 @@ module.exports = class Spell false onNewOpponentCode: (e) => - return unless @spellKey + return unless @spellKey and @canWrite e.team if e.codeLanguage and e.code - spellkeyComponents = @spellKey.split '/' - if e.code[spellkeyComponents[0]]?[spellkeyComponents[1]] - @source = e.code[spellkeyComponents[0]][spellkeyComponents[1]] + [thangSlug, methodSlug] = @spellKey.split '/' + if opponentCode = e.code[thangSlug]?[methodSlug] + @source = opponentCode @updateLanguageAether e.codeLanguage else console.error 'Spell onNewOpponentCode did not receive code', e