codecombat / codecombat Goto Github PK
View Code? Open in Web Editor NEWGame for learning how to code.
Home Page: http://codecombat.com
License: MIT License
Game for learning how to code.
Home Page: http://codecombat.com
License: MIT License
Right now, versions pages for versioned documents load the full documents. Particularly large version pages like the one for the Rescue Mission level fail to load because it's trying to load a huge number of large documents.
Have the system instead download just the parts of the documents required: the version object, the commit message, the name and the id. The function which serves versions for all documents is in /server/handlers/Handler.coffee:versions.
One cool project for learning the higher level structure of the site and incredibly useful for Archmages everywhere would be a window which displays many helpful stats about the site at any given time. Anywhere on the site, the developer could click a button or press a shortcut and open a Developer Tools like interface in a separate window which includes:
This could be used to:
We didn't have a good place to acknowledge contributors, so I started putting them in the /contribute sidebar for now. It quickly started getting long, looking terrible, and scrolling into the footer:
In an ideal world, we either make a new /contributors page and start listing everyone there, or we make a new page per class (like /contribute/archmage) to hold them all. Eventually we'll have more than just a name and a few words of text per contributor, too.
To create the new page(s), we'd add a /contribute
directory to each of app/templates
, app/styles
, and app/views
, then start putting .jade
, .sass
, and .coffee
files in there per page.
An interim improvement would be to fix the style on that sidebar to not scroll off into the footer, and possibly to look less horrible. @TomSteinbrecher expressed interest in that.
Related files:
Sometimes you want to grab some (x, y)
coordinates, so we built this stopgap app/lib/surface/CoordinateDisplay.coffee for displaying them on cursor hover after a slight delay. It's pretty bad, though:
@chloester suggested having a waving flag where you click down and every time you click to a different place, it keeps the coordinate, with a background bubble/pin. @sderickson was thinking that it'd be a button you'd press to activate the tool, and then the next point you clicked would get inserted into the editor. I tried out adding a grid, so you could just read coordinates by following the major tick lines, and that's in, but the initial grid design sucked:
It needs the numbers to go onto the Screen Layer but the grid lines to either stay on the Surface Layer or to also be on the Screen Layer but change whenever the viewport changes. More on Layers here. Maybe if we improve the CoordinateDisplay enough, we can just get rid of the grid?
Originally was thinking of having non-CodeCombat specific stuff in FoundationView/Class classes but it's probably too early to do that sort of division. Refactor FoundationClass to CocoClass and combine FoundationView with CocoView. The Foundation classes are in /app/fond, and CocoView is in /app/views/kinds.
Right now we have two basically identical views, one for level versions and one for article versions. Turn them into a superclass that both views use instead. They could also share the same template.
Then use this superclass view to create new versions views for Components, Systems and Thangs.
It would also be good to link directly to these views from the level, article and thang editors.
Aether can (in some levels) highlight the currently executing line, as well as indicating which lines have been executed already:
See the black gutter arrow and light blue highlight on line five, and the lighter blue highlight on line three? Really? You're the only one.
In our UX testing, players almost never see this even when it would be really helpful, so it's clearly not obvious enough. Also, I just made up the design as a placeholder, but I didn't make it ugly enough to warrant immediate redesign, so no one has come along to do a better job. If you have an idea for a design that meets these objectives, then let's hear it:
Currently, we're using dynamic marker ranges in ACE to add "executing"
or "executed"
classes to spans of text (stacking up to three of the "executed"
markers to indicate multiple executions), as well as adding the "executing"
class to the gutter cell for the current line:
We could get more complicated if this CSS doesn't cut it, but if you have an idea that can be done just by tweaking the effects of those classes, then it'll be easy to try out by tweaking app/styles/play/level/tome/spell.sass
Otherwise, check out how the ranges are done in app/views/play/level/tome/spell_view.coffee:highlightCurrentLine.
If you try to play Rescue Mission (or presumably any level) in Internet Explorer 9, there's only one thing that prevents it from working: there's some bug where goals don't update properly. So when you look at the GoalsView, there appear two copies of each goal, and goals never get fulfilled. When the first goal of getting Tharin out of the dead end can't succeed, then the rest of the scripts don't fire.
I as of yet have no idea why they don't work. Are they not being sent properly to the World? Are they not triggering in the World? Are they not serializing or deserializing properly? Is the GoalScriptModule not recognizing them? Is there a separate problem with the GoalsView?
It's probably something tiny and totally stupid.
When arrows/spears get stuck in the ground, they should appear buried in the ground. We have a die animation that cuts off the points, but we aren't 1) using it or 2) adjusting the rotation to make them point down into the ground.
Our arrow die state:
When an arrow misses and lodges into the ground, it should look kind of like this:
In the combat.Arrow
Component (edit the code in the level editor), which serves for both Arrow and Spear ThangTypes, do something like what the combat.Shell
Component does to set the action
to "die"
when the missile gets stuck in the ground. That should trigger the die
action animation in the CocoSprite. We'll also need to add the action.Acts
Component to the Thang. (I can add that to the Arrow and Spear ThangType default Components when it's ready.)
See #46 for some info on adjusting the apparent rotation / skew / transform properties in general. We'd want to adjust those also so that the arrows and spears appear to be buried in the ground, even though their velocity and rotation may no longer be meaningful (as they're not moving). So you'd add code in app/lib/surface/CocoSprite.coffee in perhaps the updateRotation
or updateAction
methods.
Some Scripts trigger a Backbone Mediator "level-lock-select"
event, which is currently only subscribed by the SpriteBoss and prevents you from clicking on CocoSprites to select them, or from deselecting anything if you have already selected something. This is done so that we can make sure players don't start/stop looking at the spell editor when we're trying to explain something with a script.
I don't have a good test case for right now, but basically there should be code in all other places where we publish "level-select-sprite"
through the Mediator to first subscribe to the "level-lock-select"
event and to not issue the selection event if select is locked. ThangListEntryView is one such place:
One shouldn't be able to click that Arrow Tower or a spell in its spell list to select it if select is locked. Likewise, if one has the spell editor open, one shouldn't be able to change which Spell or Thang is selected through the SpellListView.
Items, like health potions and swords and such, are currently kind of boring: they just sit on the ground. Lame. What instead they should do is what the Coins and Gems do: bob up and down in the air. But it'd be best to not have to embed a bobbing animation in each ThangType we intend to use with the inventory.Collectable
Component.
Instead we should do something like what the WizardSprite does and make the bobbing happen automatically in the updatePosition
method of any CocoSprite with thang.isCollectable == true
.
Once that started working, we could remove the built-in bobbing animation from Coin and Gem, too.
After that's working, we'd probably want to not tie bobbing to the inventory.Collectable
Component, but instead to make a new Component: display.Bobs
or something like that. That would be nice, because then we could attach it to any flying Thangs, and we could remove the special-case code from the WizardSprite, too. All that display.Bobs
would need to do is attach a bobs
or maybe bobsUpAndDown
property to the Thang, and the CocoSprite would read that instead of isCollectable
. If we really wanted, we could configure the bob distance in that Component.
Currently to save subdocuments, one needs to do something like:
state = @session.get('state')
state.scripts = {}
state.complete = false
@session.set('state', state)
We could use a plugin that would make it possible to trim lines like that to:
session.set('state.scripts', {})
LevelBus.coffee would be the prime class to benefit from this. Make sure that the patching still works.
Some candidates for plugins are in the Backbone Wiki.
Read up on Versioning for more info.
Right now if something goes wrong in the middle of switching versions, the system can be left in an unresolved state. If the existing versions are successfully updated but the new version is not successfully saved:
MongoDB does not support transactions natively, so that's not an option.
Possible solutions include:
Bear in mind that any solution must deal with the fact that there are MongoDB unique indexes in place that prevent there being more than one document with the same slug and more than one document with the same minor and major versions.
All of this is contained in the /server/models/plugins.coffee file, where it shows how versions are fetched and created and how indexes work and how named and searchable models work. There are fairly extensive tests on the plugins.spec.coffee file as well.
It's probably too early to think about how to make it at all fun / playable / performant on mobile devices (since who wants to type code on mobile?), though any steps in that direction will be good. But we should at least be able to pluck some low-hanging fruit for making the rest of the site a bit responsive so that it doesn't look stupid or break when you pull it up on an phone to see what it is.
So if someone's on top of the latest responsive real-time media query peer-to-peer streaming CSS4 accessible standards-based user-focused design principles and wants to take a crack at improving our layout for mobile, be our guest--we haven't even started, so it's only up from here!
@gsaines notices this all the time and I can never reproduce--wonder if it's something to do with my Chrome theme or his scroll bars or something. His report:
"When I hover over the available spells in the spell palette and move my mouse left and right, the popup flickers like crazy, it's mostly aesthetic, but does make it hard to focus on the contents of the popup."
This is what the popover looks like:
The problem is somewhere in our use of Bootstrap 2.3's popover in the Tome's, here being triggered by the SpellPaletteEntry. The same bug might apply to other popovers like in SpellListTabEntryView; not sure. The styles for both of those are set in the Tome's style file, since their parent elements are a few layers up from the views triggering them.
Create a system for defining, listing and enforcing all events going through Backbone.Mediator in CodeCombat. Read up on Subscriptions and JSON-Schema for this issue.
All notifications going through Backbone.Mediator only allow one argument: an object. In practice, though, these objects should follow certain structures, and could use some explaining.
Design a way to list events going through Backbone.Mediator and a JSON Schema defining the objects that they pass around. This could be a single file which is an object of subscription names to JSON schemas, or a folder of these files, all of which one way or another are fed to a modified version of Backbone.Mediator which runs tv4 validation on event objects to make sure they conform with their definitions.
This listing would be used to:
This could be done in a few different ways.
cast_begin
, cast
, and cast_end
WizardSprite action sounds, so that they're tied to the visual animations that we already trigger when players are typing.I have no idea how many sounds we need or what they might sound like, but by gosh, it's magic! It should sound like something! It would probably also help the UX to subtly indicate what's going on. Hopefully the sounds wouldn't become annoying.
Thoughts on what sounds we might use are welcome, or even code to add in the sounds. If you find some good sounds, I can put them in the WizardSprite actions, or maybe @sderickson can explain how to add them as interface sounds to our MongoDB. (Non-admins might not have permission to add them directly.)
Firefox, Chrome, and Opera can use the new WebRTC protocol to do peer-to-peer voice and video chat, which might be really cool for multiplayer (or might prove not as versatile as text chat, which we have right now). Given how many frameworks there are that make it easy to try out, it's probably worth an experiment! Here's a semi-recent list I found. @sderickson also saw interesting presentations by XIRSYS and OpenClove a few months ago.
This is, of course, an enhancement for CodeCombat's multiplayer. I'm thinking that even if it doesn't make sense to use the video chat, it could be fun to add voice chat so that players can play together more like a game. We'll just have to try it out and see if it's fun.
Set up endpoints that return multiple documents to take limit values and return cursors so the client can fetch more documents as needed. These cursors could either be directly taken from MongoDB (suboptimal since that takes server resources, but built in and workable) or they could be set up as query modifications passed to the client to be passed back for more information. For example, if the collection is sorted by date, the endpoint might return a timestamp of the last item in the collection, which would be passed back and used for the same query, but only after the given timestamp. The ideal system would be something like Google App Engine's Query Cursors, but for MongoDB queries.
Endpoints that could use cursors include versions, files, and search (which would probably just require the native MongoDB cursor system). These can be found in /server/handlers/Handler.coffee.
The biggest problem when trying to do harder levels in CodeCombat is that players have only one way of debugging their code:
this.say("Rect is at " + rect.x + ", " + rect.y + " with size " + rect.width + " x " rect.height");
It took me 30 seconds just to type that out, and I made a syntax error. This is not okay. As a Hacker News commenter put it:
"Only the very darkest realm would deprive its wizards of the mighty printf spell! To what Silicon demon legion did you pledge fealty to summon this coding abomination upon an unsuspecting land? Winter is truly coming at last."
So why is debugging so bad? Well, for one, serializing things across the web worker boundary is imperfect--you can't pass most World objects because they'll point to Thangs and Vectors and such which contain functions--and very slow if you're sending strings for each step of your algorithm for each frame of the simulation. So not only do you get really boring and unhelpful log statements like "Rect is at [Object object]", but it's very easy to blow up the simulation performance, and you have to either add a better UI than this.say()
for seeing the messages or force the player to go to the browser console. We used to do this, but we shut it off.
We could solve most of these problems, but there is a better way. An amazing way. Just lurking, waiting for us to have time to implement it. CodeCombat's programming environment should allow us to build a time-travel debugging interface that combines the best of both breakpoint-based stepping debuggers and using log statements, but far better than either. It'll be quite Victorian. You'll just scrub back and forward through your code's execution history and hover over any variable to see its value at any point in time.
jsdares actually does something like this already:
In its solution, each statement generates a step message, and when you over over that statement's frame number, it shows the step message below the statement. It also uses blue dots to show which statements were recently executed or will execute. Finally, on the left you can see it shows the values of all currently defined variables. @janpaul123's thesis discusses the interface and motivations in Chapter 3, although the implementation has advanced beyond the thesis since its writing. There is some discussion of how it's done in Chapter 4.
I was thinking of implementing something different. Instead of trying to show all variables and their values, I'd prefer to focus on one variable when the player hovers over it. CodeCombat variables are often complex objects like Thangs, so a simple string isn't going to be enough to get a good picture of their state. You might want to see a Thang's pos
property, or perhaps even its target.target.id
to see who its enemy is attacking. Clearly we're not going to serialize all that state for every variable for every frame for every Programmable method.
Instead, what I plan to do is to keep the simulated World around in the web worker thread, possibly more than one at various intervals to make things even faster, and resimulate up until the current call whenever this kind of debugging info is requested. Then we can use Aether to thoroughly inspect the state of all accessible properties, serialize everything we can, and send it over for custom presentation that's aware of our common types (like Thangs and Vectors).
I think the delay should be just a split second on most levels--suitable for idly hovering over various variables in the code to check them out. Then when the player wants to see a variable change over time, she can just scrub the playback and watch the variable change. It should be especially fast scrubbing forward, since we'll already have the World simulated up until the current frame and can just get the next frame--but it shouldn't be too bad going backwards, either.
With some cleverness plus the insanity of Aether's control flow yielding, we should also be able to scrub/step forward and backward within the current function execution on a statement-by-statement basis. We probably can't use the main playback scrubber for this, but perhaps some keyboard shortcut or traditional step forward/back/over/out buttons for when one wants to get really fine-grained could do the trick. jsdares experimented with a second timeline sort of interface, since beginners are confused by stepping debugger buttons, but as I recall having two was also confusing.
To implement this, we'll also need to do a bunch of improvements over in the Aether project, where I've added a companion issue. This issue can track the progress on the front-end interface as well as the coordination of Aether state harvesting and World resimulation in the background thread.
If we can pull this off well (and polish up some other editor rough edges), it has the potential to turn CodeCombat into the best environment for solving programming challenges that the world has ever seen, since not only will be there a sweet visual interface to see what's going on and gameplay mechanics to make it fun, but the overly powerful debugging, live-coding, and co-op multiplayer should make it possible to solve harder algorithms than you'd be able to do in a traditional environment--programming without a blindfold on, as it were.
We need client testing framework that is more thoroughly integrated with the /app code itself. Here's the gist of the plan:
These steps are in increasing importance. Just doing the first one so tests can be written for and viewed with the development server will go a long way toward covering the app with unit tests and better descriptions of how things work.
Need a way for users to modify permissions of any given document. Something like the share modal in Google Docs, where you can add and remove individuals and set permissions to read, write and owner. This would include:
Highly recommend using Treema to handle building the interface for editing the list of permissions. It's used throughout the rest of the site, and will handle a lot of the grunt work like adding and removing entries. You would only need to implement a node for editing a single row and tying into the user lookup endpoint.
See #23 for more info on i18n tags. Basically, there's no code for internationalizing ModalView titles, which are sometimes dynamic, but are usually specified as the modalTitle
property of ModalView instead of being in the Jade template where we can statically add data-i18n
attributes. We'll need to add that.
Currently when an Archer or Arrow Tower shoots an Arrow, the Arrow does properly rotate in the x-y plane so that it's facing its target, but it doesn't handle the other ones (yaw? pitch? roll?). So it doesn't point up when its z-velocity is positive, nor down when it's negative. It also doesn't appear shorter when flying more parallel to the camera's line of sight than it does when flying orthogonally.
I think one would just need to do some math to update the Arrow Thang's CocoSprite imageObject
transform properties or their shorthands (scaleX
, skewY
, rotation
, etc.) according to the velocity
of the Arrow and the Camera's view. I was trying to do it but got confused. If you like affine transforms or computer graphics, you might have some fun with this one!
It's easy to test: just play Zone of Danger and get the arrows looking good. You might want to make the arrows travel a lot slower by editing their maxSpeed
in their movement.Moves
Component, which would emphasize their arcs. One of the reasons arrows travel so fast now is that they don't look good when actually needing to arc.
The code to write may be in or related to app/lib/surface/CocoSprite.coffee in the updateScale
and updateRotation
methods.
The HUDView uses a DialogueAnimator to animate script dialogue messages in one character at a time while preserving HTML elements. However, it's really inefficient, and when the framerate is low for some reason, it can take a really long time time for the DialogueAnimator to complete (which then makes players wait a long time to go onto the next script on slow computers).
We should make the dialogue animation speed independent of any lag or dropped frames from the rest of the interface being slow, and we should probably profile it to make sure that it isn't contributing to any slowness itself.
@sderickson Did you have any ideas on a better implementation?
Related files:
In the programming.Programmable
Component, the player's code runs within a context that is a copy of part of the World, limited to the properties specified in programmableProperties
. So for example, if I'm coding chooseAction()
for my Artillery, I might have;
programmableProperties = [
'pos', 'setTarget', 'setAction', 'getNearestEnemy', 'say', 'attackRange'
];
All this code should work:
var enemy = this.getNearestEnemy();
if(this.pos.distance(enemy.pos) < this.attackRange) {
this.setTarget(enemy);
this.setAction('attack');
this.say('Die, ' + enemy.id + '!');
}
So we need the getNearestEnemy
, setTarget
, setAction
, and say
methods to actually interact with the World to get the enemy and set the target and action and such. But this code should not work:
this.world.abort();
// Good--doesn't work, because world isn't in context
this.getNearestEnemy().world.abort();
// Oops, that was the real world, wasn't it?
this.getNearestEnemy().health = -9001;
// Yup, you just killed it!
this.pos.z = 100;
// Good--doesn't work because this is just a copy of this.pos
this.getNearestEnemy().getNearestEnemy().pos.z = 100;
// Aaaaa--<splat>
I was trying to get around this with Object.defineProperty
and marking things readonly and such, but I ran into some bugs where it wouldn't work. More testing is needed. Also, more thinking, since I'm a bit confused myself as to how best to do this. I originally thought that making a copy-only context, running the code in that, and them pulling the necessary changes out with a whitelist approach would do it, but then I ran into these problems and made the functions refer to the real functions, and now we have neither full security nor full versatility.
Often we do things like link eager players to specific character classes in the /contribute page, like this: http://codecombat.com/contribute#artisan
When the page loads, though, the scroll doesn't take account the top bar, so we get the character class title cut off:
It should instead scroll like this:
We're already doing something funky with hashes in app/views/kinds/RootView.coffee:
afterInsert: ->
# force the browser to scroll to the hash
# also messes with the browser history, so perhaps come up with a better solution
super()
hash = location.hash
location.hash = ''
location.hash = hash
# TODO: automate tabs to put in hashes and navigate to them here
I wonder if this should be fixed on the HTML/CSS side, or if we should just add more code in afterInsert
to take the top bar height into account.
We use i18next for internationalization, which correctly handles language fallbacks like es-ES -> es -> en when we use it in a CocoView's render
method by calling @$el.i18n()
.
However, when we use code to grab our localization strings in other ways, like for doing script dialogue in SpriteScriptModule, we don't have any of that fallback implemented and in fact use quite lame code:
for response in responses ? []
response.text = response.i18n?[me.lang()]?.text ? response.text
text = sprite.say.i18n?[me.lang()]?.text or sprite.say.text
blurb = sprite.say.i18n?[me.lang()]?.blurb or sprite.say.blurb
sound = sprite.say.sound?[me.lang()]?.sound or sprite.say.sound
Not only do we not get the default Spanish ("es") translation if we looked for the Spain Spanish ("es-ES") translation, but we're repeating all that code each time. Also, this behaves differently than i18next in that it doesn't look for an "en" string, either, instead just using the string set in the base script message. This probably doesn't matter, but it's different from how the Views do, where they fallback to whatever is hardcoded in the template only if the "en" string doesn't exist, either.
This is how the strings are put into the scripts, inside the level editor:
This produces the following object:
{
"text": "G'day, Wizard! Come to practice? Well, let's get started...",
"i18n": {
"es-419": {
"text": "¡Buenas, Hechicero! ¿Vienes a practicar? Bueno, empecemos..."
},
"es-ES": {
"text": "¡Buenas Mago! ¿Vienes a practicar? Bien, empecemos..."
},
"fr": {
"text": "S'lut, Magicien! Venu pratiquer? Ok, bien débutons..."
},
"pt-BR": {
"text": "Bom dia, feiticeiro! Veio praticar? Então vamos começar..."
},
"de": {
"text": "'N Tach auch, Zauberer! Kommst Du zum Üben? Dann lass uns anfangen..."
},
"tr": {
"text": "İyi günler, Büyücü! Antremana mı geldin? Güzel, hadi başlayalım..."
},
"sv": {
"text": "Godagens, trollkarl! Kommit för att öva? Nå, låt oss börja..."
}
},
"sound": {
"mp3": "db/level/52740644904ac0411700067c/gday_wizard_come_to_practice.mp3",
"ogg": "db/level/52740644904ac0411700067c/gday_wizard_come_to_practice.ogg"
}
}
Perhaps we should add a utility function somewhere to grab the best string possible for a given property from any given i18n
object and start using that throughout the code.
Currently views for listing thangs, levels and articles download the complete models, and with enough of them the request can get quite big. Set up the search handler in /server/handlers/Handler.coffee to return only those properties used in the SearchView and its subclasses.
Right now it just says 'Page Not Found'. Ideas for improvement:
The 404 view is in /app/views/not_found.coffee.
Read more about our DB Filesystem for more info.
A folder system would involve:
One implementation would be to create another collection of permission'd documents that are the folders, one document for each folder. Some key elements would include:
See the existing models in /server for how to create collections and give them the permissions plugin. There are probably also perfectly serviceable folder systems that can hook into GridFS, so that could be researched.
There is at least one bug sometimes causing Thangs to think they can't get to places that they can get to, leading them to run straight at their target (since they gave up on actually pathfinding).
There's at least one other bug causing Thangs to not take wide enough corners around structural nav mesh vertices that ends up in them bouncing off of corners a few times, especially if their radius isn't an even multiple of one meter. Try looking at Taunt the Guards, where the archer is following Tharin, and setting her width/height down to 1.5m or less instead of 2m. She'll bounce off corners like crazy.
Part of this is because the grid/pathfinding resolution isn't high enough, i.e., 2.5m ogres are treated as 1.25m radius which gets rounded up to 2m which, with a 1m radius grid and 4m hallways, means the grid thinks they cannot pass.
Those are the only two bugs I know about, but probably there are others resulting from how the nav meshes are generated or how the A* vertices are picked.
In addition to fixing the bugs, it would be awesome to improve performance (should be really easy to do if you look at the code and perhaps cache rectangles' vertices) and to clean it up (the way rectangle vertex associations is currently done is just nasty).
If one were extraordinarily ambitious, one could extend the nav mesh and pathfinding to operate an convex polygons, not just rectangles.
To get an idea of where this code is coming from, just look at the ai.Pathfinds Component and the AI System (from within the level editor) and follow any imports to things like app/lib/world/Grid.coffee and app/lib/world/world_utils.coffee.
This touches upon the code that everyone trounced me with in Gridmancer, too, for generating the nav meshes.
Here are some links I found helpful when implementing this:
Fun fact: I was on an epic coding binge implementing all this stuff in the middle of the week: http://www.youtube.com/watch?v=E0qlr22cF14
The entire Inventory System is just a temporary hack right now and could use more thought in order to make useful levels that do more with inventory and items. I know Mischa's team will need more power here, so perhaps they can spearhead a new implementation of it.
One of the things we need to do is to make item pickup much cooler. Currently the item just disappears and possibly plays a sound. What it should do instead of just disappearing is to momentarily float above the collector's head, possibly with a cloud or bubble behind it, as it plays the sound, then disappear. This would look pretty good and would allow us to let any sprite collect any other sprite without having any sprite-specific collect or collected animations. Here's a hacky example:
It should feel really cool, like Link picking up a sweet item in Legend of Zelda. Having awesome pickup noises per item will also help with that; those are already ready.
Related to that, I know Mischa's team was thinking of being able to give and receive items between Thangs. It would be good to have this item-in-bubble-over-head animation code also support transferring the item, so maybe it first appears above the donor's head, then moves over to the receiver's head, then disappears.
Making items look better is also related to making them bobbing from #48.
The code for this will start in app/lib/surface/CocoSprite.coffee and could possibly extend to SpriteBoss and perhaps a new class designed for this purpose. Mark and Label might be useful examples.
Currently schemas are stored only on the server and fetched separately as needed by the client (see /app/models/CocoModel.coffee for how the schema is fetched, and the /server/db.coffee and /server/schemas/* files for how they are served). Instead, have the schemas in the client and then accessed by the server from the client code. So a general outline of steps are:
This is a good task for learning the ins and outs of how JSON-schemas are tied into the rest of the site.
The development server should be able to just download a copy of the relevant parts of the latest production database whenever it wants, so the production database should probably have a regular (hourly?) cron set up to make a dump of that available for download.
Scott and I were thinking about how it would be awesome if we had a single command people could run (like with curl) that would clone the repo and install all the dev dependencies and everything. This would make it much easier for novices to get involved.
The second part would be to consolidate the coco-brunch, coco-dev-server, coco-mongodb, and maybe coco-test-server and coco-server-test-runner scripts into one script that could be run from a single terminal, so that people don't have to set up five different terminals to get going. We'd strip down any extra logging from coco-dev-server, coco-test-server, and coco-server-test-runner so that the stdout from five things didn't get overwhelming.
I don't know if this is even possible in the browser, but wouldn't it be cool if we could transform the audio on-the-fly (or maybe once for each sound when loaded) so that in app/lib/Surface:playScrubbedSounds, we can play reversed versions of the sounds? Or even fast-forwarded versions depending on the playback speed?
If anyone knows of a cool way to do this kind of audio processing on the fly performantly, it should be a ton of fun.
Make it possible for people to delete files. It would only be useable to admins until a permissions system is in place for files.
This would only require extending /server/file.coffee.
Read up on Versioning and Permissions for this issue.
Currently it's possible to create a new version based on the permissions of any previous version of a given document, effectively making it impossible to actually revoke permissions on a versioned document. Fix the postNewVersion endpoint in /server/handlers/Handler.coffee to base permissions checking on the latest version rather than on the one passed in.
Shortly after we launched our beta, we had so many amazing Diplomats volunteer from across the world to translate CodeCombat into ~20 languages before we could blink. We set up i18next to help translate our static text and also added an InternationalizationNode to Treema to help translate strings inside our levels. Cool.
But where did we put the i18next-based strings? Why, we just hardcoded them into a bunch of CoffeeScript files in app/locale and compiled them into the app, of course!
It's past time for that to change, since I can't keep manually transferring updates for all the languages from the exploding Google Spreadsheet we used to collect the initial translations into those locale files. Instead, we need:
I keep getting bug reports like this: "During casting, sometimes everything got extremely slow and it became almost impossible to type. It made me worry I was going to lose my work, since it's hard to even select the code when it's stuck like this."
I never see it, but maybe my computer is too fast? I don't know. I can probably fix it anyway, although if anyone who does see these kinds of problems wanted to dig into it with the Chrome profiler or something, that'd be awesome.
One thought: move Aether's linting and parsing and such into a web worker? We're going to all this trouble to avoid blocking the main thread by linting and parsing and transpiling too often, but we could do it more often and not have performance problems if we did it in the background instead.
This one doesn't take much programming knowledge, just time. I went through most of the highest-use templates and added data-i18n
attributes so that we could get a first round of localizations done, but I skipped some areas, and a few things have been changed and need new tags. We need to go through the rest of the templates and add the rest of the tags.
Here's an example tag:
h1#site-slogan(data-i18n="home.slogan") Learn to Code JavaScript by Playing a Game
This then can be translated into various languages in our locale data for, say, Latin American Spanish (es-419):
translation =
home:
slogan: "Aprende a programar en JavaScript jugando"
no_ie: "¡Lo sentimos! CodeCombat no funciona en Internet Explorer 9 o versiones anteriores."
# ...
After which someone can change their language to Spanish and see:
instead of:
(We also detect the user's Accept-Language headers to guess their preferred language on first visit instead of always defaulting to English.)
Let's take our example:
h1#site-slogan(data-i18n="home.slogan") Learn to Code JavaScript by Playing a Game
This is a Jade template, so it's just a more concise way to write this HTML:
<h1 id="site-slogan" data-i18n="home.slogan">Learn to Code JavaScript by Playing a Game</h1>
All we need to do is to find elements with text that don't have these data-i18n
tags and add them. I know there are a lot in the /contribute, /legal, /about, /teachers, and /editor pages. If you want to find more, you can just set your language to Brazilian Portuguese or any other language where we have full coverage and look for things that still show up in English.
Sometimes you need to tag a link separately from surrounding text, like in this example:
span(data-i18n="contact.forum_prefix") For anything public, please try
a(href="http://discourse.codecombat.com/", data-i18n="contact.forum_page") our forum
span(data-i18n="contact.forum_suffix") instead.
I've been adding prefix and suffix tags like that so that the links can be internationalized separately. Note the trailing space in the prefix and the leading space in the suffix: if those weren't there, Jade wouldn't put spaces in between the non-linked text and the link. This is an unsatisfactory approach, because no one who is translating these strings sees those extra bits of whitespace. A better way to do this, where Diplomats don't have to care about whitespace, would be useful.
The translations themselves are currently going in app/locale, but hopefully we can just move those to the database soon by doing #22.
Defining inner functions within a method is pretty janky and doesn't work very well with Aether's transpilation assumptions, since we can't yield control or instrument execution well within them. It's also not a good way to organize anything, especially within our little spell editor UI.
It should be possible to just have a "+" button somewhere in the Tome interface that lets players define their own spells (methods), which will then be accessible just like any other spell in the Tome. Not sure where we'd put the button yet, but one obvious choice would be at the bottom of the SpellListView:
Perhaps it then opens up a ModalView asking for the name of the method and which Thang(s) should use it. After the player specifies that, we need to propagate the update across those Programmable Thangs, the Tome, and maybe the LevelSession.
Later we might let players think about saving these methods to their own method library somewhere so they'd be easily accessible in other challenges.
(Originally suggested by @MalcolmMcC)
One of the reasons that method names are so long (attackNearbyEnemy
) is that we've always been planning to add really good autocomplete, so that players don't actually have to type that stuff out. I'm not even just talking about tab-completing identifiers, either--that's something that beginners may not know they can do. We could go all out like jsdares does and obey the Victorian commandment to get something on the screen as soon as possible:
(@janpaul123, you followed the vision--how did that end up working for beginner jsdares players?)
So how will we do it? There are two parts:
For finding completions, our secret weapon will be Aether, since it should know not only which properties are available on this
, but also through runtime introspection can actually know any property available on anything, what its type is, and what its current value is. I've opened a related issue over on that project to get that part of it ready.
For autocomplete UI, I hadn't thought too much about it beyond starting with something simple and trying to evolve it toward the screenshots above while iterating through some UX tests. Developing the interface is what this issue (in the main CodeCombat project) is about.
I found a couple potentially useful links for doing autocomplete within ACE:
I'm guessing we'll probably want something weird enough that we won't really go through ACE for it, though.
The past/future paths are useful sometimes, but overwhelming or unintuitive at other times, and often are terribly slow. See more info on paths in the wiki.
The code is all in app/lib/surface/path.coffee It'd be nice to:
One could propose a better design, or just start playing with the code. Any improvements are welcome.
In UX testing, in support chats, everywhere--no one sees or thinks to click that Guide button. We've tried saying "Check the Guide" in the default code comments, which helps a little, but players still don't usually notice that, so more is needed. @sderickson 's ideas:
I thought some sort of redesign of the button itself might be in order. Maybe the word "Guide" isn't right. Maybe we need to make the first three levels' scripts need to put the big red arrow on it and tell them to click it for more info, which might at least get players playing through in order into the habit.
Related files:
It's not obvious enough yet when the code is ready to cast, is casting, or has been cast. Beginners don't realize what's going on. Here are what things look like now:
Any improved design might think about improving more than just the cast button, like changing the look of the spell editor itself. And anything involving the cast button might consider a different button design, because this one falls over for i18n:
What I'd imagine we'd do is to come up with a new design for the cast button that gives it more space, more visual prominence, and a much more obvious animation. It would be good if it looked cool, too! See the Tome docs for info on how this stuff works.
Note that the cast button does include this kind of world-casting-progress fill as it's casting. That should either be preserved in any upgraded design or moved to some other area of the interface.
Big issue here, but this could be taken any number of ways (which are not necessarily mutually exclusive). Here are some possible routes I had in mind:
This is a major project, although not as large as it sounds. The server is much, much smaller than the client, so reworking the entire thing is not a hideously large project. If you're interested in this issue you should get in touch with us to talk about how to approach this.
When people send me their multiplayer links for code submissions, I often see one of two problems. Either JSHint is warning me about mixed spaces and tabs, or I only see the first line of code. Example:
// Fill the empty space with the minimum number of rectangles.
Whereas the other player sees it just fine. If I look at the session, it contains all the code with "\r\n"
in there:
{
"code": {
"thoktar": {
"plan": "// Fill the empty space with the minimum number of rectangles.\r\n// (Rectangles should not overlap each other or walls.)\r\n// The grid size is 1 meter, but the smallest wall/floor tile is 4 meters.\r\n// If you can do better than one rectangle for every tile, let us know!\r\n// We'll help you find a programming job (if you want one).\r\n// Check the guide for more info, and press Contact below to report success.\r\n// Just include your multiplayer link in the contact email.\r\n// Make sure to sign up on the home page to save your code.\r\n\r\nvar grid = this.getNavGrid().grid;\r\nvar tileSize = 4;\r\nvar width = 0;\r\n\r\nfor (var y = 0; y + tileSize < grid.length; y += tileSize) {\r\n for (var x = 0; x + tileSize < grid[0].length; x += tileSize) {\r\n var occupied = grid[y][x].length > 0;\r\n if (!occupied) {\r\n width += tileSize;\r\n } ......."
}
}
}
ACE probably has some way of sanitizing things so that you can only have \n for newlines and spaces, not tabs, so I would start looking at the ACE docs. Otherwise, we'll probably want to do some sanitization in the spell editor, which might be a bit tricky if Firepad is enabled.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.