mirror of
https://github.com/codeninjasllc/codecombat.git
synced 2024-11-24 16:17:57 -05:00
Merge branch 'master' into production
This commit is contained in:
commit
0374c65c22
90 changed files with 1349 additions and 290 deletions
|
@ -1,5 +1,5 @@
|
|||
{me} = require 'core/auth'
|
||||
AnalyticsLogEvent = require 'models/AnalyticsLogEvent'
|
||||
SuperModel = require 'models/SuperModel'
|
||||
|
||||
debugAnalytics = false
|
||||
|
||||
|
@ -10,6 +10,7 @@ module.exports = class Tracker
|
|||
window.tracker = @
|
||||
@isProduction = document.location.href.search('codecombat.com') isnt -1
|
||||
@identify()
|
||||
@supermodel = new SuperModel()
|
||||
|
||||
identify: (traits) ->
|
||||
console.log 'Would identify', traits if debugAnalytics
|
||||
|
@ -61,19 +62,7 @@ module.exports = class Tracker
|
|||
# https://segment.com/docs/integrations/mixpanel/
|
||||
properties = properties or {}
|
||||
|
||||
# Log internally
|
||||
# Skipping heavily logged actions we don't use internally
|
||||
unless action in ['Simulator Result', 'Loaded World Map', 'Started Level Load', 'Finished Level Load', 'Homepage Loaded']
|
||||
# Trimming properties we don't use internally
|
||||
# TODO: delete internalProperites.level for 'Saw Victory' after 2/8/15. Should be using levelID instead.
|
||||
internalProperties = _.cloneDeep properties
|
||||
if action in ['Clicked Level', 'Inventory Play', 'Heard Sprite', 'Started Level', 'Saw Victory', 'Click Play', 'Choose Inventory']
|
||||
delete internalProperties.category
|
||||
delete internalProperties.label
|
||||
|
||||
console.log 'Tracking internal analytics event:', action, internalProperties, includeIntegrations if debugAnalytics
|
||||
event = new AnalyticsLogEvent event: action, properties: internalProperties
|
||||
event.save()
|
||||
@trackEventInternal action, _.cloneDeep properties
|
||||
|
||||
console.log 'Would track analytics event:', action, properties, includeIntegrations if debugAnalytics
|
||||
return unless me and @isProduction and analytics? and not me.isAdmin()
|
||||
|
@ -85,6 +74,24 @@ module.exports = class Tracker
|
|||
context.integrations[integration] = true
|
||||
analytics?.track action, properties, context
|
||||
|
||||
trackEventInternal: (event, properties) =>
|
||||
# Skipping heavily logged actions we don't use internally
|
||||
unless event in ['Simulator Result', 'Started Level Load', 'Finished Level Load']
|
||||
# Trimming properties we don't use internally
|
||||
# TODO: delete internalProperites.level for 'Saw Victory' after 2/8/15. Should be using levelID instead.
|
||||
if event in ['Clicked Level', 'Inventory Play', 'Heard Sprite', 'Started Level', 'Saw Victory', 'Click Play', 'Choose Inventory', 'Loaded World Map', 'Homepage Loaded', 'Change Hero']
|
||||
delete properties.category
|
||||
delete properties.label
|
||||
else if event in ['Started Signup', 'Finished Signup', 'Login', 'Facebook Login', 'Google Login']
|
||||
delete properties.category
|
||||
|
||||
console.log 'Tracking internal analytics event:', event, properties if debugAnalytics
|
||||
request = @supermodel.addRequestResource 'log_event', {
|
||||
url: '/db/analytics_log_event/-/log_event'
|
||||
data: {event: event, properties: properties}
|
||||
method: 'POST'
|
||||
}, 0
|
||||
request.load()
|
||||
|
||||
trackTiming: (duration, category, variable, label, samplePercentage=5) ->
|
||||
# https://developers.google.com/analytics/devguides/collection/gajs/gaTrackingTiming
|
||||
|
|
|
@ -131,7 +131,7 @@ module.exports.kindaEqual = compare = (l, r) ->
|
|||
else
|
||||
return false
|
||||
|
||||
# Return UTC string "YYYY-MM-DD" for today + offset
|
||||
# Return UTC string "YYYYMMDD" for today + offset
|
||||
module.exports.getUTCDay = (offset=0) ->
|
||||
day = new Date()
|
||||
day.setDate(day.getUTCDate() + offset)
|
||||
|
@ -140,7 +140,7 @@ module.exports.getUTCDay = (offset=0) ->
|
|||
partMonth = "0" + partMonth if partMonth < 10
|
||||
partDay = day.getUTCDate()
|
||||
partDay = "0" + partDay if partDay < 10
|
||||
"#{partYear}-#{partMonth}-#{partDay}"
|
||||
"#{partYear}#{partMonth}#{partDay}"
|
||||
|
||||
# Fast, basic way to replace text in an element when you don't need much.
|
||||
# http://stackoverflow.com/a/4962398/540620
|
||||
|
|
|
@ -220,8 +220,8 @@ module.exports = ScriptManager = class ScriptManager extends CocoClass
|
|||
@notifyScriptStateChanged()
|
||||
@scriptInProgress = true
|
||||
@currentTimeouts = []
|
||||
scriptLabel = "#{@levelID}: #{nextNoteGroup.scriptID} - #{nextNoteGroup.name}"
|
||||
application.tracker?.trackEvent 'Script Started', {label: scriptLabel}, ['Google Analytics']
|
||||
scriptLabel = "#{nextNoteGroup.scriptID} - #{nextNoteGroup.name}"
|
||||
application.tracker?.trackEvent 'Script Started', {levelID: @levelID, label: scriptLabel, ls: @session?.get('_id')}, ['Google Analytics']
|
||||
console.debug "SCRIPT: Starting note group '#{nextNoteGroup.name}'" if @debugScripts
|
||||
for module in nextNoteGroup.modules
|
||||
@processNote(note, nextNoteGroup) for note in module.startNotes()
|
||||
|
@ -283,8 +283,8 @@ module.exports = ScriptManager = class ScriptManager extends CocoClass
|
|||
return if @ending # kill infinite loops right here
|
||||
@ending = true
|
||||
return unless @currentNoteGroup?
|
||||
scriptLabel = "#{@levelID}: #{@currentNoteGroup.scriptID} - #{@currentNoteGroup.name}"
|
||||
application.tracker?.trackEvent 'Script Ended', {label: scriptLabel}, ['Google Analytics']
|
||||
scriptLabel = "#{@currentNoteGroup.scriptID} - #{@currentNoteGroup.name}"
|
||||
application.tracker?.trackEvent 'Script Ended', {levelID: @levelID, label: scriptLabel, ls: @session?.get('_id')}, ['Google Analytics']
|
||||
console.debug "SCRIPT: Ending note group '#{@currentNoteGroup.name}'" if @debugScripts
|
||||
clearTimeout(timeout) for timeout in @currentTimeouts
|
||||
for module in @currentNoteGroup.modules
|
||||
|
|
|
@ -30,6 +30,7 @@ module.exports = Lank = class Lank extends CocoClass
|
|||
thang: null
|
||||
camera: null
|
||||
showInvisible: false
|
||||
preloadSounds: true
|
||||
|
||||
possessed: false
|
||||
flipped: false
|
||||
|
@ -84,8 +85,9 @@ module.exports = Lank = class Lank extends CocoClass
|
|||
|
||||
onThangTypeLoaded: ->
|
||||
@stillLoading = false
|
||||
for trigger, sounds of @thangType.get('soundTriggers') or {} when trigger isnt 'say'
|
||||
AudioPlayer.preloadSoundReference sound for sound in sounds when sound
|
||||
if @options.preloadSounds
|
||||
for trigger, sounds of @thangType.get('soundTriggers') or {} when trigger isnt 'say'
|
||||
AudioPlayer.preloadSoundReference sound for sound in sounds when sound
|
||||
if @thangType.get('raster')
|
||||
@actions = {}
|
||||
@isRaster = true
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi
|
|||
# revert: "Revert"
|
||||
# revert_models: "Revert Models"
|
||||
# pick_a_terrain: "Pick A Terrain"
|
||||
# small: "Small"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
# grassy: "Grassy"
|
||||
# small: "Small"
|
||||
# large: "Large"
|
||||
# fork_title: "Fork New Version"
|
||||
# fork_creating: "Creating Fork..."
|
||||
# generate_terrain: "Generate Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "العربية", englishDescription: "Arabi
|
|||
# level_tab_thangs_all: "All"
|
||||
# level_tab_thangs_conditions: "Starting Conditions"
|
||||
# level_tab_thangs_add: "Add Thangs"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
# delete: "Delete"
|
||||
# duplicate: "Duplicate"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
# rotate: "Rotate"
|
||||
# level_settings_title: "Settings"
|
||||
# level_component_tab_title: "Current Components"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "български език", englishDescri
|
|||
# revert: "Revert"
|
||||
# revert_models: "Revert Models"
|
||||
# pick_a_terrain: "Pick A Terrain"
|
||||
# small: "Small"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
# grassy: "Grassy"
|
||||
# small: "Small"
|
||||
# large: "Large"
|
||||
# fork_title: "Fork New Version"
|
||||
# fork_creating: "Creating Fork..."
|
||||
# generate_terrain: "Generate Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "български език", englishDescri
|
|||
# level_tab_thangs_all: "All"
|
||||
# level_tab_thangs_conditions: "Starting Conditions"
|
||||
# level_tab_thangs_add: "Add Thangs"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
# delete: "Delete"
|
||||
# duplicate: "Duplicate"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
# rotate: "Rotate"
|
||||
# level_settings_title: "Settings"
|
||||
# level_component_tab_title: "Current Components"
|
||||
|
|
|
@ -6,12 +6,12 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
|||
play: "Jugar" # The big play button that opens up the campaign view.
|
||||
old_browser: "Uh oh, el teu navegador és massa antic per fer funcionar CodeCombat. Perdó!" # Warning that shows up on really old Firefox/Chrome/Safari
|
||||
old_browser_suffix: "Pots probar-ho igualment, però el més segur és que no funcioni."
|
||||
# ipad_browser: "Bad news: CodeCombat doesn't run on iPad in the browser. Good news: our native iPad app is awaiting Apple approval."
|
||||
ipad_browser: "Males notícies: CodeCombat no funciona en el navegador d'iPad. Bones notícies: la nostre aplicació d'iPad està esperant l'aprovació d'Apple."
|
||||
campaign: "Campanya"
|
||||
for_beginners: "Per a principiants"
|
||||
multiplayer: "Multijugador" # Not currently shown on home page
|
||||
for_developers: "Per a Desenvolupadors" # Not currently shown on home page.
|
||||
# or_ipad: "Or download for iPad"
|
||||
or_ipad: "O descarrega-la per iPad"
|
||||
|
||||
nav:
|
||||
play: "Nivells" # The top nav bar entry where players choose which levels to play
|
||||
|
@ -53,12 +53,12 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
|||
players: "jugadors" # Hover over a level on /play
|
||||
hours_played: "hores de joc" # Hover over a level on /play
|
||||
items: "Objectes" # Tooltip on item shop button from /play
|
||||
# unlock: "Unlock" # For purchasing items and heroes
|
||||
# confirm: "Confirm"
|
||||
# owned: "Owned" # For items you own
|
||||
# locked: "Locked"
|
||||
# purchasable: "Purchasable" # For a hero you unlocked but haven't purchased
|
||||
# available: "Available"
|
||||
unlock: "Desbloquejar" # For purchasing items and heroes
|
||||
confirm: "Confirmar"
|
||||
owned: "En propietat" # For items you own
|
||||
locked: "Bloquejat"
|
||||
purchasable: "Comprable" # For a hero you unlocked but haven't purchased
|
||||
available: "Disponible"
|
||||
# skills_granted: "Skills Granted" # Property documentation details
|
||||
heroes: "Herois" # Tooltip on hero shop button from /play
|
||||
achievements: "Triomfs" # Tooltip on achievement list button from /play
|
||||
|
@ -67,13 +67,13 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
|||
next: "Següent" # Go from choose hero to choose inventory before playing a level
|
||||
change_hero: "Canviar heroi" # Go back from choose inventory to choose hero
|
||||
choose_inventory: "Equipar objectes"
|
||||
# buy_gems: "Buy Gems"
|
||||
# campaign_desert: "Desert Campaign"
|
||||
# campaign_forest: "Forest Campaign"
|
||||
buy_gems: "Compra Gemes"
|
||||
campaign_desert: "Campanya del desert"
|
||||
campaign_forest: "Campanya del bosc"
|
||||
# campaign_dungeon: "Dungeon Campaign"
|
||||
# subscription_required: "Subscription Required"
|
||||
# free: "Free"
|
||||
# subscribed: "Subscribed"
|
||||
subscription_required: "Subscripció requerida"
|
||||
free: "Gratuit"
|
||||
subscribed: "Subscrit"
|
||||
older_campaigns: "Campanyes antigues"
|
||||
anonymous: "Jugador anònim"
|
||||
level_difficulty: "Dificultat: "
|
||||
|
@ -105,10 +105,10 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
|||
# authenticate_gplus: "Authenticate G+"
|
||||
# load_profile: "Load G+ Profile"
|
||||
# load_email: "Load G+ Email"
|
||||
# finishing: "Finishing"
|
||||
finishing: "Acabant"
|
||||
# sign_in_with_facebook: "Sign in with Facebook"
|
||||
# sign_in_with_gplus: "Sign in with G+"
|
||||
# signup_switch: "Want to create an account?"
|
||||
signup_switch: "Vols crear un compte?"
|
||||
|
||||
signup:
|
||||
email_announcements: "Rebre anuncis via email"
|
||||
|
@ -117,7 +117,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
|||
log_in: "Iniciar sessió amb la teva contrasenya"
|
||||
social_signup: "O, pots iniciar sesió desde Facebook o G+:"
|
||||
required: "Neccesites iniciar sesió abans ."
|
||||
# login_switch: "Already have an account?"
|
||||
login_switch: "Ja tens compte?"
|
||||
|
||||
recover:
|
||||
recover_account_title: "Recuperar Compte"
|
||||
|
@ -125,12 +125,12 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
|||
recovery_sent: "Correu de recuperació de contrasenya enviat."
|
||||
|
||||
items:
|
||||
# primary: "Primary"
|
||||
# secondary: "Secondary"
|
||||
primary: "Primari"
|
||||
secondary: "Secondari"
|
||||
armor: "Armadura"
|
||||
accessories: "Accessoris"
|
||||
# misc: "Misc"
|
||||
# books: "Books"
|
||||
books: "Llibres"
|
||||
|
||||
common:
|
||||
loading: "Carregant..."
|
||||
|
@ -145,22 +145,22 @@ 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"
|
||||
# actions: "Actions"
|
||||
# info: "Info"
|
||||
# help: "Help"
|
||||
actions: "Accions"
|
||||
info: "Info"
|
||||
help: "Ajuda"
|
||||
watch: "Veure"
|
||||
unwatch: "Amaga"
|
||||
submit_patch: "Enviar pegat"
|
||||
# submit_changes: "Submit Changes"
|
||||
|
||||
general:
|
||||
# and: "and"
|
||||
and: "i"
|
||||
name: "Nom"
|
||||
date: "Data"
|
||||
body: "Cos"
|
||||
version: "Versió"
|
||||
# pending: "Pending"
|
||||
# accepted: "Accepted"
|
||||
pending: "Pendent"
|
||||
accepted: "Acceptat"
|
||||
# rejected: "Rejected"
|
||||
# withdrawn: "Withdrawn"
|
||||
# submitter: "Submitter"
|
||||
|
@ -170,10 +170,10 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
|||
version_history: "Historial de versions"
|
||||
# version_history_for: "Version History for: "
|
||||
# select_changes: "Select two changes below to see the difference."
|
||||
# undo_prefix: "Undo"
|
||||
# undo_shortcut: "(Ctrl+Z)"
|
||||
undo_prefix: "Desfer"
|
||||
undo_shortcut: "(Ctrl+Z)"
|
||||
# redo_prefix: "Redo"
|
||||
# redo_shortcut: "(Ctrl+Shift+Z)"
|
||||
redo_shortcut: "(Ctrl+Shift+Z)"
|
||||
# play_preview: "Play preview of current level"
|
||||
result: "Resultat"
|
||||
results: "Resultats"
|
||||
|
@ -196,8 +196,8 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
|||
medium: "Intermedi"
|
||||
hard: "Difícil"
|
||||
player: "Jugador"
|
||||
# player_level: "Level" # Like player level 5, not like level: Dungeons of Kithgard
|
||||
# warrior: "Warrior"
|
||||
player_level: "Nivell" # Like player level 5, not like level: Dungeons of Kithgard
|
||||
warrior: "Guerrer"
|
||||
# ranger: "Ranger"
|
||||
# wizard: "Wizard"
|
||||
|
||||
|
@ -220,7 +220,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
|||
play_level:
|
||||
done: "Fet"
|
||||
home: "Inici" # Not used any more, will be removed soon.
|
||||
# level: "Level" # Like "Level: Dungeons of Kithgard"
|
||||
level: "Nivell" # Like "Level: Dungeons of Kithgard"
|
||||
skip: "Ometre"
|
||||
game_menu: "Menu de joc"
|
||||
guide: "Guia"
|
||||
|
@ -234,13 +234,13 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
|||
failing: "Fallant"
|
||||
action_timeline: "Cronologia d'accions"
|
||||
# click_to_select: "Click on a unit to select it."
|
||||
# control_bar_multiplayer: "Multiplayer"
|
||||
control_bar_multiplayer: "Multijugador"
|
||||
# control_bar_join_game: "Join Game"
|
||||
# reload: "Reload"
|
||||
# reload_title: "Reload All Code?"
|
||||
# reload_really: "Are you sure you want to reload this level back to the beginning?"
|
||||
# reload_confirm: "Reload All"
|
||||
# victory: "Victory"
|
||||
victory: "Victòria"
|
||||
victory_title_prefix: ""
|
||||
victory_title_suffix: " Complet"
|
||||
victory_sign_up: "Inicia sessió per a desar el progressos"
|
||||
|
@ -254,7 +254,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
|||
# victory_hour_of_code_done: "Are You Done?"
|
||||
# victory_hour_of_code_done_yes: "Yes, I'm finished with my Hour of Code™!"
|
||||
# victory_experience_gained: "XP Gained"
|
||||
# victory_gems_gained: "Gems Gained"
|
||||
victory_gems_gained: "Gemmes guanyades"
|
||||
guide_title: "Guia"
|
||||
# tome_minion_spells: "Your Minions' Spells" # Only in old-style levels.
|
||||
# tome_read_only_spells: "Read-Only Spells" # Only in old-style levels.
|
||||
|
@ -269,16 +269,16 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
|||
# tome_select_a_thang: "Select Someone for "
|
||||
# tome_available_spells: "Available Spells"
|
||||
tome_your_skills: "Les teves habilitats"
|
||||
# tome_help: "Help"
|
||||
tome_help: "Ajuda"
|
||||
# tome_current_method: "Current Method"
|
||||
# hud_continue_short: "Continue"
|
||||
hud_continue_short: "Continua"
|
||||
# code_saved: "Code Saved"
|
||||
# skip_tutorial: "Skip (esc)"
|
||||
keyboard_shortcuts: "Dreceres del teclat"
|
||||
loading_ready: "Preparat!"
|
||||
loading_start: "Comença el nivell"
|
||||
# problem_alert_title: "Fix Your Code"
|
||||
# problem_alert_help: "Help"
|
||||
problem_alert_help: "Ajuda"
|
||||
time_current: "Ara:"
|
||||
time_total: "Maxim:"
|
||||
time_goto: "Ves a:"
|
||||
|
@ -322,7 +322,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
|||
save_load_tab: "Desa/Carrega"
|
||||
options_tab: "Opcions"
|
||||
guide_tab: "Gui"
|
||||
# guide_video_tutorial: "Video Tutorial"
|
||||
guide_video_tutorial: "Video Tutorial"
|
||||
# guide_tips: "Tips"
|
||||
multiplayer_tab: "Multijugador"
|
||||
# auth_tab: "Sign Up"
|
||||
|
@ -336,31 +336,31 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
|||
|
||||
inventory:
|
||||
choose_inventory: "Equipar objectes"
|
||||
# equipped_item: "Equipped"
|
||||
equipped_item: "Equipat"
|
||||
# required_purchase_title: "Required"
|
||||
# available_item: "Available"
|
||||
available_item: "Disponible"
|
||||
# restricted_title: "Restricted"
|
||||
# should_equip: "(double-click to equip)"
|
||||
# equipped: "(equipped)"
|
||||
equipped: "(equipat)"
|
||||
# locked: "(locked)"
|
||||
# restricted: "(restricted in this level)"
|
||||
# equip: "Equip"
|
||||
# unequip: "Unequip"
|
||||
equip: "Equipa"
|
||||
unequip: "Desequipa"
|
||||
|
||||
# buy_gems:
|
||||
# few_gems: "A few gems"
|
||||
buy_gems:
|
||||
few_gems: "Algunes gemmes"
|
||||
# pile_gems: "Pile of gems"
|
||||
# chest_gems: "Chest of gems"
|
||||
# purchasing: "Purchasing..."
|
||||
chest_gems: "Cofre de gemmes"
|
||||
purchasing: "Comprant..."
|
||||
# declined: "Your card was declined"
|
||||
# retrying: "Server error, retrying."
|
||||
# prompt_title: "Not Enough Gems"
|
||||
# prompt_body: "Do you want to get more?"
|
||||
# prompt_button: "Enter Shop"
|
||||
prompt_button: "Entrar a la botiga"
|
||||
# recovered: "Previous gems purchase recovered. Please refresh the page."
|
||||
|
||||
# subscribe:
|
||||
# subscribe_title: "Subscribe"
|
||||
subscribe:
|
||||
subscribe_title: "Subscriu-te"
|
||||
# unsubscribe: "Unsubscribe"
|
||||
# levels: "Get more practice with bonus levels!"
|
||||
# heroes: "More powerful heroes!"
|
||||
|
@ -389,14 +389,14 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
|||
io_blurb: "Senzill però obscur."
|
||||
status: "Estat"
|
||||
weapons: "Armes"
|
||||
# weapons_warrior: "Swords - Short Range, No Magic"
|
||||
weapons_warrior: "Espases - Curt abast, No Magic"
|
||||
# weapons_ranger: "Crossbows, Guns - Long Range, No Magic"
|
||||
# weapons_wizard: "Wands, Staffs - Long Range, Magic"
|
||||
attack: "Dany" # Can also translate as "Attack"
|
||||
health: "Salut"
|
||||
speed: "Velocitat"
|
||||
# regeneration: "Regeneration"
|
||||
# range: "Range" # As in "attack or visual range"
|
||||
regeneration: "Regeneració"
|
||||
range: "Abast" # As in "attack or visual range"
|
||||
# blocks: "Blocks" # As in "this shield blocks this much damage"
|
||||
# backstab: "Backstab" # As in "this dagger does this much backstab damage"
|
||||
skills: "Habilitats"
|
||||
|
@ -404,21 +404,21 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
|||
# level_to_unlock: "Level to unlock:" # Label for which level you have to beat to unlock a particular hero (click a locked hero in the store to see)
|
||||
# restricted_to_certain_heroes: "Only certain heroes can play this level."
|
||||
|
||||
# skill_docs:
|
||||
skill_docs:
|
||||
# writable: "writable" # Hover over "attack" in Your Skills while playing a level to see most of this
|
||||
# read_only: "read-only"
|
||||
# action_name: "name"
|
||||
read_only: "Només lectura"
|
||||
action_name: "nom"
|
||||
# action_cooldown: "Takes"
|
||||
# action_specific_cooldown: "Cooldown"
|
||||
# action_damage: "Damage"
|
||||
# action_range: "Range"
|
||||
action_range: "Abast"
|
||||
# action_radius: "Radius"
|
||||
# action_duration: "Duration"
|
||||
# example: "Example"
|
||||
action_duration: "Duracció"
|
||||
example: "Exemple"
|
||||
# ex: "ex" # Abbreviation of "example"
|
||||
# current_value: "Current Value"
|
||||
# default_value: "Default value"
|
||||
# parameters: "Parameters"
|
||||
parameters: "Paràmetres"
|
||||
# returns: "Returns"
|
||||
# granted_by: "Granted by"
|
||||
|
||||
|
@ -608,15 +608,19 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
|||
# revert: "Revert"
|
||||
# revert_models: "Revert Models"
|
||||
# pick_a_terrain: "Pick A Terrain"
|
||||
small: "Petit"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
# grassy: "Grassy"
|
||||
small: "Petit"
|
||||
# large: "Large"
|
||||
# fork_title: "Fork New Version"
|
||||
# fork_creating: "Creating Fork..."
|
||||
# generate_terrain: "Generate Terrain"
|
||||
generate_terrain: "Generar Terreny"
|
||||
more: "Més"
|
||||
wiki: "Wiki"
|
||||
live_chat: "Xat en directe"
|
||||
# thang_main: "Main"
|
||||
thang_main: "Principal"
|
||||
# thang_spritesheets: "Spritesheets"
|
||||
# thang_colors: "Colors"
|
||||
# level_some_options: "Some Options?"
|
||||
|
@ -630,9 +634,11 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
|||
level_tab_thangs_all: "Tot"
|
||||
# level_tab_thangs_conditions: "Starting Conditions"
|
||||
# level_tab_thangs_add: "Add Thangs"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
delete: "Esborrar"
|
||||
duplicate: "Duplicar"
|
||||
# rotate: "Rotate"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
rotate: "Rotar"
|
||||
level_settings_title: "Configuració"
|
||||
level_component_tab_title: "Components actuals"
|
||||
# level_component_btn_new: "Create New Component"
|
||||
|
@ -644,7 +650,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
|||
# level_component_edit_title: "Edit Component"
|
||||
# level_component_config_schema: "Config Schema"
|
||||
level_component_settings: "Configuració"
|
||||
# level_system_edit_title: "Edit System"
|
||||
level_system_edit_title: "Editar sistema"
|
||||
create_system_title: "Crea un nou sistema"
|
||||
# new_component_title: "Create New Component"
|
||||
new_component_field_system: "Sistema"
|
||||
|
@ -815,7 +821,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
|||
achievement: "Triomf"
|
||||
category_contributor: "Contribuidor"
|
||||
# category_ladder: "Ladder"
|
||||
# category_level: "Level"
|
||||
category_level: "Nivell"
|
||||
category_miscellaneous: "Miscel·lània"
|
||||
category_levels: "Nivells"
|
||||
category_undefined: "Sense categoria"
|
||||
|
@ -830,21 +836,21 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
|||
account:
|
||||
recently_played: "Ultimanent jugat"
|
||||
no_recent_games: "No s'ha jugat en les ultimes setmanes."
|
||||
# payments: "Payments"
|
||||
payments: "Pagaments"
|
||||
# purchased: "Purchased"
|
||||
# subscription: "Subscription"
|
||||
# service_apple: "Apple"
|
||||
# service_web: "Web"
|
||||
# paid_on: "Paid On"
|
||||
service_apple: "Apple"
|
||||
service_web: "Web"
|
||||
paid_on: "Pagat a"
|
||||
# service: "Service"
|
||||
# price: "Price"
|
||||
price: "Preu"
|
||||
# gems: "Gems"
|
||||
# active: "Active"
|
||||
active: "Actiu"
|
||||
# subscribed: "Subscribed"
|
||||
# unsubscribed: "Unsubscribed"
|
||||
# active_until: "Active Until"
|
||||
# cost: "Cost"
|
||||
# next_payment: "Next Payment"
|
||||
cost: "Cost"
|
||||
next_payment: "Següent pagament"
|
||||
# card: "Card"
|
||||
# status_unsubscribed_active: "You're not subscribed and won't be billed, but your account is still active for now."
|
||||
# status_unsubscribed: "Get access to new levels, heroes, items, and bonus gems with a CodeCombat subscription!"
|
||||
|
@ -894,7 +900,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
|||
# source_document: "Source Document"
|
||||
document: "Documents"
|
||||
# sprite_sheet: "Sprite Sheet"
|
||||
# employers: "Employers"
|
||||
employers: "Treballadors"
|
||||
candidates: "Candidats"
|
||||
# candidate_sessions: "Candidate Sessions"
|
||||
# user_remark: "User Remark"
|
||||
|
@ -1114,7 +1120,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
|||
# deprecation_warning_title: "Sorry, CodeCombat is not recruiting right now."
|
||||
# deprecation_warning: "We are focusing on beginner levels instead of finding expert developers for the time being."
|
||||
# hire_developers_not_credentials: "Hire developers, not credentials." # We are not actively recruiting right now, so there's no need to add new translations for the rest of this section.
|
||||
# get_started: "Get Started"
|
||||
get_started: "Comença"
|
||||
# already_screened: "We've already technically screened all our candidates"
|
||||
# filter_further: ", but you can also filter further:"
|
||||
# filter_visa: "Visa"
|
||||
|
@ -1151,7 +1157,7 @@ module.exports = nativeDescription: "Català", englishDescription: "Catalan", tr
|
|||
# candidate_top_skills: "Top Skills"
|
||||
# candidate_years_experience: "Yrs Exp"
|
||||
# candidate_last_updated: "Last Updated"
|
||||
# candidate_who: "Who"
|
||||
candidate_who: "Qui"
|
||||
# featured_developers: "Featured Developers"
|
||||
other_developers: "Altres desenvolupadors"
|
||||
# inactive_developers: "Inactive Developers"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "čeština", englishDescription: "Czech", tr
|
|||
revert: "Vrátit"
|
||||
revert_models: "Vrátit modely"
|
||||
pick_a_terrain: "Vybrat terén"
|
||||
small: "Malý"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
grassy: "Travnatý"
|
||||
small: "Malý"
|
||||
# large: "Large"
|
||||
fork_title: "Forkovat novou verzi"
|
||||
fork_creating: "Vytváření Forku..."
|
||||
generate_terrain: "Generování terénu"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "čeština", englishDescription: "Czech", tr
|
|||
level_tab_thangs_all: "Všechny"
|
||||
level_tab_thangs_conditions: "Výchozí prostředí"
|
||||
level_tab_thangs_add: "Přidat Thangy"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
delete: "Smazat"
|
||||
duplicate: "Duplikovat"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
rotate: "Otočit"
|
||||
level_settings_title: "Nastavení"
|
||||
level_component_tab_title: "Současné komponenty"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans
|
|||
# revert: "Revert"
|
||||
# revert_models: "Revert Models"
|
||||
# pick_a_terrain: "Pick A Terrain"
|
||||
# small: "Small"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
# grassy: "Grassy"
|
||||
# small: "Small"
|
||||
# large: "Large"
|
||||
# fork_title: "Fork New Version"
|
||||
# fork_creating: "Creating Fork..."
|
||||
# generate_terrain: "Generate Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "dansk", englishDescription: "Danish", trans
|
|||
# level_tab_thangs_all: "All"
|
||||
level_tab_thangs_conditions: "Startbetingelser"
|
||||
# level_tab_thangs_add: "Add Thangs"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
# delete: "Delete"
|
||||
# duplicate: "Duplicate"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
# rotate: "Rotate"
|
||||
level_settings_title: "Instillinger"
|
||||
level_component_tab_title: "Nuværende komponenter"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription:
|
|||
revert: "Zurücksetzen"
|
||||
revert_models: "Models zurücksetzen."
|
||||
pick_a_terrain: "Wähle ein Terrain"
|
||||
small: "Klein"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
grassy: "Grasig"
|
||||
small: "Klein"
|
||||
# large: "Large"
|
||||
fork_title: "Forke neue Version"
|
||||
fork_creating: "Erzeuge Fork..."
|
||||
generate_terrain: "Generiere Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "Deutsch (Österreich)", englishDescription:
|
|||
level_tab_thangs_all: "Alle"
|
||||
level_tab_thangs_conditions: "Startbedingungen"
|
||||
level_tab_thangs_add: "Thangs hinzufügen"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
delete: "Löschen"
|
||||
duplicate: "Duplizieren"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
# rotate: "Rotate"
|
||||
level_settings_title: "Einstellungen"
|
||||
level_component_tab_title: "Aktuelle Komponenten"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge
|
|||
# revert: "Revert"
|
||||
# revert_models: "Revert Models"
|
||||
# pick_a_terrain: "Pick A Terrain"
|
||||
# small: "Small"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
# grassy: "Grassy"
|
||||
# small: "Small"
|
||||
# large: "Large"
|
||||
# fork_title: "Fork New Version"
|
||||
# fork_creating: "Creating Fork..."
|
||||
# generate_terrain: "Generate Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "Deutsch (Schweiz)", englishDescription: "Ge
|
|||
# level_tab_thangs_all: "All"
|
||||
# level_tab_thangs_conditions: "Starting Conditions"
|
||||
# level_tab_thangs_add: "Add Thangs"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
# delete: "Delete"
|
||||
# duplicate: "Duplicate"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
# rotate: "Rotate"
|
||||
# level_settings_title: "Settings"
|
||||
# level_component_tab_title: "Current Components"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
|
|||
revert: "Zurücksetzen"
|
||||
revert_models: "Modelle zurücksetzen."
|
||||
pick_a_terrain: "Wähle ein Terrain"
|
||||
small: "Klein"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
grassy: "Grasig"
|
||||
small: "Klein"
|
||||
# large: "Large"
|
||||
fork_title: "Forke neue Version"
|
||||
fork_creating: "Erzeuge Fork..."
|
||||
generate_terrain: "Generiere Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "Deutsch (Deutschland)", englishDescription:
|
|||
level_tab_thangs_all: "Alle"
|
||||
level_tab_thangs_conditions: "Startbedingungen"
|
||||
level_tab_thangs_add: "Thangs hinzufügen"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
delete: "Löschen"
|
||||
duplicate: "Duplizieren"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
rotate: "Drehen"
|
||||
level_settings_title: "Einstellungen"
|
||||
level_component_tab_title: "Aktuelle Komponenten"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre
|
|||
# revert: "Revert"
|
||||
# revert_models: "Revert Models"
|
||||
# pick_a_terrain: "Pick A Terrain"
|
||||
# small: "Small"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
# grassy: "Grassy"
|
||||
# small: "Small"
|
||||
# large: "Large"
|
||||
# fork_title: "Fork New Version"
|
||||
# fork_creating: "Creating Fork..."
|
||||
# generate_terrain: "Generate Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "Ελληνικά", englishDescription: "Gre
|
|||
# level_tab_thangs_all: "All"
|
||||
# level_tab_thangs_conditions: "Starting Conditions"
|
||||
# level_tab_thangs_add: "Add Thangs"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
# delete: "Delete"
|
||||
# duplicate: "Duplicate"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
# rotate: "Rotate"
|
||||
# level_settings_title: "Settings"
|
||||
# level_component_tab_title: "Current Components"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English
|
|||
# revert: "Revert"
|
||||
# revert_models: "Revert Models"
|
||||
# pick_a_terrain: "Pick A Terrain"
|
||||
# small: "Small"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
# grassy: "Grassy"
|
||||
# small: "Small"
|
||||
# large: "Large"
|
||||
# fork_title: "Fork New Version"
|
||||
# fork_creating: "Creating Fork..."
|
||||
# generate_terrain: "Generate Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "English (AU)", englishDescription: "English
|
|||
# level_tab_thangs_all: "All"
|
||||
# level_tab_thangs_conditions: "Starting Conditions"
|
||||
# level_tab_thangs_add: "Add Thangs"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
# delete: "Delete"
|
||||
# duplicate: "Duplicate"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
# rotate: "Rotate"
|
||||
# level_settings_title: "Settings"
|
||||
# level_component_tab_title: "Current Components"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English
|
|||
# revert: "Revert"
|
||||
# revert_models: "Revert Models"
|
||||
# pick_a_terrain: "Pick A Terrain"
|
||||
# small: "Small"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
# grassy: "Grassy"
|
||||
# small: "Small"
|
||||
# large: "Large"
|
||||
# fork_title: "Fork New Version"
|
||||
# fork_creating: "Creating Fork..."
|
||||
# generate_terrain: "Generate Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "English (UK)", englishDescription: "English
|
|||
# level_tab_thangs_all: "All"
|
||||
# level_tab_thangs_conditions: "Starting Conditions"
|
||||
# level_tab_thangs_add: "Add Thangs"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
# delete: "Delete"
|
||||
# duplicate: "Duplicate"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
# rotate: "Rotate"
|
||||
# level_settings_title: "Settings"
|
||||
# level_component_tab_title: "Current Components"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English
|
|||
# revert: "Revert"
|
||||
# revert_models: "Revert Models"
|
||||
# pick_a_terrain: "Pick A Terrain"
|
||||
# small: "Small"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
# grassy: "Grassy"
|
||||
# small: "Small"
|
||||
# large: "Large"
|
||||
# fork_title: "Fork New Version"
|
||||
# fork_creating: "Creating Fork..."
|
||||
# generate_terrain: "Generate Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "English (US)", englishDescription: "English
|
|||
# level_tab_thangs_all: "All"
|
||||
# level_tab_thangs_conditions: "Starting Conditions"
|
||||
# level_tab_thangs_add: "Add Thangs"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
# delete: "Delete"
|
||||
# duplicate: "Duplicate"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
# rotate: "Rotate"
|
||||
# level_settings_title: "Settings"
|
||||
# level_component_tab_title: "Current Components"
|
||||
|
|
|
@ -608,8 +608,12 @@
|
|||
revert: "Revert"
|
||||
revert_models: "Revert Models"
|
||||
pick_a_terrain: "Pick A Terrain"
|
||||
small: "Small"
|
||||
dungeon: "Dungeon"
|
||||
indoor: "Indoor"
|
||||
desert: "Desert"
|
||||
grassy: "Grassy"
|
||||
small: "Small"
|
||||
large: "Large"
|
||||
fork_title: "Fork New Version"
|
||||
fork_creating: "Creating Fork..."
|
||||
generate_terrain: "Generate Terrain"
|
||||
|
@ -630,8 +634,10 @@
|
|||
level_tab_thangs_all: "All"
|
||||
level_tab_thangs_conditions: "Starting Conditions"
|
||||
level_tab_thangs_add: "Add Thangs"
|
||||
config_thang: "Double click to configure a thang"
|
||||
delete: "Delete"
|
||||
duplicate: "Duplicate"
|
||||
stop_duplicate: "Stop Duplicate"
|
||||
rotate: "Rotate"
|
||||
level_settings_title: "Settings"
|
||||
level_component_tab_title: "Current Components"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "Español (América Latina)", englishDescrip
|
|||
revert: "Revertir"
|
||||
revert_models: "Revertir Modelos"
|
||||
pick_a_terrain: "Elije un Terreno"
|
||||
small: "Pequeño"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
grassy: "Herboso"
|
||||
small: "Pequeño"
|
||||
# large: "Large"
|
||||
fork_title: "Fork de Nueva Versión"
|
||||
fork_creating: "Creando Fork..."
|
||||
generate_terrain: "Generar terreno"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "Español (América Latina)", englishDescrip
|
|||
level_tab_thangs_all: "Todo"
|
||||
level_tab_thangs_conditions: "Condiciones Iniciales"
|
||||
level_tab_thangs_add: "Agregar Thangs"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
delete: "Borrar"
|
||||
duplicate: "Duplicar"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
rotate: "Rotar"
|
||||
level_settings_title: "Opciones"
|
||||
level_component_tab_title: "Componentes Actuales"
|
||||
|
|
|
@ -159,9 +159,9 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis
|
|||
date: "Fecha"
|
||||
body: "Cuerpo"
|
||||
version: "Versión"
|
||||
# pending: "Pending"
|
||||
# accepted: "Accepted"
|
||||
# rejected: "Rejected"
|
||||
pending: "Pendiente"
|
||||
accepted: "Aceptado"
|
||||
rejected: "Rechazado"
|
||||
# withdrawn: "Withdrawn"
|
||||
# submitter: "Submitter"
|
||||
# submitted: "Submitted"
|
||||
|
@ -174,7 +174,7 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis
|
|||
# undo_shortcut: "(Ctrl+Z)"
|
||||
redo_prefix: "Rehacer"
|
||||
# redo_shortcut: "(Ctrl+Shift+Z)"
|
||||
# play_preview: "Play preview of current level"
|
||||
play_preview: "Reproducir una vista previa del nivel actual"
|
||||
result: "Resultado"
|
||||
results: "Resultados"
|
||||
description: "Descripción"
|
||||
|
@ -199,7 +199,7 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis
|
|||
player_level: "Nivel" # Like player level 5, not like level: Dungeons of Kithgard
|
||||
# warrior: "Warrior"
|
||||
# ranger: "Ranger"
|
||||
# wizard: "Wizard"
|
||||
wizard: "Mago"
|
||||
|
||||
units:
|
||||
second: "segundo"
|
||||
|
@ -608,17 +608,21 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis
|
|||
revert: "Revertir"
|
||||
revert_models: "Revertir Modelos"
|
||||
pick_a_terrain: "Escoge un Terreno"
|
||||
small: "Pequeño"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
grassy: "Cubierto de hierba"
|
||||
small: "Pequeño"
|
||||
# large: "Large"
|
||||
fork_title: "Bifurcar nueva versión"
|
||||
fork_creating: "Creando bifurcación..."
|
||||
generate_terrain: "Generar Terreno"
|
||||
more: "Más"
|
||||
wiki: "Wiki"
|
||||
live_chat: "Chat en directo"
|
||||
# thang_main: "Main"
|
||||
thang_main: "Principal"
|
||||
# thang_spritesheets: "Spritesheets"
|
||||
# thang_colors: "Colors"
|
||||
thang_colors: "Colores"
|
||||
level_some_options: "¿Algunas opciones?"
|
||||
level_tab_thangs: "Objetos"
|
||||
level_tab_scripts: "Scripts"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis
|
|||
level_tab_thangs_all: "Todo"
|
||||
level_tab_thangs_conditions: "Condiciones de inicio"
|
||||
level_tab_thangs_add: "Añadir Objetos"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
delete: "Borrar"
|
||||
duplicate: "Duplicar"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
rotate: "Rotar"
|
||||
level_settings_title: "Ajustes"
|
||||
level_component_tab_title: "Componentes Actuales"
|
||||
|
@ -674,7 +680,7 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis
|
|||
|
||||
contribute:
|
||||
page_title: "Colaborar"
|
||||
# intro_blurb: "CodeCombat is 100% open source! Hundreds of dedicated players have helped us build the game into what it is today. Join us and write the next chapter in CodeCombat's quest to teach the world to code!"
|
||||
intro_blurb: "¡CodeCombat es 100% código abierto! Cientos de dedicados jugadores nos han ayudado a convertir el juego en lo que es hoy en día. !Únete a nosotros y escribe el siguiente capítulo de CodeCombat en la aventura de enseñar al mundo a programar!"
|
||||
alert_account_message_intro: "¡Hola!"
|
||||
alert_account_message: "Para suscribirse a los mails de clase, necesitas estar logeado."
|
||||
archmage_introduction: "Una de las mejores partes de desarrollar juegos es que combinan cosas muy diferentes. Gráficos, sonido, uso de redes en tiempo real, redes sociales y por supuesto mucho de los aspectos comunes de la programación, desde gestión de bases de datos a bajo nivel y administración de servidores hasta diseño de experiencia del usuario y creación de interfaces. Hay un montón de cosas por hacer y si eres un programador experimentado con interés en conocer lo que se cuece en la trastienda de CodeCombat, esta Clase puede ser la ideal para ti. Nos encantaría recibir tu ayuda para crear el mejor juego de programación de la historia."
|
||||
|
@ -844,8 +850,8 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis
|
|||
# unsubscribed: "Unsubscribed"
|
||||
# active_until: "Active Until"
|
||||
# cost: "Cost"
|
||||
# next_payment: "Next Payment"
|
||||
# card: "Card"
|
||||
next_payment: "Siguiente Pago"
|
||||
card: "Tarjeta"
|
||||
status_unsubscribed_active: "No estás suscrito y no seras facturado, pero tu cuenta sigue activa por ahora."
|
||||
status_unsubscribed: "¡Obten acceso a nuevos niveles, heroes, artículos, y joyas adicionales con una suscripción a CodeCombat!"
|
||||
|
||||
|
@ -906,7 +912,7 @@ module.exports = nativeDescription: "español (ES)", englishDescription: "Spanis
|
|||
clas: "Clasess"
|
||||
play_counts: "Contador de Juegos"
|
||||
feedback: "Apoyo"
|
||||
# payment_info: "Payment Info"
|
||||
payment_info: "Información de Pago"
|
||||
|
||||
delta:
|
||||
added: "Añadido"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian",
|
|||
# revert: "Revert"
|
||||
# revert_models: "Revert Models"
|
||||
# pick_a_terrain: "Pick A Terrain"
|
||||
# small: "Small"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
# grassy: "Grassy"
|
||||
# small: "Small"
|
||||
# large: "Large"
|
||||
# fork_title: "Fork New Version"
|
||||
# fork_creating: "Creating Fork..."
|
||||
# generate_terrain: "Generate Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "فارسی", englishDescription: "Persian",
|
|||
# level_tab_thangs_all: "All"
|
||||
# level_tab_thangs_conditions: "Starting Conditions"
|
||||
# level_tab_thangs_add: "Add Thangs"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
# delete: "Delete"
|
||||
# duplicate: "Duplicate"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
# rotate: "Rotate"
|
||||
# level_settings_title: "Settings"
|
||||
# level_component_tab_title: "Current Components"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "suomi", englishDescription: "Finnish", tran
|
|||
# revert: "Revert"
|
||||
# revert_models: "Revert Models"
|
||||
# pick_a_terrain: "Pick A Terrain"
|
||||
# small: "Small"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
# grassy: "Grassy"
|
||||
# small: "Small"
|
||||
# large: "Large"
|
||||
# fork_title: "Fork New Version"
|
||||
# fork_creating: "Creating Fork..."
|
||||
# generate_terrain: "Generate Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "suomi", englishDescription: "Finnish", tran
|
|||
# level_tab_thangs_all: "All"
|
||||
# level_tab_thangs_conditions: "Starting Conditions"
|
||||
# level_tab_thangs_add: "Add Thangs"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
# delete: "Delete"
|
||||
# duplicate: "Duplicate"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
# rotate: "Rotate"
|
||||
# level_settings_title: "Settings"
|
||||
# level_component_tab_title: "Current Components"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
|
|||
revert: "Annuler"
|
||||
revert_models: "Annuler les modèles"
|
||||
pick_a_terrain: "Choisir un terrain"
|
||||
small: "Petit"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
grassy: "Herbeux"
|
||||
small: "Petit"
|
||||
# large: "Large"
|
||||
fork_title: "Fork une nouvelle version"
|
||||
fork_creating: "Créer un Fork..."
|
||||
generate_terrain: "Générer le terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
|
|||
level_tab_thangs_all: "Tout"
|
||||
level_tab_thangs_conditions: "Conditions de départ"
|
||||
level_tab_thangs_add: "ajouter des Thangs"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
delete: "Supprimer"
|
||||
duplicate: "Dupliquer"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
rotate: "Pivoter"
|
||||
level_settings_title: "Paramètres"
|
||||
level_component_tab_title: "Composants actuels"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr
|
|||
revert: "Revertir"
|
||||
revert_models: "Revertir Modelos"
|
||||
pick_a_terrain: "Escolle un Terreno"
|
||||
small: "Pequeno"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
grassy: "Cuberto de herba"
|
||||
small: "Pequeno"
|
||||
# large: "Large"
|
||||
fork_title: "Bifurcar nova versión"
|
||||
fork_creating: "Creando bifurcación..."
|
||||
generate_terrain: "Xerar Terreo"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "Galego", englishDescription: "Galician", tr
|
|||
level_tab_thangs_all: "Todo"
|
||||
level_tab_thangs_conditions: "Condicións de inicio"
|
||||
level_tab_thangs_add: "Engadir Obxectos"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
delete: "Borrar"
|
||||
duplicate: "Duplicar"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
rotate: "Rotar"
|
||||
level_settings_title: "Axustes"
|
||||
level_component_tab_title: "Compoñentes Actuais"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "עברית", englishDescription: "Hebrew",
|
|||
# revert: "Revert"
|
||||
# revert_models: "Revert Models"
|
||||
# pick_a_terrain: "Pick A Terrain"
|
||||
# small: "Small"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
# grassy: "Grassy"
|
||||
# small: "Small"
|
||||
# large: "Large"
|
||||
# fork_title: "Fork New Version"
|
||||
# fork_creating: "Creating Fork..."
|
||||
# generate_terrain: "Generate Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "עברית", englishDescription: "Hebrew",
|
|||
# level_tab_thangs_all: "All"
|
||||
# level_tab_thangs_conditions: "Starting Conditions"
|
||||
# level_tab_thangs_add: "Add Thangs"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
# delete: "Delete"
|
||||
# duplicate: "Duplicate"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
# rotate: "Rotate"
|
||||
# level_settings_title: "Settings"
|
||||
# level_component_tab_title: "Current Components"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe
|
|||
# revert: "Revert"
|
||||
# revert_models: "Revert Models"
|
||||
# pick_a_terrain: "Pick A Terrain"
|
||||
# small: "Small"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
# grassy: "Grassy"
|
||||
# small: "Small"
|
||||
# large: "Large"
|
||||
# fork_title: "Fork New Version"
|
||||
# fork_creating: "Creating Fork..."
|
||||
# generate_terrain: "Generate Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "मानक हिन्दी", englishDe
|
|||
# level_tab_thangs_all: "All"
|
||||
# level_tab_thangs_conditions: "Starting Conditions"
|
||||
# level_tab_thangs_add: "Add Thangs"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
# delete: "Delete"
|
||||
# duplicate: "Duplicate"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
# rotate: "Rotate"
|
||||
# level_settings_title: "Settings"
|
||||
# level_component_tab_title: "Current Components"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t
|
|||
# revert: "Revert"
|
||||
# revert_models: "Revert Models"
|
||||
# pick_a_terrain: "Pick A Terrain"
|
||||
small: "Kicsi"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
grassy: "Füves"
|
||||
small: "Kicsi"
|
||||
# large: "Large"
|
||||
fork_title: "Új Verzió villára vétele"
|
||||
# fork_creating: "Creating Fork..."
|
||||
generate_terrain: "Terület generálása"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "magyar", englishDescription: "Hungarian", t
|
|||
# level_tab_thangs_all: "All"
|
||||
# level_tab_thangs_conditions: "Starting Conditions"
|
||||
# level_tab_thangs_add: "Add Thangs"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
# delete: "Delete"
|
||||
# duplicate: "Duplicate"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
# rotate: "Rotate"
|
||||
# level_settings_title: "Settings"
|
||||
# level_component_tab_title: "Current Components"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind
|
|||
# revert: "Revert"
|
||||
# revert_models: "Revert Models"
|
||||
# pick_a_terrain: "Pick A Terrain"
|
||||
# small: "Small"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
# grassy: "Grassy"
|
||||
# small: "Small"
|
||||
# large: "Large"
|
||||
# fork_title: "Fork New Version"
|
||||
# fork_creating: "Creating Fork..."
|
||||
# generate_terrain: "Generate Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "Bahasa Indonesia", englishDescription: "Ind
|
|||
# level_tab_thangs_all: "All"
|
||||
# level_tab_thangs_conditions: "Starting Conditions"
|
||||
# level_tab_thangs_add: "Add Thangs"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
# delete: "Delete"
|
||||
# duplicate: "Duplicate"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
# rotate: "Rotate"
|
||||
# level_settings_title: "Settings"
|
||||
# level_component_tab_title: "Current Components"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t
|
|||
# revert: "Revert"
|
||||
# revert_models: "Revert Models"
|
||||
# pick_a_terrain: "Pick A Terrain"
|
||||
# small: "Small"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
# grassy: "Grassy"
|
||||
# small: "Small"
|
||||
# large: "Large"
|
||||
# fork_title: "Fork New Version"
|
||||
# fork_creating: "Creating Fork..."
|
||||
# generate_terrain: "Generate Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "Italiano", englishDescription: "Italian", t
|
|||
level_tab_thangs_all: "Tutti"
|
||||
level_tab_thangs_conditions: "Condizioni iniziali"
|
||||
level_tab_thangs_add: "Aggiungi thang"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
delete: "Cancella"
|
||||
duplicate: "Duplica"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
rotate: "Ruota"
|
||||
level_settings_title: "Impostazioni"
|
||||
level_component_tab_title: "Componenti esistenti"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese",
|
|||
# revert: "Revert"
|
||||
# revert_models: "Revert Models"
|
||||
# pick_a_terrain: "Pick A Terrain"
|
||||
# small: "Small"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
# grassy: "Grassy"
|
||||
# small: "Small"
|
||||
# large: "Large"
|
||||
# fork_title: "Fork New Version"
|
||||
# fork_creating: "Creating Fork..."
|
||||
# generate_terrain: "Generate Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "日本語", englishDescription: "Japanese",
|
|||
# level_tab_thangs_all: "All"
|
||||
# level_tab_thangs_conditions: "Starting Conditions"
|
||||
# level_tab_thangs_add: "Add Thangs"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
# delete: "Delete"
|
||||
# duplicate: "Duplicate"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
# rotate: "Rotate"
|
||||
# level_settings_title: "Settings"
|
||||
# level_component_tab_title: "Current Components"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t
|
|||
revert: "되돌리기"
|
||||
revert_models: "모델 되돌리기"
|
||||
pick_a_terrain: "지형을 선택하세요."
|
||||
small: "작게"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
grassy: "풀로 덮인"
|
||||
small: "작게"
|
||||
# large: "Large"
|
||||
fork_title: "새 버전 가져오기"
|
||||
fork_creating: "포크 생성중..."
|
||||
# generate_terrain: "Generate Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "한국어", englishDescription: "Korean", t
|
|||
# level_tab_thangs_all: "All"
|
||||
level_tab_thangs_conditions: "컨디션 시작"
|
||||
level_tab_thangs_add: "Thangs 추가"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
delete: "삭제"
|
||||
# duplicate: "Duplicate"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
# rotate: "Rotate"
|
||||
level_settings_title: "설정"
|
||||
level_component_tab_title: "현재 요소들"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith
|
|||
# revert: "Revert"
|
||||
# revert_models: "Revert Models"
|
||||
# pick_a_terrain: "Pick A Terrain"
|
||||
# small: "Small"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
# grassy: "Grassy"
|
||||
# small: "Small"
|
||||
# large: "Large"
|
||||
# fork_title: "Fork New Version"
|
||||
# fork_creating: "Creating Fork..."
|
||||
# generate_terrain: "Generate Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "lietuvių kalba", englishDescription: "Lith
|
|||
# level_tab_thangs_all: "All"
|
||||
# level_tab_thangs_conditions: "Starting Conditions"
|
||||
# level_tab_thangs_add: "Add Thangs"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
# delete: "Delete"
|
||||
# duplicate: "Duplicate"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
# rotate: "Rotate"
|
||||
# level_settings_title: "Settings"
|
||||
# level_component_tab_title: "Current Components"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "Македонски", englishDescription:
|
|||
# revert: "Revert"
|
||||
# revert_models: "Revert Models"
|
||||
# pick_a_terrain: "Pick A Terrain"
|
||||
# small: "Small"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
# grassy: "Grassy"
|
||||
# small: "Small"
|
||||
# large: "Large"
|
||||
# fork_title: "Fork New Version"
|
||||
# fork_creating: "Creating Fork..."
|
||||
# generate_terrain: "Generate Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "Македонски", englishDescription:
|
|||
# level_tab_thangs_all: "All"
|
||||
# level_tab_thangs_conditions: "Starting Conditions"
|
||||
# level_tab_thangs_add: "Add Thangs"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
# delete: "Delete"
|
||||
# duplicate: "Duplicate"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
# rotate: "Rotate"
|
||||
# level_settings_title: "Settings"
|
||||
# level_component_tab_title: "Current Components"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa
|
|||
# revert: "Revert"
|
||||
# revert_models: "Revert Models"
|
||||
# pick_a_terrain: "Pick A Terrain"
|
||||
# small: "Small"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
# grassy: "Grassy"
|
||||
# small: "Small"
|
||||
# large: "Large"
|
||||
# fork_title: "Fork New Version"
|
||||
# fork_creating: "Creating Fork..."
|
||||
# generate_terrain: "Generate Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "Bahasa Melayu", englishDescription: "Bahasa
|
|||
# level_tab_thangs_all: "All"
|
||||
# level_tab_thangs_conditions: "Starting Conditions"
|
||||
# level_tab_thangs_add: "Add Thangs"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
# delete: "Delete"
|
||||
# duplicate: "Duplicate"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
# rotate: "Rotate"
|
||||
# level_settings_title: "Settings"
|
||||
# level_component_tab_title: "Current Components"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "Norsk Bokmål", englishDescription: "Norweg
|
|||
revert: "Tilbakestill"
|
||||
revert_models: "Tilbakestill Modeller"
|
||||
pick_a_terrain: "Velg Terreng"
|
||||
# small: "Small"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
# grassy: "Grassy"
|
||||
# small: "Small"
|
||||
# large: "Large"
|
||||
# fork_title: "Fork New Version"
|
||||
# fork_creating: "Creating Fork..."
|
||||
generate_terrain: "Generer Terreng"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "Norsk Bokmål", englishDescription: "Norweg
|
|||
level_tab_thangs_all: "Alle"
|
||||
# level_tab_thangs_conditions: "Starting Conditions"
|
||||
level_tab_thangs_add: "Legg til Thangs"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
delete: "Slett"
|
||||
duplicate: "Kopier"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
rotate: "Rotér"
|
||||
level_settings_title: "Innstillinger"
|
||||
# level_component_tab_title: "Current Components"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription:
|
|||
revert: "Keer wijziging terug"
|
||||
revert_models: "keer wijziging model terug"
|
||||
# pick_a_terrain: "Pick A Terrain"
|
||||
# small: "Small"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
# grassy: "Grassy"
|
||||
# small: "Small"
|
||||
# large: "Large"
|
||||
fork_title: "Kloon naar nieuwe versie"
|
||||
fork_creating: "Kloon aanmaken..."
|
||||
# generate_terrain: "Generate Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "Nederlands (België)", englishDescription:
|
|||
level_tab_thangs_all: "Alles"
|
||||
level_tab_thangs_conditions: "Start Condities"
|
||||
level_tab_thangs_add: "Voeg element toe"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
delete: "Verwijder"
|
||||
duplicate: "Dupliceer"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
# rotate: "Rotate"
|
||||
level_settings_title: "Instellingen"
|
||||
level_component_tab_title: "Huidige Componenten"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription
|
|||
revert: "Keer wijziging terug"
|
||||
revert_models: "keer wijziging model terug"
|
||||
# pick_a_terrain: "Pick A Terrain"
|
||||
# small: "Small"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
# grassy: "Grassy"
|
||||
# small: "Small"
|
||||
# large: "Large"
|
||||
fork_title: "Kloon naar nieuwe versie"
|
||||
fork_creating: "Kloon aanmaken..."
|
||||
# generate_terrain: "Generate Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "Nederlands (Nederland)", englishDescription
|
|||
level_tab_thangs_all: "Alles"
|
||||
level_tab_thangs_conditions: "Start Condities"
|
||||
level_tab_thangs_add: "Voeg element toe"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
delete: "Verwijder"
|
||||
duplicate: "Dupliceer"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
# rotate: "Rotate"
|
||||
level_settings_title: "Instellingen"
|
||||
level_component_tab_title: "Huidige Componenten"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No
|
|||
# revert: "Revert"
|
||||
# revert_models: "Revert Models"
|
||||
# pick_a_terrain: "Pick A Terrain"
|
||||
# small: "Small"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
# grassy: "Grassy"
|
||||
# small: "Small"
|
||||
# large: "Large"
|
||||
# fork_title: "Fork New Version"
|
||||
# fork_creating: "Creating Fork..."
|
||||
# generate_terrain: "Generate Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "Norwegian Nynorsk", englishDescription: "No
|
|||
# level_tab_thangs_all: "All"
|
||||
# level_tab_thangs_conditions: "Starting Conditions"
|
||||
# level_tab_thangs_add: "Add Thangs"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
# delete: "Delete"
|
||||
# duplicate: "Duplicate"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
# rotate: "Rotate"
|
||||
# level_settings_title: "Settings"
|
||||
# level_component_tab_title: "Current Components"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr
|
|||
# revert: "Revert"
|
||||
# revert_models: "Revert Models"
|
||||
# pick_a_terrain: "Pick A Terrain"
|
||||
# small: "Small"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
# grassy: "Grassy"
|
||||
# small: "Small"
|
||||
# large: "Large"
|
||||
# fork_title: "Fork New Version"
|
||||
# fork_creating: "Creating Fork..."
|
||||
# generate_terrain: "Generate Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "Norsk", englishDescription: "Norwegian", tr
|
|||
# level_tab_thangs_all: "All"
|
||||
# level_tab_thangs_conditions: "Starting Conditions"
|
||||
# level_tab_thangs_add: "Add Thangs"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
# delete: "Delete"
|
||||
# duplicate: "Duplicate"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
# rotate: "Rotate"
|
||||
# level_settings_title: "Settings"
|
||||
# level_component_tab_title: "Current Components"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish
|
|||
revert: "Przywróć"
|
||||
revert_models: "Przywróć wersję"
|
||||
# pick_a_terrain: "Pick A Terrain"
|
||||
# small: "Small"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
# grassy: "Grassy"
|
||||
# small: "Small"
|
||||
# large: "Large"
|
||||
# fork_title: "Fork New Version"
|
||||
# fork_creating: "Creating Fork..."
|
||||
# generate_terrain: "Generate Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "język polski", englishDescription: "Polish
|
|||
# level_tab_thangs_all: "All"
|
||||
level_tab_thangs_conditions: "Warunki początkowe"
|
||||
level_tab_thangs_add: "Dodaj obiekty"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
# delete: "Delete"
|
||||
# duplicate: "Duplicate"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
# rotate: "Rotate"
|
||||
level_settings_title: "Ustawienia"
|
||||
level_component_tab_title: "Aktualne komponenty"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "Português do Brasil", englishDescription:
|
|||
revert: "Reverter"
|
||||
revert_models: "Reverter Modelos"
|
||||
pick_a_terrain: "Escolha um Terreno"
|
||||
small: "Pequeno"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
grassy: "Gramado"
|
||||
small: "Pequeno"
|
||||
# large: "Large"
|
||||
fork_title: "Realizar um Novo Fork"
|
||||
fork_creating: "Criando Fork..."
|
||||
generate_terrain: "Gerando Terreno"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "Português do Brasil", englishDescription:
|
|||
level_tab_thangs_all: "Tudo"
|
||||
level_tab_thangs_conditions: "Condições de Início"
|
||||
level_tab_thangs_add: "Adicionar Thangs"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
delete: "Excluir"
|
||||
duplicate: "Duplicar"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
rotate: "Rotacionar"
|
||||
level_settings_title: "Configurações"
|
||||
level_component_tab_title: "Componentess Atuais"
|
||||
|
|
|
@ -159,10 +159,10 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
|
|||
date: "Data"
|
||||
body: "Corpo"
|
||||
version: "Versão"
|
||||
# pending: "Pending"
|
||||
# accepted: "Accepted"
|
||||
# rejected: "Rejected"
|
||||
# withdrawn: "Withdrawn"
|
||||
pending: "Pendentes"
|
||||
accepted: "Aceitadas"
|
||||
rejected: "Rejeitadas"
|
||||
withdrawn: "Canceladas"
|
||||
submitter: "Submissor"
|
||||
submitted: "Submeteu"
|
||||
commit_msg: "Mensagem da Submissão"
|
||||
|
@ -197,9 +197,9 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
|
|||
hard: "Difícil"
|
||||
player: "Jogador"
|
||||
player_level: "Nível" # Like player level 5, not like level: Dungeons of Kithgard
|
||||
# warrior: "Warrior"
|
||||
# ranger: "Ranger"
|
||||
# wizard: "Wizard"
|
||||
warrior: "Guerreiro"
|
||||
ranger: "Arqueiro"
|
||||
wizard: "Feiticeiro"
|
||||
|
||||
units:
|
||||
second: "segundo"
|
||||
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
|
|||
revert: "Reverter"
|
||||
revert_models: "Reverter Modelos"
|
||||
pick_a_terrain: "Escolhe Um Terreno"
|
||||
dungeon: "Masmorra"
|
||||
indoor: "Interior"
|
||||
desert: "Deserto"
|
||||
grassy: "Relvado"
|
||||
small: "Pequeno"
|
||||
grassy: "Com Relva"
|
||||
large: "Grande"
|
||||
fork_title: "Bifurcar Nova Versão"
|
||||
fork_creating: "A Criar Bifurcação..."
|
||||
generate_terrain: "Gerar Terreno"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "Português (Portugal)", englishDescription:
|
|||
level_tab_thangs_all: "Todos"
|
||||
level_tab_thangs_conditions: "Condições Iniciais"
|
||||
level_tab_thangs_add: "Adicionar Thangs"
|
||||
config_thang: "Clica duas vezes para configurares uma thang"
|
||||
delete: "Eliminar"
|
||||
duplicate: "Duplicar"
|
||||
stop_duplicate: "Parar de Duplicar"
|
||||
rotate: "Rodar"
|
||||
level_settings_title: "Configurações"
|
||||
level_component_tab_title: "Componentes Atuais"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman
|
|||
revert: "Revino la versiunea anterioară"
|
||||
revert_models: "Resetează Modelele"
|
||||
# pick_a_terrain: "Pick A Terrain"
|
||||
# small: "Small"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
# grassy: "Grassy"
|
||||
# small: "Small"
|
||||
# large: "Large"
|
||||
# fork_title: "Fork New Version"
|
||||
# fork_creating: "Creating Fork..."
|
||||
# generate_terrain: "Generate Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "limba română", englishDescription: "Roman
|
|||
# level_tab_thangs_all: "All"
|
||||
level_tab_thangs_conditions: "Condiți inițiale"
|
||||
level_tab_thangs_add: "Adaugă Thangs"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
# delete: "Delete"
|
||||
# duplicate: "Duplicate"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
# rotate: "Rotate"
|
||||
level_settings_title: "Setări"
|
||||
level_component_tab_title: "Componente actuale"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi
|
|||
revert: "Откатить"
|
||||
revert_models: "Откатить Модели"
|
||||
pick_a_terrain: "Выберите ландшафт"
|
||||
small: "Маленький"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
grassy: "Травянистый"
|
||||
small: "Маленький"
|
||||
# large: "Large"
|
||||
fork_title: "Форк новой версии"
|
||||
fork_creating: "Создание форка..."
|
||||
generate_terrain: "Создать ландшафт"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "русский", englishDescription: "Russi
|
|||
level_tab_thangs_all: "Все"
|
||||
level_tab_thangs_conditions: "Начальные условия"
|
||||
level_tab_thangs_add: "Добавить объект"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
delete: "Удалить"
|
||||
duplicate: "Дублировать"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
rotate: "Повернуть"
|
||||
level_settings_title: "Настройки"
|
||||
level_component_tab_title: "Текущие компоненты"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak",
|
|||
# revert: "Revert"
|
||||
# revert_models: "Revert Models"
|
||||
# pick_a_terrain: "Pick A Terrain"
|
||||
# small: "Small"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
# grassy: "Grassy"
|
||||
# small: "Small"
|
||||
# large: "Large"
|
||||
# fork_title: "Fork New Version"
|
||||
# fork_creating: "Creating Fork..."
|
||||
# generate_terrain: "Generate Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "slovenčina", englishDescription: "Slovak",
|
|||
# level_tab_thangs_all: "All"
|
||||
# level_tab_thangs_conditions: "Starting Conditions"
|
||||
# level_tab_thangs_add: "Add Thangs"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
# delete: "Delete"
|
||||
# duplicate: "Duplicate"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
# rotate: "Rotate"
|
||||
# level_settings_title: "Settings"
|
||||
# level_component_tab_title: "Current Components"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven
|
|||
# revert: "Revert"
|
||||
# revert_models: "Revert Models"
|
||||
# pick_a_terrain: "Pick A Terrain"
|
||||
# small: "Small"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
# grassy: "Grassy"
|
||||
# small: "Small"
|
||||
# large: "Large"
|
||||
# fork_title: "Fork New Version"
|
||||
# fork_creating: "Creating Fork..."
|
||||
# generate_terrain: "Generate Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "slovenščina", englishDescription: "Sloven
|
|||
# level_tab_thangs_all: "All"
|
||||
# level_tab_thangs_conditions: "Starting Conditions"
|
||||
# level_tab_thangs_add: "Add Thangs"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
# delete: "Delete"
|
||||
# duplicate: "Duplicate"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
# rotate: "Rotate"
|
||||
# level_settings_title: "Settings"
|
||||
# level_component_tab_title: "Current Components"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian
|
|||
# revert: "Revert"
|
||||
# revert_models: "Revert Models"
|
||||
# pick_a_terrain: "Pick A Terrain"
|
||||
# small: "Small"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
# grassy: "Grassy"
|
||||
# small: "Small"
|
||||
# large: "Large"
|
||||
# fork_title: "Fork New Version"
|
||||
# fork_creating: "Creating Fork..."
|
||||
# generate_terrain: "Generate Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "српски", englishDescription: "Serbian
|
|||
# level_tab_thangs_all: "All"
|
||||
# level_tab_thangs_conditions: "Starting Conditions"
|
||||
# level_tab_thangs_add: "Add Thangs"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
# delete: "Delete"
|
||||
# duplicate: "Duplicate"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
# rotate: "Rotate"
|
||||
# level_settings_title: "Settings"
|
||||
# level_component_tab_title: "Current Components"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr
|
|||
revert: "Återställ"
|
||||
revert_models: "Återställ modeller"
|
||||
# pick_a_terrain: "Pick A Terrain"
|
||||
# small: "Small"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
# grassy: "Grassy"
|
||||
# small: "Small"
|
||||
# large: "Large"
|
||||
# fork_title: "Fork New Version"
|
||||
# fork_creating: "Creating Fork..."
|
||||
# generate_terrain: "Generate Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "Svenska", englishDescription: "Swedish", tr
|
|||
# level_tab_thangs_all: "All"
|
||||
level_tab_thangs_conditions: "Startvillkor"
|
||||
level_tab_thangs_add: "Lägg till enheter"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
# delete: "Delete"
|
||||
# duplicate: "Duplicate"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
# rotate: "Rotate"
|
||||
level_settings_title: "Inställningar"
|
||||
level_component_tab_title: "Nuvarande komponenter"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra
|
|||
# revert: "Revert"
|
||||
# revert_models: "Revert Models"
|
||||
# pick_a_terrain: "Pick A Terrain"
|
||||
# small: "Small"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
# grassy: "Grassy"
|
||||
# small: "Small"
|
||||
# large: "Large"
|
||||
# fork_title: "Fork New Version"
|
||||
# fork_creating: "Creating Fork..."
|
||||
# generate_terrain: "Generate Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "ไทย", englishDescription: "Thai", tra
|
|||
# level_tab_thangs_all: "All"
|
||||
# level_tab_thangs_conditions: "Starting Conditions"
|
||||
# level_tab_thangs_add: "Add Thangs"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
# delete: "Delete"
|
||||
# duplicate: "Duplicate"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
# rotate: "Rotate"
|
||||
# level_settings_title: "Settings"
|
||||
# level_component_tab_title: "Current Components"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t
|
|||
revert: "Geri al"
|
||||
revert_models: "Önceki Modeller"
|
||||
pick_a_terrain: "Bir Arazi Seçin"
|
||||
small: "Küçük"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
grassy: "Çimli"
|
||||
small: "Küçük"
|
||||
# large: "Large"
|
||||
fork_title: "Yeni Sürüm Çatalla"
|
||||
fork_creating: "Çatal Oluşturuluyor..."
|
||||
generate_terrain: "Arazi Oluştur"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "Türkçe", englishDescription: "Turkish", t
|
|||
level_tab_thangs_all: "Tüm"
|
||||
level_tab_thangs_conditions: "Başlama Şartları"
|
||||
level_tab_thangs_add: "Nesne Ekle"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
delete: "Sil"
|
||||
duplicate: "Kopyala"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
rotate: "Döndür"
|
||||
level_settings_title: "Ayarlar"
|
||||
level_component_tab_title: "Geçerli Bileşenler"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "Українська", englishDescription:
|
|||
revert: "Повернутись"
|
||||
revert_models: "Моделі повернення"
|
||||
pick_a_terrain: "Обрати лашдшафт"
|
||||
small: "Малий"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
grassy: "Подвір'я"
|
||||
small: "Малий"
|
||||
# large: "Large"
|
||||
fork_title: "Форк нової версії"
|
||||
fork_creating: "Створення форку..."
|
||||
generate_terrain: "Згенерувати ландшафт"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "Українська", englishDescription:
|
|||
level_tab_thangs_all: "Усі"
|
||||
level_tab_thangs_conditions: "Початковий статус"
|
||||
level_tab_thangs_add: "Додати об'єкти"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
delete: "Видалити"
|
||||
duplicate: "Дублікат"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
rotate: "Повернути"
|
||||
level_settings_title: "Налаштування"
|
||||
level_component_tab_title: "Поточні компоненти"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu",
|
|||
# revert: "Revert"
|
||||
# revert_models: "Revert Models"
|
||||
# pick_a_terrain: "Pick A Terrain"
|
||||
# small: "Small"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
# grassy: "Grassy"
|
||||
# small: "Small"
|
||||
# large: "Large"
|
||||
# fork_title: "Fork New Version"
|
||||
# fork_creating: "Creating Fork..."
|
||||
# generate_terrain: "Generate Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "اُردُو", englishDescription: "Urdu",
|
|||
# level_tab_thangs_all: "All"
|
||||
# level_tab_thangs_conditions: "Starting Conditions"
|
||||
# level_tab_thangs_add: "Add Thangs"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
# delete: "Delete"
|
||||
# duplicate: "Duplicate"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
# rotate: "Rotate"
|
||||
# level_settings_title: "Settings"
|
||||
# level_component_tab_title: "Current Components"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn
|
|||
# revert: "Revert"
|
||||
# revert_models: "Revert Models"
|
||||
# pick_a_terrain: "Pick A Terrain"
|
||||
# small: "Small"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
# grassy: "Grassy"
|
||||
# small: "Small"
|
||||
# large: "Large"
|
||||
# fork_title: "Fork New Version"
|
||||
# fork_creating: "Creating Fork..."
|
||||
# generate_terrain: "Generate Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "Tiếng Việt", englishDescription: "Vietn
|
|||
# level_tab_thangs_all: "All"
|
||||
# level_tab_thangs_conditions: "Starting Conditions"
|
||||
# level_tab_thangs_add: "Add Thangs"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
# delete: "Delete"
|
||||
# duplicate: "Duplicate"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
# rotate: "Rotate"
|
||||
# level_settings_title: "Settings"
|
||||
# level_component_tab_title: "Current Components"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
|
|||
revert: "还原"
|
||||
revert_models: "还原模式"
|
||||
pick_a_terrain: "选择地形"
|
||||
small: "小的"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
grassy: "草地"
|
||||
small: "小的"
|
||||
# large: "Large"
|
||||
fork_title: "派生新版本"
|
||||
fork_creating: "正在执行派生..."
|
||||
generate_terrain: "Generate Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "简体中文", englishDescription: "Chinese
|
|||
level_tab_thangs_all: "所有"
|
||||
level_tab_thangs_conditions: "启动条件"
|
||||
level_tab_thangs_add: "增加物体"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
delete: "删除"
|
||||
duplicate: "复制"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
rotate: "旋转"
|
||||
level_settings_title: "设置"
|
||||
level_component_tab_title: "目前所有组件"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "繁体中文", englishDescription: "Chinese
|
|||
# revert: "Revert"
|
||||
# revert_models: "Revert Models"
|
||||
# pick_a_terrain: "Pick A Terrain"
|
||||
# small: "Small"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
# grassy: "Grassy"
|
||||
# small: "Small"
|
||||
# large: "Large"
|
||||
# fork_title: "Fork New Version"
|
||||
# fork_creating: "Creating Fork..."
|
||||
# generate_terrain: "Generate Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "繁体中文", englishDescription: "Chinese
|
|||
# level_tab_thangs_all: "All"
|
||||
# level_tab_thangs_conditions: "Starting Conditions"
|
||||
# level_tab_thangs_add: "Add Thangs"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
# delete: "Delete"
|
||||
# duplicate: "Duplicate"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
# rotate: "Rotate"
|
||||
# level_settings_title: "Settings"
|
||||
# level_component_tab_title: "Current Components"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi
|
|||
# revert: "Revert"
|
||||
# revert_models: "Revert Models"
|
||||
# pick_a_terrain: "Pick A Terrain"
|
||||
# small: "Small"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
# grassy: "Grassy"
|
||||
# small: "Small"
|
||||
# large: "Large"
|
||||
# fork_title: "Fork New Version"
|
||||
# fork_creating: "Creating Fork..."
|
||||
# generate_terrain: "Generate Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "吴语", englishDescription: "Wuu (Simplifi
|
|||
# level_tab_thangs_all: "All"
|
||||
# level_tab_thangs_conditions: "Starting Conditions"
|
||||
# level_tab_thangs_add: "Add Thangs"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
# delete: "Delete"
|
||||
# duplicate: "Duplicate"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
# rotate: "Rotate"
|
||||
# level_settings_title: "Settings"
|
||||
# level_component_tab_title: "Current Components"
|
||||
|
|
|
@ -608,8 +608,12 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio
|
|||
revert: "還原"
|
||||
revert_models: "還原模式"
|
||||
# pick_a_terrain: "Pick A Terrain"
|
||||
# small: "Small"
|
||||
# dungeon: "Dungeon"
|
||||
# indoor: "Indoor"
|
||||
# desert: "Desert"
|
||||
# grassy: "Grassy"
|
||||
# small: "Small"
|
||||
# large: "Large"
|
||||
fork_title: "派生新版本"
|
||||
fork_creating: "徠搭執行派生..."
|
||||
# generate_terrain: "Generate Terrain"
|
||||
|
@ -630,8 +634,10 @@ module.exports = nativeDescription: "吳語", englishDescription: "Wuu (Traditio
|
|||
level_tab_thangs_all: "所有"
|
||||
level_tab_thangs_conditions: "發動條件"
|
||||
level_tab_thangs_add: "加物事"
|
||||
# config_thang: "Double click to configure a thang"
|
||||
delete: "刪除"
|
||||
duplicate: "翻做"
|
||||
# stop_duplicate: "Stop Duplicate"
|
||||
# rotate: "Rotate"
|
||||
level_settings_title: "設定"
|
||||
level_component_tab_title: "能界所有組件"
|
||||
|
|
|
@ -6,7 +6,11 @@ AnalyticsLogEventSchema = c.object {
|
|||
}
|
||||
|
||||
_.extend AnalyticsLogEventSchema.properties,
|
||||
created: c.date({title: 'Created', readOnly: true})
|
||||
u: c.objectId(links: [{rel: 'extra', href: '/db/user/{($)}'}])
|
||||
e: {type: 'integer'}
|
||||
p: {type: 'object'}
|
||||
|
||||
# TODO: Remove these legacy properties after we stop querying for them (probably 30 days, ~2/16/15)
|
||||
user: c.objectId(links: [{rel: 'extra', href: '/db/user/{($)}'}])
|
||||
event: {type: 'string'}
|
||||
properties: {type: 'object'}
|
||||
|
|
18
app/schemas/models/analytics_perday.coffee
Normal file
18
app/schemas/models/analytics_perday.coffee
Normal file
|
@ -0,0 +1,18 @@
|
|||
c = require './../schemas'
|
||||
|
||||
AnalyticsPerDaySchema = c.object {
|
||||
title: 'Analytics per-day data'
|
||||
description: 'Analytics data aggregated into per-day chunks.'
|
||||
}
|
||||
|
||||
_.extend AnalyticsPerDaySchema.properties,
|
||||
d: {type: 'string'} # yyyymmdd day, e.g. '20150123'
|
||||
e: {type: 'integer'} # event (analytics string ID from analytics.strings)
|
||||
l: {type: 'integer'} # level (analytics ID from analytics.strings)
|
||||
f: {type: 'integer'} # filter (analytics ID from analytics.strings)
|
||||
fv: {type: 'integer'} # filter value (analytics ID from analytics.strings)
|
||||
c: {type: 'integer'} # count
|
||||
|
||||
c.extendBasicProperties AnalyticsPerDaySchema, 'analytics.perday'
|
||||
|
||||
module.exports = AnalyticsPerDaySchema
|
13
app/schemas/models/analytics_string.coffee
Normal file
13
app/schemas/models/analytics_string.coffee
Normal file
|
@ -0,0 +1,13 @@
|
|||
c = require './../schemas'
|
||||
|
||||
AnalyticsStringSchema = c.object {
|
||||
title: 'Analytics String'
|
||||
description: 'Maps strings to number IDs for improved performance.'
|
||||
}
|
||||
|
||||
_.extend AnalyticsStringSchema.properties,
|
||||
v: {type: 'string'} # value
|
||||
|
||||
c.extendBasicProperties AnalyticsStringSchema, 'analytics.string'
|
||||
|
||||
module.exports = AnalyticsStringSchema
|
|
@ -78,13 +78,11 @@
|
|||
span.sign-in-blurb(data-i18n="login.sign_in_with_facebook") Sign in with Facebook
|
||||
.facebook-login-wrapper
|
||||
.fb-login-button(data-show-faces="false", data-width="200", data-max-rows="1", data-scope="email")
|
||||
// Google+ login causing script errors on IE10
|
||||
if !isIE
|
||||
.btn.btn-danger.btn-lg.btn-illustrated.network-login
|
||||
img.network-logo(src="/images/pages/community/logo_g+.png", draggable="false")
|
||||
span.sign-in-blurb(data-i18n="login.sign_in_with_gplus") Sign in with G+
|
||||
.gplus-login-wrapper
|
||||
.gplus-login-button#gplus-login-button
|
||||
.btn.btn-danger.btn-lg.btn-illustrated.network-login
|
||||
img.network-logo(src="/images/pages/community/logo_g+.png", draggable="false")
|
||||
span.sign-in-blurb(data-i18n="login.sign_in_with_gplus") Sign in with G+
|
||||
.gplus-login-wrapper
|
||||
.gplus-login-button#gplus-login-button
|
||||
|
||||
.extra-pane
|
||||
if mode === 'login'
|
||||
|
|
|
@ -40,7 +40,7 @@ block outer_content
|
|||
#right-column
|
||||
#campaign-view
|
||||
#campaign-level-view.hidden
|
||||
if campaignDropOffs
|
||||
if campaignCompletions
|
||||
button.btn.btn-default#analytics-button(title="Analytics", data-toggle="modal" data-target="#analytics-modal") Analytics
|
||||
.modal.fade#analytics-modal(tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true")
|
||||
.modal-dialog
|
||||
|
@ -50,33 +50,25 @@ block outer_content
|
|||
span(aria-hidden="true") ×
|
||||
h4.modal-title Analytics
|
||||
.modal-body
|
||||
if campaignDropOffs.startDay
|
||||
if campaignDropOffs.endDay
|
||||
div #{campaignDropOffs.startDay} to #{campaignDropOffs.endDay}
|
||||
if campaignCompletions.startDay
|
||||
if campaignCompletions.endDay
|
||||
div #{campaignCompletions.startDay} to #{campaignCompletions.endDay}
|
||||
else
|
||||
div #{campaignDropOffs.startDay} to today
|
||||
div #{campaignCompletions.startDay} to yesterday
|
||||
table.table.table-bordered.table-condensed.table-hover(style='font-size:10pt')
|
||||
thead
|
||||
tr
|
||||
td Level
|
||||
td Started
|
||||
td Dropped
|
||||
td Drop %
|
||||
td Finished
|
||||
td Dropped
|
||||
td Drop %
|
||||
td Completion %
|
||||
tbody
|
||||
- for (var i = 0; i < campaignDropOffs.levels.length; i++)
|
||||
- for (var i = 0; i < campaignCompletions.levels.length; i++)
|
||||
tr
|
||||
td= campaignDropOffs.levels[i].level
|
||||
td= campaignDropOffs.levels[i].started
|
||||
td= campaignDropOffs.levels[i].startDropped
|
||||
td= campaignDropOffs.levels[i].startDropRate
|
||||
td= campaignDropOffs.levels[i].finished
|
||||
td= campaignDropOffs.levels[i].finishDropped
|
||||
td= campaignDropOffs.levels[i].finishDropRate
|
||||
td= campaignDropOffs.levels[i].completionRate
|
||||
td= campaignCompletions.levels[i].level
|
||||
td= campaignCompletions.levels[i].started
|
||||
td= campaignCompletions.levels[i].finished
|
||||
td= campaignCompletions.levels[i].completionRate
|
||||
else
|
||||
button.btn.btn-default.disabled#analytics-button Analytics Loading...
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ block modal-body-content
|
|||
a(href="#")
|
||||
div.choose-option(data-preset-type=preset.type, data-preset-size=size, style="background:url(/images/pages/editor/level/preset_"+preset.type+"_"+size+".jpg) no-repeat center; background-size: cover")
|
||||
div.preset-size.name-label.capitalize
|
||||
span(data-i18n="editor."+size) #{size}
|
||||
span(data-i18n="editor."+size) #{size}
|
||||
div.preset-name.capitalize
|
||||
span(data-i18n="editor."+preset.type) #{preset.type}
|
||||
.clearfix
|
||||
|
|
|
@ -4,7 +4,7 @@ button.btn#thangs-palette-toggle
|
|||
span.icon-plus
|
||||
.thangs-container.hide#all-thangs
|
||||
h3(data-i18n="editor.level_tab_thangs_title") Current Thangs
|
||||
#thangs-treema(title="Double click to configure a thang")
|
||||
#thangs-treema(data-i18n="[title]editor.config_thang", title="Double click to configure a thang")
|
||||
|
||||
.world-container
|
||||
#canvas-wrapper
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
.btn-group(data-toggle="buttons").status-buttons
|
||||
label.btn.btn-default.pending
|
||||
input(type="radio", name="status", value="pending", data-i18n="general.pending")
|
||||
| Pending
|
||||
input(type="radio", name="status", value="pending")
|
||||
span(data-i18n="general.pending") Pending
|
||||
label.btn.btn-default.accepted
|
||||
input(type="radio", name="status", value="accepted", data-i18n="general.accepted")
|
||||
| Accepted
|
||||
input(type="radio", name="status", value="accepted")
|
||||
span(data-i18n="general.accepted") Accepted
|
||||
label.btn.btn-default.rejected
|
||||
input(type="radio", name="status", value="rejected", data-i18n="general.rejected")
|
||||
| Rejected
|
||||
input(type="radio", name="status", value="rejected")
|
||||
span(data-i18n="general.rejected") Rejected
|
||||
label.btn.btn-default.withdrawn
|
||||
input(type="radio", name="status", value="withdrawn", data-i18n="general.withdrawn")
|
||||
| Withdrawn
|
||||
input(type="radio", name="status", value="withdrawn")
|
||||
span(data-i18n="general.withdrawn") Withdrawn
|
||||
|
||||
if patches.loading
|
||||
p(data-i18n="common.loading") Loading...
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
ul.nav.nav-pills.nav-stacked
|
||||
for category, index in itemCategories
|
||||
li(class=index ? "" : "active")
|
||||
li(class=index ? "" : "active", id=category + '-tab')
|
||||
a.one-line(href="#item-category-" + category, data-toggle="tab")
|
||||
img.tab-icon(src="/images/pages/play/modal/item-icon-"+category+".png", draggable="false")
|
||||
span.big-font= itemCategoryNames[index]
|
||||
|
|
|
@ -47,7 +47,7 @@ module.exports = class CampaignEditorView extends RootView
|
|||
@listenToOnce @levels, 'sync', @onFundamentalLoaded
|
||||
@listenToOnce @achievements, 'sync', @onFundamentalLoaded
|
||||
|
||||
#_.delay @getCampaignCompletions, 1000 # Roughly never finishes loading, nearly kills server.
|
||||
_.delay @getCampaignCompletions, 500
|
||||
|
||||
loadThangTypeNames: ->
|
||||
# Load the names of the ThangTypes that this level's Treema nodes might want to display.
|
||||
|
@ -132,7 +132,7 @@ module.exports = class CampaignEditorView extends RootView
|
|||
getRenderData: ->
|
||||
c = super()
|
||||
c.campaign = @campaign
|
||||
c.campaignDropOffs = @campaignDropOffs
|
||||
c.campaignCompletions = @campaignCompletions
|
||||
c
|
||||
|
||||
onClickSaveButton: ->
|
||||
|
@ -241,26 +241,24 @@ module.exports = class CampaignEditorView extends RootView
|
|||
@toSave.add achievement
|
||||
|
||||
getCampaignCompletions: =>
|
||||
# Fetch last 7 days of campaign drop-off rates
|
||||
# Fetch last 14 days of campaign drop-off rates
|
||||
|
||||
startDay = utils.getUTCDay -6
|
||||
startDay = utils.getUTCDay -14
|
||||
endDay = utils.getUTCDay -1
|
||||
|
||||
success = (data) =>
|
||||
return if @destroyed
|
||||
# API returns all the campaign data currently
|
||||
@campaignDropOffs = data[@campaignHandle]
|
||||
mapFn = (item) ->
|
||||
item.startDropRate = (item.startDropped / item.started * 100).toFixed(2)
|
||||
item.finishDropRate = (item.finishDropped / item.finished * 100).toFixed(2)
|
||||
item.completionRate = (item.finished / item.started * 100).toFixed(2)
|
||||
item
|
||||
@campaignDropOffs.levels = _.map @campaignDropOffs.levels, mapFn, @
|
||||
@campaignDropOffs.startDay = startDay
|
||||
@campaignCompletions = levels: _.map data, mapFn, @
|
||||
@campaignCompletions.startDay = "#{startDay[0..3]}-#{startDay[4..5]}-#{startDay[6..7]}"
|
||||
@campaignCompletions.endDay = "#{endDay[0..3]}-#{endDay[4..5]}-#{endDay[6..7]}"
|
||||
@render()
|
||||
|
||||
# TODO: Why do we need this url dash?
|
||||
request = @supermodel.addRequestResource 'campaign_completions', {
|
||||
url: '/db/analytics_log_event/-/campaign_completions'
|
||||
url: '/db/analytics_perday/-/campaign_completions'
|
||||
data: {startDay: startDay, slug: @campaignHandle}
|
||||
method: 'POST'
|
||||
success: success
|
||||
|
|
|
@ -49,6 +49,7 @@ module.exports = class CampaignLevelView extends CocoView
|
|||
getCommonLevelProblems: ->
|
||||
# Fetch last 30 days of common level problems
|
||||
startDay = utils.getUTCDay -29
|
||||
startDay = "#{startDay[0..3]}-#{startDay[4..5]}-#{startDay[6..7]}"
|
||||
|
||||
success = (data) =>
|
||||
return if @destroyed
|
||||
|
@ -66,7 +67,7 @@ module.exports = class CampaignLevelView extends CocoView
|
|||
request.load()
|
||||
|
||||
getLevelCompletions: ->
|
||||
# Fetch last 7 days of level completion counts
|
||||
# Fetch last 14 days of level completion counts
|
||||
success = (data) =>
|
||||
return if @destroyed
|
||||
data.sort (a, b) -> if a.created < b.created then 1 else -1
|
||||
|
@ -76,11 +77,11 @@ module.exports = class CampaignLevelView extends CocoView
|
|||
@levelCompletions = _.map data, mapFn, @
|
||||
@render()
|
||||
|
||||
startDay = utils.getUTCDay -13
|
||||
startDay = utils.getUTCDay -14
|
||||
|
||||
# TODO: Why do we need this url dash?
|
||||
request = @supermodel.addRequestResource 'level_completions', {
|
||||
url: '/db/analytics_log_event/-/level_completions'
|
||||
url: '/db/analytics_perday/-/level_completions'
|
||||
data: {startDay: startDay, slug: @levelSlug}
|
||||
method: 'POST'
|
||||
success: success
|
||||
|
@ -88,13 +89,14 @@ module.exports = class CampaignLevelView extends CocoView
|
|||
request.load()
|
||||
|
||||
getLevelPlaytimes: ->
|
||||
# Fetch last 7 days of level average playtimes
|
||||
# Fetch last 14 days of level average playtimes
|
||||
success = (data) =>
|
||||
return if @destroyed
|
||||
@levelPlaytimes = data.sort (a, b) -> if a.created < b.created then 1 else -1
|
||||
@render()
|
||||
|
||||
startDay = utils.getUTCDay -13
|
||||
startDay = "#{startDay[0..3]}-#{startDay[4..5]}-#{startDay[6..7]}"
|
||||
|
||||
# TODO: Why do we need this url dash?
|
||||
request = @supermodel.addRequestResource 'playtime_averages', {
|
||||
|
|
|
@ -625,9 +625,9 @@ module.exports = class ThangsTabView extends CocoView
|
|||
onSpriteContextMenu: (e) ->
|
||||
{clientX, clientY} = e.originalEvent.nativeEvent
|
||||
if @addThangType
|
||||
$('#duplicate a').html 'Stop Duplicate'
|
||||
$('#duplicate a').html $.i18n.t 'editor.stop_duplicate'
|
||||
else
|
||||
$('#duplicate a').html 'Duplicate'
|
||||
$('#duplicate a').html $.i18n.t 'editor.duplicate'
|
||||
$('#contextmenu').css { position: 'fixed', left: clientX, top: clientY }
|
||||
$('#contextmenu').show()
|
||||
|
||||
|
|
|
@ -192,7 +192,7 @@ module.exports = class CampaignView extends RootView
|
|||
@highlightElement '.level.next', delay: 500, duration: 60000, rotation: 0, sides: ['top']
|
||||
if @editorMode
|
||||
for level in @campaign.renderedLevels
|
||||
for nextLevelOriginal in level.nextLevels
|
||||
for nextLevelOriginal in level.nextLevels ? []
|
||||
if nextLevel = _.find(@campaign.renderedLevels, original: nextLevelOriginal)
|
||||
@createLine level.position, nextLevel.position
|
||||
@applyCampaignStyles()
|
||||
|
|
|
@ -17,6 +17,11 @@ module.exports = class LevelDialogueView extends CocoView
|
|||
'click': 'onClick'
|
||||
'click a': 'onClickLink'
|
||||
|
||||
constructor: (options) ->
|
||||
super options
|
||||
@level = options.level
|
||||
@sessionID = options.sessionID
|
||||
|
||||
onClick: (e) ->
|
||||
Backbone.Mediator.publish 'tome:focus-editor', {}
|
||||
|
||||
|
@ -33,7 +38,7 @@ module.exports = class LevelDialogueView extends CocoView
|
|||
$('body').addClass('dialogue-view-active')
|
||||
@setMessage e.message, e.mood, e.responses
|
||||
|
||||
window.tracker?.trackEvent 'Heard Sprite', {message: e.message, label: e.message}, ['Google Analytics']
|
||||
window.tracker?.trackEvent 'Heard Sprite', {message: e.message, label: e.message, ls: @sessionID}, ['Google Analytics']
|
||||
|
||||
onDialogueSoundCompleted: ->
|
||||
@$el.removeClass 'speaking'
|
||||
|
|
|
@ -239,7 +239,7 @@ module.exports = class PlayLevelView extends RootView
|
|||
@insertSubView new LevelFlagsView levelID: @levelID, world: @world if @$el.hasClass 'flags'
|
||||
@insertSubView new GoldView {}
|
||||
@insertSubView new HUDView {level: @level}
|
||||
@insertSubView new LevelDialogueView {level: @level}
|
||||
@insertSubView new LevelDialogueView {level: @level, sessionID: @session.id}
|
||||
@insertSubView new ChatView levelID: @levelID, sessionID: @session.id, session: @session
|
||||
@insertSubView new ProblemAlertView session: @session, level: @level, supermodel: @supermodel
|
||||
worldName = utils.i18n @level.attributes, 'name'
|
||||
|
@ -335,7 +335,8 @@ module.exports = class PlayLevelView extends RootView
|
|||
if @options.realTimeMultiplayerSessionID?
|
||||
Backbone.Mediator.publish 'playback:real-time-playback-waiting', {}
|
||||
@realTimeMultiplayerContinueGame @options.realTimeMultiplayerSessionID
|
||||
application.tracker?.trackEvent 'Started Level', category:'Play Level', levelID: @levelID
|
||||
# TODO: Is it possible to create a Mongoose ObjectId for 'ls', instead of the string returned from get()?
|
||||
application.tracker?.trackEvent 'Started Level', category:'Play Level', levelID: @levelID, ls: @session?.get('_id')
|
||||
|
||||
playAmbientSound: ->
|
||||
return if @destroyed
|
||||
|
@ -418,6 +419,7 @@ module.exports = class PlayLevelView extends RootView
|
|||
level: @level.get('name')
|
||||
label: @level.get('name')
|
||||
levelID: @levelID
|
||||
ls: @session?.get('_id')
|
||||
application.tracker?.trackTiming victoryTime, 'Level Victory Time', @levelID, @levelID, 100
|
||||
|
||||
showVictory: ->
|
||||
|
|
|
@ -74,7 +74,7 @@ module.exports = class ProblemAlertView extends CocoView
|
|||
@onWindowResize()
|
||||
@render()
|
||||
@onJiggleProblemAlert()
|
||||
application.tracker?.trackEvent 'Show problem alert', levelID: @level.get('slug')
|
||||
application.tracker?.trackEvent 'Show problem alert', {levelID: @level.get('slug'), ls: @session?.get('_id')}
|
||||
|
||||
onJiggleProblemAlert: ->
|
||||
return unless @problem?
|
||||
|
@ -90,7 +90,7 @@ module.exports = class ProblemAlertView extends CocoView
|
|||
@onRemoveClicked()
|
||||
|
||||
onClickProblemAlertHelp: ->
|
||||
application.tracker?.trackEvent 'Problem alert help clicked', levelID: @level.get('slug')
|
||||
application.tracker?.trackEvent 'Problem alert help clicked', {levelID: @level.get('slug'), ls: @session?.get('_id')}
|
||||
@openModalView new GameMenuModal showTab: 'guide', level: @level, session: @session, supermodel: @supermodel
|
||||
|
||||
onRemoveClicked: ->
|
||||
|
|
|
@ -14,6 +14,7 @@ module.exports = class LevelGuideView extends CocoView
|
|||
|
||||
constructor: (options) ->
|
||||
@levelID = options.level.get('slug')
|
||||
@sessionID = options.session.get('_id')
|
||||
@helpVideos = options.level.get('helpVideos') ? []
|
||||
@trackedHelpVideoStart = @trackedHelpVideoFinish = false
|
||||
|
||||
|
@ -85,12 +86,12 @@ module.exports = class LevelGuideView extends CocoView
|
|||
|
||||
onStartHelpVideo: ->
|
||||
unless @trackedHelpVideoStart
|
||||
window.tracker?.trackEvent 'Start help video', level: @levelID, style: @helpVideos[@helpVideosIndex].style
|
||||
window.tracker?.trackEvent 'Start help video', level: @levelID, ls: @sessionID, style: @helpVideos[@helpVideosIndex].style
|
||||
@trackedHelpVideoStart = true
|
||||
|
||||
onFinishHelpVideo: ->
|
||||
unless @trackedHelpVideoFinish
|
||||
window.tracker?.trackEvent 'Finish help video', level: @levelID, style: @helpVideos[@helpVideosIndex].style
|
||||
window.tracker?.trackEvent 'Finish help video', level: @levelID, ls: @sessionID, style: @helpVideos[@helpVideosIndex].style
|
||||
@trackedHelpVideoFinish = true
|
||||
|
||||
setupVideoPlayer: () ->
|
||||
|
|
|
@ -8,6 +8,8 @@ AudioPlayer = require 'lib/AudioPlayer'
|
|||
utils = require 'core/utils'
|
||||
BuyGemsModal = require 'views/play/modal/BuyGemsModal'
|
||||
Purchase = require 'models/Purchase'
|
||||
LayerAdapter = require 'lib/surface/LayerAdapter'
|
||||
Lank = require 'lib/surface/Lank'
|
||||
|
||||
module.exports = class PlayHeroesModal extends ModalView
|
||||
className: 'modal fade play-modal'
|
||||
|
@ -39,9 +41,10 @@ module.exports = class PlayHeroesModal extends ModalView
|
|||
@listenToOnce @heroes, 'sync', @onHeroesLoaded
|
||||
@supermodel.loadCollection(@heroes, 'heroes')
|
||||
@stages = {}
|
||||
@layers = []
|
||||
@session = options.session
|
||||
@initCodeLanguageList options.hadEverChosenHero
|
||||
@heroAnimationInterval = setInterval @animateHeroes, 2500
|
||||
@heroAnimationInterval = setInterval @animateHeroes, 1000
|
||||
|
||||
onHeroesLoaded: ->
|
||||
@formatHero hero for hero in @heroes.models
|
||||
|
@ -155,41 +158,29 @@ module.exports = class PlayHeroesModal extends ModalView
|
|||
@rerenderFooter()
|
||||
return
|
||||
canvas.show().prop width: @canvasWidth, height: @canvasHeight
|
||||
builder = new SpriteBuilder(fullHero)
|
||||
movieClip = builder.buildMovieClip(fullHero.get('actions').attack?.animation ? fullHero.get('actions').idle.animation)
|
||||
movieClip.scaleX = movieClip.scaleY = canvas.prop('height') / 120 # Average hero height is ~110px tall at normal resolution
|
||||
movieClip.regX = -fullHero.get('positions').registration.x
|
||||
movieClip.regY = -fullHero.get('positions').registration.y
|
||||
movieClip.x = canvas.prop('width') * 0.5
|
||||
movieClip.y = canvas.prop('height') * 0.925 # This is where the feet go.
|
||||
if fullHero.get('name') is 'Knight'
|
||||
movieClip.scaleX *= 0.7
|
||||
movieClip.scaleY *= 0.7
|
||||
if fullHero.get('name') is 'Potion Master'
|
||||
movieClip.scaleX *= 0.9
|
||||
movieClip.scaleY *= 0.9
|
||||
movieClip.regX *= 1.1
|
||||
movieClip.regY *= 1.4
|
||||
if fullHero.get('name') is 'Samurai'
|
||||
movieClip.scaleX *= 0.7
|
||||
movieClip.scaleY *= 0.7
|
||||
movieClip.regX *= 1.2
|
||||
movieClip.regY *= 1.35
|
||||
if fullHero.get('name') is 'Librarian'
|
||||
movieClip.regX *= 0.7
|
||||
movieClip.regY *= 1.2
|
||||
if fullHero.get('name') is 'Sorcerer'
|
||||
movieClip.scaleX *= 0.9
|
||||
movieClip.scaleY *= 0.9
|
||||
movieClip.regX *= 1.15
|
||||
movieClip.regY *= 1.3
|
||||
|
||||
stage = new createjs.Stage(canvas[0])
|
||||
layer = new LayerAdapter({webGL:true})
|
||||
@layers.push layer
|
||||
layer.resolutionFactor = 8 # hi res!
|
||||
layer.buildAsync = false
|
||||
multiplier = 7
|
||||
layer.scaleX = layer.scaleY = multiplier
|
||||
lank = new Lank(fullHero, {preloadSounds: false})
|
||||
|
||||
layer.addLank(lank)
|
||||
layer.on 'new-spritesheet', ->
|
||||
#- maybe put some more normalization here?
|
||||
m = multiplier
|
||||
m *= 0.75 if fullHero.get('slug') in ['knight', 'samurai', 'librarian'] # these heroes are larger for some reason, shrink 'em
|
||||
layer.container.scaleX = layer.container.scaleY = m
|
||||
layer.container.children[0].x = 160/m
|
||||
layer.container.children[0].y = 250/m
|
||||
|
||||
stage = new createjs.SpriteStage(canvas[0])
|
||||
@stages[heroIndex] = stage
|
||||
stage.addChild movieClip
|
||||
stage.addChild layer.container
|
||||
window.stage = stage
|
||||
stage.update()
|
||||
movieClip.loop = false
|
||||
movieClip.gotoAndPlay 0
|
||||
unless preloading
|
||||
createjs.Ticker.addEventListener 'tick', stage
|
||||
@playSelectionSound hero
|
||||
|
@ -203,7 +194,8 @@ module.exports = class PlayHeroesModal extends ModalView
|
|||
animateHeroes: =>
|
||||
return unless @visibleHero
|
||||
heroIndex = Math.max 0, _.findIndex(@heroes.models, ((hero) => hero.get('original') is @visibleHero.get('original')))
|
||||
@stages[heroIndex]?.children?[0]?.gotoAndPlay? 0
|
||||
animation = _.sample(['attack', 'idle', 'move_side', 'move_fore'])
|
||||
@stages[heroIndex]?.children?[0]?.children?[0]?.gotoAndPlay? animation
|
||||
|
||||
playSelectionSound: (hero) ->
|
||||
return if @$el.hasClass 'secret'
|
||||
|
@ -333,4 +325,5 @@ module.exports = class PlayHeroesModal extends ModalView
|
|||
for heroIndex, stage of @stages
|
||||
createjs.Ticker.removeEventListener "tick", stage
|
||||
stage.removeAllChildren()
|
||||
layer.destroy() for layer in @layers
|
||||
super()
|
||||
|
|
|
@ -7,6 +7,7 @@ BuyGemsModal = require 'views/play/modal/BuyGemsModal'
|
|||
CocoCollection = require 'collections/CocoCollection'
|
||||
ThangType = require 'models/ThangType'
|
||||
LevelComponent = require 'models/LevelComponent'
|
||||
Level = require 'models/Level'
|
||||
Purchase = require 'models/Purchase'
|
||||
|
||||
utils = require 'core/utils'
|
||||
|
@ -120,6 +121,9 @@ module.exports = class PlayItemsModal extends ModalView
|
|||
@itemDetailsView = new ItemDetailsView()
|
||||
@insertSubView(@itemDetailsView)
|
||||
@$el.find("a[href='#item-category-armor']").click() # Start on armor tab, if it's there.
|
||||
earnedLevels = me.get('earned')?.levels or []
|
||||
if Level.levels['defense-of-plainswood'] not in earnedLevels
|
||||
@$el.find('#misc-tab').hide()
|
||||
|
||||
onHidden: ->
|
||||
super()
|
||||
|
|
|
@ -18,17 +18,27 @@ today = today.toISOString().substr(0, 10);
|
|||
print("Today is " + today);
|
||||
|
||||
var todayMinus6 = new Date();
|
||||
todayMinus6.setDate(todayMinus6.getUTCDate() - 6);
|
||||
todayMinus6.setUTCDate(todayMinus6.getUTCDate() - 6);
|
||||
var startDate = todayMinus6.toISOString().substr(0, 10) + "T00:00:00.000Z";
|
||||
// startDate = "2014-12-31T00:00:00.000Z";
|
||||
print("Start date is " + startDate)
|
||||
// var endDate = "2015-01-06T00:00:00.000Z";
|
||||
// print("End date is " + endDate)
|
||||
|
||||
function objectIdWithTimestamp(timestamp)
|
||||
{
|
||||
// Convert string date to Date object (otherwise assume timestamp is a date)
|
||||
if (typeof(timestamp) == 'string') timestamp = new Date(timestamp);
|
||||
// Convert date object to hex seconds since Unix epoch
|
||||
var hexSeconds = Math.floor(timestamp/1000).toString(16);
|
||||
// Create an ObjectId with that hex timestamp
|
||||
var constructedObjectId = ObjectId(hexSeconds + "0000000000000000");
|
||||
return constructedObjectId
|
||||
}
|
||||
|
||||
var cursor = db['analytics.log.events'].find({
|
||||
$and: [
|
||||
{"created": { $gte: ISODate(startDate)}},
|
||||
// {"created": { $lt: ISODate(endDate)}},
|
||||
{_id: {$gte: objectIdWithTimestamp(ISODate(startDate))}},
|
||||
{$or: [ {"event" : 'Started Level'}, {"event" : 'Saw Victory'}]}
|
||||
]
|
||||
});
|
||||
|
|
207
scripts/analytics/mongodb/queries/insertPerDayAnalytics.js
Normal file
207
scripts/analytics/mongodb/queries/insertPerDayAnalytics.js
Normal file
|
@ -0,0 +1,207 @@
|
|||
// Insert per-day analytics into analytics.perdays collection
|
||||
|
||||
// Usage:
|
||||
// mongo <address>:<port>/<database> <script file> -u <username> -p <password>
|
||||
|
||||
// Completion rates (funnels) are calculated like Mixpanel
|
||||
// For a given date range, start count is the number of first steps (e.g. started a level)
|
||||
// Finish count for the same start date is how many unique users finished the remaining steps in the following ~30 days
|
||||
// https://mixpanel.com/help/questions/articles/how-are-funnels-calculated
|
||||
|
||||
// TODO: Why are Mixpanel level finish events significantly lower?
|
||||
// TODO: dungeons-of-kithgard completion rate is 62% vs. 77%
|
||||
// TODO: Similar start events, finish events off by 20% (5334 vs 6486)
|
||||
// TODO: Are Mixpanel rates accounting for finishing steps likely to be completed in the future?
|
||||
// TODO: Use Mixpanel export API to investigate
|
||||
|
||||
// TODO: Output documents updated/inserted
|
||||
|
||||
var scriptStartTime = new Date();
|
||||
var analyticsStringCache = {};
|
||||
|
||||
function log(str) {
|
||||
print(new Date().toISOString() + " " + str);
|
||||
}
|
||||
|
||||
function objectIdWithTimestamp(timestamp) {
|
||||
// Convert string date to Date object (otherwise assume timestamp is a date)
|
||||
if (typeof(timestamp) == 'string') timestamp = new Date(timestamp);
|
||||
// Convert date object to hex seconds since Unix epoch
|
||||
var hexSeconds = Math.floor(timestamp/1000).toString(16);
|
||||
// Create an ObjectId with that hex timestamp
|
||||
var constructedObjectId = ObjectId(hexSeconds + "0000000000000000");
|
||||
return constructedObjectId
|
||||
}
|
||||
|
||||
function getAnalyticsString(str) {
|
||||
if (analyticsStringCache[str]) return analyticsStringCache[str];
|
||||
|
||||
// Find existing string
|
||||
var doc = db['analytics.strings'].findOne({v: str});
|
||||
if (doc) {
|
||||
analyticsStringCache[str] = doc._id;
|
||||
return analyticsStringCache[str];
|
||||
}
|
||||
|
||||
// Insert string
|
||||
// http://docs.mongodb.org/manual/tutorial/create-an-auto-incrementing-field/#auto-increment-optimistic-loop
|
||||
doc = {v: str};
|
||||
while (true) {
|
||||
var cursor = db['analytics.strings'].find({}, {_id: 1}).sort({_id: -1}).limit(1);
|
||||
var seq = cursor.hasNext() ? cursor.next()._id + 1 : 1;
|
||||
doc._id = seq;
|
||||
var results = db['analytics.strings'].insert(doc);
|
||||
if (results.hasWriteError()) {
|
||||
if ( results.writeError.code == 11000 /* dup key */ ) continue;
|
||||
else throw new Error("ERROR: Unexpected error inserting data: " + tojson(results));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Find new string entry
|
||||
doc = db['analytics.strings'].findOne({v: str});
|
||||
if (doc) {
|
||||
analyticsStringCache[str] = doc._id;
|
||||
return analyticsStringCache[str];
|
||||
}
|
||||
throw new Error("ERROR: Did not find analytics.strings insert for: " + str);
|
||||
}
|
||||
|
||||
function getLevelFunnelData(startDay, eventFunnel) {
|
||||
if (!startDay || !eventFunnel || eventFunnel.length === 0) return {};
|
||||
|
||||
var startObj = objectIdWithTimestamp(ISODate(startDay + "T00:00:00.000Z"));
|
||||
var queryParams = {$and: [{_id: {$gte: startObj}},{"event": {$in: eventFunnel}}]};
|
||||
var cursor = db['analytics.log.events'].find(queryParams);
|
||||
|
||||
// Map ordering: level, user, event, created
|
||||
var userDataMap = {};
|
||||
while (cursor.hasNext()) {
|
||||
var doc = cursor.next();
|
||||
var created = doc._id.getTimestamp().toISOString().substring(0, 10);
|
||||
var event = doc.event;
|
||||
var properties = doc.properties;
|
||||
var user = doc.user;
|
||||
var level;
|
||||
|
||||
// TODO: Switch to properties.levelID for 'Saw Victory'
|
||||
if (event === 'Saw Victory' && properties.level) level = properties.level.toLowerCase().replace(/ /g, '-');
|
||||
else if (properties.levelID) level = properties.levelID
|
||||
else continue
|
||||
|
||||
if (!userDataMap[level]) userDataMap[level] = {};
|
||||
if (!userDataMap[level][user]) userDataMap[level][user] = {};
|
||||
if (!userDataMap[level][user][event] || userDataMap[level][user][event].localeCompare(created) > 0) {
|
||||
// if (userDataMap[level][user][event]) log("Found earlier date " + level + " " + event + " " + user + " " + userDataMap[level][user][event] + " " + created);
|
||||
userDataMap[level][user][event] = created;
|
||||
}
|
||||
}
|
||||
|
||||
// Data: level, created, event
|
||||
var levelFunnelData = {};
|
||||
for (level in userDataMap) {
|
||||
for (user in userDataMap[level]) {
|
||||
|
||||
// Find first event date
|
||||
var funnelStartDay = null;
|
||||
for (event in userDataMap[level][user]) {
|
||||
var created = userDataMap[level][user][event];
|
||||
if (!levelFunnelData[level]) levelFunnelData[level] = {};
|
||||
if (!levelFunnelData[level][created]) levelFunnelData[level][created] = {};
|
||||
if (!levelFunnelData[level][created][event]) levelFunnelData[level][created][event] = 0;
|
||||
if (eventFunnel[0] === event) {
|
||||
// First event gets attributed to current date
|
||||
levelFunnelData[level][created][event]++;
|
||||
funnelStartDay = created;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (funnelStartDay) {
|
||||
// Add remaining funnel steps/events to first step's date
|
||||
for (event in userDataMap[level][user]) {
|
||||
if (!levelFunnelData[level][funnelStartDay][event]) levelFunnelData[level][funnelStartDay][event] = 0;
|
||||
if (eventFunnel[0] != event) levelFunnelData[level][funnelStartDay][event]++;
|
||||
}
|
||||
// Zero remaining funnel events
|
||||
for (var i = 1; i < eventFunnel.length; i++) {
|
||||
var event = eventFunnel[i];
|
||||
if (!levelFunnelData[level][funnelStartDay][event]) levelFunnelData[level][funnelStartDay][event] = 0;
|
||||
}
|
||||
}
|
||||
// Else no start event in this date range
|
||||
}
|
||||
}
|
||||
return levelFunnelData;
|
||||
}
|
||||
|
||||
function insertEventCount(event, level, day, count) {
|
||||
// analytics.perdays schema in server/analytics/AnalyticsPeryDay.coffee
|
||||
day = day.replace(/-/g, '');
|
||||
|
||||
var eventID = getAnalyticsString(event);
|
||||
var levelID = getAnalyticsString(level);
|
||||
var filterID = getAnalyticsString('all');
|
||||
|
||||
var startObj = objectIdWithTimestamp(ISODate(startDay + "T00:00:00.000Z"));
|
||||
var queryParams = {$and: [{d: day}, {e: eventID}, {l: levelID}, {f: filterID}]};
|
||||
var doc = db['analytics.perdays'].findOne(queryParams);
|
||||
if (doc && doc.c === count) return;
|
||||
|
||||
if (doc && doc.c !== count) {
|
||||
// Update existing count, assume new one is more accurate
|
||||
// log("Updating count in db for " + day + " " + event + " " + level + " " + doc.c + " => " + count);
|
||||
var results = db['analytics.perdays'].update(queryParams, {$set: {c: count}});
|
||||
if (results.nMatched !== 1 && results.nModified !== 1) {
|
||||
log("ERROR: update count failed");
|
||||
printjson(results);
|
||||
}
|
||||
}
|
||||
else {
|
||||
var insertDoc = {d: day, e: eventID, l: levelID, f: filterID, c: count};
|
||||
var results = db['analytics.perdays'].insert(insertDoc);
|
||||
if (results.nInserted !== 1) {
|
||||
log("ERROR: insert event failed");
|
||||
printjson(results);
|
||||
printjson(insertDoc);
|
||||
}
|
||||
// else {
|
||||
// log("Added " + day + " " + event + " " + count + " " + level);
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Look at last 30 days, same as Mixpanel
|
||||
var numDays = 30;
|
||||
|
||||
var startDay = new Date();
|
||||
today = startDay.toISOString().substr(0, 10);
|
||||
startDay.setUTCDate(startDay.getUTCDate() - numDays);
|
||||
startDay = startDay.toISOString().substr(0, 10);
|
||||
|
||||
var levelCompletionFunnel = ['Started Level', 'Saw Victory'];
|
||||
|
||||
log("Today is " + today);
|
||||
log("Start day is " + startDay);
|
||||
log("Funnel events are " + levelCompletionFunnel);
|
||||
|
||||
log("Getting level completion data...");
|
||||
var levelCompletionData = getLevelFunnelData(startDay, levelCompletionFunnel);
|
||||
|
||||
log("Inserting aggregated level completion data...");
|
||||
for (level in levelCompletionData) {
|
||||
for (created in levelCompletionData[level]) {
|
||||
if (today === created) continue; // Never save data for today because it's incomplete
|
||||
for (event in levelCompletionData[level][created]) {
|
||||
insertEventCount(event, level, created, levelCompletionData[level][created][event]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(err) {
|
||||
log("ERROR: " + err);
|
||||
printjson(err);
|
||||
}
|
||||
|
||||
log("Script runtime: " + (new Date() - scriptStartTime));
|
|
@ -21,19 +21,28 @@ today = today.toISOString().substr(0, 10);
|
|||
print("Today is " + today);
|
||||
|
||||
var todayMinus6 = new Date();
|
||||
todayMinus6.setDate(todayMinus6.getUTCDate() - 6);
|
||||
todayMinus6.setUTCDate(todayMinus6.getUTCDate() - 6);
|
||||
var startDate = todayMinus6.toISOString().substr(0, 10) + "T00:00:00.000Z";
|
||||
print("Start date is " + startDate)
|
||||
// startDate = "2015-01-02T00:00:00.000Z";
|
||||
// var endDate = "2015-01-09T00:00:00.000Z";
|
||||
|
||||
function objectIdWithTimestamp(timestamp)
|
||||
{
|
||||
// Convert string date to Date object (otherwise assume timestamp is a date)
|
||||
if (typeof(timestamp) == 'string') timestamp = new Date(timestamp);
|
||||
// Convert date object to hex seconds since Unix epoch
|
||||
var hexSeconds = Math.floor(timestamp/1000).toString(16);
|
||||
// Create an ObjectId with that hex timestamp
|
||||
var constructedObjectId = ObjectId(hexSeconds + "0000000000000000");
|
||||
return constructedObjectId
|
||||
}
|
||||
|
||||
function getCompletionRates() {
|
||||
print("Getting completion rates...");
|
||||
var queryParams = {
|
||||
$and: [
|
||||
{"created": { $gte: ISODate(startDate)}},
|
||||
// {"created": { $lt: ISODate(endDate)}},
|
||||
// {$or: [ {"properties.level": {$exists: true}}, {"properties.levelID": {$exists: true}}]},
|
||||
{_id: {$gte: objectIdWithTimestamp(ISODate(startDate))}},
|
||||
{$or: [ {"event" : 'Started Level'}, {"event" : 'Saw Victory'}]}
|
||||
]
|
||||
};
|
||||
|
|
|
@ -80,9 +80,103 @@ class SetupFactory(object):
|
|||
class MacSetup(SetupFactory):
|
||||
def setup(self):
|
||||
super(self.__class__, self).setup()
|
||||
|
||||
class WinSetup(SetupFactory):
|
||||
def setup(self):
|
||||
super(self.__class__, self).setup()
|
||||
|
||||
class LinuxSetup(SetupFactory):
|
||||
def setup(self):
|
||||
self.distroSetup()
|
||||
super(self.__class__, self).setup()
|
||||
|
||||
def detectDistro(self):
|
||||
distro_checks = {
|
||||
"arch": "/etc/arch-release",
|
||||
"ubuntu": "/etc/lsb-release"
|
||||
}
|
||||
for distro, path in distro_checks.items():
|
||||
if os.path.exists(path):
|
||||
return(distro)
|
||||
|
||||
def distroSetup(self):
|
||||
distro = self.detectDistro()
|
||||
if distro == "arch":
|
||||
print("Arch Linux detected. Would you like to install \n"
|
||||
"NodeJS and MongoDB via pacman? [y/N]")
|
||||
if raw_input().lower() in ["y", "yes"]:
|
||||
try:
|
||||
subprocess.check_call(["pacman", "-S",
|
||||
"nodejs", "mongodb",
|
||||
"--noconfirm"])
|
||||
except subprocess.CalledProcessError as err:
|
||||
print("Installation failed. Retry, Continue, or "
|
||||
"Abort? [r/c/A]")
|
||||
answer = raw_input().lower()
|
||||
if answer in ["r", "retry"]:
|
||||
return(self.distroSetup())
|
||||
elif answer in ["c", "continue"]:
|
||||
return()
|
||||
else:
|
||||
exit(1)
|
||||
else:
|
||||
#try:
|
||||
#print("Enabling and starting MongoDB in systemd.")
|
||||
#subprocess.check_call(["systemctl", "enable",
|
||||
# "mongodb.service"])
|
||||
#subprocess.check_call(["systemctl", "start",
|
||||
# "mongodb.service"])
|
||||
#print("Node and Mongo installed. Continuing.")
|
||||
#except subprocess.CalledProcessError as err:
|
||||
#print("Mongo failed to start. Aborting")
|
||||
#exit(1)
|
||||
if distro == "ubuntu":
|
||||
print("Ubuntu installation detected. Would you like to install \n"
|
||||
"NodeJS and MongoDB via apt-get? [y/N]")
|
||||
if raw_input().lower() in ["y", "yes"]:
|
||||
print("Adding repositories for MongoDB and NodeJS...")
|
||||
try:
|
||||
subprocess.check_call(["apt-key", "adv",
|
||||
"--keyserver",
|
||||
"hkp://keyserver.ubuntu.com:80",
|
||||
"--recv", "7F0CEB10"])
|
||||
subprocess.check_call(["add-apt-repository",
|
||||
"deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen"])
|
||||
subprocess.check_call("curl -sL "
|
||||
"https://deb.nodesource.com/setup"
|
||||
" | bash", shell=True)
|
||||
subprocess.check_call(["apt-get", "update"])
|
||||
except subprocess.CalledProcessError as err:
|
||||
print("Adding repositories failed. Retry, Install without"
|
||||
"adding \nrepositories, Skip apt-get installation, "
|
||||
"or Abort? [r/i/s/A]")
|
||||
answer = raw_input().lower()
|
||||
if answer in ["r", "retry"]:
|
||||
return(self.distroSetup())
|
||||
elif answer in ["i", "install"]:
|
||||
pass
|
||||
elif answer in ["s", "skip"]:
|
||||
return()
|
||||
else:
|
||||
exit(1)
|
||||
else:
|
||||
try:
|
||||
print("Repositories added successfully. Installing NodeJS and MongoDB.")
|
||||
subprocess.check_call(["apt-get", "install",
|
||||
"nodejs", "mongodb-org",
|
||||
"build-essential", "-y"])
|
||||
except subprocess.CalledProcessError as err:
|
||||
print("Installation via apt-get failed. \nContinue "
|
||||
"with manual installation, or Abort? [c/A]")
|
||||
if raw_input().lower() in ["c", "continue"]:
|
||||
return()
|
||||
else:
|
||||
exit(1)
|
||||
else:
|
||||
print("NodeJS and MongoDB installed successfully. "
|
||||
"Staring MongoDB.")
|
||||
#try:
|
||||
#subprocess.check_call(["service", "mongod", "start"])
|
||||
#except subprocess.CalledProcessError as err:
|
||||
#print("Mongo failed to start. Aborting.")
|
||||
#exit(1)
|
||||
|
|
|
@ -4,6 +4,7 @@ import configuration
|
|||
import errors
|
||||
import subprocess
|
||||
import os
|
||||
import sys
|
||||
from which import which
|
||||
#git clone https://github.com/nwinter/codecombat.git coco
|
||||
class RepositoryInstaller():
|
||||
|
@ -64,7 +65,14 @@ class RepositoryInstaller():
|
|||
#TODO: "Replace npm with more robust package
|
||||
#npm_location = self.config.directory.bin_directory + os.sep + "node" + os.sep + "bin" + os.sep + "npm"
|
||||
npm_location = u"npm"
|
||||
return_code = subprocess.call([npm_location,u"install"],cwd=self.config.directory.root_dir + os.sep + u"coco")
|
||||
if sys.version_info[0] == 2:
|
||||
py_cmd = "python"
|
||||
else:
|
||||
py_cmd = subprocess.check_output(['which', 'python2'])
|
||||
return_code = subprocess.call([npm_location, u"install",
|
||||
"--python=" + py_cmd],
|
||||
cwd=self.config.directory.root_dir +
|
||||
os.sep + u"coco")
|
||||
if return_code:
|
||||
raise errors.CoCoError(u"Failed to install node packages")
|
||||
else:
|
||||
|
|
|
@ -2,10 +2,15 @@ mongoose = require 'mongoose'
|
|||
plugins = require '../plugins/plugins'
|
||||
|
||||
AnalyticsLogEventSchema = new mongoose.Schema({
|
||||
created:
|
||||
type: Date
|
||||
'default': Date.now
|
||||
u: mongoose.Schema.Types.ObjectId
|
||||
e: Number # event analytics.string ID
|
||||
p: mongoose.Schema.Types.Mixed
|
||||
|
||||
# TODO: Remove these legacy properties after we stop querying for them (probably 30 days, ~2/16/15)
|
||||
user: mongoose.Schema.Types.ObjectId
|
||||
event: String
|
||||
properties: mongoose.Schema.Types.Mixed
|
||||
}, {strict: false})
|
||||
AnalyticsLogEventSchema.index({event: 1, created: -1})
|
||||
AnalyticsLogEventSchema.index({event: 1, _id: 1})
|
||||
|
||||
module.exports = AnalyticsLogEvent = mongoose.model('analytics.log.event', AnalyticsLogEventSchema)
|
||||
|
|
13
server/analytics/AnalyticsPerDay.coffee
Normal file
13
server/analytics/AnalyticsPerDay.coffee
Normal file
|
@ -0,0 +1,13 @@
|
|||
mongoose = require 'mongoose'
|
||||
|
||||
AnalyticsPerDaySchema = new mongoose.Schema({
|
||||
d: {type: String} # yyyymmdd day, e.g. '20150123'
|
||||
e: {type: Number} # event (analytics string ID from analytics.strings)
|
||||
l: {type: Number} # level (analytics string ID from analytics.strings)
|
||||
f: {type: Number} # filter (analytics string ID from analytics.strings)
|
||||
fv: {type: Number} # filter value (analytics string ID from analytics.strings)
|
||||
c: {type: Number} # count
|
||||
}, {strict: false})
|
||||
|
||||
# TODO: Why can't we query against a collection with caps, like 'analytics.perDay'?
|
||||
module.exports = AnalyticsPerDay = mongoose.model('analytics.perday', AnalyticsPerDaySchema)
|
12
server/analytics/AnalyticsString.coffee
Normal file
12
server/analytics/AnalyticsString.coffee
Normal file
|
@ -0,0 +1,12 @@
|
|||
mongoose = require 'mongoose'
|
||||
|
||||
# Auto-incrementing number _id
|
||||
# http://docs.mongodb.org/manual/tutorial/create-an-auto-incrementing-field/#auto-increment-optimistic-loop
|
||||
# TODO: Why strict:false?
|
||||
|
||||
AnalyticsStringSchema = new mongoose.Schema({
|
||||
_id: {type: Number}
|
||||
v: {type: String}
|
||||
}, {strict: false})
|
||||
|
||||
module.exports = AnalyticsString = mongoose.model('analytics.string', AnalyticsStringSchema)
|
|
@ -1,13 +1,17 @@
|
|||
log = require 'winston'
|
||||
mongoose = require 'mongoose'
|
||||
utils = require '../lib/utils'
|
||||
AnalyticsLogEvent = require './AnalyticsLogEvent'
|
||||
Campaign = require '../campaigns/Campaign'
|
||||
Level = require '../levels/Level'
|
||||
Handler = require '../commons/Handler'
|
||||
log = require 'winston'
|
||||
|
||||
class AnalyticsLogEventHandler extends Handler
|
||||
modelClass: AnalyticsLogEvent
|
||||
jsonSchema: require '../../app/schemas/models/analytics_log_event'
|
||||
editableProperties: [
|
||||
'e'
|
||||
'p'
|
||||
'event'
|
||||
'properties'
|
||||
]
|
||||
|
@ -17,14 +21,118 @@ class AnalyticsLogEventHandler extends Handler
|
|||
|
||||
makeNewInstance: (req) ->
|
||||
instance = super(req)
|
||||
instance.set('u', req.user._id)
|
||||
# TODO: Remove 'user' after we stop querying for it (probably 30 days, ~2/16/15)
|
||||
instance.set('user', req.user._id)
|
||||
instance
|
||||
|
||||
getByRelationship: (req, res, args...) ->
|
||||
return @getLevelCompletionsBySlug(req, res) if args[1] is 'level_completions'
|
||||
return @getCampaignCompletionsBySlug(req, res) if args[1] is 'campaign_completions'
|
||||
return @logEvent(req, res) if args[1] is 'log_event'
|
||||
# TODO: Remove these APIs
|
||||
# return @getLevelCompletionsBySlug(req, res) if args[1] is 'level_completions'
|
||||
# return @getCampaignCompletionsBySlug(req, res) if args[1] is 'campaign_completions'
|
||||
super(arguments...)
|
||||
|
||||
logEvent: (req, res) ->
|
||||
# Converts strings to string IDs where possible, and logs the event
|
||||
user = req.user._id
|
||||
event = req.query.event or req.body.event
|
||||
properties = req.query.properties or req.body.properties
|
||||
@sendSuccess res # Return request immediately
|
||||
|
||||
saveDoc = (eventID, slimProperties) ->
|
||||
doc = new AnalyticsLogEvent
|
||||
u: user
|
||||
e: eventID
|
||||
p: slimProperties
|
||||
# TODO: Remove these legacy properties after we stop querying for them (probably 30 days, ~2/16/15)
|
||||
user: user
|
||||
event: event
|
||||
properties: properties
|
||||
doc.save()
|
||||
|
||||
utils.getAnalyticsStringID event, (eventID) ->
|
||||
if eventID > 0
|
||||
# TODO: properties slimming is pretty ugly
|
||||
slimProperties = _.cloneDeep properties
|
||||
if event in ['Clicked Level', 'Show problem alert', 'Started Level', 'Saw Victory', 'Problem alert help clicked', 'Spell palette help clicked']
|
||||
delete slimProperties.level if event is 'Saw Victory'
|
||||
properties.ls = mongoose.Types.ObjectId properties.ls if properties.ls
|
||||
slimProperties.ls = mongoose.Types.ObjectId slimProperties.ls if slimProperties.ls
|
||||
if slimProperties.levelID?
|
||||
# levelID: string => l: string ID
|
||||
utils.getAnalyticsStringID slimProperties.levelID, (levelStringID) ->
|
||||
if levelStringID > 0
|
||||
delete slimProperties.levelID
|
||||
slimProperties.l = levelStringID
|
||||
saveDoc eventID, slimProperties
|
||||
return
|
||||
else if event in ['Script Started', 'Script Ended']
|
||||
properties.ls = mongoose.Types.ObjectId properties.ls if properties.ls
|
||||
slimProperties.ls = mongoose.Types.ObjectId slimProperties.ls if slimProperties.ls
|
||||
if slimProperties.levelID? and slimProperties.label?
|
||||
# levelID: string => l: string ID
|
||||
# label: string => lb: string ID
|
||||
utils.getAnalyticsStringID slimProperties.levelID, (levelStringID) ->
|
||||
if levelStringID > 0
|
||||
delete slimProperties.levelID
|
||||
slimProperties.l = levelStringID
|
||||
utils.getAnalyticsStringID slimProperties.label, (labelStringID) ->
|
||||
if labelStringID > 0
|
||||
delete slimProperties.label
|
||||
slimProperties.lb = labelStringID
|
||||
saveDoc eventID, slimProperties
|
||||
return
|
||||
else if event is 'Heard Sprite'
|
||||
properties.ls = mongoose.Types.ObjectId properties.ls if properties.ls
|
||||
slimProperties.ls = mongoose.Types.ObjectId slimProperties.ls if slimProperties.ls
|
||||
if slimProperties.message?
|
||||
# message: string => m: string ID
|
||||
utils.getAnalyticsStringID slimProperties.message, (messageStringID) ->
|
||||
if messageStringID > 0
|
||||
delete slimProperties.message
|
||||
slimProperties.m = messageStringID
|
||||
saveDoc eventID, slimProperties
|
||||
return
|
||||
else if event in ['Start help video', 'Finish help video']
|
||||
properties.ls = mongoose.Types.ObjectId properties.ls if properties.ls
|
||||
slimProperties.ls = mongoose.Types.ObjectId slimProperties.ls if slimProperties.ls
|
||||
if slimProperties.level and slimProperties.style?
|
||||
# level: string => l: string ID
|
||||
# style: string => s: string ID
|
||||
utils.getAnalyticsStringID slimProperties.level, (levelStringID) ->
|
||||
if levelStringID > 0
|
||||
delete slimProperties.level
|
||||
slimProperties.l = levelStringID
|
||||
utils.getAnalyticsStringID slimProperties.style, (styleStringID) ->
|
||||
if styleStringID > 0
|
||||
delete slimProperties.style
|
||||
slimProperties.s = styleStringID
|
||||
saveDoc eventID, slimProperties
|
||||
return
|
||||
else if event is 'Show subscription modal'
|
||||
delete properties.category
|
||||
delete slimProperties.category
|
||||
if slimProperties.label?
|
||||
# label: string => lb: string ID
|
||||
utils.getAnalyticsStringID slimProperties.label, (labelStringID) ->
|
||||
if labelStringID > 0
|
||||
delete slimProperties.label
|
||||
slimProperties.lb = labelStringID
|
||||
if slimProperties.level?
|
||||
# level: string => l: string ID
|
||||
utils.getAnalyticsStringID slimProperties.level, (levelStringID) ->
|
||||
if levelStringID > 0
|
||||
delete slimProperties.level
|
||||
slimProperties.l = levelStringID
|
||||
saveDoc eventID, slimProperties
|
||||
return
|
||||
saveDoc eventID, slimProperties
|
||||
return
|
||||
saveDoc eventID, slimProperties
|
||||
else
|
||||
log.warn "Unable to get analytics string ID for " + event
|
||||
|
||||
getLevelCompletionsBySlug: (req, res) ->
|
||||
# Returns an array of per-day level starts and finishes
|
||||
# Parameters:
|
||||
|
@ -60,8 +168,8 @@ class AnalyticsLogEventHandler extends Handler
|
|||
queryParams = {$and: [
|
||||
{$or: [{"event" : 'Started Level'}, {"event" : 'Saw Victory'}]}
|
||||
]}
|
||||
queryParams["$and"].push created: {$gte: new Date(startDay + "T00:00:00.000Z")} if startDay?
|
||||
queryParams["$and"].push created: {$lt: new Date(endDay + "T00:00:00.000Z")} if endDay?
|
||||
queryParams["$and"].push _id: {$gte: utils.objectIdFromTimestamp(startDay + "T00:00:00.000Z")} if startDay?
|
||||
queryParams["$and"].push _id: {$lt: utils.objectIdFromTimestamp(endDay + "T00:00:00.000Z")} if endDay?
|
||||
|
||||
# Query stream is better for large results
|
||||
# http://mongoosejs.com/docs/api.html#query_Query-stream
|
||||
|
@ -221,8 +329,8 @@ class AnalyticsLogEventHandler extends Handler
|
|||
userProgression = {}
|
||||
|
||||
queryParams = {$and: [{$or: [ {"event" : 'Started Level'}, {"event" : 'Saw Victory'}]}]}
|
||||
queryParams["$and"].push created: {$gte: new Date(startDay + "T00:00:00.000Z")} if startDay?
|
||||
queryParams["$and"].push created: {$lt: new Date(endDay + "T00:00:00.000Z")} if endDay?
|
||||
queryParams["$and"].push _id: {$gte: utils.objectIdFromTimestamp(startDay + "T00:00:00.000Z")} if startDay?
|
||||
queryParams["$and"].push _id: {$lt: utils.objectIdFromTimestamp(endDay + "T00:00:00.000Z")} if endDay?
|
||||
|
||||
# Query stream is better for large results
|
||||
# http://mongoosejs.com/docs/api.html#query_Query-stream
|
||||
|
|
193
server/analytics/analytics_perday_handler.coffee
Normal file
193
server/analytics/analytics_perday_handler.coffee
Normal file
|
@ -0,0 +1,193 @@
|
|||
AnalyticsPerDay = require './AnalyticsPerDay'
|
||||
AnalyticsString = require './AnalyticsString'
|
||||
Campaign = require '../campaigns/Campaign'
|
||||
Level = require '../levels/Level'
|
||||
Handler = require '../commons/Handler'
|
||||
log = require 'winston'
|
||||
|
||||
class AnalyticsPerDayHandler extends Handler
|
||||
modelClass: AnalyticsPerDay
|
||||
jsonSchema: require '../../app/schemas/models/analytics_perday'
|
||||
|
||||
hasAccess: (req) ->
|
||||
req.method in ['GET'] or req.user?.isAdmin()
|
||||
|
||||
getByRelationship: (req, res, args...) ->
|
||||
return @getCampaignCompletionsBySlug(req, res) if args[1] is 'campaign_completions'
|
||||
return @getLevelCompletionsBySlug(req, res) if args[1] is 'level_completions'
|
||||
super(arguments...)
|
||||
|
||||
getCampaignCompletionsBySlug: (req, res) ->
|
||||
# Send back an array of level starts and finishes
|
||||
# Parameters:
|
||||
# slug - campaign slug
|
||||
# startDay - Inclusive, optional, YYYYMMDD e.g. '20141214'
|
||||
# endDay - Exclusive, optional, YYYYMMDD e.g. '20141216'
|
||||
|
||||
campaignSlug = req.query.slug or req.body.slug
|
||||
startDay = req.query.startDay or req.body.startDay
|
||||
endDay = req.query.endDay or req.body.endDay
|
||||
|
||||
# log.warn "campaign_completions campaignSlug='#{campaignSlug}' startDay=#{startDay} endDay=#{endDay}"
|
||||
|
||||
return @sendSuccess res, [] unless campaignSlug?
|
||||
|
||||
# Cache results in app server memory for 1 day
|
||||
@campaignCompletionsCache ?= {}
|
||||
@campaignCompletionsCachedSince ?= new Date()
|
||||
if (new Date()) - @campaignCompletionsCachedSince > 86400 * 1000
|
||||
@campaignCompletionsCache = {}
|
||||
@campaignCompletionsCachedSince = new Date()
|
||||
cacheKey = campaignSlug
|
||||
cacheKey += 's' + startDay if startDay?
|
||||
cacheKey += 'e' + endDay if endDay?
|
||||
return @sendSuccess res, campaignDropOffs if campaignDropOffs = @campaignCompletionsCache[cacheKey]
|
||||
|
||||
getCompletions = (orderedLevelSlugs, levelStringIDSlugMap) =>
|
||||
# 3. Send back an array of level starts and finishes
|
||||
# Input:
|
||||
# orderedLevelSlugs - Ordered list of level slugs, used for sorting results
|
||||
# levelStringIDSlugMap - Maps level string IDs to level slugs
|
||||
|
||||
campaignLevelIDs = Object.keys(levelStringIDSlugMap)
|
||||
|
||||
AnalyticsString.find({v: {$in: ['Started Level', 'Saw Victory', 'all']}}).exec (err, documents) =>
|
||||
if err? then return @sendDatabaseError res, err
|
||||
|
||||
for doc in documents
|
||||
startEventID = doc._id if doc.v is 'Started Level'
|
||||
finishEventID = doc._id if doc.v is 'Saw Victory'
|
||||
filterEventID = doc._id if doc.v is 'all'
|
||||
return @sendSuccess res, [] unless startEventID? and finishEventID? and filterEventID?
|
||||
|
||||
queryParams = {$and: [
|
||||
{$or: [{e: startEventID}, {e: finishEventID}]},
|
||||
{f: filterEventID},
|
||||
{l: {$in: campaignLevelIDs}}
|
||||
]}
|
||||
queryParams["$and"].push {d: {$gte: startDay}} if startDay?
|
||||
queryParams["$and"].push {d: {$lt: endDay}} if endDay?
|
||||
AnalyticsPerDay.find(queryParams).exec (err, documents) =>
|
||||
if err? then return @sendDatabaseError res, err
|
||||
|
||||
levelEventCounts = {}
|
||||
for doc in documents
|
||||
levelEventCounts[doc.l] ?= {}
|
||||
levelEventCounts[doc.l][doc.e] ?= 0
|
||||
levelEventCounts[doc.l][doc.e] += doc.c
|
||||
|
||||
completions = []
|
||||
for levelID of levelEventCounts
|
||||
completions.push
|
||||
level: levelStringIDSlugMap[levelID]
|
||||
started: levelEventCounts[levelID][startEventID]
|
||||
finished: levelEventCounts[levelID][finishEventID]
|
||||
completions.sort (a, b) -> orderedLevelSlugs.indexOf(a.level) - orderedLevelSlugs.indexOf(b.level)
|
||||
|
||||
@campaignCompletionsCache[cacheKey] = completions
|
||||
@sendSuccess res, completions
|
||||
|
||||
getLevelData = (campaignLevels) =>
|
||||
# 2. Get ordered level slugs and string ID to level slug mappping
|
||||
# Input:
|
||||
# campaignLevels - array of Level IDs
|
||||
|
||||
queryParams = {original: {$in: campaignLevels}, "version.isLatestMajor": true, "version.isLatestMinor": true}
|
||||
Level.find(queryParams).exec (err, documents) =>
|
||||
if err? then return @sendDatabaseError res, err
|
||||
|
||||
# Save original level ID and slug in array for sorting
|
||||
campaignOriginalSlugs = []
|
||||
for doc in documents
|
||||
campaignOriginalSlugs.push
|
||||
slug: doc.get('name').toLowerCase().replace new RegExp(' ', 'g'), '-'
|
||||
original: doc.get('original').toString()
|
||||
|
||||
# Sort slugs against original levels from campaign
|
||||
campaignOriginalSlugs.sort (a, b) ->
|
||||
if campaignLevels.indexOf(a.original) < campaignLevels.indexOf(b.original) then -1 else 1
|
||||
|
||||
# Lookup analytics string IDs for level slugs
|
||||
orderedLevelSlugs = []
|
||||
orderedLevelSlugs.push item.slug for item in campaignOriginalSlugs
|
||||
AnalyticsString.find({v: {$in: orderedLevelSlugs}}).exec (err, documents) =>
|
||||
if err? then return @sendDatabaseError res, err
|
||||
|
||||
levelStringIDSlugMap = {}
|
||||
levelStringIDSlugMap[doc._id] = doc.v for doc in documents
|
||||
getCompletions orderedLevelSlugs, levelStringIDSlugMap
|
||||
|
||||
# 1. Get campaign levels
|
||||
Campaign.find({slug: campaignSlug}).exec (err, documents) =>
|
||||
if err? then return @sendDatabaseError res, err
|
||||
campaignLevels = []
|
||||
campaignLevels.push level for level of doc.get('levels') for doc in documents
|
||||
getLevelData campaignLevels
|
||||
|
||||
getLevelCompletionsBySlug: (req, res) ->
|
||||
# Returns an array of per-day starts and finishes for given level
|
||||
# Parameters:
|
||||
# slug - level slug
|
||||
# startDay - Inclusive, optional, YYYYMMDD e.g. '20141214'
|
||||
# endDay - Exclusive, optional, YYYYMMDD e.g. '20141216'
|
||||
|
||||
# TODO: Code is similar to getCampaignCompletionsBySlug
|
||||
|
||||
levelSlug = req.query.slug or req.body.slug
|
||||
startDay = req.query.startDay or req.body.startDay
|
||||
endDay = req.query.endDay or req.body.endDay
|
||||
|
||||
return @sendSuccess res, [] unless levelSlug?
|
||||
|
||||
# log.warn "level_completions levelSlug='#{levelSlug}' startDay=#{startDay} endDay=#{endDay}"
|
||||
|
||||
# Cache results in app server memory for 1 day
|
||||
@levelCompletionsCache ?= {}
|
||||
@levelCompletionsCachedSince ?= new Date()
|
||||
if (new Date()) - @levelCompletionsCachedSince > 86400 * 1000
|
||||
@levelCompletionsCache = {}
|
||||
@levelCompletionsCachedSince = new Date()
|
||||
cacheKey = levelSlug
|
||||
cacheKey += 's' + startDay if startDay?
|
||||
cacheKey += 'e' + endDay if endDay?
|
||||
return @sendSuccess res, levelCompletions if levelCompletions = @levelCompletionsCache[cacheKey]
|
||||
|
||||
AnalyticsString.find({v: {$in: ['Started Level', 'Saw Victory', 'all', levelSlug]}}).exec (err, documents) =>
|
||||
if err? then return @sendDatabaseError res, err
|
||||
|
||||
for doc in documents
|
||||
startEventID = doc._id if doc.v is 'Started Level'
|
||||
finishEventID = doc._id if doc.v is 'Saw Victory'
|
||||
filterEventID = doc._id if doc.v is 'all'
|
||||
levelID = doc._id if doc.v is levelSlug
|
||||
return @sendSuccess res, [] unless startEventID? and finishEventID? and filterEventID? and levelID?
|
||||
|
||||
queryParams = {$and: [{$or: [{e: startEventID}, {e: finishEventID}]},{f: filterEventID},{l: levelID}]}
|
||||
queryParams["$and"].push {d: {$gte: startDay}} if startDay?
|
||||
queryParams["$and"].push {d: {$lt: endDay}} if endDay?
|
||||
AnalyticsPerDay.find(queryParams).exec (err, documents) =>
|
||||
if err? then return @sendDatabaseError res, err
|
||||
|
||||
dayEventCounts = {}
|
||||
for doc in documents
|
||||
day = doc.get('d')
|
||||
eventID = doc.get('e')
|
||||
count = doc.get('c')
|
||||
dayEventCounts[day] ?= {}
|
||||
dayEventCounts[day][eventID] = count
|
||||
|
||||
completions = []
|
||||
for day of dayEventCounts
|
||||
for eventID of dayEventCounts[day]
|
||||
eventID = parseInt eventID
|
||||
started = dayEventCounts[day][eventID] if eventID is startEventID
|
||||
finished = dayEventCounts[day][eventID] if eventID is finishEventID
|
||||
completions.push
|
||||
created: day
|
||||
started: started
|
||||
finished: finished
|
||||
|
||||
@levelCompletionsCache[cacheKey] = completions
|
||||
@sendSuccess res, completions
|
||||
|
||||
module.exports = new AnalyticsPerDayHandler()
|
9
server/analytics/analytics_string_handler.coffee
Normal file
9
server/analytics/analytics_string_handler.coffee
Normal file
|
@ -0,0 +1,9 @@
|
|||
AnalyticsString = require './AnalyticsString'
|
||||
Handler = require '../commons/Handler'
|
||||
|
||||
class AnalyticsStringHandler extends Handler
|
||||
modelClass: AnalyticsString
|
||||
jsonSchema: require '../../app/schemas/models/analytics_string'
|
||||
hasAccess: (req) -> req.method in ['GET'] or req.user?.isAdmin()
|
||||
|
||||
module.exports = new AnalyticsStringHandler()
|
|
@ -1,5 +1,7 @@
|
|||
module.exports.handlers =
|
||||
'analytics_log_event': 'analytics/analytics_log_event_handler'
|
||||
'analytics_perday': 'analytics/analytics_perday_handler'
|
||||
'analytics_string': 'analytics/analytics_string_handler'
|
||||
# TODO: Disabling this until we know why our app servers CPU grows out of control.
|
||||
# 'analytics_users_active': 'analytics/analytics_users_active_handler'
|
||||
'article': 'articles/article_handler'
|
||||
|
|
|
@ -6,6 +6,7 @@ Feedback = require './feedbacks/LevelFeedback'
|
|||
Handler = require '../commons/Handler'
|
||||
mongoose = require 'mongoose'
|
||||
async = require 'async'
|
||||
utils = require '../lib/utils'
|
||||
|
||||
LevelHandler = class LevelHandler extends Handler
|
||||
modelClass: Level
|
||||
|
@ -375,9 +376,9 @@ LevelHandler = class LevelHandler extends Handler
|
|||
|
||||
# Build query
|
||||
match = {$match: {$and: [{"state.complete": true}, {"playtime": {$gt: 0}}, {levelID: {$in: levelSlugs}}]}}
|
||||
match["$match"]["$and"].push created: {$gte: new Date(startDay + "T00:00:00.000Z")} if startDay?
|
||||
match["$match"]["$and"].push created: {$lt: new Date(endDay + "T00:00:00.000Z")} if endDay?
|
||||
project = {"$project": {"_id": 0, "levelID": 1, "playtime": 1, "created": {"$concat": [{"$substr": ["$created", 0, 4]}, "-", {"$substr": ["$created", 5, 2]}, "-", {"$substr" : ["$created", 8, 2]}]}}}
|
||||
match["$match"]["$and"].push _id: {$gte: utils.objectIdFromTimestamp(startDay + "T00:00:00.000Z")} if startDay?
|
||||
match["$match"]["$and"].push _id: {$lt: utils.objectIdFromTimestamp(endDay + "T00:00:00.000Z")} if endDay?
|
||||
project = {"$project": {"_id": 0, "levelID": 1, "playtime": 1, "created": {"$concat": [{"$substr": ["$created", 0, 10]}]}}}
|
||||
group = {"$group": {"_id": {"created": "$created", "level": "$levelID"}, "average": {"$avg": "$playtime"}}}
|
||||
query = Session.aggregate match, project, group
|
||||
|
||||
|
|
|
@ -1,3 +1,39 @@
|
|||
AnalyticsString = require '../analytics/AnalyticsString'
|
||||
mongoose = require 'mongoose'
|
||||
|
||||
module.exports =
|
||||
isID: (id) -> _.isString(id) and id.length is 24 and id.match(/[a-f0-9]/gi)?.length is 24
|
||||
objectIdFromTimestamp: (timestamp) ->
|
||||
# mongoDB ObjectId contains creation date in first 4 bytes
|
||||
# So, it can be used instead of a redundant created field
|
||||
# http://docs.mongodb.org/manual/reference/object-id/
|
||||
# http://stackoverflow.com/questions/8749971/can-i-query-mongodb-objectid-by-date
|
||||
# Convert string date to Date object (otherwise assume timestamp is a date)
|
||||
timestamp = new Date(timestamp) if typeof(timestamp) == 'string'
|
||||
# Convert date object to hex seconds since Unix epoch
|
||||
hexSeconds = Math.floor(timestamp/1000).toString(16)
|
||||
# Create an ObjectId with that hex timestamp
|
||||
mongoose.Types.ObjectId(hexSeconds + "0000000000000000")
|
||||
getAnalyticsStringID: (str, callback) ->
|
||||
return callback -1 unless str?
|
||||
@analyticsStringCache ?= {}
|
||||
return callback @analyticsStringCache[str] if @analyticsStringCache[str]
|
||||
|
||||
insertString = =>
|
||||
# http://docs.mongodb.org/manual/tutorial/create-an-auto-incrementing-field/#auto-increment-optimistic-loop
|
||||
AnalyticsString.find({}, {_id: 1}).sort({_id: -1}).limit(1).exec (err, documents) =>
|
||||
if err? then return callback -1
|
||||
seq = if documents.length > 0 then documents[0]._id + 1 else 1
|
||||
doc = new AnalyticsString _id: seq, v: str
|
||||
doc.save (err) =>
|
||||
if err? then return callback -1
|
||||
@analyticsStringCache[str] = seq
|
||||
callback seq
|
||||
|
||||
# Find existing string
|
||||
AnalyticsString.findOne(v: str).exec (err, document) =>
|
||||
if err? then return callback -1
|
||||
if document
|
||||
@analyticsStringCache[str] = document._id
|
||||
return callback @analyticsStringCache[str]
|
||||
insertString()
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
UserCodeProblem = require './UserCodeProblem'
|
||||
Handler = require '../commons/Handler'
|
||||
utils = require '../lib/utils'
|
||||
|
||||
class UserCodeProblemHandler extends Handler
|
||||
modelClass: UserCodeProblem
|
||||
|
@ -53,8 +54,8 @@ class UserCodeProblemHandler extends Handler
|
|||
|
||||
# Build query
|
||||
match = if startDay? or endDay? then {$match: {$and: []}} else {$match: {}}
|
||||
match["$match"]["$and"].push created: {$gte: new Date(startDay + "T00:00:00.000Z")} if startDay?
|
||||
match["$match"]["$and"].push created: {$lt: new Date(endDay + "T00:00:00.000Z")} if endDay?
|
||||
match["$match"]["$and"].push _id: {$gte: utils.objectIdFromTimestamp(startDay + "T00:00:00.000Z")} if startDay?
|
||||
match["$match"]["$and"].push _id: {$lt: utils.objectIdFromTimestamp(endDay + "T00:00:00.000Z")} if endDay?
|
||||
group = {"$group": {"_id": {"errMessage": "$errMessageNoLineInfo", "errHint": "$errHint", "language": "$language", "levelID": "$levelID"}, "count": {"$sum": 1}}}
|
||||
sort = { $sort : { "_id.levelID": 1, count : -1, "_id.language": 1 } }
|
||||
query = UserCodeProblem.aggregate match, group, sort
|
||||
|
|
Loading…
Reference in a new issue