mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-11-28 18:15:52 -05:00
Merge branch 'master' into production
This commit is contained in:
commit
9b62f2c7bf
6 changed files with 202 additions and 126 deletions
|
@ -86,6 +86,12 @@
|
||||||
-ms-flex-pack: justify
|
-ms-flex-pack: justify
|
||||||
justify-content: space-between
|
justify-content: space-between
|
||||||
|
|
||||||
|
@mixin flex-justify-center()
|
||||||
|
-webkit-box-pack: center
|
||||||
|
-webkit-justify-content: center
|
||||||
|
-ms-flex-pack: center
|
||||||
|
justify-content: center
|
||||||
|
|
||||||
@mixin flex-align-content-start()
|
@mixin flex-align-content-start()
|
||||||
-webkit-align-content: flex-start
|
-webkit-align-content: flex-start
|
||||||
-ms-flex-align-content: flex-start
|
-ms-flex-align-content: flex-start
|
||||||
|
|
|
@ -1,18 +1,31 @@
|
||||||
|
@import "app/styles/mixins"
|
||||||
|
@import "app/styles/bootstrap/variables"
|
||||||
|
|
||||||
#hero-victory-modal
|
#hero-victory-modal
|
||||||
|
//- Top-level modal container
|
||||||
|
.modal-dialog
|
||||||
|
margin-top: 15px
|
||||||
|
padding-top: 0
|
||||||
|
|
||||||
//- Header
|
//- Header
|
||||||
|
|
||||||
.background-wrapper
|
.background-wrapper
|
||||||
background: url("/images/pages/play/level/modal/victory_modal_background.png")
|
//background: url("/images/pages/play/level/modal/victory_modal_background.png")
|
||||||
height: 650px
|
|
||||||
width: 550px
|
width: 550px
|
||||||
|
border-width: 25px
|
||||||
|
border-image: url("/images/pages/play/level/modal/victory_modal_background.png") 25 fill round
|
||||||
|
border-radius: 10px
|
||||||
|
|
||||||
#victory-header
|
#victory-header
|
||||||
display: block
|
display: block
|
||||||
margin: 40px auto 0
|
margin: 15px auto 0
|
||||||
|
@include transition(0.25s ease-in)
|
||||||
|
|
||||||
|
&.out
|
||||||
|
margin-top: -100px
|
||||||
|
|
||||||
.modal-header
|
.modal-header
|
||||||
height: 110px
|
height: 85px
|
||||||
border: none
|
border: none
|
||||||
|
|
||||||
|
|
||||||
|
@ -28,15 +41,12 @@
|
||||||
margin: 5px auto
|
margin: 5px auto
|
||||||
position: relative
|
position: relative
|
||||||
|
|
||||||
-webkit-transition-duration: 1s
|
@include transition-duration(1s)
|
||||||
-moz-transition-duration: 1s
|
|
||||||
-o-transition-duration: 1s
|
|
||||||
transition-duration: 1s
|
|
||||||
|
|
||||||
-webkit-filter: grayscale(100%)
|
-webkit-filter: grayscale(100%) brightness(75%)
|
||||||
-moz-filter: grayscale(100%)
|
-moz-filter: grayscale(100%) brightness(75%)
|
||||||
-o-filter: grayscale(100%)
|
-o-filter: grayscale(100%) brightness(75%)
|
||||||
filter: grayscale(100%)
|
filter: grayscale(100%) brightness(75%)
|
||||||
|
|
||||||
&.earned
|
&.earned
|
||||||
-webkit-filter: none
|
-webkit-filter: none
|
||||||
|
@ -44,7 +54,11 @@
|
||||||
-o-filter: none
|
-o-filter: none
|
||||||
filter: none
|
filter: none
|
||||||
|
|
||||||
|
.achievement-description
|
||||||
|
@include opacity(1)
|
||||||
|
|
||||||
.achievement-description
|
.achievement-description
|
||||||
|
@include opacity(0.75)
|
||||||
position: absolute
|
position: absolute
|
||||||
text-align: center
|
text-align: center
|
||||||
left: 95px
|
left: 95px
|
||||||
|
@ -61,18 +75,29 @@
|
||||||
right: 23px
|
right: 23px
|
||||||
top: 41px
|
top: 41px
|
||||||
bottom: 18px
|
bottom: 18px
|
||||||
|
@include flexbox()
|
||||||
|
@include flex-justify-center()
|
||||||
|
|
||||||
//- Reward panels
|
//- Reward panels
|
||||||
|
|
||||||
.reward-panel
|
.reward-panel
|
||||||
background: url("/images/pages/play/level/modal/reward_plate.png")
|
|
||||||
background: url("/images/pages/play/level/modal/reward_plate.png")
|
background: url("/images/pages/play/level/modal/reward_plate.png")
|
||||||
width: 77px
|
width: 77px
|
||||||
height: 85px
|
height: 85px
|
||||||
float: left
|
float: left
|
||||||
margin: 0 1.8px
|
margin: 0 1.8px
|
||||||
position: relative
|
position: relative
|
||||||
|
z-index: 1
|
||||||
|
@include transition(0.25s ease)
|
||||||
|
|
||||||
|
&.animating
|
||||||
|
@include scale(1.5)
|
||||||
|
z-index: 2
|
||||||
|
|
||||||
|
.reward-text
|
||||||
|
font-size: 18px
|
||||||
|
overflow: visible
|
||||||
|
bottom: 9px
|
||||||
|
|
||||||
.reward-image-container
|
.reward-image-container
|
||||||
top: 8px
|
top: 8px
|
||||||
|
@ -81,21 +106,11 @@
|
||||||
width: 56px
|
width: 56px
|
||||||
position: relative
|
position: relative
|
||||||
|
|
||||||
-webkit-transform: scale(0, 0)
|
@include scale(0)
|
||||||
-moz-transform: scale(0, 0)
|
@include transition-duration(0.5s)
|
||||||
-o-transform: scale(0, 0)
|
|
||||||
transform: scale(0, 0)
|
|
||||||
|
|
||||||
-webkit-transition-duration: 0.5s
|
|
||||||
-moz-transition-duration: 0.5s
|
|
||||||
-o-transition-duration: 0.5s
|
|
||||||
transition-duration: 0.5s
|
|
||||||
|
|
||||||
&.show
|
&.show
|
||||||
-webkit-transform: scale(1, 1)
|
@include scale(1)
|
||||||
-moz-transform: scale(1, 1)
|
|
||||||
-o-transform: scale(1, 1)
|
|
||||||
transform: scale(1, 1)
|
|
||||||
|
|
||||||
img
|
img
|
||||||
margin: 0
|
margin: 0
|
||||||
|
@ -103,17 +118,8 @@
|
||||||
top: 50%
|
top: 50%
|
||||||
left: 50%
|
left: 50%
|
||||||
margin-right: -50%
|
margin-right: -50%
|
||||||
|
@include transition-duration(0.5s)
|
||||||
-webkit-transition-duration: 0.5s
|
@include translate(-50%, -50%)
|
||||||
-moz-transition-duration: 0.5s
|
|
||||||
-o-transition-duration: 0.5s
|
|
||||||
transition-duration: 0.5s
|
|
||||||
|
|
||||||
-webkit-transform: translate(-50%, -50%)
|
|
||||||
-moz-transform: translate(-50%, -50%)
|
|
||||||
-o-transform: translate(-50%, -50%)
|
|
||||||
transform: translate(-50%, -50%)
|
|
||||||
|
|
||||||
max-width: 56px
|
max-width: 56px
|
||||||
max-height: 55px
|
max-height: 55px
|
||||||
|
|
||||||
|
@ -131,58 +137,64 @@
|
||||||
overflow: hidden
|
overflow: hidden
|
||||||
text-overflow: ellipsis
|
text-overflow: ellipsis
|
||||||
|
|
||||||
|
|
||||||
//- Pulse effect
|
//- Pulse effect
|
||||||
|
|
||||||
@-webkit-keyframes pulse
|
+keyframes(rewardPulse)
|
||||||
from
|
from
|
||||||
-webkit-transform: translate(-50%, -50%) scale(1.0)
|
max-width: 56px
|
||||||
|
max-height: 55px
|
||||||
50%
|
50%
|
||||||
-webkit-transform: translate(-50%, -50%) scale(1.3)
|
width: 66px
|
||||||
|
max-width: 66px
|
||||||
|
max-height: 66px
|
||||||
to
|
to
|
||||||
-webkit-transform: translate(-50%, -50%) scale(1.0)
|
max-width: 56px
|
||||||
|
max-height: 55px
|
||||||
|
|
||||||
@-moz-keyframes pulse
|
.xp .pulse
|
||||||
from
|
@include animation(rewardPulse 0.15s infinite)
|
||||||
-moz-transform: translate(-50%, -50%) scale(1.0)
|
|
||||||
50%
|
|
||||||
-moz-transform: translate(-50%, -50%) scale(1.3)
|
|
||||||
to
|
|
||||||
-moz-transform: translate(-50%, -50%) scale(1.0)
|
|
||||||
|
|
||||||
@-o-keyframes pulse
|
|
||||||
from
|
|
||||||
-o-transform: translate(-50%, -50%) scale(1.0)
|
|
||||||
50%
|
|
||||||
-o-transform: translate(-50%, -50%) scale(1.3)
|
|
||||||
to
|
|
||||||
-o-transform: translate(-50%, -50%) scale(1.0)
|
|
||||||
|
|
||||||
@keyframes pulse
|
|
||||||
from
|
|
||||||
transform: translate(-50%, -50%) scale(1.0)
|
|
||||||
50%
|
|
||||||
transform: translate(-50%, -50%) scale(1.3)
|
|
||||||
to
|
|
||||||
transform: translate(-50%, -50%) scale(1.0)
|
|
||||||
|
|
||||||
.pulse
|
|
||||||
-webkit-animation: pulse 0.5s infinite
|
|
||||||
-moz-animation: pulse 0.5s infinite
|
|
||||||
-o-animation: pulse 0.5s infinite
|
|
||||||
animation: pulse 0.5s infinite
|
|
||||||
|
|
||||||
|
.gems .pulse
|
||||||
|
@include animation(rewardPulse 0.25s infinite)
|
||||||
|
|
||||||
//- Footer
|
//- Footer
|
||||||
|
|
||||||
.modal-content
|
.modal-content
|
||||||
height: 650px // so the footer appears at the bottom
|
padding-bottom: 50px // so the footer appears at the bottom
|
||||||
|
|
||||||
|
&.with-sign-up .modal-content
|
||||||
|
padding-bottom: 100px // need more space for signup poke
|
||||||
|
|
||||||
.modal-footer
|
.modal-footer
|
||||||
position: absolute
|
position: absolute
|
||||||
bottom: 20px
|
bottom: -20px
|
||||||
left: 20px
|
left: 20px
|
||||||
right: 20px
|
right: 20px
|
||||||
|
|
||||||
#totals
|
#totals
|
||||||
color: white
|
color: white
|
||||||
|
|
||||||
|
p.sign-up-poke
|
||||||
|
position: absolute
|
||||||
|
bottom: 60px
|
||||||
|
right: 20px
|
||||||
|
color: white
|
||||||
|
|
||||||
|
.sign-up-button
|
||||||
|
float: right
|
||||||
|
margin-left: 10px
|
||||||
|
|
||||||
|
html.no-borderimage
|
||||||
|
#hero-victory-modal
|
||||||
|
.background-wrapper
|
||||||
|
background: url("/images/pages/play/level/modal/victory_modal_background.png")
|
||||||
|
height: 650px
|
||||||
|
#victory-header
|
||||||
|
margin-top: 40px
|
||||||
|
.modal-header
|
||||||
|
height: 110px
|
||||||
|
.modal-content
|
||||||
|
height: 650px
|
||||||
|
padding-bottom: 0
|
||||||
|
.modal-footer
|
||||||
|
bottom: 20px
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
extends /templates/modal/modal_base
|
extends /templates/modal/modal_base
|
||||||
block modal-header-content
|
block modal-header-content
|
||||||
img(src="/images/pages/play/level/modal/victory_word.png")#victory-header
|
img(src="/images/pages/play/level/modal/victory_word.png")#victory-header.out
|
||||||
|
|
||||||
block modal-body-content
|
block modal-body-content
|
||||||
|
|
||||||
|
@ -14,21 +14,21 @@ block modal-body-content
|
||||||
div.achievement-rewards
|
div.achievement-rewards
|
||||||
- var worth = achievement.get('worth', true);
|
- var worth = achievement.get('worth', true);
|
||||||
if worth
|
if worth
|
||||||
.reward-panel.numerical(data-number=worth, data-number-unit='xp')
|
.reward-panel.numerical.xp(data-number=worth, data-number-unit='xp')
|
||||||
.reward-image-container(class=animate?'':'show')
|
.reward-image-container(class=animate?'':'show')
|
||||||
img(src="/images/pages/play/level/modal/reward_icon_xp.png")
|
img(src="/images/pages/play/level/modal/reward_icon_xp.png")
|
||||||
.reward-text= animate ? 'x0' : '+'+worth
|
.reward-text= animate ? '+0' : '+'+worth
|
||||||
|
|
||||||
if rewards.gems
|
if rewards.gems
|
||||||
.reward-panel.numerical(data-number=rewards.gems, data-number-unit='gem')
|
.reward-panel.numerical.gems(data-number=rewards.gems, data-number-unit='gem')
|
||||||
.reward-image-container(class=animate?'':'show')
|
.reward-image-container(class=animate?'':'show')
|
||||||
img(src="/images/pages/play/level/modal/reward_icon_gems.png")
|
img(src="/images/pages/play/level/modal/reward_icon_gems.png")
|
||||||
.reward-text= animate ? 'x0' : '+'+rewards.gems
|
.reward-text= animate ? '+0' : '+'+rewards.gems
|
||||||
|
|
||||||
if rewards.heroes
|
if rewards.heroes
|
||||||
for hero in rewards.heroes
|
for hero in rewards.heroes
|
||||||
- var hero = thangTypes[hero];
|
- var hero = thangTypes[hero];
|
||||||
.reward-panel
|
.reward-panel.hero
|
||||||
.reward-image-container(class=animate?'':'show')
|
.reward-image-container(class=animate?'':'show')
|
||||||
img(src=hero.getPortraitURL())
|
img(src=hero.getPortraitURL())
|
||||||
.reward-text= hero.get('name')
|
.reward-text= hero.get('name')
|
||||||
|
@ -36,7 +36,7 @@ block modal-body-content
|
||||||
if rewards.items
|
if rewards.items
|
||||||
for item in rewards.items
|
for item in rewards.items
|
||||||
- var item = thangTypes[item];
|
- var item = thangTypes[item];
|
||||||
.reward-panel
|
.reward-panel.item
|
||||||
.reward-image-container(class=animate?'':'show')
|
.reward-image-container(class=animate?'':'show')
|
||||||
img(src=item.getPortraitURL())
|
img(src=item.getPortraitURL())
|
||||||
.reward-text= item.get('name')
|
.reward-text= item.get('name')
|
||||||
|
@ -52,3 +52,8 @@ block modal-footer-content
|
||||||
|
|
||||||
button.btn.btn-warning.hide#saving-progress-label(disabled, data-i18n="play_level.victory_saving_progress") Saving Progress
|
button.btn.btn-warning.hide#saving-progress-label(disabled, data-i18n="play_level.victory_saving_progress") Saving Progress
|
||||||
a.btn.btn-success.world-map-button.hide#continue-button(href="/play-hero", data-dismiss="modal", data-i18n="play_level.victory_play_continue") Continue
|
a.btn.btn-success.world-map-button.hide#continue-button(href="/play-hero", data-dismiss="modal", data-i18n="play_level.victory_play_continue") Continue
|
||||||
|
|
||||||
|
if me.get('anonymous')
|
||||||
|
p.sign-up-poke
|
||||||
|
button.btn.btn-success.sign-up-button.btn-large(data-toggle="coco-modal", data-target="modal/SignupModal", data-i18n="play_level.victory_sign_up") Sign Up to Save Progress
|
||||||
|
span(data-i18n="play_level.victory_sign_up_poke") Want to save your code? Create a free account!
|
||||||
|
|
|
@ -25,6 +25,7 @@ module.exports = class HeroVictoryModal extends ModalView
|
||||||
@achievements = @supermodel.loadCollection(achievements, 'achievements').model
|
@achievements = @supermodel.loadCollection(achievements, 'achievements').model
|
||||||
@listenToOnce @achievements, 'sync', @onAchievementsLoaded
|
@listenToOnce @achievements, 'sync', @onAchievementsLoaded
|
||||||
@readyToContinue = false
|
@readyToContinue = false
|
||||||
|
Backbone.Mediator.publish 'audio-player:play-sound', trigger: 'victory'
|
||||||
|
|
||||||
onAchievementsLoaded: ->
|
onAchievementsLoaded: ->
|
||||||
thangTypeOriginals = []
|
thangTypeOriginals = []
|
||||||
|
@ -74,19 +75,27 @@ module.exports = class HeroVictoryModal extends ModalView
|
||||||
c.achievements = @achievements.models
|
c.achievements = @achievements.models
|
||||||
|
|
||||||
# for testing the three states
|
# for testing the three states
|
||||||
# if c.achievements.length
|
#if c.achievements.length
|
||||||
# c.achievements = [c.achievements[0].clone(), c.achievements[0].clone(), c.achievements[0].clone()]
|
# c.achievements = [c.achievements[0].clone(), c.achievements[0].clone(), c.achievements[0].clone()]
|
||||||
# for achievement, index in c.achievements
|
#for achievement, index in c.achievements
|
||||||
# achievement.completed = index > 0
|
## achievement.completed = index > 0
|
||||||
# achievement.completedAWhileAgo = index > 1
|
## achievement.completedAWhileAgo = index > 1
|
||||||
|
# achievement.completed = true
|
||||||
|
# achievement.completedAWhileAgo = false
|
||||||
|
# achievement.attributes.worth = (index + 1) * achievement.get('worth')
|
||||||
|
# rewards = achievement.get('rewards')
|
||||||
|
# rewards.gems *= (index + 1)
|
||||||
|
|
||||||
c.thangTypes = @thangTypes
|
c.thangTypes = @thangTypes
|
||||||
|
c.me = me
|
||||||
return c
|
return c
|
||||||
|
|
||||||
afterRender: ->
|
afterRender: ->
|
||||||
super()
|
super()
|
||||||
return unless @supermodel.finished()
|
return unless @supermodel.finished()
|
||||||
|
@$el.addClass 'with-sign-up' if me.get('anonymous')
|
||||||
@updateSavingProgressStatus()
|
@updateSavingProgressStatus()
|
||||||
|
@$el.find('#victory-header').delay(250).queue(-> $(@).removeClass('out').dequeue())
|
||||||
complete = _.once(_.bind(@beginAnimateNumbers, @))
|
complete = _.once(_.bind(@beginAnimateNumbers, @))
|
||||||
@animatedPanels = $()
|
@animatedPanels = $()
|
||||||
panels = @$el.find('.achievement-panel')
|
panels = @$el.find('.achievement-panel')
|
||||||
|
@ -94,15 +103,15 @@ module.exports = class HeroVictoryModal extends ModalView
|
||||||
panel = $(panel)
|
panel = $(panel)
|
||||||
continue unless panel.data('animate')
|
continue unless panel.data('animate')
|
||||||
@animatedPanels = @animatedPanels.add(panel)
|
@animatedPanels = @animatedPanels.add(panel)
|
||||||
panel.delay(500)
|
panel.delay(500) # Waiting for victory header to show up and fall
|
||||||
panel.queue(->
|
panel.queue(->
|
||||||
$(this).addClass('earned') # animate out the grayscale
|
$(@).addClass('earned') # animate out the grayscale
|
||||||
$(this).dequeue()
|
$(@).dequeue()
|
||||||
)
|
)
|
||||||
panel.delay(500)
|
panel.delay(500)
|
||||||
panel.queue(->
|
panel.queue(->
|
||||||
$(this).find('.reward-image-container').addClass('show')
|
$(@).find('.reward-image-container').addClass('show')
|
||||||
$(this).dequeue()
|
$(@).dequeue()
|
||||||
)
|
)
|
||||||
panel.delay(500)
|
panel.delay(500)
|
||||||
panel.queue(-> complete())
|
panel.queue(-> complete())
|
||||||
|
@ -116,27 +125,59 @@ module.exports = class HeroVictoryModal extends ModalView
|
||||||
unit: $(panel).data('number-unit')
|
unit: $(panel).data('number-unit')
|
||||||
})
|
})
|
||||||
|
|
||||||
# TODO: mess with this more later. Doesn't seem to work, often times will pulse background red rather than animate
|
|
||||||
# itemPanel.rootEl.find('.reward-image-container img').addClass('pulse') for itemPanel in @numericalItemPanels
|
|
||||||
@numberAnimationStart = new Date()
|
|
||||||
@totalXP = 0
|
@totalXP = 0
|
||||||
@totalXP += panel.number for panel in @numericalItemPanels when panel.unit is 'xp'
|
@totalXP += panel.number for panel in @numericalItemPanels when panel.unit is 'xp'
|
||||||
@totalGems = 0
|
@totalGems = 0
|
||||||
@totalGems += panel.number for panel in @numericalItemPanels when panel.unit is 'gem'
|
@totalGems += panel.number for panel in @numericalItemPanels when panel.unit is 'gem'
|
||||||
@gemEl = $('#gem-total')
|
@gemEl = $('#gem-total')
|
||||||
@XPEl = $('#xp-total')
|
@XPEl = $('#xp-total')
|
||||||
@numberAnimationInterval = setInterval(@tickNumberAnimation, 15 / 1000)
|
@totalXPAnimated = @totalGemsAnimated = @lastTotalXP = @lastTotalGems = 0
|
||||||
|
@numberAnimationStart = new Date()
|
||||||
|
@numberAnimationInterval = setInterval(@tickNumberAnimation, 1000 / 60)
|
||||||
|
|
||||||
tickNumberAnimation: =>
|
tickNumberAnimation: =>
|
||||||
pct = Math.min(1, (new Date() - @numberAnimationStart) / 1500)
|
# TODO: make sure the animation pulses happen when the numbers go up and sounds play (up to a max speed)
|
||||||
panel.textEl.text('+'+parseInt(panel.number*pct)) for panel in @numericalItemPanels
|
return @endAnimateNumbers() unless panel = @numericalItemPanels[0]
|
||||||
@XPEl.text('+'+parseInt(@totalXP * pct))
|
duration = Math.log10(panel.number + 1) * 1000
|
||||||
@gemEl.text('+'+parseInt(@totalGems * pct))
|
ratio = @getEaseRatio (new Date() - @numberAnimationStart), duration
|
||||||
@endAnimateNumbers() if pct is 1
|
if panel.unit is 'xp'
|
||||||
|
totalXP = @totalXPAnimated + Math.floor(ratio * panel.number)
|
||||||
|
if totalXP isnt @lastTotalXP
|
||||||
|
panel.textEl.text('+' + totalXP)
|
||||||
|
@XPEl.text('+' + totalXP)
|
||||||
|
xpTrigger = 'xp-' + (totalXP % 6) # 6 xp sounds
|
||||||
|
Backbone.Mediator.publish 'audio-player:play-sound', trigger: xpTrigger, volume: 0.5 + ratio / 2
|
||||||
|
@lastTotalXP = totalXP
|
||||||
|
else
|
||||||
|
totalGems = @totalGemsAnimated + Math.floor(ratio * panel.number)
|
||||||
|
if totalGems isnt @lastTotalGems
|
||||||
|
panel.textEl.text('+' + totalGems)
|
||||||
|
@gemEl.text('+' + totalGems)
|
||||||
|
gemTrigger = 'gem-' + (parseInt(panel.number * ratio) % 4) # 4 gem sounds
|
||||||
|
Backbone.Mediator.publish 'audio-player:play-sound', trigger: gemTrigger, volume: 0.5 + ratio / 2
|
||||||
|
@lastTotalGems = totalGems
|
||||||
|
if ratio is 1
|
||||||
|
panel.rootEl.removeClass('animating').find('.reward-image-container img').removeClass('pulse')
|
||||||
|
@numberAnimationStart = new Date()
|
||||||
|
if panel.unit is 'xp'
|
||||||
|
@totalXPAnimated += panel.number
|
||||||
|
else
|
||||||
|
@totalGemsAnimated += panel.number
|
||||||
|
@numericalItemPanels.shift()
|
||||||
|
return
|
||||||
|
panel.rootEl.addClass('animating').find('.reward-image-container img').addClass('pulse')
|
||||||
|
|
||||||
|
getEaseRatio: (timeSinceStart, duration) ->
|
||||||
|
# Ease in/out quadratic - http://gizma.com/easing/
|
||||||
|
timeSinceStart = Math.min timeSinceStart, duration
|
||||||
|
t = 2 * timeSinceStart / duration
|
||||||
|
if t < 1
|
||||||
|
return 0.5 * t * t
|
||||||
|
--t
|
||||||
|
-0.5 * (t * (t - 2) - 1)
|
||||||
|
|
||||||
endAnimateNumbers: ->
|
endAnimateNumbers: ->
|
||||||
@$el.find('.pulse').removeClass('pulse')
|
clearInterval @numberAnimationInterval
|
||||||
clearInterval(@numberAnimationInterval)
|
|
||||||
@animationComplete = true
|
@animationComplete = true
|
||||||
@updateSavingProgressStatus()
|
@updateSavingProgressStatus()
|
||||||
|
|
||||||
|
@ -144,3 +185,9 @@ module.exports = class HeroVictoryModal extends ModalView
|
||||||
return unless @animationComplete
|
return unless @animationComplete
|
||||||
@$el.find('#saving-progress-label').toggleClass('hide', @readyToContinue)
|
@$el.find('#saving-progress-label').toggleClass('hide', @readyToContinue)
|
||||||
@$el.find('#continue-button').toggleClass('hide', not @readyToContinue)
|
@$el.find('#continue-button').toggleClass('hide', not @readyToContinue)
|
||||||
|
|
||||||
|
# TODO: award heroes/items and play an awesome sound when you get one
|
||||||
|
|
||||||
|
destroy: ->
|
||||||
|
clearInterval @numberAnimationInterval
|
||||||
|
super()
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
ProblemAlertView = require './ProblemAlertView'
|
ProblemAlertView = require './ProblemAlertView'
|
||||||
Range = ace.require('ace/range').Range
|
Range = ace.require('ace/range').Range
|
||||||
UserCodeProblem = require 'models/UserCodeProblem'
|
|
||||||
|
|
||||||
module.exports = class Problem
|
module.exports = class Problem
|
||||||
annotation: null
|
annotation: null
|
||||||
|
@ -11,7 +10,6 @@ module.exports = class Problem
|
||||||
@buildAlertView() if withAlert
|
@buildAlertView() if withAlert
|
||||||
@buildMarkerRange() if isCast
|
@buildMarkerRange() if isCast
|
||||||
Backbone.Mediator.publish("problem:problem-created", line:@annotation.row, text: @annotation.text) if application.isIPadApp
|
Backbone.Mediator.publish("problem:problem-created", line:@annotation.row, text: @annotation.text) if application.isIPadApp
|
||||||
@saveUserCodeProblem() if isCast
|
|
||||||
|
|
||||||
destroy: ->
|
destroy: ->
|
||||||
unless @alertView?.destroyed
|
unless @alertView?.destroyed
|
||||||
|
@ -50,21 +48,3 @@ module.exports = class Problem
|
||||||
@ace.getSession().removeMarker @markerRange.id
|
@ace.getSession().removeMarker @markerRange.id
|
||||||
@markerRange.start.detach()
|
@markerRange.start.detach()
|
||||||
@markerRange.end.detach()
|
@markerRange.end.detach()
|
||||||
|
|
||||||
saveUserCodeProblem: () ->
|
|
||||||
@userCodeProblem = new UserCodeProblem()
|
|
||||||
@userCodeProblem.set 'code', @aether.raw
|
|
||||||
if @aetherProblem.range
|
|
||||||
rawLines = @aether.raw.split '\n'
|
|
||||||
errorLines = rawLines.slice @aetherProblem.range[0].row, @aetherProblem.range[1].row + 1
|
|
||||||
@userCodeProblem.set 'codeSnippet', errorLines.join '\n'
|
|
||||||
@userCodeProblem.set 'errHint', @aetherProblem.hint if @aetherProblem.hint
|
|
||||||
@userCodeProblem.set 'errId', @aetherProblem.id if @aetherProblem.id
|
|
||||||
@userCodeProblem.set 'errLevel', @aetherProblem.level if @aetherProblem.level
|
|
||||||
@userCodeProblem.set 'errMessage', @aetherProblem.message if @aetherProblem.message
|
|
||||||
@userCodeProblem.set 'errRange', @aetherProblem.range if @aetherProblem.range
|
|
||||||
@userCodeProblem.set 'errType', @aetherProblem.type if @aetherProblem.type
|
|
||||||
@userCodeProblem.set 'language', @aether.language.id if @aether.language?.id
|
|
||||||
@userCodeProblem.set 'levelID', @levelID if @levelID
|
|
||||||
@userCodeProblem.save()
|
|
||||||
null
|
|
|
@ -8,6 +8,7 @@ Problem = require './Problem'
|
||||||
SpellDebugView = require './SpellDebugView'
|
SpellDebugView = require './SpellDebugView'
|
||||||
SpellToolbarView = require './SpellToolbarView'
|
SpellToolbarView = require './SpellToolbarView'
|
||||||
LevelComponent = require 'models/LevelComponent'
|
LevelComponent = require 'models/LevelComponent'
|
||||||
|
UserCodeProblem = require 'models/UserCodeProblem'
|
||||||
|
|
||||||
module.exports = class SpellView extends CocoView
|
module.exports = class SpellView extends CocoView
|
||||||
id: 'spell-view'
|
id: 'spell-view'
|
||||||
|
@ -63,6 +64,7 @@ module.exports = class SpellView extends CocoView
|
||||||
@listenTo(@session, 'change:multiplayer', @onMultiplayerChanged)
|
@listenTo(@session, 'change:multiplayer', @onMultiplayerChanged)
|
||||||
@spell = options.spell
|
@spell = options.spell
|
||||||
@problems = []
|
@problems = []
|
||||||
|
@savedProblems = {} # Cache saved user code problems to prevent duplicates
|
||||||
@writable = false unless me.team in @spell.permissions.readwrite # TODO: make this do anything
|
@writable = false unless me.team in @spell.permissions.readwrite # TODO: make this do anything
|
||||||
@highlightCurrentLine = _.throttle @highlightCurrentLine, 100
|
@highlightCurrentLine = _.throttle @highlightCurrentLine, 100
|
||||||
$(window).on 'resize', @onWindowResize
|
$(window).on 'resize', @onWindowResize
|
||||||
|
@ -486,6 +488,7 @@ module.exports = class SpellView extends CocoView
|
||||||
continue if key = aetherProblem.userInfo?.key and key of seenProblemKeys
|
continue if key = aetherProblem.userInfo?.key and key of seenProblemKeys
|
||||||
seenProblemKeys[key] = true if key
|
seenProblemKeys[key] = true if key
|
||||||
@problems.push problem = new Problem aether, aetherProblem, @ace, isCast and problemIndex is 0, isCast, @spell.levelID
|
@problems.push problem = new Problem aether, aetherProblem, @ace, isCast and problemIndex is 0, isCast, @spell.levelID
|
||||||
|
@saveUserCodeProblem(aether, aetherProblem) if isCast
|
||||||
annotations.push problem.annotation if problem.annotation
|
annotations.push problem.annotation if problem.annotation
|
||||||
@aceSession.setAnnotations annotations
|
@aceSession.setAnnotations annotations
|
||||||
@highlightCurrentLine aether.flow unless _.isEmpty aether.flow
|
@highlightCurrentLine aether.flow unless _.isEmpty aether.flow
|
||||||
|
@ -498,6 +501,29 @@ module.exports = class SpellView extends CocoView
|
||||||
Backbone.Mediator.publish 'tome:problems-updated', spell: @spell, problems: @problems, isCast: isCast
|
Backbone.Mediator.publish 'tome:problems-updated', spell: @spell, problems: @problems, isCast: isCast
|
||||||
@ace.resize()
|
@ace.resize()
|
||||||
|
|
||||||
|
saveUserCodeProblem: (aether, aetherProblem) ->
|
||||||
|
# Skip duplicate problems
|
||||||
|
hashValue = aether.raw + aetherProblem.message
|
||||||
|
return if hashValue of @savedProblems
|
||||||
|
@savedProblems[hashValue] = true
|
||||||
|
# Save new problem
|
||||||
|
@userCodeProblem = new UserCodeProblem()
|
||||||
|
@userCodeProblem.set 'code', aether.raw
|
||||||
|
if aetherProblem.range
|
||||||
|
rawLines = aether.raw.split '\n'
|
||||||
|
errorLines = rawLines.slice aetherProblem.range[0].row, aetherProblem.range[1].row + 1
|
||||||
|
@userCodeProblem.set 'codeSnippet', errorLines.join '\n'
|
||||||
|
@userCodeProblem.set 'errHint', aetherProblem.hint if aetherProblem.hint
|
||||||
|
@userCodeProblem.set 'errId', aetherProblem.id if aetherProblem.id
|
||||||
|
@userCodeProblem.set 'errLevel', aetherProblem.level if aetherProblem.level
|
||||||
|
@userCodeProblem.set 'errMessage', aetherProblem.message if aetherProblem.message
|
||||||
|
@userCodeProblem.set 'errRange', aetherProblem.range if aetherProblem.range
|
||||||
|
@userCodeProblem.set 'errType', aetherProblem.type if aetherProblem.type
|
||||||
|
@userCodeProblem.set 'language', aether.language.id if aether.language?.id
|
||||||
|
@userCodeProblem.set 'levelID', @spell.levelID if @spell.levelID
|
||||||
|
@userCodeProblem.save()
|
||||||
|
null
|
||||||
|
|
||||||
# Autocast:
|
# Autocast:
|
||||||
# Goes immediately if the code is a) changed and b) complete/valid and c) the cursor is at beginning or end of a line
|
# Goes immediately if the code is a) changed and b) complete/valid and c) the cursor is at beginning or end of a line
|
||||||
# We originally thought it would:
|
# We originally thought it would:
|
||||||
|
|
Loading…
Reference in a new issue