mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-11-29 10:35:51 -05:00
Merge branch 'master' into production
This commit is contained in:
commit
ea077b227c
35 changed files with 552 additions and 398 deletions
|
@ -88,7 +88,7 @@ module.exports = ScriptManager = class ScriptManager extends CocoClass
|
||||||
scriptStates: scriptStates
|
scriptStates: scriptStates
|
||||||
timeSinceLastScriptEnded: (if @lastScriptEnded then now - @lastScriptEnded else 0) / 1000
|
timeSinceLastScriptEnded: (if @lastScriptEnded then now - @lastScriptEnded else 0) / 1000
|
||||||
|
|
||||||
Backbone.Mediator.publish 'script:tick', stateEvent # Is this even needed?
|
Backbone.Mediator.publish 'script:tick', stateEvent # Used to trigger level scripts.
|
||||||
|
|
||||||
loadFromSession: ->
|
loadFromSession: ->
|
||||||
# load the queue with note groups to skip through
|
# load the queue with note groups to skip through
|
||||||
|
|
|
@ -24,8 +24,8 @@ module.exports = class CoordinateGrid extends CocoClass
|
||||||
toString: -> '<CoordinateGrid>'
|
toString: -> '<CoordinateGrid>'
|
||||||
|
|
||||||
build: (worldSize) ->
|
build: (worldSize) ->
|
||||||
worldWidth = worldSize[0] ? 80
|
worldWidth = worldSize[0] or 80
|
||||||
worldHeight = worldSize[1] ? 68
|
worldHeight = worldSize[1] or 68
|
||||||
@gridContainer = new createjs.Container()
|
@gridContainer = new createjs.Container()
|
||||||
@gridShape = new createjs.Shape()
|
@gridShape = new createjs.Shape()
|
||||||
@gridContainer.addChild @gridShape
|
@gridContainer.addChild @gridShape
|
||||||
|
|
|
@ -224,7 +224,7 @@ module.exports = class SpriteBoss extends CocoClass
|
||||||
for slot, itemID of thang.inventoryIDs
|
for slot, itemID of thang.inventoryIDs
|
||||||
item = @world.getThangByID itemID
|
item = @world.getThangByID itemID
|
||||||
unless item.equipped
|
unless item.equipped
|
||||||
#console.log thang.id, 'equipping', item, 'in', thang.slot, 'Surface-side'
|
console.log thang.id, 'equipping', item, 'in', thang.slot, 'Surface-side, but it cannot equip?' unless item.equip
|
||||||
item.equip()
|
item.equip()
|
||||||
itemsJustEquipped.push item
|
itemsJustEquipped.push item
|
||||||
return itemsJustEquipped
|
return itemsJustEquipped
|
||||||
|
|
|
@ -486,7 +486,6 @@ module.exports = Surface = class Surface extends CocoClass
|
||||||
|
|
||||||
onMouseUp: (e) =>
|
onMouseUp: (e) =>
|
||||||
return if @disabled
|
return if @disabled
|
||||||
console.log 'yo on mouse up', e
|
|
||||||
onBackground = not @stage.hitTest e.stageX, e.stageY
|
onBackground = not @stage.hitTest e.stageX, e.stageY
|
||||||
Backbone.Mediator.publish 'surface:stage-mouse-up', onBackground: onBackground, x: e.stageX, y: e.stageY, originalEvent: e
|
Backbone.Mediator.publish 'surface:stage-mouse-up', onBackground: onBackground, x: e.stageX, y: e.stageY, originalEvent: e
|
||||||
|
|
||||||
|
@ -497,8 +496,8 @@ module.exports = Surface = class Surface extends CocoClass
|
||||||
event =
|
event =
|
||||||
deltaX: e.deltaX
|
deltaX: e.deltaX
|
||||||
deltaY: e.deltaY
|
deltaY: e.deltaY
|
||||||
screenPos: @mouseScreenPos
|
|
||||||
canvas: @canvas
|
canvas: @canvas
|
||||||
|
event.screenPos = @mouseScreenPos if @mouseScreenPos
|
||||||
Backbone.Mediator.publish 'surface:mouse-scrolled', event unless @disabled
|
Backbone.Mediator.publish 'surface:mouse-scrolled', event unless @disabled
|
||||||
|
|
||||||
hookUpChooseControls: ->
|
hookUpChooseControls: ->
|
||||||
|
|
|
@ -126,6 +126,8 @@
|
||||||
level_difficulty: "Difficulty: "
|
level_difficulty: "Difficulty: "
|
||||||
play_as: "Play As"
|
play_as: "Play As"
|
||||||
spectate: "Spectate"
|
spectate: "Spectate"
|
||||||
|
players: "players"
|
||||||
|
hours_played: "hours played"
|
||||||
|
|
||||||
contact:
|
contact:
|
||||||
contact_us: "Contact CodeCombat"
|
contact_us: "Contact CodeCombat"
|
||||||
|
@ -511,6 +513,7 @@
|
||||||
toggle_grid: "Toggle grid overlay."
|
toggle_grid: "Toggle grid overlay."
|
||||||
toggle_pathfinding: "Toggle pathfinding overlay."
|
toggle_pathfinding: "Toggle pathfinding overlay."
|
||||||
beautify: "Beautify your code by standardizing its formatting."
|
beautify: "Beautify your code by standardizing its formatting."
|
||||||
|
maximize_editor: "Maximize/minimize code editor."
|
||||||
move_wizard: "Move your Wizard around the level."
|
move_wizard: "Move your Wizard around the level."
|
||||||
|
|
||||||
admin:
|
admin:
|
||||||
|
@ -948,6 +951,7 @@
|
||||||
wizard: "Wizard"
|
wizard: "Wizard"
|
||||||
achievement: "Achievement"
|
achievement: "Achievement"
|
||||||
clas: "CLAs"
|
clas: "CLAs"
|
||||||
|
play_counts: "Play Counts"
|
||||||
|
|
||||||
delta:
|
delta:
|
||||||
added: "Added"
|
added: "Added"
|
||||||
|
|
|
@ -3,11 +3,11 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr
|
||||||
loading: "Laddar..."
|
loading: "Laddar..."
|
||||||
saving: "Sparar..."
|
saving: "Sparar..."
|
||||||
sending: "Skickar..."
|
sending: "Skickar..."
|
||||||
# send: "Send"
|
send: "Skicka"
|
||||||
cancel: "Avbryt"
|
cancel: "Avbryt"
|
||||||
save: "Spara"
|
save: "Spara"
|
||||||
# publish: "Publish"
|
publish: "Publisera"
|
||||||
# create: "Create"
|
create: "Skapa"
|
||||||
delay_1_sec: "1 sekund"
|
delay_1_sec: "1 sekund"
|
||||||
delay_3_sec: "3 sekunder"
|
delay_3_sec: "3 sekunder"
|
||||||
delay_5_sec: "5 sekunder"
|
delay_5_sec: "5 sekunder"
|
||||||
|
@ -19,21 +19,21 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr
|
||||||
# unwatch: "Unwatch"
|
# unwatch: "Unwatch"
|
||||||
# submit_patch: "Submit Patch"
|
# submit_patch: "Submit Patch"
|
||||||
|
|
||||||
# units:
|
units:
|
||||||
# second: "second"
|
second: "sekund"
|
||||||
# seconds: "seconds"
|
seconds: "sekunder"
|
||||||
# minute: "minute"
|
minute: "minut"
|
||||||
# minutes: "minutes"
|
minutes: "minuter"
|
||||||
# hour: "hour"
|
hour: "timme"
|
||||||
# hours: "hours"
|
hours: "timmar"
|
||||||
# day: "day"
|
day: "dag"
|
||||||
# days: "days"
|
days: "dagar"
|
||||||
# week: "week"
|
week: "vecka"
|
||||||
# weeks: "weeks"
|
weeks: "veckor"
|
||||||
# month: "month"
|
month: "månad"
|
||||||
# months: "months"
|
months: "månader"
|
||||||
# year: "year"
|
year: "år"
|
||||||
# years: "years"
|
years: "år"
|
||||||
|
|
||||||
modal:
|
modal:
|
||||||
close: "Stäng"
|
close: "Stäng"
|
||||||
|
@ -44,14 +44,14 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr
|
||||||
|
|
||||||
nav:
|
nav:
|
||||||
play: "Spela"
|
play: "Spela"
|
||||||
# community: "Community"
|
community: "Community"
|
||||||
editor: "Nivåredigerare"
|
editor: "Nivåredigerare"
|
||||||
blog: "Blogg"
|
blog: "Blogg"
|
||||||
forum: "Forum"
|
forum: "Forum"
|
||||||
# account: "Account"
|
account: "Konto"
|
||||||
# profile: "Profile"
|
profile: "Profil"
|
||||||
# stats: "Stats"
|
stats: "Stats"
|
||||||
# code: "Code"
|
code: "Kod"
|
||||||
admin: "Admin"
|
admin: "Admin"
|
||||||
home: "Hem"
|
home: "Hem"
|
||||||
contribute: "Bidra"
|
contribute: "Bidra"
|
||||||
|
@ -72,9 +72,9 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr
|
||||||
login:
|
login:
|
||||||
sign_up: "Skapa konto"
|
sign_up: "Skapa konto"
|
||||||
log_in: "Logga in"
|
log_in: "Logga in"
|
||||||
# logging_in: "Logging In"
|
logging_in: "Loggar In"
|
||||||
log_out: "Logga ut"
|
log_out: "Logga ut"
|
||||||
recover: "glömt lösenord"
|
recover: "Glömt lösenord"
|
||||||
|
|
||||||
recover:
|
recover:
|
||||||
recover_account_title: "Återskapa ditt konto"
|
recover_account_title: "Återskapa ditt konto"
|
||||||
|
@ -89,7 +89,7 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr
|
||||||
creating: "Skapar konto..."
|
creating: "Skapar konto..."
|
||||||
sign_up: "Skapa konto"
|
sign_up: "Skapa konto"
|
||||||
log_in: "logga in med lösenord"
|
log_in: "logga in med lösenord"
|
||||||
# social_signup: "Or, you can sign up through Facebook or G+:"
|
social_signup: "Eller så kan du logga in genom facebook eller g+:"
|
||||||
# required: "You need to log in before you can go that way."
|
# required: "You need to log in before you can go that way."
|
||||||
|
|
||||||
home:
|
home:
|
||||||
|
|
|
@ -109,10 +109,6 @@ NoteGroupSchema = c.object {title: 'Note Group', description: 'A group of notes
|
||||||
lock: {title: 'Lock', description: 'Whether the interface should be locked so that the player\'s focus is on the script, or specific areas to lock.', type: ['boolean', 'array'], items: {type: 'string', enum: ['surface', 'editor', 'palette', 'hud', 'playback', 'playback-hover', 'level']}}
|
lock: {title: 'Lock', description: 'Whether the interface should be locked so that the player\'s focus is on the script, or specific areas to lock.', type: ['boolean', 'array'], items: {type: 'string', enum: ['surface', 'editor', 'palette', 'hud', 'playback', 'playback-hover', 'level']}}
|
||||||
letterbox: {type: 'boolean', title: 'Letterbox', description: 'Turn letterbox mode on or off. Disables surface and playback controls.'}
|
letterbox: {type: 'boolean', title: 'Letterbox', description: 'Turn letterbox mode on or off. Disables surface and playback controls.'}
|
||||||
|
|
||||||
goals: c.object {title: 'Goals (Old)', description: 'Deprecated. Goals added here have no effect. Add goals in the level settings instead.'},
|
|
||||||
add: c.array {title: 'Add', description: 'Deprecated. Goals added here have no effect. Add goals in the level settings instead.'}, {}
|
|
||||||
remove: c.array {title: 'Remove', description: 'Deprecated. Goals removed here have no effect. Adjust goals in the level settings instead.'}, {}
|
|
||||||
|
|
||||||
playback: c.object {title: 'Playback', description: 'Control the playback of the level.'},
|
playback: c.object {title: 'Playback', description: 'Control the playback of the level.'},
|
||||||
playing: {type: 'boolean', title: 'Set Playing', description: 'Set whether playback is playing or paused.'}
|
playing: {type: 'boolean', title: 'Set Playing', description: 'Set whether playback is playing or paused.'}
|
||||||
scrub: c.object {title: 'Scrub', description: 'Scrub the level playback time to a certain point.', default: {offset: 2, duration: 1000, toRatio: 0.5}},
|
scrub: c.object {title: 'Scrub', description: 'Scrub the level playback time to a certain point.', default: {offset: 2, duration: 1000, toRatio: 0.5}},
|
||||||
|
|
|
@ -29,6 +29,9 @@ module.exports =
|
||||||
|
|
||||||
'editor:level-thang-done-editing': c.object {}
|
'editor:level-thang-done-editing': c.object {}
|
||||||
|
|
||||||
|
'editor:thangs-edited': c.object {required: ['thangs']},
|
||||||
|
thangs: c.array {}, {type: 'object'}
|
||||||
|
|
||||||
'editor:level-loaded': c.object {required: ['level']},
|
'editor:level-loaded': c.object {required: ['level']},
|
||||||
level: {type: 'object'}
|
level: {type: 'object'}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ goalStatesSchema =
|
||||||
{type: 'integer', minimum: 0}
|
{type: 'integer', minimum: 0}
|
||||||
{type: 'string', enum: ['end']}
|
{type: 'string', enum: ['end']}
|
||||||
]
|
]
|
||||||
team: {type: ['null', 'string']}
|
team: {type: ['null', 'string', 'undefined']}
|
||||||
|
|
||||||
worldUpdatedEventSchema = c.object {required: ['world', 'firstWorld', 'goalStates', 'team', 'firstChangedFrame']},
|
worldUpdatedEventSchema = c.object {required: ['world', 'firstWorld', 'goalStates', 'team', 'firstChangedFrame']},
|
||||||
world: {type: 'object'}
|
world: {type: 'object'}
|
||||||
|
@ -50,4 +50,4 @@ module.exports =
|
||||||
|
|
||||||
'god:debug-value-return': c.object {required: ['key']},
|
'god:debug-value-return': c.object {required: ['key']},
|
||||||
key: {type: 'string'}
|
key: {type: 'string'}
|
||||||
value: {type: 'any'}
|
value: {type: ['any', 'undefined']}
|
||||||
|
|
|
@ -140,7 +140,7 @@ module.exports =
|
||||||
{type: 'integer', minimum: 0}
|
{type: 'integer', minimum: 0}
|
||||||
{type: 'string', enum: ['end']}
|
{type: 'string', enum: ['end']}
|
||||||
]
|
]
|
||||||
team: {type: ['null', 'string']}
|
team: {type: ['null', 'string', 'undefined']}
|
||||||
goals: c.array {},
|
goals: c.array {},
|
||||||
{type: 'object'}
|
{type: 'object'}
|
||||||
overallStatus:
|
overallStatus:
|
||||||
|
|
|
@ -38,19 +38,19 @@ module.exports = # /app/lib/surface
|
||||||
|
|
||||||
'sprite:speech-updated': c.object {required: ['sprite', 'thang']},
|
'sprite:speech-updated': c.object {required: ['sprite', 'thang']},
|
||||||
sprite: {type: 'object'}
|
sprite: {type: 'object'}
|
||||||
thang: {type: 'object'}
|
thang: {type: ['object', 'null']}
|
||||||
blurb: {type: 'string'}
|
blurb: {type: ['string', 'null']}
|
||||||
message: {type: 'string'}
|
message: {type: 'string'}
|
||||||
mood: {type: 'string'}
|
mood: {type: 'string'}
|
||||||
responses: {type: 'array'}
|
responses: {type: ['array', 'null', 'undefined']}
|
||||||
spriteID: {type: 'string'}
|
spriteID: {type: 'string'}
|
||||||
sound: {type: ['null', 'undefined', 'object']}
|
sound: {type: ['null', 'undefined', 'object']}
|
||||||
|
|
||||||
'level:sprite-dialogue': c.object {required: ['spriteID', 'message']},
|
'level:sprite-dialogue': c.object {required: ['spriteID', 'message']},
|
||||||
blurb: {type: 'string'}
|
blurb: {type: ['string', 'null']}
|
||||||
message: {type: 'string'}
|
message: {type: 'string'}
|
||||||
mood: {type: 'string'}
|
mood: {type: 'string'}
|
||||||
responses: {type: 'array'}
|
responses: {type: ['array', 'null', 'undefined']}
|
||||||
spriteID: {type: 'string'}
|
spriteID: {type: 'string'}
|
||||||
sound: {type: ['null', 'undefined', 'object']}
|
sound: {type: ['null', 'undefined', 'object']}
|
||||||
|
|
||||||
|
@ -141,13 +141,13 @@ module.exports = # /app/lib/surface
|
||||||
originalEvent: {type: 'object'}
|
originalEvent: {type: 'object'}
|
||||||
worldPos: {type: ['object', 'null', 'undefined']}
|
worldPos: {type: ['object', 'null', 'undefined']}
|
||||||
|
|
||||||
'surface:stage-mouse-up': c.object {required: ['onBackground', 'x', 'y', 'originalEvent']},
|
'surface:stage-mouse-up': c.object {required: ['onBackground', 'originalEvent']},
|
||||||
onBackground: {type: 'boolean'}
|
onBackground: {type: 'boolean'}
|
||||||
x: {type: 'number'}
|
x: {type: ['number', 'undefined']}
|
||||||
y: {type: 'number'}
|
y: {type: ['number', 'undefined']}
|
||||||
originalEvent: {type: 'object'}
|
originalEvent: {type: 'object'}
|
||||||
|
|
||||||
'surface:mouse-scrolled': c.object {required: ['deltaX', 'deltaY', 'screenPos', 'canvas']},
|
'surface:mouse-scrolled': c.object {required: ['deltaX', 'deltaY', 'canvas']},
|
||||||
deltaX: {type: 'number'}
|
deltaX: {type: 'number'}
|
||||||
deltaY: {type: 'number'}
|
deltaY: {type: 'number'}
|
||||||
screenPos: c.object {required: ['x', 'y']},
|
screenPos: c.object {required: ['x', 'y']},
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
c = require 'schemas/schemas'
|
c = require 'schemas/schemas'
|
||||||
|
|
||||||
module.exports =
|
module.exports =
|
||||||
'tome:cast-spell': c.object {title: 'Cast Spell', description: 'Published when a spell is cast', required: ['spell', 'thang', 'preload', 'realTime']},
|
'tome:cast-spell': c.object {title: 'Cast Spell', description: 'Published when a spell is cast', required: []},
|
||||||
spell: {type: 'object'}
|
spell: {type: 'object'}
|
||||||
thang: {type: 'object'}
|
thang: {type: 'object'}
|
||||||
preload: {type: 'boolean'}
|
preload: {type: 'boolean'}
|
||||||
|
@ -106,4 +106,6 @@ module.exports =
|
||||||
|
|
||||||
'tome:focus-editor': c.object {title: 'Focus Editor', description: 'Published whenever we want to give focus back to the editor'}
|
'tome:focus-editor': c.object {title: 'Focus Editor', description: 'Published whenever we want to give focus back to the editor'}
|
||||||
|
|
||||||
'tome:fullscreen-view': c.object {title: 'Fullscreen View', description: 'Published when we want to make the Tome take up most of the screen'}
|
'tome:toggle-maximize': c.object {title: 'Toggle Maximize', description: 'Published when we want to make the Tome take up most of the screen'}
|
||||||
|
|
||||||
|
'tome:maximize-toggled': c.object {title: 'Maximize Toggled', description: 'Published when the Tome has changed maximize/minimize state.'}
|
||||||
|
|
|
@ -21,6 +21,11 @@
|
||||||
margin-left: 5px
|
margin-left: 5px
|
||||||
input[type="file"]
|
input[type="file"]
|
||||||
display: none
|
display: none
|
||||||
|
|
||||||
|
#thang-type-file-size
|
||||||
|
width: 350px
|
||||||
|
float: left
|
||||||
|
margin: 20px 10px
|
||||||
|
|
||||||
.treema-row img
|
.treema-row img
|
||||||
max-width: 100%
|
max-width: 100%
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
margin-left: 20px
|
margin-left: 20px
|
||||||
|
|
||||||
h3
|
h3
|
||||||
|
margin-top: 0
|
||||||
margin-bottom: 0px
|
margin-bottom: 0px
|
||||||
|
|
||||||
.level-description
|
.level-description
|
||||||
|
|
|
@ -73,7 +73,7 @@ body.is-playing
|
||||||
right: 0
|
right: 0
|
||||||
top: 0px
|
top: 0px
|
||||||
bottom: 0
|
bottom: 0
|
||||||
transition: width 0.5s ease-in-out
|
@include transition(width 0.5s ease-in-out, right 0.5s ease-in-out)
|
||||||
|
|
||||||
#pointer
|
#pointer
|
||||||
position: absolute
|
position: absolute
|
||||||
|
@ -186,9 +186,25 @@ body.is-playing
|
||||||
color: white
|
color: white
|
||||||
text-decoration: underline
|
text-decoration: underline
|
||||||
|
|
||||||
|
#fullscreen-editor-background-screen
|
||||||
|
background-color: black
|
||||||
|
opacity: 0.5
|
||||||
|
cursor: pointer
|
||||||
|
display: none
|
||||||
|
position: absolute
|
||||||
|
left: 0
|
||||||
|
right: 0
|
||||||
|
bottom: 0
|
||||||
|
top: 0
|
||||||
|
z-index: 19
|
||||||
|
|
||||||
html.fullscreen-editor
|
html.fullscreen-editor
|
||||||
#level-view
|
#level-view
|
||||||
|
#fullscreen-editor-background-screen
|
||||||
|
display: block
|
||||||
|
|
||||||
#code-area
|
#code-area
|
||||||
position: fixed
|
position: fixed
|
||||||
width: 100%
|
width: 95%
|
||||||
height: 100%
|
height: 100%
|
||||||
|
right: 0
|
||||||
|
|
|
@ -86,6 +86,7 @@ block outer_content
|
||||||
span.glyphicon.glyphicon-remove
|
span.glyphicon.glyphicon-remove
|
||||||
span.spl Clear Data
|
span.spl Clear Data
|
||||||
input#real-upload-button(type="file")
|
input#real-upload-button(type="file")
|
||||||
|
#thang-type-file-size= fileSizeString
|
||||||
div#thang-type-treema
|
div#thang-type-treema
|
||||||
.clearfix
|
.clearfix
|
||||||
div#display-col.well
|
div#display-col.well
|
||||||
|
|
|
@ -28,4 +28,11 @@ block content
|
||||||
span(data-i18n="play.level_difficulty") Difficulty:
|
span(data-i18n="play.level_difficulty") Difficulty:
|
||||||
each i in Array(level.difficulty)
|
each i in Array(level.difficulty)
|
||||||
i.icon-star
|
i.icon-star
|
||||||
|
- var playCount = levelPlayCountMap[level.id]
|
||||||
|
if playCount
|
||||||
|
div
|
||||||
|
span.spr #{playCount.sessions}
|
||||||
|
span(data-i18n="play.players") players
|
||||||
|
span.spr , #{Math.round(playCount.playtime / 3600)}
|
||||||
|
span(data-i18n="play.hours_played") hours played
|
||||||
|
|
||||||
|
|
|
@ -19,5 +19,9 @@ block content
|
||||||
span(data-i18n="play.level_difficulty") Difficulty:
|
span(data-i18n="play.level_difficulty") Difficulty:
|
||||||
each i in Array(level.difficulty)
|
each i in Array(level.difficulty)
|
||||||
| ★
|
| ★
|
||||||
|
- var playCount = levelPlayCountMap[level.id]
|
||||||
|
if playCount
|
||||||
|
span.spl.spr - #{playCount.sessions}
|
||||||
|
span(data-i18n="play.players") players
|
||||||
.play-text-container
|
.play-text-container
|
||||||
.overlay-text.play-text(data-i18n="home.play") Play
|
.overlay-text.play-text(data-i18n="home.play") Play
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
.level-content
|
.level-content
|
||||||
#control-bar-view
|
#control-bar-view
|
||||||
|
|
||||||
|
#fullscreen-editor-background-screen(title="Click to minimize the code editor")
|
||||||
|
|
||||||
#code-area
|
#code-area
|
||||||
#code-area-gradient.gradient
|
#code-area-gradient.gradient
|
||||||
|
|
|
@ -5,15 +5,15 @@ block modal-header-content
|
||||||
|
|
||||||
block modal-body-content
|
block modal-body-content
|
||||||
dl.dl-horizontal
|
dl.dl-horizontal
|
||||||
dt(title="Shift+" + enter)
|
dt(title=shift + "+" + enter)
|
||||||
code ⇧+#{enter}
|
code ⇧+#{enter}
|
||||||
dd(data-i18n="keyboard_shortcuts.cast_spell") Cast current spell.
|
dd(data-i18n="keyboard_shortcuts.cast_spell") Cast current spell.
|
||||||
dl.dl-horizontal
|
dl.dl-horizontal
|
||||||
dt(title=ctrlName + "+Shift+" + enter)
|
dt(title=ctrlName + "+" + shift + "+" + enter)
|
||||||
code #{ctrl}+⇧+#{enter}
|
code #{ctrl}+⇧+#{enter}
|
||||||
dd(data-i18n="keyboard_shortcuts.run_real_time") Run in real time.
|
dd(data-i18n="keyboard_shortcuts.run_real_time") Run in real time.
|
||||||
dl.dl-horizontal
|
dl.dl-horizontal
|
||||||
dt(title="Shift+" + space)
|
dt(title=shift + "+" + space)
|
||||||
code ⇧+#{space}
|
code ⇧+#{space}
|
||||||
dd(data-i18n="keyboard_shortcuts.continue_script") Continue past current script.
|
dd(data-i18n="keyboard_shortcuts.continue_script") Continue past current script.
|
||||||
dl.dl-horizontal
|
dl.dl-horizontal
|
||||||
|
@ -55,9 +55,13 @@ block modal-body-content
|
||||||
code #{ctrl}+O
|
code #{ctrl}+O
|
||||||
dd(data-i18n="keyboard_shortcuts.toggle_pathfinding") Toggle pathfinding overlay.
|
dd(data-i18n="keyboard_shortcuts.toggle_pathfinding") Toggle pathfinding overlay.
|
||||||
dl.dl-horizontal
|
dl.dl-horizontal
|
||||||
dt(title=ctrlName + "+Shift+B")
|
dt(title=ctrlName + "+" + shift + "+B")
|
||||||
code #{ctrl}+⇧+B
|
code #{ctrl}+⇧+B
|
||||||
dd(data-i18n="keyboard_shortcuts.beautify") Beautify your code by standardizing its formatting.
|
dd(data-i18n="keyboard_shortcuts.beautify") Beautify your code by standardizing its formatting.
|
||||||
|
dl.dl-horizontal
|
||||||
|
dt(title=ctrlName + "+" + shift + "+M")
|
||||||
|
code #{ctrl}+⇧+M
|
||||||
|
dd(data-i18n="keyboard_shortcuts.maximize_editor") Maximize/minimize code editor.
|
||||||
dl.dl-horizontal
|
dl.dl-horizontal
|
||||||
dt(title="Arrow keys")
|
dt(title="Arrow keys")
|
||||||
code ←
|
code ←
|
||||||
|
|
|
@ -7,14 +7,14 @@ img(src="/images/level/code_editor_tab_background.png").spell-tab-image-hidden.h
|
||||||
code #{methodSignature}
|
code #{methodSignature}
|
||||||
|
|
||||||
.spell-tool-buttons
|
.spell-tool-buttons
|
||||||
.btn.btn-small.fullscreen-code(title="Expand code editor")
|
.btn.btn-small.fullscreen-code(title=maximizeShortcutVerbose)
|
||||||
i.icon-fullscreen
|
i.icon-fullscreen
|
||||||
i.icon-resize-small
|
i.icon-resize-small
|
||||||
|
|
||||||
.btn.btn-small.reload-code(title="Reload original code for " + spell.name)
|
.btn.btn-small.reload-code(title="Reload original code for " + spell.name)
|
||||||
i.icon-repeat
|
i.icon-repeat
|
||||||
|
|
||||||
.btn.btn-small.beautify-code(title="Ctrl+Shift+B: Beautify code for " + spell.name)
|
.btn.btn-small.beautify-code(title=beautifyShortcutVerbose)
|
||||||
i.icon-magnet
|
i.icon-magnet
|
||||||
|
|
||||||
.clearfix
|
.clearfix
|
|
@ -12,6 +12,7 @@ module.exports = class ScriptsTabView extends CocoView
|
||||||
|
|
||||||
subscriptions:
|
subscriptions:
|
||||||
'editor:level-loaded': 'onLevelLoaded'
|
'editor:level-loaded': 'onLevelLoaded'
|
||||||
|
'editor:thangs-edited': 'onThangsEdited'
|
||||||
|
|
||||||
constructor: (options) ->
|
constructor: (options) ->
|
||||||
super options
|
super options
|
||||||
|
@ -53,7 +54,7 @@ module.exports = class ScriptsTabView extends CocoView
|
||||||
@selectedScriptPath = null
|
@selectedScriptPath = null
|
||||||
return
|
return
|
||||||
|
|
||||||
thangIDs = @getThangIDs()
|
@thangIDs = @getThangIDs()
|
||||||
treemaOptions =
|
treemaOptions =
|
||||||
world: @world
|
world: @world
|
||||||
filePath: "db/level/#{@level.get('original')}"
|
filePath: "db/level/#{@level.get('original')}"
|
||||||
|
@ -61,7 +62,7 @@ module.exports = class ScriptsTabView extends CocoView
|
||||||
view: @
|
view: @
|
||||||
schema: Level.schema.properties.scripts.items
|
schema: Level.schema.properties.scripts.items
|
||||||
data: selected.data
|
data: selected.data
|
||||||
thangIDs: thangIDs
|
thangIDs: @thangIDs
|
||||||
dimensions: @dimensions
|
dimensions: @dimensions
|
||||||
supermodel: @supermodel
|
supermodel: @supermodel
|
||||||
readOnly: me.get('anonymous')
|
readOnly: me.get('anonymous')
|
||||||
|
@ -88,7 +89,7 @@ module.exports = class ScriptsTabView extends CocoView
|
||||||
@selectedScriptPath = newPath
|
@selectedScriptPath = newPath
|
||||||
|
|
||||||
getThangIDs: ->
|
getThangIDs: ->
|
||||||
(t.id for t in @level.get('thangs') when t.id isnt 'Interface')
|
(t.id for t in @level.get('thangs'))
|
||||||
|
|
||||||
onNewScriptAdded: (scriptNode) =>
|
onNewScriptAdded: (scriptNode) =>
|
||||||
return unless scriptNode
|
return unless scriptNode
|
||||||
|
@ -110,6 +111,11 @@ module.exports = class ScriptsTabView extends CocoView
|
||||||
onScriptChanged: =>
|
onScriptChanged: =>
|
||||||
@scriptsTreema.set(@selectedScriptPath, @scriptTreema.data)
|
@scriptsTreema.set(@selectedScriptPath, @scriptTreema.data)
|
||||||
|
|
||||||
|
onThangsEdited: (e) ->
|
||||||
|
# Update in-place so existing Treema nodes refer to the same array.
|
||||||
|
@thangIDs.splice(0, @thangIDs.length, @getThangIDs()...)
|
||||||
|
|
||||||
|
|
||||||
class ScriptsNode extends TreemaArrayNode
|
class ScriptsNode extends TreemaArrayNode
|
||||||
nodeDescription: 'Script'
|
nodeDescription: 'Script'
|
||||||
addNewChild: ->
|
addNewChild: ->
|
||||||
|
|
|
@ -18,6 +18,7 @@ module.exports = class SettingsTabView extends CocoView
|
||||||
|
|
||||||
subscriptions:
|
subscriptions:
|
||||||
'editor:level-loaded': 'onLevelLoaded'
|
'editor:level-loaded': 'onLevelLoaded'
|
||||||
|
'editor:thangs-edited': 'onThangsEdited'
|
||||||
|
|
||||||
constructor: (options) ->
|
constructor: (options) ->
|
||||||
super options
|
super options
|
||||||
|
@ -29,7 +30,7 @@ module.exports = class SettingsTabView extends CocoView
|
||||||
schema = _.cloneDeep Level.schema
|
schema = _.cloneDeep Level.schema
|
||||||
schema.properties = _.pick schema.properties, (value, key) => key in @editableSettings
|
schema.properties = _.pick schema.properties, (value, key) => key in @editableSettings
|
||||||
schema.required = _.intersection schema.required, @editableSettings
|
schema.required = _.intersection schema.required, @editableSettings
|
||||||
thangIDs = @getThangIDs()
|
@thangIDs = @getThangIDs()
|
||||||
treemaOptions =
|
treemaOptions =
|
||||||
filePath: "db/level/#{@level.get('original')}"
|
filePath: "db/level/#{@level.get('original')}"
|
||||||
supermodel: @supermodel
|
supermodel: @supermodel
|
||||||
|
@ -37,7 +38,7 @@ module.exports = class SettingsTabView extends CocoView
|
||||||
data: data
|
data: data
|
||||||
readOnly: me.get('anonymous')
|
readOnly: me.get('anonymous')
|
||||||
callbacks: {change: @onSettingsChanged}
|
callbacks: {change: @onSettingsChanged}
|
||||||
thangIDs: thangIDs
|
thangIDs: @thangIDs
|
||||||
nodeClasses:
|
nodeClasses:
|
||||||
object: SettingsNode
|
object: SettingsNode
|
||||||
thang: nodes.ThangNode
|
thang: nodes.ThangNode
|
||||||
|
@ -47,7 +48,7 @@ module.exports = class SettingsTabView extends CocoView
|
||||||
@settingsTreema.open()
|
@settingsTreema.open()
|
||||||
|
|
||||||
getThangIDs: ->
|
getThangIDs: ->
|
||||||
(t.id for t in @level.get('thangs') when t.id isnt 'Interface')
|
(t.id for t in @level.get('thangs'))
|
||||||
|
|
||||||
onSettingsChanged: (e) =>
|
onSettingsChanged: (e) =>
|
||||||
$('.level-title').text @settingsTreema.data.name
|
$('.level-title').text @settingsTreema.data.name
|
||||||
|
@ -55,5 +56,10 @@ module.exports = class SettingsTabView extends CocoView
|
||||||
continue if @settingsTreema.data[key] is undefined
|
continue if @settingsTreema.data[key] is undefined
|
||||||
@level.set key, @settingsTreema.data[key]
|
@level.set key, @settingsTreema.data[key]
|
||||||
|
|
||||||
|
onThangsEdited: (e) ->
|
||||||
|
# Update in-place so existing Treema nodes refer to the same array.
|
||||||
|
@thangIDs.splice(0, @thangIDs.length, @getThangIDs()...)
|
||||||
|
|
||||||
|
|
||||||
class SettingsNode extends TreemaObjectNode
|
class SettingsNode extends TreemaObjectNode
|
||||||
nodeDescription: 'Settings'
|
nodeDescription: 'Settings'
|
||||||
|
|
|
@ -402,7 +402,7 @@ module.exports = class ThangsTabView extends CocoView
|
||||||
thang.isSelectable = not thang.isLand for thang in @world.thangs # let us select walls and such
|
thang.isSelectable = not thang.isLand for thang in @world.thangs # let us select walls and such
|
||||||
@surface?.setWorld @world
|
@surface?.setWorld @world
|
||||||
@selectAddThangType @addThangType, @cloneSourceThang if @addThangType # make another addThang sprite, since the World just refreshed
|
@selectAddThangType @addThangType, @cloneSourceThang if @addThangType # make another addThang sprite, since the World just refreshed
|
||||||
null
|
Backbone.Mediator.publish 'editor:thangs-edited', thangs: @world.thangs
|
||||||
|
|
||||||
onTreemaThangSelected: (e, selectedTreemas) =>
|
onTreemaThangSelected: (e, selectedTreemas) =>
|
||||||
selectedThangID = _.last(selectedTreemas)?.data.id
|
selectedThangID = _.last(selectedTreemas)?.data.id
|
||||||
|
|
|
@ -56,6 +56,7 @@ module.exports = class ThangTypeEditView extends RootView
|
||||||
@thangType.saveBackups = true
|
@thangType.saveBackups = true
|
||||||
@listenToOnce @thangType, 'sync', ->
|
@listenToOnce @thangType, 'sync', ->
|
||||||
@files = @supermodel.loadCollection(new DocumentFiles(@thangType), 'files').model
|
@files = @supermodel.loadCollection(new DocumentFiles(@thangType), 'files').model
|
||||||
|
@updateFileSize()
|
||||||
@refreshAnimation = _.debounce @refreshAnimation, 500
|
@refreshAnimation = _.debounce @refreshAnimation, 500
|
||||||
|
|
||||||
getRenderData: (context={}) ->
|
getRenderData: (context={}) ->
|
||||||
|
@ -64,6 +65,7 @@ module.exports = class ThangTypeEditView extends RootView
|
||||||
context.animations = @getAnimationNames()
|
context.animations = @getAnimationNames()
|
||||||
context.authorized = not me.get('anonymous')
|
context.authorized = not me.get('anonymous')
|
||||||
context.recentlyPlayedLevels = storage.load('recently-played-levels') ? ['items']
|
context.recentlyPlayedLevels = storage.load('recently-played-levels') ? ['items']
|
||||||
|
context.fileSizeString = @fileSizeString
|
||||||
context
|
context
|
||||||
|
|
||||||
getAnimationNames: ->
|
getAnimationNames: ->
|
||||||
|
@ -197,6 +199,16 @@ module.exports = class ThangTypeEditView extends RootView
|
||||||
@treema.set('raw', @thangType.get('raw'))
|
@treema.set('raw', @thangType.get('raw'))
|
||||||
@updateSelectBox()
|
@updateSelectBox()
|
||||||
@refreshAnimation()
|
@refreshAnimation()
|
||||||
|
@updateFileSize()
|
||||||
|
|
||||||
|
updateFileSize: ->
|
||||||
|
file = JSON.stringify(@thangType.attributes)
|
||||||
|
compressed = LZString.compress(file)
|
||||||
|
size = (file.length / 1024).toFixed(1) + "KB"
|
||||||
|
compressedSize = (compressed.length / 1024).toFixed(1) + "KB"
|
||||||
|
gzipCompressedSize = compressedSize * 1.65 # just based on comparing ogre barracks
|
||||||
|
@fileSizeString = "Size: #{size} (~#{compressedSize} gzipped)"
|
||||||
|
@$el.find('#thang-type-file-size').text @fileSizeString
|
||||||
|
|
||||||
# animation select
|
# animation select
|
||||||
|
|
||||||
|
|
|
@ -18,293 +18,318 @@ module.exports = class MainPlayView extends RootView
|
||||||
constructor: (options) ->
|
constructor: (options) ->
|
||||||
super options
|
super options
|
||||||
@levelStatusMap = {}
|
@levelStatusMap = {}
|
||||||
|
@levelPlayCountMap = {}
|
||||||
@sessions = @supermodel.loadCollection(new LevelSessionsCollection(), 'your_sessions', null, 0).model
|
@sessions = @supermodel.loadCollection(new LevelSessionsCollection(), 'your_sessions', null, 0).model
|
||||||
@listenToOnce @sessions, 'sync', @onSessionsLoaded
|
@listenToOnce @sessions, 'sync', @onSessionsLoaded
|
||||||
|
@getLevelPlayCounts()
|
||||||
|
|
||||||
onSessionsLoaded: (e) ->
|
onSessionsLoaded: (e) ->
|
||||||
for session in @sessions.models
|
for session in @sessions.models
|
||||||
@levelStatusMap[session.get('levelID')] = if session.get('state')?.complete then 'complete' else 'started'
|
@levelStatusMap[session.get('levelID')] = if session.get('state')?.complete then 'complete' else 'started'
|
||||||
@render()
|
@render()
|
||||||
|
|
||||||
|
getLevelPlayCounts: ->
|
||||||
|
success = (levelPlayCounts) =>
|
||||||
|
return if @destroyed
|
||||||
|
for level in levelPlayCounts
|
||||||
|
@levelPlayCountMap[level._id] = playtime: level.playtime, sessions: level.sessions
|
||||||
|
@render() if @supermodel.finished()
|
||||||
|
|
||||||
|
levelIDs = []
|
||||||
|
for campaign in campaigns
|
||||||
|
for level in campaign.levels
|
||||||
|
levelIDs.push level.id
|
||||||
|
levelPlayCountsRequest = @supermodel.addRequestResource 'play_counts', {
|
||||||
|
url: '/db/level/-/play_counts'
|
||||||
|
data: {ids: levelIDs}
|
||||||
|
method: 'POST'
|
||||||
|
success: success
|
||||||
|
}, 0
|
||||||
|
levelPlayCountsRequest.load()
|
||||||
|
|
||||||
getRenderData: (context={}) ->
|
getRenderData: (context={}) ->
|
||||||
context = super(context)
|
context = super(context)
|
||||||
tutorials = [
|
context.campaigns = campaigns
|
||||||
{
|
|
||||||
name: 'Rescue Mission'
|
|
||||||
difficulty: 1
|
|
||||||
id: 'rescue-mission'
|
|
||||||
image: '/file/db/level/52740644904ac0411700067c/rescue_mission_icon.png'
|
|
||||||
description: 'Tharin has been captured!'
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: 'Grab the Mushroom'
|
|
||||||
difficulty: 1
|
|
||||||
id: 'grab-the-mushroom'
|
|
||||||
image: '/file/db/level/529662dfe0df8f0000000007/grab_the_mushroom_icon.png'
|
|
||||||
description: 'Grab a powerup and smash a big ogre.'
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: 'Drink Me'
|
|
||||||
difficulty: 1
|
|
||||||
id: 'drink-me'
|
|
||||||
image: '/file/db/level/525dc5589a0765e496000006/drink_me_icon.png'
|
|
||||||
description: 'Drink up and slay two munchkins.'
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: 'Taunt the Guards'
|
|
||||||
difficulty: 1
|
|
||||||
id: 'taunt-the-guards'
|
|
||||||
image: '/file/db/level/5276c9bdcf83207a2801ff8f/taunt_icon.png'
|
|
||||||
description: 'Tharin, if clever, can escape with Phoebe.'
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: 'It\'s a Trap'
|
|
||||||
difficulty: 1
|
|
||||||
id: 'its-a-trap'
|
|
||||||
image: '/file/db/level/528aea2d7f37fc4e0700016b/its_a_trap_icon.png'
|
|
||||||
description: 'Organize a dungeon ambush with archers.'
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: 'Break the Prison'
|
|
||||||
difficulty: 1
|
|
||||||
id: 'break-the-prison'
|
|
||||||
image: '/file/db/level/5275272c69abdcb12401216e/break_the_prison_icon.png'
|
|
||||||
description: 'More comrades are imprisoned!'
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: 'Taunt'
|
|
||||||
difficulty: 1
|
|
||||||
id: 'taunt'
|
|
||||||
image: '/file/db/level/525f150306e1ab0962000018/taunt_icon.png'
|
|
||||||
description: 'Taunt the ogre to claim victory.'
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: 'Cowardly Taunt'
|
|
||||||
difficulty: 1
|
|
||||||
id: 'cowardly-taunt'
|
|
||||||
image: '/file/db/level/525abfd9b12777d78e000009/cowardly_taunt_icon.png'
|
|
||||||
description: 'Lure infuriated ogres to their doom.'
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: 'Commanding Followers'
|
|
||||||
difficulty: 1
|
|
||||||
id: 'commanding-followers'
|
|
||||||
image: '/file/db/level/525ef8ef06e1ab0962000003/commanding_followers_icon.png'
|
|
||||||
description: 'Lead allied soldiers into battle.'
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: 'Mobile Artillery'
|
|
||||||
difficulty: 1
|
|
||||||
id: 'mobile-artillery'
|
|
||||||
image: '/file/db/level/525085419851b83f4b000001/mobile_artillery_icon.png'
|
|
||||||
description: 'Blow ogres up!'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
experienced = [
|
|
||||||
{
|
|
||||||
name: 'Hunter Triplets'
|
|
||||||
difficulty: 2
|
|
||||||
id: 'hunter-triplets'
|
|
||||||
image: '/file/db/level/526711d9add4f8965f000002/hunter_triplets_icon.png'
|
|
||||||
description: 'Three soldiers go ogre hunting.'
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: 'Emphasis on Aim'
|
|
||||||
difficulty: 2
|
|
||||||
id: 'emphasis-on-aim'
|
|
||||||
image: '/file/db/level/525f384d96cd77000000000f/munchkin_masher_icon.png'
|
|
||||||
description: 'Choose your targets carefully.'
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: 'Zone of Danger'
|
|
||||||
difficulty: 3
|
|
||||||
id: 'zone-of-danger'
|
|
||||||
image: '/file/db/level/526ae95c1e5cd30000000008/zone_of_danger_icon.png'
|
|
||||||
description: 'Target the ogres swarming into arrow range.'
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: 'Molotov Medic'
|
|
||||||
difficulty: 2
|
|
||||||
id: 'molotov-medic'
|
|
||||||
image: '/file/db/level/52602ecb026e8481e7000001/generic_1.png'
|
|
||||||
description: 'Tharin must play support in this dungeon battle.'
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: 'Gridmancer'
|
|
||||||
difficulty: 5
|
|
||||||
id: 'gridmancer'
|
|
||||||
image: '/file/db/level/52ae2460ef42c52f13000008/gridmancer_icon.png'
|
|
||||||
description: 'Super algorithm challenge level!'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
arenas = [
|
|
||||||
{
|
|
||||||
name: 'Criss-Cross'
|
|
||||||
difficulty: 5
|
|
||||||
id: 'criss-cross'
|
|
||||||
image: '/file/db/level/528aea2d7f37fc4e0700016b/its_a_trap_icon.png'
|
|
||||||
description: 'Participate in a bidding war with opponents to reach the other side!'
|
|
||||||
levelPath: 'ladder'
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: 'Greed'
|
|
||||||
difficulty: 4
|
|
||||||
id: 'greed'
|
|
||||||
image: '/file/db/level/526fd3043c637ece50001bb2/the_herd_icon.png'
|
|
||||||
description: 'Liked Dungeon Arena and Gold Rush? Put them together in this economic arena!'
|
|
||||||
levelPath: 'ladder'
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: 'Dungeon Arena'
|
|
||||||
difficulty: 3
|
|
||||||
id: 'dungeon-arena'
|
|
||||||
image: '/file/db/level/526ae95c1e5cd30000000008/zone_of_danger_icon.png'
|
|
||||||
description: 'Play head-to-head against fellow Wizards in a dungeon melee!'
|
|
||||||
levelPath: 'ladder'
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: 'Gold Rush'
|
|
||||||
difficulty: 3
|
|
||||||
id: 'gold-rush'
|
|
||||||
image: '/file/db/level/52602ecb026e8481e7000001/generic_1.png'
|
|
||||||
description: 'Prove you are better at collecting gold than your opponent!'
|
|
||||||
levelPath: 'ladder'
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: 'Brawlwood'
|
|
||||||
difficulty: 4
|
|
||||||
id: 'brawlwood'
|
|
||||||
image: '/file/db/level/525ef8ef06e1ab0962000003/commanding_followers_icon.png'
|
|
||||||
description: 'Combat the armies of other Wizards in a strategic forest arena! (Fast computer required.)'
|
|
||||||
levelPath: 'ladder'
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: 'Sky Span (Testing)'
|
|
||||||
difficulty: 3
|
|
||||||
id: 'sky-span'
|
|
||||||
image: '/file/db/level/526ae95c1e5cd30000000008/zone_of_danger_icon.png'
|
|
||||||
description: 'Preview version of an upgraded Dungeon Arena. Help us with hero balance before release!'
|
|
||||||
levelPath: 'ladder'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
classicAlgorithms = [
|
|
||||||
{
|
|
||||||
name: 'Bubble Sort Bootcamp Battle'
|
|
||||||
difficulty: 3
|
|
||||||
id: 'bubble-sort-bootcamp-battle'
|
|
||||||
image: '/file/db/level/525ef8ef06e1ab0962000003/commanding_followers_icon.png'
|
|
||||||
description: 'Write a bubble sort to organize your soldiers. - by Alexandru Caciulescu'
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: 'Ogres of Hanoi'
|
|
||||||
difficulty: 3
|
|
||||||
id: 'ogres-of-hanoi'
|
|
||||||
image: '/file/db/level/526fd3043c637ece50001bb2/the_herd_icon.png'
|
|
||||||
description: 'Transfer a stack of ogres while preserving their honor. - by Alexandru Caciulescu'
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: 'Danger! Minefield'
|
|
||||||
difficulty: 3
|
|
||||||
id: 'danger-minefield'
|
|
||||||
image: '/file/db/level/526bda3fe79aefde2a003e36/mobile_artillery_icon.png'
|
|
||||||
description: 'Learn how to find prime numbers while defusing mines! - by Alexandru Caciulescu'
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: 'K-means++ Cluster Wars'
|
|
||||||
difficulty: 4
|
|
||||||
id: 'k-means-cluster-wars'
|
|
||||||
image: '/file/db/level/525ef8ef06e1ab0962000003/commanding_followers_icon.png'
|
|
||||||
description: 'Learn cluster analysis while leading armies into battle! - by Alexandru Caciulescu'
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: 'Quicksort the Spiral'
|
|
||||||
difficulty: 3
|
|
||||||
id: 'quicksort-the-spiral'
|
|
||||||
image: '/file/db/level/525ef8ef06e1ab0962000003/commanding_followers_icon.png'
|
|
||||||
description: 'Learn Quicksort while sorting a spiral of ogres! - by Alexandru Caciulescu'
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: 'Minimax Tic-Tac-Toe'
|
|
||||||
difficulty: 4
|
|
||||||
id: 'minimax-tic-tac-toe'
|
|
||||||
image: '/file/db/level/525ef8ef06e1ab0962000003/commanding_followers_icon.png'
|
|
||||||
description: 'Learn how to make a game AI with the Minimax algorithm. - by Alexandru Caciulescu'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
playerCreated = [
|
|
||||||
{
|
|
||||||
name: 'Extra Extrapolation'
|
|
||||||
difficulty: 2
|
|
||||||
id: 'extra-extrapolation'
|
|
||||||
image: '/file/db/level/526bda3fe79aefde2a003e36/mobile_artillery_icon.png'
|
|
||||||
description: 'Predict your target\'s position for deadly aim. - by Sootn'
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: 'The Right Route'
|
|
||||||
difficulty: 1
|
|
||||||
id: 'the-right-route'
|
|
||||||
image: '/file/db/level/526fd3043c637ece50001bb2/the_herd_icon.png'
|
|
||||||
description: 'Strike at the weak point in an array of enemies. - by Aftermath'
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: 'Sword Loop'
|
|
||||||
difficulty: 2
|
|
||||||
id: 'sword-loop'
|
|
||||||
image: '/file/db/level/525dc5589a0765e496000006/drink_me_icon.png'
|
|
||||||
description: 'Kill the ogres and save the peasants with for-loops. - by Prabh Simran Singh Baweja'
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: 'Coin Mania'
|
|
||||||
difficulty: 2
|
|
||||||
id: 'coin-mania'
|
|
||||||
image: '/file/db/level/529662dfe0df8f0000000007/grab_the_mushroom_icon.png'
|
|
||||||
description: 'Learn while-loops to grab coins and potions. - by Prabh Simran Singh Baweja'
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: 'Find the Spy'
|
|
||||||
difficulty: 2
|
|
||||||
id: 'find-the-spy'
|
|
||||||
image: '/file/db/level/526ae95c1e5cd30000000008/zone_of_danger_icon.png'
|
|
||||||
description: 'Identify the spies hidden among your soldiers - by Nathan Gossett'
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: 'Harvest Time'
|
|
||||||
difficulty: 2
|
|
||||||
id: 'harvest-time'
|
|
||||||
image: '/file/db/level/529662dfe0df8f0000000007/grab_the_mushroom_icon.png'
|
|
||||||
description: 'Collect a hundred mushrooms in just five lines of code - by Nathan Gossett'
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: 'Guide Everyone Home'
|
|
||||||
difficulty: 2
|
|
||||||
id: 'guide-everyone-home'
|
|
||||||
image: '/file/db/level/52740644904ac0411700067c/rescue_mission_icon.png'
|
|
||||||
description: 'Fetch the wizards teleporting into the area - by Nathan Gossett'
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: "Let's go Fly a Kite"
|
|
||||||
difficulty: 3
|
|
||||||
id: 'lets-go-fly-a-kite'
|
|
||||||
image: '/file/db/level/526711d9add4f8965f000002/hunter_triplets_icon.png'
|
|
||||||
description: 'There is a horde of ogres marching on your village. Stay out of reach and use your bow to take them out! - by Danny Whittaker'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
context.campaigns = [
|
|
||||||
{id: 'beginner', name: 'Beginner Campaign', description: '... in which you learn the wizardry of programming.', levels: tutorials}
|
|
||||||
{id: 'multiplayer', name: 'Multiplayer Arenas', description: '... in which you code head-to-head against other players.', levels: arenas}
|
|
||||||
{id: 'dev', name: 'Random Harder Levels', description: '... in which you learn the interface while doing something a little harder.', levels: experienced}
|
|
||||||
{id: 'classic' ,name: 'Classic Algorithms', description: '... in which you learn the most popular algorithms in Computer Science.', levels: classicAlgorithms}
|
|
||||||
{id: 'player_created', name: 'Player-Created', description: '... in which you battle against the creativity of your fellow <a href=\"/contribute#artisan\">Artisan Wizards</a>.', levels: playerCreated}
|
|
||||||
]
|
|
||||||
context.levelStatusMap = @levelStatusMap
|
context.levelStatusMap = @levelStatusMap
|
||||||
|
context.levelPlayCountMap = @levelPlayCountMap
|
||||||
context
|
context
|
||||||
|
|
||||||
afterRender: ->
|
afterRender: ->
|
||||||
super()
|
super()
|
||||||
@$el.find('.modal').on 'shown.bs.modal', ->
|
@$el.find('.modal').on 'shown.bs.modal', ->
|
||||||
$('input:visible:first', @).focus()
|
$('input:visible:first', @).focus()
|
||||||
|
|
||||||
|
|
||||||
|
tutorials = [
|
||||||
|
{
|
||||||
|
name: 'Rescue Mission'
|
||||||
|
difficulty: 1
|
||||||
|
id: 'rescue-mission'
|
||||||
|
image: '/file/db/level/52740644904ac0411700067c/rescue_mission_icon.png'
|
||||||
|
description: 'Tharin has been captured!'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'Grab the Mushroom'
|
||||||
|
difficulty: 1
|
||||||
|
id: 'grab-the-mushroom'
|
||||||
|
image: '/file/db/level/529662dfe0df8f0000000007/grab_the_mushroom_icon.png'
|
||||||
|
description: 'Grab a powerup and smash a big ogre.'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'Drink Me'
|
||||||
|
difficulty: 1
|
||||||
|
id: 'drink-me'
|
||||||
|
image: '/file/db/level/525dc5589a0765e496000006/drink_me_icon.png'
|
||||||
|
description: 'Drink up and slay two munchkins.'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'Taunt the Guards'
|
||||||
|
difficulty: 1
|
||||||
|
id: 'taunt-the-guards'
|
||||||
|
image: '/file/db/level/5276c9bdcf83207a2801ff8f/taunt_icon.png'
|
||||||
|
description: 'Tharin, if clever, can escape with Phoebe.'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'It\'s a Trap'
|
||||||
|
difficulty: 1
|
||||||
|
id: 'its-a-trap'
|
||||||
|
image: '/file/db/level/528aea2d7f37fc4e0700016b/its_a_trap_icon.png'
|
||||||
|
description: 'Organize a dungeon ambush with archers.'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'Break the Prison'
|
||||||
|
difficulty: 1
|
||||||
|
id: 'break-the-prison'
|
||||||
|
image: '/file/db/level/5275272c69abdcb12401216e/break_the_prison_icon.png'
|
||||||
|
description: 'More comrades are imprisoned!'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'Taunt'
|
||||||
|
difficulty: 1
|
||||||
|
id: 'taunt'
|
||||||
|
image: '/file/db/level/525f150306e1ab0962000018/taunt_icon.png'
|
||||||
|
description: 'Taunt the ogre to claim victory.'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'Cowardly Taunt'
|
||||||
|
difficulty: 1
|
||||||
|
id: 'cowardly-taunt'
|
||||||
|
image: '/file/db/level/525abfd9b12777d78e000009/cowardly_taunt_icon.png'
|
||||||
|
description: 'Lure infuriated ogres to their doom.'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'Commanding Followers'
|
||||||
|
difficulty: 1
|
||||||
|
id: 'commanding-followers'
|
||||||
|
image: '/file/db/level/525ef8ef06e1ab0962000003/commanding_followers_icon.png'
|
||||||
|
description: 'Lead allied soldiers into battle.'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'Mobile Artillery'
|
||||||
|
difficulty: 1
|
||||||
|
id: 'mobile-artillery'
|
||||||
|
image: '/file/db/level/525085419851b83f4b000001/mobile_artillery_icon.png'
|
||||||
|
description: 'Blow ogres up!'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
experienced = [
|
||||||
|
{
|
||||||
|
name: 'Hunter Triplets'
|
||||||
|
difficulty: 2
|
||||||
|
id: 'hunter-triplets'
|
||||||
|
image: '/file/db/level/526711d9add4f8965f000002/hunter_triplets_icon.png'
|
||||||
|
description: 'Three soldiers go ogre hunting.'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'Emphasis on Aim'
|
||||||
|
difficulty: 2
|
||||||
|
id: 'emphasis-on-aim'
|
||||||
|
image: '/file/db/level/525f384d96cd77000000000f/munchkin_masher_icon.png'
|
||||||
|
description: 'Choose your targets carefully.'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'Zone of Danger'
|
||||||
|
difficulty: 3
|
||||||
|
id: 'zone-of-danger'
|
||||||
|
image: '/file/db/level/526ae95c1e5cd30000000008/zone_of_danger_icon.png'
|
||||||
|
description: 'Target the ogres swarming into arrow range.'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'Molotov Medic'
|
||||||
|
difficulty: 2
|
||||||
|
id: 'molotov-medic'
|
||||||
|
image: '/file/db/level/52602ecb026e8481e7000001/generic_1.png'
|
||||||
|
description: 'Tharin must play support in this dungeon battle.'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'Gridmancer'
|
||||||
|
difficulty: 5
|
||||||
|
id: 'gridmancer'
|
||||||
|
image: '/file/db/level/52ae2460ef42c52f13000008/gridmancer_icon.png'
|
||||||
|
description: 'Super algorithm challenge level!'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
arenas = [
|
||||||
|
{
|
||||||
|
name: 'Criss-Cross'
|
||||||
|
difficulty: 5
|
||||||
|
id: 'criss-cross'
|
||||||
|
image: '/file/db/level/528aea2d7f37fc4e0700016b/its_a_trap_icon.png'
|
||||||
|
description: 'Participate in a bidding war with opponents to reach the other side!'
|
||||||
|
levelPath: 'ladder'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'Greed'
|
||||||
|
difficulty: 4
|
||||||
|
id: 'greed'
|
||||||
|
image: '/file/db/level/526fd3043c637ece50001bb2/the_herd_icon.png'
|
||||||
|
description: 'Liked Dungeon Arena and Gold Rush? Put them together in this economic arena!'
|
||||||
|
levelPath: 'ladder'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'Dungeon Arena'
|
||||||
|
difficulty: 3
|
||||||
|
id: 'dungeon-arena'
|
||||||
|
image: '/file/db/level/526ae95c1e5cd30000000008/zone_of_danger_icon.png'
|
||||||
|
description: 'Play head-to-head against fellow Wizards in a dungeon melee!'
|
||||||
|
levelPath: 'ladder'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'Gold Rush'
|
||||||
|
difficulty: 3
|
||||||
|
id: 'gold-rush'
|
||||||
|
image: '/file/db/level/52602ecb026e8481e7000001/generic_1.png'
|
||||||
|
description: 'Prove you are better at collecting gold than your opponent!'
|
||||||
|
levelPath: 'ladder'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'Brawlwood'
|
||||||
|
difficulty: 4
|
||||||
|
id: 'brawlwood'
|
||||||
|
image: '/file/db/level/525ef8ef06e1ab0962000003/commanding_followers_icon.png'
|
||||||
|
description: 'Combat the armies of other Wizards in a strategic forest arena! (Fast computer required.)'
|
||||||
|
levelPath: 'ladder'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'Sky Span (Testing)'
|
||||||
|
difficulty: 3
|
||||||
|
id: 'sky-span'
|
||||||
|
image: '/file/db/level/526ae95c1e5cd30000000008/zone_of_danger_icon.png'
|
||||||
|
description: 'Preview version of an upgraded Dungeon Arena. Help us with hero balance before release!'
|
||||||
|
levelPath: 'ladder'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
classicAlgorithms = [
|
||||||
|
{
|
||||||
|
name: 'Bubble Sort Bootcamp Battle'
|
||||||
|
difficulty: 3
|
||||||
|
id: 'bubble-sort-bootcamp-battle'
|
||||||
|
image: '/file/db/level/525ef8ef06e1ab0962000003/commanding_followers_icon.png'
|
||||||
|
description: 'Write a bubble sort to organize your soldiers. - by Alexandru Caciulescu'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'Ogres of Hanoi'
|
||||||
|
difficulty: 3
|
||||||
|
id: 'ogres-of-hanoi'
|
||||||
|
image: '/file/db/level/526fd3043c637ece50001bb2/the_herd_icon.png'
|
||||||
|
description: 'Transfer a stack of ogres while preserving their honor. - by Alexandru Caciulescu'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'Danger! Minefield'
|
||||||
|
difficulty: 3
|
||||||
|
id: 'danger-minefield'
|
||||||
|
image: '/file/db/level/526bda3fe79aefde2a003e36/mobile_artillery_icon.png'
|
||||||
|
description: 'Learn how to find prime numbers while defusing mines! - by Alexandru Caciulescu'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'K-means++ Cluster Wars'
|
||||||
|
difficulty: 4
|
||||||
|
id: 'k-means-cluster-wars'
|
||||||
|
image: '/file/db/level/525ef8ef06e1ab0962000003/commanding_followers_icon.png'
|
||||||
|
description: 'Learn cluster analysis while leading armies into battle! - by Alexandru Caciulescu'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'Quicksort the Spiral'
|
||||||
|
difficulty: 3
|
||||||
|
id: 'quicksort-the-spiral'
|
||||||
|
image: '/file/db/level/525ef8ef06e1ab0962000003/commanding_followers_icon.png'
|
||||||
|
description: 'Learn Quicksort while sorting a spiral of ogres! - by Alexandru Caciulescu'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'Minimax Tic-Tac-Toe'
|
||||||
|
difficulty: 4
|
||||||
|
id: 'minimax-tic-tac-toe'
|
||||||
|
image: '/file/db/level/525ef8ef06e1ab0962000003/commanding_followers_icon.png'
|
||||||
|
description: 'Learn how to make a game AI with the Minimax algorithm. - by Alexandru Caciulescu'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
playerCreated = [
|
||||||
|
{
|
||||||
|
name: 'Extra Extrapolation'
|
||||||
|
difficulty: 2
|
||||||
|
id: 'extra-extrapolation'
|
||||||
|
image: '/file/db/level/526bda3fe79aefde2a003e36/mobile_artillery_icon.png'
|
||||||
|
description: 'Predict your target\'s position for deadly aim. - by Sootn'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'The Right Route'
|
||||||
|
difficulty: 1
|
||||||
|
id: 'the-right-route'
|
||||||
|
image: '/file/db/level/526fd3043c637ece50001bb2/the_herd_icon.png'
|
||||||
|
description: 'Strike at the weak point in an array of enemies. - by Aftermath'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'Sword Loop'
|
||||||
|
difficulty: 2
|
||||||
|
id: 'sword-loop'
|
||||||
|
image: '/file/db/level/525dc5589a0765e496000006/drink_me_icon.png'
|
||||||
|
description: 'Kill the ogres and save the peasants with for-loops. - by Prabh Simran Singh Baweja'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'Coin Mania'
|
||||||
|
difficulty: 2
|
||||||
|
id: 'coin-mania'
|
||||||
|
image: '/file/db/level/529662dfe0df8f0000000007/grab_the_mushroom_icon.png'
|
||||||
|
description: 'Learn while-loops to grab coins and potions. - by Prabh Simran Singh Baweja'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'Find the Spy'
|
||||||
|
difficulty: 2
|
||||||
|
id: 'find-the-spy'
|
||||||
|
image: '/file/db/level/526ae95c1e5cd30000000008/zone_of_danger_icon.png'
|
||||||
|
description: 'Identify the spies hidden among your soldiers - by Nathan Gossett'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'Harvest Time'
|
||||||
|
difficulty: 2
|
||||||
|
id: 'harvest-time'
|
||||||
|
image: '/file/db/level/529662dfe0df8f0000000007/grab_the_mushroom_icon.png'
|
||||||
|
description: 'Collect a hundred mushrooms in just five lines of code - by Nathan Gossett'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'Guide Everyone Home'
|
||||||
|
difficulty: 2
|
||||||
|
id: 'guide-everyone-home'
|
||||||
|
image: '/file/db/level/52740644904ac0411700067c/rescue_mission_icon.png'
|
||||||
|
description: 'Fetch the wizards teleporting into the area - by Nathan Gossett'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: "Let's go Fly a Kite"
|
||||||
|
difficulty: 3
|
||||||
|
id: 'lets-go-fly-a-kite'
|
||||||
|
image: '/file/db/level/526711d9add4f8965f000002/hunter_triplets_icon.png'
|
||||||
|
description: 'There is a horde of ogres marching on your village. Stay out of reach and use your bow to take them out! - by Danny Whittaker'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
campaigns = [
|
||||||
|
{id: 'beginner', name: 'Beginner Campaign', description: '... in which you learn the wizardry of programming.', levels: tutorials}
|
||||||
|
{id: 'multiplayer', name: 'Multiplayer Arenas', description: '... in which you code head-to-head against other players.', levels: arenas}
|
||||||
|
{id: 'dev', name: 'Random Harder Levels', description: '... in which you learn the interface while doing something a little harder.', levels: experienced}
|
||||||
|
{id: 'classic' ,name: 'Classic Algorithms', description: '... in which you learn the most popular algorithms in Computer Science.', levels: classicAlgorithms}
|
||||||
|
{id: 'player_created', name: 'Player-Created', description: '... in which you battle against the creativity of your fellow <a href=\"/contribute#artisan\">Artisan Wizards</a>.', levels: playerCreated}
|
||||||
|
]
|
||||||
|
|
|
@ -18,68 +18,93 @@ module.exports = class LadderHomeView extends RootView
|
||||||
constructor: (options) ->
|
constructor: (options) ->
|
||||||
super options
|
super options
|
||||||
@levelStatusMap = {}
|
@levelStatusMap = {}
|
||||||
|
@levelPlayCountMap = {}
|
||||||
@sessions = @supermodel.loadCollection(new LevelSessionsCollection(), 'your_sessions', null, 0).model
|
@sessions = @supermodel.loadCollection(new LevelSessionsCollection(), 'your_sessions', null, 0).model
|
||||||
@listenToOnce @sessions, 'sync', @onSessionsLoaded
|
@listenToOnce @sessions, 'sync', @onSessionsLoaded
|
||||||
|
@getLevelPlayCounts()
|
||||||
|
|
||||||
onSessionsLoaded: (e) ->
|
onSessionsLoaded: (e) ->
|
||||||
for session in @sessions.models
|
for session in @sessions.models
|
||||||
@levelStatusMap[session.get('levelID')] = if session.get('state')?.complete then 'complete' else 'started'
|
@levelStatusMap[session.get('levelID')] = if session.get('state')?.complete then 'complete' else 'started'
|
||||||
@render()
|
@render()
|
||||||
|
|
||||||
|
getLevelPlayCounts: ->
|
||||||
|
success = (levelPlayCounts) =>
|
||||||
|
return if @destroyed
|
||||||
|
for level in levelPlayCounts
|
||||||
|
@levelPlayCountMap[level._id] = playtime: level.playtime, sessions: level.sessions
|
||||||
|
@render() if @supermodel.finished()
|
||||||
|
|
||||||
|
levelIDs = []
|
||||||
|
for campaign in campaigns
|
||||||
|
for level in campaign.levels
|
||||||
|
levelIDs.push level.id
|
||||||
|
levelPlayCountsRequest = @supermodel.addRequestResource 'play_counts', {
|
||||||
|
url: '/db/level/-/play_counts'
|
||||||
|
data: {ids: levelIDs}
|
||||||
|
method: 'POST'
|
||||||
|
success: success
|
||||||
|
}, 0
|
||||||
|
levelPlayCountsRequest.load()
|
||||||
|
|
||||||
getRenderData: (context={}) ->
|
getRenderData: (context={}) ->
|
||||||
context = super(context)
|
context = super(context)
|
||||||
arenas = [
|
|
||||||
{
|
|
||||||
name: 'Criss-Cross'
|
|
||||||
difficulty: 5
|
|
||||||
id: 'criss-cross'
|
|
||||||
image: '/file/db/level/5391f3d519dc22b8082159b2/banner2.png'
|
|
||||||
description: 'Participate in a bidding war with opponents to reach the other side!'
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: 'Greed'
|
|
||||||
difficulty: 4
|
|
||||||
id: 'greed'
|
|
||||||
image: '/file/db/level/53558b5a9914f5a90d7ccddb/greed_banner.jpg'
|
|
||||||
description: 'Liked Dungeon Arena and Gold Rush? Put them together in this economic arena!'
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: 'Sky Span (Testing)'
|
|
||||||
difficulty: 3
|
|
||||||
id: 'sky-span'
|
|
||||||
image: '/file/db/level/53c80fce0ddbef000084c667/sky-Span-banner.jpg'
|
|
||||||
description: 'Preview version of an upgraded Dungeon Arena. Help us with hero balance before release!'
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: 'Dungeon Arena'
|
|
||||||
difficulty: 3
|
|
||||||
id: 'dungeon-arena'
|
|
||||||
image: '/file/db/level/53173f76c269d400000543c2/Level%20Banner%20Dungeon%20Arena.jpg'
|
|
||||||
description: 'Play head-to-head against fellow Wizards in a dungeon melee!'
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: 'Gold Rush'
|
|
||||||
difficulty: 3
|
|
||||||
id: 'gold-rush'
|
|
||||||
image: '/file/db/level/533353722a61b7ca6832840c/Gold-Rush.png'
|
|
||||||
description: 'Prove you are better at collecting gold than your opponent!'
|
|
||||||
}
|
|
||||||
{
|
|
||||||
name: 'Brawlwood'
|
|
||||||
difficulty: 4
|
|
||||||
id: 'brawlwood'
|
|
||||||
image: '/file/db/level/52d97ecd32362bc86e004e87/Level%20Banner%20Brawlwood.jpg'
|
|
||||||
description: 'Combat the armies of other Wizards in a strategic forest arena! (Fast computer required.)'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
context.campaigns = [
|
|
||||||
{id: 'multiplayer', name: 'Multiplayer Arenas', description: '... in which you code head-to-head against other players.', levels: arenas}
|
|
||||||
]
|
|
||||||
context.levelStatusMap = @levelStatusMap
|
context.levelStatusMap = @levelStatusMap
|
||||||
|
context.levelPlayCountMap = @levelPlayCountMap
|
||||||
|
context.campaigns = campaigns
|
||||||
context
|
context
|
||||||
|
|
||||||
afterRender: ->
|
afterRender: ->
|
||||||
super()
|
super()
|
||||||
@$el.find('.modal').on 'shown.bs.modal', ->
|
@$el.find('.modal').on 'shown.bs.modal', ->
|
||||||
$('input:visible:first', @).focus()
|
$('input:visible:first', @).focus()
|
||||||
|
|
||||||
|
|
||||||
|
arenas = [
|
||||||
|
{
|
||||||
|
name: 'Criss-Cross'
|
||||||
|
difficulty: 5
|
||||||
|
id: 'criss-cross'
|
||||||
|
image: '/file/db/level/5391f3d519dc22b8082159b2/banner2.png'
|
||||||
|
description: 'Participate in a bidding war with opponents to reach the other side!'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'Greed'
|
||||||
|
difficulty: 4
|
||||||
|
id: 'greed'
|
||||||
|
image: '/file/db/level/53558b5a9914f5a90d7ccddb/greed_banner.jpg'
|
||||||
|
description: 'Liked Dungeon Arena and Gold Rush? Put them together in this economic arena!'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'Sky Span (Testing)'
|
||||||
|
difficulty: 3
|
||||||
|
id: 'sky-span'
|
||||||
|
image: '/file/db/level/53c80fce0ddbef000084c667/sky-Span-banner.jpg'
|
||||||
|
description: 'Preview version of an upgraded Dungeon Arena. Help us with hero balance before release!'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'Dungeon Arena'
|
||||||
|
difficulty: 3
|
||||||
|
id: 'dungeon-arena'
|
||||||
|
image: '/file/db/level/53173f76c269d400000543c2/Level%20Banner%20Dungeon%20Arena.jpg'
|
||||||
|
description: 'Play head-to-head against fellow Wizards in a dungeon melee!'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'Gold Rush'
|
||||||
|
difficulty: 3
|
||||||
|
id: 'gold-rush'
|
||||||
|
image: '/file/db/level/533353722a61b7ca6832840c/Gold-Rush.png'
|
||||||
|
description: 'Prove you are better at collecting gold than your opponent!'
|
||||||
|
}
|
||||||
|
{
|
||||||
|
name: 'Brawlwood'
|
||||||
|
difficulty: 4
|
||||||
|
id: 'brawlwood'
|
||||||
|
image: '/file/db/level/52d97ecd32362bc86e004e87/Level%20Banner%20Brawlwood.jpg'
|
||||||
|
description: 'Combat the armies of other Wizards in a strategic forest arena! (Fast computer required.)'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
campaigns = [
|
||||||
|
{id: 'multiplayer', name: 'Multiplayer Arenas', description: '... in which you code head-to-head against other players.', levels: arenas}
|
||||||
|
]
|
||||||
|
|
|
@ -31,7 +31,7 @@ module.exports = class LevelHUDView extends CocoView
|
||||||
@$el.addClass 'no-selection'
|
@$el.addClass 'no-selection'
|
||||||
|
|
||||||
onClick: (e) ->
|
onClick: (e) ->
|
||||||
Backbone.Mediator.publish 'tome:focus-editor' unless $(e.target).parents('.thang-props').length
|
Backbone.Mediator.publish 'tome:focus-editor', {} unless $(e.target).parents('.thang-props').length
|
||||||
|
|
||||||
onFrameChanged: (e) ->
|
onFrameChanged: (e) ->
|
||||||
@timeProgress = e.progress
|
@timeProgress = e.progress
|
||||||
|
@ -207,10 +207,10 @@ module.exports = class LevelHUDView extends CocoView
|
||||||
if @lastResponses
|
if @lastResponses
|
||||||
buttons = $('.enter button')
|
buttons = $('.enter button')
|
||||||
for response, i in @lastResponses
|
for response, i in @lastResponses
|
||||||
f = (r) => => setTimeout((-> Backbone.Mediator.publish(r.channel, r.event)), 10)
|
f = (r) => => setTimeout((-> Backbone.Mediator.publish(r.channel, r.event or {})), 10)
|
||||||
$(buttons[i]).click(f(response))
|
$(buttons[i]).click(f(response))
|
||||||
else
|
else
|
||||||
$('.enter', @bubble).click(-> Backbone.Mediator.publish('script:end-current-script'))
|
$('.enter', @bubble).click(-> Backbone.Mediator.publish('script:end-current-script', {}))
|
||||||
return
|
return
|
||||||
@animator.tick()
|
@animator.tick()
|
||||||
|
|
||||||
|
@ -221,7 +221,7 @@ module.exports = class LevelHUDView extends CocoView
|
||||||
# If we decide that always having the last one fire is bad, we should make it smarter.
|
# If we decide that always having the last one fire is bad, we should make it smarter.
|
||||||
return unless @lastResponses?.length
|
return unless @lastResponses?.length
|
||||||
r = @lastResponses[@lastResponses.length - 1]
|
r = @lastResponses[@lastResponses.length - 1]
|
||||||
_.delay (-> Backbone.Mediator.publish(r.channel, r.event)), 10
|
_.delay (-> Backbone.Mediator.publish(r.channel, r.event or {})), 10
|
||||||
|
|
||||||
onEscapePressed: (e) ->
|
onEscapePressed: (e) ->
|
||||||
@escapePressed = true
|
@escapePressed = true
|
||||||
|
|
|
@ -68,6 +68,7 @@ module.exports = class PlayLevelView extends RootView
|
||||||
|
|
||||||
events:
|
events:
|
||||||
'click #level-done-button': 'onDonePressed'
|
'click #level-done-button': 'onDonePressed'
|
||||||
|
'click #fullscreen-editor-background-screen': (e) -> Backbone.Mediator.publish 'tome:toggle-maximize', {}
|
||||||
|
|
||||||
shortcuts:
|
shortcuts:
|
||||||
'ctrl+s': 'onCtrlS'
|
'ctrl+s': 'onCtrlS'
|
||||||
|
|
|
@ -14,4 +14,5 @@ module.exports = class KeyboardShortcutsModal extends ModalView
|
||||||
c.enter = $.i18n.t 'keyboard_shortcuts.enter'
|
c.enter = $.i18n.t 'keyboard_shortcuts.enter'
|
||||||
c.space = $.i18n.t 'keyboard_shortcuts.space'
|
c.space = $.i18n.t 'keyboard_shortcuts.space'
|
||||||
c.escapeKey = $.i18n.t 'keyboard_shortcuts.escape'
|
c.escapeKey = $.i18n.t 'keyboard_shortcuts.escape'
|
||||||
|
c.shift = $.i18n.t 'keyboard_shortcuts.shift'
|
||||||
c
|
c
|
||||||
|
|
|
@ -171,7 +171,7 @@ module.exports = class SpellDebugView extends CocoView
|
||||||
@frameRate = e.world.frameRate
|
@frameRate = e.world.frameRate
|
||||||
|
|
||||||
onFrameChanged: (data) ->
|
onFrameChanged: (data) ->
|
||||||
@currentFrame = data.frame
|
@currentFrame = Math.round(data.frame)
|
||||||
@frameRate = data.world.frameRate
|
@frameRate = data.world.frameRate
|
||||||
|
|
||||||
onSpellChangedCalculation: (data) ->
|
onSpellChangedCalculation: (data) ->
|
||||||
|
|
|
@ -16,19 +16,23 @@ module.exports = class SpellListTabEntryView extends SpellListEntryView
|
||||||
'tome:spell-changed': 'onSpellChanged'
|
'tome:spell-changed': 'onSpellChanged'
|
||||||
'god:new-world-created': 'onNewWorld'
|
'god:new-world-created': 'onNewWorld'
|
||||||
'tome:spell-changed-language': 'onSpellChangedLanguage'
|
'tome:spell-changed-language': 'onSpellChangedLanguage'
|
||||||
'tome:fullscreen-view': 'onFullscreenClick'
|
'tome:toggle-maximize': 'onToggleMaximize'
|
||||||
|
|
||||||
events:
|
events:
|
||||||
'click .spell-list-button': 'onDropdownClick'
|
'click .spell-list-button': 'onDropdownClick'
|
||||||
'click .reload-code': 'onCodeReload'
|
'click .reload-code': 'onCodeReload'
|
||||||
'click .beautify-code': 'onBeautifyClick'
|
'click .beautify-code': 'onBeautifyClick'
|
||||||
'click .fullscreen-code': 'onFullscreenClick'
|
'click .fullscreen-code': 'onToggleMaximize'
|
||||||
|
|
||||||
constructor: (options) ->
|
constructor: (options) ->
|
||||||
super options
|
super options
|
||||||
|
|
||||||
getRenderData: (context={}) ->
|
getRenderData: (context={}) ->
|
||||||
context = super context
|
context = super context
|
||||||
|
ctrl = if @isMac() then 'Cmd' else 'Ctrl'
|
||||||
|
shift = $.i18n.t 'keyboard_shortcuts.shift'
|
||||||
|
context.beautifyShortcutVerbose = "#{ctrl}+#{shift}+B: #{$.i18n.t 'keyboard_shortcuts.beautify'}"
|
||||||
|
context.maximizeShortcutVerbose = "#{ctrl}+#{shift}+M: #{$.i18n.t 'keyboard_shortcuts.maximize_editor'}"
|
||||||
context
|
context
|
||||||
|
|
||||||
afterRender: ->
|
afterRender: ->
|
||||||
|
@ -86,19 +90,20 @@ module.exports = class SpellListTabEntryView extends SpellListEntryView
|
||||||
return unless @controlsEnabled
|
return unless @controlsEnabled
|
||||||
Backbone.Mediator.publish 'tome:toggle-spell-list', {}
|
Backbone.Mediator.publish 'tome:toggle-spell-list', {}
|
||||||
|
|
||||||
onCodeReload: ->
|
onCodeReload: (e) ->
|
||||||
return unless @controlsEnabled
|
return unless @controlsEnabled
|
||||||
Backbone.Mediator.publish 'tome:reload-code', spell: @spell
|
Backbone.Mediator.publish 'tome:reload-code', spell: @spell
|
||||||
|
|
||||||
onBeautifyClick: ->
|
onBeautifyClick: (e) ->
|
||||||
return unless @controlsEnabled
|
return unless @controlsEnabled
|
||||||
Backbone.Mediator.publish 'tome:spell-beautify', spell: @spell
|
Backbone.Mediator.publish 'tome:spell-beautify', spell: @spell
|
||||||
|
|
||||||
onFullscreenClick: ->
|
onToggleMaximize: (e) ->
|
||||||
$codearea = $('html')
|
$codearea = $('html')
|
||||||
$('#code-area').css 'z-index', 20 unless $codearea.hasClass 'fullscreen-editor'
|
$('#code-area').css 'z-index', 20 unless $codearea.hasClass 'fullscreen-editor'
|
||||||
$('html').toggleClass 'fullscreen-editor'
|
$('html').toggleClass 'fullscreen-editor'
|
||||||
$('.fullscreen-code').toggleClass 'maximized'
|
$('.fullscreen-code').toggleClass 'maximized'
|
||||||
|
Backbone.Mediator.publish 'tome:maximize-toggled', {}
|
||||||
|
|
||||||
updateReloadButton: ->
|
updateReloadButton: ->
|
||||||
changed = @spell.hasChanged null, @spell.getSource()
|
changed = @spell.hasChanged null, @spell.getSource()
|
||||||
|
|
|
@ -50,6 +50,7 @@ module.exports = class SpellView extends CocoView
|
||||||
'tome:update-snippets': 'addZatannaSnippets'
|
'tome:update-snippets': 'addZatannaSnippets'
|
||||||
'tome:insert-snippet': 'onInsertSnippet'
|
'tome:insert-snippet': 'onInsertSnippet'
|
||||||
'tome:spell-beautify': 'onSpellBeautify'
|
'tome:spell-beautify': 'onSpellBeautify'
|
||||||
|
'tome:maximize-toggled': 'onMaximizeToggled'
|
||||||
'script:state-changed': 'onScriptStateChange'
|
'script:state-changed': 'onScriptStateChange'
|
||||||
|
|
||||||
events:
|
events:
|
||||||
|
@ -181,8 +182,8 @@ module.exports = class SpellView extends CocoView
|
||||||
exec: -> # just prevent default ACE go-to-line alert
|
exec: -> # just prevent default ACE go-to-line alert
|
||||||
addCommand
|
addCommand
|
||||||
name: 'open-fullscreen-editor'
|
name: 'open-fullscreen-editor'
|
||||||
bindKey: {win: 'Alt-Shift-F', mac: 'Ctrl-Shift-F'}
|
bindKey: {win: 'Ctrl-Shift-M', mac: 'Command-Shift-M|Ctrl-Shift-M'}
|
||||||
exec: -> Backbone.Mediator.publish 'tome:fullscreen-view', {}
|
exec: -> Backbone.Mediator.publish 'tome:toggle-maximize', {}
|
||||||
|
|
||||||
fillACE: ->
|
fillACE: ->
|
||||||
@ace.setValue @spell.source
|
@ace.setValue @spell.source
|
||||||
|
@ -665,6 +666,9 @@ module.exports = class SpellView extends CocoView
|
||||||
pretty = @spellThang.aether.beautify ugly
|
pretty = @spellThang.aether.beautify ugly
|
||||||
@ace.setValue pretty
|
@ace.setValue pretty
|
||||||
|
|
||||||
|
onMaximizeToggled: (e) ->
|
||||||
|
@ace.resize true
|
||||||
|
|
||||||
onChangeEditorConfig: (e) ->
|
onChangeEditorConfig: (e) ->
|
||||||
aceConfig = me.get('aceConfig') ? {}
|
aceConfig = me.get('aceConfig') ? {}
|
||||||
@ace.setDisplayIndentGuides aceConfig.indentGuides # default false
|
@ace.setDisplayIndentGuides aceConfig.indentGuides # default false
|
||||||
|
|
|
@ -42,6 +42,7 @@ LevelHandler = class LevelHandler extends Handler
|
||||||
return @getLeaderboardGPlusFriends(req, res, args[0]) if args[1] is 'leaderboard_gplus_friends'
|
return @getLeaderboardGPlusFriends(req, res, args[0]) if args[1] is 'leaderboard_gplus_friends'
|
||||||
return @getHistogramData(req, res, args[0]) if args[1] is 'histogram_data'
|
return @getHistogramData(req, res, args[0]) if args[1] is 'histogram_data'
|
||||||
return @checkExistence(req, res, args[0]) if args[1] is 'exists'
|
return @checkExistence(req, res, args[0]) if args[1] is 'exists'
|
||||||
|
return @getPlayCountsBySlugs(req, res) if args[1] is 'play_counts'
|
||||||
super(arguments...)
|
super(arguments...)
|
||||||
|
|
||||||
fetchLevelByIDAndHandleErrors: (id, req, res, callback) ->
|
fetchLevelByIDAndHandleErrors: (id, req, res, callback) ->
|
||||||
|
@ -278,4 +279,27 @@ LevelHandler = class LevelHandler extends Handler
|
||||||
return @sendNotFoundError(res) unless doc?
|
return @sendNotFoundError(res) unless doc?
|
||||||
@sendSuccess(res, doc)
|
@sendSuccess(res, doc)
|
||||||
|
|
||||||
|
getPlayCountsBySlugs: (req, res) ->
|
||||||
|
# This is hella slow (4s on my box), so relying on some dumb caching for it.
|
||||||
|
# If we can't make this faster with indexing or something, we might want to maintain the counts another way.
|
||||||
|
levelIDs = req.query.ids or req.body.ids
|
||||||
|
@playCountCache ?= {}
|
||||||
|
@playCountCachedSince ?= new Date()
|
||||||
|
if (new Date()) - @playCountCachedSince > 86400 * 1000 # Dumb cache expiration
|
||||||
|
@playCountCache = {}
|
||||||
|
@playCountCacheSince = new Date()
|
||||||
|
cacheKey = levelIDs.join ','
|
||||||
|
if playCounts = @playCountCache[cacheKey]
|
||||||
|
return @sendSuccess res, playCounts
|
||||||
|
query = Session.aggregate [
|
||||||
|
{$match: {levelID: {$in: levelIDs}}},
|
||||||
|
{$group: {_id: "$levelID", playtime: {$sum: "$playtime"}, sessions: {$sum: 1}}},
|
||||||
|
{$sort: {sessions: -1}}
|
||||||
|
]
|
||||||
|
query.exec (err, data) =>
|
||||||
|
if err? then return @sendDatabaseError res, err
|
||||||
|
@playCountCache[cacheKey] = data
|
||||||
|
@sendSuccess res, data
|
||||||
|
|
||||||
|
|
||||||
module.exports = new LevelHandler()
|
module.exports = new LevelHandler()
|
||||||
|
|
1
vendor/scripts/lz-string-1.3.3-min.js
vendored
Normal file
1
vendor/scripts/lz-string-1.3.3-min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue