Preparing for being able to use multiple hero classes. Deleted old ChooseHeroView. Improvements to missile trajectories.

This commit is contained in:
Nick Winter 2014-11-11 16:36:44 -08:00
parent 12e860a985
commit 0af7b4d5b7
14 changed files with 66 additions and 586 deletions

View file

@ -210,12 +210,12 @@ module.exports = LevelOptions =
'drop-the-flag':
requiredGear: {'programming-book': 'programmaticon-i', feet: 'leather-boots', flag: 'basic-flags', eyes: 'wooden-glasses', 'right-hand': 'builders-hammer'}
restrictedGear: {'right-hand': 'longsword'}
'rich-forager':
requiredGear: {'programming-book': 'programmaticon-i', feet: 'leather-boots', flag: 'basic-flags', eyes: 'wooden-glasses', torso: 'leather-tunic', 'right-hand': 'longsword', 'left-hand': 'bronze-shield'}
restrictedGear: {'right-hand': 'builders-hammer'}
'deadly-pursuit':
requiredGear: {'programming-book': 'programmaticon-i', feet: 'leather-boots', flag: 'basic-flags', eyes: 'wooden-glasses', 'right-hand': 'builders-hammer'}
restrictedGear: {'right-hand': 'longsword'}
'rich-forager':
requiredGear: {'programming-book': 'programmaticon-i', feet: 'leather-boots', flag: 'basic-flags', eyes: 'wooden-glasses', torso: 'leather-tunic', 'right-hand': 'longsword', 'left-hand': 'bronze-shield'}
restrictedGear: {'right-hand': 'builders-hammer'}
'multiplayer-treasure-grove':
requiredGear: {'programming-book': 'programmaticon-i', feet: 'leather-boots', flag: 'basic-flags', eyes: 'wooden-glasses', torso: 'leather-tunic'}
restrictedGear: {}

View file

@ -59,7 +59,8 @@ module.exports = Lank = class Lank extends CocoClass
constructor: (@thangType, options) ->
super()
@isMissile = @thangType.get('name') in ['Arrow', 'Spear']
spriteName = @thangType.get('name')
@isMissile = /(Missile|Arrow|Spear)/.test(spriteName) and not /Tower/.test(spriteName)
@options = _.extend($.extend(true, {}, @options), options)
@setThang @options.thang
if @thang?

View file

@ -97,19 +97,23 @@ module.exports = class Level extends CocoModel
placeholdersUsed[placeholderComponent.original] = true
placeholderConfig = placeholderComponent.config ? {}
if placeholderConfig.pos # Pull in Physical pos x and y
levelThangComponent.config ?= {}
levelThangComponent.config.pos ?= {}
levelThangComponent.config.pos.x = placeholderConfig.pos.x
levelThangComponent.config.pos.y = placeholderConfig.pos.y
levelThangComponent.config.rotation = placeholderConfig.rotation
else if placeholderConfig.team # Pull in Allied team
levelThangComponent.config ?= {}
levelThangComponent.config.team = placeholderConfig.team
else if placeholderConfig.significantProperty # For levels where we cheat on what counts as an enemy
levelThangComponent.config ?= {}
levelThangComponent.config.significantProperty = placeholderConfig.significantProperty
else if placeholderConfig.programmableMethods
# Take the ThangType default Programmable and merge level-specific Component config into it
copy = $.extend true, {}, placeholderConfig
levelThangComponent.config = _.merge copy, levelThangComponent.config
else if placeholderConfig.extraHUDProperties
levelThangComponent.config ?= {}
levelThangComponent.config.extraHUDProperties = _.union(levelThangComponent.config.extraHUDProperties ? [], placeholderConfig.extraHUDProperties)
if isHero

View file

@ -19,6 +19,7 @@ module.exports = class ThangType extends CocoModel
'robot-walker': '5301696ad82649ec2c0c9b0d'
'michael-heasell': '53e126a4e06b897606d38bef'
'ian-elliott': '53e12be0d042f23505c3023b'
'ninja': '52fc0ed77e01835453bd8f6c'
@items:
'simple-boots': '53e237bf53457600003e3f05'
urlRoot: '/db/thang.type'
@ -305,7 +306,7 @@ module.exports = class ThangType extends CocoModel
['Warrior', 'Ranger', 'Wizard']
getHeroStats: ->
# Translate from raw hero properties into appropriate display values for the ChooseHeroView.
# Translate from raw hero properties into appropriate display values for the PlayHeroesModal.
# Adapted from https://docs.google.com/a/codecombat.com/spreadsheets/d/1BGI1bzT4xHvWA81aeyIaCKWWw9zxn7-MwDdydmB5vw4/edit#gid=809922675
return unless heroClass = @get('heroClass')
components = @get('components') or []

View file

@ -85,7 +85,10 @@ module.exports = class User extends CocoModel
gemsSpent = @get('spent') ? 0
gemsEarned + gemsPurchased - gemsSpent
heroes: -> (me.get('earned')?.heroes ? []).concat(me.get('purchased')?.heroes ? [])
heroes: ->
heroes = (me.get('earned')?.heroes ? []).concat(me.get('purchased')?.heroes ? []).concat([ThangType.heroes.captain, ThangType.heroes.knight])
heroes = heroes.concat [ThangType.heroes.ninja, ThangType.heroes.librarian] if me.isAdmin()
heroes
items: -> (me.get('earned')?.items ? []).concat(me.get('purchased')?.items ? []).concat([ThangType.items['simple-boots']])
levels: -> (me.get('earned')?.levels ? []).concat(me.get('purchased')?.levels ? [])
ownsHero: (heroOriginal) -> heroOriginal in @heroes()

View file

@ -1,176 +0,0 @@
@import "app/styles/mixins"
@import "app/styles/bootstrap/variables"
$maxHeroPortraitSize: 100px
$minHeroPortraitSize: 50px
$heroCanvasHeight: 265px
#choose-hero-view
#hero-carousel
margin: 0 auto 20px auto
.carousel-indicator-container
height: $maxHeroPortraitSize
text-align: center
width: 100%
margin-bottom: 10px
margin-top: 10px
position: relative
z-index: 1
.carousel-indicators
position: static
height: $maxHeroPortraitSize
width: 100%
margin-left: 0
background-repeat: no-repeat
.hero-indicator
width: $maxHeroPortraitSize
height: $maxHeroPortraitSize
margin: 0px 3px
border: 2px solid black
border-radius: 2px
position: relative
.hero-avatar
width: 100%
height: 100%
background-size: contain
&.initialized
@include transition(0.5s ease)
&.active
border-color: gold
&.locked
.hero-avatar
@include filter(contrast(50%) brightness(65%))
.lock-indicator
position: absolute
width: 40%
left: 30%
top: 30%
@include filter(invert(90%))
.hero-item
&.locked
@include opacity(0.6)
canvas, .hero-feature-image, .hero-stats
width: 45%
height: $heroCanvasHeight
float: left
canvas, .hero-feature-image
margin-left: 5%
.hero-feature-image
display: none
text-align: center
img
height: $heroCanvasHeight
.hero-stats
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.6)
.hero-class, .hero-status, .hero-skills
font-size: 14px
.hero-stat
margin: 10px 0
.progress
width: 90%
text-align: center
height: 26px
position: relative
margin: 0
.hero-stat-label
position: absolute
width: 100%
font-size: 18px
text-transform: uppercase
color: white
text-shadow: 0px 1px 0px black, 0px -1px 0px black, -1px 0px 0px black, 1px 0px 0px black
.carousel-control.left
border-top-left-radius: 10px
border-bottom-left-radius: 10px
.carousel-control.right
border-top-right-radius: 10px
border-bottom-right-radius: 10px
.carousel-control
height: 75%
top: 30%
width: 10%
.select-group
display: inline-block
min-height: 20px
margin-top: 10px
margin-bottom: 10px
padding-left: 20px
vertical-align: middle
label
margin-right: 20px
margin-bottom: 0
text-align: top
&[for="option-code-language"]
vertical-align: top
margin-top: 10px
select
font-size: 18px
.fancy-select
display: inline-block
.trigger, .options
text-transform: none
background-color: #def
.options
padding-left: 5px
overflow: visible
.selected
color: #111
.hover
color: #111
background-color: #abc
.options
li
padding-left: 40px
background: transparent url(/images/common/code_languages/javascript_small.png) no-repeat left center
background-size: 32px 32px
margin-bottom: -1px
&[data-value="python"]
background-image: url(/images/common/code_languages/python_small.png)
&[data-value="coffeescript"]
background-image: url(/images/common/code_languages/coffeescript_small.png)
&[data-value="clojure"]
background-image: url(/images/common/code_languages/clojure_small.png)
&[data-value="lua"]
background-image: url(/images/common/code_languages/lua_small.png)
&[data-value="io"]
background-image: url(/images/common/code_languages/io_small.png)
body.ipad #choose-hero-view
// iPad is Python-only for now.
.form
display: none

View file

@ -1,65 +0,0 @@
#hero-carousel.carousel.slide(data-interval=0)
.carousel-indicator-container
ol.carousel-indicators
for hero, index in heroes
li(data-hero-id=hero.get('original'), title=hero.get('name'), data-slide-to=index, data-target="#hero-carousel", class="hero-indicator" + (hero.locked ? " locked" : ""))
.hero-avatar
if hero.locked
img.lock-indicator(src="/images/pages/game-menu/lock.png")
.carousel-inner
for hero in heroes
- var info = heroInfo[hero.get('slug')]
div(class="item hero-item" + (hero.locked ? " locked" : ""), data-hero-id=hero.get('original'))
canvas.hero-canvas
.hero-feature-image
img
.hero-stats
h2= info.fullName
//.hero-description= info.description // Not until we have written the descriptions.
.hero-status
span(data-i18n="choose_hero.status") Status
span.spr :
if hero.locked
| #{info.status}
else
| Available
.hero-class
span(data-i18n="choose_hero.weapons") Weapons
span.spr :
| #{info.weapons}
for statInfo in [{name: 'attack', color: 'danger'}, {name: 'health', color: 'info'}, {name: 'speed', color: 'success'}]
- var tooltip = "";
- if (statInfo.name == 'attack')
- tooltip = Math.round(100 * info[statInfo.name + 'Factor']) + '% ' + info.class.toLowerCase() + ' weapon damage';
- else if (statInfo.name == 'health')
- tooltip = Math.round(100 * info[statInfo.name + 'Factor']) + '% ' + info.class.toLowerCase() + ' armor health';
- else if (statInfo.name == 'speed')
- tooltip = info.speedAbsolute + ' meters per second';
.hero-stat(title=tooltip)
.progress
div(class="progress-bar progress-bar-" + statInfo.color, style="width: " + (10 * info[statInfo.name]) + "%")
.hero-stat-label
span(data-i18n="choose_hero." + statInfo.name)
span.spr :
| #{info[statInfo.name]}
if info.skills
.hero-skills
span(data-i18n="choose_hero.skills") Skills
span.spr :
for skill in info.skills
code.spl.spr= skill
a.carousel-control.left(role="button", data-slide="prev", href="#hero-carousel")
span.glyphicon.glyphicon-chevron-left
a.carousel-control.right(role="button", data-slide="next", href="#hero-carousel")
span.glyphicon.glyphicon-chevron-right
.form
.form-group.select-group
//span.help-block(data-i18n="choose_hero.programming_language_description") Which programming language do you want to use?
label.control-label(for="option-code-language", data-i18n="choose_hero.programming_language") Programming Language
select#option-code-language(name="code-language")
for option in codeLanguages
option(value=option.id, selected=codeLanguage === option.id)= option.name

View file

@ -1,293 +0,0 @@
CocoView = require 'views/kinds/CocoView'
template = require 'templates/game-menu/choose-hero-view'
{me} = require 'lib/auth'
ThangType = require 'models/ThangType'
CocoCollection = require 'collections/CocoCollection'
SpriteBuilder = require 'lib/sprites/SpriteBuilder'
AudioPlayer = require 'lib/AudioPlayer'
module.exports = class ChooseHeroView extends CocoView
id: 'choose-hero-view'
className: 'tab-pane'
template: template
events:
'slide.bs.carousel #hero-carousel': 'onHeroChanged'
'change #option-code-language': 'onCodeLanguageChanged'
shortcuts:
'left': -> @$el.find('#hero-carousel').carousel('prev') if @heroes.models.length and not @$el.hasClass 'secret'
'right': -> @$el.find('#hero-carousel').carousel('next') if @heroes.models.length and not @$el.hasClass 'secret'
constructor: (options) ->
super options
@heroes = new CocoCollection([], {model: ThangType})
@heroes.url = '/db/thang.type?view=heroes&project=original,name,slug,soundTriggers,featureImage,gems,heroClass,description,components'
@supermodel.loadCollection(@heroes, 'heroes')
@stages = {}
destroy: ->
for heroIndex, stage of @stages
createjs.Ticker.removeEventListener "tick", stage
stage.removeAllChildren()
super()
getRenderData: (context={}) ->
context = super(context)
context.heroes = @heroes.models
hero.locked = temporaryHeroInfo[hero.get('slug')].status is 'Locked' and not me.ownsHero hero.get('original') for hero in context.heroes
context.codeLanguages = [
{id: 'python', name: 'Python (Default)'}
{id: 'javascript', name: 'JavaScript'}
{id: 'coffeescript', name: 'CoffeeScript'}
{id: 'clojure', name: 'Clojure (Experimental)'}
{id: 'lua', name: 'Lua (Experimental)'}
{id: 'io', name: 'Io (Experimental)'}
]
context.codeLanguage = @codeLanguage = @options.session.get('codeLanguage') ? me.get('aceConfig')?.language ? 'python'
context.heroInfo = temporaryHeroInfo
context
afterRender: ->
super()
return unless @supermodel.finished()
heroes = @heroes.models
@$el.find('.hero-indicator').each ->
heroID = $(@).data('hero-id')
hero = _.find heroes, (hero) -> hero.get('original') is heroID
$(@).find('.hero-avatar').css('background-image', "url(#{hero.getPortraitURL()})").tooltip()
_.defer => $(@).addClass 'initialized'
@canvasWidth = 313 # @$el.find('canvas').width() # unreliable, whatever
@canvasHeight = @$el.find('canvas').height()
heroConfig = @options.session.get('heroConfig') ? me.get('heroConfig') ? {}
heroIndex = Math.max 0, _.findIndex(heroes, ((hero) -> hero.get('original') is heroConfig.thangType))
@$el.find(".hero-item:nth-child(#{heroIndex + 1}), .hero-indicator:nth-child(#{heroIndex + 1})").addClass('active')
@onHeroChanged direction: null, relatedTarget: @$el.find('.hero-item')[heroIndex]
@$el.find('.hero-stat').tooltip()
@buildCodeLanguages()
onHeroChanged: (e) ->
direction = e.direction # 'left' or 'right'
heroItem = $(e.relatedTarget)
hero = _.find @heroes.models, (hero) -> hero.get('original') is heroItem.data('hero-id')
return console.error "Couldn't find hero from heroItem:", heroItem unless hero
heroIndex = heroItem.index()
@$el.find('.hero-indicator').each ->
distance = Math.min 3, Math.abs $(@).index() - heroIndex
size = 100 - (50 / 3) * distance
$(@).css width: size, height: size, top: -(100 - size) / 2
heroInfo = temporaryHeroInfo[hero.get('slug')]
locked = heroInfo.status is 'Locked' and not me.ownsHero ThangType.heroes[hero.get('slug')]
hero = @loadHero hero, heroIndex
@preloadHero heroIndex + 1
@preloadHero heroIndex - 1
@selectedHero = hero unless locked
Backbone.Mediator.publish 'level:hero-selection-updated', hero: @selectedHero
$('#choose-inventory-button').prop 'disabled', locked
getFullHero: (original) ->
url = "/db/thang.type/#{original}/version"
if fullHero = @supermodel.getModel url
return fullHero
fullHero = new ThangType()
fullHero.setURL url
fullHero = (@supermodel.loadModel fullHero, 'thang').model
fullHero
preloadHero: (heroIndex) ->
return unless hero = @heroes.models[heroIndex]
@loadHero hero, heroIndex, true
loadHero: (hero, heroIndex, preloading=false) ->
createjs.Ticker.removeEventListener 'tick', stage for stage in _.values @stages
if featureImage = hero.get 'featureImage'
$(".hero-item[data-hero-id='#{hero.get('original')}'] canvas").hide()
$(".hero-item[data-hero-id='#{hero.get('original')}'] .hero-feature-image").show().find('img').prop('src', '/file/' + featureImage)
@playSelectionSound hero unless preloading
return hero
createjs.Ticker.setFPS 30 # In case we paused it from being inactive somewhere else
if stage = @stages[heroIndex]
unless preloading
_.defer -> createjs.Ticker.addEventListener 'tick', stage # Deferred, otherwise it won't start updating for some reason.
@playSelectionSound hero
return hero
fullHero = @getFullHero hero.get 'original'
onLoaded = =>
return unless canvas = $(".hero-item[data-hero-id='#{fullHero.get('original')}'] canvas")
canvas.show().prop width: @canvasWidth, height: @canvasHeight
builder = new SpriteBuilder(fullHero)
movieClip = builder.buildMovieClip(fullHero.get('actions').attack?.animation ? fullHero.get('actions').idle.animation)
movieClip.scaleX = movieClip.scaleY = canvas.prop('height') / 120 # Average hero height is ~110px tall at normal resolution
if fullHero.get('name') in ['Knight', 'Robot Walker'] # These are too big, so shrink them.
movieClip.scaleX *= 0.7
movieClip.scaleY *= 0.7
movieClip.regX = -fullHero.get('positions').registration.x
movieClip.regY = -fullHero.get('positions').registration.y
movieClip.x = canvas.prop('width') * 0.5
movieClip.y = canvas.prop('height') * 0.925 # This is where the feet go.
stage = new createjs.Stage(canvas[0])
@stages[heroIndex] = stage
stage.addChild movieClip
stage.update()
movieClip.gotoAndPlay 0
unless preloading
createjs.Ticker.addEventListener 'tick', stage
@playSelectionSound hero
if fullHero.loaded
_.defer onLoaded
else
@listenToOnce fullHero, 'sync', onLoaded
fullHero
playSelectionSound: (hero) ->
return if @$el.hasClass 'secret'
@currentSoundInstance?.stop()
return unless sounds = hero.get('soundTriggers')?.selected
return unless sound = sounds[Math.floor Math.random() * sounds.length]
name = AudioPlayer.nameForSoundReference sound
AudioPlayer.preloadSoundReference sound
@currentSoundInstance = AudioPlayer.playSound name, 1
@currentSoundInstance
buildCodeLanguages: ->
$select = @$el.find('#option-code-language')
$select.fancySelect().parent().find('.options li').each ->
languageName = $(@).text()
languageID = $(@).data('value')
blurb = $.i18n.t("choose_hero.#{languageID}_blurb")
$(@).text("#{languageName} - #{blurb}")
onCodeLanguageChanged: (e) ->
@codeLanguage = @$el.find('#option-code-language').val()
@codeLanguageChanged = true
onShown: ->
# Called when we switch tabs to this within the modal
onHidden: ->
# Called when the modal itself is dismissed
temporaryHeroInfo =
knight:
fullName: 'Tharin Thunderfist'
weapons: 'Swords - Short Range, No Magic'
class: 'Warrior'
description: 'Beefcake! Beefcaaake!'
status: 'Available'
attack: 8
attackFactor: 1.2
health: 8.5
healthFactor: 1.4
speed: 1.5
speedAbsolute: 6
captain:
fullName: 'Captain Anya Weston'
weapons: 'Swords - Short Range, No Magic'
class: 'Warrior'
description: 'Don\'t bother me, I\'m winning this fight for you.'
status: 'Available'
attack: 8
attackFactor: 1.2
health: 8.5
healthFactor: 1.4
speed: 1.5
speedAbsolute: 6
thoktar:
fullName: 'Thoktar the Devourer'
weapons: 'Wands, Staffs - Long Range, Magic'
class: 'Wizard'
description: '???'
status: 'Locked'
attack: 5
attackFactor: 2
health: 4.5
healthFactor: 1.4
speed: 2.5
speedAbsolute: 7
skills: ['summonElemental', 'devour']
equestrian:
fullName: 'Rider Reynaldo'
weapons: 'Crossbows, Guns - Long Range, No Magic'
class: 'Ranger'
description: '???'
status: 'Locked'
attack: 6
attackFactor: 1.4
health: 7
healthFactor: 1.8
speed: 1.5
speedAbsolute: 6
skills: ['hide']
'potion-master':
fullName: 'Master Snake'
weapons: 'Wands, Staffs - Long Range, Magic'
class: 'Wizard'
description: '???'
status: 'Locked'
attack: 2
attackFactor: 0.833
health: 4
healthFactor: 1.2
speed: 6
speedAbsolute: 11
skills: ['brewPotion']
librarian:
fullName: 'Hushbaum'
weapons: 'Wands, Staffs - Long Range, Magic'
class: 'Wizard'
description: '???'
status: 'Locked'
attack: 3
attackFactor: 1.2
health: 4.5
healthFactor: 1.4
speed: 2.5
speedAbsolute: 7
'robot-walker':
fullName: '???'
weapons: '???'
class: 'Ranger'
description: '???'
status: 'Locked'
attack: 6.5
attackFactor: 1.6
health: 5.5
healthFactor: 1.2
speed: 6
speedAbsolute: 11
skills: ['???', '???', '???']
'michael-heasell':
fullName: '???'
weapons: '???'
class: 'Ranger'
description: '???'
status: 'Locked'
attack: 4
attackFactor: 0.714
health: 5
healthFactor: 1
speed: 10
speedAbsolute: 16
skills: ['???', '???']
'ian-elliott':
fullName: '???'
weapons: 'Swords - Short Range, No Magic'
class: 'Warrior'
description: '???'
status: 'Locked'
attack: 9.5
attackFactor: 1.8
health: 6.5
healthFactor: 0.714
speed: 3.5
speedAbsolute: 8
skills: ['trueStrike']

View file

@ -357,35 +357,47 @@ module.exports = class InventoryModal extends ModalView
@remainingRequiredEquipment = []
@$el.find('.should-equip').removeClass('should-equip')
inWorldMap = $('#world-map-view').length
if heroClass = @selectedHero?.get('heroClass')
for slot, item of _.clone equipment
itemModel = @items.findWhere original: item
unless itemModel and heroClass in itemModel.classes
console.log 'Unequipping', itemModel.get('heroClass'), 'item', itemModel.get('name'), 'from slot due to class restrictions.'
@unequipItemFromSlot @$el.find(".item-slot[data-slot='#{slot}']")
for slot, item of restrictedGear
equipped = equipment[slot]
if equipped and equipped is gear[restrictedGear[slot]]
console.log 'Unequipping restricted item', restrictedGear[slot], 'for', slot, 'before level', @options.levelID
@unequipItemFromSlot @$el.find(".item-slot[data-slot='#{slot}']")
for slot, item of requiredGear
#continue if item is 'leather-tunic' and inWorldMap and @options.levelID is 'the-raised-sword' # Don't tell them they need it until they need it in the level # ... when we make it so that you can buy it
equipped = equipment[slot]
continue if equipped and not (
(item is 'builders-hammer' and equipped in [gear['simple-sword'], gear['long-sword']]) or
(item in ['simple-sword', 'long-sword'] and equipped is gear['builders-hammer']) or
(item is 'leather-boots' and equipped is gear['simple-boots']) or
(item is 'simple-boots' and equipped is gear['leather-boots'])
)
itemModel = @items.findWhere {slug: item}
continue unless itemModel
availableSlotSelector = "#unequipped .item[data-item-id='#{itemModel.id}']"
@highlightElement availableSlotSelector, delay: 500, sides: ['right'], rotation: Math.PI / 2
@$el.find(availableSlotSelector).addClass 'should-equip'
@$el.find("#equipped div[data-slot='#{slot}']").addClass 'should-equip'
@$el.find('#double-click-hint').removeClass('secret')
@remainingRequiredEquipment.push slot: slot, item: gear[item]
if heroClass is 'Warrior'
# After they switch to a ranger or wizard, we stop being so finicky about gear.
for slot, item of requiredGear
#continue if item is 'leather-tunic' and inWorldMap and @options.levelID is 'the-raised-sword' # Don't tell them they need it until they need it in the level # ... when we make it so that you can buy it
equipped = equipment[slot]
continue if equipped and not (
(item is 'builders-hammer' and equipped in [gear['simple-sword'], gear['long-sword']]) or
(item in ['simple-sword', 'long-sword'] and equipped is gear['builders-hammer']) or
(item is 'leather-boots' and equipped is gear['simple-boots']) or
(item is 'simple-boots' and equipped is gear['leather-boots'])
)
itemModel = @items.findWhere {slug: item}
continue unless itemModel
availableSlotSelector = "#unequipped .item[data-item-id='#{itemModel.id}']"
@highlightElement availableSlotSelector, delay: 500, sides: ['right'], rotation: Math.PI / 2
@$el.find(availableSlotSelector).addClass 'should-equip'
@$el.find("#equipped div[data-slot='#{slot}']").addClass 'should-equip'
@$el.find('#double-click-hint').removeClass('secret')
@remainingRequiredEquipment.push slot: slot, item: gear[item]
if hadRequired and not @remainingRequiredEquipment.length
@endHighlight()
@highlightElement '#play-level-button', duration: 5000
$('#play-level-button').prop('disabled', @remainingRequiredEquipment.length > 0)
setHero: (@selectedHero) ->
if @selectedHero.loading
@listenToOnce @selectedHero, 'sync', => @setHero? @selectedHero
return
@$el.removeClass('Warrior Ranger Wizard').addClass(@selectedHero.get('heroClass'))
@requireLevelEquipment()
@render()
onShown: ->
@ -418,8 +430,11 @@ module.exports = class InventoryModal extends ModalView
inventory = @getCurrentEquipmentConfig()
patchSession = patchMe = false
patchSession ||= not _.isEqual inventory, sessionHeroConfig.inventory
patchMe ||= not _.isEqual inventory, lastHeroConfig.inventory
sessionHeroConfig.inventory = inventory
if hero = @selectedHero.get('original')
patchSession ||= not _.isEqual hero, sessionHeroConfig.thangType
sessionHeroConfig.thangType = hero
patchMe ||= not _.isEqual inventory, lastHeroConfig.inventory
lastHeroConfig.inventory = inventory
if patchMe
console.log 'setting me.heroConfig to', JSON.stringify(lastHeroConfig)

View file

@ -708,21 +708,10 @@ forest = [
id: 'drop-the-flag'
description: 'it is hot'
disabled: not me.isAdmin()
nextLevels:
continue: 'rich-forager'
x: 77.54
y: 50.94
}
{
name: 'Rich Forager'
type: 'hero'
id: 'rich-forager'
description: 'if i were a rich man'
disabled: not me.isAdmin()
nextLevels:
continue: 'deadly-pursuit'
x: 77.54
y: 45.94
y: 50.94
}
{
name: 'Deadly Pursuit'
@ -731,10 +720,21 @@ forest = [
description: 'what is a dirt nap? it is death.'
disabled: not me.isAdmin()
nextLevels:
continue: 'multiplayer-treasure-grove'
continue: 'rich-forager'
x: 77.54
y: 40.94
}
{
name: 'Rich Forager'
type: 'hero'
id: 'rich-forager'
description: 'if i were a rich man'
disabled: not me.isAdmin()
nextLevels:
continue: 'multiplayer-treasure-grove'
x: 77.54
y: 45.94
}
{
name: 'Multiplayer Treasure Grove'
type: 'hero'

View file

@ -63,6 +63,7 @@ module.exports = class ControlBarView extends CocoView
@homeLink = c.homeLink = '/'
@homeViewClass = require 'views/HomeView'
c.editorLink = "/editor/level/#{@level.get('slug')}"
c.homeLink = @homeLink
c
showGameMenuModal: ->

View file

@ -98,8 +98,8 @@ module.exports = class LevelHUDView extends CocoView
@stage?.stopTalking()
createProperties: ->
if @thang.id is 'Hero Placeholder'
name = {knight: 'Tharin', captain: 'Anya'}[@thang.type] ? 'Hero'
if @thang.id in ['Hero Placeholder', 'Hero Placeholder 1']
name = {knight: 'Tharin', captain: 'Anya', librarian: 'Hushbaum', sorcerer: 'Pender', 'potion-master': 'Omarn', samurai: 'Hattori', ninja: 'Amara'}[@thang.type] ? 'Hero'
else
name = if @thang.type then "#{@thang.id} - #{@thang.type}" else @thang.id
@$el.find('.thang-name').text name

View file

@ -29,6 +29,7 @@ module.exports = class PlayHeroesModal extends ModalView
@heroes = new CocoCollection([], {model: ThangType})
@heroes.url = '/db/thang.type?view=heroes'
@heroes.setProjection ['original','name','slug','soundTriggers','featureImage','gems','heroClass','description','components','extendedName']
@heroes.comparator = 'gems'
@listenToOnce @heroes, 'sync', @onHeroesLoaded
@supermodel.loadCollection(@heroes, 'heroes')
@stages = {}

View file

@ -1,17 +1,6 @@
ThangType = require './ThangType'
Handler = require '../../commons/Handler'
heroes =
captain: '529ec584c423d4e83b000014'
knight: '529ffbf1cf1818f2be000001'
librarian: '52fbf74b7e01835453bd8d8e'
equestrian: '52e95b4222efc8e70900175d'
'potion-master': '52e9adf7427172ae56002172'
thoktar: '52a00542cf1818f2be000006'
'robot-walker': '5301696ad82649ec2c0c9b0d'
'michael-heasell': '53e126a4e06b897606d38bef'
'ian-elliott': '53e12be0d042f23505c3023b'
ThangTypeHandler = class ThangTypeHandler extends Handler
modelClass: ThangType
jsonSchema: require '../../../app/schemas/models/thang_type'
@ -64,8 +53,7 @@ ThangTypeHandler = class ThangTypeHandler extends Handler
query.kind = 'Item'
query.tier = {$exists: true} # Items without a tier don't show up anywhere, whereas items without gems don't show up in the store
else if req.query.view is 'heroes'
#query.kind = 'Hero' # TODO: when ChooseHeroView is refactored, just use this
query.original = {$in: _.values heroes} # TODO: when ChooseHeroView is refactored, don't do this
query.kind = 'Hero'
else if req.query.view is 'i18n-coverage'
query.i18nCoverage = {$exists: true}