2014-10-30 22:58:11 -04:00
RootView = require ' views/kinds/RootView '
2014-09-17 21:56:08 -04:00
template = require ' templates/play/world-map-view '
LevelSession = require ' models/LevelSession '
CocoCollection = require ' collections/CocoCollection '
AudioPlayer = require ' lib/AudioPlayer '
2014-11-06 19:23:23 -05:00
LevelSetupManager = require ' lib/LevelSetupManager '
2014-10-02 01:02:52 -04:00
ThangType = require ' models/ThangType '
2014-10-15 13:28:29 -04:00
MusicPlayer = require ' lib/surface/MusicPlayer '
storage = require ' lib/storage '
2014-11-02 21:36:43 -05:00
AuthModal = require ' views/modal/AuthModal '
2014-09-17 21:56:08 -04:00
class LevelSessionsCollection extends CocoCollection
url: ' '
model: LevelSession
constructor: (model) ->
super ( )
@url = " /db/user/ #{ me . id } /level.sessions?project=state.complete,levelID "
2014-09-18 21:25:33 -04:00
module.exports = class WorldMapView extends RootView
2014-09-17 21:56:08 -04:00
id: ' world-map-view '
template: template
events:
2014-09-23 22:12:05 -04:00
' click .map-background ' : ' onClickMap '
2014-09-19 14:03:38 -04:00
' click .level a ' : ' onClickLevel '
2014-09-23 22:12:05 -04:00
' click .level-info-container .start-level ' : ' onClickStartLevel '
2014-09-24 18:21:39 -04:00
' mouseenter .level a ' : ' onMouseEnterLevel '
' mouseleave .level a ' : ' onMouseLeaveLevel '
' mousemove .map ' : ' onMouseMoveMap '
2014-10-06 13:17:40 -04:00
' click # volume-button ' : ' onToggleVolume '
2014-09-17 21:56:08 -04:00
2014-10-29 12:47:00 -04:00
constructor: (options, @terrain) ->
@ terrain ? = ' dungeon ' # or 'forest'
2014-09-17 21:56:08 -04:00
super options
2014-10-22 18:42:51 -04:00
@nextLevel = @ getQueryVariable ' next '
2014-09-17 21:56:08 -04:00
@levelStatusMap = { }
@levelPlayCountMap = { }
@sessions = @ supermodel . loadCollection ( new LevelSessionsCollection ( ) , ' your_sessions ' , null , 0 ) . model
@ listenToOnce @ sessions , ' sync ' , @ onSessionsLoaded
@ getLevelPlayCounts ( )
$ ( window ) . on ' resize ' , @ onWindowResize
2014-09-25 14:37:29 -04:00
@ playAmbientSound ( )
2014-10-15 13:28:29 -04:00
@probablyCachedMusic = storage . load ( " loaded-menu-music- #{ @ terrain } " )
musicDelay = if @ probablyCachedMusic then 1000 else 10000
@playMusicTimeout = _ . delay ( => @ playMusic ( ) unless @ destroyed ) , musicDelay
2014-10-02 01:02:52 -04:00
@ preloadTopHeroes ( )
2014-10-02 14:18:43 -04:00
@hadEverChosenHero = me . get ( ' heroConfig ' ) ? . thangType
2014-10-21 19:49:25 -04:00
window . tracker ? . trackEvent ' World Map ' , Action: ' Loaded '
2014-09-17 21:56:08 -04:00
2014-09-18 21:25:33 -04:00
destroy: ->
$ ( window ) . off ' resize ' , @ onWindowResize
2014-09-25 14:37:29 -04:00
if ambientSound = @ ambientSound
# Doesn't seem to work; stops immediately.
createjs . Tween . get ( ambientSound ) . to ( { volume: 0.0 } , 1500 ) . call -> ambientSound . stop ( )
2014-10-15 13:28:29 -04:00
@ musicPlayer ? . destroy ( )
clearTimeout @ playMusicTimeout
2014-09-18 21:25:33 -04:00
super ( )
2014-09-17 21:56:08 -04:00
getLevelPlayCounts: ->
2014-10-14 12:54:18 -04:00
return unless me . isAdmin ( )
2014-09-17 21:56:08 -04:00
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={}) ->
context = super ( context )
2014-10-29 12:47:00 -04:00
context.campaign = _ . find campaigns , { id: @ terrain }
for level , index in context . campaign . levels
level . x ? = 10 + 80 * Math . random ( )
level . y ? = 10 + 80 * Math . random ( )
2014-11-01 17:15:57 -04:00
level.locked = index > 0 and not me . ownsLevel level . original
2014-10-29 12:47:00 -04:00
window . levelUnlocksNotWorking = true if level . locked and level . id is @ nextLevel # Temporary
level.locked = false if window . levelUnlocksNotWorking # Temporary; also possible in HeroVictoryModal
2014-11-05 16:28:54 -05:00
level.locked = false if @ levelStatusMap [ level . id ] in [ ' started ' , ' complete ' ]
2014-11-06 21:18:57 -05:00
level.disabled = false if @ levelStatusMap [ level . id ] in [ ' started ' , ' complete ' ]
2014-10-29 12:47:00 -04:00
level.color = ' rgb(255, 80, 60) '
if level . practice
level.color = ' rgb(80, 130, 200) ' unless me . getBranchingGroup ( ) is ' all-practice '
level.hidden = true if me . getBranchingGroup ( ) is ' no-practice '
2014-09-17 21:56:08 -04:00
context.levelStatusMap = @ levelStatusMap
context.levelPlayCountMap = @ levelPlayCountMap
2014-09-24 18:21:39 -04:00
context.isIPadApp = application . isIPadApp
2014-10-02 17:05:18 -04:00
context.mapType = _ . string . slugify @ terrain
2014-10-22 18:42:51 -04:00
context.nextLevel = @ nextLevel
2014-09-17 21:56:08 -04:00
context
afterRender: ->
super ( )
@ onWindowResize ( )
2014-09-24 18:21:39 -04:00
unless application . isIPadApp
2014-10-29 13:47:17 -04:00
_ . defer => @ $el ? . find ( ' .game-controls .btn ' ) . tooltip ( ) # Have to defer or i18n doesn't take effect.
2014-09-24 18:21:39 -04:00
@ $el . find ( ' .level ' ) . tooltip ( )
2014-10-02 17:05:18 -04:00
@ $el . addClass _ . string . slugify @ terrain
2014-10-06 13:17:40 -04:00
@ updateVolume ( )
2014-11-01 19:59:12 -04:00
@ highlightElement ' .level.next ' , delay: 500 , duration: 60000 , rotation: 0 , sides: [ ' top ' ] unless window . currentModal
2014-09-17 21:56:08 -04:00
2014-11-02 21:36:43 -05:00
afterInsert: ->
super ( )
return unless @ getQueryVariable ' signup '
return if me . get ( ' email ' )
@ endHighlight ( )
authModal = new AuthModal supermodel: @ supermodel
authModal.mode = ' signup '
@ openModalView authModal
2014-09-17 21:56:08 -04:00
onSessionsLoaded: (e) ->
for session in @ sessions . models
@ levelStatusMap [ session . get ( ' levelID ' ) ] = if session . get ( ' state ' ) ? . complete then ' complete ' else ' started '
@ render ( )
onClickMap: (e) ->
2014-09-23 22:12:05 -04:00
@ $levelInfo ? . hide ( )
2014-09-17 21:56:08 -04:00
# Easy-ish way of figuring out coordinates for placing level dots.
x = e . offsetX / @ $el . find ( ' .map-background ' ) . width ( )
y = ( 1 - e . offsetY / @ $el . find ( ' .map-background ' ) . height ( ) )
console . log " x: #{ ( 100 * x ) . toFixed ( 2 ) } \n y: #{ ( 100 * y ) . toFixed ( 2 ) } \n "
2014-09-19 14:03:38 -04:00
onClickLevel: (e) ->
2014-09-20 01:15:58 -04:00
e . preventDefault ( )
2014-09-23 22:12:05 -04:00
e . stopPropagation ( )
@ $levelInfo ? . hide ( )
2014-09-26 05:28:54 -04:00
return if $ ( e . target ) . attr ( ' disabled ' ) or $ ( e . target ) . parent ( ) . hasClass ' locked '
2014-09-24 18:21:39 -04:00
if application . isIPadApp
levelID = $ ( e . target ) . parents ( ' .level ' ) . data ( ' level-id ' )
@$levelInfo = @ $el . find ( " .level-info-container[data-level-id= #{ levelID } ] " ) . show ( )
@ adjustLevelInfoPosition e
2014-10-24 16:03:10 -04:00
@ endHighlight ( )
2014-09-24 18:21:39 -04:00
else
2014-10-21 19:49:25 -04:00
levelElement = $ ( e . target ) . parents ( ' .level ' )
levelID = levelElement . data ( ' level-id ' )
@ startLevel levelElement
window . tracker ? . trackEvent ' World Map ' , Action: ' Play Level ' , levelID: levelID
2014-09-17 21:56:08 -04:00
2014-09-23 22:12:05 -04:00
onClickStartLevel: (e) ->
2014-09-24 18:21:39 -04:00
@ startLevel $ ( e . target ) . parents ( ' .level-info-container ' )
startLevel: (levelElement) ->
2014-11-06 19:23:23 -05:00
setupManager = new LevelSetupManager supermodel: @ supermodel , levelID: levelElement . data ( ' level-id ' ) , levelPath: levelElement . data ( ' level-path ' ) , levelName: levelElement . data ( ' level-name ' ) , hadEverChosenHero: @ hadEverChosenHero , parent: @
setupManager . open ( )
2014-09-23 22:12:05 -04:00
@ $levelInfo ? . hide ( )
2014-09-17 21:56:08 -04:00
2014-09-24 18:21:39 -04:00
onMouseEnterLevel: (e) ->
return if application . isIPadApp
levelID = $ ( e . target ) . parents ( ' .level ' ) . data ( ' level-id ' )
@$levelInfo = @ $el . find ( " .level-info-container[data-level-id= #{ levelID } ] " ) . show ( )
@ adjustLevelInfoPosition e
2014-10-15 13:33:33 -04:00
@ endHighlight ( )
2014-09-24 18:21:39 -04:00
onMouseLeaveLevel: (e) ->
return if application . isIPadApp
levelID = $ ( e . target ) . parents ( ' .level ' ) . data ( ' level-id ' )
@ $el . find ( " .level-info-container[data-level-id= ' #{ levelID } ' ] " ) . hide ( )
onMouseMoveMap: (e) ->
return if application . isIPadApp
@ adjustLevelInfoPosition e
2014-09-17 21:56:08 -04:00
adjustLevelInfoPosition: (e) ->
return unless @ $levelInfo
@ $map ? = @ $el . find ( ' .map ' )
mapOffset = @ $map . offset ( )
mapX = e . pageX - mapOffset . left
mapY = e . pageY - mapOffset . top
margin = 20
width = @ $levelInfo . outerWidth ( )
@ $levelInfo . css ( ' left ' , Math . min ( Math . max ( margin , mapX - width / 2 ) , @ $map . width ( ) - width - margin ) )
height = @ $levelInfo . outerHeight ( )
top = mapY - @ $levelInfo . outerHeight ( ) - 60
if top < 20
top = mapY + 60
@ $levelInfo . css ( ' top ' , top )
onWindowResize: (e) =>
2014-10-02 17:05:18 -04:00
mapHeight = iPadHeight = 1536
2014-10-29 12:47:00 -04:00
mapWidth = if @ terrain is ' dungeon ' then 2350 else 2500
2014-10-02 17:05:18 -04:00
iPadWidth = 2048
2014-10-01 21:17:21 -04:00
aspectRatio = mapWidth / mapHeight
2014-10-02 17:05:18 -04:00
iPadAspectRatio = iPadWidth / iPadHeight
2014-09-17 21:56:08 -04:00
pageWidth = $ ( window ) . width ( )
pageHeight = $ ( window ) . height ( )
2014-10-01 21:17:21 -04:00
widthRatio = pageWidth / mapWidth
heightRatio = pageHeight / mapHeight
2014-10-02 17:05:18 -04:00
iPadWidthRatio = pageWidth / iPadWidth
2014-10-29 12:47:00 -04:00
if @ terrain is ' dungeon '
2014-10-02 17:05:18 -04:00
# Make sure we can see almost the whole map, fading to background in one dimension.
if heightRatio <= iPadWidthRatio
# Full width, full height, left and right margin
resultingHeight = pageHeight
resultingWidth = resultingHeight * aspectRatio
else if iPadWidthRatio < heightRatio * ( iPadAspectRatio / aspectRatio )
# Cropped width, full height, left and right margin
resultingWidth = pageWidth
resultingHeight = resultingWidth / aspectRatio
else
# Cropped width, full height, top and bottom margin
resultingWidth = pageWidth * aspectRatio / iPadAspectRatio
resultingHeight = resultingWidth / aspectRatio
2014-09-17 21:56:08 -04:00
else
2014-10-02 17:05:18 -04:00
# Scale it in either dimension so that we're always full on one of the dimensions.
if heightRatio > widthRatio
resultingHeight = pageHeight
resultingWidth = resultingHeight * aspectRatio
else
resultingWidth = pageWidth
resultingHeight = resultingWidth / aspectRatio
2014-09-17 21:56:08 -04:00
resultingMarginX = ( pageWidth - resultingWidth ) / 2
resultingMarginY = ( pageHeight - resultingHeight ) / 2
@ $el . find ( ' .map ' ) . css ( width: resultingWidth , height: resultingHeight , ' margin-left ' : resultingMarginX , ' margin-top ' : resultingMarginY )
2014-09-25 14:37:29 -04:00
playAmbientSound: ->
return if @ ambientSound
2014-10-29 12:47:00 -04:00
return unless file = { dungeon: ' ambient-dungeon ' , forest: ' ambient-map-grass ' } [ @ terrain ]
2014-09-25 14:37:29 -04:00
src = " /file/interface/ #{ file } #{ AudioPlayer . ext } "
unless AudioPlayer . getStatus ( src ) ? . loaded
AudioPlayer . preloadSound src
Backbone . Mediator . subscribeOnce ' audio-player:loaded ' , @ playAmbientSound , @
return
@ambientSound = createjs . Sound . play src , loop : - 1 , volume: 0.1
createjs . Tween . get ( @ ambientSound ) . to ( { volume: 1.0 } , 1000 )
2014-10-15 13:28:29 -04:00
playMusic: ->
@musicPlayer = new MusicPlayer ( )
2014-10-29 12:47:00 -04:00
musicFile = { dungeon: ' /music/music-menu-dungeon ' , forest: ' /music/music-menu-grass ' } [ @ terrain ]
2014-10-15 13:28:29 -04:00
Backbone . Mediator . publish ' music-player:play-music ' , play: true , file: musicFile
storage . save ( " loaded-menu-music- #{ @ terrain } " , true ) unless @ probablyCachedMusic
2014-10-02 01:02:52 -04:00
preloadTopHeroes: ->
for heroID in [ ' captain ' , ' knight ' ]
url = " /db/thang.type/ #{ ThangType . heroes [ heroID ] } /version "
continue if @ supermodel . getModel url
fullHero = new ThangType ( )
fullHero . setURL url
@ supermodel . loadModel fullHero , ' thang '
2014-10-06 13:17:40 -04:00
updateVolume: (volume) ->
volume ? = me . get ( ' volume ' ) ? 1.0
classes = [ ' vol-off ' , ' vol-down ' , ' vol-up ' ]
button = $ ( ' # volume-button ' , @ $el )
button . toggleClass ' vol-off ' , volume <= 0.0
button . toggleClass ' vol-down ' , 0.0 < volume < 1.0
button . toggleClass ' vol-up ' , volume >= 1.0
createjs . Sound . setVolume ( if volume is 1 then 0.6 else volume ) # Quieter for now until individual sound FX controls work again.
if volume isnt me . get ' volume '
me . set ' volume ' , volume
me . patch ( )
onToggleVolume: (e) ->
button = $ ( e . target ) . closest ( ' # volume-button ' )
classes = [ ' vol-off ' , ' vol-down ' , ' vol-up ' ]
volumes = [ 0 , 0.4 , 1.0 ]
for oldClass , i in classes
if button . hasClass oldClass
newI = ( i + 1 ) % classes . length
break
else if i is classes . length - 1 # no oldClass
newI = 2
@ updateVolume volumes [ newI ]
2014-09-25 14:37:29 -04:00
2014-09-17 21:56:08 -04:00
tutorials = [
{
name: ' Rescue Mission '
difficulty: 1
id: ' rescue-mission '
image: ' /file/db/level/52740644904ac0411700067c/rescue_mission_icon.png '
description: ' Tharin has been captured! Start here. '
x: 17.23
y: 36.94
}
{
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. '
x: 22.6
y: 35.1
}
{
name: ' Drink Me '
difficulty: 1
id: ' drink-me '
image: ' /file/db/level/525dc5589a0765e496000006/drink_me_icon.png '
description: ' Drink up and slay two munchkins. '
x: 27.74
y: 35.17
}
{
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. '
x: 32.7
y: 36.7
}
{
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. '
x: 37.6
y: 40.0
}
{
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! '
x: 44.1
y: 39.5
}
{
name: ' Taunt '
difficulty: 1
id: ' taunt '
image: ' /file/db/level/525f150306e1ab0962000018/taunt_icon.png '
description: ' Taunt the ogre to claim victory. '
x: 38.5
y: 44.1
}
{
name: ' Cowardly Taunt '
difficulty: 1
id: ' cowardly-taunt '
image: ' /file/db/level/525abfd9b12777d78e000009/cowardly_taunt_icon.png '
description: ' Lure infuriated ogres to their doom. '
x: 39.2
y: 50.1
}
{
name: ' Commanding Followers '
difficulty: 1
id: ' commanding-followers '
image: ' /file/db/level/525ef8ef06e1ab0962000003/commanding_followers_icon.png '
description: ' Lead allied soldiers into battle. '
x: 39.1
y: 54.7
}
{
name: ' Mobile Artillery '
difficulty: 1
id: ' mobile-artillery '
image: ' /file/db/level/525085419851b83f4b000001/mobile_artillery_icon.png '
description: ' Blow ogres up! '
x: 39.5
y: 60.2
}
]
experienced = [
{
name: ' Hunter Triplets '
difficulty: 2
id: ' hunter-triplets '
image: ' /file/db/level/526711d9add4f8965f000002/hunter_triplets_icon.png '
description: ' Three soldiers go ogre hunting. '
x: 51.76
y: 35.5
}
{
name: ' Emphasis on Aim '
difficulty: 2
id: ' emphasis-on-aim '
image: ' /file/db/level/525f384d96cd77000000000f/munchkin_masher_icon.png '
description: ' Choose your targets carefully. '
x: 61.47
y: 33.46
}
{
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. '
x: 65.72
y: 26.72
}
{
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. '
x: 70.95
y: 18.64
}
{
name: ' Gridmancer '
difficulty: 5
id: ' gridmancer '
image: ' /file/db/level/52ae2460ef42c52f13000008/gridmancer_icon.png '
description: ' Super algorithm challenge level! '
x: 61.41
y: 17.22
}
]
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 '
x: 49.43
y: 21.48
}
{
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 '
x: 45.00
y: 23.34
}
{
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 '
x: 36.82
y: 23.17
}
{
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 '
x: 30.8
y: 16.87
}
{
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 '
x: 41.93
y: 12.79
}
{
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 '
x: 53.12
y: 11.37
}
]
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 '
x: 26.37
y: 93.02
}
{
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 '
x: 32.39
y: 92.67
}
{
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 '
x: 38.07
y: 92.76
}
{
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 '
x: 43.75
y: 90.36
}
{
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 '
x: 48.97
y: 87.08
}
{
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 '
x: 55.96
y: 82.73
}
]
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 '
x: 42.67
y: 67.98
}
{
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 '
x: 47.38
y: 70.55
}
{
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 '
x: 52.66
y: 69.66
}
{
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 '
x: 58.46
y: 66.38
}
{
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 '
x: 63.11
y: 62.74
}
{
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 '
x: 69.19
y: 60.61
}
{
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 '
x: 77.54
y: 65.94
}
{
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 '
x: 84.29
y: 61.23
}
]
2014-10-29 12:47:00 -04:00
dungeon = [
2014-09-19 14:03:38 -04:00
{
name: ' Dungeons of Kithgard '
type: ' hero '
difficulty: 1
id: ' dungeons-of-kithgard '
2014-09-26 05:28:54 -04:00
original: ' 528110f30268d018e3000001 '
2014-09-19 14:03:38 -04:00
description: ' Grab the gem, but touch nothing else. Start here. '
2014-10-02 17:36:26 -04:00
x: 14
y: 15.5
2014-10-22 18:42:51 -04:00
nextLevels:
continue : ' gems-in-the-deep '
skip_ahead: ' shadow-guard '
2014-09-19 14:03:38 -04:00
}
{
name: ' Gems in the Deep '
type: ' hero '
difficulty: 1
id: ' gems-in-the-deep '
2014-09-26 05:28:54 -04:00
original: ' 54173c90844506ae0195a0b4 '
2014-09-19 14:03:38 -04:00
description: ' Quickly collect the gems; you will need them. '
2014-10-30 22:58:11 -04:00
x: 29
y: 12
2014-10-22 18:42:51 -04:00
nextLevels:
continue : ' shadow-guard '
2014-11-05 13:40:37 -05:00
skip_ahead: ' forgetful-gemsmith '
2014-09-19 14:03:38 -04:00
}
{
name: ' Shadow Guard '
type: ' hero '
difficulty: 1
id: ' shadow-guard '
2014-09-26 05:28:54 -04:00
original: ' 54174347844506ae0195a0b8 '
2014-09-19 14:03:38 -04:00
description: ' Evade the Kithgard minion. '
2014-11-05 13:40:37 -05:00
x: 41
y: 13
2014-10-22 18:42:51 -04:00
nextLevels:
2014-10-30 22:14:40 -04:00
more_practice: ' kounter-kithwise '
2014-11-05 13:40:37 -05:00
continue : ' forgetful-gemsmith '
2014-10-22 18:42:51 -04:00
}
{
2014-10-30 22:14:40 -04:00
name: ' Kounter Kithwise '
2014-10-22 18:42:51 -04:00
type: ' hero '
difficulty: 1
2014-10-30 22:14:40 -04:00
id: ' kounter-kithwise '
original: ' 54527a6257e83800009730c7 '
2014-10-22 18:42:51 -04:00
description: ' Practice your evasion skills with more guards. '
2014-11-05 13:40:37 -05:00
x: 50
y: 14
2014-10-30 22:14:40 -04:00
nextLevels:
2014-11-05 13:40:37 -05:00
#more_practice: 'crawlways-of-kithgard'
2014-10-30 22:14:40 -04:00
continue : ' true-names '
practice: true
}
2014-11-05 13:40:37 -05:00
#{
# 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
#}
2014-10-30 22:14:40 -04:00
{
2014-11-05 13:40:37 -05:00
name: ' Forgetful Gemsmith '
2014-10-30 22:14:40 -04:00
type: ' hero '
difficulty: 1
2014-11-05 13:40:37 -05:00
id: ' forgetful-gemsmith '
original: ' 544a98f62d002f0000fe331a '
description: ' Grab even more gems as you practice moving. '
x: 63
y: 13
2014-10-22 18:42:51 -04:00
nextLevels:
2014-11-05 13:40:37 -05:00
continue : ' shadow-guard '
2014-09-19 14:03:38 -04:00
}
{
name: ' True Names '
type: ' hero '
difficulty: 1
id: ' true-names '
2014-09-26 05:28:54 -04:00
original: ' 541875da4c16460000ab990f '
2014-09-19 14:03:38 -04:00
description: ' Learn an enemy \' s true name to defeat it. '
2014-10-02 17:36:26 -04:00
x: 74
2014-11-05 13:40:37 -05:00
y: 14
2014-10-22 18:42:51 -04:00
nextLevels:
2014-10-30 22:14:40 -04:00
more_practice: ' favorable-odds '
2014-10-22 18:42:51 -04:00
continue : ' the-raised-sword '
}
{
2014-10-30 22:14:40 -04:00
name: ' Favorable Odds '
2014-10-22 18:42:51 -04:00
type: ' hero '
difficulty: 1
2014-10-30 22:14:40 -04:00
id: ' favorable-odds '
original: ' 5452972f57e83800009730de '
2014-10-22 18:42:51 -04:00
description: ' Test out your battle skills by defeating more munchkins. '
x: 80.85
2014-10-30 22:58:11 -04:00
y: 16
2014-10-22 18:42:51 -04:00
nextLevels:
continue : ' the-raised-sword '
practice: true
2014-09-19 14:03:38 -04:00
}
{
name: ' The Raised Sword '
type: ' hero '
difficulty: 1
id: ' the-raised-sword '
2014-09-26 05:28:54 -04:00
original: ' 5418aec24c16460000ab9aa6 '
2014-09-19 14:03:38 -04:00
description: ' Learn to equip yourself for combat. '
2014-10-02 17:36:26 -04:00
x: 85
y: 20
2014-10-22 18:42:51 -04:00
nextLevels:
continue : ' the-first-kithmaze '
2014-09-19 14:03:38 -04:00
}
{
name: ' The First Kithmaze '
type: ' hero '
difficulty: 1
id: ' the-first-kithmaze '
2014-09-26 05:28:54 -04:00
original: ' 5418b9d64c16460000ab9ab4 '
2014-10-30 22:14:40 -04:00
description: ' The builders of Kithgard constructed many mazes to confuse travelers. '
2014-10-31 00:34:49 -04:00
x: 78
y: 29
2014-10-22 18:42:51 -04:00
nextLevels:
2014-10-30 22:14:40 -04:00
more_practice: ' descending-further '
2014-10-22 18:42:51 -04:00
continue : ' the-second-kithmaze '
2014-11-05 13:40:37 -05:00
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 '
2014-10-22 18:42:51 -04:00
}
{
2014-10-30 22:14:40 -04:00
name: ' Descending Further '
2014-10-22 18:42:51 -04:00
type: ' hero '
difficulty: 1
2014-10-30 22:14:40 -04:00
id: ' descending-further '
original: ' 5452a84d57e83800009730e4 '
2014-10-22 18:42:51 -04:00
description: ' Another day, another maze. '
2014-10-31 00:34:49 -04:00
x: 70
y: 28
2014-10-22 18:42:51 -04:00
nextLevels:
continue : ' the-second-kithmaze '
practice: true
2014-09-19 14:03:38 -04:00
}
{
name: ' The Second Kithmaze '
type: ' hero '
difficulty: 1
id: ' the-second-kithmaze '
2014-09-26 05:28:54 -04:00
original: ' 5418cf256bae62f707c7e1c3 '
2014-09-19 14:03:38 -04:00
description: ' Many have tried, few have found their way through this maze. '
2014-10-30 22:58:11 -04:00
x: 59
y: 25
2014-10-22 18:42:51 -04:00
nextLevels:
2014-11-05 13:40:37 -05:00
continue : ' dread-door '
2014-10-22 18:42:51 -04:00
}
2014-09-19 14:03:38 -04:00
{
2014-11-05 13:40:37 -05:00
name: ' Dread Door '
2014-09-19 14:03:38 -04:00
type: ' hero '
difficulty: 1
2014-11-05 13:40:37 -05:00
id: ' dread-door '
2014-09-26 05:28:54 -04:00
original: ' 5418d40f4c16460000ab9ac2 '
2014-11-05 13:40:37 -05:00
description: ' Behind a dread door lies a chest full of riches. '
2014-10-30 22:58:11 -04:00
x: 60
y: 34
2014-10-30 22:14:40 -04:00
nextLevels:
continue : ' known-enemy '
}
{
name: ' Known Enemy '
type: ' hero '
difficulty: 1
id: ' known-enemy '
original: ' 5452adea57e83800009730ee '
description: ' Begin to use variables in your battles. '
2014-10-31 00:34:49 -04:00
x: 68
y: 42
2014-10-30 22:14:40 -04:00
nextLevels:
continue : ' master-of-names '
}
{
name: ' Master of Names '
type: ' hero '
difficulty: 1
id: ' master-of-names '
original: ' 5452c3ce57e83800009730f7 '
description: ' Use your glasses to defend yourself from the Kithmen. '
2014-10-31 00:34:49 -04:00
x: 75
y: 46
2014-10-22 18:42:51 -04:00
nextLevels:
continue : ' lowly-kithmen '
2014-10-30 22:14:40 -04:00
skip_ahead: ' closing-the-distance '
2014-09-19 14:03:38 -04:00
}
{
2014-09-20 18:18:21 -04:00
name: ' Lowly Kithmen '
2014-09-19 14:03:38 -04:00
type: ' hero '
difficulty: 1
2014-09-20 18:18:21 -04:00
id: ' lowly-kithmen '
2014-09-26 05:28:54 -04:00
original: ' 541b24511ccc8eaae19f3c1f '
2014-10-30 22:14:40 -04:00
description: ' Now that you can see them, they \' re everywhere! '
2014-10-31 00:34:49 -04:00
x: 86
y: 43
2014-10-22 18:42:51 -04:00
nextLevels:
continue : ' closing-the-distance '
skip_ahead: ' the-final-kithmaze '
}
2014-09-19 14:03:38 -04:00
{
2014-10-06 20:46:13 -04:00
name: ' Closing the Distance '
2014-09-19 14:03:38 -04:00
type: ' hero '
difficulty: 1
2014-10-06 20:46:13 -04:00
id: ' closing-the-distance '
2014-09-26 05:28:54 -04:00
original: ' 541b288e1ccc8eaae19f3c25 '
2014-09-19 14:03:38 -04:00
description: ' Kithmen are not the only ones to stand in your way. '
2014-10-31 00:34:49 -04:00
x: 93
y: 47
2014-10-22 18:42:51 -04:00
nextLevels:
2014-10-30 22:14:40 -04:00
more_practice: ' tactical-strike '
2014-10-22 18:42:51 -04:00
continue : ' the-final-kithmaze '
}
{
2014-10-30 22:14:40 -04:00
name: ' Tactical Strike '
2014-10-22 18:42:51 -04:00
type: ' hero '
difficulty: 1
2014-10-30 22:14:40 -04:00
id: ' tactical-strike '
original: ' 5452cfa706a59e000067e4f5 '
description: ' They \' re, uh, coming right for us! Sneak up behind them. '
2014-11-01 13:22:38 -04:00
x: 88.65
y: 63.06
2014-10-22 18:42:51 -04:00
nextLevels:
continue : ' the-final-kithmaze '
practice: true
2014-09-19 14:03:38 -04:00
}
{
name: ' The Final Kithmaze '
type: ' hero '
2014-09-23 14:39:56 -04:00
difficulty: 1
2014-09-19 14:03:38 -04:00
id: ' the-final-kithmaze '
2014-09-26 05:28:54 -04:00
original: ' 541b434e1ccc8eaae19f3c33 '
2014-09-19 14:03:38 -04:00
description: ' To escape you must find your way through an Elder Kithman \' s maze. '
2014-11-01 13:22:38 -04:00
x: 81.93
y: 65.86
2014-10-22 18:42:51 -04:00
nextLevels:
2014-10-30 22:14:40 -04:00
more_practice: ' the-gauntlet '
2014-10-22 18:42:51 -04:00
continue : ' kithgard-gates '
}
{
2014-10-30 22:14:40 -04:00
name: ' The Gauntlet '
2014-10-22 18:42:51 -04:00
type: ' hero '
difficulty: 1
2014-10-30 22:14:40 -04:00
id: ' the-gauntlet '
original: ' 5452d8b906a59e000067e4fa '
description: ' Rush for the stairs, battling foes at every turn. '
2014-11-01 13:22:38 -04:00
x: 84.89
y: 73.88
2014-10-22 18:42:51 -04:00
nextLevels:
continue : ' kithgard-gates '
practice: true
2014-09-19 14:03:38 -04:00
}
{
name: ' Kithgard Gates '
type: ' hero '
difficulty: 1
id: ' kithgard-gates '
2014-09-26 05:28:54 -04:00
original: ' 541c9a30c6362edfb0f34479 '
2014-09-22 17:05:13 -04:00
description: ' Escape the Kithgard dungeons and don \' t let the guardians get you. '
2014-10-02 17:36:26 -04:00
x: 89
y: 82
2014-10-22 18:42:51 -04:00
nextLevels:
2014-10-26 18:31:20 -04:00
continue : ' defense-of-plainswood '
2014-09-19 14:03:38 -04:00
}
2014-10-29 13:47:17 -04:00
{
name: ' Cavern Survival '
type: ' hero-ladder '
difficulty: 1
id: ' cavern-survival '
original: ' 544437e0645c0c0000c3291d '
description: ' Stay alive longer than your opponent amidst hordes of ogres! '
disabled: not me . isAdmin ( )
x: 17.54
y: 78.39
}
2014-10-29 12:47:00 -04:00
]
forest = [
2014-09-19 14:03:38 -04:00
{
2014-10-26 18:31:20 -04:00
name: ' Defense of Plainswood '
2014-09-19 14:03:38 -04:00
type: ' hero '
difficulty: 1
2014-10-26 18:31:20 -04:00
id: ' defense-of-plainswood '
2014-09-26 05:28:54 -04:00
original: ' 541b67f71ccc8eaae19f3c62 '
2014-09-19 14:03:38 -04:00
description: ' Protect the peasants from the pursuing ogres. '
2014-11-01 12:35:19 -04:00
nextLevels:
continue : ' winding-trail '
2014-10-29 13:47:17 -04:00
x: 29.63
y: 53.69
2014-09-19 14:03:38 -04:00
}
2014-10-18 17:51:43 -04:00
{
2014-10-29 13:47:17 -04:00
name: ' Winding Trail '
type: ' hero '
2014-10-18 17:51:43 -04:00
difficulty: 1
2014-10-29 13:47:17 -04:00
id: ' winding-trail '
original: ' 5446cb40ce01c23e05ecf027 '
description: ' Stay alive and navigate through the forest. '
2014-11-01 12:35:19 -04:00
nextLevels:
continue : ' thornbush-farm '
2014-10-29 13:47:17 -04:00
x: 39.03
y: 54.97
}
{
name: ' Thornbush Farm '
type: ' hero '
difficulty: 1
id: ' thornbush-farm '
original: ' 5447030525cce60000745e2a '
description: ' Determine refugee peasant from ogre when defending the farm. '
2014-11-01 12:35:19 -04:00
nextLevels:
continue : ' a-fiery-trap '
2014-10-29 13:47:17 -04:00
x: 44.09
y: 57.75
}
{
name: ' A Fiery Trap '
type: ' hero '
difficulty: 1
id: ' a-fiery-trap '
original: ' 5448330517d7283e051f9b9e '
description: ' Patrol the village entrances, but stay defensive. '
disabled: true
x: 40.14
y: 63.96
2014-10-18 17:51:43 -04:00
}
2014-09-19 14:03:38 -04:00
#{
# name: ''
# type: 'hero'
# difficulty: 1
# id: ''
# description: ''
# x: 58.46
# y: 66.38
# }
#{
# name: ''
# type: 'hero'
# difficulty: 1
# id: ''
# description: ''
# x: 63.11
# y: 62.74
# }
#{
# name: ''
# type: 'hero'
# difficulty: 1
# id: ''
# description: ''
# x: 69.19
# y: 60.61
# }
#{
# name: ''
# type: 'hero'
# difficulty: 1
# id: ''
# description: ''
# x: 77.54
# y: 65.94
#}
2014-11-06 21:18:57 -05:00
{
name: ' Coinucopia '
type: ' hero '
difficulty: 1
id: ' coinucopia '
description: ' Start playing in real-time with input flags as you collect gold coins! '
x: 58.54
y: 66.73
disabled: true
}
2014-10-29 13:47:17 -04:00
{
name: ' Dueling Grounds '
type: ' hero-ladder '
difficulty: 1
id: ' dueling-grounds '
original: ' 5442ba0e1e835500007eb1c7 '
description: ' Battle head-to-head against another hero in this basic beginner combat arena. '
disabled: not me . isAdmin ( )
x: 25.5
y: 77.5
}
2014-09-19 14:03:38 -04:00
]
2014-10-22 18:42:51 -04:00
WorldMapView.campaigns = campaigns = [
2014-09-19 14:03:38 -04:00
#{id: 'beginner', name: 'Beginner Campaign', description: '... in which you learn the wizardry of programming.', levels: tutorials, color: "rgb(255, 80, 60)"}
#{id: 'multiplayer', name: 'Multiplayer Arenas', description: '... in which you code head-to-head against other players.', levels: arenas, color: "rgb(80, 5, 60)"}
#{id: 'dev', name: 'Random Harder Levels', description: '... in which you learn the interface while doing something a little harder.', levels: experienced, color: "rgb(80, 60, 255)"}
2014-10-19 07:50:58 -04:00
#{id: 'classic_algorithms' ,name: 'Classic Algorithms', description: '... in which you learn the most popular algorithms in Computer Science.', levels: classicAlgorithms, color: "rgb(110, 80, 120)"}
2014-09-19 14:03:38 -04:00
#{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, color: "rgb(160, 160, 180)"}
2014-10-29 12:47:00 -04:00
{ id: ' dungeon ' , name: ' Dungeon Campaign ' , levels: dungeon }
{ id: ' forest ' , name: ' Forest Campaign ' , levels: forest }
2014-09-17 21:56:08 -04:00
]
2014-11-05 13:40:37 -05:00
# 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 '