mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-11-29 02:25:37 -05:00
Merge branch 'master' into production
This commit is contained in:
commit
a93daa9e73
25 changed files with 462 additions and 272 deletions
|
@ -31,6 +31,7 @@ module.exports = class CocoRouter extends Backbone.Router
|
||||||
'admin/clas': go('admin/CLAsView')
|
'admin/clas': go('admin/CLAsView')
|
||||||
'admin/employers': go('admin/EmployersListView')
|
'admin/employers': go('admin/EmployersListView')
|
||||||
'admin/files': go('admin/FilesView')
|
'admin/files': go('admin/FilesView')
|
||||||
|
'admin/growth': go('admin/GrowthView')
|
||||||
'admin/level-sessions': go('admin/LevelSessionsView')
|
'admin/level-sessions': go('admin/LevelSessionsView')
|
||||||
'admin/users': go('admin/UsersView')
|
'admin/users': go('admin/UsersView')
|
||||||
'admin/base': go('admin/BaseView')
|
'admin/base': go('admin/BaseView')
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 13 KiB |
|
@ -99,7 +99,7 @@ module.exports = class World
|
||||||
continueLaterFn = =>
|
continueLaterFn = =>
|
||||||
@loadFrames(loadedCallback, errorCallback, loadProgressCallback, preloadedCallback, skipDeferredLoading, loadUntilFrame) unless @destroyed
|
@loadFrames(loadedCallback, errorCallback, loadProgressCallback, preloadedCallback, skipDeferredLoading, loadUntilFrame) unless @destroyed
|
||||||
if @realTime and not @countdownFinished
|
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
|
@realTimeSpeedFactor = 5
|
||||||
else if @levelID in ['forgotten-gemsmith', 'descending-further', 'tactical-strike', 'kithgard-gates']
|
else if @levelID in ['forgotten-gemsmith', 'descending-further', 'tactical-strike', 'kithgard-gates']
|
||||||
@realTimeSpeedFactor = 3
|
@realTimeSpeedFactor = 3
|
||||||
|
|
|
@ -102,6 +102,8 @@ module.exports = class Level extends CocoModel
|
||||||
levelThangComponent.config.pos.y = placeholderConfig.pos.y
|
levelThangComponent.config.pos.y = placeholderConfig.pos.y
|
||||||
else if placeholderConfig.team # Pull in Allied team
|
else if placeholderConfig.team # Pull in Allied team
|
||||||
levelThangComponent.config.team = placeholderConfig.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
|
else if placeholderConfig.programmableMethods
|
||||||
# Take the ThangType default Programmable and merge level-specific Component config into it
|
# Take the ThangType default Programmable and merge level-specific Component config into it
|
||||||
copy = $.extend true, {}, placeholderConfig
|
copy = $.extend true, {}, placeholderConfig
|
||||||
|
|
|
@ -88,7 +88,7 @@ module.exports = class User extends CocoModel
|
||||||
when 2 then 'choice-explicit'
|
when 2 then 'choice-explicit'
|
||||||
when 3 then 'choice-implicit'
|
when 3 then 'choice-implicit'
|
||||||
@branchingGroup = 'choice-explicit' if me.isAdmin()
|
@branchingGroup = 'choice-explicit' if me.isAdmin()
|
||||||
application.tracker.identify branchingGroup: @branchingGroup
|
application.tracker.identify branchingGroup: @branchingGroup unless me.isAdmin()
|
||||||
@branchingGroup
|
@branchingGroup
|
||||||
|
|
||||||
getHighlightArrowSoundGroup: ->
|
getHighlightArrowSoundGroup: ->
|
||||||
|
@ -98,5 +98,15 @@ module.exports = class User extends CocoModel
|
||||||
when 0, 1, 2, 3 then 'sound-off'
|
when 0, 1, 2, 3 then 'sound-off'
|
||||||
when 4, 5, 6, 7 then 'sound-on'
|
when 4, 5, 6, 7 then 'sound-on'
|
||||||
@highlightArrowGroup = 'sound-off' if me.isAdmin()
|
@highlightArrowGroup = 'sound-off' if me.isAdmin()
|
||||||
application.tracker.identify highlightArrowGroup: @highlightArrowGroup
|
application.tracker.identify highlightArrowGroup: @highlightArrowGroup unless me.isAdmin()
|
||||||
@highlightArrowGroup
|
@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
|
||||||
|
|
|
@ -153,40 +153,6 @@ a
|
||||||
cursor: pointer
|
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 screens for everything but the play view
|
||||||
.loading-screen
|
.loading-screen
|
||||||
.progress
|
.progress
|
||||||
|
@ -339,3 +305,7 @@ kbd
|
||||||
&.gem-60
|
&.gem-60
|
||||||
width: 60px
|
width: 60px
|
||||||
height: 60px
|
height: 60px
|
||||||
|
|
||||||
|
.popover
|
||||||
|
border-image: url(/images/level/popover_background.png) 29 39 fill stretch
|
||||||
|
border-width: 15px 20px
|
||||||
|
|
|
@ -144,14 +144,6 @@ $level-resize-transition-time: 0.5s
|
||||||
left: -3px
|
left: -3px
|
||||||
bottom: 0
|
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
|
#canvas-left-gradient
|
||||||
left: 0px
|
left: 0px
|
||||||
width: 5px
|
width: 5px
|
||||||
|
|
|
@ -3,36 +3,37 @@
|
||||||
|
|
||||||
#goals-view
|
#goals-view
|
||||||
position: absolute
|
position: absolute
|
||||||
left: 10px
|
left: -15px
|
||||||
top: -100px
|
top: -100px
|
||||||
@include transition(0.5s ease-in-out)
|
@include transition(0.5s ease-in-out)
|
||||||
background-color: rgba(200,200,200,1.0)
|
background: transparent url(/images/level/goals_background.png)
|
||||||
|
background-size: 100% 100%
|
||||||
|
|
||||||
border: black
|
padding: 19px 17px 2px 25px
|
||||||
padding: 15px 7px 2px 5px
|
|
||||||
box-sizing: border-box
|
|
||||||
border: 1px solid #333
|
|
||||||
border-radius: 5px
|
|
||||||
z-index: 3
|
z-index: 3
|
||||||
font-size: 14px
|
font-size: 14px
|
||||||
|
|
||||||
&.brighter
|
&.brighter
|
||||||
font-size: 18px
|
font-size: 18px
|
||||||
font-size: 1.4vw
|
font-size: 1.4vw
|
||||||
@include box-shadow(0px 0px 12px white)
|
//@include box-shadow(0px 0px 12px white)
|
||||||
|
|
||||||
.goals-status
|
.goals-status
|
||||||
margin: 0
|
margin: 0
|
||||||
color: black
|
margin-top: 10px
|
||||||
|
color: white
|
||||||
|
text-transform: uppercase
|
||||||
|
|
||||||
.success
|
.success
|
||||||
color: darkgreen
|
color: lightgreen
|
||||||
|
text-shadow: 1px 1px 0px black
|
||||||
.timed-out
|
.timed-out
|
||||||
color: darkslategray
|
color: rgb(230, 230, 230)
|
||||||
.failure
|
.failure
|
||||||
color: darkred
|
color: rgb(239, 61, 71)
|
||||||
|
text-shadow: 1px 1px 0px black
|
||||||
.incomplete
|
.incomplete
|
||||||
color: darkgoldenrod
|
color: rgb(245, 170, 49)
|
||||||
|
|
||||||
ul
|
ul
|
||||||
padding-left: 0
|
padding-left: 0
|
||||||
|
@ -59,3 +60,4 @@
|
||||||
|
|
||||||
#goals-view.collapsed ul
|
#goals-view.collapsed ul
|
||||||
display: none
|
display: none
|
||||||
|
|
||||||
|
|
|
@ -78,6 +78,7 @@
|
||||||
|
|
||||||
.start-level-button
|
.start-level-button
|
||||||
font-size: 40px
|
font-size: 40px
|
||||||
|
font-variant: small-caps
|
||||||
|
|
||||||
.left-wing, .right-wing
|
.left-wing, .right-wing
|
||||||
width: 100%
|
width: 100%
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
// TODO: Replace this devart with nice shinies
|
// TODO: Replace this devart with nice shinies
|
||||||
|
|
||||||
#multiplayer-status-view
|
#multiplayer-status-view
|
||||||
|
position: absolute
|
||||||
|
|
||||||
.player-count
|
.player-count
|
||||||
color: white
|
color: white
|
||||||
.players-available
|
.players-available
|
||||||
|
|
|
@ -2,50 +2,52 @@
|
||||||
@import "app/styles/bootstrap/variables"
|
@import "app/styles/bootstrap/variables"
|
||||||
|
|
||||||
#playback-view
|
#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%
|
width: 55%
|
||||||
height: 30px
|
height: 60px
|
||||||
|
padding-top: 17px
|
||||||
position: relative
|
position: relative
|
||||||
background: #383434
|
background: transparent url(/images/level/scrubber_background.png)
|
||||||
// Counteract 50px height of absolutely positioned control bar.
|
background-size: 100% 100%
|
||||||
margin-top: 50px
|
// Counteract 50px height of absolutely positioned control bar, but overlap by 10px of jagged transparent top.
|
||||||
|
margin-top: 50px - 10px
|
||||||
|
z-index: 2
|
||||||
|
|
||||||
button
|
button
|
||||||
height: 26px
|
font-size: 26px
|
||||||
|
margin-left: 10px
|
||||||
background: transparent
|
background: transparent
|
||||||
@include opacity(0.50)
|
@include opacity(0.75)
|
||||||
i
|
color: $playback-button-color
|
||||||
|
text-shadow: 1px 1px 0px black
|
||||||
|
|
||||||
|
.glyphicon
|
||||||
position: relative
|
position: relative
|
||||||
|
|
||||||
button:hover
|
button:hover
|
||||||
@include opacity(0.75)
|
@include opacity(1)
|
||||||
|
|
||||||
#play-button, #volume-button, #music-button
|
#play-button, #volume-button, #music-button
|
||||||
float: left
|
float: left
|
||||||
margin-left: 2px
|
|
||||||
margin-top: 2px
|
|
||||||
width: 25px
|
|
||||||
position: relative
|
position: relative
|
||||||
|
|
||||||
#music-button
|
#music-button
|
||||||
@include opacity(0.25)
|
@include opacity(0.5)
|
||||||
font-size: 20px
|
|
||||||
span
|
span
|
||||||
position: relative
|
position: relative
|
||||||
left: -3px
|
left: -3px
|
||||||
top: -2px
|
top: -2px
|
||||||
&:hover
|
&:hover
|
||||||
@include opacity(0.50)
|
@include opacity(0.75)
|
||||||
&.music-on
|
&.music-on
|
||||||
@include opacity(0.50)
|
@include opacity(0.75)
|
||||||
&:hover
|
&:hover
|
||||||
@include opacity(0.75)
|
@include opacity(1)
|
||||||
|
|
||||||
#play-button, #volume-button
|
#play-button, #volume-button
|
||||||
i
|
.glyphicon
|
||||||
display: none
|
display: none
|
||||||
position: absolute
|
|
||||||
left: 2px
|
|
||||||
top: 2px
|
|
||||||
|
|
||||||
#settings-button
|
#settings-button
|
||||||
padding-left: 4px
|
padding-left: 4px
|
||||||
|
@ -54,51 +56,58 @@
|
||||||
#playback-settings
|
#playback-settings
|
||||||
float: right
|
float: right
|
||||||
position: relative
|
position: relative
|
||||||
top: 2px
|
margin-right: 10px
|
||||||
margin-right: 2px
|
ul button
|
||||||
ul i
|
|
||||||
margin: 0 10px
|
margin: 0 10px
|
||||||
li:hover
|
li:hover
|
||||||
background: #add8e6
|
background: #add8e6
|
||||||
|
|
||||||
#play-button.disabled i
|
#play-button.disabled .glyphicon
|
||||||
@include opacity(0.5)
|
@include opacity(0.75)
|
||||||
#play-button.playing i.icon-pause
|
#play-button.playing .glyphicon-pause
|
||||||
display: inline-block
|
display: inline-block
|
||||||
#play-button.paused i.icon-play
|
#play-button.paused .glyphicon-play
|
||||||
display: inline-block
|
display: inline-block
|
||||||
#play-button.ended i.icon-repeat
|
#play-button.ended .glyphicon-repeat
|
||||||
display: inline-block
|
display: inline-block
|
||||||
|
|
||||||
#volume-button.vol-up i.icon-volume-up
|
#volume-button.vol-up .glyphicon.glyphicon-volume-up
|
||||||
display: inline-block
|
display: inline-block
|
||||||
#volume-button.vol-off i.icon-volume-off
|
#volume-button.vol-off .glyphicon.glyphicon-volume-off
|
||||||
display: inline-block
|
display: inline-block
|
||||||
@include opacity(0.50)
|
@include opacity(0.75)
|
||||||
&:hover
|
&:hover
|
||||||
@include opacity(0.75)
|
@include opacity(1)
|
||||||
#volume-button.vol-down i.icon-volume-down
|
#volume-button.vol-down .glyphicon.glyphicon-volume-down
|
||||||
display: inline-block
|
display: inline-block
|
||||||
|
|
||||||
.scrubber
|
.scrubber
|
||||||
position: absolute
|
position: absolute
|
||||||
left: 100px
|
left: 170px
|
||||||
top: 0px
|
top: 21px
|
||||||
bottom: 0px
|
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
|
.progress
|
||||||
float: left
|
float: left
|
||||||
width: 100%
|
width: 100%
|
||||||
height: 14px
|
height: 12px
|
||||||
margin-top: 8px
|
|
||||||
cursor: pointer
|
cursor: pointer
|
||||||
overflow: visible
|
overflow: visible
|
||||||
border: 1px solid #444
|
border: 1px solid #444
|
||||||
// Remove gradient background in favor of solid fill
|
background: rgb(80, 67, 53)
|
||||||
background-color: #888
|
border-radius: 6px
|
||||||
background-image: none
|
|
||||||
border-radius: 0
|
|
||||||
border: 0
|
border: 0
|
||||||
// Can't do this transition because handle then jitters, but would be good for streaming.
|
// Can't do this transition because handle then jitters, but would be good for streaming.
|
||||||
//@include transition(width .2s linear)
|
//@include transition(width .2s linear)
|
||||||
|
@ -113,19 +122,19 @@
|
||||||
@include transition(width .0s linear)
|
@include transition(width .0s linear)
|
||||||
position: relative
|
position: relative
|
||||||
// Remove gradient background in favor of solid fill
|
// Remove gradient background in favor of solid fill
|
||||||
background-color: #67A4C8
|
background: rgb(245, 170, 49)
|
||||||
//background-image: none // gradient looks kind of cool though; keep it in
|
border: 1px solid rgb(62, 45, 16)
|
||||||
|
border-radius: 6px
|
||||||
|
|
||||||
.scrubber-handle
|
.scrubber-handle
|
||||||
cursor: pointer
|
cursor: pointer
|
||||||
position: absolute
|
position: absolute
|
||||||
right: -16px
|
right: -18px
|
||||||
top: -9px
|
top: -11px
|
||||||
background: transparent url(/images/level/playback_thumb.png)
|
background: transparent url(/images/level/scrubber_knob.png)
|
||||||
width: 32px
|
background-size: contain
|
||||||
height: 32px
|
width: 36px
|
||||||
// z: above the gradient line bordering the playback bar
|
height: 36px
|
||||||
z-index: 6
|
|
||||||
|
|
||||||
.ui-slider-handle
|
.ui-slider-handle
|
||||||
height: 100%
|
height: 100%
|
||||||
|
@ -140,3 +149,5 @@
|
||||||
body.ipad #playback-view
|
body.ipad #playback-view
|
||||||
#playback-settings
|
#playback-settings
|
||||||
display: none
|
display: none
|
||||||
|
.scrubber
|
||||||
|
right: 25px
|
||||||
|
|
|
@ -124,14 +124,6 @@
|
||||||
left: -3px
|
left: -3px
|
||||||
bottom: 0
|
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
|
#canvas-left-gradient
|
||||||
left: 0px
|
left: 0px
|
||||||
width: 5px
|
width: 5px
|
||||||
|
|
|
@ -43,6 +43,9 @@ block content
|
||||||
a(href="/admin/base", data-i18n="admin.av_other_debug_base_url") Base (for debugging base.jade)
|
a(href="/admin/base", data-i18n="admin.av_other_debug_base_url") Base (for debugging base.jade)
|
||||||
li
|
li
|
||||||
a(href="/admin/clas", data-i18n="admin.clas") CLAs
|
a(href="/admin/clas", data-i18n="admin.clas") CLAs
|
||||||
|
if me.isAdmin()
|
||||||
|
li
|
||||||
|
a(href="/admin/growth", data-i18n="admin.growth") Growth
|
||||||
|
|
||||||
hr
|
hr
|
||||||
|
|
||||||
|
|
32
app/templates/admin/growth.jade
Normal file
32
app/templates/admin/growth.jade
Normal file
|
@ -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
|
|
@ -1,6 +1,6 @@
|
||||||
ul#primary-goals-list
|
ul#primary-goals-list
|
||||||
div.goals-status
|
div.goals-status
|
||||||
strong(data-i18n="play_level.goals") Goals
|
span(data-i18n="play_level.goals") Goals
|
||||||
span.spr :
|
span.spr :
|
||||||
span(data-i18n="play_level.success").secret.goal-status.success Success!
|
span(data-i18n="play_level.success").secret.goal-status.success Success!
|
||||||
span(data-i18n="play_level.incomplete").secret.goal-status.incomplete Incomplete
|
span(data-i18n="play_level.incomplete").secret.goal-status.incomplete Incomplete
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
#hud-top-gradient.gradient
|
|
||||||
|
|
||||||
.center
|
.center
|
||||||
|
|
||||||
.thang-canvas-wrapper.thang-elem
|
.thang-canvas-wrapper.thang-elem
|
||||||
|
|
|
@ -1,45 +1,29 @@
|
||||||
button.btn.btn-xs.btn-inverse#play-button.paused(title="Ctrl/Cmd + P: Toggle level play/pause")
|
button.btn.btn-xs.btn-inverse#play-button.paused(title="Ctrl/Cmd + P: Toggle level play/pause")
|
||||||
i.icon-play.icon-white.big
|
.glyphicon.glyphicon-play
|
||||||
i.icon-pause.icon-white.big
|
.glyphicon.glyphicon-pause
|
||||||
i.icon-repeat.icon-white.big
|
.glyphicon.glyphicon-repeat
|
||||||
|
|
||||||
button.btn.btn-xs.btn-inverse#volume-button(title="Adjust volume")
|
button.btn.btn-xs.btn-inverse#volume-button(title="Adjust volume")
|
||||||
i.icon-volume-off.icon-white.big
|
.glyphicon.glyphicon-volume-off
|
||||||
i.icon-volume-down.icon-white.big
|
.glyphicon.glyphicon-volume-down
|
||||||
i.icon-volume-up.icon-white.big
|
.glyphicon.glyphicon-volume-up
|
||||||
button.btn.btn-xs.btn-inverse#music-button(title="Toggle Music")
|
button.btn.btn-xs.btn-inverse#music-button(title="Toggle Music")
|
||||||
span ♫
|
span ♫
|
||||||
|
|
||||||
.scrubber
|
.scrubber
|
||||||
.progress.secret#timeProgress
|
.scrubber-inner
|
||||||
.progress-bar
|
.progress.secret#timeProgress
|
||||||
.scrubber-handle
|
.progress-bar
|
||||||
.popover.fade.top.in#timePopover
|
.scrubber-handle
|
||||||
.arrow
|
.popover.fade.top.in#timePopover
|
||||||
h3.popover-title
|
.arrow
|
||||||
.popover-content
|
h3.popover-title
|
||||||
|
.popover-content
|
||||||
|
|
||||||
.btn-group.dropup#playback-settings
|
.btn-group.dropup#playback-settings
|
||||||
button.btn.btn-xs.btn-inverse.toggle-fullscreen(title="Toggle fullscreen")
|
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)")
|
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)")
|
button.btn.btn-xs.btn-inverse#zoom-out-button(title="Zoom Out (or scroll up)")
|
||||||
i.icon-zoom-out.icon-white
|
.glyphicon.glyphicon-zoom-out
|
||||||
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
|
|
||||||
|
|
192
app/views/admin/GrowthView.coffee
Normal file
192
app/views/admin/GrowthView.coffee
Normal file
|
@ -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)
|
||||||
|
|
|
@ -340,23 +340,25 @@ module.exports = class InventoryView extends CocoView
|
||||||
'simple-sword': '53e218d853457600003e3ebe'
|
'simple-sword': '53e218d853457600003e3ebe'
|
||||||
'leather-tunic': '53e22eac53457600003e3efc'
|
'leather-tunic': '53e22eac53457600003e3efc'
|
||||||
'leather-boots': '53e2384453457600003e3f07'
|
'leather-boots': '53e2384453457600003e3f07'
|
||||||
|
'leather-belt': '5437002a7beba4a82024a97d'
|
||||||
'programmaticon-i': '53e4108204c00d4607a89f78'
|
'programmaticon-i': '53e4108204c00d4607a89f78'
|
||||||
'crude-glasses': '53e238df53457600003e3f0b'
|
'crude-glasses': '53e238df53457600003e3f0b'
|
||||||
'builders-hammer': '53f4e6e3d822c23505b74f42'
|
'builders-hammer': '53f4e6e3d822c23505b74f42'
|
||||||
gearByLevel =
|
gearByLevel =
|
||||||
'dungeons-of-kithgard': {feet: 'simple-boots'}
|
'dungeons-of-kithgard': {feet: 'simple-boots'}
|
||||||
'gems-in-the-deep': {feet: 'simple-boots'}
|
'gems-in-the-deep': {feet: 'simple-boots'}
|
||||||
'forgetful-gemsmith': {feet: 'simple-boots'}
|
|
||||||
'shadow-guard': {feet: 'simple-boots'}
|
'shadow-guard': {feet: 'simple-boots'}
|
||||||
'kounter-kithwise': {feet: 'simple-boots'}
|
'kounter-kithwise': {feet: 'simple-boots'}
|
||||||
'crawlways-of-kithgard': {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'}
|
'favorable-odds': {feet: 'simple-boots', 'right-hand': 'simple-sword'}
|
||||||
'the-raised-sword': {feet: 'simple-boots', 'right-hand': 'simple-sword', torso: 'leather-tunic'}
|
'the-raised-sword': {feet: 'simple-boots', 'right-hand': 'simple-sword', torso: 'leather-tunic'}
|
||||||
'the-first-kithmaze': {feet: 'simple-boots', 'programming-book': 'programmaticon-i'}
|
'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'}
|
'descending-further': {feet: 'simple-boots', 'programming-book': 'programmaticon-i'}
|
||||||
'the-second-kithmaze': {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'}
|
'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'}
|
'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'}
|
'lowly-kithmen': {feet: 'simple-boots', 'right-hand': 'simple-sword', 'programming-book': 'programmaticon-i', eyes: 'crude-glasses'}
|
||||||
|
|
|
@ -630,22 +630,8 @@ dungeon = [
|
||||||
x: 29
|
x: 29
|
||||||
y: 12
|
y: 12
|
||||||
nextLevels:
|
nextLevels:
|
||||||
more_practice: 'forgetful-gemsmith'
|
|
||||||
continue: 'shadow-guard'
|
continue: 'shadow-guard'
|
||||||
skip_ahead: 'true-names'
|
skip_ahead: 'forgetful-gemsmith'
|
||||||
}
|
|
||||||
{
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
name: 'Shadow Guard'
|
name: 'Shadow Guard'
|
||||||
|
@ -654,11 +640,11 @@ dungeon = [
|
||||||
id: 'shadow-guard'
|
id: 'shadow-guard'
|
||||||
original: '54174347844506ae0195a0b8'
|
original: '54174347844506ae0195a0b8'
|
||||||
description: 'Evade the Kithgard minion.'
|
description: 'Evade the Kithgard minion.'
|
||||||
x: 50
|
x: 41
|
||||||
y: 11
|
y: 13
|
||||||
nextLevels:
|
nextLevels:
|
||||||
more_practice: 'kounter-kithwise'
|
more_practice: 'kounter-kithwise'
|
||||||
continue: 'true-names'
|
continue: 'forgetful-gemsmith'
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
name: 'Kounter Kithwise'
|
name: 'Kounter Kithwise'
|
||||||
|
@ -667,25 +653,37 @@ dungeon = [
|
||||||
id: 'kounter-kithwise'
|
id: 'kounter-kithwise'
|
||||||
original: '54527a6257e83800009730c7'
|
original: '54527a6257e83800009730c7'
|
||||||
description: 'Practice your evasion skills with more guards.'
|
description: 'Practice your evasion skills with more guards.'
|
||||||
x: 58
|
x: 50
|
||||||
y: 10
|
y: 14
|
||||||
nextLevels:
|
nextLevels:
|
||||||
more_practice: 'crawlways-of-kithgard'
|
#more_practice: 'crawlways-of-kithgard'
|
||||||
continue: 'true-names'
|
continue: 'true-names'
|
||||||
practice: true
|
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'
|
type: 'hero'
|
||||||
difficulty: 1
|
difficulty: 1
|
||||||
id: 'crawlways-of-kithgard'
|
id: 'forgetful-gemsmith'
|
||||||
original: '545287ef57e83800009730d5'
|
original: '544a98f62d002f0000fe331a'
|
||||||
description: 'Dart in and grab the gem–at the right moment.'
|
description: 'Grab even more gems as you practice moving.'
|
||||||
x: 67
|
x: 63
|
||||||
y: 10
|
y: 13
|
||||||
nextLevels:
|
nextLevels:
|
||||||
continue: 'true-names'
|
continue: 'shadow-guard'
|
||||||
practice: true
|
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
name: 'True Names'
|
name: 'True Names'
|
||||||
|
@ -695,7 +693,7 @@ dungeon = [
|
||||||
original: '541875da4c16460000ab990f'
|
original: '541875da4c16460000ab990f'
|
||||||
description: 'Learn an enemy\'s true name to defeat it.'
|
description: 'Learn an enemy\'s true name to defeat it.'
|
||||||
x: 74
|
x: 74
|
||||||
y: 12
|
y: 14
|
||||||
nextLevels:
|
nextLevels:
|
||||||
more_practice: 'favorable-odds'
|
more_practice: 'favorable-odds'
|
||||||
continue: 'the-raised-sword'
|
continue: 'the-raised-sword'
|
||||||
|
@ -737,7 +735,21 @@ dungeon = [
|
||||||
nextLevels:
|
nextLevels:
|
||||||
more_practice: 'descending-further'
|
more_practice: 'descending-further'
|
||||||
continue: 'the-second-kithmaze'
|
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'
|
name: 'Descending Further'
|
||||||
|
@ -762,15 +774,15 @@ dungeon = [
|
||||||
x: 59
|
x: 59
|
||||||
y: 25
|
y: 25
|
||||||
nextLevels:
|
nextLevels:
|
||||||
continue: 'new-sight'
|
continue: 'dread-door'
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
name: 'New Sight'
|
name: 'Dread Door'
|
||||||
type: 'hero'
|
type: 'hero'
|
||||||
difficulty: 1
|
difficulty: 1
|
||||||
id: 'new-sight'
|
id: 'dread-door'
|
||||||
original: '5418d40f4c16460000ab9ac2'
|
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
|
x: 60
|
||||||
y: 34
|
y: 34
|
||||||
nextLevels:
|
nextLevels:
|
||||||
|
@ -1006,3 +1018,9 @@ WorldMapView.campaigns = campaigns = [
|
||||||
{id: 'dungeon', name: 'Dungeon Campaign', levels: dungeon }
|
{id: 'dungeon', name: 'Dungeon Campaign', levels: dungeon }
|
||||||
{id: 'forest', name: 'Forest Campaign', levels: forest }
|
{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'
|
||||||
|
|
|
@ -101,7 +101,7 @@ module.exports = class LevelGoalsView extends CocoView
|
||||||
return if expand is @expanded
|
return if expand is @expanded
|
||||||
@updateHeight()
|
@updateHeight()
|
||||||
sound = if expand then 'goals-expand' else 'goals-collapse'
|
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
|
@$el.css 'top', top
|
||||||
if @soundTimeout
|
if @soundTimeout
|
||||||
# Don't play the sound we were going to play after all; the transition has reversed.
|
# Don't play the sound we were going to play after all; the transition has reversed.
|
||||||
|
|
|
@ -30,7 +30,7 @@ module.exports = class LevelHUDView extends CocoView
|
||||||
afterRender: ->
|
afterRender: ->
|
||||||
super()
|
super()
|
||||||
@$el.addClass 'no-selection'
|
@$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
|
@hidesHUD = true
|
||||||
@$el.addClass 'hide-hud-properties'
|
@$el.addClass 'hide-hud-properties'
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,6 @@ module.exports = class LevelPlaybackView extends CocoView
|
||||||
'level:scrub-forward': 'onScrubForward'
|
'level:scrub-forward': 'onScrubForward'
|
||||||
'level:scrub-back': 'onScrubBack'
|
'level:scrub-back': 'onScrubBack'
|
||||||
'level:set-volume': 'onSetVolume'
|
'level:set-volume': 'onSetVolume'
|
||||||
'level:set-debug': 'onSetDebug'
|
|
||||||
'surface:frame-changed': 'onFrameChanged'
|
'surface:frame-changed': 'onFrameChanged'
|
||||||
'god:new-world-created': 'onNewWorld'
|
'god:new-world-created': 'onNewWorld'
|
||||||
'god:streaming-world-updated': 'onNewWorld'
|
'god:streaming-world-updated': 'onNewWorld'
|
||||||
|
@ -28,10 +27,6 @@ module.exports = class LevelPlaybackView extends CocoView
|
||||||
'real-time-multiplayer:manual-cast': 'onRealTimeMultiplayerCast'
|
'real-time-multiplayer:manual-cast': 'onRealTimeMultiplayerCast'
|
||||||
|
|
||||||
events:
|
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 #music-button': 'onToggleMusic'
|
||||||
'click #zoom-in-button': -> Backbone.Mediator.publish 'camera:zoom-in', {} unless @shouldIgnore()
|
'click #zoom-in-button': -> Backbone.Mediator.publish 'camera:zoom-in', {} unless @shouldIgnore()
|
||||||
'click #zoom-out-button': -> Backbone.Mediator.publish 'camera:zoom-out', {} 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+]': 'onScrubForward'
|
||||||
'⌘+⇧+], ctrl+⇧+]': 'onSingleScrubForward'
|
'⌘+⇧+], 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: ->
|
constructor: ->
|
||||||
super(arguments...)
|
super(arguments...)
|
||||||
me.on('change:music', @updateMusicButton, @)
|
me.on('change:music', @updateMusicButton, @)
|
||||||
|
@ -192,20 +134,6 @@ module.exports = class LevelPlaybackView extends CocoView
|
||||||
@currentTime = 0
|
@currentTime = 0
|
||||||
@lastLoadedFrameCount = loadedFrameCount
|
@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) ->
|
onDisableControls: (e) ->
|
||||||
if not e.controls or 'playback' in e.controls
|
if not e.controls or 'playback' in e.controls
|
||||||
@disabled = true
|
@disabled = true
|
||||||
|
@ -340,10 +268,6 @@ module.exports = class LevelPlaybackView extends CocoView
|
||||||
Backbone.Mediator.publish 'level:set-letterbox', on: false
|
Backbone.Mediator.publish 'level:set-letterbox', on: false
|
||||||
Backbone.Mediator.publish 'playback:real-time-playback-ended', {}
|
Backbone.Mediator.publish 'playback:real-time-playback-ended', {}
|
||||||
|
|
||||||
onSetDebug: (e) ->
|
|
||||||
flag = $('#debug-toggle i.icon-ok')
|
|
||||||
flag.toggleClass 'invisible', not e.debug
|
|
||||||
|
|
||||||
# to refactor
|
# to refactor
|
||||||
|
|
||||||
hookUpScrubber: ->
|
hookUpScrubber: ->
|
||||||
|
@ -423,3 +347,56 @@ module.exports = class LevelPlaybackView extends CocoView
|
||||||
$(window).off('resize', @onWindowResize)
|
$(window).off('resize', @onWindowResize)
|
||||||
@onWindowResize = null
|
@onWindowResize = null
|
||||||
super()
|
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()
|
||||||
|
|
|
@ -108,7 +108,7 @@ module.exports = class CastButtonView extends CocoView
|
||||||
else if castable
|
else if castable
|
||||||
s = $.i18n.t('play_level.tome_cast_button_run')
|
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.
|
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
|
s += ' ' + @castShortcut
|
||||||
else
|
else
|
||||||
s = $.i18n.t('play_level.tome_cast_button_ran')
|
s = $.i18n.t('play_level.tome_cast_button_ran')
|
||||||
|
|
|
@ -871,4 +871,5 @@ requiredCodePerLevel =
|
||||||
'dungeons-of-kithgard': ['moveRight']
|
'dungeons-of-kithgard': ['moveRight']
|
||||||
'true-names': ['Brak']
|
'true-names': ['Brak']
|
||||||
'the-first-kithmaze': ['loop']
|
'the-first-kithmaze': ['loop']
|
||||||
|
'haunted-kithmaze': ['loop']
|
||||||
'lowly-kithmen': ['findNearestEnemy']
|
'lowly-kithmen': ['findNearestEnemy']
|
||||||
|
|
Loading…
Reference in a new issue