From 3a811a05fd82dbc31d279686b306dbe0c54ade57 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Tue, 4 Nov 2014 16:09:29 -0800 Subject: [PATCH 1/5] Paying attention to level-specific Allied significantProperty overrides in Hero Placeholder replacement. --- app/models/Level.coffee | 2 ++ 1 file changed, 2 insertions(+) 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 From 49b8c745a07f26a213833c330ca0742c98cebe1b Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Tue, 4 Nov 2014 17:20:58 -0800 Subject: [PATCH 2/5] New GoalsView background. Be my guest with any text color improvements. --- app/styles/play/level/goals.sass | 30 ++++++++++++---------- app/styles/play/level/loading.sass | 1 + app/views/play/level/LevelGoalsView.coffee | 2 +- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/app/styles/play/level/goals.sass b/app/styles/play/level/goals.sass index 26f7bd5de..94aef8605 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 6px 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/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. From f461aeca957396a0e337fc8329a1ca6f99d2c5c9 Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Tue, 4 Nov 2014 19:03:35 -0800 Subject: [PATCH 3/5] Implemented new playback scrubber textures. --- .../images/level/scrubber_background.png | Bin 19807 -> 13366 bytes app/styles/base.sass | 38 +----- app/styles/play/level.sass | 8 -- app/styles/play/level/goals.sass | 2 +- app/styles/play/level/multiplayer-status.sass | 2 + app/styles/play/level/playback.sass | 115 +++++++++------- app/styles/play/spectate.sass | 8 -- app/templates/play/level/goals.jade | 2 +- app/templates/play/level/hud.jade | 2 - app/templates/play/level/playback.jade | 50 +++---- app/views/play/level/LevelPlaybackView.coffee | 129 +++++++----------- 11 files changed, 141 insertions(+), 215 deletions(-) diff --git a/app/assets/images/level/scrubber_background.png b/app/assets/images/level/scrubber_background.png index a70abeb975a3de45c7d5e9318ff60535b0b78996..517a4c8c46bca5b355abdbb0ddc4b192883e8d11 100644 GIT binary patch literal 13366 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 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() From 40506d23a66bccb61302b30d04df6003a28c25b0 Mon Sep 17 00:00:00 2001 From: Matt Lott Date: Tue, 4 Nov 2014 22:17:49 -0800 Subject: [PATCH 4/5] Admin growth view --- app/Router.coffee | 1 + app/templates/admin.jade | 3 + app/templates/admin/growth.jade | 32 +++++ app/views/admin/GrowthView.coffee | 192 ++++++++++++++++++++++++++++++ 4 files changed, 228 insertions(+) create mode 100644 app/templates/admin/growth.jade create mode 100644 app/views/admin/GrowthView.coffee 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/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/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) + From 6efcbc5ead9930891e9e7d7b51a273bd8231179f Mon Sep 17 00:00:00 2001 From: Nick Winter Date: Wed, 5 Nov 2014 10:40:37 -0800 Subject: [PATCH 5/5] Hid Crawlways of Kithgard. A/B testing Haunted Kithmaze vs. The First Kithmaze. Renamed New Sight to Dread Door and awarded its glasses later. --- app/lib/world/world.coffee | 2 +- app/models/User.coffee | 14 ++- app/views/game-menu/InventoryView.coffee | 8 +- app/views/play/WorldMapView.coffee | 88 +++++++++++-------- app/views/play/level/LevelHUDView.coffee | 2 +- .../play/level/tome/CastButtonView.coffee | 2 +- app/views/play/level/tome/SpellView.coffee | 1 + 7 files changed, 74 insertions(+), 43 deletions(-) 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/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/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/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/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']