codecombat/app/views/play/level/LevelGoalsView.coffee
Nick Winter d77625bc77 Game dev levels (#3810)
* Tweak API doc behavior and styling

* Instead of moving to the left during active dialogues, just move to the top
* Allow pointer events
* Adjust close button
* Re-enable pinning API docs for game-dev and web-dev levels

* Make sidebar in PlayGameDevLevelView stretch, better layout columns

* Set up content of PlayGameDevLevelView sidebar to scroll

* Add rest of PlayGameDevLevelView sidebar content, rework what loading looks like

* Finish PlayGameDevLevelView

* Add share area below
* Cover the brown background, paint it gray

* Tweak PlayGameDevLevelView

* Have progress bar show everything
* Fix Surface resize handling

* Fix PlayGameDevLevelView resizing incorrectly when playing

* Add GameDevVictoryModal to PlayGameDevLevelView

* Don't show missing-doctype annotation in Ace

* Hook up GameDevVictoryModal copy button

* Fix onChangeAnnotation runtime error

* Fix onLevelLoaded runtime error

* Have CourseVictoryModal link to /courses when course is done

* Trim, update CourseDetailsView

* Remove last vestiges of teacherMode
* Remove giant navigation buttons at top
* Quick switch to flat style

* Add analytics for game-dev

* Update Analytics events for gamedev

* Prefix event names with context
* Send to Mixpanel
* Include more properties

* Mostly set up indefinite play and autocast for game-dev levels

* Set up cast buttons and shortcut for game-dev

* Add rudimentary instructions when students play game-dev levels

* Couple tweaks

* fix a bit of code that expects frames to always stick around
* have PlayGameDevLevelView render a couple frames on load

* API Docs use 'game' instead of 'hero'

* Move tags to head without combining

* Add HTML comment-start string

Fixes missing entry point arrows

* Fix some whitespace
2016-07-28 13:39:58 -07:00

149 lines
5.1 KiB
CoffeeScript

CocoView = require 'views/core/CocoView'
template = require 'templates/play/level/goals'
{me} = require 'core/auth'
utils = require 'core/utils'
stateIconMap =
success: 'glyphicon-ok'
failure: 'glyphicon-remove'
module.exports = class LevelGoalsView extends CocoView
id: 'goals-view'
template: template
className: 'secret expanded'
playbackEnded: false
subscriptions:
'goal-manager:new-goal-states': 'onNewGoalStates'
'tome:cast-spells': 'onTomeCast'
'level:set-letterbox': 'onSetLetterbox'
'level:set-playing': 'onSetPlaying'
'surface:playback-restarted': 'onSurfacePlaybackRestarted'
'surface:playback-ended': 'onSurfacePlaybackEnded'
events:
'mouseenter': ->
@mouseEntered = true
@updatePlacement()
'mouseleave': ->
@mouseEntered = false
@updatePlacement()
constructor: (options) ->
super options
@level = options.level
onNewGoalStates: (e) ->
firstRun = not @previousGoalStatus?
@previousGoalStatus ?= {}
@$el.find('.goal-status').addClass 'secret'
classToShow = null
classToShow = 'success' if e.overallStatus is 'success'
classToShow = 'failure' if e.overallStatus is 'failure'
classToShow ?= 'timed-out' if e.timedOut
classToShow ?= 'incomplete'
@$el.find('.goal-status.'+classToShow).removeClass 'secret'
list = $('#primary-goals-list', @$el)
list.empty()
goals = []
for goal in e.goals
state = e.goalStates[goal.id]
continue if goal.optional and @level.isType('course') and state.status isnt 'success'
if goal.hiddenGoal
continue if goal.optional and state.status isnt 'success'
continue if not goal.optional and state.status isnt 'failure'
continue if goal.team and me.team isnt goal.team
text = utils.i18n goal, 'name'
if state.killed
dead = _.filter(_.values(state.killed)).length
targeted = _.values(state.killed).length
if targeted > 1
# Does this make sense?
if goal.isPositive
completed = dead
else
completed = targeted - dead
text = text + " (#{completed}/#{targeted})"
# This should really get refactored, along with GoalManager, so that goals have a standard
# representation of how many are done, how many are needed, what that means, etc.
li = $('<li></li>').addClass("status-#{state.status}").text(text)
iconClass = stateIconMap[state.status]
li.prepend($('<i></i>').addClass("glyphicon #{iconClass or ''}")) # If empty, insert a .glyphicon to take up space
list.append(li)
goals.push goal
if not firstRun and state.status is 'success' and @previousGoalStatus[goal.id] isnt 'success'
@soundToPlayWhenPlaybackEnded = 'goal-success'
else if not firstRun and state.status isnt 'success' and @previousGoalStatus[goal.id] is 'success'
@soundToPlayWhenPlaybackEnded = 'goal-incomplete-again'
else
@soundToPlayWhenPlaybackEnded = null
@previousGoalStatus[goal.id] = state.status
if goals.length > 0 and @$el.hasClass 'secret'
@$el.removeClass('secret')
@lastSizeTweenTime = new Date()
@updatePlacement()
onTomeCast: (e) ->
return if e.preload
@$el.find('.goal-status').addClass('secret')
@$el.find('.goal-status.running').removeClass('secret')
onSetPlaying: (e) ->
return unless e.playing
# Automatically hide it while we replay
@mouseEntered = false
@expanded = true
@updatePlacement()
onSurfacePlaybackRestarted: ->
@playbackEnded = false
@$el.removeClass 'brighter'
@lastSizeTweenTime = new Date()
@updatePlacement()
onSurfacePlaybackEnded: ->
return if @level.isType('game-dev')
@playbackEnded = true
@updateHeight()
@$el.addClass 'brighter'
@lastSizeTweenTime = new Date()
@updatePlacement()
if @soundToPlayWhenPlaybackEnded
@playSound @soundToPlayWhenPlaybackEnded
updateHeight: ->
return if @$el.hasClass('brighter') or @$el.hasClass('secret')
return if (new Date() - @lastSizeTweenTime) < 500 # Don't measure this while still animating, might get the wrong value. Should match sass transition time.
@normalHeight = @$el.outerHeight()
updatePlacement: ->
# Expand it if it's at the end. Mousing over reverses this.
expand = @playbackEnded isnt @mouseEntered
return if expand is @expanded
@updateHeight()
sound = if expand then 'goals-expand' else 'goals-collapse'
if expand
top = -5
else
height = @normalHeight
height = @$el.outerHeight() if not height or @playbackEnded
top = 41 - height
@$el.css 'top', top
if @soundTimeout
# Don't play the sound we were going to play after all; the transition has reversed.
clearTimeout @soundTimeout
@soundTimeout = null
else if @expanded?
# Play it when the transition ends, not when it begins.
@soundTimeout = _.delay @playToggleSound, 500, sound
@expanded = expand
playToggleSound: (sound) =>
return if @destroyed
@playSound sound unless @options.level.isType('game-dev')
@soundTimeout = null
onSetLetterbox: (e) ->
@$el.toggle not e.on
@updatePlacement()