Thursday, 20 November 2014

Maddening Javascript

Does this look right to you:

function (x) {return function() {_.buy(x)}}(i)

Last night I ran into a problem that I didn't quite know how to solve. I've recoded features so that they have multiple stages contained within them. For example, instead of having a separate feature for each rarity of items, I have a second feature, Items, that you can buy all the ranks through. First I recoded it so that the stages were linear - the feature kept track of which stage it was on by use of an integer - then I decided that I should redo it so that it's non-linear. This is all in preparation for adding a town with upgradeable buildings, so you can see how I would want to have multiple choices of upgrades at the same time.

So when you click a feature I need the game to bring up a menu, and I need to create that menu by running through the stages and seeing which ones are currently available so it can add them to the list.

The game has only one menu object. If you have a menu open and you click another thing that opens a menu then the first menu will disappear, that's because it's actually the same menu that is just relocated and drawn. To create a menu you have to pass the menu an array. Each element of the array has to either be a string or an object. If it is a string then the string is appended to the HTML of the menu. If it is an object then the game creates a div and sets its innerHTML to the name property and adds an event listener that runs the Func property on a click.

So I looped through the stages of the feature in a for loop and whenever I came to an available feature I pushed two things to the array that would generate the menu. First I pushed some text that describes the upgrade and its cost. Then I pushed the object to make the menu, which has a name of something like "Buy this" and a Func of function() {_.buy(i)} where i is the variable I'm looping on.

When I did this, the menu came up correctly and it correctly offered to let me buy common items. However, when I clicked the option, it bought legendary items instead.

Because the function that I attached to the menu was defined inside the same function where the loop was happening, when it went to pull up that function it did it within the scope of the function that created the menu. That means that instead of using what i was at the time when that menu option was created, it used what i was now, after all menu options were created, which is the last stage on the list.

At this point I actually just got up from my computer and walked away. The logic of passing functions to functions has occasionally caused me problems, but I actually just couldn't think of what I should do about this.

Once my head was away from the frustration of the situation and into the cold air, the answer was clear, and not that challenging. All I had to do was make a function that returned the function and it would fix the value being passed at that moment. As long as I actually execute the function that I'm passing i to, the value will be locked in at the time of execution and the game won't look back to see what i is when the menu item is clicked.

The resulting code has the look of ln ex, but it does its job.

Anyway, yes, I'm working on a town with upgradeable buildings, the primary purpose of which - in it's initial implementation - will be to get a guildhall so that you can learn a class and then get class abilities.

No comments:

Post a Comment