This commit is contained in:
Scott Erickson 2014-10-01 13:26:45 -07:00
commit e9222f34bd
64 changed files with 343 additions and 273 deletions

2
.gitignore vendored
View file

@ -88,6 +88,8 @@ login.coffee
# debugging
*.heapsnapshot
npm-debug.log*
temp/
### If you add something here, copy it to the end of .npmignore, too. ###

View file

@ -104,3 +104,6 @@ login.coffee
# debugging
*.heapsnapshot
npm-debug.log*
temp/

View file

@ -177,3 +177,7 @@ module.exports = class CocoRouter extends Backbone.Router
@openView view
else
@openView e.view
navigate: (fragment, options) ->
super fragment, options
Backbone.Mediator.publish 'router:navigated', route: fragment

View file

@ -61,10 +61,12 @@ setUpBackboneMediator = ->
Backbone.Mediator.addChannelSchemas schemas for channel, schemas of channelSchemas
Backbone.Mediator.setValidationEnabled document.location.href.search(/codecombat.com/) is -1
if webkit?.messageHandlers
window.iPadSubscriptions = 'application:error': true # We try to subscribe to this one before it's all set up, so just do it.
originalPublish = Backbone.Mediator.publish
Backbone.Mediator.publish = ->
originalPublish.apply Backbone.Mediator, arguments
webkit.messageHandlers.backboneEventHandler?.postMessage channel: arguments[0], event: serializeForIOS(arguments[1] ? {})
if window.iPadSubscriptions[arguments[0]]
webkit.messageHandlers.backboneEventHandler?.postMessage channel: arguments[0], event: serializeForIOS(arguments[1] ? {})
setUpMoment = ->
{me} = require 'lib/auth'
@ -106,6 +108,12 @@ watchForErrors = ->
noty text: message, layout: 'topCenter', type: 'error', killer: false, timeout: 5000, dismissQueue: true, maxVisible: 3, callback: {onClose: -> --currentErrors}
Backbone.Mediator.publish 'application:error', message: msg # For iOS app
window.addIPadSubscription = (channel) ->
window.iPadSubscriptions[channel] = true
window.removeIPadSubscription = (channel) ->
window.iPadSubscriptions[channel] = false
setUpIOSLogging = ->
return unless webkit?.messageHandlers
for level in ['debug', 'log', 'info', 'warn', 'error']

View file

@ -506,7 +506,10 @@ module.exports = Surface = class Surface extends CocoClass
oldHeight = parseInt @normalCanvas.attr('height'), 10
aspectRatio = oldWidth / oldHeight
pageWidth = $('#page-container').width() - 17 # 17px nano scroll bar
if @realTime or @options.spectateGame
if application.isIPadApp
newWidth = 1024
newHeight = newWidth / aspectRatio
else if @realTime or @options.spectateGame
pageHeight = $('#page-container').height() - $('#control-bar-view').outerHeight() - $('#playback-view').outerHeight()
newWidth = Math.min pageWidth, pageHeight * aspectRatio
newHeight = newWidth / aspectRatio
@ -517,6 +520,7 @@ module.exports = Surface = class Surface extends CocoClass
newWidth = 0.55 * pageWidth
newHeight = newWidth / aspectRatio
return unless newWidth > 0 and newHeight > 0
return if newWidth is oldWidth and newHeight is oldHeight
##if InstallTrigger? # Firefox rendering performance goes down as canvas size goes up
## newWidth = Math.min 924, newWidth
## newHeight = Math.min 589, newHeight
@ -524,14 +528,14 @@ module.exports = Surface = class Surface extends CocoClass
#@normalCanvas.height newHeight
scaleFactor = if application.isIPadApp then 2 else 1 # Retina
@normalCanvas.add(@webGLCanvas).attr width: newWidth * scaleFactor, height: newHeight * scaleFactor
# Cannot do this to the webGLStage because it does not use scaleX/Y.
# Instead the LayerAdapter scales webGL-enabled layers.
# Instead the LayerAdapter scales webGL-enabled layers.
@webGLStage.updateViewport(@webGLCanvas[0].width, @webGLCanvas[0].height)
@normalStage.scaleX *= newWidth / oldWidth
@normalStage.scaleY *= newHeight / oldHeight
@camera.onResize newWidth, newHeight
#- Camera focus on hero
focusOnHero: ->

View file

@ -3,11 +3,11 @@ module.exports = nativeDescription: "български език", englishDescri
loading: "Зареждане..."
saving: "Записване..."
sending: "Изпращане..."
# send: "Send"
send: "Изпрати"
cancel: "Отказ"
save: "Запис"
# publish: "Publish"
# create: "Create"
publish: "Публикирай"
create: "Создавай"
delay_1_sec: "1 секунда"
delay_3_sec: "3 секунди"
delay_5_sec: "5 секунди"
@ -19,21 +19,21 @@ module.exports = nativeDescription: "български език", englishDescri
# unwatch: "Unwatch"
# submit_patch: "Submit Patch"
# units:
# second: "second"
# seconds: "seconds"
# minute: "minute"
# minutes: "minutes"
# hour: "hour"
# hours: "hours"
# day: "day"
# days: "days"
# week: "week"
# weeks: "weeks"
# month: "month"
# months: "months"
# year: "year"
# years: "years"
units:
second: "секунда"
seconds: "секунди"
minute: "минута"
minutes: "минути"
hour: "час"
hours: "часове"
day: "ден"
days: "дни"
week: "седмица"
weeks: "седмици"
month: "месец"
months: "месеци"
year: "година"
years: "години"
modal:
close: "Затвори"
@ -44,20 +44,20 @@ module.exports = nativeDescription: "български език", englishDescri
nav:
play: "Нива" # The top nav bar entry where players choose which levels to play
# community: "Community"
community: "Обшност"
editor: "Редактор"
blog: "Блог"
forum: "Форум"
# account: "Account"
# profile: "Profile"
account: "Сметката"
profile: "Профил"
# stats: "Stats"
# code: "Code"
# admin: "Admin"
# home: "Home"
home: "Начало"
# contribute: "Contribute"
# legal: "Legal"
# about: "About"
# contact: "Contact"
about: "За нас"
contact: "Контакти"
# twitter_follow: "Follow"
# employers: "Employers"
@ -72,7 +72,7 @@ module.exports = nativeDescription: "български език", englishDescri
login:
sign_up: "Създай Профил"
log_in: "Вход"
# logging_in: "Logging In"
logging_in: "Влизане..."
log_out: "Изход"
recover: "Възстанови акаунт"
@ -82,16 +82,16 @@ module.exports = nativeDescription: "български език", englishDescri
# recovery_sent: "Recovery email sent."
signup:
# create_account_title: "Create Account to Save Progress"
# description: "It's free. Just need a couple things and you'll be good to go:"
# email_announcements: "Receive announcements by email"
create_account_title: "Создавай нов сметката за да записва прогрес"
description: "Е безплатен. Само ще трабва неколко неща и ти ще си готов:"
email_announcements: "Получава анонци през имейл"
# coppa: "13+ or non-USA "
coppa_why: "(Защо?)"
creating: "Създаване на профил..."
sign_up: "Регистриране"
log_in: "Вход с парола"
# social_signup: "Or, you can sign up through Facebook or G+:"
# required: "You need to log in before you can go that way."
social_signup: "Или, можеш да зарегистрираваш през Facebook или G+:"
required: "Трабва да влезиш преди можеш да ходиш на там."
home:
slogan: "Научи се да програмираш, докато играеш игра "
@ -165,11 +165,11 @@ module.exports = nativeDescription: "български език", englishDescri
# recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns."
diplomat_suggestion:
# title: "Help translate CodeCombat!"
title: "Дай да помогни да преводи CodeCombat!"
# sub_heading: "We need your language skills."
pitch_body: "We develop CodeCombat in English, but we already have players all over the world. Many of them want to play in Bulgarian but don't speak English, so if you can speak both, please consider signing up to be a Diplomat and help translate both the CodeCombat website and all the levels into Bulgarian."
missing_translations: "Until we can translate everything into Bulgarian, you'll see English when Bulgarian isn't available."
# learn_more: "Learn more about being a Diplomat"
learn_more: "Научи повече за ставане Дипломат"
# subscribe_as_diplomat: "Subscribe as a Diplomat"
# wizard_settings:

View file

@ -15,9 +15,9 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
fork: "Fork"
play: "Jugar" # When used as an action verb, like "Play next level"
retry: "Tornar a intentar"
# watch: "Watch"
# unwatch: "Unwatch"
# submit_patch: "Submit Patch"
watch: "Veure"
unwatch: "Amaga"
submit_patch: "Enviar pegat"
units:
second: "segon"
@ -51,7 +51,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
account: "Compte"
profile: "Perfil"
stats: "Estats"
# code: "Code"
code: "Codi"
admin: "Admin"
home: "Inici"
contribute: "Col·laborar"
@ -63,7 +63,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
versions:
save_version_title: "Guarda una nova versió"
# new_major_version: "New Major Version"
new_major_version: "Nova versió principal"
cla_prefix: "Per guardar els canvis primer has d'acceptar"
cla_url: "CLA"
cla_suffix: "."
@ -105,10 +105,10 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
multiplayer: "Multijugador"
for_developers: "Per a Desenvolupadors"
javascript_blurb: "El llenguatge de les webs. Útil per escriure pagines web, aplicacions web, jocs en HTML5 i servidors."
# python_blurb: "Simple yet powerful, Python is a great general purpose programming language."
# coffeescript_blurb: "Nicer JavaScript syntax."
# clojure_blurb: "A modern Lisp."
# lua_blurb: "Game scripting language."
python_blurb: "Simple però poderós, Python és un bon llenguatge d'us general."
coffeescript_blurb: "Sintaxi JavaScript millorat."
clojure_blurb: "Un Lisp modern."
lua_blurb: "Llenguatge script per a jocs."
io_blurb: "Senzill però obscur."
play:
@ -126,7 +126,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
campaign_player_created: "Creats pel Jugador"
campaign_player_created_description: "... on lluites contra la creativitat dels teus companys <a href=\"/contribute#artisan\">Artisan Wizards</a>."
campaign_classic_algorithms: "Algoritmes classics"
# campaign_classic_algorithms_description: "... in which you learn the most popular algorithms in Computer Science."
campaign_classic_algorithms_description: "... on pots aprendre els algoritmes més populars de l'informàtica."
level_difficulty: "Dificultat: "
play_as: "Jugar com"
spectate: "Spectate"
@ -162,7 +162,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
forum_suffix: " sinó"
send: "Enviar comentari"
contact_candidate: "Contactar amb el candidat"
# recruitment_reminder: "Use this form to reach out to candidates you are interested in interviewing. Remember that CodeCombat charges 15% of first-year salary. The fee is due upon hiring the employee and is refundable for 90 days if the employee does not remain employed. Part time, remote, and contract employees are free, as are interns."
recruitment_reminder: "Utilitza aquest formulari per a contactar amb els candidats que vols entrevistar. Recorda que CodeCombat cobrará el 15% del sou del primer any. El cost es per la contactacio del treballador i es reemborsable durant 90 dies si el treballdor no roman contractat . Temporals, a distancia i treballadors de contracte són gratuits, com els becaris."
diplomat_suggestion:
title: "Ajuda a traduir CodeCombat!"
@ -179,12 +179,12 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
color: "Color"
group: "Grup"
clothes: "Roba"
# trim: "Trim"
trim: "Decoració"
cloud: "Nuvol"
team: "Equip"
# spell: "Spell"
spell: "Encanteri"
boots: "Botes"
# hue: "Hue"
hue: "Matriu"
saturation: "Saturació"
lightness: "Brillantor"
@ -202,21 +202,21 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
wizard_color: "Color de la roba"
new_password: "Contrasenya nova"
new_password_verify: "Verifica"
# email_subscriptions: "Email Subscriptions"
# email_subscriptions_none: "No Email Subscriptions."
# email_announcements: "Announcements"
# email_announcements_description: "Get emails on the latest news and developments at CodeCombat."
email_subscriptions: "Subscripcions via correu electrònic"
email_subscriptions_none: "Sense subsrcipcions de correu electrònic."
email_announcements: "Notícies"
email_announcements_description: "Rebre les últimes notícies i desenvolupaments de CodeCombat."
email_notifications: "Notificacions"
# email_notifications_summary: "Controls for personalized, automatic email notifications related to your CodeCombat activity."
email_notifications_summary: "Controls per personalitzar les teves notificacions d'email automàtiques relacionades amb la teva activitat a CodeCombat."
email_any_notes: "Cap notificació"
# email_any_notes_description: "Disable to stop all activity notification emails."
email_any_notes_description: "Desactiva totes les notificacions via correu electrònic."
email_news: "Noticies"
email_recruit_notes: "Oportunitats de feina"
# email_recruit_notes_description: "If you play really well, we may contact you about getting you a (better) job."
# contributor_emails: "Contributor Class Emails"
# contribute_prefix: "We're looking for people to join our party! Check out the "
# contribute_page: "contribute page"
# contribute_suffix: " to find out more."
contribute_suffix: " per a trobar més."
email_toggle: "Activa-ho tot"
error_saving: "Error en desar"
saved: "Canvis desats"
@ -232,14 +232,14 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
settings: "Configuració"
edit_profile: "Modifica el perfil"
done_editing: "Acaba l'edició"
# profile_for_prefix: "Profile for "
profile_for_prefix: "Perfil de "
profile_for_suffix: ""
# featured: "Featured"
featured: "Destacat"
# not_featured: "Not Featured"
looking_for: "Buscant:"
# last_updated: "Last updated:"
last_updated: "Ultima actualització:"
contact: "Contacta"
# active: "Looking for interview offers now"
active: "Buscant entrevistes de feina"
# inactive: "Not looking for offers right now"
complete: "complet"
next: "Seguent"
@ -247,7 +247,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
next_country: "escull el teu país."
next_name: "nom?"
next_short_description: "escriu una breu descripció."
# next_long_description: "describe your desired position."
next_long_description: "descriu el lloc de feina que desitges."
# next_skills: "list at least five skills."
# next_work: "chronicle your work history."
# next_education: "recount your educational ordeals."
@ -596,11 +596,11 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
editor:
# main_title: "CodeCombat Editors"
# article_title: "Article Editor"
article_title: "Editor d'articles "
# thang_title: "Thang Editor"
level_title: "Editor de nivells"
achievement_title: "Editor de triomfs"
# back: "Back"
back: "Enrere"
# revert: "Revert"
# revert_models: "Revert Models"
# pick_a_terrain: "Pick A Terrain"
@ -616,21 +616,21 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
# level_tab_thangs: "Thangs"
# level_tab_scripts: "Scripts"
level_tab_settings: "Configuració"
# level_tab_components: "Components"
# level_tab_systems: "Systems"
level_tab_components: "Components"
level_tab_systems: "Sistemes"
# level_tab_docs: "Documentation"
# level_tab_thangs_title: "Current Thangs"
# level_tab_thangs_all: "All"
level_tab_thangs_all: "Tot"
# level_tab_thangs_conditions: "Starting Conditions"
# level_tab_thangs_add: "Add Thangs"
delete: "Esborrar"
duplicate: "Duplicar"
level_settings_title: "Configuració"
# level_component_tab_title: "Current Components"
level_component_tab_title: "Components actuals"
# level_component_btn_new: "Create New Component"
# level_systems_tab_title: "Current Systems"
level_systems_tab_title: "Sistemes actuals"
# level_systems_btn_new: "Create New System"
# level_systems_btn_add: "Add System"
level_systems_btn_add: "Afegir sistema"
# level_components_title: "Back to All Thangs"
# level_components_type: "Type"
# level_component_edit_title: "Edit Component"
@ -678,7 +678,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
# subject: "Subject"
# email: "Email"
password: "Contrasenya"
# message: "Message"
message: "Missatge"
# code: "Code"
# ladder: "Ladder"
# when: "When"
@ -718,8 +718,8 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
matt_title: "Programador"
# matt_blurb: "Bicyclist"
# legal:
# page_title: "Legal"
legal:
page_title: "Legalitat"
# opensource_intro: "CodeCombat is free to play and completely open source."
# opensource_description_prefix: "Check out "
# github_url: "our GitHub"
@ -728,7 +728,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
# opensource_description_suffix: "for a list of the software that makes this game possible."
# practices_title: "Respectful Best Practices"
# practices_description: "These are our promises to you, the player, in slightly less legalese."
# privacy_title: "Privacy"
privacy_title: "Privacitat"
# privacy_description: "We will not sell any of your personal information. We intend to make money through recruitment eventually, but rest assured we will not distribute your personal information to interested companies without your explicit consent."
# security_title: "Security"
# security_description: "We strive to keep your personal information safe. As an open source project, our site is freely open to anyone to review and improve our security systems."
@ -887,13 +887,13 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
# simulate_all: "RESET AND SIMULATE GAMES"
# games_simulated_by: "Games simulated by you:"
# games_simulated_for: "Games simulated for you:"
# games_simulated: "Games simulated"
games_simulated: "Partides simulades"
games_played: "Partides guanyades"
# ratio: "Ratio"
# leaderboard: "Leaderboard"
# battle_as: "Battle as "
# summary_your: "Your "
# summary_matches: "Matches - "
summary_your: "Les teves "
summary_matches: "Partides - "
summary_wins: " Victories, "
summary_losses: " Derrotes"
# rank_no_code: "No New Code to Rank"
@ -910,42 +910,42 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
choose_opponent: "Escull adversari"
select_your_language: "Escull el teu idioma!"
tutorial_play: "Juga el tutorial"
# tutorial_recommended: "Recommended if you've never played before"
# tutorial_skip: "Skip Tutorial"
tutorial_recommended: "Recomenat si no has jugat abans"
tutorial_skip: "Salta el tutorial"
# tutorial_not_sure: "Not sure what's going on?"
# tutorial_play_first: "Play the Tutorial first."
# simple_ai: "Simple AI"
tutorial_play_first: "Juga el tutorial primer."
simple_ai: "IA simple"
# warmup: "Warmup"
# friends_playing: "Friends Playing"
friends_playing: "Amics jugant"
# log_in_for_friends: "Log in to play with your friends!"
# social_connect_blurb: "Connect and play against your friends!"
# invite_friends_to_battle: "Invite your friends to join you in battle!"
# fight: "Fight!"
# watch_victory: "Watch your victory"
# defeat_the: "Defeat the"
# tournament_ends: "Tournament ends"
# tournament_ended: "Tournament ended"
# tournament_rules: "Tournament Rules"
fight: "Lluita!"
watch_victory: "Mira la teva victòria"
defeat_the: "Derrota a"
tournament_ends: "El torneig acaba"
tournament_ended: "El torneig ha acabat"
tournament_rules: "Normes del torneig"
# tournament_blurb: "Write code, collect gold, build armies, crush foes, win prizes, and upgrade your career in our $40,000 Greed tournament! Check out the details"
# tournament_blurb_criss_cross: "Win bids, construct paths, outwit opponents, grab gems, and upgrade your career in our Criss-Cross tournament! Check out the details"
# tournament_blurb_blog: "on our blog"
tournament_blurb_blog: "en el nostre blog"
rules: "Normes"
winners: "Guanyadors"
ladder_prizes:
title: "Premis del torneig"
# blurb_1: "These prizes will be awarded according to"
blurb_1: "Aquests premis seran guanyats d'acord amb"
blurb_2: "Les normes del torneig"
# blurb_3: "to the top human and ogre players."
# blurb_4: "Two teams means double the prizes!"
# blurb_5: "(There will be two first place winners, two second-place winners, etc.)"
blurb_3: "els millors jugadors humans i ogres."
blurb_4: "Dos equips signifiquen el doble de premis!"
blurb_5: "(Hi haura dos guanyadors pel primer lloc, dos pels del segon lloc, etc.)"
rank: "Rang"
prizes: "Premis"
total_value: "Valor total"
# in_cash: "in cash"
# custom_wizard: "Custom CodeCombat Wizard"
# custom_avatar: "Custom CodeCombat avatar"
# heap: "for six months of \"Startup\" access"
in_cash: "en diners"
custom_wizard: "Personalitza el teu bruixot de CodeCombat"
custom_avatar: "Personalitza el teu avatar de CodeCombat"
heap: "per sis mesosd'acces \"Startup\" "
credits: "credits"
# one_month_coupon: "coupon: choose either Rails or HTML"
# one_month_discount: "discount, 30% off: choose either Rails or HTML"
@ -953,21 +953,21 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
# oreilly: "ebook of your choice"
loading_error:
# could_not_load: "Error loading from server"
could_not_load: "Error de carrega del servidor"
connection_failure: "Connexió fallida."
# unauthorized: "You need to be signed in. Do you have cookies disabled?"
# forbidden: "You do not have the permissions."
forbidden: "No disposes dels permisos."
not_found: "No trobat."
# not_allowed: "Method not allowed."
not_allowed: "Metode no permès."
# timeout: "Server timeout."
# conflict: "Resource conflict."
# bad_input: "Bad input."
bad_input: "Entrada incorrecta."
server_error: "Error del servidor."
unknown: "Error desconegut."
resources:
sessions: "Sessions"
# your_sessions: "Your Sessions"
your_sessions: "Les teves sessions"
level: "Nivell"
# social_network_apis: "Social Network APIs"
# facebook_status: "Facebook Status"
@ -975,24 +975,24 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
# facebook_friend_sessions: "Facebook Friend Sessions"
# gplus_friends: "G+ Friends"
# gplus_friend_sessions: "G+ Friend Sessions"
# leaderboard: "Leaderboard"
# user_schema: "User Schema"
# user_profile: "User Profile"
# patches: "Patches"
# patched_model: "Source Document"
# model: "Model"
leaderboard: "Classificació"
user_schema: "Esquema d'usuari"
user_profile: "Perfil d'usuari"
patches: "Pegats"
patched_model: "Document font"
model: "Model"
system: "Sistema"
systems: "Sistemes"
# component: "Component"
# components: "Components"
component: "Component"
components: "Components"
# thang: "Thang"
# thangs: "Thangs"
level_session: "La teva sessió"
# opponent_session: "Opponent Session"
# article: "Article"
opponent_session: "Sessió de l'adversari"
article: "Article"
user_names: "Noms d'usuaris"
# thang_names: "Thang Names"
files: "Archius"
files: "Arxius"
# top_simulators: "Top Simulators"
# source_document: "Source Document"
document: "Documents"
@ -1007,15 +1007,15 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
heroes: "Herois"
wizard: "Bruixot"
achievement: "Triomf"
# clas: "CLAs"
clas: "CLAs"
# play_counts: "Play Counts"
# feedback: "Feedback"
feedback: "Feedback"
delta:
added: "Afegit"
modified: "Modificat"
deleted: "Eliminat"
# moved_index: "Moved Index"
moved_index: "Índex desplaçat"
# text_diff: "Text Diff"
# merge_conflict_with: "MERGE CONFLICT WITH"
no_changes: "Sense canvis"

View file

@ -59,6 +59,7 @@
about: "About"
contact: "Contact"
twitter_follow: "Follow"
teachers: "Teachers"
employers: "Employers"
versions:

View file

@ -117,7 +117,7 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
adventurer_forum: "fórum do Aventureiro"
adventurer_suffix: "."
campaign_beginner: "Campanha para Iniciantes"
# campaign_old_beginner: "Old Beginner Campaign"
campaign_old_beginner: "Campanha para Iniciantes Antiga"
campaign_beginner_description: "... onde aprendes a magia da programação."
campaign_dev: "Níveis mais Difíceis Aleatórios"
campaign_dev_description: "... onde aprendes a interface enquanto fazes coisas um bocadinho mais difíceis."
@ -140,8 +140,8 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
next: "Seguinte"
previous: "Anterior"
choose_inventory: "Equipar Itens"
# older_campaigns: "Older Campaigns"
# anonymous: "Anonymous Player"
older_campaigns: "Campanhas Mais Antigas"
anonymous: "Jogador Anónimo"
items:
armor: "Armadura"
@ -419,9 +419,9 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
tome_cast_button_running: "A Correr"
tome_cast_button_ran: "Corrido"
tome_submit_button: "Submeter"
# 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: "Recarregar o código original para este método" # Title text for individual method reload button.
tome_select_method: "Selecionar um método"
tome_see_all_methods: "Ver todos os métodos editáveis" # Title text for method list selector (shown when there are multiple programmable methdos).
tome_select_a_thang: "Seleciona Alguém para "
tome_available_spells: "Feitiços Disponíveis"
tome_your_skills: "As Tuas Habilidades"

View file

@ -61,7 +61,7 @@ _.extend AchievementSchema.properties,
description: 'For repeatables only. Denotes the field a repeatable achievement needs for its calculations'
recalculable:
type: 'boolean'
description: 'Needs to be set to true before it is eligible for recalculation.'
description: 'Deprecated: all achievements must be recalculable now. Used to need to be set to true before it is eligible for recalculation.'
function:
type: 'object'
description: 'Function that gives total experience for X amount achieved'

View file

@ -29,6 +29,9 @@ module.exports =
viewClass: {type: 'function'}
viewArgs: {type: 'array'}
'router:navigated': c.object {required: ['route']},
route: {type: 'string'}
'achievements:new': c.object {required: ['earnedAchievements']},
earnedAchievements: {type: 'object'}

View file

@ -1,5 +1,5 @@
@import "../bootstrap/variables"
@import "../bootstrap/mixins"
@import "app/styles/bootstrap/variables"
@import "app/styles/mixins"
#account-home
dl

View file

@ -1,5 +1,5 @@
@import "bootstrap/variables"
@import "bootstrap/mixins"
@import "app/styles/bootstrap/variables"
@import "app/styles/mixins"
html
background-color: #2f261d

View file

@ -1,5 +1,5 @@
@import "../bootstrap/variables"
@import "../bootstrap/mixins"
@import "app/styles/bootstrap/variables"
@import "app/styles/mixins"
// This is still very blocky. Browser reflows? Investigate why.
.open > .dropdown-menu

View file

@ -1,5 +1,5 @@
@import "bootstrap/variables"
@import "bootstrap/mixins"
@import "app/styles/bootstrap/variables"
@import "app/styles/mixins"
#community-view

View file

@ -1,4 +1,4 @@
@import "../../bootstrap/mixins"
@import "app/styles/mixins"
#add-thangs-view
$addPaletteIconColumns: 6

View file

@ -1,4 +1,4 @@
@import "../../../bootstrap/mixins"
@import "app/styles/mixins"
#world-select-modal
@include user-select(none)
@ -25,4 +25,4 @@
width: 23%
box-sizing: border-box
text-align: center
margin: 0 1% 10px
margin: 0 1% 10px

View file

@ -1,4 +1,4 @@
@import "../../bootstrap/mixins"
@import "app/styles/mixins"
#thangs-tab-view
$extantThangsWidth: 250px
@ -121,4 +121,4 @@
z-index: 1
#normal-surface
z-index: 2
z-index: 2

View file

@ -1,5 +1,5 @@
@import "bootstrap/mixins"
@import "bootstrap/variables"
@import "app/styles/mixins"
@import "app/styles/bootstrap/variables"
#front-view
h1

View file

@ -1,5 +1,5 @@
@import "bootstrap/mixins"
@import "bootstrap/variables"
@import "app/styles/mixins"
@import "app/styles/bootstrap/variables"
#home-view

View file

@ -1,5 +1,5 @@
@import "bootstrap/mixins"
@import "bootstrap/variables"
@import "app/styles/mixins"
@import "app/styles/bootstrap/variables"
#not-found-view

View file

@ -1,5 +1,5 @@
@import "bootstrap/mixins"
@import "bootstrap/variables"
@import "app/styles/mixins"
@import "app/styles/bootstrap/variables"
#play-view
.row

View file

@ -1,4 +1,4 @@
@import "app/styles/bootstrap/mixins"
@import "app/styles/mixins"
@import "app/styles/bootstrap/variables"
#ladder-home-view

View file

@ -1,5 +1,5 @@
@import "app/styles/bootstrap/mixins"
@import "app/styles/mixins"
@import "app/styles/bootstrap/variables"
body.is-playing
background-color: black
@ -267,6 +267,9 @@ body.ipad #level-view
a, .editor-dash
display: none
#goals-view
left: 40px
#level-chat-view
bottom: 40px

View file

@ -1,4 +1,5 @@
@import "../../bootstrap/mixins"
@import "app/styles/mixins"
@import "app/styles/bootstrap/variables"
#level-chat-view
@ -76,4 +77,4 @@
border-color: rgba(0,0,0,0.4)
border-radius: 2px
border-width: 1px
text-shadow: 1px 1px 1px black
text-shadow: 1px 1px 1px black

View file

@ -1,5 +1,5 @@
@import "../../bootstrap/variables"
@import "../../bootstrap/mixins"
@import "app/styles/mixins"
@import "app/styles/bootstrap/variables"
#control-bar-view
width: 55%

View file

@ -1,4 +1,5 @@
@import "../../bootstrap/mixins"
@import "app/styles/mixins"
@import "app/styles/bootstrap/variables"
#goals-view
position: absolute
@ -50,4 +51,4 @@
display: none
#goals-view.collapsed ul
display: none
display: none

View file

@ -1,5 +1,5 @@
@import "app/styles/mixins"
@import "app/styles/bootstrap/mixins"
@import "app/styles/bootstrap/variables"
#gold-view
display: none

View file

@ -1,5 +1,5 @@
@import "app/styles/bootstrap/mixins"
@import "app/styles/mixins"
@import "app/styles/bootstrap/variables"
#thang-hud.no-selection
.center

View file

@ -1,5 +1,5 @@
@import "app/styles/mixins"
@import "app/styles/bootstrap/mixins"
@import "app/styles/bootstrap/variables"
#level-flags-view
display: none

View file

@ -1,5 +1,5 @@
@import "app/styles/bootstrap/mixins"
@import "app/styles/mixins"
@import "app/styles/bootstrap/variables"
@mixin wing-background($url: '', $backgroundPosition: left)
background: black

View file

@ -1,4 +1,5 @@
@import "../../bootstrap/mixins"
@import "app/styles/mixins"
@import "app/styles/bootstrap/variables"
#playback-view
width: 55%

View file

@ -1,5 +1,5 @@
@import "app/styles/bootstrap/mixins"
@import "app/styles/mixins"
@import "app/styles/bootstrap/variables"
.thang-avatar-view

View file

@ -1,5 +1,5 @@
@import "app/styles/bootstrap/mixins"
@import "app/styles/mixins"
@import "app/styles/bootstrap/variables"
+keyframes(castablePulse)
from

View file

@ -1,4 +1,5 @@
@import "../../../bootstrap/mixins"
@import "app/styles/mixins"
@import "app/styles/bootstrap/variables"
.problem-alert
z-index: 10

View file

@ -1,4 +1,5 @@
@import "app/styles/bootstrap/mixins"
@import "app/styles/mixins"
@import "app/styles/bootstrap/variables"
@mixin editor-height($extraHeight)
@include box-sizing(border-box)

View file

@ -1,4 +1,5 @@
@import "../../../bootstrap/mixins"
@import "app/styles/mixins"
@import "app/styles/bootstrap/variables"
.spell-debug-view
position: absolute

View file

@ -1,4 +1,5 @@
@import "../../../bootstrap/mixins"
@import "app/styles/mixins"
@import "app/styles/bootstrap/variables"
#spell-list-view
display: none
@ -15,4 +16,4 @@ html.no-borderimage
#spell-list-view
background: transparent url(/images/level/popover_background.png)
background-size: 100% 100%
border: 0
border: 0

View file

@ -1,4 +1,5 @@
@import "../../../bootstrap/mixins"
@import "app/styles/mixins"
@import "app/styles/bootstrap/variables"
.spell-list-entry-view
code

View file

@ -1,4 +1,5 @@
@import "../../../bootstrap/mixins"
@import "app/styles/mixins"
@import "app/styles/bootstrap/variables"
.spell-list-entry-view
.spell-list-entry-thangs-view
@ -25,4 +26,4 @@ html.no-borderimage
.spell-list-entry-thangs-view
background: transparent url(/images/level/popover_background.png)
background-size: 100% 100%
border: 0
border: 0

View file

@ -1,4 +1,5 @@
@import "../../../bootstrap/mixins"
@import "app/styles/mixins"
@import "app/styles/bootstrap/variables"
#spell-palette-view
position: absolute

View file

@ -1,5 +1,5 @@
@import "app/styles/bootstrap/mixins"
@import "app/styles/mixins"
@import "app/styles/bootstrap/variables"
#level-view.hero .spell-palette-entry-view
// Not clickable.

View file

@ -1,4 +1,5 @@
@import "../../../bootstrap/mixins"
@import "app/styles/mixins"
@import "app/styles/bootstrap/variables"
.spell-toolbar-view
position: relative

View file

@ -1,4 +1,5 @@
@import "../../../bootstrap/mixins"
@import "app/styles/mixins"
@import "app/styles/bootstrap/variables"
#thang-list-view
margin: 50px 15px 15px 15px

View file

@ -1,4 +1,5 @@
@import "../../../bootstrap/mixins"
@import "app/styles/mixins"
@import "app/styles/bootstrap/variables"
.thang-list-entry-view
@include opacity(0.90)

View file

@ -1,4 +1,5 @@
@import "../../../bootstrap/mixins"
@import "app/styles/mixins"
@import "app/styles/bootstrap/variables"
.thang-list-entry-spells

View file

@ -1,5 +1,5 @@
@import "app/styles/bootstrap/mixins"
@import "app/styles/mixins"
@import "app/styles/bootstrap/variables"
#tome-view
height: 100%

View file

@ -1,5 +1,5 @@
@import "app/styles/bootstrap/mixins"
@import "app/styles/mixins"
@import "app/styles/bootstrap/variables"
#spectate-level-view
#playback-view

View file

@ -1,6 +1,5 @@
@import "app/styles/bootstrap/mixins"
@import "app/styles/bootstrap/variables"
@import "app/styles/mixins"
@import "app/styles/bootstrap/variables"
$forestMapWidth: 2500
$forestMapHeight: 1536

View file

@ -1,5 +1,5 @@
@import "bootstrap/variables"
@import "bootstrap/mixins"
@import "app/styles/mixins"
@import "app/styles/bootstrap/variables"
#employers-wrapper
background-color: #B4B4B4

View file

@ -1,5 +1,5 @@
@import "../bootstrap/variables"
@import "../bootstrap/mixins"
@import "app/styles/mixins"
@import "app/styles/bootstrap/variables"
#user-home
margin-top: 20px

View file

@ -75,7 +75,7 @@ body
a(title='Contact', tabindex=-1, data-toggle="coco-modal", data-target="modal/ContactModal", data-i18n="nav.contact") Contact
a(href='http://blog.codecombat.com/', data-i18n="nav.blog") Blog
a(href='http://discourse.codecombat.com/', data-i18n="nav.forum") Forum
a(href='/teachers', data-i18n="nav.forum") Teachers
a(href='/teachers', data-i18n="nav.teachers") Teachers
if me.isAdmin()
a(href='/admin', data-i18n="nav.admin") Admin

View file

@ -10,6 +10,7 @@ block content
li.active
| #{achievement.attributes.name}
button.achievement-tool-button(data-i18n="", disabled=me.isAdmin() === true ? undefined : "true").btn.btn-primary#recalculate-all-button Recalculate All
button.achievement-tool-button(data-i18n="", disabled=me.isAdmin() === true ? undefined : "true").btn.btn-primary#recalculate-button Recalculate
button.achievement-tool-button(data-i18n="common.delete", disabled=me.isAdmin() === true ? undefined : "true").btn.btn-primary#delete-button Delete
button.achievement-tool-button(data-i18n="common.save", disabled=me.isAdmin() === true ? undefined : "true").btn.btn-primary#save-button Save

View file

@ -12,7 +12,7 @@ block content
p CodeCombat is free to play and does not require students to sign up. We encourage teachers to
a(href="http://codecombat.com/play") play through the campaign
| to try it out, but the only thing you absolutely need to do to be ready is ensure students have access to a computer or iPad.
| to try it out, but the only thing you absolutely need to do to be ready is ensure students have access to a computer or iPad.
p It is not necessary for teachers to be comfortable with computer science concepts for students to have fun learning with CodeCombat.

View file

@ -14,6 +14,7 @@ module.exports = class AchievementEditView extends RootView
events:
'click #save-button': 'saveAchievement'
'click #recalculate-button': 'confirmRecalculation'
'click #recalculate-all-button': 'confirmAllRecalculation'
'click #delete-button': 'confirmDeletion'
constructor: (options, @achievementID) ->
@ -82,17 +83,21 @@ module.exports = class AchievementEditView extends RootView
url = "/editor/achievement/#{@achievement.get('slug') or @achievement.id}"
document.location.href = url
confirmRecalculation: ->
confirmRecalculation: (e, all=false) ->
renderData =
'confirmTitle': 'Are you really sure?'
'confirmBody': 'This will trigger recalculation of the achievement for all users. Are you really sure you want to go down this path?'
'confirmBody': "This will trigger recalculation of #{if all then 'all achievements' else 'the achievement'} for all users. Are you really sure you want to go down this path?"
'confirmDecline': 'Not really'
'confirmConfirm': 'Definitely'
confirmModal = new ConfirmModal renderData
confirmModal.on 'confirm', @recalculateAchievement
@recalculatingAll = all
@openModalView confirmModal
confirmAllRecalculation: (e) ->
@confirmRecalculation e, true
confirmDeletion: ->
renderData =
'confirmTitle': 'Are you really sure?'
@ -105,8 +110,9 @@ module.exports = class AchievementEditView extends RootView
@openModalView confirmModal
recalculateAchievement: =>
data = if @recalculatingAll then {} else {achievements: [@achievement.get('slug') or @achievement.get('_id')]}
$.ajax
data: JSON.stringify(earnedAchievements: [@achievement.get('slug') or @achievement.get('_id')])
data: JSON.stringify data
success: (data, status, jqXHR) ->
noty
timeout: 5000

View file

@ -68,8 +68,7 @@ module.exports = class WorldMapView extends RootView
for level, index in campaign.levels
level.x ?= 10 + 80 * Math.random()
level.y ?= 10 + 80 * Math.random()
#level.locked = index > 0 and not me.earnedLevel level.original
level.locked = false # Not working yet, but time for sleep.
level.locked = index > 0 and not me.earnedLevel level.original
context.levelStatusMap = @levelStatusMap
context.levelPlayCountMap = @levelPlayCountMap
context.isIPadApp = application.isIPadApp

View file

@ -272,7 +272,7 @@ module.exports = class PlayLevelView extends RootView
@insertSubView new ChatView levelID: @levelID, sessionID: @session.id, session: @session
worldName = utils.i18n @level.attributes, 'name'
@controlBar = @insertSubView new ControlBarView {worldName: worldName, session: @session, level: @level, supermodel: @supermodel}
Backbone.Mediator.publish('level:set-debug', debug: true) if @isIPadApp() # if me.displayName() is 'Nick'
_.delay (=> Backbone.Mediator.publish('level:set-debug', debug: true)), 5000 if @isIPadApp() # if me.displayName() is 'Nick'
initVolume: ->
volume = me.get('volume')

View file

@ -25,11 +25,11 @@ exports.config =
vendor[\/\\](?!scripts[\/\\]Box2d)
|bower_components[\/\\](?!aether)
)///
'javascripts/box2d.js': ///^(
'javascripts/box2d.js': ///^(
# Include box2dweb for profiling and IE9
# Vector renamed to Box2DVector to avoid name collisions
# TODO: move this to assets/lib since we're not really joining anything here?
(vendor[\/\\]scripts[\/\\]Box2dWeb-2.1.a.3)
(vendor[\/\\]scripts[\/\\]Box2dWeb-2.1.a.3)
)///
'javascripts/lodash.js': ///^(
(bower_components[\/\\]lodash[\/\\]dist[\/\\]lodash.js)
@ -92,6 +92,9 @@ exports.config =
uglify:
output:
semicolons: false
sass:
mode: 'ruby'
allowCache: true
onCompile: (files) ->
exec = require('child_process').exec

View file

@ -75,7 +75,7 @@
"javascript-brunch": "> 1.0 < 1.8",
"coffee-script-brunch": "https://github.com/brunch/coffee-script-brunch/tarball/master",
"coffeelint-brunch": "> 1.0 < 1.8",
"sass-brunch": "1.8.3",
"sass-brunch": "1.7.0",
"css-brunch": "> 1.0 < 1.8",
"jade-brunch": "> 1.0 < 1.8",
"uglify-js-brunch": "~1.7.4",
@ -86,7 +86,6 @@
"marked": "0.2.x",
"telepath-brunch": "https://github.com/nwinter/telepath-brunch/tarball/master",
"bower": "~1.3.8",
"bless-brunch": "https://github.com/ThomasConner/bless-brunch/tarball/master",
"karma-script-launcher": "~0.1.0",
"karma-chrome-launcher": "~0.1.2",
"karma-firefox-launcher": "~0.1.3",

View file

@ -30,8 +30,6 @@ AchievementSchema.methods.getExpFunction = ->
TreemaUtils.populateDefaults(func, jsonschema.properties.function)
return utils.functionCreators[func.kind](func.parameters) if func.kind of utils.functionCreators
AchievementSchema.methods.isRecalculable = -> @get('recalculable') isnt false
AchievementSchema.statics.jsonschema = jsonschema
AchievementSchema.statics.earnedAchievements = {}

View file

@ -5,7 +5,7 @@ class AchievementHandler extends Handler
modelClass: Achievement
# Used to determine which properties requests may edit
editableProperties: ['name', 'query', 'worth', 'collection', 'description', 'userField', 'proportionalTo', 'icon', 'function', 'related', 'difficulty', 'category', 'recalculable', 'rewards']
editableProperties: ['name', 'query', 'worth', 'collection', 'description', 'userField', 'proportionalTo', 'icon', 'function', 'related', 'difficulty', 'category', 'rewards']
allowedMethods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE']
jsonSchema = require '../../app/schemas/models/achievement.coffee'

View file

@ -17,7 +17,7 @@ class EarnedAchievementHandler extends Handler
recalculate: (req, res) ->
onSuccess = (data) => log.debug 'Finished recalculating achievements'
if 'achievements' of req.body # Support both slugs and IDs separated by commas
achievementSlugsOrIDs = req.body.earnedAchievements
achievementSlugsOrIDs = req.body.achievements
EarnedAchievementHandler.recalculate achievementSlugsOrIDs, onSuccess
else
EarnedAchievementHandler.recalculate onSuccess
@ -27,8 +27,10 @@ class EarnedAchievementHandler extends Handler
if _.isArray callbackOrSlugsOrIDs # slugs or ids
achievementSlugs = (thing for thing in callbackOrSlugsOrIDs when not Handler.isID(thing))
achievementIDs = (thing for thing in callbackOrSlugsOrIDs when Handler.isID(thing))
recalculatingAll = false
else # just a callback
callback = callbackOrSlugsOrIDs
recalculatingAll = true
t0 = new Date().getTime()
total = 100000
User.count {anonymous:false}, (err, count) -> total = count
@ -94,8 +96,6 @@ class EarnedAchievementHandler extends Handler
newTotalRewards = heroes: [], items: [], levels: [], gems: 0
async.each achievements, ((achievement, doneWithAchievement) ->
return doneWithAchievement() unless achievement.isRecalculable()
isRepeatable = achievement.get('proportionalTo')?
model = mongoose.modelNameByCollection(achievement.get('collection'))
return doneWithAchievement new Error "Model with collection '#{achievement.get 'collection'}' doesn't exist." unless model?
@ -137,36 +137,58 @@ class EarnedAchievementHandler extends Handler
doneWithAchievement err
), (err) -> # Wrap up a user, save points
log.error err if err
# Since some achievements cannot be recalculated it's important to deduct the old amount of exp
# and add the new amount, instead of just setting to the new amount
#console.log 'User', user.get('name'), 'had newTotalPoints', newTotalPoints, 'and newTotalRewards', newTotalRewards
#console.log 'User', user.get('name'), 'had newTotalPoints', newTotalPoints, 'and newTotalRewards', newTotalRewards, 'previousRewards', previousRewards
return doneWithUser(user) unless newTotalPoints or newTotalRewards.gems or _.some(newTotalRewards, (r) -> r.length)
# log.debug "Matched a total of #{newTotalPoints} new points"
# log.debug "Incrementing score for these achievements with #{newTotalPoints - previousPoints}"
pointDelta = newTotalPoints - previousPoints
pctDone = (100 * usersFinished / total).toFixed(2)
console.log "Updated points to #{newTotalPoints}(+#{newTotalPoints - previousPoints}) for #{user.get('name') or '???'} (#{user.get('_id')}) (#{pctDone}%)"
update = {$inc: {points: newTotalPoints - previousPoints}}
console.log "Updated points to #{newTotalPoints} (#{if pointDelta < 0 then '' else '+'}#{pointDelta}) for #{user.get('name') or '???'} (#{user.get('_id')}) (#{pctDone}%)"
if recalculatingAll
update = {$set: {points: newTotalPoints, 'earned.gems': 0, 'earned.heroes': [], 'earned.items': [], 'earned.levels': []}}
else
update = {$inc: {points: pointDelta}}
secondUpdate = {} # In case we need to pull, then push.
for rewardType, rewards of newTotalRewards
updateKey = "earned.#{rewardType}"
if rewardType is 'gems'
update.$inc['earned.gems'] = rewards - previousRewards.gems
if recalculatingAll
update.$set[updateKey] = rewards
else
update.$inc[updateKey] = rewards - previousRewards.gems
else
previousCounts = _.countBy previousRewards[rewardType]
newCounts = _.countBy rewards
relevantRewards = _.union _.keys(previousCounts), _.keys(newCounts)
for reward in relevantRewards
[previousCount, newCount] = [previousCounts[reward], newCounts[reward]]
if newCount and not previousCount
update.$addToSet ?= {}
update.$addToSet["earned.#{rewardType}"] ?= {$each: []}
update.$addToSet["earned.#{rewardType}"].$each.push reward
else if previousCount and not newCount
# Might $pull $each also work here?
update.$pullAll ?= {}
update.$pullAll["earned.#{rewardType}"] ?= []
update.$pullAll["earned.#{rewardType}"].push reward
if recalculatingAll
update.$set[updateKey] = _.uniq rewards
else
previousCounts = _.countBy previousRewards[rewardType]
newCounts = _.countBy rewards
relevantRewards = _.union _.keys(previousCounts), _.keys(newCounts)
for reward in relevantRewards
[previousCount, newCount] = [previousCounts[reward], newCounts[reward]]
if newCount and not previousCount
update.$addToSet ?= {}
update.$addToSet[updateKey] ?= {$each: []}
update.$addToSet[updateKey].$each.push reward
else if previousCount and not newCount
# Might $pull $each also work here?
update.$pullAll ?= {}
update.$pullAll[updateKey] ?= []
update.$pullAll[updateKey].push reward
if update.$addToSet?[updateKey] and update.$pullAll?[updateKey]
# Perform the update in two calls to avoid "MongoError: Cannot update 'earned.levels' and 'earned.levels' at the same time"
secondUpdate.$addToSet ?= {}
secondUpdate.$addToSet[updateKey] = update.$addToSet[updateKey]
delete update.$addToSet[updateKey]
delete update.$addToSet unless _.size update.$addToSet
#console.log 'recalculatingAll?', recalculatingAll, 'so update is', update, 'secondUpdate', secondUpdate
User.update {_id: userID}, update, {}, (err) ->
log.error err if err?
doneWithUser(user)
if _.size secondUpdate
User.update {_id: userID}, secondUpdate, {}, (err) ->
log.error err if err?
doneWithUser user
else
doneWithUser user
module.exports = new EarnedAchievementHandler()

View file

@ -34,64 +34,65 @@ AchievablePlugin = (schema, options) ->
if category of loadedAchievements
docObj = doc.toObject()
for achievement in loadedAchievements[category]
query = achievement.get('query')
isRepeatable = achievement.get('proportionalTo')?
alreadyAchieved = if isNew then false else LocalMongo.matchesQuery originalDocObj, query
newlyAchieved = LocalMongo.matchesQuery(docObj, query)
#log.debug 'isRepeatable: ' + isRepeatable
#log.debug 'alreadyAchieved: ' + alreadyAchieved
#log.debug 'newlyAchieved: ' + newlyAchieved
do (achievement) ->
query = achievement.get('query')
isRepeatable = achievement.get('proportionalTo')?
alreadyAchieved = if isNew then false else LocalMongo.matchesQuery originalDocObj, query
newlyAchieved = LocalMongo.matchesQuery(docObj, query)
#log.debug 'isRepeatable: ' + isRepeatable
#log.debug 'alreadyAchieved: ' + alreadyAchieved
#log.debug 'newlyAchieved: ' + newlyAchieved
userObjectID = doc.get(achievement.get('userField'))
userID = if _.isObject userObjectID then userObjectID.toHexString() else userObjectID # Standardize! Use strings, not ObjectId's
userObjectID = doc.get(achievement.get('userField'))
userID = if _.isObject userObjectID then userObjectID.toHexString() else userObjectID # Standardize! Use strings, not ObjectId's
if newlyAchieved and (not alreadyAchieved or isRepeatable)
earned =
user: userID
achievement: achievement._id.toHexString()
achievementName: achievement.get 'name'
earnedRewarsd: achievement.get 'rewards'
if newlyAchieved and (not alreadyAchieved or isRepeatable)
earned =
user: userID
achievement: achievement._id.toHexString()
achievementName: achievement.get 'name'
earnedRewarsd: achievement.get 'rewards'
worth = achievement.get('worth') ? 10
earnedPoints = 0
wrapUp = ->
# Update user's experience points
update = {$inc: {points: earnedPoints}}
for rewardType, rewards of achievement.get('rewards') ? {}
if rewardType is 'gems'
update.$inc['earned.gems'] = rewards if rewards
else if rewards.length
update.$addToSet ?= {}
update.$addToSet["earned.#{rewardType}"] = $each: rewards
User.update {_id: userID}, update, {}, (err, count) ->
log.error err if err?
worth = achievement.get('worth') ? 10
earnedPoints = 0
wrapUp = ->
# Update user's experience points
update = {$inc: {points: earnedPoints}}
for rewardType, rewards of achievement.get('rewards') ? {}
if rewardType is 'gems'
update.$inc['earned.gems'] = rewards if rewards
else if rewards.length
update.$addToSet ?= {}
update.$addToSet["earned.#{rewardType}"] = $each: rewards
User.update {_id: userID}, update, {}, (err, count) ->
log.error err if err?
if isRepeatable
#log.debug 'Upserting repeatable achievement called \'' + (achievement.get 'name') + '\' for ' + userID
proportionalTo = achievement.get 'proportionalTo'
originalAmount = if originalDocObj then util.getByPath(originalDocObj, proportionalTo) or 0 else 0
newAmount = docObj[proportionalTo]
if isRepeatable
#log.debug 'Upserting repeatable achievement called \'' + (achievement.get 'name') + '\' for ' + userID
proportionalTo = achievement.get 'proportionalTo'
originalAmount = if originalDocObj then util.getByPath(originalDocObj, proportionalTo) or 0 else 0
newAmount = docObj[proportionalTo]
if originalAmount isnt newAmount
expFunction = achievement.getExpFunction()
earned.notified = false
earned.achievedAmount = newAmount
earned.earnedPoints = (expFunction(newAmount) - expFunction(originalAmount)) * worth
earned.previouslyAchievedAmount = originalAmount
EarnedAchievement.update {achievement: earned.achievement, user: earned.user}, earned, {upsert: true}, (err) ->
return log.debug err if err?
if originalAmount isnt newAmount
expFunction = achievement.getExpFunction()
earned.notified = false
earned.achievedAmount = newAmount
earned.earnedPoints = (expFunction(newAmount) - expFunction(originalAmount)) * worth
earned.previouslyAchievedAmount = originalAmount
EarnedAchievement.update {achievement: earned.achievement, user: earned.user}, earned, {upsert: true}, (err) ->
return log.debug err if err?
earnedPoints = earned.earnedPoints
#log.debug earnedPoints
wrapUp()
earnedPoints = earned.earnedPoints
#log.debug earnedPoints
wrapUp()
else # not alreadyAchieved
#log.debug 'Creating a new earned achievement called \'' + (achievement.get 'name') + '\' for ' + userID
earned.earnedPoints = worth
(new EarnedAchievement(earned)).save (err, doc) ->
return log.error err if err?
earnedPoints = worth
wrapUp()
else # not alreadyAchieved
#log.debug 'Creating a new earned achievement called \'' + (achievement.get 'name') + '\' for ' + userID
earned.earnedPoints = worth
(new EarnedAchievement(earned)).save (err, doc) ->
return log.error err if err?
earnedPoints = worth
wrapUp()
delete before[doc.id] if doc.id of before

View file

@ -528,7 +528,7 @@ var p = SpriteStage.prototype = new createjs.Stage();
* into itself).
**/
p.draw = function(ctx, ignoreCache) {
if (ctx === this._webGLContext || ctx instanceof WebGLRenderingContext) {
if (typeof WebGLRenderingContext !== 'undefined' && (ctx === this._webGLContext || ctx instanceof WebGLRenderingContext)) {
this._drawWebGLKids(this.children, ctx);
// If there is a remaining texture, draw it: