Merge branch 'master' into production

This commit is contained in:
Nick Winter 2014-11-10 15:18:06 -08:00
commit c9a7a2ea0a
11 changed files with 116 additions and 30 deletions

View file

@ -117,7 +117,7 @@ module.exports = class LevelLoader extends CocoClass
@loadDefaultComponentsForThangType itemThangType
@loadThangsRequiredByThangType itemThangType
@sessionDependenciesRegistered[session.id] = true
if _.size(@sessionDependenciesRegistered) is 2 and not (r for r in @worldNecessities when r?).length
if _.size(@sessionDependenciesRegistered) is 2 and @checkAllWorldNecessitiesRegisteredAndLoaded()
@onWorldNecessitiesLoaded()
consolidateFlagHistory: ->
@ -204,6 +204,8 @@ module.exports = class LevelLoader extends CocoClass
for thangType in thangNames.models
@loadDefaultComponentsForThangType(thangType)
@loadThangsRequiredByThangType(thangType)
@thangNamesLoaded = true
@onWorldNecessitiesLoaded() if @checkAllWorldNecessitiesRegisteredAndLoaded()
loadDefaultComponentsForThangType: (thangType) ->
return unless components = thangType.get('components')
@ -220,8 +222,14 @@ module.exports = class LevelLoader extends CocoClass
return unless index >= 0
@worldNecessities.splice(index, 1)
@worldNecessities = (r for r in @worldNecessities when r?)
if @worldNecessities.length is 0 and (not @sessionDependenciesRegistered or @sessionDependenciesRegistered[@session.id] and (not @opponentSession or @sessionDependenciesRegistered[@opponentSession.id]))
@onWorldNecessitiesLoaded()
@onWorldNecessitiesLoaded() if @checkAllWorldNecessitiesRegisteredAndLoaded()
checkAllWorldNecessitiesRegisteredAndLoaded: ->
return false unless _.filter(@worldNecessities).length is 0
return false unless @thangNamesLoaded
return false if @sessionDependenciesRegistered and not @sessionDependenciesRegistered[@session.id]
return false if @opponentSession and not @sessionDependenciesRegistered[@opponentSession.id]
true
onWorldNecessitiesLoaded: ->
@initWorld()

View file

@ -6,6 +6,7 @@ module.exports = LevelOptions =
hidesHUD: true
hidesSay: true
hidesCodeToolbar: true
hidesRealTimePlayback: true
requiredGear: {feet: 'simple-boots'}
restrictedGear: {feet: 'leather-boots'}
'gems-in-the-deep':
@ -15,6 +16,7 @@ module.exports = LevelOptions =
hidesHUD: true
hidesSay: true
hidesCodeToolbar: true
hidesRealTimePlayback: true
requiredGear: {feet: 'simple-boots'}
restrictedGear: {feet: 'leather-boots'}
'shadow-guard':
@ -24,6 +26,7 @@ module.exports = LevelOptions =
hidesHUD: true
hidesSay: true
hidesCodeToolbar: true
hidesRealTimePlayback: true
requiredGear: {feet: 'simple-boots'}
restrictedGear: {feet: 'leather-boots', 'right-hand': 'simple-sword'}
'kounter-kithwise':
@ -32,6 +35,7 @@ module.exports = LevelOptions =
hidesHUD: true
hidesSay: true
hidesCodeToolbar: true
hidesRealTimePlayback: true
requiredGear: {feet: 'simple-boots'}
restrictedGear: {feet: 'leather-boots', 'right-hand': 'simple-sword', 'programming-book': 'programmaticon-i'}
'crawlways-of-kithgard':
@ -40,6 +44,7 @@ module.exports = LevelOptions =
hidesHUD: true
hidesSay: true
hidesCodeToolbar: true
hidesRealTimePlayback: true
requiredGear: {feet: 'simple-boots'}
restrictedGear: {feet: 'leather-boots', 'right-hand': 'simple-sword', 'programming-book': 'programmaticon-i'}
'forgetful-gemsmith':
@ -48,6 +53,7 @@ module.exports = LevelOptions =
hidesHUD: true
hidesSay: true
hidesCodeToolbar: true
hidesRealTimePlayback: true
requiredGear: {feet: 'simple-boots'}
restrictedGear: {feet: 'leather-boots', 'programming-book': 'programmaticon-i'}
'true-names':
@ -56,6 +62,7 @@ module.exports = LevelOptions =
hidesHUD: true
hidesSay: true
hidesCodeToolbar: true
hidesRealTimePlayback: true
requiredGear: {feet: 'simple-boots', 'right-hand': 'simple-sword', waist: 'leather-belt'}
restrictedGear: {feet: 'leather-boots'}
'favorable-odds':
@ -63,6 +70,7 @@ module.exports = LevelOptions =
hidesHUD: true
hidesSay: true
hidesCodeToolbar: true
hidesRealTimePlayback: true
requiredGear: {feet: 'simple-boots', 'right-hand': 'simple-sword'}
restrictedGear: {feet: 'leather-boots'}
'the-raised-sword':
@ -70,6 +78,7 @@ module.exports = LevelOptions =
hidesHUD: true
hidesSay: true
hidesCodeToolbar: true
hidesRealTimePlayback: true
requiredGear: {feet: 'simple-boots', 'right-hand': 'simple-sword', torso: 'leather-tunic'}
restrictedGear: {feet: 'leather-boots'}
'the-first-kithmaze':
@ -77,6 +86,7 @@ module.exports = LevelOptions =
hidesHUD: true
hidesSay: true
hidesCodeToolbar: true
hidesRealTimePlayback: true
requiredGear: {feet: 'simple-boots', 'programming-book': 'programmaticon-i'}
restrictedGear: {feet: 'leather-boots'}
'haunted-kithmaze':
@ -84,76 +94,90 @@ module.exports = LevelOptions =
hidesHUD: true
hidesSay: true
hidesCodeToolbar: true
hidesRealTimePlayback: true
requiredGear: {feet: 'simple-boots', 'programming-book': 'programmaticon-i'}
restrictedGear: {feet: 'leather-boots'}
'descending-further':
hidesHUD: true
hidesSay: true
hidesCodeToolbar: true
hidesRealTimePlayback: true
requiredGear: {feet: 'simple-boots', 'programming-book': 'programmaticon-i'}
restrictedGear: {feet: 'leather-boots'}
'the-second-kithmaze':
hidesHUD: true
hidesSay: true
hidesCodeToolbar: true
hidesRealTimePlayback: true
requiredGear: {feet: 'simple-boots', 'programming-book': 'programmaticon-i'}
restrictedGear: {feet: 'leather-boots'}
'dread-door':
hidesHUD: true
hidesSay: true
hidesCodeToolbar: true
hidesRealTimePlayback: true
requiredGear: {'right-hand': 'simple-sword', 'programming-book': 'programmaticon-i'}
restrictedGear: {feet: 'leather-boots'}
'known-enemy':
hidesHUD: true
hidesSay: true
hidesCodeToolbar: true
hidesRealTimePlayback: true
requiredGear: {'right-hand': 'simple-sword', 'programming-book': 'programmaticon-i', torso: 'leather-tunic'}
restrictedGear: {feet: 'leather-boots'}
'master-of-names':
hidesHUD: true
hidesSay: true
hidesCodeToolbar: true
hidesRealTimePlayback: true
requiredGear: {feet: 'simple-boots', 'right-hand': 'simple-sword', 'programming-book': 'programmaticon-i', eyes: 'crude-glasses', torso: 'leather-tunic'}
restrictedGear: {feet: 'leather-boots'}
'lowly-kithmen':
hidesHUD: true
hidesSay: true
hidesCodeToolbar: true
hidesRealTimePlayback: true
requiredGear: {feet: 'simple-boots', 'right-hand': 'simple-sword', 'programming-book': 'programmaticon-i', eyes: 'crude-glasses', torso: 'leather-tunic'}
restrictedGear: {feet: 'leather-boots'}
'closing-the-distance':
hidesHUD: true
hidesSay: true
hidesCodeToolbar: true
hidesRealTimePlayback: true
requiredGear: {feet: 'simple-boots', 'right-hand': 'simple-sword', torso: 'leather-tunic', eyes: 'crude-glasses'}
restrictedGear: {feet: 'leather-boots'}
'tactical-strike':
hidesHUD: true
hidesSay: true
hidesCodeToolbar: true
hidesRealTimePlayback: true
requiredGear: {feet: 'simple-boots', 'right-hand': 'simple-sword', torso: 'leather-tunic', eyes: 'crude-glasses'}
restrictedGear: {feet: 'leather-boots'}
'the-final-kithmaze':
hidesHUD: true
hidesSay: true
hidesCodeToolbar: true
hidesRealTimePlayback: true
requiredGear: {feet: 'simple-boots', 'right-hand': 'simple-sword', torso: 'leather-tunic', 'programming-book': 'programmaticon-i', eyes: 'crude-glasses'}
'the-gauntlet':
hidesHUD: true
hidesSay: true
hidesCodeToolbar: true
hidesRealTimePlayback: true
requiredGear: {feet: 'simple-boots', 'right-hand': 'simple-sword', torso: 'leather-tunic', 'programming-book': 'programmaticon-i', eyes: 'crude-glasses'}
restrictedGear: {feet: 'leather-boots'}
'kithgard-gates':
hidesSay: true
hidesCodeToolbar: true
hidesRealTimePlayback: true
requiredGear: {feet: 'simple-boots', 'right-hand': 'builders-hammer', torso: 'leather-tunic'}
restrictedGear: {'right-hand': 'simple-sword'}
'defense-of-plainswood':
hidesRealTimePlayback: true
requiredGear: {feet: 'simple-boots', 'right-hand': 'builders-hammer'}
restrictedGear: {'right-hand': 'simple-sword'}
'winding-trail':
hidesRealTimePlayback: true
requiredGear: {feet: 'leather-boots', 'right-hand': 'builders-hammer'}
restrictedGear: {feet: 'simple-boots', 'right-hand': 'simple-sword'}
'thornbush-farm':

View file

@ -47,7 +47,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
play:
play_as: "Jogar Como" # Ladder page
spectate: "Espectar" # Ladder page
spectate: "Assistir" # Ladder page
players: "jogadores" # Hover over a level on /play
hours_played: "horas jogadas" # Hover over a level on /play
items: "Itens" # Tooltip on item shop button from /play
@ -55,7 +55,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
confirm: "Confirmar"
owned: "Obtido" # For items you own
locked: "Bloqueado"
# available: "Available"
available: "Disponível"
skills_granted: "Habilidades Garantidas" # Property documentation details
heroes: "Heróis" # Tooltip on hero shop button from /play
achievements: "Conquistas" # Tooltip on achievement list button from /play
@ -191,14 +191,14 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
restart: "Reiniciar"
goals: "Objetivos"
goal: "Objetivo"
# running: "Running..."
running: "A executar..."
success: "Sucesso!"
incomplete: "Incompletos"
timed_out: "Ficaste sem tempo"
failing: "A falhar"
action_timeline: "Linha do Tempo de Ações"
click_to_select: "Clica numa unidade para selecioná-la."
# reload: "Reload"
reload: "Recarregar"
reload_title: "Recarregar o Código Todo?"
reload_really: "Tens a certeza que queres recarregar este nível de volta ao início?"
reload_confirm: "Recarregar Tudo"
@ -234,14 +234,14 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
tome_select_a_thang: "Seleciona Alguém para "
tome_available_spells: "Feitiços Disponíveis"
tome_your_skills: "As Tuas Habilidades"
# tome_current_method: "Current Method"
tome_current_method: "Método Atual"
hud_continue_short: "Continuar"
code_saved: "Código Guardado"
skip_tutorial: "Saltar (esc)"
keyboard_shortcuts: "Atalhos do Teclado"
loading_ready: "Pronto!"
loading_start: "Iniciar Nível"
# problem_alert_title: "Fix Your Code"
problem_alert_title: "Corrige o Teu Código"
time_current: "Agora:"
time_total: "Máximo:"
time_goto: "Ir para:"
@ -301,7 +301,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
should_equip: "(clica duas vezes para equipares)"
equipped: "(equipado)"
locked: "(bloqueado)"
# restricted: "(restricted in this level)"
restricted: "(restrito neste nível)"
choose_hero:
choose_hero: "Escolhe o Teu Herói"
@ -315,9 +315,9 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
io_blurb: "Simples mas obscuro."
status: "Estado"
weapons: "Armas"
# weapons_warrior: "Swords - Short Range, No Magic"
# weapons_ranger: "Crossbows, Guns - Long Range, No Magic"
# weapons_wizard: "Wands, Staffs - Long Range, Magic"
weapons_warrior: "Espadas - Curto Alcance, Sem Magia"
weapons_ranger: "Arcos, Armas - Longo Alcance, Sem Magia"
weapons_wizard: "Varinhas, Bastões - Longo Alcance, Magia"
attack: "Ataque" # Can also translate as "Attack"
health: "Vida"
speed: "Velocidade"
@ -447,8 +447,8 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
enter: "Enter"
escape: "Esc"
shift: "Shift"
# run_code: "Run current code."
run_real_time: "Correr em tempo real."
run_code: "Executar código atual."
run_real_time: "Executar em tempo real."
continue_script: "Saltar o script atual."
skip_scripts: "Saltar todos os scripts saltáveis."
toggle_playback: "Alternar entre Jogar e Pausar."
@ -628,9 +628,9 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
diplomat_launch_url: "lançamento em Outubro"
diplomat_introduction_suf: "é que há um interesse considerável no CodeCombat noutros países! Estamos a construir um exército de tradutores dispostos a transformar um conjunto de palavras noutro conjuto de palavras, para conseguir que o CodeCombat fique o mais acessível quanto posível em todo o mundo. Se gostas de dar espreitadelas a conteúdos futuros e disponibilizar estes níveis para os teus colegas nacionais o mais depressa possível, então esta classe talvez seja para ti."
diplomat_attribute_1: "Fluência em Inglês e no idioma para o qual gostarias de traduzir. Quando são tentadas passar ideias complicadas, é importante uma excelente compreensão das duas!"
# diplomat_i18n_page_prefix: "You can start translating our levels by going to our"
# diplomat_i18n_page: "translations page"
# diplomat_i18n_page_suffix: ", or our interface and website on GitHub."
diplomat_i18n_page_prefix: "Podes começar a traduzir os nossos níveis se fores à nossa"
diplomat_i18n_page: "página de traduções"
diplomat_i18n_page_suffix: ", ou a nossa interface e website no GitHub."
diplomat_join_pref_github: "Encontra o ficheiro 'locale' do teu idioma "
diplomat_github_url: "no GitHub"
diplomat_join_suf_github: ", edita-o online e submete um 'pull request'. Assinala ainda esta caixa abaixo para ficares atualizado em relação a novos desenvolvimentos da internacionalização!"

View file

@ -53,7 +53,7 @@
border-image: url(/images/level/code_toolbar_run_button_active_pressed.png) 14 20 20 20 fill round
padding: 2px 0 0 2px
&.submit-button
&.submit-button, &.done-button
margin-left: 10px
border-image: url(/images/level/code_toolbar_submit_button_active.png) 14 20 20 20 fill round
@ -66,7 +66,7 @@
&:hover, &.castable
@include opacity(1)
.submit-button > *
.submit-button > *, .done-button > *
@include opacity(0.9)
&:hover
@ -90,7 +90,7 @@
&.winnable
.btn.btn-illustrated
&.submit-button
&.submit-button, &.done-button
font-weight: bold
@include animation(winnablePulse 3s infinite)
@ -114,7 +114,7 @@ html.no-borderimage #cast-button-view
padding: 9px 8px 8px 12px
border: 0
&.submit-button
&.submit-button, &.done-button
background-image: url(/images/level/code_toolbar_submit_button_active_pressed.png)
border: 0
@ -133,7 +133,7 @@ html.no-borderimage #cast-button-view
&.winnable
.btn.btn-illustrated
&.submit-button
&.submit-button, &.done-button
border: 0
background-image: url(/images/level/code_toolbar_submit_button_zazz.png)

View file

@ -18,7 +18,9 @@
position: absolute
height: 100%
top: -50px
width: 85%
width: 65%
width: -webkit-calc(85% - 60px)
width: calc(85% - 60px)
left: 10%
display: inline-block
cursor: pointer
@ -63,6 +65,7 @@
position: absolute
z-index: 2
width: 60px
top: -50px
right: 2%
box-sizing: border-box
opacity: 0.25

View file

@ -6,3 +6,6 @@ button.btn.btn-lg.btn-illustrated.submit-button(title=castRealTimeVerbose)
span= testSubmitText
else
span(data-i18n="play_level.tome_submit_button") Submit
button.btn.btn-lg.btn-illustrated.done-button.secret
span(data-i18n="play_level.done") Done

View file

@ -160,6 +160,7 @@ module.exports = class HeroVictoryModal extends ModalView
@insertSubView @ladderSubmissionView, @$el.find('.ladder-submission-view')
beginSequentialAnimations: ->
return if @destroyed
@sequentialAnimatedPanels = _.map(@animatedPanels.find('.reward-panel'), (panel) -> {
number: $(panel).data('number')
textEl: $(panel).find('.reward-text')

View file

@ -10,6 +10,7 @@ module.exports = class CastButtonView extends CocoView
events:
'click .cast-button': 'onCastButtonClick'
'click .submit-button': 'onCastRealTimeButtonClick'
'click .done-button': 'onDoneButtonClick'
subscriptions:
'tome:spell-changed': 'onSpellChanged'
@ -26,7 +27,8 @@ module.exports = class CastButtonView extends CocoView
@spells = options.spells
@levelID = options.levelID
@castShortcut = '⇧↵'
@initButtonTextABTest()
@levelOptions = LevelOptions[@options.levelID] ? {}
@initButtonTextABTest() unless @levelOptions.hidesRealTimePlayback
getRenderData: (context={}) ->
context = super context
@ -47,7 +49,12 @@ module.exports = class CastButtonView extends CocoView
#delay = me.get('autocastDelay') # No more autocast
delay = 90019001
@setAutocastDelay delay
@$el.find('.submit-button').hide() if LevelOptions[@options.levelID]?.hidesSubmitUntilRun # Hide Submit for the first few until they run it once.
if @levelOptions.hidesSubmitUntilRun or @levelOptions.hidesRealTimePlayback
@$el.find('.submit-button').hide() # Hide Submit for the first few until they run it once.
if @options.session.get('state')?.complete and @levelOptions.hidesRealTimePlayback
@$el.find('.done-button').show()
if @options.levelID is 'thornbush-farm'# and not @options.session.get('state')?.complete
@$el.find('.submit-button').hide() # Hide submit until first win so that script can explain it.
attachTo: (spellView) ->
@$el.detach().prependTo(spellView.toolbarView.$el).show()
@ -67,6 +74,9 @@ module.exports = class CastButtonView extends CocoView
else
Backbone.Mediator.publish 'tome:manual-cast', {realTime: true}
onDoneButtonClick: (e) ->
Backbone.Mediator.publish 'level:show-victory', showModal: true
onSpellChanged: (e) ->
@updateCastButton()
@ -91,11 +101,16 @@ module.exports = class CastButtonView extends CocoView
@winnable = winnable
@$el.toggleClass 'winnable', @winnable
Backbone.Mediator.publish 'tome:winnability-updated', winnable: @winnable
if @winnable or (@hasCastOnce and @options.levelID isnt 'dungeons-of-kithgard') # Show once 1) we think they will win or 2) they have hit run once. (Only #1 on the fist level.)
@$el.find('.submit-button').show() # In case we hid it, like on the first level.
if @levelOptions.hidesRealTimePlayback
@$el.find('.done-button').toggle @winnable
else if @winnable and @options.levelID is 'thornbush-farm'
@$el.find('.submit-button').show() # Hide submit until first win so that script can explain it.
onGoalsCalculated: (e) ->
# When preloading, with real-time playback enabled, we highlight the submit button when we think they'll win.
return unless e.preload
return if @levelOptions.hidesRealTimePlayback
return if @options.levelID is 'thornbush-farm' # Don't show it until they actually win for this first one.
@onNewGoalStates e
updateCastButton: ->
@ -113,7 +128,7 @@ module.exports = class CastButtonView extends CocoView
castText = $.i18n.t('play_level.tome_cast_button_running')
else if castable or true
castText = $.i18n.t('play_level.tome_cast_button_run')
unless LevelOptions[@options.levelID]?.hidesRunShortcut # Hide for first few.
unless @levelOptions.hidesRunShortcut # Hide for first few.
castText += ' ' + @castShortcut
else
castText = $.i18n.t('play_level.tome_cast_button_ran')

View file

@ -62,7 +62,7 @@ module.exports = class TomeView extends CocoView
programmableThangs = _.filter @options.thangs, 'isProgrammable'
@createSpells programmableThangs, programmableThangs[0]?.world # Do before spellList, thangList, and castButton
@spellList = @insertSubView new SpellListView spells: @spells, supermodel: @supermodel, level: @options.level
@castButton = @insertSubView new CastButtonView spells: @spells, levelID: @options.levelID
@castButton = @insertSubView new CastButtonView spells: @spells, levelID: @options.levelID, session: @options.session
@teamSpellMap = @generateTeamSpellMap(@spells)
unless programmableThangs.length
@cast()

View file

@ -0,0 +1,21 @@
var usersWithPurchases = db.users.find({"purchased.items": {$exists: true}}).toArray();
var itemsConverted = 0;
var usersConverted = 0;
for (var i = 0; i < usersWithPurchases.length; ++i) {
var user = usersWithPurchases[i];
var items = user.purchased.items;
var convertThisUser = false;
for (var j = 0; j < items.length; ++j) {
var item = items[j];
if (typeof item != 'string') {
items[j] = '' + item;
++itemsConverted;
convertThisUser = true;
}
}
if (convertThisUser) {
db.users.save(user);
++usersConverted;
}
}
print("Had to convert", itemsConverted, "items and", usersConverted, "users");

View file

@ -1,13 +1,18 @@
log = require 'winston'
module.exports.custom = (res, code=500, message='Internal Server Error') ->
log.debug "#{code}: #{message}"
res.send code, message
res.end()
module.exports.unauthorized = (res, message='Unauthorized') ->
# TODO: The response MUST include a WWW-Authenticate header field
log.debug "401: #{message}"
res.send 401, message
res.end()
module.exports.forbidden = (res, message='Forbidden') ->
log.debug "403: #{message}"
res.send 403, message
res.end()
@ -16,27 +21,33 @@ module.exports.notFound = (res, message='Not found.') ->
res.end()
module.exports.badMethod = (res, allowed=['GET', 'POST', 'PUT', 'PATCH'], message='Method Not Allowed') ->
log.debug "405: #{message}"
allowHeader = _.reduce allowed, ((str, current) -> str += ', ' + current)
res.set 'Allow', allowHeader # TODO not sure if these are always the case
res.send 405, message
res.end()
module.exports.conflict = (res, message='Conflict. File exists') ->
log.debug "409: #{message}"
res.send 409, message
res.end()
module.exports.badInput = (res, message='Unprocessable Entity. Bad Input.') ->
log.debug "422: #{message}"
res.send 422, message
res.end()
module.exports.serverError = (res, message='Internal Server Error') ->
log.debug "500: #{message}"
res.send 500, message
res.end()
module.exports.gatewayTimeoutError = (res, message='Gateway timeout') ->
log.debug "504: #{message}"
res.send 504, message
res.end()
module.exports.clientTimeout = (res, message='The server did not receive the client response in a timely manner') ->
log.debug "408: #{message}"
res.send 408, message
res.end()