Palagpat Coding

Fun with JavaScript, game theory, and the occasional outbreak of seriousness

Monday, September 27, 2010

Reclaiming Content: A Manifesto

I've been thinking for a while now about trying to get away from using a hosted blog, but I wasn't satisfied with WordPress — I found it too complicated to do what I really wanted with it, plus it opened me up to all kinds of comment spam and malicious attacks of other sorts. Yes, I realize that I could keep it patched up and minimize my attack profile, but it would still be there. That's the hazard of using something that's widespread: it makes an attractive target.

Beyond that, I'd also like to archive the URLs to things I share online, e.g. on Twitter. There are services that help with this sort of thing: del.icio.us, bit.ly, etc etc etc. But anytime I'm giving my data to someone else, I'm a little uneasy, for a couple of reasons:

  1. What are they going to do with that information? Google and Facebook have both gotten really good at targeting ads at me, and I don't like it.
  2. What happens if their servers suddenly dump my data, like Twitter recently did to my @-replies, or like Google Buzz did a few weeks ago to Leo Laporte?

So, here's what I'm going to do about it.

Manifesto of the Independent Voice

I reject the notion that free on the Internet can only be achieved through selling one's soul to corporate interests. I declare that search engines and blog services do not own the rights to my voice and my words; I do!

I hereby declare my intention to divorce myself from all third-party content management systems. If I cannot find a suitable open-source blogging platform or content management system, I will build my own.

Small.

Fast.

Simple.

Free and Independent.

Mine.

Labels: , ,

In Case You Missed It: JSConf.eu 2010 (day 2)

As expected, Day 1 of this weekend's JSConf.eu JavaScript conference in Berlin was, as the kids say, EPIC. So much so, in fact, that I found myself following the Twitter channel all morning, and retweeting WAY more often than usual. Of course, a lot of those retweets were to presenter-uploaded slide decks, but here they all are in one place, in case you missed it:

Speaker(s)Topic
THEATRE (TRACK A)
Douglas CrockfordLoopage
Jens ArpsThe hitchhiker's guide to client side persistent data storage
Ryan DahlTechniques for a single stack world
Jed SchmidtGetting functional with (fab)
Tom Hughes-CroucherDon't write spaghetti code in server side JavaScript
Kevin Dangoor, Joe Walker & Patrick WaltonBespinSkywriter: The JavaScript Programmer's Editor
Jörn Zaefferer & Nikolai OnkenRobotic JavaScript
Mark WubbenBrowser Extensions for Web Hackers (code)
Alexander LangNot your unit test (note: Chrome on OS X)
Stoyan StefanovPerformance Patterns
Chris Williams (JSConf co-creator)Community.js

MUSIC SCHOOL (TRACK B)
Paul IrishThe State of HTML5 : Inaugural Address (note: Webkit/Chrome required) (sidebar on polyfills)
Tobias SchneiderNot your Mother's JavaScript!
Sebastian WernerIntroducing Unify - A Framework for Cross Platform Applications
Guillermo RauchSocket.IO: Web Sockets for Everyone (nodestream, demo)
Thomas SteinerPirating the Semantic Web with JavaScript
Rotislav HristovThe Art of deep linking and AJAX crawling
Kris KowalCommonJS, I Promise
Aaron QuintThe Front-end Takeover
John David DaltonSrsly R1pp3d J@vaScript
embedJS teamWhat is embedJS (I don't see this one on the schedule, only in the Twitter stream; where was it?)

Undoubtedly there will be more slide decks uploaded as presenters return home over the next day or two, and when they do, I'll update this post to reflect those additions. For now, though, I've got plenty of reading material to digest, and to share with my co-workers. ;)

Projects released and/or updated this weekend (as compiled by Brian Leroux):

Yet another great conference has come and gone, but now we can start looking forward to JSConf 2011. And next time, I'm going to do all in my power to give my recap first-hand.

Updates:

  • 9/27 0747 - Added link to Jed Schmidt's slides
  • 9/27 1403 - Added link to Sebastian Werner's slides
  • 9/27 1732 - Added link to Tom Hughes-Crouche's slides
  • 9/28 1108 - Added link to Jens Arps' slides
  • 9/28 1442 - Added link to Alexander Lang's slides (note that they require Chrome on OS X for best experience)
  • 9/28 2330 - Added link to Tobias Schneider's slides

Labels: , ,

Saturday, September 25, 2010

In Case You Missed It: JSConf.eu 2010 (day 1)

This weekend, the second annual JSConf.eu conference was held in Berlin, Germany. If you've been keeping track, this JSConf juggernaut just keeps getting bigger and more epic with each successive iteration. Continuing my series, In Case You Missed It, let's take a peek at this weekend's published schedule and the accompanying Twitter stream to see if the trend continues.

Day 1: Saturday, September 25, 2010
Speaker(s)Topic
THEATRE (TRACK A)
Dion Almaer & Ben GalbraithUsing the Web to deliver the next wave of computing experiences
Peter HigginsYour library sucks, and why you should use it
Jenn LukasJavaScript + Web Standards II: The Quickening
Tim CaswellTechniques and Tools for Taming Tangled Twisted Trains of Thought (PDF)
Ulrike MüllerServer-side JavaScript the untold story
Nicole SullivanCSS Lint for Massive Sites
Brian LeRouxPhoneGap: Love the Web and Lose the SDK
Ben FirshmanLessons learnt pushing browsers to the limit (links: 1 2 3 4)
Mutwin KrausUsing canvas to develop classic 2D games
Rebecca MurphyThe jQuery Divide
Pete LePageChakra: Building A New JavaScript Engine For Internet Explorer 9
/be (Brendan Eich)Proxies are Awesome!

MUSIC SCHOOL (TRACK B)
Robert NymanHTML5 APIs - The new Frontier
Nicolas Garcia BelmonteCreating Interactive Data Visualizations for the Web
Sebastian Deutsch & Stephan SeidtIf it moves they will watch (sample code)
Paul BakausAves Engine: High performance browser games
Mikeal Rogersnode.js + CouchDB == Crazy Delicious
Felix GeisendörferDirty NoSQL (code, blog)
Philip Hofstetternode.js in production use: tempalias.com
Markus FranzBetter Life with shared resources
Joe McCannRapid Prototyping for Multiple Platforms with JavaScript
Fabian JakobsKick ass code editing and end-to-end Javascript debugging

Some of my favorite finds/revelations of the day:

Look for tomorrow's recap late in the afternoon, and as always, if there are slide decks from other talks online that I don't have linked here, please note it in the comments below, and I'll update accordingly.

Updates:

  • 9/26 0530 - Added link to Rebecca Murphey's slide deck
  • 9/26 1813 - Added links to Jenn Lukas & Fabian Jakobs' slide decks, and removed speakers whose sessions were cancelled.
  • 9/26 1839 - Added link to Philip Hofstetter's slide deck
  • 9/26 1847 - Added link to Mutwin Kraus's github repository (which includes the slides)
  • 9/28 0549 - Added link to Brendan Eich's slides
  • 9/28 1119 - Added link to Felix Geisendörfer's slides & recap

Labels: , ,

Monday, August 23, 2010

Cloning Zelda: Candles, or the Magic of dojo.connect

The versatile blue candle

It's been a while since I've worked on Canvassa, my HTML5 love letter to the original 8-bit NES Legend of Zelda. If you remember last time, we worked out some kinks in the code that were causing it to crash in Google Chrome. Today, it's back to implementing missing functionality: namely, how to make the blue candle behave appropriately. Fortunately, Dojo provides us a way to do this, and it's actually a pretty cool feature of the framework. Read on to see how it works.

Read more »

Labels: , , ,

Wednesday, June 09, 2010

In Case You Missed It: TXJS

Last Saturday was the inaugural TXJS (Texas JavaScript) conference in Austin, Texas. Unfortunately, as with JSConf, I wasn't able to attend, but I still wanted to stay abreast of its happenings. Just as I did with JSConf, I've looked over the schedule, picked through the Twitter stream, followed the presenters, and where possible, found links to the slides they each presented. All for you, Dear Readers (well, for me too... but mostly for you ;) ).

So anyway, without further adieu, here we go!

Read more »

Labels: , ,

Tuesday, May 04, 2010

JSConf 2010: Final Thoughts and Links

Today I saw in my blog reader that Guillermo Rauch over at DevThought has posted his list of JSConf links, similar to mine (Day 1 and Day 2). He's got a few links I haven't got in mine, but I have some he doesn't, too (he only listed Track A talks). So in the interest of cross-pollination, I've updated my posts and will give him a heads-up of my additions as well, when I can (my work firewall blocks his site for some reason).

Also, in the interest of pointing out other coverage of the best pirate-themed JavaScript conference on the East Coast, here are a few other blogs that have posted cool wrap-ups:

  • Cowboy, a.k.a. Ben Alman, posted this great summary of the talks he attended, complete with photos
  • Kevin Dangoor posted a similar wrapup to his own blog, with some nice commentary on Day 1 talks.
  • Ted Leung did a great overview of the conference as a whole
  • Rey Bango posted a bunch of awesome video interviews he took while attending. Go. Watch. I'll wait.

Oh, and in only-slightly-related news, I'm now on Reddit. Don't know that I'll post there much, but thought it was worth mentioning.

Finally, a note on plans. I think enough people appreciated these "In Case You Missed It" posts, that I'm going to try to keep doing them — at least when the conference interests me. Here are the next few I've got my eye on covering, time permitting:

ConferenceDate
GDC Canada6-7 May
Google I/O19-20 May
TXJS5 Jun
VSLive!2-6 Aug
GDC Austin5-8 Oct
JSConf.eu25-26 Sept

Labels: , , , ,

Sunday, April 25, 2010

How to move to Blogspot and keep your blog on your own domain

Recently faced with the problem of how to have my blogs remain in their pre-existing locations while complying with Blogger's mandatory move away from FTP publishing, I think I've found a pretty easy solution: if you're on a LAMP server (which I suspect most of you are; it's nearly ubiquitous), it's as easy as removing all other index.* files and creating a new index.php file in the appropriate folder, and dropping in these 5 lines of PHP code, changing the URL in the first line as appropriate:

$session = curl_init("http://your.web.address");
curl_setopt($session, CURLOPT_HEADER, 0);
$content = curl_exec($session);
curl_close($session);
echo $content;

cURL is the secret sauce in there, and I could go into a lot more depth, but if you don't need to worry about authentication or anything more complex than a simple page-fetch, there's no need. I allow the post-comments and labels pages to link directly to Blogspot, and I use Feedburner to host my RSS feeds, so this simple solution gets me 95% of the way there. The rest is just resource-management and wise template design.

I love it when something that looks like it'll be difficult turns out to be so easy! But please, if I'm doing anything that opens me up to possible security problems (I don't think I am, but...), please let me know in the comments.

Labels: , , ,

Friday, April 23, 2010

In Case You Missed It: JSConf.us 2010 (day 2)

Here's part 2 of my attempt to distill the juicy bits from last weekend's JSConf presentations (here's part 1). Of course, this is only a pale imitation of the experience of actually attending the conference, but hopefully it will serve to keep you, dear readers, up to speed on the latest, bleeding-edge movements within the JavaScript community. Let's get to it:

In Case You Missed It: JSConf.us 2010

Day 2: Sunday, April 18, 2010

Speaker(s)Topic
TRACK A
Mike Ball & Evin GranoSproutCore: Introducing Greenhouse
Dmitry BaranovskiyRaphaël the Great
CommonJS Panel DiscussionJavaScript Outside of the Browser - Where it is and where it's going! (wait for the video)
Makinde AdeagboPrimer: Facebook's 2k of JavaScript to power (almost) all interactions
John-David DaltonAll you can leet - Coding for performance, CSS engines, and sandboxed natives
Felix GeisendoerferDirty NoSQL (cancelled due to Icelandic ash)
Jed SchmidtA (fab) approach to web apps
Justin MeyerThin Server Architecture and JavaScriptMVC 3.0 (site)
Steve SoudersThe Best of Steve
Marak Squires, @ScurvyConf Masterhook.io: the crowd favorite from ScurvyConf
Billy HoffmanJavaScript's Evil Side (introducing Zoompf)
/be (Brendan Eich)"An Introduction to JavaScript" (was actually a discussion of JS's future; wait for the video)
Alex RussellGoogle Chrome Frame
TRACK B
Jonathan JulianHangover.js
Shea FrederickExt JS App UI Morphing
Kyle SimpsonDude wheres my UI Architecture
Adam MooreRunning YUI 3 on Node.js
Dean LandoltPintura, Perstore and Awesome-Oriented Persistence
Suvajit GuptaColossal JavaScript
Malte UblJavaScript - The Private Parts
David FurferoSexy.js Boots Bundles Binding
Rob TsukMojo: Developing and Building a touch UI framework for mobile using the Web (or, Writing webOS Services in JavaScript)
Gonzalo CorderoCreating custom modules using YUI3
Alexandre MorgautTaking over the enterprise one hook at a time (cancelled due to ash)
Jan LehnardtEvently: Declarative CouchDB Applications that practically write themselves
Brian LeRouxPhoneGap Hack Around
Tom, Jenn, Adam & DerekJavaScript. @$#*% yeah. (wait for the video)
Ray MorganMustache is simplifying your templates
Brian LeRouxbest of wtfjs

As with Part 1, this is what I've been able to find so far. A lot of the Track B and panel discussions were largely unscripted, and I'm pretty sure Track B wasn't taped, so A lot of the slide decks haven't shown up online yet, so I've tried to link to the most relevant blogs talking about the topics in question. In a few instances (e.g. Rob Tsuk's talk on Mojo/webOS development for Palm), I've failed to find much of anything relevant online, so we'll have to wait and see what (if anything) eventually finds its way online. This is of course one of the big reasons why actually attending a conference is preferable to just reading about it after the fact, and I highly encourage you to take that opportunity when it presents itself.

As before, if there are slide decks from other talks online that I don't have linked here, please note it in the comments below, and I'll update accordingly.

Updates:

  • 2010-04-23: @Getify corrected my bad assumption about the Track B talks, so I've revised the last paragraph.
  • 2010-04-26: Gonzalo Cordero just posted his YUI3 talk slides, so I've added them.
  • 2010-04-27: Dude sent me the link to JDalton's slide deck, so I've added it to the Track A listing.
  • 2010-05-05: Paul Irish graciously shared his copy of Justin Meyer's JavascriptMVC notes, so I've linked them in.
  • 2010-05-12: Tom Hughes-Croucher posted his JSConf recap on the YDN Blog, and it included refs to a couple of the Yahoo presentations that I'd been missing. They're now linked into the table.

Labels: , ,

Tuesday, April 20, 2010

In Case You Missed It: JSConf.us 2010 (day 1)

Last weekend, the second annual JSConf conference was held in Washington DC. If you've been reading for a while, you know I went last year and loved it. This year, unfortunately, my work and personal schedules conflicted, so this week I'm playing catch-up: looking over the published schedule, then picking through the Twitter streamfirehose to find the presenters, and where possible, the slides they presented.

In doing this, it occurred to me that there may be value in what I'm putting together, at least until the conference's producers can catch their breath and upload the session videos (which, experience tells me, might take a little while). With that introduction, I present to you the first of what may become an ongoing series, which I'm calling In Case You Missed It:

In Case You Missed It: JSConf.us 2010

Day 1: Saturday, April 17, 2010

Speaker(s)Topic
TRACK A
Douglas CrockfordReally. JavaScript.
Michael ErlewineMozilla's JetPack
Ben Galbraith, Dion Almaer, & Matt McNultySolving Device Fragmentation: How Do You Support 12,320 Different Mobile Platforms?
Francisco TolmaskySocratic: Documentation Done Right
Jenn LukasJavaScript and Web Standards Sitting in a Tree
Ryah DahlLess is More in Node.js
Tobias SchneiderFlash is dead, long live Flash! (Gordon: a pure-JS Flash replacement)
Aaron NewtonProgramming To Patterns (per Guillermo Rauch)
Tom Hughes-CroucherPiratin' the YQL way
Aaron QuintMaking Bacon: Making Code (video, blog)
TRACK B
Chris Williams (JSConf co-creator)How I Pulled off JSConf
Brian MitchellJavaScript's Twin (Lua for JavaScript developers)
Jeremy AshkenasA Cup of CoffeeScript
Paul IrishProgressive Advancement on the High Seas of Web8
Zach CarterBuild your own programming language with JavaScript
Michael MahemoffThe Lost Hacks Ridiculous Browser Tricks from the World of Single-Page Applications
Nikolai OnkenThe browser controlling hardware real pirate hackery
Nikolai Onken & Tobias von KlipsteinWhat the heck are HTML5 Apps?
John ResigCall for a better DOM API
Kevin Dangoor & Patrick WaltonBespin: the extensible in-browser code editor
Jeffrey Van GoghThe Reactive Extensions for JavaScript
Alistair MacDonaldWebGL Demo Overload
Matthew FowleNew Web Architecture *

As of this morning, this is what I could find. If there are slide decks from other talks online that I don't have linked here, please note it in the comments below, and I'll update accordingly.

Footnotes:

  • This is actually Matthew Fowle's ScruvyConf presentation; he did his Track B presentation slides on the fly.

Updates:

  • 2010/04/20 1:12pm - akahn mentioned in the comments that the title of Aaron Quint's talk was wrong; I took the original published name, but the slides carry a different one. The table now reflects both.
  • 2010/04/20 2:35pm - Looking over this table, I realized I'd left off the entire back half of Track B. It's there now.
  • 2010/04/20 10:13pm - Ray mentioned in the comments that Jed Schmidt's (fab) talk was moved to Day 2 Track A, so I've removed it from the Track B list here.
  • 2010/04/25 - Matthew Fowle sent me his contact info, so I've linked him up above.
  • 2010/04/28 - Chris Williams has provided me more of the slide decks that the presenters gave to the JSConf organizers. I will be putting these online and linking to them as they become available; this morning I've linked up Tobias Schneider's Gordon slides.
  • 2010/05/04 - Guillermo Rauch over at DevThought has posted his list of JSConf slides, and had a few I'd missed; I've added these now.

Labels: , ,

Saturday, February 13, 2010

Cloning Zelda: Fixing Chrome

Chrome displaying a 'broken' tab

This week, an epic snowfall in the mid-Atlantic states kept me home from work for four straight days, giving me lots of spare time to play with my kids, and work on some side projects. One of these was addressing a long-standing problem with Canvassa: namely, why it was broken in Google's Chrome browser.

The initial impetus for this task came from a comment left by Gaby de Wilde on a recent blog entry, in which he asked if I knew why the project wasn't working on Chrome. I was a bit embarrassed to admit that I wasn't sure, so it drove me to look into why.

Step 1: Get My Feelings Hurt

First stop was to fire up Chrome and open the JavaScript Console. Then I loaded up the main Canvassa URL and examined the error console. Sure enough, after loading several of my class files, it barfed on Game.js. Unfortunately, due to the nature of Dojo's on-demand script loader, the console didn't actually tell me WHY that class didn't load.

Given that the error console wasn't being very forthcoming, my next stop was Douglas Crockford's JSLint code-validation tool. Needless to say, it lived up to its billing and "hurt my feelings," at least a little bit. It turns out I was cutting quite a few corners that Crockford argues shouldn't be cut: omitting semicolons, defining my variables wrong, and not filtering for..in blocks seem to be my most common errors. Firefox and IE don't care about this sort of thing; they just blithely ignore it and carry on with what you intended to say, instead of making a scene in front of the entire classroom, going on about how "rules are for everyone, including you, Mister Potter."

Ahem.

Step 2: Learn Something About Chrome's Canvas Implementation

Once JSLint had issued me 50 demerits and was satisfied with the quality of my Game.js class file, I uploaded the fixed copy to my web server and went back to Chrome to reload the main page. Success... for a few minutes anyway. After playing the game for a few minutes, I began to notice that the game would, at seemingly random times, appear to "freeze up" and stop drawing its sprites. After a few seconds, though, they would all come back and the game would resume. Opening the JavaScript console again, I noticed that the freezeup occurred every time the app generated a flood of these errors:

Error drawing game sprites: DOMCoreException [in game_drawSprites(default)]

I added some additional logging to the game_drawSprites() function, and was able to determine that the exception was occurring in two cases: when Leevers or Zolas went under the ground/water, and when Link killed monsters. Looking for commonalities in those two cases, I found the answer: in both the "digger"-type monsters' animations, I defined the "under water/ground" animation — which should be invisible to the player — by specifying the animation frame's sprite coordinates as outside the boundaries of the source image. Looking in monster/_base.js, the base class for all monsters, I saw that I was doing this same thing in the default monster-death animation:

// /loc/monster/Zola.js: 'underwater' animation definition:
  this._stateDefs[4] = { name: 'underwater', ... anim: [
     [ {x:900,y:900,t:120} ]
  ]};

// note that the monsters.png image is 160x304 pixels, so (900,900) is off the edge

// /loc/monster/_base.js: 'die' animation definition:
  { name: 'die', ... anim: [
    [ {x:64,y:16,t:6},{x:80,y:16,t:3},{x:200,y:16,t:20} ]
  ]}

// again, (200,16) is beyond the 160x304 edge of monsters.png

When I originally set up the sprite animation system, I thought I was being terribly clever to define "invisible" animation frames in this way. My good buddy Firefox just ignored these calls, or at the very least failed quietly, so no sprite would be displayed for that animation tick. Chrome, however, doesn't see this as clever at all: if you try to pass invalid x,y coordinates to Canvas's 2d context object's drawImage() function, it throws a big, loud exception.

Of course, my approach had one other problem as well: if I ever expanded the monster sprite tile image to include more sprites, these supposedly "invisible" animations could suddenly be showing frames of some other sprite's animations! Yeah... bad idea.

Step 3: Go With What I Know

As with my last Canvassa update, the solution to this problem came to me by way of my MUGEN experience. With that game engine, the way you define an "invisible" animation frame is by using a sprite index of -1 (note that MUGEN's animation frame format is spriteX, spriteY, offsetX, offsetY, time):

; teleport: disappear and reappear behind my opponent
[Begin Action 1200]
1200,0, 0,0, 5
1200,1, 0,0, 5
1200,2, 0,0, 5
-1,-1, 0,0, 20  ; invisible for 20 ticks before re-appearing
1200,3, 0,0, 5
1200,4, 0,0, 5
1200,5, 0,0, 5

So, adopting this convention, I made the following, small changes to my code:

// /loc/Sprite.js: added this single line to draw():
   if (cut.x === -1 || cut.y === -1) { return; } // (-1 means "don't draw me")

// /loc/monster/Zola.js: changed the 'underwater' animation definition to:
  this._stateDefs[4] = { name: 'underwater', ... anim: [
     [ {x:-1,y:-1,t:120} ]
  ]};

// /loc/monster/_base.js: 'die' animation definition:
  { name: 'die', ... anim: [
    [ {x:64,y:16,t:6},{x:80,y:16,t:3},{x:-1,y:-1,t:20} ]
  ]}

Once I made those changes and uploaded them to my server, I reloaded the game url in Chrome, and it ran without a hitch. Go on, see for yourself.

So MUGEN saves the day (again). I'm always pleasantly surprised when experience in one realm helps me solve problems in another, but considering how often it happens, I really shouldn't be. Experience is experience... no matter what language or platform it comes from.

Labels: , , ,

Monday, January 25, 2010

Why data transformations are hard (part 1)

Per my plan for January, last Friday was the day I'd planned to have several EFR segmentation files ready for use later on in testing the software I'm going to build for my Linguistics MA capstone project.

My task for that week was to take several XML movie transcripts (in segmented, time-aligned XCES format) and spit out text files in a rather simple format I'll call EFR-segmentation (or segfile for short), which can be parsed by other pre-existing tools in the EFR toolchain. Here's a sample of the input and target formats, so we can clearly define our start and end points:

XCES format (the source):
  
    
EFR-segfile format (the target):
2460-2520 (00:01:20:20 - 00:01:22:11)
Scene 20: T14S
//T: So, you do like it, don't you?

Since the starting point was an XML format, my first thought was to use XSLT transformations to accomplish the reformatting. I created a stylesheet, segfile.xsl, to transform the XCES data files into something closely resembling the EFR-segfile format... but it was lacking in a couple of minor-but-not-insignificant ways. Observe:

Almost-but-not-quite-segfile format:
00:01:20,664
Scene 20: T14S
//T: So , you do like it , don' t you ?

The major problem with this output is the timestamp, which in XCES is formatted like hh:mm:ss,ms. EFR-segfile's timestamp format, on the other hand, is start_frame-end_frame (hh:mm:ss:frame - hh:mm:ss:frame). The milliseconds-to-frames part is easy:

var old_format = "00:02:36,532";
var parts = old_format.split(',');
// X ms * (1 sec/1000 ms) * (30 frames/1 sec) = Y frames
var new_format = parts[0] + ':' + Math.round(parseInt(parts[1]) * 0.03);
// new_format == "00:02:36:16"

... but the rest of that format change is not so simple. We're lucky in the above segment, because there are two timestamps: one at the beginning and one at the end. This seems to be the exception, however, and not the rule, so we'll still need to keep some kind of "current timecode" variable on hand while our parser works its way through the file. Once this is addressed, a second task is to convert the segment start and end times from the hh:mm:ss:ff format into raw frame counts, since EFR segfiles require both in order to have a valid timestamp line. This is a simple enough calculation, but again, doing it in XSLT is like trying to cut a 2x4 with a hammer, when we have other tools in our toolbox.

Besides the timestamp-formatting issues, there was another glitch: the punctuation characters in the transcript line spit out by the XSLT are padded by unnecessary whitespace. For sentence terminators and splicers (periods, commas, etc), this doesn't present a huge problem. But inter-word apostrophes, indicating contractions, split up a word in such a way that any downstream term lookup routine will fail to match it properly. This kind of minor text cleanup would be a snap with my Bat'leth tool; it's designed with text parsing in mind, and has a lot of flexibility. But unfortunately, the timestamp conversion is more complex than Bat'leth's search-and-replace regular expressions can handle, so even if I used it for transcript cleanup, I'd still need another tool to do the timestamps.

Given these shortcomings, I ruled out both XSLT and Bat'Leth as monolithic solutions, although each could contribute a step to the overall parsing workflow. I really wanted to have a single piece of software that could do both parts, however, so I decided to try doing it in Javascript. Then, I fell down the hole that is cross-browser XML — which is a whole different blog post I'll have to write soon. (executive summary in one word: complicated). Given the difficulty of that approach and the pressing deadline, I ended up writing something quick and dirty in Visual Basic 6. Not my first choice, but it got the job done.

The story doesn't end there, though. One of the movies I wanted to treat was 1985's The Goonies, something of a cultural touchstone for my generation. I found an XCES alignment file for the movie in the OPUS OpenSubtitles corpus that I'm using, fed it through my parser, and got this EFR-segfile. Looked pretty good, I thought... so I fired up my EFR authoring software to verify the transcript alignment. And.... it didn't match up! Tune in later this week for part 2 of this post, when we try to figure out why.

Labels: , , , ,

Friday, January 08, 2010

View-Source is Good?

Alex Russell, founder and president of the Dojo Foundation, recently wrote on his blog about the importance of "view source" to the evolution and growth of the World Wide Web:

Web developers get started by taking some code, pasting it into a file, loading it in a browser and switching between editor and browser between even the most minor changes. This is a stark contrast with other types of development, notably those that impose a compilation step on development, in which the process of seeing what what done requires an intermediate action. In other words, immediacy of output helps build an understanding of how the system will behave, and ctrl-r becomes a seductive and productive way for developers to accelerate their learning in the copy-paste-tweak loop. The only required equipment is a text editor and a web browser, tools that are free and work together instantly. That is to say, there’s no waiting between when you save the file to disk and when you can view the results. It’s just a ctrl-r away.

As I read the above quote, it hit me: this is exactly why I love developing in old-school Visual Basic, and why even now VB.NET doesn't feel natural to me. For almost 10 years, my work was primarily done in Visual Basic 5 (and later 6), where the normal process of development runs something like:

  1. Design something in the UI
  2. Write an event handlers for the UI component
  3. Run the app in debug mode and test the new feature
  4. Break program execution and tweak the event handler
  5. Continue program execution and test the revised code
  6. Repeat steps 1-5 as necessary

This is precisely the same reason why I love web development so much: the immediacy of the feedback loop, the ability to see your changes almost as you make them, and to, essentially, evolve the code to achieve your end goals. Tools like Firebug have made this process even easier, allowing me to actually poke and prod UI elements in runtime without having to change and refresh the underlying code. And I absolutely love the ability to see a cool effect coded by someone else, and do the old "right-click, inspect element" to see how they did it.

Interestingly, the parallels between VB and the web don't end there. From time to time you hear developers of languages like Java and C++ pooh-pooh both Visual Basic and Javascript as being somehow "inferior". The simple syntax, lack of a compiler and need for a runtime are somehow seen as negatives, even though the trend from the beginning has always been towards more, not less, abstraction.

Now, do I use VB6 for all of my Windows desktop development? No, I also code in C++, C#, and sometimes even batch scripts. Do I use JavaScript for all of my web development? No, all of my back-end and template code is typically written in Python, PHP, and the like. But for when I need a simple tool to do a simple job, simple code wins every time.

Labels: , ,

Friday, January 01, 2010

Happy Birthday, Canvassa

I just realized it's been a year since my first post detailing my efforts to re-create The NES Legend of Zelda in Javascript and HTML5/Canvas. I'm currently on hiatus, recuperating from minor sinus surgery earlier in the week, but I have an update in mind for later this month.

Until then, enjoy the current version here, and have a happy 2010.

Labels: , ,

Thursday, December 24, 2009

Cloning Zelda: Harmful Changes (and a Christmas Miracle)

Yesterday's post, which I called "Harmless Animations", led to me making a stupid – and harmful – change that I should have known better. It's worth talking about what happened, and why.

When I first implemented the loc.Explod class and the four SwordFlash elements, this is what the code that added them to the game screen looked like:

terminate: function swordProj_terminate() {
    game.insertProjectile(new loc.SwordFlashNW({'pos':this.getPos(),'owner':this.owner}));
    game.insertProjectile(new loc.SwordFlashSW({'pos':this.getPos(),'owner':this.owner}));
    game.insertProjectile(new loc.SwordFlashNE({'pos':this.getPos(),'owner':this.owner}));
    game.insertProjectile(new loc.SwordFlashSE({'pos':this.getPos(),'owner':this.owner}));

    this.inherited(arguments);
}

When I was blogging about the new code yesterday, I "streamlined" that function a bit to look like this:

terminate: function swordProj_terminate() {
    var myPos = this.getPos();
    game.insertProjectile(new loc.SwordFlashNW({'pos':myPos,'owner':this.owner}));
    game.insertProjectile(new loc.SwordFlashSW({'pos':myPos,'owner':this.owner}));
    game.insertProjectile(new loc.SwordFlashNE({'pos':myPos,'owner':this.owner}));
    game.insertProjectile(new loc.SwordFlashSE({'pos':myPos,'owner':this.owner}));

    this.inherited(arguments);
}

Do you see what I broke? It's probably not at all obvious... it certainly wasn't to me. I thought that I was optimizing the code by only running getPos() once and passing its value to all four child explods. But last night, I went to change the live code to reflect the blog post, in case anyone wanted to examine the new bits in context. When I did so, my sword flashes stopped behaving right! If the sword hit a monster, all four explods sat still in the exact spot where they were inserted, and if the sword made it to the end of the screen, two of them would come straight back, rather than at their proper angles. What the heck did I do?!?

I really should have known better, since this has tripped me up before. To explain what the difference is in these two code blocks, you need to know what the getPos() method (which is inherited from loc.Sprite) actually does:

getPos: function sprite_pos() {
    return dojo.clone(this.pos);
}

dojo.clone() is a cool little function. In a nutshell, it creates deep copies of JavaScript objects, which is tremendously useful if you want something approximating a struct datatype. This is exactly how I'm doing (x,y) pairs in Canvassa, which I use for both sprite position and velocity:

dojo.declare("loc.Sprite", null, {
    pos: {x:0, y:0},
    vector: {x:0,y:0},
...
})

The problem comes when you try to reuse one of these position values, which is precisely what the second code block tried to do, and is precisely why I created the getPos() method in the first place! Since a sprite's position is an object and not a struct, I need to dojo.clone() it to get a copy of its contained values instead of a copy of its pointer reference (see Jonathan Snook's explanation of why JavaScript works this way). Now, I'm not a n00b programmer. I've been around the block a few times, and have coded in nearly a dozen different languages in my time, and in many of them I was well aware of this Pass-by-Reference/Pass-by-Value distinction. And yet, in an effort to clean up my code, I managed to instantiate four separate SwordFlash explods and give all four of them the same position object! So when the game told them to update their positions, each in turn would modify its position object relative to its personal velocity:

// let's assume the starting position is {x:32, y:64}

// in SwordFlashNW.updatePosition():
this.pos.x += -3;  // {x:29, y:64}
this.pos.y += -3;  // {x:29, y:61}

// SwordFlashSW.updatePosition():
this.pos.x += -3;  // {x:26, y:61}
this.pos.y += 3;  // {x:26, y:64}

// SwordFlashNE.updatePosition():
this.pos.x += 3;  // {x:29, y:64}
this.pos.y += -3;  // {x:29, y:61}

// SwordFlashSE.updatePosition():
this.pos.x += 3;  // {x:32, y:61}
this.pos.y += 3;  // {x:32, y:64}

// end position for all four: {x:32, y:64}

So when all four explods were operating on a single, shared position object, the net result was no movement at all! And when, in the edge case, two of the explods went off the screen and were removed, the other two combined forces to push the position in a single direction. D'OH!

So, hopefully this time I've learned the lesson well enough that it'll sink in: my sprites' pos and vector values are objects, and I need to call getPos() EVERY time I want a non-interfering copy. Sure, it's not as profound a December lesson as "I will honour Christmas in my heart, and try to keep it all the year," but it'll do.

Happy Christmas to all, and to all a good night!

Labels: , , ,

Wednesday, December 23, 2009

Cloning Zelda: Harmless Animations

flashing shards fly off an octoroc when Link kills it with his sword

So.

Long time no update, eh? (at least, it's been a while since a substantial update)

A few weeks ago, I added something new and kinda cool to Canvassa. We've had sword projectiles for a while now, but it felt like something was missing... in the original game, when a thrown sword hits an enemy or a wall, you get this cool effect that the above screenshot shows off: a handful of flashing shards that fly off in 4 different directions. I'd been missing these, and wanted to add them to Canvassa, but I was afraid I had a "painted into a corner" situation on my hands: the game engine I'd built up so far had a couple of different types of sprites it handles, and the sword shards don't really fit any of them:

  • Player
    • used for: player main sprites
    • movement: user-controlled
    • attributes: can be hurt if touched by a monster or projectile
  • Monsters
    • used for: octorocs, tectites, etc.
    • movement: autonomous (via AI)
    • attributes: can be hurt if hit by player's weapon or projectile
  • Items
    • used for: small hearts, rupees, etc.
    • movement: none (except for fairies)
    • attributes: can be picked up if touched
  • Projectiles
    • used for: throwing sword, octoroc rocks, etc.
    • movement: pre-determined
    • attributes: can do damage if it touches a player or monster

Sword shards don't neatly fit into any of these boxes: they aren't controlled by the player or an AI, and while they do move in a predetermined way and can't be picked up (like projectiles), they don't hit obstacles or do damage to anything the way projectiles do.

As I mulled over the problem of how to implement these shards, inspiration struck: the MUGEN Explod construct! For the past several years I've developed something of a name for myself as a programmer (and occasional graphic artist) of custom characters for the open MUGEN fighting game engine. In my time working with that programming model, I've become familiar with its 4 tiers of sprite object hierarchy:

  • Player
    • used for: the character's core sprites
    • movement: user-controlled
    • attributes: can do damage and be damaged
  • Helper
    • used for: extensions of the character such as weapons, clones, etc.
    • movement: user-controlled or autonomous
    • attributes: can do damage and be damaged
  • Projectile
    • used for: things thrown/shot/expelled by the player
    • movement: pre-determined
    • attributes: can do damage and be blocked (but not hurt, per se)
  • Explod (no, I didn't misspell it)
    • used for: used for visual effects such as dust or hit sparks
    • movement: pre-determined
    • attributes: cannot do damage or be damaged

The above 4-way distinction between MUGEN sprite types seems to map pretty well to the engine I'm building, and it's a metaphor I'm familiar with, so I determined to apply it. For my purposes, Explods act almost exactly like Projectiles, except for two things: they don't do damage to anyone (or anything), and they have a time limit, after which they get removed from the screen. So the simplest thing to do is to subclass the loc.Projectile class and override the default behavior for these two differences:

dojo.declare("loc.Explod", loc.Projectile, {
    timeout: -1,
    constructor: function(args){
        /* required args: 'pos' and 'owner' (inherited from Projectile)
           optional args: 'timeout' (defaults to -1, meaning it never gets removed) and 'vel' (movement velocity) */
        dojo.mixin(this, args);
    },
    hit: function explod_hit() {
        // do nothing when making contact with another sprite
    },
    updatePosition: function() {
        if (this.timeout-- == 0) {
            this.terminate();   // when timeout runs down, tell the game to remove me
        }
        this.inherited(arguments);
    },
    _animateCurrent: function explod_animateCurrent() {
        return true;  // override default behavior to animate even if motionless
    }
});
View the code

Simple, right? Now, when I need an Explod-type effect, I simply subclass loc.Explod and add the necessary animation details. For the shattering sword, I defined four SwordFlash subclasses, one for each of the four shards created when a SwordProj projectile is terminated (NW, SW, NE, and SE, for the direction they move). In each, I specified the initial velocity and timeout values to be used (though I could also specify these during object instantiation, if I wanted to override the defaults):

dojo.declare("loc.SwordFlashNW", loc.Explod, {
    constructor: function(args){
        dojo.mixin(this, args);
        this.width = 8; this.height = 10;
        this.vel = {x: -3, y: -3}; this.timeout = 10;
        this._stateDefs = [ { faceted:false, nextState: 0, canMove: true,
            anim: [ [{x:152,y:11,t:1},{x:160,y:11,t:1}] ] }];
    }
});
View the code

With these four classes now defined, all I had to do was add a custom terminate() function to the loc.SwordProj "throwing sword" class, using it to insert a new instance of each explod into game.projectiles:

terminate: function swordProj_terminate() {
    var myPos = this.getPos();
    game.insertProjectile(new loc.SwordFlashNW({'pos':myPos,'owner':this.owner}));
    game.insertProjectile(new loc.SwordFlashSW({'pos':myPos,'owner':this.owner}));
    game.insertProjectile(new loc.SwordFlashNE({'pos':myPos,'owner':this.owner}));
    game.insertProjectile(new loc.SwordFlashSE({'pos':myPos,'owner':this.owner}));

    this.inherited(arguments);
}
View the code

You can see the results here.

If you clicked the above link and tested out the new feature, one thing you likely noticed is that the sword only works if you have full health; get touched by a monster, and suddenly you can't attack anymore... lame! Next time, we'll look at how the MUGEN Helper construct can help us fix this problem.

Labels: , , , ,

Thursday, December 03, 2009

Blogger is Busted (or, Hacking your Template to Eliminate the "a[...] is null or not an object" Error)

Earlier this morning, I posted a quick test to see if backlinks are working with Blogger-powered blogs posting to FTP, as I'm doing. Turns out that they're not (note the absence of backlinkage on that page I just linked to).

Now, this isn't a huge problem for me. Would I like the ego-stroking that comes with having backlinks, showing that people like what I'm doing enough to share it? Well, sure I would. But is it worth the pain and hassle? No. So I went into my blogger settings panel, and disabled all the bits pertaining to backlinks, and removed the backlink scripts from my custom template.

Unsurprisingly, this didn't solve the underlying problem -- my site was throwing JavaScript errors! I already knew what was causing the errors: as I said in my previous post, Blogger is auto-inserting some invalid-in-my-context JS code after every post, precisely where the </Blogger> tag lies in my template. Searching around a bit more, I found a solution at Google's Blogger support forum:

Kham Level 1 8/29/09 This has done the trick for me. Wrap all instances of <blogger> and </blogger> with both the HTML and JavaScript comments. <!--/* <Blogger> */ --> <!--/* </Blogger> */ -->

Stupid, stupid, stupid! Since I can't get Blogger to stop inserting the offending code, I have to force it to be commented out! Maybe it's time to take another look at alternative blog engines...

Labels: ,

Friday, November 06, 2009

Javascript Snippet: Flip Text Upside-down

This week I was playing with my Google Analytics account to see what was happening with my blogs; it turns out that my MUGEN blog is about a bajillion times more popular than this one. Guess I'm learning, as Arthur Conan Doyle did, that you don't get to pick what you're known for (sigh). That said, the most popular post here on the ol' Coding blog was this one, where I talked about my home-grown tag cloud generator. So, although Canvassa isn't going to be dropped, I'm going to pepper in more of these "snippets" types of articles from time to time... like today!

Back in April of this year, YouTube did an April Fool's joke where they turned their site upside-down by means of a sneaky little code snippet that swapped out all alphanumeric characters with characters that looked like upside-down versions of themselves (for example, 6 became 9, M became W, etc). Later, Paul Irish adapted the code into a jQuery plugin. Still later, I followed his lead and adapted the code as a Dojo plugin, and posted it on Twitter. But I never blogged about it here.

In fact, it's even been in one of my Github repositories for months now, completely undocumented save its internal code comments. Surprise!

Click here to flip all the posts on this page, re-arranging them from top to bottom, or click here to flip their text in place without moving it around.

I wanted to go back re-implement the code as a bookmarklet, but it's currently dependent on (and namespaced in) Dojo, so that didn't get done. Maybe for the next snippet.

Oh, also of note: I've re-arranged my site template a bit, moving navigation from the sidebar to the header, adding more social networking links to my profile, and pushing it further down the sidebar. This is part of my effort to harmonize all three of my blogs into a single site, and there will likely be more changes before that effort is complete.

Update, 11/30/2009: Some recent template changes to the blog have broken the dynamically-loaded code in this post; I'm working on a fix.

Labels: , ,

Wednesday, October 14, 2009

Cloning Zelda: Adding a Title Screen

Legend of Zelda title screen

First, a note on the server move: you may have noticed that my domain was offline for a few days last week; this was because I flubbed the domain transfer to my new server. Anyway, all was eventually straightened out, and I've successfully migrated all of my content from the old server to the new one. If you see anything here on the site that's broken, post a comment and let me know.

Now, on to today's subject: in trying to add several new states to the Game class for Canvassa, I've learned something interesting about Javascript's in operator: it doesn't really work for arrays! More specifically, it does in fact work, just not the way one would expect.

I do a lot of work in Python. In that language, you can do something like the following, and have a reasonable expectation of success:

>>> myList = [5,10,15,20]
>>> print (10 in myList)
True

So in a nutshell, you can define a list of values, and then check a reference value for membership in that list. Simple, and useful. I was doing this in Canvassa's Game class to test for the existence of a caller-specified game state before actually changing to the new state:

    this.constants.states = { overworld: 0, inventory: 1, 
                              dungeon: 2, map: 3, 
                              dying: 4, gameover: 5, cheat: 99, 
                              enum: [0,1,2,3,4,5,99] };
    ...
    changeState: function game_changeState(newState) {
        if (newState in this.constants.states.enum) {
            this.currentState = newState;
            this.drawBG();
        } else {
            console.log("Game.changeState() -- invalid state:", newState);
        }
    }

So basically I was using game.constants.states.enum as a list to keep track of the valid state values that changeState() should accept. Since the states I was using were all 0-based and sequential (except for the "cheat" state, which I haven't tested since Part 2 or so), this worked just fine. But in truth, it wasn't actually doing what I thought it was doing, as I was about to find out (dun-dun-DUN!)

I've been bothered for a while now about how I was loading the quest data (which handles map layouts, item and monster placement, and so on). Basically I was using the dojo.require() function to lazy-load the _quest1.js file, which doesn't actually contain a dojo class definition, but simply assigned the quest data to a namespaced variable, loc.gameData, which my classes could then refer to as a global. All kinds of poor, hackish behavior going on there. Plus, I'm building this game engine to support multiple (and, eventually, user-created) quests, so hard-coding one into the game bootstrapping logic is stupid and short-sighted. In order to correct this egregious flaw in the least destructive way possible, I reasoned, I should add several new states to the Game class, using negative integers to represent these meta-states that exist outside the regular game loop:

    this.constants.states = { title: -1, menu: -2, loading: -3,
                              overworld: 0, inventory: 1, 
                              dungeon: 2, map: 3, 
                              dying: 4, gameover: 5, cheat: 99, 
                              enum: [-3,-2,-1,0,1,2,3,4,5,99] };

In theory, the game engine could now start in the title state, and when the player presses start, change to the menu state, then allow quest selection from there. (for now, I'm simply XHR-loading _quest1.js when the user presses start, and will add the menu state later). But instead of working as I expected, this is when everything started to fall apart. I made the above change and a few others that would direct the game to load the necessary image resources, and then call game.changeState(game.constants.states.title) to display the title screen. BOOM! JavaScript error:

Game.changeState() -- invalid state: -1

Spending some time testing various expressions in the Firebug console, I eventually figured it out: the 'in' operator, when applied to JavaScript arrays, tests for the existence of a given index, not value! Observe this little exchange from my Firebug console:

>>> game.constants.states.enum
[-3, -2, -1, 0, 1, 2, 3, 4, 5, 99]
>>> -1 in game.constants.states.enum
false
>>> 8 in game.constants.states.enum
true

Like I said on Twitter when I discovered this: Freaky! I spent a few minutes looking at ways to get around this apparent misbehavior on JavaScript's part (I've since come to terms with why it works the way it does, but it's totally counter-intuitive to my way of thinking), and found this method, suggested by JavaScript rockstar Jonathan Snook.

In JavaScript, there's an in operator that tests whether a property is in an object. We can actually use this to mimic the PHP function in_array.
if(name in {'bobby':'', 'sue':'','smith':''}) { ... }
If the value of name matches any of the keys in the object literal, it returns true.

Jonathan then offers a refinement via a small function that takes a string array as input, and returns an object literal that can then be tested for membership via in. A reasonable approach, but not really one I wanted to pursue, if something simpler would work. This is what I came up with:

    this.constants.states = { overworld: 'overworld',
                              inventory: 'inventory',
                              dungeon: 'dungeon',
                              map: 'map',
                              dying: 'dying',
                              gameover: 'gameover',
                              title: 'title',
                              menu: 'menu',
                              loading: 'loading',
                              cheat: 'cheat' };
    ...
    changeState: function game_changeState(newState) {
        if (newState in this.constants.states) {
            this.currentState = newState;
            this.drawBG();
        } else {
            console.log("Game.changeState() -- invalid state:", newState);
        }
    }

Et, voila! Simple and elegant. I still have my pseudo-enumeration of valid game states, but I've eliminated the unnecessary "enum: [1,2,3...]" construction, and changed the state values from meaningless integers into strings that matched their own keys. Now my code can still refer to states via their game.constants.states.* enumeration values, and in works as expected, since those strings resolve as properties of the game.constants.states object literal.

There was a little more code cleanup necessary to support this change (for instance, I had to define a list of "unpausable" states so that the user wouldn't be able to put the game in pause mode from the title screen), and there are still a few lingering, unrelated problems in the 0.5 codebase, but we now have a title screen, and the ability to add new game states and check for their existence. All in all, a good night's work.

Labels: , , ,

Friday, October 02, 2009

Cloning Zelda: Progress and a Change of Methodology

So those of you who are hitting the work-in-progress Canvassa build may have noticed that I've added sound support back in. It's not very Dojo-y at the moment, but I got tired of not hearing anything as I tested, so it's in. I've also got the bug about predefined items (e.g. the sword on screen 1) reappearing after you pick them up.

The other major change in the Canvassa effort is how I'll be measuring progress. I started out treating it almost as a tutorial of how to build a game like this in JavaScript+Dojo, and to a certain extent I'll continue to do so, but the posts will stray from the "Part 1 / 2a / 2b..." format that I've been using, and go to a slightly more traditional "dot release" approach: right now I'm working on version 0.5, and when it's finished and the Github repository is updated, I'll move to v0.6. All development will be done in the work-in-progress Canvassa location, so no more Part 1 / Part 2 / etc. subfolders. Hopefully this will be easier for me to work with and stay on task, and will be more in line with what you, my faithful reader, would want to see anyway. (of course, if I'm wrong in my estimation, feel free to let me know in the comments).

Beyond that, not much else has progressed this week; I'll be moving this blog and my others to my new host soon, and I'm still trying to decide how exactly I want to set things up (one of the main points of uncertainty is whether to keep the three blogs separate, or merge them into a single stream with tags/categories to differentiate them. If I do merge the blogs, I'll keep my RSS feeds the same (I'll re-orient the FeedBurner links to point to the appropriate places), so those of you reading me through a feed reader probably won't notice much of a difference. Hopefully, I'll be able to get the buyog.com domain transferred over smoothly to my new host as well, but if not, the buyog.net domain will get you to the new content. When the move actually happens, I'll make a note of it here so you're all aware.

Labels: , , ,

Thursday, September 24, 2009

Canvassa 0.5 Bug List

Thought I'd peel back the curtain a little tonight. Here's what's left to do with my Canvassa Zelda clone before I can call version 0.5 (i.e. Part 5) done and ready to move on to 0.6:

  • Add text to the page describing the game controls
  • Fix projectile management (I'm not keeping track of them correctly, especially the sword)
  • Add the sword projectile "flash" effect when it hits something
  • Limit the blue candle to a single use per screen
  • Make Link & the monsters get knocked back when hit
  • Fix the number of hits required to kill monsters (esp. Zola)
  • Permanently remove predefined items from the map when picked up (e.g. the wooden sword on the start screen -- grab it, leave the screen, then come back: it's there again!)
  • Make killed monsters leave behind hearts and rupees (fairies and clocks come later)
  • Split Items.js file into multiple subfiles, parallel to what Peter Higgins did to Monsters.js for me last week

I'll be working on a few of these things tonight; maybe even have a more verbose blog post to go along with it (hope springs eternal, anyway)

Labels: , ,