Starting new front view (at /front for now) and play view (at /play-hero for now).

This commit is contained in:
Nick Winter 2014-09-17 18:56:08 -07:00
parent d96d0d65f3
commit 95e37fa3b2
39 changed files with 1116 additions and 34 deletions

View file

@ -14,7 +14,8 @@ module.exports = class CocoRouter extends Backbone.Router
Backbone.Mediator.subscribe 'router:navigate', @onNavigate, @
routes:
'': go('HomeView')
'': go('HomeView') # This will go somewhere deprecated when FrontView is done.
'front': go('FrontView') # This will become '' when it is done.
'about': go('AboutView')
@ -73,7 +74,8 @@ module.exports = class CocoRouter extends Backbone.Router
'multiplayer': go('MultiplayerView')
'play': go('play/MainPlayView')
'play': go('play/MainPlayView') # This will become 'play-old' or something.
'play-hero': go('play/WorldMapView') # This will become 'play' when it is done.
'play/ladder/:levelID': go('play/ladder/LadderView')
'play/ladder': go('play/ladder/MainLadderView')
'play/level/:levelID': go('play/level/PlayLevelView')

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

View file

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View file

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View file

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View file

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

View file

@ -131,6 +131,19 @@
spectate: "Spectate"
players: "players"
hours_played: "hours played"
items: "Items"
heroes: "Heroes"
achievements: "Achievements"
account: "Account"
settings: "Settings"
items:
armor: "Armor"
hands: "Hands"
accessories: "Accessories"
books: "Books"
minions: "Minions"
misc: "Misc"
contact:
contact_us: "Contact CodeCombat"
@ -878,7 +891,6 @@
tutorial_play_first: "Play the Tutorial first."
simple_ai: "Simple AI"
warmup: "Warmup"
vs: "VS"
friends_playing: "Friends Playing"
log_in_for_friends: "Log in to play with your friends!"
social_connect_blurb: "Connect and play against your friends!"

View file

@ -104,7 +104,7 @@ NoteGroupSchema = c.object {title: 'Note Group', description: 'A group of notes
target: c.shortString(title: 'Target', description: 'Target highlight element DOM selector string.')
delay: {type: 'integer', minimum: 0, title: 'Delay', description: 'Show the highlight after this many milliseconds. Doesn\'t affect the dim shade cutout highlight method.'}
offset: _.extend _.cloneDeep(PointSchema), {title: 'Offset', description: 'Pointing arrow tip offset in pixels from the default target.', format: null}
rotation: {type: 'number', minimum: 0, title: 'Rotation', description: 'Rotation of the pointing arrow, in radians. PI / 2 points left, PI points up, etc.'}
rotation: {type: 'number', minimum: 0, title: 'Rotation', description: 'Rotation of the pointing arrow, in radians. PI / 2 points left, PI points up, etc.', format: 'radians'}
sides: c.array {title: 'Sides', description: 'Which sides of the target element to point at.'}, {type: 'string', 'enum': ['left', 'right', 'top', 'bottom'], title: 'Side', description: 'A side of the target element to point at.'}
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.'}

View file

@ -0,0 +1,30 @@
@import "bootstrap/mixins"
@import "bootstrap/variables"
#front-view
h1
text-align: center
margin-top: 0
.platform-choices
a
text-align: center
.panel
@include transition(background-color 0.5s ease)
&:hover
text-decoration: none
.panel
background-color: rgb(230, 230, 255)
.platform-ios
img
transform: scaleY(-1)
@media only screen and (max-width: 800px)
#front-view
#site-slogan
font-size: 30px
margin-bottom: 30px

View file

@ -47,3 +47,17 @@
color: black
text-shadow: 0 1px 0 white
.modal.play-modal
.modal-header
border: 0
text-align: center
padding: 0
margin: 0 25px
h3
margin-bottom: 0
.modal-body
padding-top: 0

View file

@ -16,8 +16,7 @@
height: 100px
overflow: hidden
background: white
border: 1px solid #333
border-radius: 5px
border-radius: 8px
position: relative
-webkit-transition: opacity 0.3s ease-in-out
-moz-transition: opacity 0.3s ease-in-out
@ -46,15 +45,15 @@
.my-team-icon
height: 60px
position: relative
top: -10px
left: 10px
top: -3.5px
left: 13.5px
z-index: 0
.opponent-team-icon
height: 60px
position: relative
top: 10px
right: 10px
top: 16.5px
right: 13.5px
z-index: 0
float: right
-moz-transform: scaleX(-1)
@ -78,19 +77,19 @@
z-index: 1
.name-label
border-bottom: 20px solid lightslategray
height: 0
height: 20px
width: 40%
position: absolute
bottom: 0
color: black
color: white
font-weight: bold
text-shadow: -1px -1px 0px black
text-align: center
z-index: 2
span
position: relative
top: 1px
top: 4px
.code-language
position: absolute
@ -116,9 +115,6 @@
right: 3px
.difficulty
border-top: 25px solid darkgray
border-left: 20px solid transparent
border-right: 20px solid transparent
height: 0
width: 30%
position: absolute
@ -131,17 +127,21 @@
span
position: relative
top: -25px
top: 6px
.play-option
background-image: url(/images/pages/play/ladder/warmup_button.png)
.easy-option .difficulty
border-top: 25px solid limegreen
.easy-option
background-image: url(/images/pages/play/ladder/easy_button.png)
.medium-option .difficulty
border-top: 25px solid darkorange
.medium-option
background-image: url(/images/pages/play/ladder/medium_button.png)
.hard-option .difficulty
border-top: 25px solid black
color: white
.hard-option
background-image: url(/images/pages/play/ladder/hard_button.png)
.difficulty
color: white
.vs
position: absolute

View file

@ -0,0 +1,4 @@
#play-account-modal
.account-view
color: black

View file

@ -0,0 +1,4 @@
#play-achievements-modal
.achievement-view
color: black

View file

@ -0,0 +1,4 @@
#play-heroes-modal
.hero-view
color: black

View file

@ -0,0 +1,8 @@
#play-items-modal
.item-view
width: 420px
float: left
background-color: white
border-radius: 6px
margin: 5px

View file

@ -0,0 +1,4 @@
#play-settings-modal
.settings-view
color: black

View file

@ -0,0 +1,151 @@
@import "app/styles/bootstrap/mixins"
@import "app/styles/bootstrap/variables"
$forestMapWidth: 2500
$forestMapHeight: 1536
$forestMapSeaBackground: #71bad0
$levelDotWidth: 2%
$levelDotHeight: $levelDotWidth * $forestMapWidth / $forestMapHeight
$levelDotZ: $levelDotHeight * 0.25
$levelDotHoverZ: $levelDotZ * 2
$levelDotShadowWidth: 0.8 * $levelDotWidth
$levelDotShadowHeight: 0.8 * $levelDotHeight
$levelClickRadius: 40px
$gameControlSize: 80px
$gameControlMargin: 40px
#world-map-view
width: 100%
height: 100%
background-color: $forestMapSeaBackground
.map
//background: white url("/images/pages/play/map_forest.jpg") no-repeat center center
position: relative
.map-background
width: 100%
height: 100%
.level, .level-shadow
position: absolute
border-radius: 100%
transform: scaleY(0.75)
.level
z-index: 2
width: $levelDotWidth
height: $levelDotHeight
margin-left: -0.5 * $levelDotWidth
margin-bottom: -$levelDotHeight / 3 + $levelDotZ
border: 2px groove white
@include transition(margin-bottom 0.5s ease)
&.disabled
opacity: 0.7
&.first
width: 2 * $levelDotWidth
height: 2 * $levelDotHeight
margin-left: -0.5 * 2 * $levelDotWidth
margin-bottom: -2 * $levelDotHeight / 3 + 2 * $levelDotZ
.level-shadow
z-index: 1
width: $levelDotShadowWidth
height: $levelDotShadowHeight
margin-left: -0.5 * $levelDotShadowWidth
margin-bottom: -$levelDotShadowHeight / 3
background-color: black
box-shadow: 0px 0px 10px black
@include opacity(0.75)
&.first
width: 2 * $levelDotShadowWidth
height: 2 * $levelDotShadowHeight
margin-left: -0.5 * 2 * $levelDotShadowWidth
margin-bottom: -2 * $levelDotShadowHeight / 3
.level:hover
margin-bottom: -$levelDotHeight / 3 + $levelDotHoverZ
.level
a
display: block
padding: $levelClickRadius
margin-left: -0.5 * $levelClickRadius
margin-top: -0.5 * $levelClickRadius
border-radius: $levelClickRadius
&.first a
padding: 2 * $levelClickRadius
margin-left: 2 * -0.5 * $levelClickRadius
margin-top: 2 * -0.5 * $levelClickRadius
border-radius: 2 * $levelClickRadius
.level-info-container
display: none
position: absolute
z-index: 3
padding: 10px 10px 30px 10px
border-image: url(/images/level/popover_background.png) 18 fill round
border-width: 15px
.level-image
float: left
margin-right: 20px
.level-info.complete h3:after
content: " - Complete!"
color: green
.level-info.started h3:after
content: " - Started"
color: desaturate(green, 50%)
.level-info
width: 330px
float: left
h3
margin-top: 0
margin-bottom: 0px
.level-description
color: black
text-shadow: 0 1px 0 white
.campaign-label
text-shadow: 0 1px 0 white
.game-controls
position: absolute
right: 1%
bottom: 1%
button.btn
&:not(:first-child)
margin-left: $gameControlMargin
width: $gameControlSize
height: $gameControlSize
background: url(/images/pages/play/menu_icons.png) no-repeat
background-size: cover
@include transition(0.5s ease)
box-shadow: 0 0 0 #bbf
border: 0
border-radius: 12px
&:hover
box-shadow: 0 0 12px #bbf
&:active
box-shadow: 0 0 20px white
&.heroes
background-position-x: -1 * $gameControlSize
&.achievements
background-position-x: -2 * $gameControlSize
&.account
background-position-x: -3 * $gameControlSize
&.settings
background-position-x: -4 * $gameControlSize

View file

@ -0,0 +1,28 @@
extends /templates/base
block content
.page-header
h1#site-slogan
span(data-i18n="home.slogan") Learn to Code by Playing a Game
small.spl.spr -
small free!
.row.platform-choices
.col-xs-6.platform-ios
a(href="#")
.panel.panel-default
.panel-body
h1 Play on iPad
img.img-responsive(src="/images/pages/front/play_web.jpg")
p.lead Get the app!
.col-xs-6.platform-web
a(href="/play-hero")
.panel.panel-default
.panel-body
h1 Play on web
img.img-responsive(src="/images/pages/front/play_web.jpg")
p.lead Play right now!
.clearfix

26
app/templates/front.jade Normal file
View file

@ -0,0 +1,26 @@
extends /templates/base
block content
.page-header
h1#site-slogan
span(data-i18n="home.slogan") Learn to Code by Playing a Game
small.spl.spr -
small free!
.row.platform-choices
.col-xs-6.platform-ios
a(href="#")
.panel.panel-default
.panel-body
h1 Play on iPad
em - get the app!
.col-xs-6.platform-web
a(href="/play")
.panel.panel-default
.panel-body
h1 Play on web
em - play right now!
.clearfix

View file

@ -6,14 +6,20 @@
if closeButton
.button.close(type="button", data-dismiss="modal", aria-hidden="true") ×
block modal-header-content
h3 man bites God
if headerContent
h3!= headerContent
else
h3 man bites God
block modal-body
.modal-body
block modal-body-content
p Man Bites God are the bad boys of the Melbourne live music and comedy scene. It is like being drowned in a bathtub of harmony.
img(src="http://www.manbitesgod.com/images/picturecoupleb.jpg")
img(src="http://www.manbitesgod.com/images/manrantb.jpg")
if bodyContent
div!= bodyContent
else
p Man Bites God are the bad boys of the Melbourne live music and comedy scene. It is like being drowned in a bathtub of harmony.
img(src="http://www.manbitesgod.com/images/picturecoupleb.jpg")
img(src="http://www.manbitesgod.com/images/manrantb.jpg")
.modal-body.wait.secret
block modal-body-wait-content

View file

@ -37,7 +37,6 @@ block modal-body-content
//span.code-language(style="background-image: url(/images/common/code_languages/javascript_small.png)")
div.difficulty
span(data-i18n="ladder.warmup") Warmup
div(data-i18n="ladder.vs").vs VS
if challengers.easy
a(href="/play/level/#{levelID}?team=#{teamID}&opponent=#{challengers.easy.sessionID}")
@ -54,7 +53,6 @@ block modal-body-content
span.code-language(style="background-image: url(/images/common/code_languages/" + challengers.easy.codeLanguage + "_small.png)")
div.difficulty
span(data-i18n="general.easy") Easy
div(data-i18n="ladder.vs").vs VS
if challengers.medium
a(href="/play/level/#{levelID}?team=#{teamID}&opponent=#{challengers.medium.sessionID}")
@ -71,7 +69,6 @@ block modal-body-content
span.code-language(style="background-image: url(/images/common/code_languages/" + challengers.medium.codeLanguage + "_small.png)")
div.difficulty
span(data-i18n="general.medium") Medium
div(data-i18n="ladder.vs").vs VS
if challengers.hard
a(href="/play/level/#{levelID}?team=#{teamID}&opponent=#{challengers.hard.sessionID}")
@ -88,6 +85,5 @@ block modal-body-content
span.code-language(style="background-image: url(/images/common/code_languages/" + challengers.hard.codeLanguage + "_small.png)")
div.difficulty
span(data-i18n="general.hard") Hard
div(data-i18n="ladder.vs").vs VS
block modal-footer

View file

@ -0,0 +1,7 @@
extends /templates/modal/modal_base
block modal-header-content
h3(data-i18n="play.account") Account
block modal-body-content
p TODO: show all dem account

View file

@ -0,0 +1,7 @@
extends /templates/modal/modal_base
block modal-header-content
h3(data-i18n="play.achievements") Achievements
block modal-body-content
p TODO: show all dem achievements

View file

@ -0,0 +1,7 @@
extends /templates/modal/modal_base
block modal-header-content
h3(data-i18n="play.heroes") Heroes
block modal-body-content
p TODO: show all dem heroes

View file

@ -0,0 +1,17 @@
extends /templates/modal/modal_base
block modal-header-content
h3(data-i18n="play.items") Items
block modal-body-content
ul.nav.nav-tabs
for slotGroup, index in slotGroupsArray
li(class=index ? "" : "active")
a(href="#slot-group-" + slotGroup, data-toggle="tab")
span= slotGroupsNames[index]
.tab-content
for slotGroup, index in slotGroupsArray
.tab-pane(id="slot-group-" + slotGroup, class=index ? "" : "active")
h3 buy some #{slotGroupsNames[index]} yo:
for item in slotGroups[slotGroup]
.replace-me(data-item-id=item.id)

View file

@ -0,0 +1,7 @@
extends /templates/modal/modal_base
block modal-header-content
h3(data-i18n="play.settings") Settings
block modal-body-content
p TODO: show all dem settings

View file

@ -0,0 +1,34 @@
.map
img.map-background(src="/images/pages/play/map_forest.jpg", alt="")
each campaign in campaigns
each level in campaign.levels
div(style="left: #{level.x}%; bottom: #{level.y}%; background-color: #{campaign.color}", class="level" + (level.first ? " first" : "") + (level.disabled ? " disabled" : ""), data-level-id=level.id)
a(href=level.disabled ? "/play" : "/play/#{level.levelPath || 'level'}/#{level.id}", disabled=level.disabled, class=levelStatusMap[level.id] || "")
div(style="left: #{level.x}%; bottom: #{level.y}%", class="level-shadow" + (level.first ? " first" : ""))
.level-info-container(data-level-id=level.id)
if level.image
img.level-image(src="#{level.image}", alt="#{level.name}")
else
img.level-image(src="/images/generic-icon.png", alt="#{level.name}")
div(class="level-info " + (levelStatusMap[level.id] || ""))
h3= level.name + (level.disabled ? " (Coming soon!)" : "")
.level-description= level.description
span(data-i18n="play.level_difficulty") Difficulty:
each i in Array(level.difficulty)
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
.campaign-label(style="color: #{campaign.color}")= campaign.name
.game-controls
button.btn.items(data-toggle='coco-modal', data-target='play/modal/PlayItemsModal', data-i18n="[title]play.items")
button.btn.heroes(data-toggle='coco-modal', data-target='play/modal/PlayHeroesModal', data-i18n="[title]play.heroes")
button.btn.achievements(data-toggle='coco-modal', data-target='play/modal/PlayAchievementsModal', data-i18n="[title]play.achievements")
button.btn.account(data-toggle='coco-modal', data-target='play/modal/PlayAccountModal', data-i18n="[title]play.account")
button.btn.settings(data-toggle='coco-modal', data-target='play/modal/PlaySettingsModal', data-i18n="[title]play.settings")

View file

@ -0,0 +1,34 @@
RootView = require 'views/kinds/RootView'
template = require 'templates/front-view'
{me} = require '/lib/auth'
ModalView = require 'views/kinds/ModalView'
module.exports = class FrontView extends RootView
id: 'front-view'
template: template
events:
'click .platform-ios a': 'onIOSClicked'
getRenderData: ->
c = super()
if $.browser
majorVersion = $.browser.versionNumber
c.isOldBrowser = true if $.browser.mozilla && majorVersion < 21
c.isOldBrowser = true if $.browser.chrome && majorVersion < 17
c.isOldBrowser = true if $.browser.safari && majorVersion < 6
else
console.warn 'no more jquery browser version...'
c
afterRender: ->
super()
onIOSClicked: (e) ->
header = 'Sorry, the iPad app isn\'t ready yet'
body = '''
<p class="lead">We are working on it!</p>
<p>For now, try playing on the web, and totally sign up (with emails enabled) so you can be the first to hear when it is ready.</p>
'''
notImplementedModal = new ModalView headerContent: header, bodyContent: body
@openModalView notImplementedModal

View file

@ -41,6 +41,7 @@ module.exports = class GameMenuModal extends ModalView
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-tab-switch', volume: 1
onHidden: ->
super()
subview.onHidden?() for subviewKey, subview of @subviews
me.patch()
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-close', volume: 1

View file

@ -176,7 +176,7 @@ module.exports = class CocoView extends Backbone.View
target = elem.data('target')
Modal = require 'views/'+target
e.stopPropagation()
@openModalView new Modal
@openModalView new Modal supermodel: @supermodal
openModalView: (modalView, softly=false) ->
return if waitingModal # can only have one waiting at once

View file

@ -7,6 +7,7 @@ module.exports = class ModalView extends CocoView
modalWidthPercent: null
plain: false
instant: false
template: require 'templates/modal/modal_base'
events:
'click a': 'toggleModal'
@ -26,6 +27,8 @@ module.exports = class ModalView extends CocoView
getRenderData: (context={}) ->
context = super(context)
context.closeButton = @closeButton
context.headerContent = @options.headerContent
context.bodyContent = @options.bodyContent
context
subscriptions:

View file

@ -0,0 +1,472 @@
RootView = require 'views/kinds/RootView'
template = require 'templates/play/world-map-view'
LevelSession = require 'models/LevelSession'
CocoCollection = require 'collections/CocoCollection'
AudioPlayer = require 'lib/AudioPlayer'
class LevelSessionsCollection extends CocoCollection
url: ''
model: LevelSession
constructor: (model) ->
super()
@url = "/db/user/#{me.id}/level.sessions?project=state.complete,levelID"
module.exports = class MainPlayView extends RootView
id: 'world-map-view'
template: template
events:
'click .map': 'onClickMap'
'click .game-controls button': 'onClickGameControl'
'mouseenter .level a': 'onMouseEnterLevel'
'mouseleave .level a': 'onMouseLeaveLevel'
'mousemove .map': 'onMouseMoveMap'
constructor: (options) ->
super options
@levelStatusMap = {}
@levelPlayCountMap = {}
@sessions = @supermodel.loadCollection(new LevelSessionsCollection(), 'your_sessions', null, 0).model
@listenToOnce @sessions, 'sync', @onSessionsLoaded
@getLevelPlayCounts()
$(window).on 'resize', @onWindowResize
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={}) ->
context = super(context)
context.campaigns = campaigns
for campaign in context.campaigns
for level in campaign.levels
level.x ?= 10 + 80 * Math.random()
level.y ?= 10 + 80 * Math.random()
context.levelStatusMap = @levelStatusMap
context.levelPlayCountMap = @levelPlayCountMap
context
afterRender: ->
super()
@onWindowResize()
_.defer => @$el.find('.game-controls button').tooltip() # Have to defer or i18n doesn't take effect.
onSessionsLoaded: (e) ->
for session in @sessions.models
@levelStatusMap[session.get('levelID')] = if session.get('state')?.complete then 'complete' else 'started'
@render()
onClickMap: (e) ->
# 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"
onClickGameControl: (e) ->
onMouseEnterLevel: (e) ->
levelID = $(e.target).parents('.level').data('level-id')
@$levelInfo = @$el.find(".level-info-container[data-level-id=#{levelID}]").show()
@adjustLevelInfoPosition e
onMouseLeaveLevel: (e) ->
levelID = $(e.target).parents('.level').data('level-id')
@$el.find(".level-info-container[data-level-id='#{levelID}']").hide()
onMouseMoveMap: (e) ->
@adjustLevelInfoPosition e
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) =>
forestMapWidth = 2401
forestMapHeight = 1536
aspectRatio = forestMapWidth / forestMapHeight
pageWidth = $(window).width()
pageHeight = $(window).height()
widthRatio = pageWidth / forestMapWidth
heightRatio = pageHeight / forestMapHeight
if widthRatio > heightRatio
resultingWidth = pageWidth
resultingHeight = resultingWidth / aspectRatio
else
resultingHeight = pageHeight
resultingWidth = resultingHeight * aspectRatio
resultingMarginX = (pageWidth - resultingWidth) / 2
resultingMarginY = (pageHeight - resultingHeight) / 2
@$el.find('.map').css(width: resultingWidth, height: resultingHeight, 'margin-left': resultingMarginX, 'margin-top': resultingMarginY)
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.'
first: true
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
}
]
campaigns = [
{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)"}
{id: 'classic' ,name: 'Classic Algorithms', description: '... in which you learn the most popular algorithms in Computer Science.', levels: classicAlgorithms, color: "rgb(110, 80, 120)"}
{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)"}
]

View file

@ -0,0 +1,28 @@
ModalView = require 'views/kinds/ModalView'
template = require 'templates/play/modal/play-account-modal'
module.exports = class PlayAccountModal extends ModalView
className: 'modal fade play-modal'
template: template
modalWidthPercent: 90
id: 'play-account-modal'
#instant: true
#events:
# 'change input.select': 'onSelectionChanged'
constructor: (options) ->
super options
getRenderData: (context={}) ->
context = super(context)
context
afterRender: ->
super()
return unless @supermodel.finished()
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-open', volume: 1
onHidden: ->
super()
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-close', volume: 1

View file

@ -0,0 +1,35 @@
ModalView = require 'views/kinds/ModalView'
template = require 'templates/play/modal/play-achievements-modal'
CocoCollection = require 'collections/CocoCollection'
Achievement = require 'models/Achievement'
#AchievementView = require 'views/game-menu/AchievementView'
module.exports = class PlayAchievementsModal extends ModalView
className: 'modal fade play-modal'
template: template
modalWidthPercent: 90
id: 'play-achievements-modal'
#instant: true
#events:
# 'change input.select': 'onSelectionChanged'
constructor: (options) ->
super options
#@achievements = new CocoCollection([], {model: Achievement})
#@achievements.url = '/db/thang.type?view=achievements&project=name,description,components,original,rasterIcon'
#@supermodel.loadCollection(@achievements, 'achievements')
getRenderData: (context={}) ->
context = super(context)
#context.achievements = @achievements.models
context
afterRender: ->
super()
return unless @supermodel.finished()
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-open', volume: 1
onHidden: ->
super()
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-close', volume: 1

View file

@ -0,0 +1,47 @@
ModalView = require 'views/kinds/ModalView'
template = require 'templates/play/modal/play-heroes-modal'
CocoCollection = require 'collections/CocoCollection'
ThangType = require 'models/ThangType'
#HeroView = require 'views/game-menu/HeroView'
module.exports = class PlayHeroesModal extends ModalView
className: 'modal fade play-modal'
template: template
modalWidthPercent: 90
id: 'play-heroes-modal'
#instant: true
#events:
# 'change input.select': 'onSelectionChanged'
constructor: (options) ->
super options
@heroes = new CocoCollection([], {model: ThangType})
@heroes.url = '/db/thang.type?view=heroes&project=name,description,components,original,rasterIcon'
@supermodel.loadCollection(@heroes, 'heroes')
getRenderData: (context={}) ->
context = super(context)
context.heroes = @heroes.models
context
afterRender: ->
super()
return unless @supermodel.finished()
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-open', volume: 1
#@addHeroViews()
onHidden: ->
super()
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-close', volume: 1
#addHeroViews: ->
# keys = (hero.id for hero in @heroes.models)
# heroMap = _.zipObject keys, @heroes.models
# for heroStub in @$el.find('.replace-me')
# heroID = $(heroStub).data('hero-id')
# hero = heroMap[heroID]
# heroView = new HeroView({hero: hero, includes: {name: true, stats: true, props: true}})
# heroView.render()
# $(heroStub).replaceWith(heroView.$el)
# @registerSubView(heroView)

View file

@ -0,0 +1,66 @@
ModalView = require 'views/kinds/ModalView'
template = require 'templates/play/modal/play-items-modal'
CocoCollection = require 'collections/CocoCollection'
ThangType = require 'models/ThangType'
ItemView = require 'views/game-menu/ItemView'
module.exports = class PlayItemsModal extends ModalView
className: 'modal fade play-modal'
template: template
modalWidthPercent: 90
id: 'play-items-modal'
#instant: true
slotGroups:
armor: ['torso', 'head', 'gloves', 'feet']
hands: ['right-hand', 'left-hand']
accessories: ['eyes', 'neck', 'left-ring', 'right-ring', 'waist']
books: ['programming-book', 'spellbook']
minions: ['minion', 'pet']
misc: ['misc-0', 'misc-1', 'misc-2', 'misc-3', 'misc-4']
#events:
# 'change input.select': 'onSelectionChanged'
constructor: (options) ->
super options
@items = new CocoCollection([], {model: ThangType})
@items.url = '/db/thang.type?view=items&project=name,description,components,original,rasterIcon'
@supermodel.loadCollection(@items, 'items')
groupItems: ->
groups = {}
for item in @items.models
itemSlots = item.getAllowedSlots()
for group, groupSlots of @slotGroups
if _.find itemSlots, ((slot) -> slot in groupSlots)
groups[group] ?= []
groups[group].push item
groups
getRenderData: (context={}) ->
context = super(context)
context.slotGroups = @groupItems()
context.slotGroupsArray = _.keys context.slotGroups
context.slotGroupsNames = ($.i18n.t "items.#{slotGroup}" for slotGroup in context.slotGroupsArray)
context
afterRender: ->
super()
return unless @supermodel.finished()
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-open', volume: 1
@addItemViews()
onHidden: ->
super()
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-close', volume: 1
addItemViews: ->
keys = (item.id for item in @items.models)
itemMap = _.zipObject keys, @items.models
for itemStub in @$el.find('.replace-me')
itemID = $(itemStub).data('item-id')
item = itemMap[itemID]
itemView = new ItemView({item: item, includes: {name: true, stats: true, props: true}})
itemView.render()
$(itemStub).replaceWith(itemView.$el)
@registerSubView(itemView)

View file

@ -0,0 +1,28 @@
ModalView = require 'views/kinds/ModalView'
template = require 'templates/play/modal/play-settings-modal'
module.exports = class PlaySettingsModal extends ModalView
className: 'modal fade play-modal'
template: template
modalWidthPercent: 90
id: 'play-settings-modal'
#instant: true
#events:
# 'change input.select': 'onSelectionChanged'
constructor: (options) ->
super options
getRenderData: (context={}) ->
context = super(context)
context
afterRender: ->
super()
return unless @supermodel.finished()
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-open', volume: 1
onHidden: ->
super()
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'game-menu-close', volume: 1