Dec 6, 2020

Side Track: Mega Man 2 Weapons

This was a rather simple reverse engineering endeavor. As much as I hate the rest of Capcom's code, the weapon codes were pretty simple and easy to read. The only reason I'm even bothering with this post is because I drafted a 360° Quick Boomerang code for someone recently.

For each weapon, the first code block goes inside Rock when he shoots his weapon. This is pared down heavily from the original code, since nobody probably cares how the program decides when Rock gets to attack. The second code goes inside the weapon itself. Note that instead of my usual hspd and vspd variables, I use vector0 and vector1; this ties in with my Quick Boomerang code. If you don't like those variable names, use hspd for vector0 and vspd for vector1.


Atomic Fire

Some codes help you discover what certain variables are for. This was one such code.
 
with instance_create(x,y,obj_AtomicHeat) {
    vector0 = 0;
    vector1 = 0;


switch mode {
    case 0: if charge < 255
                charge++;
            var i = 0;
            if global.stepcounter & 4 {
                i++;
                if charge > $7C {
                    i++;
                    if charge > $BA
                        i++;
                }
            }
            var n;
            switch i {
                case 0: global.pallet[0,1]=$0F;
                        global.pallet[0,3]=$15;
                        n = 7;
                        break;
                case 1: global.pallet[0,1]=$31;
                        global.pallet[0,3]=$15;
                        n = 1;
                        break;
                case 2: global.pallet[0,1]=$35;
                        global.pallet[0,3]=$2C;
                        n = 6;
                        break;
                case 3: global.pallet[0,1]=$35;
                        global.pallet[0,3]=$35;
                        n = 10;
                        break;
            }
            if global.stepcounter & 7 == 0
                image_index = i;
            y = obj_Rock.y;
            x = obj_Rock.x;
            if n > ammo[wep.H] {
                //revert everything if ammo too low
                charge = 0;
                global.pallet[0,1]=$0F;
                global.pallet[0,3]=$15;
                instance_destroy();
            }
            else
            if inputs & bit.ATTACK == 0 {
                //revert pallet & create shot if button released
                charge = 0;
                global.pallet[0,1]=$0F;
                global.pallet[0,3]=$15;
                mode = i;
                size = i;
                image_speed = 1/4;
                switch i {
                    case 0: image_index = 0;
                            sprite_index = spr_AtomicFire;
                            break;
                    case 1: image_index = 1;
                            sprite_index = spr_AtomicBullet;
                            break;
                    case 2: image_index = 4;
                            sprite_index = spr_AtomicBlast;
                            break;
                    case 3: image_index = 7;
                            sprite_index = spr_AtomicBoom;
                            break;
                }
                ammo[wep.H] -= n;
                vector0 = 4 * obj_Rock.image_xscale;
                vector1 = 0;
                instance_destroy();
            }
            //update Rock's sprite to match pallet
            break;
            
    case 1: if image_index == 3
                image_index = 1;
            x += vector0;
            y += vector1;
            break;
    
    case 2: if image_index == 6
                image_index = 4;
            x += vector0;
            y += vector1;
            break;
    
    case 3: if image_index == 9
                image_index = 7;
            x += vector0;
            y += vector1;
            break;
}

Air Tornado

for(var i=0; i<3; i++;)
with instance_create(x+16,y,obj_AirTornado) {
    image_xscale = other.image_xscale;
    switch i {
        case 0: vector0 = (1+$19/$100)*image_xscale; break;
        case 1: vector0 = (1+$99/$100)*image_xscale; break;
        case 2: vector0 = (2+$33/$100)*image_xscale; break;
    }
    
    vector0 *= image_xscale;
    vector1 = 1/4;
}
ammo[wep.A] -= 2;


vector1 += 1/16;
x += vector0;
y -= vector1;

Leaf Shield

The leaves don't actually revolve around Rock, it's just the sprite animation. The variable global.offscreen tells the game if Rock has jumped or fallen vertically offscreen, so image_index 6 is actually blank.

for(var i=0; i<4; i++;)
with instance_create(x,y,obj_LeafShield) {
    image_xscale = other.image_xscale;
    vector0 = 0;
    vector1 = 0;
    mode = 0;
    timer = 0;
    ID = i;


if mode {
    if global.offscreen
        image_index = 6;
    else
    if image_index == 5
        image_index = 1;
    if mode == 1 {
        if global.stepcounter & 7 == 0
            audio_play_sound(sfx_LeafShield,1,0);
        x = obj_Rock.x;
        if global.offscreen
            y = 0;
        else
            y = obj_Rock.y;
        if inputs & bit.DIRECTIONS {
            if global.offscreen{
                instance_destroy();
                exit;
            }
            if inputs & (bit.LEFT | bit.RIGHT) {
                vector0 = 4;
                if inputs & bit.LEFT
                    vector0 = -vector0;
            }
            else {
                vector1 = 4;
                if inputs & bit.UP
                    vector1 = -vector1;
            }
            ammo[wep.W] -= 3;
            mode++;
        }
    }
    else {
        x += vector0;
        y += vector1;
    }
}
else {
    image_index = 0;
    if ID & 1
        x = obj_Rock.x + timer;
    else
        x = obj_Rock.x - timer;
    if ID & 2
        y = obj_Rock.y + timer;
    else
        y = obj_Rock.y - timer;
    
    if timer < 12
        timer += 2;
    else {
        with obj_LeafShield
            if id != 0
                instance_destroy();
            else {
                mode = 1;
                image_index = 1;
            }
    }
}

Bubble Lead

with instance_create(x+16,y,obj_BubbleLead) {
    image_xscale = other.image_xscale;
    vector0 = 1 * image_xscale;
    vector1 = 2;
    massive = 1;
}
if ++shots_fired == 2 {
    shots_fired = 0;
    ammo[wep.B]--;
}


if tilemap_get_at_pixel(solids,x+8*image_xscale,y) {
    x -= vector0;
    xbounce = 1;
}
if tilemap_get_at_pixel(solids,x+8,y+8*sign(-vector1|1)) || tilemap_get_at_pixel(solids,x-8,y+8*sign(-vector1|1)) {
    if vector1 < 0 //if falling
        y -= y + 7 & 15;
    else
        y += y + 7 & 15 ^ 15;
    vector1 = -1/4;
    massive = 0;
    if !ybounce {
        ybounce = 1;
        vector0 = 2 * image_xscale;
    }
    else
    if xbounce
        instance_destroy();
}
x += vector0;
y -= vector1;
if massive
    vector1 += grav;

Quick Boomerang

This is the 360° mouse-directed version. For the normal version, just remove the bits regarding dir.

with instance_create(x+8,y-16,obj_QuickBoomerang) {
    image_xscale = other.image_xscale;
    vector0 = (4+$71/$100);
    vector1 = (2+$AA/$100) * image_xscale;
    dir = point_direction(xstart,ystart,mouse_x,mouse_y)
    timer = 0;
}
if ++shots_fired == 8 {
    ammo[wep.Q]--;
    shots_fired = 0;
}


if timer < $12
    vector1 -= $4B/$100 * image_xscale;
else
if timer == $12
    vector0 *= -1;
else
if timer == $23
    instance_destroy();
else
    vector1 += $4B/$100 * image_xscale;
timer++;
x += lengthdir_x(vector0,dir) - lengthdir_y(vector1,dir); //vector0
y += lengthdir_x(vector1,dir) + lengthdir_y(vector0,dir); //vector1
  

 
Flash Timer

The sparkles are just a "simple" sprite. This weapon introduces an hcarry variable to handle horizontal movement of platforms. The Flash Timer disables horizontal platform control.

with instance_create(x,y,obj_FlashTimer) {
    timer = 15;

if --vector0 == 0 {
    vector0 = 15;
    if --ammo[wep.F] == 0 {
        instance_destroy();
        global.flashy = 0;
        global.resume = 1;
        exit;
    }
}
global.flashy = 1;
obj_Rock.hcarry = 0;
y = $80;
x = view_xview+$80;

Metal Blade

Nothing special here, just the code to create the blades.
 
with instance_create(x,y,obj_MetalBlade) {
    switch inputs & bit.DIRECTIONS {
        case 0:
        case 4:
        case 8: vector0 = 4; vector1 = 0; break;
        case 1: vector0 = 0; vector1 = 4; break;
        case 2: vector0 = 0; vector1 = 4; break;
        case 5:
        case 9: vector0 = 2+$D4/$100; vector1 = 2+$D4/$100; break;
        case 6:
        case 10: vector0 = 2+$D4/$100; vector1 = -2-$D4/$100; break;
        
        default: vector0 = 0; vector1 = 0; break;
    }
    vector0 *= image_xscale;
}
ammo[wep.M]--;
 

Crash Bomb

with instance_create(x+16,y,obj_CrashBomb) {
     image_xscale = image_xscale;
     vector0 = 4 * image_xscale;
     vector1 = 0;


if mode {
    if mode == 1 {
        if image_index == 4
            image_index = 2;
        if --timer == 0 {
            image_index = 5;
            timer = $38;
            image_speed = 0;
            mode++;
        }
    }
    else {
        if timer & 7 == 0 {
            audio_play_sound(sfx_CrashBomb,1,0);
            var n = timer >> 1 & 12;
            if !instance_exists(obj_CrashBoom)
            repeat 4
                instance_create(x,y,obj_CrashBoom);
            with obj_CrashBoom {
                x = other.x;
                y = other.y;
                switch n++ {
                    case 0: y-=8; x-=8; break;
                    case 1: y-=16; x+=8; break;
                    case 2: y+=8; x+=0; break;
                    case 3: y+=0; x+=16; break;
                    case 4: y-=8; x-=8; break;
                    case 5: y-=8; x+=16; break;
                    case 6: y+=8; x-=16; break;
                    case 7: y+=0; x+=8; break;
                    case 8: y-=16; x+=0; break;
                    case 9: y+=0; x+=0; break;
                    case 10: y+=16; x-=8; break;
                    case 11: y+=16; x+=16; break;
                    case 12: y-=16; x-=16; break;
                    case 13: y-=8; x+=16; break;
                    case 14: y+=8; x-=16; break;
                    case 15: y+=8; x+=8; break;
                }
            }
        }
        if --timer == 0 {
            instance_destroy();
            exit;
        }
    }
}
else {
    image_index = 0;
    if tilemap_get_at_pixel(solids,x+6*sign(vector0),y-8) | tilemap_get_at_pixel(solids,x+6*sign(vector0),y+8) {
        audio_play_sound(sfx_CrashStick,1,0);
        status &= $FE; //I think this disable enemy collision
        image_index++;
        mode++;
        timer = $7E;
    }
    else {
        x += vector0;
        y += vector1;
        exit;
    }
}
if x < view_xview || x > view_xview+view_wview
    instance_destroy();

No comments:

Post a Comment

©TheouAegis Productions™. Powered by Blogger.