Palagpat Coding

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

Saturday, March 14, 2009

Cloning Zelda, part 4C: Combat (finally!)

Previous posts in this series: Part 1 | Part 2 | Part 3A | Part 3B | Part 4A | Part 4B

First, a technical note: I recently discovered that this blog was completely unreadable in Internet Explorer. I've been using Firefox almost exclusively for some time now, but was at a bit of a loss to explain why this was happening -- anyway, turns out it was because I was using a CSS file that specifies different rendering layouts for different browser platforms (i.e. smartphones, etc) using the @media tag -- which IE ignores! Oops. Anyway, tonight I finally tracked it down with the help of the cool IE Tab add-on for Firefox, and this blog is once again readable to users of Microsoft's wacky browser.

Now, to the subject at hand: I've decided that I'm biting off entirely too much with the original task distribution I came up with for this Javascript Zelda-cloning effort. Part 3 ended up having a Part 3A and a Part 3B, and Part 4 has ended up as 3 sub-sections: Part 4a dealt with the issue of game size, since I actually prefer playing it in its original size, but my son likes playing it in the double-sized version I originally started with. Part 4b came about because I was tired of the game being silent and I discovered a way to implement sound pretty easily. The original goal of Part 4, though, was about making the combat system work. This is now, for the most part, done; at least I'm ready to blog about it and move on to something else.

So, what did I do to finish part 4? Several things:

  1. Added player hearts / hit points
  2. Added player's sword (press the A button, or the Z key on your keyboard)
  3. Added sword projectiles when health is full
  4. Added collision detection, so monsters can hurt the player, and the player can hurt the monsters.

Those were the main points. But in order to make them work reasonably well, I had to also implement several other things:

  1. Added a basic animation state system to the player class, since he now has several different possible animation states (walking, swinging his sword, getting hit, dying, etc)
  2. Added a new "game over" state to the game, for when the player dies
  3. That meant implementing the game's text system as well, because I didn't feel like hacking something temporary that would get replaced soon; my test harness for the text system is here
  4. Started the basics of the item system, since Link needed a way to get (and throw) the sword, plus he needed to be able to pick up hearts dropped by the monsters he killed in order to replenish his health. The basics of the item heirarchy are in the new items.js class.
  5. Plus, the aforementioned scaling and sound updates that came in with parts 4A and 4B.

The most interesting part of all of this new stuff is probably the collision detection. I implemented it by adding a new doHitTests() method to the main game loop in game.js, beautifully rendered here using Alex Gorbatchev's awesome SyntaxHighlighter tool:

this.main = function() {
    // update monster positions (but only if on the overworld/dungeon screens)
    if (this.currentMode == 0 || this.currentMode == 2) {
        for each (var enemy in this.enemies) {
            enemy.think();
        }

        // move all active projectiles
        for each (var proj in this.projectiles) {
            proj.updatePosition()
        }

        // do sprite collision tests
        this.doHitTests();
    }

    // draw everything
    this.drawSprites();
}

this.doHitTests = function() {
    // we only care about collision detection if in overworld or dungeon modes
    if (this.currentMode == 0 || this.currentMode == 2) {
        // do sprite collision tests
        var hits = game.hitTest(player.pos.x, player.pos.y, 12,12);

        // check to see if any monsters are touching the player;
        // if so, he's hurt to the tune of their relative strength
        for each (var attacker in hits[0]) {
            if (attacker.canAttack()) {     // ignore is monster is stunned or dying
                player.getHit(attacker.strength);
                break;
            }
        }

        // see if there is anything in this area to pick up
        for each (var item in hits[1]) {
            player.getItem(item);
            delete game.items[item._index];
        }

        // if there are any projectiles in play, see if they hit anything, too
        for each (var proj in this.projectiles) {
            // player-owned projectiles can only damage enemies, and vice-versa,
            // so check projectile type against hit target type
            if (proj.owner == player) {
                hits = game.hitTest(proj.pos.x, proj.pos.y, proj.width,proj.height);

                for each (var enemy in hits[0]) {
                    enemy.getHit(proj.power);
                }
            }
        }

        // note: player class handles attack collision detection internally
        // by making a call to game.hitTest(x,y,w,h)
    }
}

this.hitTest = function(x,y,w,h){
    // check all active monsters to see if they are within the
    // specified area; return a list of any that are found there
    var enemies = []; projectiles = []; items = [];
    for each (var enemy in this.enemies) {
        var dx = Math.abs(x-enemy.pos.x);
        var dy = Math.abs(y-enemy.pos.y);
        if (dx <= w && dy <= h) {
            enemies.push(enemy);
        }
    }

    for (var i in this.items) {
        var item = this.items[i];
        item._index = i;
        var dx = Math.abs(x-item.pos.x);
        var dy = Math.abs(y-item.pos.y);
        if (dx <= w && dy <= h) {
            items.push(item);
        }
    }


    return [enemies,items];
}

Not too much more to say here. The AI of some of the monsters (most notably the tectites and peahats) is still pretty wonky, and none of the monsters have projectile attacks yet, but that'll be easy enough to adapt from the sword projectiles we gave to the player in this update. The game is finally getting pretty interesting to play, and it's time to move on.

Check out the latest version of the game here: small version or large version

Next time, we'll expand on the Items class heirarchy and get the player's inventory system working.

Labels: , ,

0 Comments:

Post a Comment

Subscribe to Post Comments [Atom]

<< Home