Merge branch 'master' into production

This commit is contained in:
Nick Winter 2014-02-06 17:31:46 -08:00
commit 837cc94f38
42 changed files with 144 additions and 438 deletions

View file

@ -20,7 +20,7 @@ elementAcceptsKeystrokes = (el) ->
# not radio, checkbox, range, or color
return (tag is 'textarea' or (tag is 'input' and type in textInputTypes) or el.contentEditable in ["", "true"]) and not (el.readOnly or el.disabled)
COMMON_FILES = ['/images/modal_background.png', '/images/level/code_palette_background.png']
COMMON_FILES = ['/images/pages/base/modal_background.png', '/images/level/code_palette_background.png']
preload = (arrayOfImages) ->
$(arrayOfImages).each ->
$('<img/>')[0].src = @

View file

@ -3,7 +3,7 @@
## Uncomment to imitate IE9 (and in world_utils.coffee)
#window.Worker = null
#window.Float32Array = null
# (except that we won't have included vendor_with_box2d.js, so Collision won't run, things won't move, etc.)
# Also uncomment vendor_with_box2d.js in index.html if you want Collision to run and things to move.
module.exports = class God
@ids: ['Athena', 'Baldr', 'Crom', 'Dagr', 'Eris', 'Freyja', 'Great Gish', 'Hades', 'Ishtar', 'Janus', 'Khronos', 'Loki', 'Marduk', 'Negafook', 'Odin', 'Poseidon', 'Quetzalcoatl', 'Ra', 'Shiva', 'Thor', 'Umvelinqangi', 'Týr', 'Vishnu', 'Wepwawet', 'Xipe Totec', 'Yahweh', 'Zeus', '上帝', 'Tiamat', '盘古', 'Phoebe', 'Artemis', 'Osiris', "嫦娥", 'Anhur', 'Teshub', 'Enlil', 'Perkele', 'Aether', 'Chaos', 'Hera', 'Iris', 'Theia', 'Uranus', 'Stribog', 'Sabazios', 'Izanagi', 'Ao', 'Tāwhirimātea', 'Tengri', 'Inmar', 'Torngarsuk', 'Centzonhuitznahua', 'Hunab Ku', 'Apollo', 'Helios', 'Thoth', 'Hyperion', 'Alectrona', 'Eos', 'Mitra', 'Saranyu', 'Freyr', 'Koyash', 'Atropos', 'Clotho', 'Lachesis', 'Tyche', 'Skuld', 'Urðr', 'Verðandi', 'Camaxtli', 'Huhetotl', 'Set', 'Anu', 'Allah', 'Anshar', 'Hermes', 'Lugh', 'Brigit', 'Manannan Mac Lir', 'Persephone', 'Mercury', 'Venus', 'Mars', 'Azrael', 'He-Man', 'Anansi', 'Issek', 'Mog', 'Kos', 'Amaterasu Omikami', 'Raijin', 'Susanowo', 'Blind Io', 'The Lady', 'Offler', 'Ptah', 'Anubis', 'Ereshkigal', 'Nergal', 'Thanatos', 'Macaria', 'Angelos', 'Erebus', 'Hecate', 'Hel', 'Orcus', 'Ishtar-Deela Nakh', 'Prometheus', 'Hephaestos', 'Sekhmet', 'Ares', 'Enyo', 'Otrera', 'Pele', 'Hadúr', 'Hachiman', 'Dayisun Tngri', 'Ullr', 'Lua', 'Minerva']
@ -25,17 +25,18 @@ module.exports = class God
@createWorld()
getAngel: ->
freeAngel = null
for angel in @angels
return angel.enslave() unless angel.busy
if angel.busy
angel.abort()
else
freeAngel ?= angel
return freeAngel.enslave() if freeAngel
maxedOut = @angels.length is @maxAngels
if not maxedOut
angel = new Angel @
@angels.push angel
return angel.enslave()
oldestAngel = {started: new Date(2099, 1, 1)}
for angel in @angels
oldestAngel = angel if angel.started < oldestAngel.started
oldestAngel.abort()
null
angelInfinitelyLooped: (angel) ->
@ -91,8 +92,11 @@ module.exports = class God
Backbone.Mediator.publish('god:new-world-created', world: @world, firstWorld: @firstWorld, errorCount: errorCount, goalStates: @latestGoalStates)
for scriptNote in @world.scriptNotes
Backbone.Mediator.publish scriptNote.channel, scriptNote.event
@goalManager?.world = newWorld
@firstWorld = false
@testWorld = null
unless _.find @angels, 'busy'
@spells = null # Don't hold onto old spells; memory leaks
getUserCodeMap: ->
userCodeMap = {}
@ -171,6 +175,7 @@ class Angel
@busy = true
@started = new Date()
@purgatoryTimer = setInterval @testWorker, @infiniteLoopIntervalDuration
@spawnWorker() unless @worker
@
free: ->
@ -178,6 +183,8 @@ class Angel
@started = null
clearInterval @purgatoryTimer
@purgatoryTimer = null
@worker?.terminate()
@worker = null
@
abort: ->
@ -185,9 +192,9 @@ class Angel
@worker.postMessage {func: 'abort'}
terminate: =>
@worker.terminate()
@worker?.terminate()
@worker = null
return if @dead
@spawnWorker()
@free()
@god.angelAborted @
@ -219,7 +226,9 @@ class Angel
clearTimeout @abortTimeout
@free()
@god.angelAborted @
@worker.terminate() if @god.dead
if @god.dead
@worker.terminate()
@worker = null
when 'reportIn'
clearTimeout @condemnTimeout
else

View file

@ -116,7 +116,8 @@ module.exports = class LevelLoader extends CocoClass
# World init
initWorld: ->
return if @world
return if @initialized
@initialized = true
@world = new World @level.get('name')
serializedLevel = @level.serialize(@supermodel)
@world.loadFromLevel serializedLevel, false
@ -203,9 +204,9 @@ module.exports = class LevelLoader extends CocoClass
notifyProgress: ->
Backbone.Mediator.publish 'level-loader:progress-changed', progress: @progress()
@initWorld() if @allDone()
# @trigger 'ready-to-init-world' if @allDone()
@trigger 'loaded-all' if @progress() is 1
destroy: ->
@world = null # don't hold onto garbage
@supermodel.off 'loaded-one', @onSupermodelLoadedOne
super()

View file

@ -26,6 +26,7 @@ module.exports = class Tracker
olark 'api.chat.updateVisitorNickname', snippet: me.displayName()
updatePlayState: (level, session) ->
return unless olark?
link = "codecombat.com/play/level/#{level.get('slug') or level.id}?session=#{session.id}"
snippet = [
"#{link}"

View file

@ -83,7 +83,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
toString: -> "<CocoSprite: #{@thang?.id}>"
buildSpriteSheet: ->
options = @thang?.getSpriteOptions?() or {}
options = @thang?.getSpriteOptions?() or @options
options.colorConfig = @options.colorConfig if @options.colorConfig
options.async = false
@thangType.getSpriteSheet options
@ -131,6 +131,8 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
playAction: (action) ->
@currentAction = action
return @hide() unless action.animation or action.container or action.relatedActions
@show()
return @updateActionDirection() unless action.animation or action.container
m = if action.container then "gotoAndStop" else "gotoAndPlay"
@imageObject[m] action.name
@ -142,6 +144,14 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
start = Math.floor(Math.random() * action.frames.length)
@imageObject.currentAnimationFrame = start
hide: ->
@hiding = true
@updateAlpha()
show: ->
@hiding = false
@updateAlpha()
update: ->
# Gets the sprite to reflect what the current state of the thangs and surface are
return if @stillLoading
@ -193,6 +203,7 @@ module.exports = CocoSprite = class CocoSprite extends CocoClass
@imageObject.scaleY = @originalScaleY * scaleY * scaleFactorY
updateAlpha: ->
@imageObject.alpha = if @hiding then 0 else 1
return unless @thang?.alpha?
@imageObject.alpha = @thang.alpha
if @options.showInvisible

View file

@ -69,7 +69,7 @@ module.exports = class Layer extends createjs.Container
bz = if b.z then b.z else 1000
if az == bz
return 0 unless a.sprite?.thang?.pos and b.sprite?.thang?.pos
return b.sprite.thang.pos.y - a.sprite.thang.pos.y
return (b.sprite.thang.pos.y - a.sprite.thang.pos.y) or (b.sprite.thang.pos.x - a.sprite.thang.pos.x)
return az - bz
onZoomUpdated: (e) ->

View file

@ -19,6 +19,7 @@ module.exports = class SpriteBoss extends CocoClass
'level-suppress-selection-sounds': 'onSuppressSelectionSounds'
'level-lock-select': 'onSetLockSelect'
'level:restarted': 'onLevelRestarted'
'god:new-world-created': 'onNewWorld'
constructor: (@options) ->
super()
@ -87,7 +88,7 @@ module.exports = class SpriteBoss extends CocoClass
@selectionMark = new Mark name: 'selection', camera: @camera, layer: @spriteLayers["Ground"], thangType: @thangTypeFor("Selection")
createSpriteOptions: (options) ->
_.extend options, camera: @camera, resolutionFactor: 4, groundLayer: @spriteLayers["Ground"], textLayer: @surfaceTextLayer, floatingLayer: @spriteLayers["Floating"], markThangTypes: @markThangTypes(), spriteSheetCache: @spriteSheetCache, showInvisible: @options.showInvisible, world: @world
_.extend options, camera: @camera, resolutionFactor: 4, groundLayer: @spriteLayers["Ground"], textLayer: @surfaceTextLayer, floatingLayer: @spriteLayers["Floating"], markThangTypes: @markThangTypes(), spriteSheetCache: @spriteSheetCache, showInvisible: @options.showInvisible
createIndieSprites: (indieSprites, withWizards) ->
unless @indieSprites
@ -195,6 +196,9 @@ module.exports = class SpriteBoss extends CocoClass
spriteFor: (thangID) -> @sprites[thangID]
onNewWorld: (e) ->
@world = @options.world = e.world
# Selection
onSuppressSelectionSounds: (e) -> @suppressSelectionSounds = e.suppress

View file

@ -90,6 +90,7 @@ module.exports = class GoalManager extends CocoClass
# passes the word along
onNewWorldCreated: (e) =>
@updateGoalStates(e.goalStates) if e.goalStates?
@world = e.world
updateGoalStates: (newGoalStates) ->
for goalID, goalState of newGoalStates

View file

@ -32,12 +32,6 @@ module.exports = class World
@rand = new Rand 0
@frames = [new WorldFrame(@, 0)]
# --- This config needs to move into Systems config --- TODO
playableTeams: ["humans"]
teamForPlayer: (n) ->
@playableTeams[n % @playableTeams.length]
# -----------------------------------------------------
getFrame: (frameIndex) ->
# Optimize it a bit--assume we have all if @ended and are at the previous frame otherwise
frames = @frames
@ -78,7 +72,7 @@ module.exports = class World
(@runtimeErrors ?= []).push error
(@unhandledRuntimeErrors ?= []).push error
loadFrames: (loadedCallback, errorCallback, loadProgressCallback) =>
loadFrames: (loadedCallback, errorCallback, loadProgressCallback) ->
return if @aborted
unless @thangs.length
console.log "Warning: loadFrames called on empty World (no thangs)."
@ -464,3 +458,7 @@ module.exports = class World
colorConfigs = {}
colorConfigs[teamName] = config.color for teamName, config of teamConfigs
colorConfigs
teamForPlayer: (n) ->
playableTeams = @playableTeams ? ['humans']
playableTeams[n % playableTeams.length]

View file

@ -27,7 +27,7 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
admin: "Admin"
home: "Accueil"
contribute: "Contribuer"
legal: "Mention légales"
legal: "Mentions légales"
about: "À propos"
contact: "Contact"
twitter_follow: "Suivre"
@ -37,7 +37,7 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
save_version_title: "Enregistrer une nouvelle version"
new_major_version: "Nouvelle version majeure"
cla_prefix: "Pour enregistrer vos modifications vous devez d'abord accepter notre"
cla_url: "Licence de Copyright"
cla_url: "Copyright"
cla_suffix: "."
cla_agree: "J'accepte"
@ -54,7 +54,7 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
signup:
create_account_title: "Créer un compte pour sauvegarder votre progression"
description: "C'est gratuit. Simplement quelques informations et vous pourrez commencer :"
email_announcements: "Recevoir les annonces par courriel"
email_announcements: "Recevoir les annonces par email"
coppa: "13+ ou hors É-U"
coppa_why: "(Pourquoi?)"
creating: "Création du compte en cours..."
@ -62,7 +62,7 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
log_in: "se connecter avec votre mot de passe"
home:
slogan: "Apprenez à coder en JavaScript en jouant"
slogan: "Apprenez à coder en JavaScript tout en jouant"
no_ie: "CodeCombat ne fonctionnera pas sous Internet Explorer 9 ou moins. Désolé !"
no_mobile: "CodeCombat n'a pas été créé pour les plateformes mobiles donc il est possible qu'il ne fonctionne pas correctement ! "
play: "Jouer"
@ -77,16 +77,16 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
campaign_dev: "Niveaux aléatoires plus difficiles"
campaign_dev_description: "... dans lesquels vous apprendrez à utiliser l'interface en faisant quelque chose d'un petit peu plus dur."
campaign_multiplayer: "Campagne multi-joueurs"
campaign_multiplayer_description: "... dans laquelle vous codez en face à face contre d'autre joueurs."
campaign_multiplayer_description: "... dans laquelle vous coderez en face à face contre d'autre joueurs."
campaign_player_created: "Niveaux créés par les joueurs"
campaign_player_created_description: "... Dans laquelle vous serz confrontés à la créativité des votres.<a href=\"/contribute#artisan\">Artisan Wizards</a>."
campaign_player_created_description: "... Dans laquelle vous serez confrontés à la créativité des votres.<a href=\"/contribute#artisan\">Artisan Wizards</a>."
level_difficulty: "Difficulté: "
contact:
contact_us: "Contacter CodeCombat"
welcome: "Ravi d'avoir de vos nouvelles! Utilisez ce formulaire pour nous envoyer un mail."
contribute_prefix: "Si vous voulez contribuer, consultez notre "
contribute_page: "page des contributions"
contribute_page: "page de contributions"
contribute_suffix: "!"
forum_prefix: "Pour tout sujet d'ordre publique, merci d'utiliser "
forum_page: "notre forum"
@ -99,10 +99,10 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
pitch_body: "Nous développons CodeCombat en Anglais, mais nous avons déjà des joueurs de partout dans le monde. Beaucoup d'entre eux veulent jouer en Français mais ne parlent pas l'anglais, donc si vous parlez aussi bien l'anglais que le français, aidez-nous en devenant traducteur et aidez à traduire aussi bien le site que tous les niveaux en français."
missing_translations: "Jusqu'à ce que nous ayons tout traduit en français, vous verrez de l'anglais quand le français ne sera pas disponible."
learn_more: "Apprenez en plus sur les Traducteurs"
subscribe_as_diplomat: "Souscrire en tant que traducteur"
subscribe_as_diplomat: "S'inscrire en tant que traducteur"
wizard_settings:
title: "Paramètres du Wizard"
title: "Paramètres du Magicien"
customize_avatar: "Personnaliser votre avatar"
account_settings:
@ -126,7 +126,7 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
email_announcements_description: "Recevoir des mails sur les dernières actualités et sur le développement de CodeCombat."
contributor_emails: "Emails des contributeurs"
contribute_prefix: "Nous recherchons des personnes pour se joindre à notre groupe! Consultez la "
contribute_page: "page de contribution"
contribute_page: "page de contributions"
contribute_suffix: " pour en savoir plus."
email_toggle: "Tout basculer"
error_saving: "Probleme d'enregistrement"
@ -190,7 +190,7 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
tome_select_spell: "Choisissez un sort"
tome_select_a_thang: "Sélectionnez une unité pour"
tome_available_spells: "Sorts diponibles"
hud_continue: "Continuer (appuie sur shift-espace)"
hud_continue: "Continuer (appuie sur shift ou espace)"
# spell_saved: "Spell Saved"
admin:
@ -205,13 +205,13 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
editor:
main_title: "Éditeurs CodeCombat"
main_description: "Crée tes propres niveaux, campagnes, unités et contenu éducatif. Nous vous fournissons tous les outils dont vous avez besoin!"
main_description: "Créé tes propres niveaux, campagnes, unités et contenus éducatifs. Nous vous fournissons tous les outils dont vous avez besoin!"
article_title: "Éditeur d'article"
article_description: "Écris des articles qui donnent aux joueurs un aperçu des concepts de programmation qui peuvent être utilisés dans différents niveaux et campagnes."
thang_title: "Éditeur Thang"
thang_description: "Crée des unités, définis leur comportement de base, graphisme et son. Pour l'instant ne supporte que l'importation d'images vectorielles exportées de Flash."
thang_description: "Créé des unités, définis leur comportement de base, graphisme et son. Pour l'instant cette fonctionnalité ne supporte que l'importation d'images vectorielles exportées depuis Flash."
level_title: "Éditeur de niveau"
level_description: "Inclut les outils de script, le téléversement de vidéos, et l'élaboration de logiques personnalisées pour créer toutes sortes de niveaux. Tout ce que nous utilisons nous-mêmes!"
level_description: "Inclut les outils de script, l'upload de vidéos, et l'élaboration de logiques personnalisées pour créer toutes sortes de niveaux. Tout ce que nous utilisons nous-mêmes!"
security_notice: "Plusieurs caractéristiques majeures de cet éditeur ne sont pas encore activées par défaut. Quand nous aurons amélioré la sécurité de ces éléments, ils seront rendus disponibles. Si vous voulez les utiliser plus rapidement, "
contact_us: "contactez nous!"
hipchat_prefix: "Vous pouvez aussi nous trouver dans notre "
@ -253,7 +253,7 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
# version_history_for: "Version History for: "
results: "Résultats"
description: "Description"
email: "Courriel"
email: "Email"
message: "Message"
about:
@ -262,13 +262,13 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
who_description_prefix: "ont lancé CodeCombat ensemble en 2013. Nous avons aussi créé "
who_description_suffix: "en 2008, l'améliorant jusqu'au rang de première application web et iOS pour apprendre à écrire les caractères chinois et japonais."
who_description_ending: "Maintenant nous apprenons aux gens à coder."
why_paragraph_1: "En développant Skritter, George ne savait pas programmer et était frustré de ne pas pouvoir implémenter ses idées. Ensuite, il essaya d'apprendre, mais les cours n'étaient pas assez rapides. Son colocataire, voulant se requalifier et arrêter d'apprendre, essaya Codecademy, mais \"s'ennuya.\" Chaque semaine un nouvel ami commença Codecademy, puis abandonna. Nous nous sommes rendus compte que nous avions résolu le même problème avec Skritter: les gens apprennant grâce à des cours lents et intensifs quand nous avons besoin d'expérience rapide et intensive. Nous savons comment remédier à ça."
why_paragraph_1: "En développant Skritter, George ne savait pas programmer et était frustré de ne pas pouvoir implémenter ses idées. Ensuite, il essaya d'apprendre, mais les cours n'étaient pas assez rapides. Son colocataire, voulant se requalifier et arrêter d'apprendre, essaya Codecademy, mais \"s'ennuya.\" Chaque semaine un nouvel ami commençait Codecademy, puis abandonnait. Nous nous sommes rendus compte que nous avions résolu le même problème avec Skritter: les gens apprennant grâce à des cours lents et intensifs quand nous avons besoin d'expérience rapide et intensive. Nous savons comment remédier à ça."
why_paragraph_2: "Besoin d'apprendre à développer? Vous n'avez pas besoin de cours. Vous avez besoin d'écrire beaucoup de code et de vous amuser en le faisant."
why_paragraph_3_prefix: "C'est ce dont il s'agit en programmation. Ca doit être amusant. Pas amusant comme"
why_paragraph_3_prefix: "C'est ce dont il s'agit en programmation. Ça doit être amusant. Pas amusant comme"
why_paragraph_3_italic: "génial un badge"
why_paragraph_3_center: "mais amusant comme"
why_paragraph_3_italic_caps: "NAN MAMAN JE DOIS FINIR MON NIVEAU!"
why_paragraph_3_suffix: "C'est pourquoi CodeCombat est un jeu multijoueur, pas un cours avec une leçon jouée. Nous n'arrêterons pas avant que vous ne puissiez plus arrêter--mais cette fois, c'est une bonne chose."
why_paragraph_3_suffix: "C'est pourquoi CodeCombat est un jeu multijoueur, pas un cours avec une leçon jouée. Nous n'arrêterons pas avant que vous ne puissiez plus arrêtermais cette fois, c'est une bonne chose."
why_paragraph_4: "Si vous devez devenir accro à un jeu, accrochez-vous à celui-ci et devenez un des mages de l'âge de la technologie."
why_ending: "Et oh, c'est gratuit. "
why_ending_url: "Commence ton apprentissage maintenant!"
@ -279,7 +279,7 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
michael_description: "Programmeur, administrateur réseau, et l'enfant prodige du premier cycle, Michael est la personne qui maintient nos serveurs en ligne."
legal:
page_title: "Judiciaire"
page_title: "Légal"
opensource_intro: "CodeCombat est complètement gratuit et open source."
opensource_description_prefix: "Regardez "
github_url: "notre GitHub"
@ -301,16 +301,16 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
recruitment_title: "Recrutement"
recruitment_description_prefix: "Ici chez CodeCombat, vous allez devenir un magicien puissant, pas seulement dans le jeu, mais aussi dans la vie réelle."
url_hire_programmers: "Personne ne peut recruter des développeurs aussi vite"
recruitment_description_suffix: "donc une fois que vous aurez aiguisé votre savoir-faire et si vous l'acceptez, nous montrerons vos meilleurs codes aux milliers d'employeurs qui attendent une chance de vous recruter. Ils nous payent un peu pour ensuite vous payer"
recruitment_description_suffix: "donc une fois que vous aurez aiguisé votre savoir-faire et si vous l'acceptez, nous montrerons vos meilleurs bouts de code aux milliers d'employeurs qui attendent une chance de vous recruter. Ils nous payent un peu pour ensuite vous payer"
recruitment_description_italic: "beaucoup"
recruitment_description_ending: "le site reste gratuit et tout le monde est content. C'est le plan."
recruitment_description_ending: "le site reste gratuit et tout le monde est content. C'est le but."
copyrights_title: "Copyrights et Licences"
contributor_title: "Contributor License Agreement"
contributor_description_prefix: "Toute contribution, sur le site et sur le répertoire GitHub, est sujette à nos"
cla_url: "CLA"
contributor_description_suffix: "auxquels vous devez agréer avant de contribuer."
contributor_description_suffix: "auxquelles vous devez vous soumettre avant de contribuer."
code_title: "Code - MIT"
code_description_prefix: "Tout code possédé CodeCombat ou hébergé sur codecombat.com, sur le répertoire GitHub ou dans la base de donnée de codecombat.com, est sous la licence"
code_description_prefix: "Tout code siglé CodeCombat ou hébergé sur codecombat.com, sur le répertoire GitHub ou dans la base de données de codecombat.com, est sous la licence"
mit_license_url: "MIT"
code_description_suffix: "Cela inclut le code dans Systèmes et Composants qui est rendu disponible par CodeCombat dans le but de créer des niveaux."
art_title: "Art/Musique - Creative Commons "
@ -323,7 +323,7 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
art_sprites: "les sprites"
art_other: "tout le reste du contenu non-code qui est rendu accessible lors de la création de niveaux."
art_access: "Pour l'instant il n'y a aucun système universel et facile pour rassembler ces ressources. En général, accédez y par les URL comme le fait le site, contactez-nous pour de l'aide, ou aidez-nous à agrandir le site pour rendre ces ressources plus facilement accessibles."
art_paragraph_1: "Pour l'attribution, s'il vous plait, nommez et liez codecombat.com près de la source utilisée ou dans un endroit approprié. Par exemple:"
art_paragraph_1: "Pour l'attribution, s'il vous plait, nommez et référencez codecombat.com près de la source utilisée ou dans un endroit approprié. Par exemple:"
use_list_1: "Si utilisé dans un film ou un autre jeu, incluez codecombat.com dans le générique."
use_list_2: "Si utilisé sur un site web, incluez un lien près de l'utilisation, par exemple sous une image, ou sur une page d'attribution générale où vous pourrez aussi mentionner les autres travaux en Creative Commons et les logiciels open source utilisés sur votre site. Quelque chose qui fait clairement référence à CodeCombat, comme un article de blog mentionnant CodeCombat, n'a pas besoin d'attribution séparée."
art_paragraph_2: "Si le contenu utilisé n'est pas créé par CodeCombat mais par un autre utilisateur de codecombat.com, attribuez le à cet utilisateur, et suivez les recommandations fournies dans la ressource de la description s'il y en a."
@ -334,10 +334,10 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
rights_description: "la description"
rights_writings: "l'écriture"
rights_media: "les médias (sons, musiques) et tous les autres contenus créatifs créés spécialement pour ce niveau et non rendus généralement accessibles en créant des niveaux."
rights_clarification: "Pour clarifier, tout ce qui est rendu accessible dans l'éditeur de niveaux dans le but de créer des niveaux est sous licence CC, tandis que le contenu créé avec l'éditeur de niveaux ou téléversé dans le but de créer un niveau ne l'est pas."
rights_clarification: "Pour clarifier, tout ce qui est rendu accessible dans l'éditeur de niveaux dans le but de créer des niveaux est sous licence CC, tandis que le contenu créé avec l'éditeur de niveaux ou uploadé dans le but de créer un niveau ne l'est pas."
nutshell_title: "En un mot"
nutshell_description: "Chaque ressource que nous fournissons dans l'éditeur de niveau est libre d'utilisation pour créer des niveaux. Mais nous nous réservons le droit de restreindre la distribution des niveaux créés (qui sont créés sur codecombat.com) ils peuvent donc devenir payants dans le futur, si c'est ce qui doit arriver."
canonical: "La version de ce document est la version définitive et canonique. En cas d'irrégularités dans les traductions, le document anglais l'emporte."
canonical: "La version de ce document est la version définitive et canonique. En cas d'irrégularité dans les traductions, le document anglais fait foi."
contribute:
page_title: "Contribution"
@ -381,38 +381,38 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
adventurer_introduction: "Soyons clair à propos de votre rôle : vous êtes le tank. Vous allez subir beaucoup de dommages. Nous avons besoin de gens pour essayer les nouveaux niveaux et aider à identifier comment améliorer les choses. La douleur sera énorme; faire de bons jeux est une longue tâche et personne n'y arrive du premier coup. Si vous pouvez résister et avez un gros score de constitution, alors cette classe est faite pour vous."
adventurer_attribute_1: "Une soif d'apprendre. Vous voulez apprendre à développer et nous voulons vous apprendre. Vous allez toutefois faire la plupart de l'apprentissage."
adventurer_attribute_2: "Charismatique. Soyez doux mais exprimez-vous sur ce qui a besoin d'être amélioré, et faites des propositions sur comment l'améliorer."
adventurer_join_pref: "Soit être ensemble avec (ou recruter!) un artisan et travailler avec lui, ou cocher la case ci-dessous pour recevoir un email quand il y aura de nouveaux niveaux à tester. Nous parlons aussi des niveaux à réviser sur notre réseau"
adventurer_join_pref: "Soit faire équipe avec (ou recruter!) un artisan et travailler avec lui, ou cocher la case ci-dessous pour recevoir un email quand il y aura de nouveaux niveaux à tester. Nous parlons aussi des niveaux à réviser sur notre réseau"
adventurer_forum_url: "notre forum"
adventurer_join_suf: "si vous préférez être avertis ainsi, inscrivez-vous ici!"
more_about_adventurer: "En apprendre plus sur devenir un Aventurier brave"
more_about_adventurer: "En apprendre plus sur devenir un brave Aventurier"
adventurer_subscribe_desc: "Recevoir un email lorsqu'il y a de nouveaux niveaux à tester."
scribe_introduction_pref: "CodeCombat n'est pas seulement un paquet de niveaux. Il contiendra aussi des ressources pour la connaissance, un wiki des concepts de programmation que les niveaux pourront illustrer. Dans ce but, chaque Artisan pourra, au lieu d'avoir à décrire en détail ce qu'est un opérateur de comparaison, seulement lier son niveau à l'article qui le décrit et qui a été écrit pour aider les joueurs. Quelque chose dans le sens de ce que le "
scribe_introduction_pref: "CodeCombat n'est pas seulement un ensemble de niveaux. Il contiendra aussi des ressources pour la connaissance, un wiki des concepts de programmation que les niveaux pourront illustrer. Dans ce but, chaque Artisan pourra, au lieu d'avoir à décrire en détail ce qu'est un opérateur de comparaison, seulement lier son niveau à l'article qui le décrit et qui a été écrit pour aider les joueurs. Quelque chose dans le sens de ce que le "
scribe_introduction_url_mozilla: "Mozilla Developer Network"
scribe_introduction_suf: " a développé. Si votre représentation de l'amusement est l'articulation des concepts de programmation au format Markdown, alors cette classe est pour vous."
scribe_attribute_1: "Les compétences rédactionnelles sont quasiment la seule chose dont vous aurez besoin. Pas seulement la grammaire et l'orthographe, mais être capable de lier des idées ensembles."
scribe_introduction_suf: " a développé. Si votre définition de l'amusement passe par le format Markdown, alors cette classe est pour vous."
scribe_attribute_1: "Les compétences rédactionnelles sont quasiment la seule chose dont vous aurez besoin. Pas seulement la grammaire et l'orthographe, mais être également capable de lier des idées ensembles."
contact_us_url: "Contactez-nous"
scribe_join_description: "parlez nous un peu de vous, de votre expérience en programmation et de quels sujets vous souhaitez traiter. Nous partirons de là!"
more_about_scribe: "En apprendre plus sur comment devenir un Scribe assidu"
scribe_subscribe_desc: "Recevoir un email sur les annonces d'écriture d'article."
diplomat_introduction_pref: "Si nous avons appris quelque chose du "
diplomat_launch_url: "lancement en octobre"
diplomat_introduction_suf: "c'est qu'il y a un intérêt considérable pour CodeCombat dans d'autres pays, particulièrement au Brésil! Nous créons une équipe de traducteurs pour changer une liste de mots en une autre pour que CodeCombat soit le plus accessible possible à travers le monde. Si vous aimez avoir des aperçus des prochains contenus et avoir les niveaux dans votre langue le plus tôt possible, alors cette classe est faite pour vous."
diplomat_attribute_1: "Des facilités en anglais et dans la langue que vous souhaitez traduire. Pour transmettre des idées complexes, c'est important d'avoir une solide compréhension des deux!"
diplomat_introduction_suf: "c'est qu'il y a un intérêt considérable pour CodeCombat dans d'autres pays, particulièrement au Brésil! Nous créons une équipe de traducteurs pour changer une liste de mots en une autre pour que CodeCombat soit le plus accessible possible à travers le monde. Si vous souhaitez avoir un aperçu des prochains contenus et avoir les niveaux dans votre langue le plus tôt possible, alors cette classe est faite pour vous."
diplomat_attribute_1: "Des facilités en anglais et dans la langue que vous souhaitez traduire. Pour transmettre des idées complexes, il est important d'avoir une solide compréhension des deux!"
diplomat_join_pref: "Nous avons commencé plusieurs traductions sur "
diplomat_doc_url: "ce post du forum"
diplomat_join_suf: "regardez les et ajoutez des choses à votre langue. Aussi, cochez la case ci-dessous pour rester à jour sur les nouveaux développement d'i18n!"
more_about_diplomat: "En apprendre plus sur comment devenir un bon diplomate"
diplomat_subscribe_desc: "Recevoir un email sur le développement i18n et les niveaux à traduire."
ambassador_introduction: "C'est la communauté que nous avons construite, et vous en êtes les connexions. Nous avons une discussion Olark, des emails et les réseaux sociaux avec plusieurs personnes avec qui parler, et l'aide vient à la fois du jeu lui-même et grâce à lui. Si vous voulez aider les gens, prendre part à l'aventure et vous amusez, avec un bon feeling de CodeCombat et de là où nous allons, alors cette classe est faite pour vous."
ambassador_attribute_1: "Compétences en communication. Être capable d'identifier les problèmes que les joueurs ont et les aider à les résoudre. Mais aussi nous garder informés de ce que les joueurs disent, ce qu'ils aiment et n'aiment pas et d'autres choses de ce type!"
ambassador_introduction: "C'est la communauté que nous construisons, et vous en êtes les connexions. Nous avons des discussions via le chat Olark, emails et les réseaux sociaux avec plusieurs personnes, et l'aide vient à la fois du jeu lui-même et grâce à lui. Si vous voulez aider les gens, prendre part à l'aventure et vous amuser, avec un bon feeling de CodeCombat et ce vers quoi nous allons, alors cette classe est faite pour vous."
ambassador_attribute_1: "Compétences en communication. Être capable d'identifier les problèmes que les joueurs ont et les aider à les résoudre. Mais aussi nous tenir informés de ce que les joueurs disent, ce qu'ils aiment et n'aiment pas et d'autres choses de ce genre!"
ambassador_join_desc: "parlez-nous un peu de vous, de ce que vous avez fait et ce qui vous aimeriez faire. Nous partirons de ça!"
ambassador_join_note_strong: "Note"
ambassador_join_note_desc: "Une de nos priorités est de développer un jeu multijoueur où les joueurs qui ont du mal à réussir un niveau peuvent demander de l'aide à un joueur de plus haut niveau. Ce sera un bon moyen pour que les ambassadeurs fassent leur travail. Nous vous garderons en ligne!"
more_about_ambassador: "En apprendre plus sur comment devenir un Ambassadeur serviable"
more_about_ambassador: "En apprendre plus sur comment devenir un serviable Ambassadeur"
ambassador_subscribe_desc: "Recevoir un email sur les mises à jour de l'aide et les développements multijoueur."
counselor_introduction_1: "Avez-vous une expérience de vie? Ou toute autre expérience qui peut nous aider à décider comment diriger CodeCombat? De tous ces rôles, ce sera probablement celui qui prend le moins de temps, mais vous ferez la différence. Nous recherchons des sages, particulièrement dans les domaines de : l'apprentissage, le développement de jeux, la gestion de projets open source, le recrutement technique, l'entreprenariat, ou la conception."
counselor_introduction_2: "Ou vraiment toute chose en rapport avec le développement de CodeCombat. Si vous avez des connaissances et que vous voulez les partager pour aider le projet à avancer, alors cette classe est faite pour vous."
counselor_attribute_1: "De l'expérience, dans un des domaines ci-dessus ou quelque chose que vous pensez utile."
counselor_introduction_1: "Avez-vous de l'expérience dans la vie? Ou toute autre expérience qui peut nous aider à décider comment diriger CodeCombat? De tous ces rôles, ce sera probablement celui qui prend le moins de temps, mais vous ferez la différence. Nous recherchons des sages, particulièrement dans les domaines de : l'apprentissage, le développement de jeux, la gestion de projets open source, le recrutement technique, l'entreprenariat, ou la conception."
counselor_introduction_2: "Ou vraiment toutes choses en rapport avec le développement de CodeCombat. Si vous avez des connaissances et que vous voulez les partager pour aider le projet à avancer, alors cette classe est faite pour vous."
counselor_attribute_1: "De l'expérience, dans un des domaines ci-dessus ou quelque chose que vous pensez être utile."
counselor_attribute_2: "Un peu de temps libre!"
counselor_join_desc: "parlez-nous un peu de vous, de ce que vous avez fait et ce que vous aimeriez faire. Nous vous mettrons dans notre liste de contacts et ferons appel à vous quand nous aurons besoin de conseils (pas trop souvent)."
more_about_counselor: "En apprendre plus sur devenir un précieux Conseiller"
@ -421,8 +421,8 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
powerful_archmages: "Nos puissants Archimages :"
creative_artisans: "Nos Artisans créatifs :"
brave_adventurers: "Nos braves Aventuriers :"
translating_diplomats: "Nos Diplomates traduteurs :"
helpful_ambassadors: "Nos Ambassadeurs serviables :"
translating_diplomats: "Nos Diplomates traducteurs :"
helpful_ambassadors: "Nos serviables Ambassadeurs :"
classes:
archmage_title: "Archimage"
@ -430,12 +430,13 @@ module.exports = nativeDescription: "français", englishDescription: "French", t
artisan_title: "Artisan"
artisan_title_description: "(Créateur de niveau)"
adventurer_title: "Aventurier"
adventurer_title_description: "(Testeur de jouabilité)"
adventurer_title_description: "(Testeur de niveau)"
scribe_title: "Scribe"
scribe_title_description: "(Éditeur d'article)"
scribe_title_description: "(Rédacteur d'articles)"
diplomat_title: "Diplomate"
diplomat_title_description: "(Traducteur)"
ambassador_title: "Ambassadeur"
ambassador_title_description: "(Aide)"
counselor_title: "Conseiller"
counselor_title_description: "(Expert/Professeur)"

View file

@ -57,14 +57,14 @@
box-sizing: border-box
#thang-components-edit-view
position: absolute
top: 45px
bottom: 0
.treema-root
background-color: white
border-radius: 4px
#thang-components-edit-view
position: absolute
top: 130px
bottom: 0
.treema-root
background-color: white
border-radius: 4px
#spritesheets
border: 1px solid green

View file

@ -79,7 +79,7 @@ block content
li Greek - Stergios
li Latin American Spanish - Jesús Ruppel, Matthew Burt, Mariano Luzza
li Spain Spanish - Matthew Burt, DanielRodriguezRivero, Anon
li French - Xeonarno, Elfisen, Armaldio, MartinDelille, pstweb, veritable, jaybi, Anon
li French - Xeonarno, Elfisen, Armaldio, MartinDelille, pstweb, veritable, jaybi, xavismeh, Anon
li Hungarian - ferpeter, csuvsaregal, atlantisguru, Anon
li Japanese - g1itch, kengos
li Chinese - Adam23, spacepope

View file

@ -24,6 +24,12 @@
span(data-i18n="play_level.multiplayer_hint") Click the link to select all, then press ⌘-C or Ctrl-C to copy the link.
p(data-i18n="play_level.multiplayer_coming_soon") More multiplayer features to come!
if playableTeams.length > 1
#multiplayer-team-selection
for team in playableTeams
label(for="multiplayer_team_"+team)= team
input(id="multiplayer_team_"+team, type="radio", name="multiplayer", value=team)
.modal-footer
a(href='#', data-dismiss="modal", aria-hidden="true", data-i18n="modal.close").btn.btn-primary Close

View file

@ -52,7 +52,6 @@ module.exports = class EditorLevelView extends View
@files.fetch()
onAllLoaded: =>
@originalLevelAttributes = _.cloneDeep @level.attributes
@level.unset('nextLevel') if _.isString(@level.get('nextLevel'))
@initWorld()
@startsLoading = false
@ -93,7 +92,7 @@ module.exports = class EditorLevelView extends View
@childWindow.focus()
startCommittingLevel: (e) ->
levelSaveView = new LevelSaveView level: @level, supermodel: @supermodel, originalLevelAttributes: @originalLevelAttributes
levelSaveView = new LevelSaveView level: @level, supermodel: @supermodel
@openModalView levelSaveView
Backbone.Mediator.publish 'level:view-switched', e

View file

@ -16,13 +16,11 @@ module.exports = class LevelSaveView extends SaveVersionModal
constructor: (options) ->
super options
@level = options.level
@originalLevelAttributes = options.originalLevelAttributes
@levelNeedsSave = not _.isEqual @level.attributes, @originalLevelAttributes
getRenderData: (context={}) =>
context = super(context)
context.level = @level
context.levelNeedsSave = @levelNeedsSave
context.levelNeedsSave = @level.hasLocalChanges()
context.modifiedComponents = _.filter @supermodel.getModels(LevelComponent), @shouldSaveEntity
context.modifiedSystems = _.filter @supermodel.getModels(LevelSystem), @shouldSaveEntity
context

View file

@ -31,6 +31,7 @@ module.exports = class ControlBarView extends View
@worldName = options.worldName
@session = options.session
@level = options.level
@playableTeams = options.playableTeams
super options
setBus: (@bus) ->
@ -55,7 +56,7 @@ module.exports = class ControlBarView extends View
@openModalView(new DocsModal(options))
showMultiplayerModal: ->
@openModalView(new MultiplayerModal(session: @session))
@openModalView(new MultiplayerModal(session: @session, playableTeams: @playableTeams))
showRestartModal: ->
@openModalView(new ReloadModal())

View file

@ -301,6 +301,7 @@ module.exports = class HUDView extends View
[newFrame, newAction] = [hist.frame, hist.name]
continue if newAction is lastAction
if newFrame > lastFrame
# TODO: don't push it if it didn't exist until then
(@timespans[lastAction] ?= []).push [lastFrame * dt, newFrame * dt]
[lastFrame, lastAction] = [newFrame, newAction]

View file

@ -1,5 +1,6 @@
View = require 'views/kinds/ModalView'
template = require 'templates/play/level/modal/multiplayer'
{me} = require('lib/auth')
module.exports = class MultiplayerModal extends View
id: 'level-multiplayer-modal'
@ -13,6 +14,7 @@ module.exports = class MultiplayerModal extends View
super(options)
@session = options.session
@session.on 'change:multiplayer', @updateLinkSection
@playableTeams = options.playableTeams
getRenderData: ->
c = super()
@ -20,13 +22,17 @@ module.exports = class MultiplayerModal extends View
'?session=' +
@session.id)
c.multiplayer = @session.get('multiplayer')
c.playableTeams = @playableTeams
c
afterRender: ->
super()
@updateLinkSection()
@$el.find('#multiplayer-team-selection input')
.prop('checked', -> $(@).val() is me.team)
.bind('change', -> Backbone.Mediator.publish 'level:set-team', team: $(@).val())
onClickLink: (e) =>
onClickLink: (e) ->
e.target.select()
updateLinkSection: =>

View file

@ -28,7 +28,10 @@ module.exports = class Spell
@tabView.render()
addThang: (thang) ->
@thangs[thang.id] ?= {thang: thang, aether: @createAether(thang), castAether: null}
if @thangs[thang.id]
@thangs[thang.id].thang = thang
else
@thangs[thang.id] = {thang: thang, aether: @createAether(thang), castAether: null}
removeThangID: (thangID) ->
delete @thangs[thangID]

View file

@ -134,3 +134,7 @@ module.exports = class ThangListEntryView extends View
return unless currentThang = e.world.thangMap[@thang.id]
@$el.toggle Boolean(currentThang.exists)
@$el.toggleClass 'dead', currentThang.health <= 0 if currentThang.exists
destroy: ->
super()
@avatar.destroy()

View file

@ -65,6 +65,7 @@ module.exports = class TomeView extends View
else
@cast()
console.warn "Warning: There are no Programmable Thangs in this level, which makes it unplayable."
delete @options.thangs
onNewWorld: (e) ->
thangs = _.filter e.world.thangs, 'isSelectable'
@ -95,7 +96,7 @@ module.exports = class TomeView extends View
@spells[spellKey].addThang thang for spellKey in spellKeys
else
delete @thangSpells[thangID]
@spells[spellKey].removeThangID thangID for spellKey in spellKeys
spell.removeThangID thangID for spell in @spells
null
onSpellLoaded: (e) ->

View file

@ -50,13 +50,14 @@ module.exports = class PlayLevelView extends View
'level-focus-dom': 'onFocusDom'
'level-disable-controls': 'onDisableControls'
'level-enable-controls': 'onEnableControls'
'god:new-world-created': 'onNewWorld'
'god:infinite-loop': 'onInfiniteLoop'
'bus:connected': 'onBusConnected'
'level-reload-from-data': 'onLevelReloadFromData'
'play-next-level': 'onPlayNextLevel'
'edit-wizard-settings': 'showWizardSettingsModal'
'surface:world-set-up': 'onSurfaceSetUpNewWorld'
'level:session-will-save': 'onSessionWillSave'
'level:set-team': 'setTeam'
events:
'click #level-done-button': 'onDonePressed'
@ -102,7 +103,6 @@ module.exports = class PlayLevelView extends View
load: ->
@levelLoader = new LevelLoader(@levelID, @supermodel, @sessionID)
@levelLoader.once 'ready-to-init-world', @onReadyToInitWorld
@levelLoader.once 'loaded-all', @onLevelLoaderLoaded
getRenderData: ->
@ -120,8 +120,10 @@ module.exports = class PlayLevelView extends View
@session = @levelLoader.session
@level = @levelLoader.level
@world = @levelLoader.world
@levelLoader.destroy()
@levelLoader = null
@loadingScreen.destroy()
@setTeam @world.teamForPlayer 1 # We don't know which player we are; this will go away--temp TODO
@setTeam @world.teamForPlayer _.size @session.get 'players' # TODO: players aren't initialized yet?
@initSurface()
@initGod()
@initGoalManager()
@ -155,7 +157,7 @@ module.exports = class PlayLevelView extends View
@insertSubView new HUDView {}
@insertSubView new ChatView levelID: @levelID, sessionID: @session.id, session: @session
worldName = @level.get('i18n')?[me.lang()]?.name ? @level.get('name')
@controlBar = @insertSubView new ControlBarView {worldName: worldName, session: @session, level: @level, supermodel: @supermodel}
@controlBar = @insertSubView new ControlBarView {worldName: worldName, session: @session, level: @level, supermodel: @supermodel, playableTeams: @world.playableTeams}
#Backbone.Mediator.publish('level-set-debug', debug: true) if me.displayName() is 'Nick!'
afterInsert: ->
@ -206,6 +208,9 @@ module.exports = class PlayLevelView extends View
$('#level-done-button', @$el).hide()
window.tracker?.trackEvent 'Confirmed Restart', level: @world.name, label: @world.name
onNewWorld: (e) ->
@world = e.world
onInfiniteLoop: (e) ->
return unless e.firstWorld
@openModalView new InfiniteLoopModal()
@ -355,13 +360,6 @@ module.exports = class PlayLevelView extends View
@bus.setSession(@session)
@bus.connect() if @session.get('multiplayer')
onBusConnected: ->
# Need to set this team stuff before the Tome loads... let's think about this with Scott.
#@setTeam @world.teamForPlayer(@bus.countPlayers()) unless me.team
#$('#multiplayer-team-selection').toggle(@world.playableTeams.length > 1)
# .find('input').prop('checked', -> $(@).val() is me.team)
# .bind('change', @setTeam)
onSessionWillSave: (e) ->
# Something interesting has happened, so (at a lower frequency), we'll save a screenshot.
@saveScreenshot e.session
@ -372,13 +370,14 @@ module.exports = class PlayLevelView extends View
session.save {screenshot: screenshot}, {patch: true}
setTeam: (team) ->
team = $(@).val() unless _.isString team
team = team?.team unless _.isString team
team ?= 'humans'
me.team = team
Backbone.Mediator.publish 'level:team-set', team: team
destroy: ->
super()
@levelLoader.destroy()
@levelLoader?.destroy()
@surface?.destroy()
@god?.destroy()
@goalManager?.destroy()

View file

@ -1,22 +0,0 @@
mongoose = require('mongoose')
plugins = require('../plugins/plugins')
NestedLevelSchema = new mongoose.Schema(
name: String
description: String
thumbnail: Buffer
original: {type: mongoose.Schema.ObjectId, ref: 'level'}
majorVersion: Number
)
CampaignSchema = new mongoose.Schema(
description: String
levels: [NestedLevelSchema]
)
CampaignSchema.plugin(plugins.NamedPlugin)
CampaignSchema.plugin(plugins.PermissionsPlugin)
CampaignSchema.plugin(plugins.SearchablePlugin, {searchable: ['name', 'description']})
module.exports = Campaign = mongoose.model('campaign', CampaignSchema)

View file

@ -1,17 +0,0 @@
mongoose = require('mongoose')
LevelStatusSchema = new mongoose.Schema(
lastPlayed: Date
victorious: Boolean
original: {type: mongoose.Schema.ObjectId, ref: 'level'}
majorVersion: Number
)
CampaignStatusSchema = new mongoose.Schema(
user: {type: mongoose.Schema.ObjectId, ref: 'User'}
campaign: {type: mongoose.Schema.ObjectId, ref: 'campaign'}
levelStatuses: [LevelStatusSchema]
)
CampaignStatusSchema.index {user: 1, campaign: 1}, {unique: true}
module.exports = CampaignStatus = mongoose.model('campaign.status', CampaignStatusSchema)

View file

@ -1,10 +0,0 @@
Campaign = require('./Campaign')
Handler = require('../commons/Handler')
CampaignHandler = class CampaignHandler extends Handler
modelClass: Campaign
editableProperties: ['name', 'description', 'levels']
module.exports = new CampaignHandler()

View file

@ -1,13 +0,0 @@
CampaignStatus = require('./CampaignStatus')
Handler = require('../commons/Handler')
CampaignStatusHandler = class CampaignStatusHandler extends Handler
modelClass: CampaignStatus
editableProperties: ['levelStatuses']
postEditableProperties: ['campaign', 'user']
post: (req, res) ->
req.body.user = req.user._id
super(req, res)
module.exports = new CampaignStatusHandler()

View file

@ -1,12 +1,8 @@
module.exports.handlers =
'article': 'articles/article_handler'
'campaign': 'campaigns/campaign_handler'
'campaign_status': 'campaigns/campaign_status_handler'
'file': 'files/file_handler'
'level': 'levels/level_handler'
'level_component': 'levels/components/level_component_handler'
'level_draft': 'levels/drafts/level_draft_handler'
'level_feedback': 'levels/feedbacks/level_feedback_handler'
'level_session': 'levels/sessions/level_session_handler'
'level_system': 'levels/systems/level_system_handler'
@ -16,7 +12,6 @@ module.exports.handlers =
module.exports.schemas =
'article': 'articles/article_schema'
'common': 'commons/schemas'
#'file': 'files/file_schema'
'i18n': 'commons/i18n_schema'
'level': 'levels/level_schema'
'level_component': 'levels/components/level_component_schema'
@ -38,4 +33,4 @@ module.exports.routes =
'routes/languages'
'routes/mail'
'routes/sprites'
]
]

View file

@ -1,8 +0,0 @@
mongoose = require('mongoose')
plugins = require('../plugins/plugins')
FileSchema = new mongoose.Schema()
FileSchema.plugin(plugins.SearchablePlugin, {searchable: ['metadata.description', 'metadata.name']})
module.exports = mongoose.model('media.files', FileSchema)

View file

@ -1,13 +0,0 @@
File = require('./File')
Handler = require('../commons/Handler')
FileHandler = class FileHandler extends Handler
modelClass: File
editableProperties: ['metadata']
hasAccess: (req) ->
req.method is 'GET'
# TODO: once we're building the clients, need special GET handler, search handler
module.exports = new FileHandler()

View file

@ -1,25 +0,0 @@
c = require '../commons/schemas'
FileSchema = c.baseSchema()
_.extend(FileSchema.properties, {
filename: c.shortStringProp()
contentType: c.shortStringProp()
length: { type: 'number' }
chunkSize: { type: 'number', format: 'hidden' }
uploadDate: { type: 'string' }
aliases: {}
metadata:
type: 'object'
additionalProperties: false
name: c.shortStringArrayProp()
description: { type: 'string' }
createdFor: { type: 'array', items: {}}
path: { type: 'string' }
creator: { type: 'string' }
})
c.extendSearchableProperties(FileSchema.properties.metadata)
FileSchema.format = 'file'
module.exports = FileSchema

View file

@ -1,9 +0,0 @@
mongoose = require('mongoose')
Level = require('../Level')
LevelDraftSchema = new mongoose.Schema(
user: {type: mongoose.Schema.ObjectId, ref: 'User'}
level: {}
)
module.exports = LevelDraft = mongoose.model('level.draft', LevelDraftSchema)

View file

@ -1,13 +0,0 @@
LevelDraft = require('./LevelDraft')
Handler = require('../../commons/Handler')
LevelDraftHandler = class LevelDraftHandler extends Handler
modelClass: LevelDraft
editableProperties: ['level']
postEditableProperties: ['user']
post: (req, res) ->
req.body.user = req.user._id
super(req, res)
module.exports = new LevelDraftHandler()

View file

@ -30,6 +30,7 @@ GoalSchema = c.object {title: "Goal", description: "A goal that the player can a
worldEndsAfter: {title: 'World Ends After', description: "When included, ends the world this many seconds after this goal succeeds or fails.", type: 'number', minimum: 0, exclusiveMinimum: true, maximum: 300, default: 3}
howMany: {title: "How Many", description: "When included, require only this many of the listed goal targets instead of all of them.", type: 'integer', minimum: 1}
hiddenGoal: {title: "Hidden", description: "Hidden goals don't show up in the goals area for the player until they're failed. (Usually they're obvious, like 'don't die'.)", 'type': 'boolean', default: false}
team: c.shortString(title: 'Team', description: 'Name of the team this goal is for, if it is not for all of the playable teams.')
killThangs: c.array {title: "Kill Thangs", description: "A list of Thang IDs the player should kill, or team names.", uniqueItems: true, minItems: 1, "default": ["ogres"]}, thang
saveThangs: c.array {title: "Save Thangs", description: "A list of Thang IDs the player should save, or team names", uniqueItems: true, minItems: 1, "default": ["humans"]}, thang
getToLocations: c.object {title: "Get To Locations", description: "TODO: explain", required: ["who", "targets"]},

View file

@ -1,4 +1,4 @@
describe 'editor/level/thangs_tab', ->
describe 'editor/level/systems_tab', ->
SystemsTabView = require 'views/editor/level/systems_tab_view'
it 'does stuff', ->

View file

@ -10,12 +10,9 @@ path = require('path')
models_path = [
'../../server/articles/Article'
'../../server/campaigns/Campaign'
'../../server/campaigns/CampaignStatus'
'../../server/levels/Level'
'../../server/levels/components/LevelComponent'
'../../server/levels/systems/LevelSystem'
'../../server/levels/drafts/LevelDraft'
'../../server/levels/sessions/LevelSession'
'../../server/levels/thangs/LevelThangType'
'../../server/users/User'

View file

@ -1,42 +0,0 @@
require '../common'
describe '/db/campaign', ->
request = require 'request'
it 'clears the db first', (done) ->
clearModels [User, Campaign], (err) ->
throw err if err
done()
campaign = {name: 'A', description:'B'}
url = getURL('/db/campaign')
campaigns = {}
it 'allows making Campaigns.', (done) ->
loginJoe (user) ->
campaign.permissions = [access: 'owner', target: user._id]
request.post {uri:url, json:campaign}, (err, res, body) ->
expect(res.statusCode).toBe(200)
expect(body.permissions).toBeDefined()
campaigns[0] = body
done()
it 'does not allow other users access', (done) ->
loginSam ->
request.get {uri:url+'/'+campaigns[0]._id}, (err, res, body) ->
expect(res.statusCode).toBe(403)
done()
it 'allows editing permissions.', (done) ->
loginJoe ->
campaigns[0].permissions.push(access: 'read', target: 'public')
request.put {uri:url, json:campaigns[0]}, (err, res, body) ->
expect(res.statusCode).toBe(200)
expect(body.permissions.length).toBe(2)
campaigns[0] = body
done()
it 'allows anyone to access it through public permissions', (done) ->
loginSam ->
request.get {uri:url+'/'+campaigns[0]._id}, (err, res, body) ->
expect(res.statusCode).toBe(200)
done()

View file

@ -1,21 +0,0 @@
require '../common'
describe '/db/campaign_status', ->
request = require 'request'
it 'clears the db first', (done) ->
clearModels [Campaign, CampaignStatus], (err) ->
throw err if err
done()
user = new User(name:'sup')
campaign = new Campaign(name:'Project Vengeance.', permissions: simplePermissions)
stat = {campaign: campaign._id, user: user._id}
url = getURL('/db/campaign_status')
it 'can make a CampaignStatus, and ignores the user property given.', (done) ->
loginJoe (joe) ->
request.post {uri:url, json:stat}, (err, res, body) ->
expect(res.statusCode).toBe(200)
expect(body.user).toBe(joe._id.toString())
expect(body.user).not.toBe(user._id.toString())
done()

View file

@ -1,23 +0,0 @@
require '../common'
describe '/db/campaign_draft', ->
draft = {
level: {}
user: 'yoyoyo'
}
request = require 'request'
it 'clears the db first', (done) ->
clearModels [LevelDraft], (err) ->
throw err if err
done()
url = getURL('/db/level_draft')
it 'can make a LevelDraft, and ignores the user property given.', (done) ->
loginJoe (joe) ->
request.post {uri:url, json:draft}, (err, res, body) ->
expect(res.statusCode).toBe(200)
expect(body.user).toBe(joe._id.toString())
done()

View file

@ -1,34 +0,0 @@
require '../../common'
describe 'CampaignStatus', ->
user = new User(name:'sup')
campaign = new Campaign(name:'Project Vengeance.', permissions: simplePermissions)
stat = new CampaignStatus(user: user._id, campaign: campaign._id)
it 'clears things first', (done) ->
clearModels [User, Campaign, CampaignStatus], (err) ->
expect(err).toBeNull()
done()
it 'can be saved', (done) ->
saveModels [user, campaign, stat], (err) ->
expect(err).toBeNull()
done()
it 'can populate', (done) ->
CampaignStatus
.findOne({_id:stat._id})
.populate('user')
.populate('campaign')
.exec (err, c) ->
expect(err).toBe(null)
expect(c.user.get('name')).toBeDefined()
expect(c.campaign.get('name')).toBeDefined()
done()
it 'rejects duplicates', (done) ->
stat2 = new CampaignStatus(user: user._id, campaign: campaign._id)
stat2.save (err) ->
expect(err).not.toBe(null)
done()

View file

@ -1,31 +0,0 @@
require '../../common'
describe 'LevelDraft', ->
level = new Level(
name: "King's Peak Redux"
description: 'Climb a mountain.'
permissions: simplePermissions
original: new ObjectId()
)
it 'clears things first', (done) ->
clearModels [Level, LevelDraft], (err) ->
expect(err).toBeNull()
done()
it 'saves', (done) ->
level.save (err) ->
throw err if err
draft = new LevelDraft(
user: new ObjectId()
level: level
)
draft.save (err) ->
throw err if err
LevelDraft.findOne {_id:draft._id}, (err, fetched) ->
expect(fetched.level.original).toBeDefined()
done()

View file

@ -1,26 +0,0 @@
require '../../common'
describe 'Campaign', ->
raw =
name:'Battlefield 1942'
description:'Vacation all over the world!'
levels: []
permissions:[
target:'not_the_public'
access:'owner'
]
campaign = new Campaign(raw)
it 'clears things first', (done) ->
Campaign.remove {}, (err) ->
expect(err).toBeNull()
done()
it 'can be saved', (done) ->
campaign.save (err) ->
expect(err).toBeNull()
done()

View file

@ -328,27 +328,3 @@ describe 'SearchablePlugin', ->
target:'not_the_public'
access:'owner'
]
campaign = new Campaign(raw)
it 'clears things first', (done) ->
Campaign.remove {}, (err) ->
expect(err).toBeNull()
done()
it 'hides private entities from public searches', (done) ->
campaign.save (err) ->
throw err if err
done()
Campaign.textSearch 'battlefield', {filter:{ index: true}}, (err, results) ->
expect(results.results.length).toBe(0)
done()
it 'allows private searches for owning users', (done) ->
campaign.save (err) ->
throw err if err
Campaign.textSearch 'battlefield', {filter: { index: 'not_the_public' }}, (err, results) ->
expect(results.results.length).toBeGreaterThan(0)
done()