diff --git a/app/Router.coffee b/app/Router.coffee index cce426ea1..c266a23ae 100644 --- a/app/Router.coffee +++ b/app/Router.coffee @@ -31,6 +31,7 @@ module.exports = class CocoRouter extends Backbone.Router 'admin/clas': go('admin/CLAsView') 'admin/employers': go('admin/EmployersListView') 'admin/files': go('admin/FilesView') + 'admin/growth': go('admin/GrowthView') 'admin/level-sessions': go('admin/LevelSessionsView') 'admin/users': go('admin/UsersView') 'admin/base': go('admin/BaseView') diff --git a/app/assets/images/level/scrubber_background.png b/app/assets/images/level/scrubber_background.png index a70abeb97..517a4c8c4 100644 Binary files a/app/assets/images/level/scrubber_background.png and b/app/assets/images/level/scrubber_background.png differ diff --git a/app/lib/world/world.coffee b/app/lib/world/world.coffee index 8a52db01d..1e47e5414 100644 --- a/app/lib/world/world.coffee +++ b/app/lib/world/world.coffee @@ -99,7 +99,7 @@ module.exports = class World continueLaterFn = => @loadFrames(loadedCallback, errorCallback, loadProgressCallback, preloadedCallback, skipDeferredLoading, loadUntilFrame) unless @destroyed if @realTime and not @countdownFinished - if @levelID in ['the-first-kithmaze', 'the-second-kithmaze', 'the-final-kithmaze', 'the-gauntlet', 'winding-trail', 'thornbush-farm'] + if @levelID in ['the-first-kithmaze', 'haunted-kithmaze', 'the-second-kithmaze', 'the-final-kithmaze', 'the-gauntlet', 'winding-trail', 'thornbush-farm'] @realTimeSpeedFactor = 5 else if @levelID in ['forgotten-gemsmith', 'descending-further', 'tactical-strike', 'kithgard-gates'] @realTimeSpeedFactor = 3 diff --git a/app/models/Level.coffee b/app/models/Level.coffee index 2e72342f2..4018fa923 100644 --- a/app/models/Level.coffee +++ b/app/models/Level.coffee @@ -102,6 +102,8 @@ module.exports = class Level extends CocoModel levelThangComponent.config.pos.y = placeholderConfig.pos.y else if placeholderConfig.team # Pull in Allied team levelThangComponent.config.team = placeholderConfig.team + else if placeholderConfig.significantProperty # For levels where we cheat on what counts as an enemy + levelThangComponent.config.significantProperty = placeholderConfig.significantProperty else if placeholderConfig.programmableMethods # Take the ThangType default Programmable and merge level-specific Component config into it copy = $.extend true, {}, placeholderConfig diff --git a/app/models/User.coffee b/app/models/User.coffee index 4dae57c6a..9fa59cb0f 100644 --- a/app/models/User.coffee +++ b/app/models/User.coffee @@ -88,7 +88,7 @@ module.exports = class User extends CocoModel when 2 then 'choice-explicit' when 3 then 'choice-implicit' @branchingGroup = 'choice-explicit' if me.isAdmin() - application.tracker.identify branchingGroup: @branchingGroup + application.tracker.identify branchingGroup: @branchingGroup unless me.isAdmin() @branchingGroup getHighlightArrowSoundGroup: -> @@ -98,5 +98,15 @@ module.exports = class User extends CocoModel when 0, 1, 2, 3 then 'sound-off' when 4, 5, 6, 7 then 'sound-on' @highlightArrowGroup = 'sound-off' if me.isAdmin() - application.tracker.identify highlightArrowGroup: @highlightArrowGroup + application.tracker.identify highlightArrowGroup: @highlightArrowGroup unless me.isAdmin() @highlightArrowGroup + + getKithmazeGroup: -> + return @kithmazeGroup if @kithmazeGroup + group = me.get('testGroupNumber') % 16 + @kithmazeGroup = switch group + when 0, 1, 2, 3, 4, 5, 6, 7 then 'the-first-kithmaze' + when 8, 9, 10, 11, 12, 13, 14, 15 then 'haunted-kithmaze' + @kithmazeGroup = 'haunted-kithmaze' if me.isAdmin() + application.tracker.identify kithmazeGroup: @kithmazeGroup unless me.isAdmin() + @kithmazeGroup diff --git a/app/styles/base.sass b/app/styles/base.sass index 0acc6fb48..581dd249a 100644 --- a/app/styles/base.sass +++ b/app/styles/base.sass @@ -153,40 +153,6 @@ a cursor: pointer -// Bigger versions of some Bootstrap icons -// TODO: make the non-white versions of these if we ever need them -.icon.big - background-image: url(/images/pages/base/glyphicons-simplified.png) - -.icon-white.big - background-image: url(/images/pages/base/glyphicons-simplified.png) - -.icon.big, .icon-white.big - width: 19px - height: 19px - line-height: 19px - -.icon-pause.big - background-position: -114px 0px - -.icon-play.big - background-position: -95px 0px - -.icon-repeat.big - background-position: -76px 0px - -.icon-volume-off.big - background-position: -57px 0px - -.icon-volume-down.big - background-position: -38px 0px - -.icon-volume-up.big - background-position: -19px 0px - -.icon-cog.big - background-position: 0px 0px - // loading screens for everything but the play view .loading-screen .progress @@ -339,3 +305,7 @@ kbd &.gem-60 width: 60px height: 60px + +.popover + border-image: url(/images/level/popover_background.png) 29 39 fill stretch + border-width: 15px 20px diff --git a/app/styles/play/level.sass b/app/styles/play/level.sass index 2a1fc972d..3ba3274b6 100644 --- a/app/styles/play/level.sass +++ b/app/styles/play/level.sass @@ -144,14 +144,6 @@ $level-resize-transition-time: 0.5s left: -3px bottom: 0 - #hud-top-gradient - top: -32px - background: linear-gradient(to bottom, rgba(0,0,0,0) 0%,rgba(0,0,0,0.8*$GI) 100%) - left: 0 - right: 0 - bottom: 0 - height: 3px - #canvas-left-gradient left: 0px width: 5px diff --git a/app/styles/play/level/goals.sass b/app/styles/play/level/goals.sass index 26f7bd5de..b65998a21 100644 --- a/app/styles/play/level/goals.sass +++ b/app/styles/play/level/goals.sass @@ -3,36 +3,37 @@ #goals-view position: absolute - left: 10px + left: -15px top: -100px @include transition(0.5s ease-in-out) - background-color: rgba(200,200,200,1.0) - - border: black - padding: 15px 7px 2px 5px - box-sizing: border-box - border: 1px solid #333 - border-radius: 5px + background: transparent url(/images/level/goals_background.png) + background-size: 100% 100% + + padding: 19px 17px 2px 25px z-index: 3 font-size: 14px &.brighter font-size: 18px font-size: 1.4vw - @include box-shadow(0px 0px 12px white) + //@include box-shadow(0px 0px 12px white) .goals-status margin: 0 - color: black + margin-top: 10px + color: white + text-transform: uppercase .success - color: darkgreen + color: lightgreen + text-shadow: 1px 1px 0px black .timed-out - color: darkslategray + color: rgb(230, 230, 230) .failure - color: darkred + color: rgb(239, 61, 71) + text-shadow: 1px 1px 0px black .incomplete - color: darkgoldenrod + color: rgb(245, 170, 49) ul padding-left: 0 @@ -59,3 +60,4 @@ #goals-view.collapsed ul display: none + diff --git a/app/styles/play/level/loading.sass b/app/styles/play/level/loading.sass index 2f829f5dc..a6b03af32 100644 --- a/app/styles/play/level/loading.sass +++ b/app/styles/play/level/loading.sass @@ -78,6 +78,7 @@ .start-level-button font-size: 40px + font-variant: small-caps .left-wing, .right-wing width: 100% diff --git a/app/styles/play/level/multiplayer-status.sass b/app/styles/play/level/multiplayer-status.sass index 0e16da6bf..c0fe93828 100644 --- a/app/styles/play/level/multiplayer-status.sass +++ b/app/styles/play/level/multiplayer-status.sass @@ -4,6 +4,8 @@ // TODO: Replace this devart with nice shinies #multiplayer-status-view + position: absolute + .player-count color: white .players-available diff --git a/app/styles/play/level/playback.sass b/app/styles/play/level/playback.sass index 2434fb1af..92e5a2c81 100644 --- a/app/styles/play/level/playback.sass +++ b/app/styles/play/level/playback.sass @@ -2,50 +2,52 @@ @import "app/styles/bootstrap/variables" #playback-view + $playback-button-color: rgb(248, 197, 146) + // When 75% alpha, it will look like the rgb(194, 154, 114) from Heald's design width: 55% - height: 30px + height: 60px + padding-top: 17px position: relative - background: #383434 - // Counteract 50px height of absolutely positioned control bar. - margin-top: 50px + background: transparent url(/images/level/scrubber_background.png) + background-size: 100% 100% + // Counteract 50px height of absolutely positioned control bar, but overlap by 10px of jagged transparent top. + margin-top: 50px - 10px + z-index: 2 button - height: 26px + font-size: 26px + margin-left: 10px background: transparent - @include opacity(0.50) - i + @include opacity(0.75) + color: $playback-button-color + text-shadow: 1px 1px 0px black + + .glyphicon position: relative button:hover - @include opacity(0.75) - + @include opacity(1) + #play-button, #volume-button, #music-button float: left - margin-left: 2px - margin-top: 2px - width: 25px position: relative #music-button - @include opacity(0.25) - font-size: 20px + @include opacity(0.5) span position: relative left: -3px top: -2px &:hover - @include opacity(0.50) + @include opacity(0.75) &.music-on - @include opacity(0.50) + @include opacity(0.75) &:hover - @include opacity(0.75) + @include opacity(1) #play-button, #volume-button - i + .glyphicon display: none - position: absolute - left: 2px - top: 2px #settings-button padding-left: 4px @@ -54,51 +56,58 @@ #playback-settings float: right position: relative - top: 2px - margin-right: 2px - ul i + margin-right: 10px + ul button margin: 0 10px li:hover background: #add8e6 - #play-button.disabled i - @include opacity(0.5) - #play-button.playing i.icon-pause + #play-button.disabled .glyphicon + @include opacity(0.75) + #play-button.playing .glyphicon-pause display: inline-block - #play-button.paused i.icon-play + #play-button.paused .glyphicon-play display: inline-block - #play-button.ended i.icon-repeat + #play-button.ended .glyphicon-repeat display: inline-block - #volume-button.vol-up i.icon-volume-up + #volume-button.vol-up .glyphicon.glyphicon-volume-up display: inline-block - #volume-button.vol-off i.icon-volume-off + #volume-button.vol-off .glyphicon.glyphicon-volume-off display: inline-block - @include opacity(0.50) + @include opacity(0.75) &:hover - @include opacity(0.75) - #volume-button.vol-down i.icon-volume-down + @include opacity(1) + #volume-button.vol-down .glyphicon.glyphicon-volume-down display: inline-block .scrubber position: absolute - left: 100px - top: 0px + left: 170px + top: 21px bottom: 0px - right: 125px + right: 175px + background: rgb(3, 3, 3) + height: 28px + border: 1px solid rgb(67, 67, 44) + border-radius: 14px + + .scrubber-inner + border: 1px solid rgb(44, 38, 29) + width: 100% + height: 100% + border-radius: 14px + padding: 6px 8px .progress float: left width: 100% - height: 14px - margin-top: 8px + height: 12px cursor: pointer overflow: visible border: 1px solid #444 - // Remove gradient background in favor of solid fill - background-color: #888 - background-image: none - border-radius: 0 + background: rgb(80, 67, 53) + border-radius: 6px border: 0 // Can't do this transition because handle then jitters, but would be good for streaming. //@include transition(width .2s linear) @@ -113,19 +122,19 @@ @include transition(width .0s linear) position: relative // Remove gradient background in favor of solid fill - background-color: #67A4C8 - //background-image: none // gradient looks kind of cool though; keep it in + background: rgb(245, 170, 49) + border: 1px solid rgb(62, 45, 16) + border-radius: 6px .scrubber-handle cursor: pointer position: absolute - right: -16px - top: -9px - background: transparent url(/images/level/playback_thumb.png) - width: 32px - height: 32px - // z: above the gradient line bordering the playback bar - z-index: 6 + right: -18px + top: -11px + background: transparent url(/images/level/scrubber_knob.png) + background-size: contain + width: 36px + height: 36px .ui-slider-handle height: 100% @@ -140,3 +149,5 @@ body.ipad #playback-view #playback-settings display: none + .scrubber + right: 25px diff --git a/app/styles/play/spectate.sass b/app/styles/play/spectate.sass index 227b86db0..d102a7197 100644 --- a/app/styles/play/spectate.sass +++ b/app/styles/play/spectate.sass @@ -124,14 +124,6 @@ left: -3px bottom: 0 - #hud-top-gradient - top: -32px - background: linear-gradient(to bottom, rgba(0,0,0,0) 0%,rgba(0,0,0,0.8*$GI) 100%) - left: 0 - right: 0 - bottom: 0 - height: 3px - #canvas-left-gradient left: 0px width: 5px diff --git a/app/templates/admin.jade b/app/templates/admin.jade index af59ee831..36d754468 100644 --- a/app/templates/admin.jade +++ b/app/templates/admin.jade @@ -43,6 +43,9 @@ block content a(href="/admin/base", data-i18n="admin.av_other_debug_base_url") Base (for debugging base.jade) li a(href="/admin/clas", data-i18n="admin.clas") CLAs + if me.isAdmin() + li + a(href="/admin/growth", data-i18n="admin.growth") Growth hr diff --git a/app/templates/admin/growth.jade b/app/templates/admin/growth.jade new file mode 100644 index 000000000..0ff7a3c1d --- /dev/null +++ b/app/templates/admin/growth.jade @@ -0,0 +1,32 @@ +extends /templates/base + +block content + + h1(data-i18n="admin.growth_title") Growth + if me.isAdmin() + if crunchingData + h4 Cruncing Data.. + else + h2 Registered Users + h3 Per-Day + h4 Totals + svg.perDayTotal + h4 Added + svg.perDayAdded + table.table.table-striped.table-bordered.table-condensed + -for (var i = 0; i < usersPerDay.length; i++) + tr + td= usersPerDay[i].date + td= usersPerDay[i].added + td= usersPerDay[i].total + h3 Per-Month + h4 Totals + svg.perMonthTotal + h4 Added + svg.perMonthAdded + table.table.table-striped.table-bordered.table-condensed + -for (var i = 0; i < usersPerMonth.length; i++) + tr + td= usersPerMonth[i].date + td= usersPerMonth[i].added + td= usersPerMonth[i].total diff --git a/app/templates/play/level/goals.jade b/app/templates/play/level/goals.jade index d1e0b689c..5186106dd 100644 --- a/app/templates/play/level/goals.jade +++ b/app/templates/play/level/goals.jade @@ -1,6 +1,6 @@ ul#primary-goals-list div.goals-status - strong(data-i18n="play_level.goals") Goals + span(data-i18n="play_level.goals") Goals span.spr : span(data-i18n="play_level.success").secret.goal-status.success Success! span(data-i18n="play_level.incomplete").secret.goal-status.incomplete Incomplete diff --git a/app/templates/play/level/hud.jade b/app/templates/play/level/hud.jade index 5153f1379..5e093bf04 100644 --- a/app/templates/play/level/hud.jade +++ b/app/templates/play/level/hud.jade @@ -1,5 +1,3 @@ -#hud-top-gradient.gradient - .center .thang-canvas-wrapper.thang-elem diff --git a/app/templates/play/level/playback.jade b/app/templates/play/level/playback.jade index 5502f78cb..5d494cb67 100644 --- a/app/templates/play/level/playback.jade +++ b/app/templates/play/level/playback.jade @@ -1,45 +1,29 @@ button.btn.btn-xs.btn-inverse#play-button.paused(title="Ctrl/Cmd + P: Toggle level play/pause") - i.icon-play.icon-white.big - i.icon-pause.icon-white.big - i.icon-repeat.icon-white.big + .glyphicon.glyphicon-play + .glyphicon.glyphicon-pause + .glyphicon.glyphicon-repeat button.btn.btn-xs.btn-inverse#volume-button(title="Adjust volume") - i.icon-volume-off.icon-white.big - i.icon-volume-down.icon-white.big - i.icon-volume-up.icon-white.big + .glyphicon.glyphicon-volume-off + .glyphicon.glyphicon-volume-down + .glyphicon.glyphicon-volume-up button.btn.btn-xs.btn-inverse#music-button(title="Toggle Music") span ♫ .scrubber - .progress.secret#timeProgress - .progress-bar - .scrubber-handle - .popover.fade.top.in#timePopover - .arrow - h3.popover-title - .popover-content + .scrubber-inner + .progress.secret#timeProgress + .progress-bar + .scrubber-handle + .popover.fade.top.in#timePopover + .arrow + h3.popover-title + .popover-content .btn-group.dropup#playback-settings button.btn.btn-xs.btn-inverse.toggle-fullscreen(title="Toggle fullscreen") - i.icon-fullscreen.icon-white + .glyphicon.glyphicon-fullscreen button.btn.btn-xs.btn-inverse#zoom-in-button(title="Zoom In (or scroll down)") - i.icon-zoom-in.icon-white + .glyphicon.glyphicon-zoom-in button.btn.btn-xs.btn-inverse#zoom-out-button(title="Zoom Out (or scroll up)") - i.icon-zoom-out.icon-white - button.btn.btn-xs.btn-inverse.dropdown-toggle(data-toggle="dropdown")#settings-button - i.icon-cog.icon-white.big - ul.dropdown-menu - if me.get('name') == "Nick" - li(title="Ctrl/Cmd + \\: Toggle debug display").selectable#debug-toggle - i.icon-globe - | Debug Mode - i.icon-ok.secret - li.selectable#view-keyboard-shortcuts - i.icon-info-sign - span(data-i18n="play_level.keyboard_shortcuts") Key Shortcuts - li.selectable#edit-wizard-settings - i.icon-user - span(data-i18n="play_level.customize_wizard") Customize Wizard - li.selectable#edit-editor-config - i.icon-edit - span(data-i18n="play_level.editor_config") Editor Config + .glyphicon.glyphicon-zoom-out diff --git a/app/views/admin/GrowthView.coffee b/app/views/admin/GrowthView.coffee new file mode 100644 index 000000000..b77b88e92 --- /dev/null +++ b/app/views/admin/GrowthView.coffee @@ -0,0 +1,192 @@ +RootView = require 'views/kinds/RootView' +template = require 'templates/admin/growth' +RealTimeCollection = require 'collections/RealTimeCollection' + +# Growth View ################### +# +# Display interesting growth data. +# +# Currently shows: +# Registered user totals and added, per-day and per-month +# 7-day moving average for registered users added per-day +# +# TODO: @padding isn't applied correctly +# TODO: aggregate recent data if missing? +# + +module.exports = class GrowthView extends RootView + id: 'admin-growth-view' + template: template + height: 300 + width: 1000 + xAxisGuideHeight: 80 + yAxisGuideWidth: 60 + padding: 10 + + constructor: (options) -> + super options + @usersPerMonth = new RealTimeCollection 'growth/users/registered/per-month' + @usersPerMonth.on 'add', @refreshData + @usersPerDay = new RealTimeCollection 'growth/users/registered/per-day' + @usersPerDay.on 'add', @refreshData + + destroy: -> + @usersPerMonth.off 'add', @refreshData + @usersPerDay.off 'add', @refreshData + + refreshData: => + @render() + + getRenderData: -> + c = super() + c.crunchingData = @usersPerMonth.length is 0 and @usersPerDay.length is 0 + c.usersPerDay = [] + # @usersPerDay.each (item) -> + # c.usersPerDay.push date: item.get('id'), added: item.get('added'), total: item.get('total') + c.usersPerMonth = [] + # @usersPerMonth.each (item) -> + # c.usersPerMonth.push date: item.get('id'), added: item.get('added'), total: item.get('total') + c + + afterRender: -> + super() + if me.isAdmin() + @createPerDayChart() + @createPerMonthChart() + + createPerDayChart: -> + addedData = [] + totalData = [] + @usersPerDay.each (item) -> + addedData.push id: item.get('id'), value: item.get('added') + totalData.push id: item.get('id'), value: item.get('total') + @createLineChart ".perDayTotal", totalData, 1000 + @createLineChart ".perDayAdded", addedData, 10, true + + createPerMonthChart: -> + addedData = [] + totalData = [] + @usersPerMonth.each (item) -> + addedData.push id: item.get('id'), value: item.get('added') + totalData.push id: item.get('id'), value: item.get('total') + @createLineChart ".perMonthTotal", totalData, 1000 + @createLineChart ".perMonthAdded", addedData, 1000 + + createLineChart: (selector, data, guidelineSpacing, sevenDayAverage=false) -> + return unless data.length > 1 + + minVal = d3.min(data, (d) -> d.value) + maxVal = d3.max(data, (d) -> d.value) + + widthSpacing = (@width - @yAxisGuideWidth - @padding) / (data.length - 1) + + y = d3.scale.linear() + .domain([minVal, maxVal]) + .range([@height - @xAxisGuideHeight - 2 * @padding, 0]) + + points = [] + for i in [0...data.length] + points.push id: data[i].id, x: i * widthSpacing + @yAxisGuideWidth, y: y(data[i].value) + @padding + + links = [] + for i in [0...points.length - 1] + if points[i] and points[i + 1] + links.push start: points[i], end: points[i + 1] + + guidelines = [] + diff = maxVal - minVal + interval = Math.floor(diff / 5) + for i in [0..4] + yVal = i * interval + minVal + yVal = Math.floor(yVal / guidelineSpacing) * guidelineSpacing + guidelines.push start: {id: yVal, x: 0, y: y(yVal)}, end: {id: yVal, x: @width, y: y(yVal)} + + sevenPoints = [] + sevenLinks = [] + if sevenDayAverage + sevenTotal = 0 + for i in [0...data.length] + sevenTotal += data[i].value + if i > 5 + sevenAvg = sevenTotal / 7 + sevenPoints.push x: i * widthSpacing + @yAxisGuideWidth, y: y(sevenAvg) + @padding + if i > 6 + sevenTotal -= data[i - 7].value + for i in [0...sevenPoints.length - 1] + if sevenPoints[i] and sevenPoints[i + 1] + sevenLinks.push start: sevenPoints[i], end: sevenPoints[i + 1] + + chart = d3.select(selector) + .attr("width", @width) + .attr("height", @height) + + chart.selectAll(".circle") + .data(points) + .enter() + .append("circle") + .attr("cx", (d) -> d.x ) + .attr("cy", (d) -> d.y ) + .attr("r", "2px") + .attr("fill", "black") + + chart.selectAll(".text") + .data(points) + .enter() + .append("text") + .attr("dy", ".35em") + .attr("transform", (d, i) => "translate(" + d.x + "," + @height + ") rotate(270)") + .text((d) -> + if d.id.length is 8 + return "#{parseInt(d.id[4..5])}/#{parseInt(d.id[6..7])}/#{d.id[0..3]}" + else + return "#{parseInt(d.id[4..5])}/#{d.id[0..3]}" + ) + + chart.selectAll('.line') + .data(links) + .enter() + .append("line") + .attr("x1", (d) -> d.start.x ) + .attr("y1", (d) -> d.start.y ) + .attr("x2", (d) -> d.end.x ) + .attr("y2", (d) -> d.end.y ) + .style("stroke", "rgb(6,120,155)") + + chart.selectAll(".circle") + .data(sevenPoints) + .enter() + .append("circle") + .attr("cx", (d) -> d.x ) + .attr("cy", (d) -> d.y ) + .attr("r", "2px") + .attr("fill", "purple") + + chart.selectAll('.line') + .data(sevenLinks) + .enter() + .append("line") + .attr("x1", (d) -> d.start.x ) + .attr("y1", (d) -> d.start.y ) + .attr("x2", (d) -> d.end.x ) + .attr("y2", (d) -> d.end.y ) + .style("stroke", "rgb(200,0,0)") + + chart.selectAll('.line') + .data(guidelines) + .enter() + .append("line") + .attr("x1", (d) -> d.start.x ) + .attr("y1", (d) -> d.start.y ) + .attr("x2", (d) -> d.end.x ) + .attr("y2", (d) -> d.end.y ) + .style("stroke", "rgb(140,140,140)") + + chart.selectAll(".text") + .data(guidelines) + .enter() + .append("text") + .attr("x", (d) -> d.start.x) + .attr("y", (d) -> d.start.y - 6) + .attr("dy", ".35em") + .text((d) -> d.start.id) + diff --git a/app/views/game-menu/InventoryView.coffee b/app/views/game-menu/InventoryView.coffee index e8c5d1624..85de15b85 100644 --- a/app/views/game-menu/InventoryView.coffee +++ b/app/views/game-menu/InventoryView.coffee @@ -340,23 +340,25 @@ module.exports = class InventoryView extends CocoView 'simple-sword': '53e218d853457600003e3ebe' 'leather-tunic': '53e22eac53457600003e3efc' 'leather-boots': '53e2384453457600003e3f07' + 'leather-belt': '5437002a7beba4a82024a97d' 'programmaticon-i': '53e4108204c00d4607a89f78' 'crude-glasses': '53e238df53457600003e3f0b' 'builders-hammer': '53f4e6e3d822c23505b74f42' gearByLevel = 'dungeons-of-kithgard': {feet: 'simple-boots'} 'gems-in-the-deep': {feet: 'simple-boots'} - 'forgetful-gemsmith': {feet: 'simple-boots'} 'shadow-guard': {feet: 'simple-boots'} 'kounter-kithwise': {feet: 'simple-boots'} 'crawlways-of-kithgard': {feet: 'simple-boots'} - 'true-names': {feet: 'simple-boots', 'right-hand': 'simple-sword'} + 'forgetful-gemsmith': {feet: 'simple-boots'} + 'true-names': {feet: 'simple-boots', 'right-hand': 'simple-sword', waist: 'leather-belt'} 'favorable-odds': {feet: 'simple-boots', 'right-hand': 'simple-sword'} 'the-raised-sword': {feet: 'simple-boots', 'right-hand': 'simple-sword', torso: 'leather-tunic'} 'the-first-kithmaze': {feet: 'simple-boots', 'programming-book': 'programmaticon-i'} + 'haunted-kithmaze': {feet: 'simple-boots', 'programming-book': 'programmaticon-i'} 'descending-further': {feet: 'simple-boots', 'programming-book': 'programmaticon-i'} 'the-second-kithmaze': {feet: 'simple-boots', 'programming-book': 'programmaticon-i'} - 'new-sight': {'right-hand': 'simple-sword', 'programming-book': 'programmaticon-i'} + 'dread-door': {'right-hand': 'simple-sword', 'programming-book': 'programmaticon-i'} 'known-enemy': {'right-hand': 'simple-sword', 'programming-book': 'programmaticon-i'} 'master-of-names': {feet: 'simple-boots', 'right-hand': 'simple-sword', 'programming-book': 'programmaticon-i', eyes: 'crude-glasses'} 'lowly-kithmen': {feet: 'simple-boots', 'right-hand': 'simple-sword', 'programming-book': 'programmaticon-i', eyes: 'crude-glasses'} diff --git a/app/views/play/WorldMapView.coffee b/app/views/play/WorldMapView.coffee index d1751f5db..79d87188a 100644 --- a/app/views/play/WorldMapView.coffee +++ b/app/views/play/WorldMapView.coffee @@ -630,22 +630,8 @@ dungeon = [ x: 29 y: 12 nextLevels: - more_practice: 'forgetful-gemsmith' continue: 'shadow-guard' - skip_ahead: 'true-names' - } - { - name: 'Forgetful Gemsmith' - type: 'hero' - difficulty: 1 - id: 'forgetful-gemsmith' - original: '544a98f62d002f0000fe331a' - description: 'Grab even more gems as you practice moving.' - x: 38 - y: 12 - nextLevels: - continue: 'shadow-guard' - practice: true + skip_ahead: 'forgetful-gemsmith' } { name: 'Shadow Guard' @@ -654,11 +640,11 @@ dungeon = [ id: 'shadow-guard' original: '54174347844506ae0195a0b8' description: 'Evade the Kithgard minion.' - x: 50 - y: 11 + x: 41 + y: 13 nextLevels: more_practice: 'kounter-kithwise' - continue: 'true-names' + continue: 'forgetful-gemsmith' } { name: 'Kounter Kithwise' @@ -667,25 +653,37 @@ dungeon = [ id: 'kounter-kithwise' original: '54527a6257e83800009730c7' description: 'Practice your evasion skills with more guards.' - x: 58 - y: 10 + x: 50 + y: 14 nextLevels: - more_practice: 'crawlways-of-kithgard' + #more_practice: 'crawlways-of-kithgard' continue: 'true-names' practice: true } + #{ + # name: 'Crawlways of Kithgard' + # type: 'hero' + # difficulty: 1 + # id: 'crawlways-of-kithgard' + # original: '545287ef57e83800009730d5' + # description: 'Dart in and grab the gem–at the right moment.' + # x: 57 + # y: 12 + # nextLevels: + # continue: 'true-names' + # practice: true + #} { - name: 'Crawlways of Kithgard' + name: 'Forgetful Gemsmith' type: 'hero' difficulty: 1 - id: 'crawlways-of-kithgard' - original: '545287ef57e83800009730d5' - description: 'Dart in and grab the gem–at the right moment.' - x: 67 - y: 10 + id: 'forgetful-gemsmith' + original: '544a98f62d002f0000fe331a' + description: 'Grab even more gems as you practice moving.' + x: 63 + y: 13 nextLevels: - continue: 'true-names' - practice: true + continue: 'shadow-guard' } { name: 'True Names' @@ -695,7 +693,7 @@ dungeon = [ original: '541875da4c16460000ab990f' description: 'Learn an enemy\'s true name to defeat it.' x: 74 - y: 12 + y: 14 nextLevels: more_practice: 'favorable-odds' continue: 'the-raised-sword' @@ -737,7 +735,21 @@ dungeon = [ nextLevels: more_practice: 'descending-further' continue: 'the-second-kithmaze' - skip_ahead: 'new-sight' + skip_ahead: 'dread-door' + } + { + name: 'Haunted Kithmaze' + type: 'hero' + difficulty: 1 + id: 'haunted-kithmaze' + original: '545a5914d820eb0000f6dc0a' + description: 'The builders of Kithgard constructed many mazes to confuse travelers.' + x: 78 + y: 29 + nextLevels: + more_practice: 'descending-further' + continue: 'the-second-kithmaze' + skip_ahead: 'dread-door' } { name: 'Descending Further' @@ -762,15 +774,15 @@ dungeon = [ x: 59 y: 25 nextLevels: - continue: 'new-sight' + continue: 'dread-door' } { - name: 'New Sight' + name: 'Dread Door' type: 'hero' difficulty: 1 - id: 'new-sight' + id: 'dread-door' original: '5418d40f4c16460000ab9ac2' - description: 'A true name can only be seen with the correct lenses.' + description: 'Behind a dread door lies a chest full of riches.' x: 60 y: 34 nextLevels: @@ -1006,3 +1018,9 @@ WorldMapView.campaigns = campaigns = [ {id: 'dungeon', name: 'Dungeon Campaign', levels: dungeon } {id: 'forest', name: 'Forest Campaign', levels: forest } ] + +# A/B testing first kithmaze level: The First Kithmaze vs. Haunted Kithmaze +if me.getKithmazeGroup() is 'the-first-kithmaze' + _.remove dungeon, id: 'haunted-kithmaze' +else + _.remove dungeon, id: 'the-first-kithmaze' diff --git a/app/views/play/level/LevelGoalsView.coffee b/app/views/play/level/LevelGoalsView.coffee index 087e3831a..843a409dd 100644 --- a/app/views/play/level/LevelGoalsView.coffee +++ b/app/views/play/level/LevelGoalsView.coffee @@ -101,7 +101,7 @@ module.exports = class LevelGoalsView extends CocoView return if expand is @expanded @updateHeight() sound = if expand then 'goals-expand' else 'goals-collapse' - top = if expand then -10 else 26 - (@normalHeight ? @$el.outerHeight()) + top = if expand then -5 else 36 - (@normalHeight ? @$el.outerHeight()) @$el.css 'top', top if @soundTimeout # Don't play the sound we were going to play after all; the transition has reversed. diff --git a/app/views/play/level/LevelHUDView.coffee b/app/views/play/level/LevelHUDView.coffee index 4e135e093..7265ef1e0 100644 --- a/app/views/play/level/LevelHUDView.coffee +++ b/app/views/play/level/LevelHUDView.coffee @@ -30,7 +30,7 @@ module.exports = class LevelHUDView extends CocoView afterRender: -> super() @$el.addClass 'no-selection' - if @options.level.get('slug') in ['dungeons-of-kithgard', 'gems-in-the-deep', 'forgetful-gemsmith', 'shadow-guard', 'kounter-kithwise', 'crawlways-of-kithgard', 'true-names', 'favorable-odds', 'the-raised-sword', 'the-first-kithmaze', 'descending-further', 'the-second-kithmaze', 'new-sight', 'known-enemy', 'master-of-names', 'lowly-kithmen', 'closing-the-distance', 'tactical-strike', 'the-final-kithmaze', 'the-gauntlet'] + if @options.level.get('slug') in ['dungeons-of-kithgard', 'gems-in-the-deep', 'forgetful-gemsmith', 'shadow-guard', 'kounter-kithwise', 'crawlways-of-kithgard', 'true-names', 'favorable-odds', 'the-raised-sword', 'the-first-kithmaze', 'haunted-kithmaze', 'descending-further', 'the-second-kithmaze', 'dread-door', 'known-enemy', 'master-of-names', 'lowly-kithmen', 'closing-the-distance', 'tactical-strike', 'the-final-kithmaze', 'the-gauntlet'] @hidesHUD = true @$el.addClass 'hide-hud-properties' diff --git a/app/views/play/level/LevelPlaybackView.coffee b/app/views/play/level/LevelPlaybackView.coffee index 81bf36555..31b495642 100644 --- a/app/views/play/level/LevelPlaybackView.coffee +++ b/app/views/play/level/LevelPlaybackView.coffee @@ -17,7 +17,6 @@ module.exports = class LevelPlaybackView extends CocoView 'level:scrub-forward': 'onScrubForward' 'level:scrub-back': 'onScrubBack' 'level:set-volume': 'onSetVolume' - 'level:set-debug': 'onSetDebug' 'surface:frame-changed': 'onFrameChanged' 'god:new-world-created': 'onNewWorld' 'god:streaming-world-updated': 'onNewWorld' @@ -28,10 +27,6 @@ module.exports = class LevelPlaybackView extends CocoView 'real-time-multiplayer:manual-cast': 'onRealTimeMultiplayerCast' events: - 'click #debug-toggle': 'onToggleDebug' - 'click #edit-wizard-settings': 'onEditWizardSettings' - 'click #edit-editor-config': 'onEditEditorConfig' - 'click #view-keyboard-shortcuts': 'onViewKeyboardShortcuts' 'click #music-button': 'onToggleMusic' 'click #zoom-in-button': -> Backbone.Mediator.publish 'camera:zoom-in', {} unless @shouldIgnore() 'click #zoom-out-button': -> Backbone.Mediator.publish 'camera:zoom-out', {} unless @shouldIgnore() @@ -52,59 +47,6 @@ module.exports = class LevelPlaybackView extends CocoView '⌘+], ctrl+]': 'onScrubForward' '⌘+⇧+], ctrl+⇧+]': 'onSingleScrubForward' - # popover that shows at the current mouse position on the progressbar, using the bootstrap popover. - # Could make this into a jQuery plugins itself theoretically. - class HoverPopup extends $.fn.popover.Constructor - constructor: () -> - @enabled = true - @shown = false - @type = 'HoverPopup' - @options = - placement: 'top' - container: 'body' - animation: true - html: true - delay: - show: 400 - @$element = $('#timeProgress') - @$tip = $('#timePopover') - - @content = '' - - getContent: -> @content - - show: -> - unless @shown - super() - @shown = true - - updateContent: (@content) -> - @setContent() - @$tip.addClass('fade top in') - - onHover: (@e) -> - pos = @getPosition() - actualWidth = @$tip[0].offsetWidth - actualHeight = @$tip[0].offsetHeight - calculatedOffset = - top: pos.top - actualHeight - left: pos.left + pos.width / 2 - actualWidth / 2 - this.applyPlacement(calculatedOffset, 'top') - - getPosition: -> - top: @$element.offset().top - left: if @e? then @e.pageX else @$element.offset().left - height: 0 - width: 0 - - hide: -> - super() - @shown = false - - disable: -> - super() - @hide() - constructor: -> super(arguments...) me.on('change:music', @updateMusicButton, @) @@ -192,20 +134,6 @@ module.exports = class LevelPlaybackView extends CocoView @currentTime = 0 @lastLoadedFrameCount = loadedFrameCount - onToggleDebug: -> - return if @shouldIgnore() - flag = $('#debug-toggle i.icon-ok') - Backbone.Mediator.publish('level:set-debug', {debug: flag.hasClass('invisible')}) - - onEditWizardSettings: -> - Backbone.Mediator.publish 'level:edit-wizard-settings', {} - - onEditEditorConfig: -> - @openModalView new EditorConfigModal session: @options.session - - onViewKeyboardShortcuts: -> - @openModalView new KeyboardShortcutsModal() - onDisableControls: (e) -> if not e.controls or 'playback' in e.controls @disabled = true @@ -340,10 +268,6 @@ module.exports = class LevelPlaybackView extends CocoView Backbone.Mediator.publish 'level:set-letterbox', on: false Backbone.Mediator.publish 'playback:real-time-playback-ended', {} - onSetDebug: (e) -> - flag = $('#debug-toggle i.icon-ok') - flag.toggleClass 'invisible', not e.debug - # to refactor hookUpScrubber: -> @@ -423,3 +347,56 @@ module.exports = class LevelPlaybackView extends CocoView $(window).off('resize', @onWindowResize) @onWindowResize = null super() + +# popover that shows at the current mouse position on the progressbar, using the bootstrap popover. +# Could make this into a jQuery plugins itself theoretically. +class HoverPopup extends $.fn.popover.Constructor + constructor: () -> + @enabled = true + @shown = false + @type = 'HoverPopup' + @options = + placement: 'top' + container: 'body' + animation: true + html: true + delay: + show: 400 + @$element = $('#timeProgress') + @$tip = $('#timePopover') + + @content = '' + + getContent: -> @content + + show: -> + unless @shown + super() + @shown = true + + updateContent: (@content) -> + @setContent() + @$tip.addClass('fade top in') + + onHover: (@e) -> + pos = @getPosition() + actualWidth = @$tip[0].offsetWidth + actualHeight = @$tip[0].offsetHeight + calculatedOffset = + top: pos.top - actualHeight + left: pos.left + pos.width / 2 - actualWidth / 2 + this.applyPlacement(calculatedOffset, 'top') + + getPosition: -> + top: @$element.offset().top + left: if @e? then @e.pageX else @$element.offset().left + height: 0 + width: 0 + + hide: -> + super() + @shown = false + + disable: -> + super() + @hide() diff --git a/app/views/play/level/tome/CastButtonView.coffee b/app/views/play/level/tome/CastButtonView.coffee index da3898669..6b868e202 100644 --- a/app/views/play/level/tome/CastButtonView.coffee +++ b/app/views/play/level/tome/CastButtonView.coffee @@ -108,7 +108,7 @@ module.exports = class CastButtonView extends CocoView else if castable s = $.i18n.t('play_level.tome_cast_button_run') s = $.i18n.t('play_level.tome_cast_button_casting') if s is 'Run' and me.get('preferredLanguage').split('-')[0] isnt 'en' # Temporary, if tome_cast_button_running isn't translated. - unless @options.levelID in ['dungeons-of-kithgard', 'gems-in-the-deep', 'shadow-guard', 'true-names', 'the-raised-sword', 'the-first-kithmaze'] # Hide for first few. + unless @options.levelID in ['dungeons-of-kithgard', 'gems-in-the-deep', 'shadow-guard', 'forgetful-gemsmith', 'kounter-kithwise', 'true-names', 'the-raised-sword', 'favorable-odds', 'the-first-kithmaze', 'haunted-kithmaze'] # Hide for first few. s += ' ' + @castShortcut else s = $.i18n.t('play_level.tome_cast_button_ran') diff --git a/app/views/play/level/tome/SpellView.coffee b/app/views/play/level/tome/SpellView.coffee index d71f201fb..c63afedc5 100644 --- a/app/views/play/level/tome/SpellView.coffee +++ b/app/views/play/level/tome/SpellView.coffee @@ -871,4 +871,5 @@ requiredCodePerLevel = 'dungeons-of-kithgard': ['moveRight'] 'true-names': ['Brak'] 'the-first-kithmaze': ['loop'] + 'haunted-kithmaze': ['loop'] 'lowly-kithmen': ['findNearestEnemy']