Sep 8, 2014

Someone requested a code for smooth random movement, specifically the Peahats from The Legend Of Zelda -- those flying monsters with propellers on their heads that can only be hurt when they land. You know me and my big mouth: "I could probably take a look at the original code for you and work something up."

Turns out Zelda's programmers, along with having Nintendo's quirky BIT method of programming, were fans of the simple state machine. And whereas Konami used just two variables for randomness in CV3, Zelda's programmers used one for each instance on the screen.

Anyway, here's a mockup of the Peahat code in GML:

var states;
states[0] = 8;
states[1] = 9;
states[2] = 1;
states[3] = 5;
states[4] = 4;
states[5] = 6;
states[6] = 2;
states[7] = 10;
if !alarm[1]
    alarm[1] = $80>>(id & 7);
alarm[1] = alarm[1]>>1;
alarm[1] |= ((alarm[1] & 1) ^ (alarm[2] & 1))<<7;
alarm[2] -= 1;
alarm[0] &= $FF; //41F
alarm[1] &= $FF; //1A
alarm[2] &= $FF; //2A
alarm[3] &= $FF; //412
alarm[4] &= $FF; //42C

switch base
{

case 0:
//    0    //
alarm[0] += 1;
if alarm[0] & $E0 == $A0
    base = 1;
else
if alarm[0] & $E0 == 0
{
    alarm[2] = alarm[1] & $3F | $40;
    base = 5;
}
break;


case 1:
//    1    //
var i;
i = 2;
if alarm[1] < $B0
{
    i += 1;
    if alarm[1] < $20
        i += 1;
}
base = i;
alarm[4] = 6;
break;


case 2:
//    2    //
if !alarm[2]
{
    alarm[4] -= 1;
    if alarm[4]
    {
        alarm[2] = $10;
        var i;
        i = 0;
        if Link.x < x
            i = 2;
        else
        if Link.x > x
            i = 1;
        var k;
        k = 0;
        if Link.y < y
            k = 8;
        else
        if Link.y > y
            k = 4;
        k |= i;
       
        i = 7;
        while i+1
        if states[i] == state
            break;
        else i -= 1;
        if i < 0
            i = 0;

        var m;
        m = 3;
        i += 1;
        while m
        {
            i &= 7;
            if states[i] != k
                break;
            i -= 1;
            m -= 1;
        }
        if m==0
            m = 3;
        i += 1;
        while m
        {
            i &= 7;
            if states[i] & k
                if states[i] | k < 7
                    state = states[i] | k;
            i += 1;
            m -= 1;
        }
        if m+1
        {
            if !m
                i -= 1;
            state = states[i];
        }
    }
    else
        base = 1;
}
break;


case 3:
//    3    //
if !alarm[2]
{
    alarm[4] -= 1;
    if alarm[4]
    {
        alarm[2] = $10;
        var i;
        i = 7;
        while i+1
        if states[i] == state
            break;
        else i -= 1;
        if i < 0
            i = 0;
        if h019 < $A0
            i += 1;
        if h019 < $50
            i -= 2;
        i &= 7;
        state = states[i];
    }
    else
        base = 1;
}
break;


case 4:
//    4    //
alarm[0] -= 1;
if alarm[0] & $E0 >= $A0
    base = 1;
else
if alarm[0] & $E0 == 0
{
    alarm[2] = alarm[1] & $3F | $40;
    base = 5;
}
break;


case 5:
//    5   //
if !alarm[2]
    base = 0;
break;


}

//These next subroutines run during all states.

alarm[3] += alarm[0] & $E0;
if alarm[3] & $100
{
    //Move the Peahat
    if state & 1
        x += 1;
    if state & 2
        x -= 1;
    if state & 4
        y += 1;
    if state & 8
        y -= 1;

    alarm[5] += 1;
    image_index += alarm[5] & 1;

    //Check if still in the boundary, and if not...
    i = 1;
    if state & 2
        if bbox_left < 17
            i = 0;
    if state & 1
        if bbox_right > room_width - 17
            i = 0;
    if state & 8
        if bbox_top < 17
            i = 0;
    if state & 4
        if bbox_bottom > room_height - 17
            i = 0
    if !i
    {
        if !state
        {
            i = 7;
            while i+1
            if states[i] == state
                break;
            else i -= 1;
            if i < 0
                i = 0;
            i = i + 4 & 7;
            state = states[i];
        }
    }
}

4 comments:

  1. Hey, was wondering if you were still working on your Castlevania engine. If not, I was wondering if you were interested in sharing it with me? I was mostly interested in using the stairs portion of the engine.

    Credit will be left where due of course.

    magusonline@hotmail.com

    ReplyDelete
    Replies
    1. I'm still working on it off and on. Been rewriting some of my old code since a lot of it wasn't compatible with GM:Studio. Been trying to tackle bosses, but their programmer's code is a lot different than that of normal enemies, so it's harder for me to read.

      Stairs are a breeze to incorporate in GM (easier than on the NES). Read the old RAM Offsets post near the bottom for info about stairs. You basically just have a stair object and when the player presses up, you search for the nearest "UP" stair object and if the player is close enough to it you move him toward it. You will want to familiarize yourself with programming in finite states, as Konami's programmers frequently do that. That's why Trevor can't move when he's (Moon)walking to a nearby stair object. But the stairs themselves are just empty tiles, they aren't objects -- only the landings/balconies are objects.

      Delete
  2. Hi, I read your post in here:

    http://castlevaniadungeon.net/forums/index.php?topic=6279.0

    I think the pixel perfect upscale is only working in this (binary) percentage : 100%, 200%, 400%, 800%, 1600%, etc.

    any non bainary like 300%, 500%, 600%, 700%, 900%, etc will not work. What do you think?

    sorry if my english is bad

    ReplyDelete
    Replies
    1. I think it has to do with how GM's surfaces are scaled. The application surface itself seems to be the issue, I think. If the view_wport == view_wview*2, then there is tearing and improper scaling at least on some systems; but if view_wport == view_wview*2-1, then the tearing and scaling issues go away. Although it may indeed be a binary issue as well; the application surface may have the wrong bits set.

      Delete