Merge branch 'master' into production

This commit is contained in:
Nick Winter 2014-10-10 19:20:10 -07:00
commit fb39081380
12 changed files with 163 additions and 95 deletions

View file

@ -29,7 +29,7 @@ module.exports = class LevelBus extends Bus
constructor: ->
super(arguments...)
@changedSessionProperties = {}
@saveSession = _.debounce(@saveSession, 1000, {maxWait: 5000})
@saveSession = _.debounce(@reallySaveSession, 1000, {maxWait: 5000})
@playerIsIdle = false
init: ->
@ -190,7 +190,7 @@ module.exports = class LevelBus extends Bus
state.complete = true
@session.set('state', state)
@changedSessionProperties.state = true
@saveSession()
@reallySaveSession() # Make sure it saves right away; don't debounce it.
onNewGoalStates: ({goalStates})->
state = @session.get 'state'
@ -232,7 +232,8 @@ module.exports = class LevelBus extends Bus
@changedSessionProperties.permissions = true
@saveSession()
saveSession: ->
# Debounced as saveSession
reallySaveSession: ->
return if _.isEmpty @changedSessionProperties
# don't let peeking admins mess with the session accidentally
return unless @session.get('multiplayer') or @session.get('creator') is me.id

View file

@ -8,7 +8,7 @@ module.exports.createAetherOptions = (options) ->
aetherOptions =
functionName: options.functionName
protectAPI: not options.skipProtectAPI
includeFlow: options.includeFlow
includeFlow: Boolean options.includeFlow
yieldConditionally: options.functionName is 'plan'
simpleLoops: true
globals: ['Vector', '_']
@ -47,3 +47,4 @@ functionParameters =
chooseAction: []
plan: []
initializeCentroids: []
update: []

View file

@ -6,7 +6,7 @@
** Paths and target pieces (and ghosts?)
** Normal Thangs, bots, wizards (z-indexing based on World-determined sprite.thang.pos.z/y, mainly, instead of sprite-map-determined sprite.z, which we rename to... something)
** Above-thang marks (blood, highlight) and health bars
* Stage (Regular Canvas)
** Camera border
** surfaceTextLayer (speech, names)
@ -14,7 +14,7 @@
*** Letterbox
**** Letterbox top and bottom
*** FPS display, maybe grid axis labels, coordinate hover
** Grid lines--somewhere--we will figure it out, do not really need it at first
###
@ -26,7 +26,7 @@ SingularSprite = require './SingularSprite'
NEVER_RENDER_ANYTHING = false # set to true to test placeholders
module.exports = LayerAdapter = class LayerAdapter extends CocoClass
# Intermediary between a Surface Stage and a top-level static normal Container or hot-swapped WebGL SpriteContainer.
# It handles zooming in different ways and, if webGL, creating and assigning spriteSheets.
@ -79,8 +79,9 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
toString: -> "<Layer #{@layerPriority}: #{@name}>"
#- Layer ordering
updateLayerOrder: ->
return if @destroyed
@container.sortChildren @layerOrderComparator
layerOrderComparator: (a, b) ->
@ -105,7 +106,7 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
return 0 unless aPos and bPos
return (bPos.y - aPos.y) or (bPos.x - aPos.x)
return az - bz
#- Zoom updating
onZoomUpdated: (e) ->
@ -141,13 +142,13 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
child.scaleY *= @container.scaleY
#- Adding, removing children for WebGL layers.
addLank: (lank) ->
# TODO: Move this into the production DB rather than setting it dynamically.
if lank.thangType?.get('name') is 'Highlight'
lank.thangType.set('spriteType', 'segmented')
lank.options.resolutionFactor = @resolutionFactor
lank.layer = @
@listenTo(lank, 'action-needs-render', @onActionNeedsRender)
@lanks.push lank
@ -164,7 +165,7 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
@lanks = _.without @lanks, lank
#- Loading network resources dynamically
loadThangType: (thangType) ->
if not thangType.isFullyLoaded()
thangType.setProjection null
@ -185,7 +186,7 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
@renderNewSpriteSheet()
#- Adding to the list of things we need to render
onActionNeedsRender: (lank, action) ->
@upsertActionToRender(lank.thangType, action.name, lank.options.colorConfig)
@ -214,16 +215,16 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
@_renderNewSpriteSheet(false)
#- Rendering sprite sheets
renderNewSpriteSheet: ->
@willRender = false
return if @numThingsLoading
@_renderNewSpriteSheet()
_renderNewSpriteSheet: (async) ->
@asyncBuilder.stopAsync() if @asyncBuilder
@asyncBuilder = null
async ?= @buildAsync
builder = new createjs.SpriteSheetBuilder()
groups = _.groupBy(@toRenderBundles, ((bundle) -> @renderGroupingKey(bundle.thangType, '', bundle.colorConfig)), @)
@ -233,7 +234,7 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
dimension = @resolutionFactor * SPRITE_PLACEHOLDER_WIDTH
placeholder.setBounds(0, 0, dimension, dimension)
builder.addFrame(placeholder)
# Add custom graphics
extantGraphics = if @spriteSheet?.resolutionFactor is @resolutionFactor then @spriteSheet.getAnimations() else []
for key, graphic of @customGraphics
@ -244,7 +245,7 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
else
frame = builder.addFrame(graphic.graphic, graphic.bounds, @resolutionFactor)
builder.addAnimation(key, [frame], false)
# Render ThangTypes
groups = {} if NEVER_RENDER_ANYTHING
for bundleGrouping in _.values(groups)
@ -259,7 +260,7 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
@renderSingularThangType(args...)
else
@renderRasterThangType(thangType, builder)
if async
try
builder.buildAsync()
@ -272,11 +273,11 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
sheet = builder.build()
@onBuildSpriteSheetComplete({async:async}, builder)
return sheet
onBuildSpriteSheetComplete: (e, builder) ->
return if @initializing or @destroyed
@asyncBuilder = null
if builder.spriteSheet._images.length > 1
total = 0
# get a rough estimate of how much smaller the spritesheet needs to be
@ -285,10 +286,10 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
@resolutionFactor /= (Math.max(1.1, Math.sqrt(total)))
@_renderNewSpriteSheet(e.async)
return
@spriteSheet = builder.spriteSheet
@spriteSheet.resolutionFactor = @resolutionFactor
oldLayer = @container
oldLayer = @container
@container = new createjs.SpriteContainer(@spriteSheet)
for lank in @lanks
console.log 'zombie sprite found on layer', @name if lank.destroyed
@ -307,7 +308,7 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
lank.updateScale()
lank.updateRotation()
@trigger 'new-spritesheet'
resetSpriteSheet: ->
@removeLank(lank) for lank in @lanks.slice(0)
@toRenderBundles = []
@ -317,9 +318,9 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
@initializing = false
#- Placeholder
createPlaceholder: ->
# TODO: Experiment with this. Perhaps have rectangles if default layer is obstacle or floor,
# TODO: Experiment with this. Perhaps have rectangles if default layer is obstacle or floor,
# and different colors for different layers.
g = new createjs.Graphics()
g.setStrokeStyle(5)
@ -341,7 +342,7 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
else
g.drawRect(bounds...)
new createjs.Shape(g)
#- Rendering containers for segmented thang types
renderSegmentedThangType: (thangType, colorConfig, actionNames, spriteSheetBuilder) ->
@ -353,7 +354,7 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
else if action.animation
animationContainers = @getContainersForAnimation(thangType, action.animation, action)
containersToRender[container.gn] = true for container in animationContainers
spriteBuilder = new SpriteBuilder(thangType, {colorConfig: colorConfig})
for containerGlobalName in _.keys(containersToRender)
containerKey = @renderGroupingKey(thangType, containerGlobalName, colorConfig)
@ -374,9 +375,9 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
for animation in thangType.get('raw').animations[animation].animations
containers = containers.concat(@getContainersForAnimation(thangType, animation.gn, action))
return containers
#- Rendering sprite sheets for singular thang types
renderSingularThangType: (thangType, colorConfig, actionNames, spriteSheetBuilder) ->
actionObjects = _.values(thangType.getActions())
animationActions = []
@ -384,14 +385,14 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
continue unless a.animation
continue unless a.name in actionNames
animationActions.push(a)
spriteBuilder = new SpriteBuilder(thangType, {colorConfig: colorConfig})
animationGroups = _.groupBy animationActions, (action) -> action.animation
for animationName, actions of animationGroups
renderAll = _.any actions, (action) -> action.frames is undefined
scale = actions[0].scale or thangType.get('scale') or 1
actionKeys = (@renderGroupingKey(thangType, action.name, colorConfig) for action in actions)
if @spriteSheet?.resolutionFactor is @resolutionFactor and _.all(actionKeys, (key) => key in @spriteSheet.getAnimations())
framesNeeded = _.uniq(_.flatten((@spriteSheet.getAnimation(key)).frames for key in actionKeys))
@ -406,9 +407,9 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
next = @nextForAction(action)
spriteSheetBuilder.addAnimation(key, frames, next)
continue
mc = spriteBuilder.buildMovieClip(animationName, null, null, null, {'temp':0})
if renderAll
res = spriteSheetBuilder.addMovieClip(mc, null, scale * @resolutionFactor)
frames = spriteSheetBuilder._animations['temp'].frames
@ -420,23 +421,23 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
frame = parseInt(frame)
f = _.bind(mc.gotoAndStop, mc, frame)
framesMap[frame] = spriteSheetBuilder.addFrame(mc, null, scale * @resolutionFactor, f)
for action in actions
name = @renderGroupingKey(thangType, action.name, colorConfig)
if action.frames
frames = (framesMap[parseInt(frame)] for frame in action.frames.split(','))
else
frames = _.sortBy(_.values(framesMap))
next = @nextForAction(action)
spriteSheetBuilder.addAnimation(name, frames, next)
spriteSheetBuilder.addAnimation(name, frames, next)
containerActions = []
for a in actionObjects
continue unless a.container
continue unless a.name in actionNames
containerActions.push(a)
containerGroups = _.groupBy containerActions, (action) -> action.container
for containerName, actions of containerGroups
container = spriteBuilder.buildContainerFromStore(containerName)
@ -444,25 +445,25 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
frame = spriteSheetBuilder.addFrame(container, null, scale * @resolutionFactor)
for action in actions
name = @renderGroupingKey(thangType, action.name, colorConfig)
spriteSheetBuilder.addAnimation(name, [frame], false)
spriteSheetBuilder.addAnimation(name, [frame], false)
nextForAction: (action) ->
next = true
next = action.goesTo if action.goesTo
next = false if action.loops is false
return next
#- Rendering frames for raster thang types
renderRasterThangType: (thangType, spriteSheetBuilder) ->
unless thangType.rasterImage
console.error("Cannot render the LayerAdapter SpriteSheet until the raster image for <#{thangType.get('name')}> is loaded.")
bm = new createjs.Bitmap(thangType.rasterImage[0])
scale = thangType.get('scale') or 1
frame = spriteSheetBuilder.addFrame(bm, null, scale)
spriteSheetBuilder.addAnimation(@renderGroupingKey(thangType), [frame], false)
#- Distributing new Segmented/Singular/RasterSprites to Lanks
setSpriteToLank: (lank) ->
@ -474,7 +475,7 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
sprite.regX = @resolutionFactor * SPRITE_PLACEHOLDER_WIDTH / 2
sprite.regY = @resolutionFactor * SPRITE_PLACEHOLDER_WIDTH
sprite.baseScaleX = sprite.baseScaleY = sprite.scaleX = sprite.scaleY = 10 / (@resolutionFactor * SPRITE_PLACEHOLDER_WIDTH)
else if lank.thangType.get('raster')
sprite = new createjs.Sprite(@spriteSheet)
scale = lank.thangType.get('scale') or 1
@ -483,7 +484,7 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
sprite.regY = -reg.y * scale
sprite.gotoAndStop(@renderGroupingKey(lank.thangType))
sprite.baseScaleX = sprite.baseScaleY = 1
else
SpriteClass = if (lank.thangType.get('spriteType') or @defaultSpriteType) is 'segmented' then SegmentedSprite else SingularSprite
prefix = @renderGroupingKey(lank.thangType, null, lank.options.colorConfig) + '.'
@ -507,4 +508,4 @@ module.exports = LayerAdapter = class LayerAdapter extends CocoClass
destroy: ->
child.destroy?() for child in @container.children
@asyncBuilder.stopAsync() if @asyncBuilder
super()
super()

View file

@ -58,14 +58,14 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis
hours_played: "horas jugadas" # Hover over a level on /play
# items: "Items" # Tooltip on item shop button from /play
# heroes: "Heroes" # Tooltip on hero shop button from /play
# achievements: "Achievements" # Tooltip on achievement list button from /play
# account: "Account" # Tooltip on account button from /play
achievements: "Logros" # Tooltip on achievement list button from /play
account: "Cuenta" # Tooltip on account button from /play
# settings: "Settings" # Tooltip on settings button from /play
# next: "Next" # Go from choose hero to choose inventory before playing a level
# change_hero: "Change Hero" # Go back from choose inventory to choose hero
next: "Siguiente Heroe" # Go from choose hero to choose inventory before playing a level
change_hero: "Seleccionar Heroe" # Go back from choose inventory to choose hero
# choose_inventory: "Equip Items"
# older_campaigns: "Older Campaigns"
# anonymous: "Anonymous Player"
older_campaigns: "Campañas Anteriores"
anonymous: "Jugador Anonimo"
level_difficulty: "Dificultad: "
campaign_beginner: "Campaña de Principiante"
choose_your_level: "Elige tu nivel" # The rest of this section is the old play view at /play-old and isn't very important.
@ -182,12 +182,12 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis
play_level:
done: "Hecho"
home: "Inicio"
# skip: "Skip"
skip: "Saltar"
game_menu: "Menu del Juego"
guide: "Guía"
restart: "Reiniciar"
goals: "Objetivos"
# goal: "Goal"
goal: "Objetivo Principal"
success: "Exito!"
incomplete: "Incompleto"
timed_out: "Te has quedado sin tiempo"
@ -204,7 +204,7 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis
victory_rate_the_level: "Puntúa este nivel: " # Only in old-style levels.
victory_return_to_ladder: "Volver a Clasificación"
victory_play_next_level: "Jugar el siguiente nivel" # Only in old-style levels.
# victory_play_continue: "Continue"
victory_play_continue: "Continuar"
victory_go_home: "Ir a Inicio" # Only in old-style levels.
victory_review: "¡Cuéntanos más!" # Only in old-style levels.
victory_hour_of_code_done: "¿Ya terminaste?"
@ -220,18 +220,18 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis
# tome_cast_button_running: "Running"
# tome_cast_button_ran: "Ran"
# tome_submit_button: "Submit"
# tome_reload_method: "Reload original code for this method" # Title text for individual method reload button.
# tome_select_method: "Select a Method"
# tome_see_all_methods: "See all methods you can edit" # Title text for method list selector (shown when there are multiple programmable methdos).
tome_reload_method: "Recargcar código original para este método" # Title text for individual method reload button.
tome_select_method: "Seleccionar método"
tome_see_all_methods: "Métodos que pueden ser editados" # Title text for method list selector (shown when there are multiple programmable methdos).
tome_select_a_thang: "Selecciona a alguien para "
tome_available_spells: "Hechizos disponibles"
# tome_your_skills: "Your Skills"
tome_your_skills: "Tus Habilidades"
hud_continue: "Continuar (pulsa Shift+Space)"
spell_saved: "Hechizo guardado"
skip_tutorial: "Saltar (esc)"
keyboard_shortcuts: "Atajos de teclado"
loading_ready: "¡Listo!"
# loading_start: "Start Level"
loading_start: "Iniciar Nivel"
time_current: "Ahora:"
time_total: "Máx:"
time_goto: "Ir a:"
@ -266,7 +266,7 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis
tip_hardware_problem: "P: Cuantos programadores hacen falta para cambiar una bombilla? R: Ninguno, es un problema de hardware."
# tip_hofstadters_law: "Hofstadter's Law: It always takes longer than you expect, even when you take into account Hofstadter's Law."
# tip_premature_optimization: "Premature optimization is the root of all evil. - Donald Knuth"
# tip_brute_force: "When in doubt, use brute force. - Ken Thompson"
tip_brute_force: "Cuando haya dudas, usa la fuerza bruta. - Ken Thompson"
customize_wizard: "Personalizar Mago"
game_menu:
@ -286,14 +286,14 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis
# inventory:
# choose_inventory: "Equip Items"
# choose_hero:
# choose_hero: "Choose Your Hero"
# programming_language: "Programming Language"
# programming_language_description: "Which programming language do you want to use?"
# status: "Status"
# weapons: "Weapons"
# health: "Health"
# speed: "Speed"
choose_hero:
choose_hero: "Selecciona tu Heroe"
programming_language: "Lenguaje de Programación"
programming_language_description: "¿Qué lenguaje de programación deseas usar?"
status: "Estado"
weapons: "Armas"
health: "Salud"
speed: "Velocidad"
save_load:
granularity_saved_games: "Salvado"

View file

@ -28,3 +28,5 @@ module.exports =
'world:attacked-when-out-of-range': c.object {required: ['thang']},
replacedNoteChain: {type: 'array'}
thang: {type: 'object'}
'world:custom-script-trigger': {type: 'object'}

View file

@ -68,6 +68,10 @@ $stashWidth: $totalWidth - $equippedWidth - $stashMargin
border-color: #28f
@include box-shadow(0 0 10px #28f)
&.should-equip
background-color: #8fa
outline: 2px solid #8af
&.droppable
outline: 2px solid blue
@ -189,6 +193,14 @@ $stashWidth: $totalWidth - $equippedWidth - $stashMargin
.item-info:after
content: ' (available)'
&.should-equip
background-color: #8fa
outline: 2px solid #8af
.item-info:after
content: ' (drag to equip)'
font-weight: bold
&.equipped
background-color: #ff5

View file

@ -14,9 +14,9 @@ block modal-footer-content
if readyToRank
.ladder-submission-view
else if level.get('type') === 'ladder'
a.btn.btn-primary(href="/play/ladder/#{level.get('slug')}#my-matches", data-dismiss="modal", data-i18n="play_level.victory_go_ladder") Return to Ladder
a.btn.btn-primary(href="/play/ladder/#{level.get('slug')}#my-matches", data-dismiss="modal", data-i18n="play_level.victory_return_to_ladder") Return to Ladder
else if level.get('type', true) === 'hero'
a.btn.btn-success.world-map-button(href="/play-hero", data-dismiss="modal", data-i18n="play_level.victory_continue") Continue
a.btn.btn-success.world-map-button(href="/play-hero", data-dismiss="modal", data-i18n="play_level.victory_play_continue") Continue
else if hasNextLevel
button.btn.btn-success.next-level-button(data-dismiss="modal", data-i18n="play_level.victory_play_next_level") Play Next Level
else

View file

@ -29,7 +29,7 @@ module.exports = class InventoryView extends CocoView
@items = new CocoCollection([], {model: ThangType})
@equipment = options.equipment or @options.session?.get('heroConfig')?.inventory or me.get('heroConfig')?.inventory or {}
@equipment = $.extend true, {}, @equipment
@assignLevelEquipment()
@requireLevelEquipment()
@items.url = '/db/thang.type?view=items&project=name,components,original,rasterIcon'
@supermodel.loadCollection(@items, 'items')
@ -103,25 +103,44 @@ module.exports = class InventoryView extends CocoView
for itemSlot in @$el.find '.item-slot'
slot = $(itemSlot).data 'slot'
#$(itemSlot).find('.placeholder').css('background-image', "url(/images/pages/game-menu/slot-#{slot}.png)")
do (slot) =>
do (slot, itemSlot) =>
$(itemSlot).droppable
drop: (e, ui) => @onAvailableItemDoubleClick()
accept: (el) -> $(el).parent().hasClass slot
activeClass: 'droppable'
hoverClass: 'droppable-hover'
tolerance: 'touch'
@makeEquippedSlotDraggable $(itemSlot)
@$el.find('#selected-items').hide() # Hide until one is selected
@delegateEvents()
if @selectedHero and not @startedLoadingFirstHero
@loadHero()
@requireLevelEquipment()
afterInsert: ->
super()
@canvasWidth = @$el.find('canvas').innerWidth()
@canvasHeight = @$el.find('canvas').innerHeight()
@inserted = true
makeEquippedSlotDraggable: (slot) ->
unequip = => @unequipItemFromSlot slot
shouldStayEquippedWhenDropped = (isValidDrop) ->
pos = $(@).position()
revert = Math.abs(pos.left) < $(@).outerWidth() and Math.abs(pos.top) < $(@).outerHeight()
unequip() if not revert
revert
# TODO: figure out how to make this actually above the available items list (the .ui-draggable-helper img is still inside .item-view and so underlaps...)
$(slot).find('img').draggable
revert: shouldStayEquippedWhenDropped
appendTo: @$el
cursorAt: {left: 35.5, top: 35.5}
revertDuration: 200
distance: 10
scroll: false
zIndex: 100
clearSelection: ->
@$el.find('.item-slot.selected').removeClass 'selected'
@ -197,7 +216,10 @@ module.exports = class InventoryView extends CocoView
for el in @$el.find('#available-equipment .list-group-item')
itemID = $(el).find('.item-view').data('item-id')
if itemID is itemIDToUnequip
return $(el).removeClass('equipped')
unequipped = $(el).removeClass('equipped')
break
@requireLevelEquipment() if unequipped
return unequipped
equipSelectedItemToSlot: (slot) ->
selectedItemContainer = @getSelectedAvailableItemContainer()
@ -207,6 +229,8 @@ module.exports = class InventoryView extends CocoView
slotContainer.html(newItemHTML)
slotContainer.find('.item-view').data('item-id', selectedItemContainer.find('.item-view').data('item-id'))
@$el.find('.list-group-item').removeClass('active')
@makeEquippedSlotDraggable slot
@requireLevelEquipment()
onSelectionChanged: ->
@$el.find('.item-slot').show()
@ -290,7 +314,7 @@ module.exports = class InventoryView extends CocoView
config[slotName] = item.get('original')
config
assignLevelEquipment: ->
requireLevelEquipment: ->
# This is temporary, until we have a more general way of awarding items and configuring needed/locked items per level.
gear =
'simple-boots': '53e237bf53457600003e3f05'
@ -306,17 +330,37 @@ module.exports = class InventoryView extends CocoView
'shadow-guard': {feet: 'simple-boots'}
'true-names': {feet: 'simple-boots', 'right-hand': 'longsword'}
'the-raised-sword': {feet: 'simple-boots', 'right-hand': 'longsword', torso: 'leather-tunic'}
'the-first-kithmaze': {feet: 'simple-boots', 'right-hand': 'longsword', torso: 'leather-tunic', 'programming-book': 'programmaticon-i'}
'the-second-kithmaze': {feet: 'simple-boots', 'right-hand': 'longsword', torso: 'leather-tunic', 'programming-book': 'programmaticon-i'}
'new-sight': {feet: 'simple-boots', 'right-hand': 'longsword', torso: 'leather-tunic', 'programming-book': 'programmaticon-i'}
'lowly-kithmen': {feet: 'simple-boots', 'right-hand': 'longsword', torso: 'leather-tunic', 'programming-book': 'programmaticon-i', eyes: 'crude-glasses'}
'a-bolt-in-the-dark': {feet: 'simple-boots', 'right-hand': 'longsword', torso: 'leather-tunic', 'programming-book': 'programmaticon-i', eyes: 'crude-glasses'}
'the-first-kithmaze': {feet: 'simple-boots', 'programming-book': 'programmaticon-i'}
'the-second-kithmaze': {feet: 'simple-boots', 'programming-book': 'programmaticon-i'}
'new-sight': {'right-hand': 'longsword', 'programming-book': 'programmaticon-i'}
'lowly-kithmen': {feet: 'simple-boots', 'right-hand': 'longsword', 'programming-book': 'programmaticon-i', eyes: 'crude-glasses'}
'a-bolt-in-the-dark': {feet: 'simple-boots', 'right-hand': 'longsword', torso: 'leather-tunic', eyes: 'crude-glasses'}
'the-final-kithmaze': {feet: 'simple-boots', 'right-hand': 'longsword', torso: 'leather-tunic', 'programming-book': 'programmaticon-i', eyes: 'crude-glasses'}
'kithgard-gates': {feet: 'simple-boots', 'right-hand': 'builders-hammer', torso: 'leather-tunic', 'programming-book': 'programmaticon-i', eyes: 'crude-glasses'}
'defence-of-plainswood': {feet: 'simple-boots', 'right-hand': 'builders-hammer', torso: 'leather-tunic', 'programming-book': 'programmaticon-i', eyes: 'crude-glasses'}
'kithgard-gates': {feet: 'simple-boots', 'right-hand': 'builders-hammer'}
'defence-of-plainswood': {feet: 'simple-boots', 'right-hand': 'builders-hammer'}
# TODO: figure out leather boots for plainswood (or next one?)
return unless necessaryGear = gearByLevel[@options.levelID]
for slot, item of necessaryGear ? {}
@equipment[slot] ?= gear[item]
if @inserted
if @supermodel.finished()
equipment = @getCurrentEquipmentConfig() # Make sure @equipment is updated
else
equipment = @equipment
hadRequired = @remainingRequiredEquipment?.length
@remainingRequiredEquipment = []
@$el.find('.should-equip').removeClass('should-equip')
inWorldMap = $('#world-map-view').length
for slot, item of necessaryGear
continue if item is 'leather-tunic' and inWorldMap # Don't tell them they need it until they need it in the level
continue if equipment[slot] and not (item is 'builders-hammer' and @equipment[slot] is gear.longsword)
availableSlotSelector = "#available-equipment li[data-item-id='#{gear[item]}']"
@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'
@remainingRequiredEquipment.push slot: slot, item: gear[item]
if hadRequired and not @remainingRequiredEquipment.length
@endHighlight()
@highlightElement (if inWorldMap then '#play-level-button' else '.overlaid-close-button'), duration: 5000
$('#play-level-button').prop('disabled', @remainingRequiredEquipment.length > 0)
# Restrict available items to those that would be available by this item.
@allowedItems = []
@ -370,7 +414,9 @@ module.exports = class InventoryView extends CocoView
onShown: ->
# Called when we switch tabs to this within the modal
@requireLevelEquipment()
@loadHero()
onHidden: ->
# Called when the modal itself is dismissed
@endHighlight()

View file

@ -60,6 +60,7 @@ module.exports = class CocoView extends Backbone.View
view.destroy() for id, view of @subviews
$('#modal-wrapper .modal').off 'hidden.bs.modal', @modalClosed
@endHighlight()
@getPointer(false).remove()
@[key] = undefined for key, value of @
@destroyed = true
@off = doNothing
@ -354,16 +355,17 @@ module.exports = class CocoView extends Backbone.View
setTimeout (=> $pointer.css transition: 'all 0.4s ease-in', transform: "rotate(#{@pointerRotation}rad) translate(-3px, #{@pointerRadialDistance}px)"), 800
endHighlight: ->
@getPointer().css('opacity', 0.0)
@getPointer(false).css('opacity', 0.0)
clearInterval @pointerInterval
clearTimeout @pointerDelayTimeout
clearTimeout @pointerDurationTimeout
@pointerInterval = @pointerDelayTimeout = @pointerDurationTimeout = null
getPointer: ->
return $pointer if ($pointer = @$el.find('.highlight-pointer')) and $pointer.length
$pointer = $('<img src="/images/level/pointer.png" class="highlight-pointer">')
@$el.append($pointer)
getPointer: (add=true) ->
return $pointer if ($pointer = $(".highlight-pointer[data-cid='#{@cid}']")) and ($pointer.length or not add)
$pointer = $("<img src='/images/level/pointer.png' class='highlight-pointer' data-cid='#{@cid}'>")
$pointer.css('z-index', 1040) if @$el.parents('#modal-wrapper').length
$('body').append($pointer)
$pointer
# Utilities

View file

@ -430,7 +430,7 @@ module.exports = class PlayLevelView extends RootView
viewArgs: [{supermodel: @supermodel, autoUnveil: true}, @levelID]
}
onWindowResize: (e) -> @endHighlight()
onWindowResize: (e) => @endHighlight()
onDisableControls: (e) ->
return if e.controls and not ('level' in e.controls)

View file

@ -656,6 +656,8 @@ module.exports = class SpellView extends CocoView
session.addGutterDecoration index, 'comment-line'
onAnnotationClick: ->
# @ is the gutter element
msg = "Edit line #{$(@).index() + 1} to fix it."
alertBox = $("<div class='alert alert-info fade in'>#{msg}</div>")
offset = $(@).offset()
offset.left -= 162 # default width of the Bootstrap alert here

View file

@ -64,7 +64,7 @@ module.exports = class PlayLevelModal extends ModalView
updateConfig: (callback, skipSessionSave) ->
sessionHeroConfig = @options.session.get('heroConfig') ? {}
lastHeroConfig = me.get('heroConfig') ? {}
thangType = @subviews.choose_hero_view.selectedHero.get 'original'
thangType = @subviews.choose_hero_view.selectedHero?.get('original') ? sessionHeroConfig.thangType ? lastHeroConfig.thangType
inventory = @subviews.inventory_view.getCurrentEquipmentConfig()
patchSession = patchMe = false
props = thangType: thangType, inventory: inventory
@ -101,6 +101,7 @@ module.exports = class PlayLevelModal extends ModalView
@chooseHeroView.onShown()
onClickPlayLevel: (e) ->
return if @$el.find('#play-level-button').prop 'disabled'
@showLoading()
@updateConfig =>
@navigatingToPlay = true