mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-11-27 17:45:40 -05:00
Added new LevelDialogueView. Messed around with dimming a bit more.
This commit is contained in:
parent
4cb641689c
commit
1b94868197
15 changed files with 225 additions and 17 deletions
|
@ -638,8 +638,9 @@ module.exports = Lank = class Lank extends CocoClass
|
|||
|
||||
onDialogue: (e) ->
|
||||
return unless @thang?.id is e.spriteID
|
||||
label = @addLabel 'dialogue', Label.STYLE_DIALOGUE
|
||||
label.setText e.blurb or '...'
|
||||
unless @thang?.id is 'Hero Placeholder' # Don't show these for heroes, because they aren't actually first-person, just LevelDialogueView narration
|
||||
label = @addLabel 'dialogue', Label.STYLE_DIALOGUE
|
||||
label.setText e.blurb or '...'
|
||||
sound = e.sound ? AudioPlayer.soundForDialogue e.message, @thangType.get 'soundTriggers'
|
||||
@dialogueSoundInstance?.stop()
|
||||
if @dialogueSoundInstance = @playSound sound, false
|
||||
|
|
|
@ -235,7 +235,6 @@
|
|||
tome_available_spells: "Available Spells"
|
||||
tome_your_skills: "Your Skills"
|
||||
tome_current_method: "Current Method"
|
||||
hud_continue: "Continue (shift+space)"
|
||||
hud_continue_short: "Continue"
|
||||
code_saved: "Code Saved"
|
||||
skip_tutorial: "Skip (esc)"
|
||||
|
|
|
@ -50,6 +50,8 @@ $level-resize-transition-time: 0.5s
|
|||
#stop-real-time-playback-button
|
||||
display: block
|
||||
z-index: 20
|
||||
#level-dialogue-view
|
||||
display: none
|
||||
|
||||
.level-content
|
||||
margin: 0px auto
|
||||
|
|
|
@ -8,9 +8,11 @@
|
|||
overflow: visible
|
||||
|
||||
&.controls-disabled
|
||||
@include opacity(0.5)
|
||||
pointer-events: none
|
||||
|
||||
.wood-background, .hinge, .avatar-wrapper-container, .center
|
||||
@include filter(brightness(50%))
|
||||
|
||||
.wood-background
|
||||
position: absolute
|
||||
left: 0
|
||||
|
@ -19,7 +21,7 @@
|
|||
background-size: auto 100%
|
||||
width: 100%
|
||||
height: 100px
|
||||
z-index: 2
|
||||
z-index: 4
|
||||
|
||||
.hinge
|
||||
position: absolute
|
||||
|
@ -28,7 +30,7 @@
|
|||
width: 27px
|
||||
height: 44px
|
||||
background-size: contain
|
||||
z-index: 2
|
||||
z-index: 4
|
||||
pointer-events: none
|
||||
|
||||
.hinge-0
|
||||
|
@ -50,7 +52,7 @@
|
|||
left: 18%
|
||||
left: -webkit-calc(50% - (560px - 100px) / 2 - 10px)
|
||||
left: calc(50% - (560px - 100px) / 2 - 10px)
|
||||
z-index: 3
|
||||
z-index: 5
|
||||
|
||||
.thang-canvas-wrapper
|
||||
width: 80px
|
||||
|
@ -99,7 +101,7 @@
|
|||
font-family: Open Sans Condensed
|
||||
font-weight: bold
|
||||
font-size: 16px
|
||||
z-index: 2
|
||||
z-index: 4
|
||||
@include transition(0.5s ease)
|
||||
|
||||
&:hover
|
||||
|
|
91
app/styles/play/level/level-dialogue-view.sass
Normal file
91
app/styles/play/level/level-dialogue-view.sass
Normal file
|
@ -0,0 +1,91 @@
|
|||
@import "app/styles/mixins"
|
||||
@import "app/styles/bootstrap/variables"
|
||||
|
||||
#level-dialogue-view
|
||||
+keyframes(speakingPulse)
|
||||
from
|
||||
@include box-shadow(0px 0px 8px #333)
|
||||
color: white
|
||||
50%
|
||||
@include box-shadow(0px 0px 35px skyblue)
|
||||
color: skyblue
|
||||
to
|
||||
@include box-shadow(0px 0px 8px #333)
|
||||
color: white
|
||||
|
||||
width: 417px
|
||||
height: 296px
|
||||
background: transparent url(/images/level/code_palette_wood_background.png)
|
||||
background-size: 100% auto
|
||||
position: absolute
|
||||
bottom: -296px + 40px
|
||||
//left: -webkit-calc(27.5% - 417px / 2)
|
||||
left: -webkit-calc(55% - 417px)
|
||||
// Bounce in
|
||||
@include transition(1s cubic-bezier(.17,.89,.42,1.36))
|
||||
z-index: 2
|
||||
|
||||
&.active
|
||||
display: block
|
||||
bottom: -20px
|
||||
|
||||
&.speaking
|
||||
.dialogue-area
|
||||
.bubble
|
||||
@include animation(speakingPulse 1.5s infinite)
|
||||
|
||||
.dialogue-area
|
||||
position: relative
|
||||
height: 100%
|
||||
width: 100%
|
||||
z-index: 1
|
||||
|
||||
.bubble
|
||||
position: relative
|
||||
margin: 20px
|
||||
padding: 10px 10px 30px 10px
|
||||
color: white
|
||||
font-weight: bold
|
||||
background: rgb(45, 35, 234)
|
||||
border: black solid 1px
|
||||
border-radius: 10px
|
||||
font-size: 18px
|
||||
line-height: 20px
|
||||
|
||||
strong
|
||||
color: #09B057
|
||||
|
||||
.hud-hint
|
||||
font-weight: normal
|
||||
color: #ddd
|
||||
font-size: 14px
|
||||
line-height: 16px
|
||||
vertical-align: middle
|
||||
|
||||
.enter
|
||||
position: absolute
|
||||
right: 10px
|
||||
bottom: 10px
|
||||
div.dot
|
||||
background: #337
|
||||
width: 8px
|
||||
height: 8px
|
||||
position: absolute
|
||||
right: 8px
|
||||
top: 9px
|
||||
border-radius: 5px
|
||||
|
||||
button, .alert
|
||||
padding: 2px 5px
|
||||
|
||||
.enter button.with-dot
|
||||
padding-right: 20px
|
||||
|
||||
h3
|
||||
margin: 0
|
||||
font-size: 16px
|
||||
line-height: 16px
|
||||
color: #338
|
||||
|
||||
button
|
||||
margin-left: 10px
|
|
@ -12,7 +12,11 @@
|
|||
background-size: 100% 100%
|
||||
// Counteract 50px height of absolutely positioned control bar, but overlap by 10px of jagged transparent top.
|
||||
margin-top: 50px - 10px
|
||||
z-index: 2
|
||||
z-index: 3
|
||||
|
||||
&.controls-disabled
|
||||
pointer-events: none
|
||||
@include filter(brightness(50%))
|
||||
|
||||
button
|
||||
font-size: 26px
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
&.read-only
|
||||
background: linear-gradient(to bottom, rgba(0,0,0,0.25) 0%,rgba(0,0,0,0.25) 100%), url(/images/level/code_editor_top_bar_wood_background.png)
|
||||
background-size: 100% 100%
|
||||
> *
|
||||
> *:not(.spell-tool-buttons)
|
||||
@include opacity(0.5)
|
||||
|
||||
.thang-avatar-view
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
|
||||
#thang-hud
|
||||
|
||||
#level-dialogue-view
|
||||
|
||||
button.btn.btn-lg.btn-warning.banner.header-font#stop-real-time-playback-button(title="Stop real-time playback", data-i18n="play_level.skip") Skip
|
||||
|
||||
.footer
|
||||
|
|
2
app/templates/play/level/level-dialogue-view.jade
Normal file
2
app/templates/play/level/level-dialogue-view.jade
Normal file
|
@ -0,0 +1,2 @@
|
|||
.dialogue-area
|
||||
p.bubble.dialogue-bubble
|
|
@ -495,9 +495,9 @@ requiredGearByLevel =
|
|||
'descending-further': {feet: 'simple-boots', 'programming-book': 'programmaticon-i'}
|
||||
'the-second-kithmaze': {feet: 'simple-boots', 'programming-book': 'programmaticon-i'}
|
||||
'dread-door': {'right-hand': 'simple-sword', 'programming-book': 'programmaticon-i'}
|
||||
'known-enemy': {'right-hand': 'simple-sword', 'programming-book': 'programmaticon-i'}
|
||||
'master-of-names': {feet: 'simple-boots', 'right-hand': 'simple-sword', 'programming-book': 'programmaticon-i', eyes: 'crude-glasses'}
|
||||
'lowly-kithmen': {feet: 'simple-boots', 'right-hand': 'simple-sword', 'programming-book': 'programmaticon-i', eyes: 'crude-glasses'}
|
||||
'known-enemy': {'right-hand': 'simple-sword', 'programming-book': 'programmaticon-i', torso: 'leather-tunic'}
|
||||
'master-of-names': {feet: 'simple-boots', 'right-hand': 'simple-sword', 'programming-book': 'programmaticon-i', eyes: 'crude-glasses', torso: 'leather-tunic'}
|
||||
'lowly-kithmen': {feet: 'simple-boots', 'right-hand': 'simple-sword', 'programming-book': 'programmaticon-i', eyes: 'crude-glasses', torso: 'leather-tunic'}
|
||||
'closing-the-distance': {feet: 'simple-boots', 'right-hand': 'simple-sword', torso: 'leather-tunic', eyes: 'crude-glasses'}
|
||||
'tactical-strike': {feet: 'simple-boots', 'right-hand': 'simple-sword', torso: 'leather-tunic', eyes: 'crude-glasses'}
|
||||
'the-final-kithmaze': {feet: 'simple-boots', 'right-hand': 'simple-sword', torso: 'leather-tunic', 'programming-book': 'programmaticon-i', eyes: 'crude-glasses'}
|
||||
|
|
|
@ -9,7 +9,7 @@ module.exports = class DialogueAnimator
|
|||
@childrenToAdd = _.map(d[0].childNodes, (e) -> return e)
|
||||
@t0 = new Date()
|
||||
@charsAdded = 0
|
||||
@charsPerSecond = 50
|
||||
@charsPerSecond = 25
|
||||
|
||||
tick: ->
|
||||
if not @charsToAdd and not @childAnimator
|
||||
|
|
104
app/views/play/level/LevelDialogueView.coffee
Normal file
104
app/views/play/level/LevelDialogueView.coffee
Normal file
|
@ -0,0 +1,104 @@
|
|||
CocoView = require 'views/kinds/CocoView'
|
||||
template = require 'templates/play/level/level-dialogue-view'
|
||||
DialogueAnimator = require './DialogueAnimator'
|
||||
|
||||
module.exports = class LevelDialogueView extends CocoView
|
||||
id: 'level-dialogue-view'
|
||||
template: template
|
||||
|
||||
subscriptions:
|
||||
'sprite:speech-updated': 'onSpriteDialogue'
|
||||
'level:sprite-clear-dialogue': 'onSpriteClearDialogue'
|
||||
'level:shift-space-pressed': 'onShiftSpacePressed'
|
||||
'level:escape-pressed': 'onEscapePressed'
|
||||
'sprite:dialogue-sound-completed': 'onDialogueSoundCompleted'
|
||||
|
||||
events:
|
||||
'click': 'onClick'
|
||||
|
||||
onClick: (e) ->
|
||||
Backbone.Mediator.publish 'tome:focus-editor', {}
|
||||
|
||||
onFrameChanged: (e) ->
|
||||
@timeProgress = e.progress
|
||||
@update()
|
||||
|
||||
onSpriteDialogue: (e) ->
|
||||
return unless e.message
|
||||
@$el.addClass 'active speaking'
|
||||
@setMessage e.message, e.mood, e.responses
|
||||
|
||||
window.tracker?.trackEvent 'Heard Sprite', {message: e.message, label: e.message}, ['Google Analytics']
|
||||
|
||||
onDialogueSoundCompleted: ->
|
||||
@$el.removeClass 'speaking'
|
||||
|
||||
onSpriteClearDialogue: ->
|
||||
@$el.removeClass 'active speaking'
|
||||
|
||||
setMessage: (message, mood, responses) ->
|
||||
message = marked message
|
||||
# Fix old HTML icons like <i class='icon-play'></i> in the Markdown
|
||||
message = message.replace /<i class='(.+?)'><\/i>/, "<i class='$1'></i>"
|
||||
clearInterval(@messageInterval) if @messageInterval
|
||||
@bubble = $('.dialogue-bubble', @$el)
|
||||
@bubble.removeClass(@lastMood) if @lastMood
|
||||
@lastMood = mood
|
||||
@bubble.text('')
|
||||
group = $('<div class="enter secret"></div>')
|
||||
@bubble.append(group)
|
||||
if responses
|
||||
@lastResponses = responses
|
||||
for response in responses
|
||||
button = $('<button class="btn btn-small banner"></button>').text(response.text)
|
||||
button.addClass response.buttonClass if response.buttonClass
|
||||
group.append(button)
|
||||
response.button = $('button:last', group)
|
||||
else
|
||||
s = $.i18n.t('play_level.hud_continue_short', defaultValue: 'Continue')
|
||||
sk = $.i18n.t('play_level.skip_tutorial', defaultValue: 'skip: esc')
|
||||
if not @escapePressed
|
||||
group.append('<span class="hud-hint">' + sk + '</span>')
|
||||
group.append($('<button class="btn btn-small banner with-dot">' + s + ' <div class="dot"></div></button>'))
|
||||
@lastResponses = null
|
||||
@animator = new DialogueAnimator(message, @bubble)
|
||||
@messageInterval = setInterval(@addMoreMessage, 1000 / 30) # 30 FPS
|
||||
|
||||
addMoreMessage: =>
|
||||
if @animator.done()
|
||||
clearInterval(@messageInterval)
|
||||
@messageInterval = null
|
||||
$('.enter', @bubble).removeClass('secret').css('opacity', 0.0).delay(500).animate({opacity: 1.0}, 500, @animateEnterButton)
|
||||
if @lastResponses
|
||||
buttons = $('.enter button')
|
||||
for response, i in @lastResponses
|
||||
channel = response.channel.replace 'level-set-playing', 'level:set-playing' # Easier than migrating all those victory buttons.
|
||||
f = (r) => => setTimeout((-> Backbone.Mediator.publish(channel, r.event or {})), 10)
|
||||
$(buttons[i]).click(f(response))
|
||||
else
|
||||
$('.enter', @bubble).click(-> Backbone.Mediator.publish('script:end-current-script', {}))
|
||||
return
|
||||
@animator.tick()
|
||||
|
||||
onShiftSpacePressed: (e) ->
|
||||
@shiftSpacePressed = (@shiftSpacePressed || 0) + 1
|
||||
# We don't need to handle script:end-current-script--that's done--but if we do have
|
||||
# custom buttons, then we need to trigger the one that should fire (the last one).
|
||||
# If we decide that always having the last one fire is bad, we should make it smarter.
|
||||
return unless @lastResponses?.length
|
||||
r = @lastResponses[@lastResponses.length - 1]
|
||||
channel = r.channel.replace 'level-set-playing', 'level:set-playing'
|
||||
_.delay (-> Backbone.Mediator.publish(channel, r.event or {})), 10
|
||||
|
||||
onEscapePressed: (e) ->
|
||||
@escapePressed = true
|
||||
|
||||
animateEnterButton: =>
|
||||
return unless @bubble
|
||||
button = $('.enter', @bubble)
|
||||
dot = $('.dot', button)
|
||||
dot.animate({opacity: 0.2}, 300).animate({opacity: 1.9}, 600, @animateEnterButton)
|
||||
|
||||
destroy: ->
|
||||
clearInterval(@messageInterval) if @messageInterval
|
||||
super()
|
|
@ -1,7 +1,6 @@
|
|||
CocoView = require 'views/kinds/CocoView'
|
||||
template = require 'templates/play/level/hud'
|
||||
prop_template = require 'templates/play/level/hud_prop'
|
||||
DialogueAnimator = require './DialogueAnimator'
|
||||
|
||||
module.exports = class LevelHUDView extends CocoView
|
||||
id: 'thang-hud'
|
||||
|
@ -180,6 +179,4 @@ module.exports = class LevelHUDView extends CocoView
|
|||
|
||||
destroy: ->
|
||||
@stage?.stopTalking()
|
||||
clearInterval(@messageInterval) if @messageInterval
|
||||
clearTimeout @hintNextSelectionTimeout if @hintNextSelectionTimeout
|
||||
super()
|
||||
|
|
|
@ -141,6 +141,7 @@ module.exports = class LevelPlaybackView extends CocoView
|
|||
console.warn('error disabling scrubber', error)
|
||||
@timePopup?.disable()
|
||||
$('#volume-button', @$el).removeClass('disabled')
|
||||
@$el.addClass 'controls-disabled'
|
||||
|
||||
onEnableControls: (e) ->
|
||||
return if @realTime
|
||||
|
@ -152,6 +153,7 @@ module.exports = class LevelPlaybackView extends CocoView
|
|||
catch error
|
||||
console.warn('error enabling scrubber', error)
|
||||
@timePopup?.enable()
|
||||
@$el.removeClass 'controls-disabled'
|
||||
|
||||
onSetPlaying: (e) ->
|
||||
@playing = (e ? {}).playing ? true
|
||||
|
|
|
@ -27,6 +27,7 @@ ProblemAlertView = require './tome/ProblemAlertView'
|
|||
TomeView = require './tome/TomeView'
|
||||
ChatView = require './LevelChatView'
|
||||
HUDView = require './LevelHUDView'
|
||||
LevelDialogueView = require './LevelDialogueView'
|
||||
ControlBarView = require './ControlBarView'
|
||||
LevelPlaybackView = require './LevelPlaybackView'
|
||||
GoalsView = require './LevelGoalsView'
|
||||
|
@ -246,6 +247,7 @@ module.exports = class PlayLevelView extends RootView
|
|||
@insertSubView new LevelFlagsView world: @world if (@levelID in ['sky-span', 'coinucopia']) or @level.get('type', true) in ['hero-ladder', 'hero-coop'] # TODO: figure out when flags are available
|
||||
@insertSubView new GoldView {}
|
||||
@insertSubView new HUDView {level: @level}
|
||||
@insertSubView new LevelDialogueView {level: @level}
|
||||
@insertSubView new ChatView levelID: @levelID, sessionID: @session.id, session: @session
|
||||
if @level.get('type') in ['ladder', 'hero-ladder']
|
||||
@insertSubView new MultiplayerStatusView levelID: @levelID, session: @session, level: @level
|
||||
|
|
Loading…
Reference in a new issue