Castlevania Dungeon Forums
The Castlevania Dungeon Forums => Fan Stuff => Topic started by: TheouAegis on May 04, 2016, 11:29:37 AM
-
UPDATE: So I don't have to keep searching through this thread every time I post, I'm writing a table of contents for each code I add.
CASTLEVANIA III- Reply #8
- Sleeping Bat
- Flying Bat
- Fleaman
- Crow
- Reply #10
- Medusa Head
- Medusa Skull
- Winged Demon
- Living Armor
- Bone Statue
- Mudman
- Slime
- Bullfrog
- Ghost (fast)
- Ghost (slow)
- Wall Hugger
- Reply #12
- Reply #14
- Fire Walker
- Ember
- Buried Zombie
- Walking Dead
- Roaming Skeleton
- Sword Skeleton
- Dullahan
- Whip Skeleton
- Bone Skeleton
- Bone
- Blood Skeleton
- Reply #20
- Merman (ambush)
- Merman (swimming)
- Merman (floating)
- Merman (flood)
- Mushroom
- Floating Spore
- Spore Pollen
- Owl
- Reply #23
- Bone Dragon Head
- Bone Dragon Tail
- Reply #24
- Flying Eye
- Teardrop
- Axe Knight
- Knight's Axe
- Reply #25
- Mummy
- Bandage
- Harpy
- Fleaman Bomb
- Spider
- Spider Thread
- Spiderling
So, figured I'd share a couple of the simpler codes I've encountered in Castlevania games in case anyone may want to include them in their fan games. Some I've mentioned before, but whatever.
Medusae (heads, CV1 boss) | Bats | Winged Demons | Mummy Wraps
One of the Castlevania series' all-encompassing codes is a lot simpler than using trig-based sine waves.
speedvertical += ( y0 - y ) / 2n/ 256
y0 is the axis over which the period crosses. n is a modifier that extends the wavelength of the period. The 256 is somewhat arbitrary - since the NES was an 8bit system, the value used was 256.
UPDATE EDIT: In the codes that appear later in this thread, references to the function move_sinewave() refer to the following code:
[/list][/list][/list][/list][/list][/list]vspd -= (y - ystart >> argument0)/$100;
Directed Fireballs (e.g., CV1 boss bat and Dracula)
This code was used quite extensively in CV1 and is fairly simple to work with. Basically, it uses a derivative of the distance between the player and the enemy as the speed.
speedvertical = ( y1 - y0 ) * n / 256
or
speedhorizontal = ( x1 - x0 ) * n / 256
In all cases, only the higher one was used, while the other speed vector was set to some arbitrary value. Again, the 256 is arbitrary based on the memory size and/or view size. For multiple fireballs that spread, three values were used for y1 (the destination). If only one vector is set with variable speed, then the lower the invariable speed, the more the attack will spread.
Everything... practically
Timers. Timers. Timers.
Just in case you missed it the first three times - timers. The Castlevania series is a heavily timed game. Bowzer's minions have dumb code, Dracula's minions don't:
- Skeletons won't just walk to the end of a ledge and then turn around and walk to the other edge; once their timer counts down all the way, they'll turn around where they're at.
- Crows swoop down following aerodynamics [very] loosely. When the timer counts down, they stop in place, face the player, then swoop again.
- Medusa's Bust in CV1 follows the normal periodic wave pattern (see above) as long as her timer is counting down. Once it has counted down, she stops in place, then starts a new wave.
I have code for almost all enemies and bosses in CV1 and all enemies in CV3 (excl. bosses), so if you need a particular enemy's code, just ask.
-
This is cool. I say if you have the time post as many as possible. I could definitely use all of it.
-
This is cool. I say if you have the time post as many as possible. I could definitely use all of it.
I second that.
-
I third that! =D
-
I can't make sense of code so I'll just say fourth that and leave it at that.
-
Thanks a lot! The bat code works perfectly. I've adapted it a little for the Game Maker engine:
vspeed += (ystart+48-y)/power(2,n)/256;
I've set "n" to 2, that works best for me. 48 is the maximum (and minimum) height for flying arc, if you want your bats start flying upwards, change it to -48.
"Directed fireballs" works well too, but I've found their movement too rough, if using only one speed increment at time. Now I'm using this code:
hspeed = (Player.x+16-x)*2/256;
vspeed = (Player.y+16-y)*2/256;
hspeed+=sign(hspeed)*0.5;
vspeed+=sign(vspeed)*0.5;
Player.x+16 and Player.y+16 is my Player's central spot. Two last increments are that arbitrary values.
-
@Lanforse:
The fireballs vary from enemy to enemy. It's essentially:
var temp;
temp[1] = x-player.x;
temp[0] = y-player.y;
if abs(temp[0]) > abs(temp[1])
{
vspeed = temp[0] * n / 256;
hspeed = a * sign(temp[1]);
}
else
{
hspeed = temp[1] * n / 256;
vspeed = a * sign(temp[1]);
}
I'm going back to double check the modifiers, but from my first read-thru of the codes in CV1, the values of n and a for each boss should be as follows:
Dracula Fire (both forms)
n = 2;
a = 2;
Death's Scythes
n = 1;
a = 7/8;
Boss Bat's and Igor's Fireballs
n = 3/2;
a = 3/2;
Boss Bat's Primary Dive
n = 3/4;
a = 3/4;
Boss Bat's Secondary Dive
n = 2;
a = 2;
Try those values with my code and see how they work out for you. The roughness may be an issue with floating point rounding, or it could be because you used both distance values instead of just one. Except for Death's Scythes (not sure why his is different), the modifier and outlying speed are both the same.
I may be nice and post at least the enemy codes for everyone...
-
Okay, I will start off with some code. But first, a couple disclaimers about my codes...
- Konami used a finite state machine for all their code (even the game engine itself was a giant finite state machine). If you're not even familiar with a finite state machine, research it yourself.
- Each state in the game was basically its own script and each state machine was just an array of which scripts to call at the appropriate time. So the difference between my code an Konami's code is that theirs is much more concise.
- I use Game Maker, so consequently the code syntax is in GML.
- My code has its own little quirks (everyone has their own style of coding), but I will change some things around for the sake of clarification when posting code here. Hopefully some things will actually be clearer...
- You can change the code to suit your own games, which is ultimately what I was planning on doing with some of these anyway. Konami owns the rights to all these codes. Want to know who to credit from Konami, google it (be thorough, sometimes the producer also coded). Even though the code I actually post would fall under the realm of "public domain", give me credit in your games if you use any of these, since it was me who spent the last 5 or 6 years (I have ADHD, remember) studying NES coding and translating Konami's code from cryptic 6502 ASM into comprehensible GML.
- All my code is written based on the code I adapted from CV3. CV1 had its own styles and mechanics, which I opted to not use and so modified the code to more closely resemble how it may have looked in CV3.
One more important note is my code uses enumerators for enemy status, an attribute of CV3 that wasn't in the other games. The enumerators are as follows:
OFFSCREEN = 1;
FROZEN = 2;
PASSTHRU = 16;
STILL = 32;
MOVING = 64;
HIDDEN = 128;
You'll see throughout the code lines such as
status |= MOVING;
or
status &= ~STILL
If you're not familiar with bitwise operators, |= sets the status, while &=~ clears the status.
OFFSCREEN status is used to tell the game when the enemy is not in view. Usually this triggers the HIDDEN status, which of course tells the game not to draw the enemy's sprite. FROZEN status is used for Sypha's ice spell. PASSTHRU is used when enemies are ethereal and cannot be hurt (likewise, they cannot hurt the player either). STILL status tells the game to not animate the enemy's sprite. And of course, MOVING tells the game whether or not to handle its horizontal and vertical speeds.
In the original Castlevania, the MOVING status wasn't used because each state specified whether to run the horizontal movement code, vertical movement code, both codes, or neither. The HIDDEN status wasn't used because they just drew the sprite off-screen. The PASSTHRU status wasn't used because they just negated the states an enemy would be invulnerable.
-
Sleeping Bat
timeline_index = st_zzzbat
switch timeline_position
{
case 0:
sprite_index = sprite_Sleeping_Bat;
image_speed = 0;
image_index = 0;
timeline_position += 1;
status |= STILL;
break;
case 1:
if abs(x - Belmont.x) < $60
if abs(y - Belmont.y) < $20
timeline_position += 1;
break;
case 2:
sprite_index = sprite_Flying_Bat;
image_index = 0;
image_speed = 1/10;
status &= ~STILL;
status |= MOVING;
en_xscale_set(); //Set the xscale to face the player
hspd = 5/4 * image_xscale;
timeline_position += 1;
break;
case 3:
vspd = 3/2;
alarm[02] = $20;
timeline_position += 1;
break;
case 4:
alarm[02] -= 1;
if alarm[02]
vspd -= 10/256;
else
{
timeline_index = st_flybat;
timeline_position = 1;
}
break;
}
Flying Bat
timeline_index = st_flybat
switch timeline_position
{
case 0:
sprite_index = sprite_Flying_Bat;
image_index = 1;
image_speed = 1/10;
status |= MOVING;
en_xscale_set(); //set xscale to face player
hspd = 5/4 * image_xscale;
timeline_position += 1;
break;
case 1:
vspd = -3/4;
ystart = y;
timeline_position += 1;
break;
case 2:
move_sinewave(0); //see top post for code
break;
}
Fleaman / Gremlin
timeline_index = st_fleaman
switch timeline_position
{
case 0:
timeline_position += 1;
alarm[3] = 0;
//junk state
break;
case 1:
en_xscale_set(); //set xscale to face player
if status & OFFSCREEN
image_xscale*= -1;
timeline_position += 1;
break;
case 2:
sprite_index = sprite_Fleaman;
image_index = 0;
image_speed = 1/10; //If you want to animate it while it waits
timeline_position += 1;
break;
case 3:
alarm[02] = $28;
timeline_position += 1;
break;
case 4:
case 10:
alarm[2] -= 1;
if !alarm[2]
{
status |= STILL;
timeline_position += 1;
}
break;
case 5:
var i; i=0;
timeline_position += 1;
en_xscale_set();
if status & OFFSCREEN
image_xscale *= -1;
if alarm[03] == 5
image_xscale *= -1;
else
{
status |= MOVING;
if abs(x - obj_Belmont.x) < $50
i = 1;
else
if image_xscale == obj_Belmont.image_xscale
i = 1;
else
{
alarm[03] += 1;
alarm[02] = $1C;
}
if i
{
alarm[02] = 9/64; //Fleaman's gravity
vspd = -1;
hspd = 2 * image_xscale;
}
}
break;
case 6:
image_index = 2;
timeline_position += 1;
break;
case 7:
vspd += alarm[2];
if !tile_map_read(4,image_xscale) //check if there's a collision above
{
if !tile_map_read(2,image_xscale) //check if there's a collision in front
{
if !(vspd < 0)
if tile_map_read(3,1) //check if there's a collision below
{
hspd = 0;
vspd = 0
y &= ~$F;
timeline_position += 1;
}
}
else
move_horz_rev(1);
}
else
if vspd < 0
move_vert_rev();
break;
case 8:
image_index = 0;
timeline_position += 1;
break;
case 9:
alarm[2] = 8;
timeline_position += 1;
break;
case 11:
timeline_position = 5;
break;
}
Crow
timeline_index = st_crow
switch timeline_position
{
case 0:
en_xscale_set();
sprite_index = sprite_Crow;
image_index = 3;
image_speed = 0;
status |= STILL;
timeline_position += 1;
break;
case 1:
if abs(x - Belmont.x) < $78
if abs(y - Belmont.y) < $80
timeline_position += 1;
break;
case 2:
image_speed = 1/8;
image_index = 0;
status &= ~STILL;
timeline_position += 1;
break;
case 3:
alarm[03] = 1;
alarm[04] = 0;
status |= MOVING;
hspd = 0;
vspd = 0;
if abs(y-obj_Belmont.y) < $40
switch irandom(3)
{
case 0: alarm[2] = $20; break;
case 1: alarm[2] = $2C; break;
case 2: alarm[2] = $30; break;
case 3: alarm[2] = $38; break;
}
else
switch irandom(3)
{
case 0: alarm[2] = $10; break;
case 1: alarm[2] = $18; break;
case 2: alarm[2] = $1C; break;
case 3: alarm[2] = $14; break;
}
timeline_position += 1;
break;
case 4:
alarm[2] -= 1;
if alarm[02]
{
en_xscale_set(); //set xscale to face sprite
if status & OFFSCREEN
image_xscale *= -1;
vspd += 1/32*(-alarm[3]);
hspd += 1/32 * -image_xscale; //if it doesn't look right, negate this
}
else
{
switch irandom(3)
{
case 0: alarm[2] = $20; break;
case 1: alarm[2] = $18; break;
case 2: alarm[2] = $1C; break;
case 3: alarm[2] = $28; break;
}
status &= ~MOVING;
timeline_position += 1;
}
break;
case 5:
alarm[2] -= 1;
if !alarm[02]
{
status |= MOVING;
hspd = 0;
vspd = 0;
alarm[04] += 1;
if alarm[04] != 8
{
timeline_position -= 1;
alarm[3] = sign(y - Belmont.y | 1);
if abs(y-obj_Belmont.y) < $20
{
switch irandom(3)
{
case 0: vspd = 1;
alarm[02] = $30;
break;
case 1: vspd = 5/8;
alarm[02] = $48;
break;
case 2: vspd = 3/4;
alarm[02] = $38;
break;
case 3: vspd = 9/8;
alarm[02] = $2C;
break;
}
hspd = 1/4 * image_xscale;
}
else
{
switch irandom(3)
{
case 0: vspd = 3/2;
alarm[02] = $60;
break;
case 1: vspd = 7/4;
alarm[02] = $70;
break;
case 2: vspd = 2;
alarm[02] = $58;
break;
case 3: vspd = 9/4;
alarm[02] = $50;
break;
}
hspd = 3/4 * image_xscale;
}
if alarm[03]
vspd *= -1;
}
else
{
en_xscale_set();
timeline_index = -1;
hspd = 2 * image_xscale;
}
}
break;
}
-
What a great work!
I've always thought that game's logic is almost impossible to be reverse engineered... And now I'm looking at the sources, it's so fascinating! Beautiful, just beautiful!
As for the fireballs, that was purely my mistake. I've put this code into "step" event because I've mistaken them from CONSTANTLY homing fireballs (like flame from RoB Giant Bat boss). Sorry about that.
-
Thanks for catching that typo, though. I typed it up on the fly. I fixed that post.
Continuing on with CV3 code...
Medusa Head
timeline_index = st_medhead
switch timeline_position
{
case 0:
timeline_position += 1;
break;
case 1:
status |= MOVING;
en_xscale_set();
hspd = 0;
vspd = 0;
hspd = 17/16 * image_xscale;
timeline_position += 1;
break;
case 2:
sprite_index = sprite_Medusa_Head
image_speed = 1/6;
image_index = 0;
status &= ~STILL;
timeline_position += 1;
break;
case 3:
alarm[03] = 1;
vspd = 2;
ystart = y;
timeline_position += 1;
break;
case 4:
move_sinewave();
break;
}
Medusa Skull (from US CV3 hard mode)
timeline_index = st_medskull
switch timeline_position
{
case 0:
status &= ~MOVING;
timeline_position += 1;
//junk state
break;
case 1:
status |= MOVING;
en_xscale_set();
hspd = 0;
vspd = 0;
hspd = 17/16 * image_xscale;
timeline_position += 1;
break;
case 2:
sprite_index = sprite_Medusa_Skull;
image_speed = 1/10;
status &= ~STILL;
timeline_position += 1;
break;
case 3:
alarm[05] = y < view_height >> 1;
switch (system.alarm[01] & 3)
{
case 0:
vspd = -2;
alarm[04] = $3F;
break;
case 1:
vspd = -7/2;
alarm[04] = $4C;
break;
case 2:
vspd = -1;
alarm[04] = $1B;
break;
case 3:
vspd = -5/2;
alarm[04] = $28;
break;
}
if alarm[05]
vspd *= -1;
alarm[04] = 1; //makes the SWITCH junk code
alarm[03] = 1;
alarm[02] = y;
timeline_position += 1;
break;
case 4:
alarm[04] -= 1;
if alarm[04]
move_sinewave();
else
{
switch irandom(3)
{
case 0:
vspd = -2;
alarm[04] = $3F;
break;
case 1:
vspd = -7/2;
alarm[04] = $4C;
break;
case 2:
vspd = -1;
alarm[04] = $1B;
break;
case 3:
vspd = -5/2;
alarm[04] = $28;
break;
}
if alarm[05]
vspd *= -1;
}
break;
}
Winged Demon
timeline_position = st_wingdemon
switch timeline_position
{
case 0:
sprite_index = sprite_Wing_Demon;
image_speed = 1/12;
image_index = 0;
status |= MOVING;
en_xscale_set();
hspd = 0;
vspd = 0;
hspd = 17/16 * image_xscale;
timeline_position += 1;
break;
case 1:
alarm[04] = 0;
alarm[02] = y;
alarm[03] = $3F;
vspd = -1;
timeline_position += 1;
case 2:
alarm[03] -= 1;
if !alarm[03]
{
alarm[04] += 1;
switch alarm[04]
{
case 1:
case 5:
caps = 7/4;
alarm[03] = $27;
break;
case 2:
vspd = -3;
alarm[03] = $3C;
break;
case 3:
vspd = 19/4;
alarm[03] = $1D;
break;
case 4:
vspd = -1;
alarm[03] = $3F;
break;
//Alarm[04] should never exceed 5. This was a bug in CV3's code.
case 6:
vspd = $3F/$100;
alarm[03] = $DE;
break;
case 7:
vspd = -$3FC9/$100;
alarm[03] = $1D;
break;
}
}
move_sinewave();
break;
}
Living Armor
timeline_index = st_armor
switch timeline_position
{
case 0:
sprite_index = sprite_Armor;
image_speed = 1/10;
image_index = 0;
timeline_position += 1;
break;
case 1:
timeline_position += 1;
status |= MOVING;
en_xscale_set();
vspd = 0;
hspd = 3/8 * image_xscale;
break;
case 2:
if !tile_map_read($6,image_xscale) //check for collision below
{
hspd = 0;
vspd = 1;
timeline_position += 1;
exit;
}
y &= ~$F;
if tile_map_read($8,image_xscale) //check for collision in front of feet
if !tile_map_read($C,image_xscale) //check for no collision in front of knees
{
alarm[02] -= 1;
if alarm[02]
exit;
}
hspd *= -1;
image_xscale *= -1;
switch irandom(3)
{
case 0:
alarm[02] = $A9;
break;
case 1:
alarm[02] = $D1;
break;
case 2:
alarm[02] = $97;
break;
case 3:
alarm[02] = $C3;
break;
}
break;
case 3:
vspd += 1/16;
if tile_map_read($6,image_xscale) //check for collision below
{
y &= ~$F;
hspd = 0;
vspd = 0;
timeline_position += 1;
}
break;
case 4:
timeline_position = 1;
break;
}
Bone Statue
timeline_index = st_bonestat
The bone statue changes palette when it flashes. It's referred to as a different sprite here.
Disclaimer: The fireball sprite has its origin set below the sprite.
switch timeline_position
{
case 0:
//Set the xscale when the sprite is placed in the room
image_xscale = 1;
timeline_position += 1;
break;
case 1:
sprite_index = sprite_Bone_Statue;
image_speed = 0;
timeline_position += 1;
break;
case 2:
alarm[2] = $20;
timeline_position += 1;
break;
case 3:
alarm[2] -= 1;
if !alarm[2]
timeline_position += 1;
break;
case 4:
alarm[3] = 8;
alarm[2] = 4;
timeline_position += 1;
break;
case 5:
//sprite_BPflash is used for the red palette here
alarm[02] -= 1;
if !alarm[02]
{
if sprite_index == sprite_Bone_Statue
sprite_index = sprite_BPflash;
else
sprite_index = sprite_Bone_Statue;
alarm[02] = 4;
alarm[03] -= 1;
if !alarm[03]
timeline_position += 1;
}
break;
case 6:
alarm[3] = 4;
alarm[2] = 8;
timeline_position += 1;
break;
case 7:
var i,k;
alarm[02] -= 1;
if !alarm[02]
{
alarm[03] -= 1;
if alarm[03]
{
alarm[02] = $26;
if !(status & (HIDDEN|OFFSCREEN))
{
i = 8 * sign(obj_Belmont.x - x);
if image_xscale == sign(x - obj_Belmont.x)
k = 9;
else
k = -6;
spawn_fireball(i,k); //spawn a fireball at x+i,y+k
}
}
else
timeline_position += 1;
}
break;
case 8:
alarm[2] = $A0;
timeline_position += 1;
break;
case 9:
timeline_position = 2;
//renders previous state as junk unless set to 3
break;
}
Mudman
timeline_index = st_mudman
switch timeline_position
{
case 0:
y = $A8;
if tile_map_read($D,0) != 1 //ensure it spawns on mud
{
enemy_destroy();
exit;
}
if tile_map_read($17,0) //ensure it doesn't spawn in a wall
{
enemy_destroy();
exit;
}
y -= $08;
status |= PASSTHRU|STILL;
timeline_position += 1;
break;
case 1:
play_sound(snd_0c); //see thread containing audio mappings
timeline_position += 1;
break;
case 2:
sprite_index = sprite_Mudman;
timeline_position += 1;
break;
case 3:
alarm[2] = $12;
timeline_position += 1;
break;
case 4:
case 8:
case 15:
case 18:
alarm[2] -= 1;
if !alarm[2]
timeline_position += 1;
break;
case 5:
timeline_position += 1;
status &= ~PASSTHRU;
break;
case 10:
image_speed = 1/8;
status &= ~STILL;
case 6:
image_index += 1;
timeline_position += 1;
break;
case 7:
case 14:
case 17:
alarm[2] = 8;
timeline_position += 1;
break;
case 9:
switch (myIndex + irandom(255) & $07) + (hard_mode << 3)
{
case 0: alarm[2] = $88; break;
case 1: alarm[2] = $F4; break;
case 2: alarm[2] = $62; break;
case 3: alarm[2] = $B3; break;
case 4: alarm[2] = $C4; break;
case 5: alarm[2] = $A9; break;
case 6: alarm[2] = $91; break;
case 7: alarm[2] = $77; break;
case 8: alarm[2] = $68; break;
case 9: alarm[2] = $84; break;
case 10: alarm[2] = $42; break;
case 11: alarm[2] = $73; break;
case 12: alarm[2] = $24; break;
case 13: alarm[2] = $59; break;
case 14: alarm[2] = $31; break;
case 15: alarm[2] = $57; break;
}
timeline_position += 1;
break;
case 11:
timeline_position += 1;
status |= MOVING;
en_xscale_set();
vspd = 0;
hspd = 3/8 * image_xscale;
break;
case 12:
alarm[2] -= 1;
if alarm[2]
if !tile_map_read(3,image_xscale) //check for collision in front
exit;
status &= ~MOVING;
timeline_position += 1;
break;
case 13:
status |= STILL;
case 16:
image_index -= 1;
timeline_position += 1;
break;
case 19:
enemy_destroy();
break;
}
Slime
timeline_index = st_slime
At each vspd=0, there should be an hspd=0 but I removed most for redundancy.
switch timeline_position
{
case 0:
sprite_index = sprite_Slime;
status |= STILL;
status &= ~HIDDEN;
timeline_position += 1;
break;
case 1:
alarm[2] = $20;
timeline_position += 1;
break;
case 2:
case 8:
case 15:
case 24:
alarm[2] -= 1;
if !alarm[2]
timeline_position += 1;
break;
case 3:
timeline_position += 1;
en_xscale_set();
status |= MOVING;
if abs(x - Belmont.x)<$40
if image_xscale == Belmont.image_xscale
{
alarm[4] += 1;
alarm[2] = $12;
vspd = -2;
hspd = 9/8 * image_xscale;
exit;
}
vspd = -1;
hspd = 2 * image_xscale;
break;
case 10:
image_index += 1;
case 4:
case 16:
image_index += 1;
timeline_position += 1;
break;
case 5:
if y-view_yview > $F7
{
visible = 0;
timeline_position = 23;
hspd = 0;
vspd = 0;
exit;
}
vspd += 9/64;
if tile_map_read(4,image_xscale) //check for collision centered
if vspd < 0
vspd *= -1;
else exit;
else
if tile_map_read(2,image_xscale) //check for collision in front
{
hspd *= -1;
exit;
}
else
if vspd < 0
exit;
if tile_map_read(3,image_xscale) //check for collision below
{
play_sound(snd_0a);
hspd = 0;
vspd = 0;
y &= ~$F;
timeline_position += 1;
}
break;
case 6:
image_index -= 1;
timeline_position += 1;
break;
case 7:
case 14:
alarm[2] = $10;
timeline_position += 1;
break;
case 9:
if alarm[4] == 2
{
alarm[4] = 0;
timeline_position += 1;
}
else
timeline_position = 1;
break;
case 11:
hspd = 0;
vspd = -1;
timeline_position += 1;
case 12:
vspd -= 1/16;
if tile_map_read(4,image_xscale); //check for collision centered
{
vspd = 0;
timeline_position += 1;
exit;
}
if y-view_yview < $30
{
vspd = 0;
status |= HIDDEN;
timeline_position = 23;
exit;
}
break;
case 13:
case 26:
image_index = 3;
timeline_position += 1;
break;
case 17:
en_xscale_set();
hspd = 3/4 * image_xscale;
vspd = 3/2;
timeline_position += 1;
break;
case 18:
if tile_map_read(4,image_xscale) //check for collision centered
{
alarm[4] += 1;
hspd = 0;
vspd = 0;
timeline_position += 1;
exit;
}
vspd -= 5/64;
if vspd < -3
if y-view_yview < $30
{
hspd = 0;
vspd = 0;
status |= HIDDEN;
timeline_position = 23;
}
else
vspd = -3;
break;
case 19:
if alarm[4] == 2
{
alarm[4] = 0;
timeline_position += 1;
}
else
timeline_position = 13;
break;
case 25:
y = view_yview + $30;
x = Belmont.x;
case 20:
vspd = 3/2;
timeline_position += 1;
break;
case 21:
if tile_map_read(3,image_xscale) //check for collision below
{
vspd = 0;
timeline_position += 1;
exit;
}
vspd += 1/16;
if vspd>2
vspd = 2;
break;
case 22:
case 28:
timeline_position = 0;
break;
case 23:
alarm[2] = $40;
timeline_position += 1;
break;
case 27:
if tile_map_read(3,image_xscale) //check for collision below
{
vspd = 0;
timeline_position += 1;
}
break;
}
Bullfrog
timeline_index = st_frog
The tongue was treated as part of the sprite, which is unfortunate for Game Maker users (bad memory management), so you may want to modify the code to make the tongue a separate object.
switch timeline_position
{
case 0:
timeline_position += 1;
alarm[3] = 5;
alarm[2] = 0; //Junk code I think
break;
case 1:
y = view_yview + $A8; //Remove this line to aid level design
if tile_map_read($D,0) == $1 //ensure it spawns in mud
timeline_position += 1;
else
enemy_destroy();
break;
case 2:
case 15:
timeline_position += 1;
en_xscale_set();
break;
case 3:
timeline_position += 1;
play_sound(snd_20); //splash
break;
case 4:
case 16:
if abs(y - Belmont.y) < $18
if abs(x - Belmont.x) < $30
{
timeline_position = $17;
exit;
}
var temp; temp = 0;
if timeline_position == 4
if subroom //basically, check if frog is "outdoors"
temp = 1;
if temp //Frogs jump high out of mud unless in caves
{
vspd = -4;
hspd = 3/4 * image_xscale;
alarm[2] = $20;
}
else
{
vspd = -3;
hspd = 9/8 * image_xscale;
alarm[2] = $28;
}
alarm[3] -= 1;
if !alarm[3]
timeline_position = 41;
timeline_position += 1;
if tile_map_read($8,image_xscale) != $1 //check if out of the mud yet
timeline_position += 1;
status |= MOVING;
break;
case 5:
case 9:
case 17:
case 21:
timeline_position += 1;
spawn_mud(0,0); //create the mud splash effect
break;
case 6:
case 18:
timeline_position += 1;
status |= MOVING;
break;
case 7:
sprite_index = sprite_Bullfrog;
image_speed = 0;
image_index = 0;
status |= STILL;
timeline_position += 1;
break;
case 8:
case 20:
vspd += alarm[2]/$100;
if vspd<0 exit;
switch tile_map_read($8,1) //check for collision below
{
case 0: //no collision
image_index = 1;
exit;
case 1: //collision with mud
y &= ~$F;
timeline_position += 1;
break;
default: //all other collisions
y &= ~7;
break;
}
timeline_position += 1;
image_index = 2;
status &= ~MOVING;
hspd = 0;
vspd = 0;
break;
case 10:
alarm[2] = 6;
timeline_position += 1;
break;
case 11:
case 14:
case 25:
case 28:
case 31:
case 34:
case 37:
case 40:
alarm[2] -= 1;
if !alarm[2]
timeline_position += 1;
break;
case 12:
case 23:
image_index = 3;
timeline_position += 1;
break;
case 26:
case 29:
case 32:
image_index += 1;
timeline_position += 1;
break;
case 13:
alarm[2] = $20;
timeline_position += 1;
break;
case 19:
image_index = 0;
timeline_position += 1;
break;
case 22:
case 41:
timeline_position = 10;
break;
case 24:
case 27:
case 30:
case 33:
case 36:
case 39:
alarm[2] = 2;
timeline_position += 1;
break;
case 35:
case 38:
image_index -= 1;
timeline_position += 1;
break;
}
Ghost (fast)
timeline_index = st_ghost
switch timeline_position
{
case 0:
sprite_index = sprite_Ectoplasm;
image_speed = 1/8;
image_index = 0;
status &= ~STILL;
timeline_position += 1;
break;
case 1:
status |= MOVING; //kinda pointless without speed, but it's there
timeline_position += 1;
break;
case 2:
alarm[2] = $16;
timeline_position += 1;
break;
case 3:
case 7:
alarm[2] -= 1;
if !alarm[2]
timeline_position += 1;
break;
case 4:
sprite_index = sprite_Ghost;
image_index = 0;
image_speed = 1/A;
timeline_position += 1;
break;
case 5:
en_xscale_set();
if status & OFFSCREEN
image_xscale *= -1;
hspd = (2+global.hard_mode)/4 * image_xscale;
vspd = 1/4;
if y > obj_Belmont.y
vspd *= -1;
timeline_position += 1;
break;
case 6:
alarm[2] = $20;
timeline_position += 1;
break;
case 8:
timeline_position = 5;
break;
}
Ghost (slow)
timeline_index = st_sloghost
switch timeline_position
{
case 0:
sprite_index = sprite_Ectoplasm
image_speed = 1/8;
image_index = 0;
status &= ~STILL;
timeline_position += 1;
//set cycle limit to
/*
if timeline_index==st_sloghost
if image_index>=2
image_index = 0;
*/
break;
case 1:
status |= PASSTHRU;
timeline_position += 1;
break;
case 2:
if myIndex & 1 //this causes 2 ghosts to appear at different times
alarm[02] = $D0;
else
alarm[02] = $40;
timeline_position += 1;
break;
case 3:
alarm[2] -= 1;
if !alarm[2]
timeline_position += 1;
break;
case 4:
status &= ~PASSTHRU;
timeline_position += 1;
break;
case 5:
status |= MOVING;
timeline_position += 1;
break;
case 6:
image_index = 0;
image_speed = 1/8;
timeline_index = st_ghost;
timeline_position = 2;
break;
}
Wallhugger
timeline_index = st_fuzzy
Set alarm[4] to adjust the speed.
I haven't tested this code, so xx and yy may be swapped.
switch timeline_position
{
case 0:
sprite_index = sprite_Wallhugger
image_index = 0;
image_speed = 1/4;
status &= ~STILL;
timeline_position += 1;
break;
case 1:
var temp; temp = 0;
switch alarm[4]
{
case 0:
alarm[2] = 0;
alarm[3] = 0
alarm[4] = 2;
break;
case 1:
alarm[2] = 1;
alarm[3] = 2;
alarm[4] = 1;
break;
case 2:
alarm[2] = 1;
alarm[3] = 2;
alarm[4] = 2;
break;
case 3:
alarm[2] = 0;
alarm[3] = 0;
alarm[4] = 3;
break;
case 4:
alarm[2] = 0;
alarm[3] = 2;
alarm[4] = 3;
break;
default:
alarm[2] = 0;
alarm[3] = 0;
alarm[4] = 0;
break;
}
timeline_position += 1;
status |= MOVING;
temp = 1;
break;
case 2:
var temp; temp = 0;
var xx,yy;
//This determines the tile_map_read arguments
switch alarm[3]|alarm[2]<<3
{
case 0:
xx = 12
yy = 7
break;
case 1:
xx = -8
yy = 12
break;
case 2:
xx = -12
yy = -8
break;
case 8:
xx = -12
yy = 7
break;
case 9:
xx = -8
yy = -12
break;
case 10:
xx = 12
yy = -8
break;
}
if !tile_map_read(xx,yy)
{
timeline_position += 1;
switch alarm[3]|alarm[2]<<3
{
case 0:
case 6:
case 10:
alarm[3] = 1;
break;
case 1:
case 7:
case 11:
alarm[3] = 2;
break;
case 2:
case 4:
case 8:
alarm[3] = 3;
break;
default:
alarm[3] = 0;
break;
}
temp = 1;
break;
}
else
break;
}
if temp
{
temp = alarm[4]/4;
switch alarm[3]
{
case 0:
hspd = -1/2 - temp;
vspd = 0;
break;
case 1:
hspd = 0;
vspd = 1/2 + temp;
break;
case 2:
hspd = 1/2 + temp;
vspd = 0;
break;
case 3:
hspd = 0;
vspd = -1/2 - temp;
break;
}
}
COMING SOON: ...when i get around to it
Skeletons
Zombies
Fire Walker
Just a reminder disclaimer: I haven't tested any of these codes, so no guarantees are made. They were just translated from the original NES assembly to the best of my ability. I have double-checked them before posting them here, but not actually tested. So in short, use at your own risk.
-
I found this thread very useful, and I have to make to you some questions @TheouAegis:
Do you have by chance the 1-heart waving and the jump move codes?
Does the Castlevania 1 codes are the same as Vampire Killer's?
-
You mean MSX's version? Not sure about that. I haven't played the MSX version and don't have an MSX emulator. Watching longplay videos, it's hard for me to say. for the most part, the game design appears very close for most enemies with minor differences.
Single Heart Item Drop
if timeline_position > 2
{
if abs(x - Belmont.x) < 12
if abs(y - Belmont.y) < 20
{
play_sound(snd_19);
subHearts += 1;
timeline_position = 5;
}
}
switch timeline_position
{
case 0:
status |= STILL;
sprite_index = spr_SmallHeart;
timeline_position+=1
break;
case 1:
status |= MOVING;
vspd = 1/4;
timeline_position+=1;
break;
case 2:
timeline_position+=1
alarm[3] = 0;
alarm[2] = 1;
break;
case 3:
alarm[2] -= 1;
if !alarm[2]
{
alarm[3] ^= 1;
hspeed = -alarm[3] | 1;
alarm[2] = $38;
}
if alarm[3]
hspeed += 1/32;
else
hspeed -= 1/32;
if x < view_xview+4
instance_destroy();
else
if x > view_xview+$FB
instance_destroy();
else
if y < view_yview+8
instance_destroy();
else
if y > view_yview+$D7
instance_destroy();
if tile_map_read($0E,1) //check for collision below
{
vspeed = 0;
hspeed = 0;
timeline_position += 1;
}
break;
case 4:
alarm[2] -= 1;
if !alarm[02]
timeline_position += 1;
break;
case 5:
instance_destroy();
break;
}
-
Little more clarification on the coding style (for those that use Game Maker and those that just want to read the code):
In Game Maker, by default alarms are timers that - when assigned a corresponding routine - will count down and execute code upon reaching 0. The fact of the matter is the alarm array is just a series of variables that do nothing by themselves if no routine is defined for them. At one time I used the alarm array for everything in my code, which wasn't an issue for me at first; after leaving the project for a couple months to spend time in the real world, when I got back to working on it I had forgotten what all the alarms were stand-ins for. I've switched many of my alarms over to better-named variables, but there are still some that I retain the alarm use for.
alarm[2]
This is used (almost) exclusively for a countdown timer. Common code with it is:
alarm[2] -= 1; if alarm[2] break;
In actuality, it's used for NES RAM offset $[#606+X] in CV3 and offset $[#5DC+X] in CV1. Most of the time those RAM offsets are used for countdown timers, but once in a while they're used for other things. Either way, don't worry about it, alarm[2] is just another variable.
alarm[3]
This is used sometimes as a secondary timer or counter (e.g., counting fleaman jumps). Again, don't worry, it's just another variable.
system.alarm[0]
If I accidentally leave this in my code (or intentionally leave it in because i can't think of anything else), this is a global variable that counts up steadily each step. In other words, somewhere in your project you should have a controller run this line of code every step:
alarm[0] += 1;
Of course you can name the variable whatever you want. In my project, it's just alarm[0] inside an object called system.
In Game Maker, there is a feature called timelines. Timelines are basically a form of finite state machine that run on their own if you tell them to do so. However, just like alarms, the timeline variables are just that - variables. They don't do anything themselves without corresponding routines (i.e., timelines). I only use two of the variables in my code.
timeline_index
Once in a while (e.g., sleeping bats) you'll see a reference to timeline_index. This is simply the script that the state machine to run is under. For example, when the sleeping bat has swooped down, it uses essentially the same code as the flying bat. Rather than having duplicate code, I change the ID of the script to execute each step; so the first script run is saved in timeline_index and then timeline_index is changed to hold the ID of the next script to run.
timeline_position
This is the value of each state within the current finite state machine. You can replace timeline_position with state if it makes you feel better. I like that timeline_position is such a long variable name, so it makes it stand out more in code for me.
play_sound
This isn't an actual function in Game Maker. So both GM users and non-users alike should be able to figure out what this script should more or less do....
$ (dollar sign)
In Game Maker, the dollar sign denotes hexadecimal. So make sure if you're coding for something other than Game Maker that you change the dollar sign to the corresponding hexadecimal symbol (or convert it to decimal yourself).
hspd vspd hspeed vspeed
Sometimes I forget to use hspd or vspd and instead use the built-in Game Maker variables hspeed and vspeed. Do NOT use hspeed and vspeed; they will just make things harder on you. For those that don't use Game Maker and can't figure it out themselves, hspd is the horizontal speed and vspd is the vertical speed. Positive is right or down, negative is left or up.
instance_destroy()
For the non-users, just try to think about what the function might do...
view_xview view_yview
In Game Maker, to limit what's shown in the room/map, you set a view. At any time, a view will be located in the room at the coordinates (view_xview, view_yview). In NES games, the view is technically 256x256 pixels in size, but much less is shown, obviously. We can ignore the bottom of the view, so it's still okay to think of the view as 256x256 with 32 pixels hacked off from the bottom.
tile_map_read
Okay, this one may need a little explanation. If you're one of those programmers that still uses objects for ground and fills the room up with hundreds or thousands of these little ground objects that serve no purpose other than to prevent the player from moving through the pixels they occupy, then you should learn to break out of that habit. Sprites are freaking amazingly fast in Game Maker, making them better than tiles, but that's just a flaw in Game Maker's programming. Once you get out of the drawing phase, all those objects have the potential to slow your game down drastically. That's where a tile map comes into play. Use a tile map, pick a particular point around an instance, check if that instance occupies a cell in the tile map that is flagged as solid. That's what this function does. There's no one way to do that, so I won't go into the code I use.
-
Continuing on with CV3's AI codes...
Fire Walker
timeline_index = st_fireman
switch timeline_position
{
case 0:
sprite_index = spr_Fireman
status &= ~STILL;
image_index = 0;
image_speed = 1/$10;
timeline_position += 1;
break;
case 1:
status |= MOVING;
en_xscale_set(); //set xscale to face Belmont
hspd = 1/2 * image_xscale;
timeline_position += 1;
break;
case 2:
alarm[2] = $40;
timeline_position += 1;
break;
case 3:
if !tile_map_read($5,image_xscale) //check if no collision in front below
{
hspd *= -1;
image_xscale *= -1;
}
else
if tile_map_read($0,image_xscale) //check if collision in front ahead
{
hspd *= -1;
image_xscale *= -1;
}
else
alarm[02] -= 1;
if !alarm[02]
{
alarm[02] = $40;
en_xscale_set(); //Set xscale to face player
hspd = abs(hspd) * image_xscale;
}
if image_index == 2
if tile_map_read($B,image_xscale) //Check for collision behind and below
spawn_ember($06*image_xscale,$07); //Spawn ember 6 px behind (and 7 px below)
break;
}
Ember (for Fire Walker)
timeline_index = st_ember
Ember sprite, like all small sprites in CV3, is 8 pixels above the origin.
switch timeline_position
{
case 0:
sprite_index = spr_Ember;
image_speed = 1/$A;
image_index = 0;
timeline_positon += 1;
break;
case 1:
alarm[2] = $FF;
timeline_position += 1;
break;
case 2:
case 5:
alarm[2] -= 1;
if !alarm[2]
timeline_position += 1;
break;
case 3:
image_index = 2;
status |= STILL;
timeline_position += 1;
break;
case 4:
alarm[2] = $A;
timeline_position += 1;
break;
case 6:
instance_destroy();
break;
}
Buried Zombie (comes out of the ground)
timeline_index = st_digzombie
Code is based around level design, so understand it before you use it.
switch timeline_position
{
case 0:
var i;
i = (system.alarm[0] + x & $07)+((global.block>0)+(global.hard_mode>0)<<3);
//You can use irandom(7) for the first part if you want
switch i
{
case 17: x = $D8; break;
case 14:
case 22: x = $C0; break;
case 1:
case 11: x = $A0; break;
case 4: x = $98; break;
case 6: x = $90; break;
case 3: x = $88; break;
case 5: x = $78; break;
case 7: x = $70; break;
case 0:
case 2: x = $68; break;
case 13: x = $60; break;
case 12: x = $5C; break;
case 8: x = $58; break;
case 15: x = $50; break;
case 9: x = $4C; break;
case 10: x = $48; break;
case 21: x = $44; break;
case 19: x = $40; break;
case 18:
case 20: x = $38; break;
case 16: x = $30; break;
case 23: x = $2C; break;
}
x = (obj_Belmont.x - view_xview + x & $FF) + view_xview;
status |= PASSTHRU + STILL;
timeline_position += 1;
break;
case 1:
timeline_position += 1;
en_xscale_set(); //set scale to face Belmont
break;
case 2:
sprite_index = spr_Zombie;
image_index = 2;
image_speed = 0;
timeline_position += 1;
break;
case 3:
case 7:
case 15:
case 18:
alarm[02] = $08;
timeline_position += 1;
break;
case 4:
case 8:
case 16:
case 19:
alarm[02] -= 1;
if !alarm[02]
timeline_position += 1;
break;
case 5:
timeline_position += 1;
status &= ~PASSTHRU;
break;
case 6:
image_index += 1;
status |= STILL;
timeline_position += 1;
break;
case 9:
switch irandom(3) //(spawn+Belmont.x & system.alarm[01])+system.alarm[0] & $03
{
case 0:
alarm[02] = $F8;
break;
case 1:
alarm[02] = $B0;
break;
case 2:
alarm[02] = $A0;
break;
case 3:
alarm[02] = $C8;
break;
}
timeline_position += 1;
break;
case 10:
image_index = 0;
image_speed = 1/$10;
status &= ~STILL;
status |= MOVING;
en_xscale_set(); //set xscale to face Belmont
hspd = 1/2 * image_xscale;
break;
case 11:
if !tile_map_read(1,image_xscale) //check if no collision below
{
hspd = 0;
vspd = 1;
timeline_position += 1;
}
if tile_map_read(0,image_xscale) //check if collision in front
{
hspd *= -1;
image_xscale *= -1;
exit;
}
alarm[02] -= 1;
if !alarm[02]
timeline_position += 2;
break;
case 12:
if tile_map_read(1,image_xscale) //check if collision below
{
vspd = 0;
y &= ~15;
status &= ~STILL;
timeline_position = 9;
}
else
{
vspd += 5/64;
}
break;
case 13:
status &= ~MOVING;
timeline_position += 1;
break;
case 14:
status |= STILL;
image_speed = 0;
image_index = 3;
timeline_position += 1;
break;
case 17:
image_index -= 1;
timeline_position += 1;
break;
case 20:
instance_destroy();
break;
}
Walking Dead
timeline_index = st_edgezomb
The first zombies you encounter. Code is based around level design, so make sure you understand it before you use it.
switch timeline_position
{
case 0:
y = $A4;
repeat(2)
if tile_map_read(0,0) //Check if collision right at its spawn
{
y -= $14;
timeline_position += 1;
exit;
}
else y += $10;
instance_destroy();
break;
case 1:
sprite_index = spr_Zombie;
image_index = 0;
image_speed = 1/$10;
status &= ~STILL;
status |= MOVING;
en_xscale_set(); //set xscale to face Belmont
hspd = 1/2 * image_xscale;
break;
case 2:
if !tile_map_read(1,1) //check if no collision below
{
hspd = 0;
vspd = 1;
timeline_position += 1;
}
else
if tile_map_read(0,1) //check if collision ahead
{
hspd *= -1;
image_xscale *= -1;
}
break;
case 3:
if tile_map_read(1,1) //check if collision below
{
vspd = 0;
y &= ~15;
timeline_position = 1;
}
else
vspd += 5/64;
break;
}
Roaming Skeleton
timeline_index = st_normskel
switch timeline_position
{
case 0:
timeline_position += 1;
sprite_index = spr_Skeleton;
status &= ~STILL;
image_speed = 1/($14-$04*(palette==3)); //blood skeletons animate faster
image_index = 0;
break;
case 1:
status |= MOVING;
en_xscale_set();
hspd = ($60+$20*(palette==3))/$100 * image_xscale;
timeline_position += 1;
break;
case 2:
alarm[02] = $C0-$60*(palette==3);
timeline_position += 1;
break;
case 3:
if palette != 3
if !tile_map_read($D,image_xscale) //check if no collision below
{
hspd = 0;
vspd = 1;
timeline_position += 1;
exit;
}
if !tile_map_read($5,image_xscale) //check if no collision ahead below
{
hspd *= -1;
image_xscale *= -1;
exit;
}
y &= ~$F; //This is located here in the code
if tile_map_read($0,image_xscale) //check if collision in front
{
hspd *= -1;
image_xcale *= -1;
exit;
}
alarm[02] -= 1;
if !alarm[02]
{
status &= ~MOVING;
timeline_position += 1+(palette==3);
}
break;
case 4:
//This is run when non-jumping enemies fall
vspd += 1/16;
if tile_map_read($06,1) //check if collision below
{
y &= ~$F;
vspd = 0;
timeline_position += 1;
}
break;
case 5:
timeline_position = 1;
break;
}
Sword Skeleton
timeline_index = st_swordskel
The sword is part of the skeleton's sprite, but you can have parallel code draw the sword depending on the Skeleton's image_index.
switch timeline_position
{
case 0:
sprite_index = spr_Sword_Skeleton;
image_speed = 1/$10;
image_index = 0;
status &= ~STILL;
status |= MOVING;
en_xscale_set();
hspd = 1/2 * image_xscale;
timeline_position += 1;
break;
case 1:
//Direction setting script for enemies like Whip and Sword Skeletons
var temp;
status |= MOVING;
hspd = 0;
alarm[03] = $28;
en_xscale_set();
if status & OFFSCREEN
{
image_xscale *= -1;
if abs(x-obj_Belmont.x) > $3F
temp = 0;
else
temp = 1;
}
else
{
if abs(x-obj_Belmont.x) < $40
temp = 0;
else
temp = 1;
}
if temp
hspd = 19/16 * (obj_Belmont.x < x);
else
hspd = 19/16 * (x < obj_Belmont.x);
timeline_position += 1;
break;
case 2:
//Walking collision check for enemies like Whip and Sword Skeletons
if !tile_map_read($13,image_xscale) //check if no collision below
{
hspd = 0;
vspd = 1;
timeline_position += 1
exit;
}
//check if no collision below in direction of movement
switch tile_map_read($05,-sign(hspeed))
{
case 0:
case 4:
move_horz_rev();
exit;
}
if tile_map_read($00,-sign(hspeed)) //check if collision in direction of movement
hspd *= -1;
else
{
alarm[03] -= 1;
if !alarm[03]
{
hspd = 0;
timeline_position += 3;
}
else
hspd += 1/64 * -sign(hspd);
}
break;
case 3:
//This is run when non-jumping enemies fall
vspd += 1/16;
if tile_map_read($06,1) //check if collision below
{
y &= ~$F;
vspd = 0;
timeline_position += 1;
}
break;
case 4:
case 11:
timeline_position = 0;
break;
case 5:
image_index = 2;
status |= STILL;
status &= ~MOVING;
timeline_position += 1;
break;
case 6:
case 9:
case 15:
alarm[02] = $08;
timeline_position += 1;
break;
case 7:
case 10:
case 13:
case 16:
alarm[2] -= 1;
if !alarm[2]
timeline_position += 1;
break;
case 8:
case 11:
x += 8 * image_xscale
image_index += 1;
timeline_position += 1;
break;
case 12:
alarm[02] = $10;
timeline_position += 1;
break;
case 14:
x += 8 * -image_xscale;
image_index = 2;
timeline_position += 1;
break;
}
Dullahan
timeline_index = st_dhuron
It is basically a faster Sword Skeleton, so the code is identical except for a couple lines.
switch timeline_position
{
case 0:
sprite_index = spr_Dullahan;
image_speed = 1/$A;
image_index = 0;
status &= ~STILL;
status |= MOVING;
en_xscale_set();
hspd = 3/8 * image_xscale;
timeline_position += 1;
break;
case 1:
//Direction setting script for enemies like Whip and Sword Skeletons
var temp;
status |= MOVING;
hspd = 0;
alarm[03] = $28;
en_xscale_set();
if status & OFFSCREEN
{
image_xscale *= -1;
if abs(x-Belmont.x) > $3F
temp = 0;
else
temp = 1;
}
else
{
if abs(x-Belmont.x) < $40
temp = 0;
else
temp = 1;
}
if temp == 1
hspd = 19/16 * (Belmont.x < x);
else
hspd = 19/16 * (x < Belmont.x);
timeline_position += 1;
break;
case 2:
//Walking collision check for enemies like Whip and Sword Skeletons
if !tile_map_read($13,image_xscale)
{
hspd = 0;
vspd = 1;
timeline_position += 1
exit;
}
switch tile_map_read($05,-sign(hspeed))
{
case 0:
case 4:
hspd *= -1;
exit;
}
if tile_map_read($00,-sign(hspeed))
hspd *= -1;
else
{
alarm[03] -= 1;
if !alarm[03]
{
hspd = 0;
timeline_position += 3;
}
else
hspd += 1/64 * -sign(hspd);
}
break;
case 3:
//This is run when non-jumping enemies fall
vspd += 1/16;
if tile_map_read($06,1)
{
y &= ~$F;
vspd = 0;
timeline_position += 1;
}
break;
case 4:
case 11:
timeline_position = 0;
break;
case 5:
image_index = 2;
status |= STILL;
status &= ~MOVING;
timeline_position += 1;
break;
case 6:
case 9:
case 15:
alarm[02] = 4;
timeline_position += 1;
break;
case 7:
case 10:
case 13:
case 16:
alarm[2] -= 1;
if !alarm[2]
timeline_position += 1;
break;
case 8:
case 11:
x += 8 * image_xscale;
image_index += 1;
timeline_position += 1;
break;
case 12:
alarm[02] = 8;
timeline_position += 1;
break;
case 14:
x += 8 * -image_xscale;
image_index = 2;
timeline_position += 1;
break;
}
Whip Skeleton
timeline_index = st_whipskel
It is basically a Sword Skeleton with much longer reach, so the code is pretty much identical to a Sword Skeleton. However, the second half deviates a bit.
//Whip is drawn with the sprite 32px long
switch timeline_position
{
case 0:
sprite_index = spr_Whip_Skeleton;
image_speed = 1/$A;
image_index = 0;
status &= ~STILL;
status |= MOVING;
en_xscale_set();
hspd = 1/2 * image_xscale;
timeline_position += 1;
break;
case 1:
//Direction setting script for enemies like Whip and Sword Skeletons
var temp;
status |= MOVING;
hspd = 0;
alarm[03] = $28;
en_xscale_set();
if status & OFFSCREEN
{
image_xscale *= -1;
if abs(x-Belmont.x) > $3F
temp = 0;
else
temp = 1;
}
else
{
if abs(x-Belmont.x) < $40
temp = 0;
else
temp = 1;
}
if temp == 1
hspd = 19/16 * (Belmont.x < x);
else
hspd = 19/16 * (x < Belmont.x);
timeline_position += 1;
break;
case 2:
//Walking collision check for enemies like Whip and Sword Skeletons
if !tile_map_read($13,image_xscale)
{
hspd = 0;
vspd = 1;
timeline_position += 1
exit;
}
switch tile_map_read($05,-sign(hspeed))
{
case 0:
case 4:
hspd *= -1;
exit;
}
if tile_map_read($00,-sign(hspeed))
hspd *= -1;
else
{
alarm[03] -= 1;
if !alarm[03]
{
hspd = 0;
timeline_position += 3;
}
else
hspd += 1/64 * -sign(hspd);
}
break;
case 3:
//This is run when non-jumping enemies fall
vspd += 1/16;
if tile_map_read($06,1)
{
y &= ~$F;
vspd = 0;
timeline_position += 1;
}
break;
case 13:
case 4:
timeline_position = 0;
break;
case 5:
if abs(x-Belmont.x) > $47
timeline_position = 0;
else timeline_position += 1;
break;
case 6:
hspd = 0;
timeline_position += 1;
break;
case 7:
image_index = 2;
status |= STILL;
timeline_position += 1;
break;
case 8:
case 11:
alarm[2] = $8;
timeline_position += 1;
break;
case 9:
case 12:
alarm[2] -= 1;
if !alarm[2]
timeline_position += 1;
break;
case 10:
image_index += 1;
timeline_position += 1;
break;
}
Bone Skeleton
timeline_index = st_boneskel
switch timeline_position
{
case 0:
sprite_index = spr_Bone_Skeleton;
image_speed = 1/$10;
image_index = 0;
status &= ~STILL;
timeline_position += 1
break;
case 1:
//Direction setting script for enemies like Whip and Sword Skeletons
var temp;
status |= MOVING;
alarm[03] = $28;
en_xscale_set();
if status & OFFSCREEN
{
image_xscale *= -1;
if abs(x-Belmont.x) > $3F
temp = 0;
else
temp = 1;
}
else
{
if abs(x-Belmont.x) < $40
temp = 0;
else
temp = 1;
}
if temp == 1
hspd = 19/16 * (obj_Belmont.x < x);
else
hspd = 19/16 *(x < obj_Belmont.x);
timeline_position += 1
break;
case 2:
var temp;
temp = 0;
if !tile_map_read($5,hspd<0) //check if no collision below in direction of travel
temp = 1;
else
if !tile_map_read($14,hspd<0) //check if no collision ahead in direction of travel
{
alarm[03] -= 1;
if !alarm[03]
timeline_position = 4
else
hspd += 1/16 * -sign(hspd);
}
else
if tile_map_read($04,hspd<0) //check if collision ahead at face height
hspd *= -1;
else temp = 1;
if temp
{
hspd = 1;
vspd = -2;
status |= STILL;
timeline_position += 1;
}
break;
case 3:
vspd += 5/64;
if vspd > 2
vspd = 2;
//Will front foot land?
if !tile_map_read($5,hspd<0)
break;
//If so, will back foot land?
if !tile_map_read($6,!image_xscale)
{
//Jumped into a wall, so drop straight down
hspd = 0;
vspd = 3/2;
timeline_position = 11;
}
else
{
vspd = 0;
hspd = 0;
status &= ~STILL;
y &= ~15;
}
break;
case 4:
hspd = 0;
timeline_position+=1;
break;
case 5:
image_speed = 1/8;
image_index = 4;
timeline_position += 1;
break;
case 6:
alarm[2] = $F;
timeline_position += 1;
break;
case 7:
alarm[2] -= 1;
if !alarm[2]
timeline_position += 1;
break;
case 8:
if !(status & (HIDDEN|OFFSCREEN))
{
sound_play(snd_EnemyToss);
spawn_bone(0,-12); //throw a bone 12 px above origin
}
timeline_position += 1;
break;
case 9:
alarm[2] = $30;
timeline_position += 1;
//junk state, not sure what it's for
break;
case 10:
timeline_position = 0;
break;
case 11:
if !tile_map_read(6,1) //check for collision below back foot
vspd += 5/64;;
else
{
vspd = 0;
status &= ~STILL;
y &= ~$F;
timeline_position = 4;
}
break;
}
Bone Projectile
timeline_index = st_bone
For use with Bone Skeleton's bone.
switch timeline_position
{
case 0:
sprite_index = spr_Bone;
image_speed = 1/$C;
image_index = 0;
status &= ~STILL;
status |= MOVING;
hspd = 3/4 * sign(Belmont.x - x);
vspd = -11/4;
timeline_position += 1;
break;
case 1:
vspd += 13/128;
projectile_destroy(); //If projectile is off-screen, destroy it
break;
}
Blood Skeleton (bonus code)
timeline_index = st_bloodskel
Blood Skeletons have extra code run when the Skeleton is injured. The rest of the time it's just the normal code for that particular Skeleton class.
switch timeline_position
{
case 0:
timeline_position += 1;
status |= PASSTHRU;
break;
case 1:
timeline_position += 1;
status &= ~MOVING;
break;
case 2:
timeline_position += 1;
image_index = 6; //Originally, I made the bone pile part of the skeleton's sprite
image_speed = 1/$18;
status &= ~STILL;
break;
case 3:
alarm[2] = $38;
timeline_position += 1;
break;
case 4:
case 7:
case 10:
case 13:
alarm[2] -= 1;
if !alarm[2]
timeline_position += 1;
break;
case 5:
status |= STILL;
timeline_position += 1;
break;
case 6:
alarm[2] = $50;
timeline_position += 1;
break;
case 8:
case 11:
image_index -= 1;
timeline_position += 1;
break;
case 9:
case 12:
alarm[2] = $18;
timeline_position += 1;
break;
case 14:
status &= ~PASSTHRU;
timeline_position += 1;
break;
case 15:
timeline_position = 0;
timeline_index = en_timeline_get(object_index); //Run the normal state machine
break;
}
Coming Soon:
Mermen
Owl
Fungi
and more...
-
You mean MSX's version? Not sure about that. I haven't played the MSX version and don't have an MSX emulator. Watching longplay videos, it's hard for me to say. for the most part, the game design appears very close for most enemies with minor differences.
Sorry for the late reply, I was so busy these days about Vampire Killer yeah I meant the MSX2's, thanx for the single heart you shared I don't know how to express myself, the thing is I'm not used to programming so Could you point me out, please! where is the code for the animation only? because I read something like "sound" and "alarms" that confuses me, I'll try to run it using the devkitSMS in C, It is the first time I try moving sprites by itself and if I succeed then I'll try to make the same with the water splash.
-
The small heart sprite isn't animated. The motion of the heart is determined by hspd (horizontal vector) and vspd (vertical vector). For something as simple as item drops, you can skip the status |= lines, even if you decide to actually use the control system.
if timeline_position > 2 The heart can't be grabbed until state 3
{
if abs(x - Belmont.x) < 12 When the player is within range of the heart...
if abs(y - Belmont.y) < 20
{
play_sound(snd_19); Play the "heart acquired" sound effect
subHearts += 1; Increase the number of hearts
timeline_position = 5; Jump to the last state
}
}
switch timeline_position
{
case 0:
status |= STILL;
sprite_index = spr_SmallHeart; Set the sprite of the heart
timeline_position+=1 Go to next state
break;
case 1:
status |= MOVING;
vspd = 1/4; Set the vertical speed to 1/4
timeline_position+=1;
break;
case 2:
timeline_position+=1
alarm[3] = 0; This determines the left/right direction
alarm[2] = 1; This is a timer
break;
case 3:
alarm[2] -= 1; Count down the timer
if !alarm[2] Check if the timer reached 0
{
alarm[3] ^= 1; Change directions
hspeed = -alarm[3] | 1; Set the horizontal speed
alarm[2] = $38; Reset the timer
}
Add horizontal air resistance to the heart
if alarm[3]
hspeed += 1/32;
else
hspeed -= 1/32;
If the heart leaves the screen, destroy it
if x < view_xview+4
instance_destroy();
else
if x > view_xview+$FB
instance_destroy();
else
if y < view_yview+8
instance_destroy();
else
if y > view_yview+$D7
instance_destroy();
If the heart touches the ground, stop it
if tile_map_read($0E,1) //check for collision below
{
vspeed = 0;
hspeed = 0;
timeline_position += 1;
}
break;
case 4:
alarm[2] -= 1; Count down to when the heart finally vanishes
if !alarm[02]
timeline_position += 1;
break;
case 5:
instance_destroy();
break;
}
I just realized I left out the final timer value for how long to sit on the ground. I want to say it's $78, but I'm probably wrong. I'll have to look it up again later.
-
I tried to run the single heart code by hand and got stuck here:
case 3:
alarm[2] -= 1; Count down the timer
if !alarm[2] Check if the timer reached 0
{
alarm[3] ^= 1; Change directions
alarm[3] is never going to change the direction the value is always 1 unles a "|" is used instead of "^", I tried to add the horizontal air resistance without success, do you mind giving me a hand? in order to add the horizontal air resistance, this is the code in devkitSMS I made:
#include "SMSlib.h" // we're including the library with the functions we will use
#include "assets.h" // we're including the assets we created before
#define DIR_SE 1
#define DIR_SW 2
#define STOPIT 3
#define HEART_SPEED_NORM_X (unsigned int)((1<<8)/2) //hspd 1/2
#define HEART_SPEED_NORM_Y (unsigned int)((1<<8)/4) //vspd 1/4
#define HEART_W 8 //heart width
#define HEART_H 8 //heart height
#define LEFT_LIMIT (200-32)
#define RIGHT_LIMIT (200)
#define BOTTOM_LIMIT (192-24)
#define SPRITE_TILES 256
#define HEART_TILES (SPRITE_TILES+72) //single heart
unsigned int HeartDir, HeartX, HeartY;
void initGame (void) {
HeartX=((200-16)<<8); //original position in X
HeartY=(50)<<8; //original position in Y
HeartDir=DIR_SW;
}
void moveHeart (void) {
switch (HeartDir) {
case DIR_SE:HeartX+=HEART_SPEED_NORM_X; HeartY+=HEART_SPEED_NORM_Y; break;
//direction SE
case DIR_SW:HeartX-=HEART_SPEED_NORM_X; HeartY+=HEART_SPEED_NORM_Y; break;
//direction SW
case STOPIT: break;
}
if (((HeartY/256)+HEART_H)==BOTTOM_LIMIT) {
HeartDir=STOPIT;
// bottom limit collisions
}
if (((HeartX/256)-HEART_W)<LEFT_LIMIT) {
// left limit collisions
if (HeartDir==DIR_SW) HeartDir=DIR_SE;
}
if (((HeartX/256)+HEART_W)>RIGHT_LIMIT) {
// right limit collisions
if (HeartDir==DIR_SE) HeartDir=DIR_SW;
}
}
void drawHeart (void) {
SMS_addSprite((HeartX>>8)-(HEART_W/2), (HeartY>>8)-(HEART_H/2), HEART_TILES);
// (x,y,tile)
}
void main (void) {
SMS_setSpriteMode (SPRITEMODE_NORMAL);
loadAssets();
initGame();
SMS_displayOn();
for (;;) {
moveHeart();
SMS_initSprites();
drawHeart();
SMS_finalizeSprites();
SMS_waitForVBlank();
SMS_copySpritestoSAT();
}
}
-
Make sure alarm 3 starts at zero. And the carat is for bitwise xor. As long as alarm 3 is defaulted to zero when the heart is created, then xor 1 will always toggle it.
-
I'll probably post more tonight... maybe... But just a heads up for anyone hoping for CV3 boss code:
I haven't delved very far into CV3 boss codes at all. I started on Dracula, the Vampire Bat, Cyclops, and Death; however, bosses were coded by someone other than whoever coded the enemies and Trevor. I'm thinking it was Kitamoto, possibly Okuda; I strongly doubt Akamatsu programmed the bosses, because the rest of the code seems to follow a particular direction (and he was the director).
So, even though I have half of Death's code done, I don't expect to have any of the boss codes properly translated any time in the near future. For now, you'll have to settle on whatever I actually get uploaded to her.
-
UPDATE: The mermen and many other enemies as reflected in my code are assumed to use a custom sprite animation code. If you want to use the built-in functionality of frontends like Game Maker, you'll need to use multiple sprites. Any time a code sets the image_index and either sets image_speed to 0 or status |= STILL, you can include those frames of the animation in one combined sprite; but any time the code sets image_speed to some value other than 0 and status &= ~STILL, you'll need a separate sprite just for that animation. I'm aware that me using the built-in variables image_speed and image_index makes that so confusing, but I wasn't thinking about GM's mechanics at the time of writing all of this.
Merman (Ambush)
timeline_index = st_merjump
These are the ones closer to the original Castlevania Merman that just jump out of the water without warning, spit a couple times, then jump back down. This is not the Merman that appears in the flooded rooms of the sunken city.
EDIT: Fixed an error with timeline_position 12 and 23 - I was too hasty writing it originally and left out a large chunk of the code. There is no typo here - if the break points aren't encountered, cases 12 and 23 run on into case 9.
switch timeline_position
{
case 0:
swimmer = 0; //my own addition
var temp;
status |= STILL;
switch (!!global.block + global.hard_mode << 3) + (system.alarm[0] + x & $7) //not a typo
{
case 0:
case 2:
temp = $68;
break;
case 1:
case 11:
temp = $A0;
break;
case 3:
temp = $88;
break;
case 4:
temp = $98;
break;
case 5:
temp = $78;
break;
case 6:
temp = $90;
break;
case 7:
temp = $70;
break;
case 8:
temp = $58;
break;
case 9:
temp = $4C;
break;
case 10:
temp = $48;
break;
case 12:
temp = $5C;
break;
case 13:
temp = $60;
break;
case 14:
case 22:
temp = $C0;
break;
case 15:
temp = $50;
break;
case 16:
temp = $30;
break;
case 17:
temp = $D8;
break;
case 18:
case 20:
temp = $38;
break;
case 19:
temp = $40;
break;
case 21:
temp = $44;
break;
case 23:
temp = $2C;
break;
}
x = (Belmont.x - view_xview + temp & $FF) + view_xview;
timeline_position += 1;
break;
case 1:
case 13:
case 24:
en_xscale_set();
timeline_position += 1;
break;
case 2:
sprite_index = spr_Merman;
image_speed = 0;
image_index = 5; //looking up
status |= STILL;
timeline_position += 1;
break;
case 3:
case 33:
status |= MOVING;
timeline_position += 1;
break;
case 4:
timeline_position += 1;
play_sound(snd_20); //play splash sfx
break;
case 5:
timeline_position += 1;
vspd = -7;
alarm[2] = $2C;
break;
case 6:
vspd += alarm[2]/$100;
if vspd<0 exit;
if vspd>=3
if y-view_yview > $AF
{
if swimmer
{
timeline_position = $35;
spawn_splash(0,-$18);
}
else
timeline_position = 0;
exit;
}
if !tile_map_read($6,image_xscale) exit; //check for collision below
timeline_position += 1;
y &= ~$F;
vspd = 0;
break;
case 7:
image_index = 3; //looking down
status |= STILL;
timeline_position += 1;
break;
case 8:
alarm[2] = 6;
timeline_position += 1;
break;
case 12:
case 23:
if !tile_map_read($D,0) //($0,$10)
{
move_stop();
move_vert_set(1,0);
timeline_position = 37;
break;
}
else
if tile_map_read(5,image_xscale)
{
move_horz_rev(1);
break;
else
if tile_map_read(0,image_xscale)
{
move_horz_rev(1);
break;
}
case 9:
case 17:
case 20:
case 28:
case 31:
alarm[2] -= 1;
if !alarm[2]
timeline_position += 1;
break;
case 10:
case 21:
timeline_position += 1;
status &= ~STILL;
status |= MOVING;
image_index = 0; //2 frame cycle
image_speed = 1/$10;
en_xscale_set();
hspd = 7/16 * image_xscale;
break;
case 11:
case 22:
alarm[2] = $90;
timeline_position += 1;
break;
case 14:
case 25:
status &= ~MOVING;
status |= STILL;
timeline_position += 1;
break;
case 15:
case 26:
image_index = 2; //open mouth
timeline_position += 1;
break;
case 16:
case 19:
case 27:
case 30:
alarm[2] = $18;
timeline_position += 1;
break;
case 18:
case 29:
timeline_position += 1;
if status & (HIDDEN|OFFSCREEN) exit;
play_sound(snd_31);
spawn_fireball(3,-9); //create a fireball at (3,-9) relative to xscale
break;
case 32:
timeline_position += 1;
vspd = -1;
break;
case 34:
vspd += 1/8;
if swimmer
{
if y-view_yview > $BF-$10*(stage==8) //Sunken City had higher water
{
timeline_position += 1;
spawn_splash(0,-$18);
}
}
else
if y-view_yview > $F7
timeline_position += 1;
//junk code - controller destroys anything below $F8 anyway
break;
case 35:
timeline_position += 1;
play_sound(snd_21); //play sploosh sfx
break;
case 36:
instance_destroy();
break;
}
Merman (Swimming)
timeline_index = st_merswim
Similar to previous Merman, but swims in rather than just appearing suddenly.
switch timeline_position
{
case 0:
swimmer = 1; //my own addition
if tile_map_read($17,0) //make sure not spawning in ground
enemy_destroy();
timeline_position += 1;
break;
case 1:
alarm[2] = $10;
sprite_index = spr_FishShadow.gif;
image_speed = 1/12;
image_index = 6;
status &= ~STILL;
timeline_position += 1;
break;
case 2:
status |= MOVING;
en_xscale_set();
hspd = 3/2 * image_xscale;
timeline_position += 1;
break;
case 3:
timeline_position += 1;
alarm[2] = abs(x-Belmont.x)<$40;
break;
case 4:
if system.alarm[0] & 1 //originally ! but this is faster
status ^= FLICKER; //flicker effect
if abs(x-Belmont.x) < $40
if alarm[2]
{
if x < Belmont.x
timeline_position += 1;
}
else
if x >= Belmont.x
timeline_position += 1;
break;
case 5:
status &= ~FLICKER;
timeline_position += 1;
break;
case 6:
status &= ~MOVING; //a move_stop would have been better
timeline_position += 1;
break;
case 7:
case 20:
en_xscale_set();
timeline_position += 1;
break;
case 8:
sprite_index = spr_Merman;
image_index = 5;
status |= STILL;
timeline_position += 1;
break;
case 9:
status |= MOVING;
timeline_position += 1;
break;
case 10:
play_sound(snd_20); //play splash sfx
timeline_position += 1;
break;
case 11:
timeline_position += 1;
hspd = 0;
vspd = -6;
alarm[2] = $24;
break;
case 12:
spawn_splash(0,-$18);
timeline_index = st_merjump;
timeline_position = 6;
break;
}
Merman (Floating)
timeline_index = st_merfloat
The ones that you see in the aqueducts.
switch timeline_position
{
case 0:
if tile_map_read($17,0) //make sure not spawned in ground
instance_destroy();
else
timeline_position += 1;
break;
case 1:
sprite_index = spr_FishShadow;
image_index = 6;
image_speed = 1/12;
status &= !STILL;
timeline_position += 1;
break;
case 2:
timeline_position += 1;
en_xscale_set();
status |= MOVING;
hspd = 9/8 * image_xscale;
break;
case 3:
alarm[3] = $60;
timeline_position += 1;
vspd = -1/16;
alarm[2] = y;
break;
case 4:
alarm[3] -= 1;
if !alarm[3]
{
timeline_position += 1;
exit;
}
move_sinewave();
if !tile_map_read($A,image_xscale) //check if dropping down waterfall
{
move_stop();
hspd = 1 * image_xscale;
timeline_position = $16;
}
break;
case 5:
timeline_position += 1;
status &= ~MOVING;
break;
case 6:
en_xscale_set();
timeline_position += 1;
break;
case 7:
y -= 8; //because the shadow is at the top of the sprite
timeline_position += 1;
break;
case 8:
timeline_position += 1;
status |= STILL;
break;
case 9:
image_index = 0;
timeline_position += 1;
break;
case 10:
timeline_position += 1;
play_sound(snd_20); //play splash effect
break;
case 11:
timeline_position += 1;
spawn_splash(0,-$18);
break;
case 12:
case 18:
timeline_position += 1;
alarm[2] = $18;
break;
case 13:
case 16:
case 19:
alarm[2] -= 1;
if !alarm[2]
timeline_position += 1;
break;
case 14:
image_index = 2;
timeline_position += 1;
break;
case 15:
alarm[2] = $10;
timeline_position += 1;
break;
case 17:
if status & (HIDDEN|OFFSCREEN) exit;
sound_play(snd_31);
spawn_fireball(8,-9);
timeline_position += 1;
break;
case 20:
y += 8;
timeline_position += 1;
break;
case 21:
timeline_position = 1;
break;
case 22:
vspd += 5/64;
if tile_map_read($A,image_xscale)
timeline_position = 1;
break;
}
The Merman in the sunken city is essentially the same as the regular Merman, except for a few things:
- Spawn height and splash height (when the effect is created and sound is played) is based on the current height of the water.
- Only one fireball is spat.
- The tile map collision is garbage due to a bad typo and poor planning.
- The terrain never changes as the water rises, so where it appears there is water, it could still be solid ground and so the Merman can't spawn.
- Konami's collision code had basically 2 arguments - the horizontal offset and vertical offset from the instance's coordinates to check for a tile collision. Instead of passing the offsets, they passed the coordinates themselves. This had 3 ramifications:
- A merman could fail to spawn because the collision check was done on a solid nowhere near the merman.
- A merman could spawn inside a solid tile (graphical glitch) because the collision check was done on an empty tile nowhere near the merman.
- Mermen will cease spawning temporarily because the collision check will eventually fall outside the spawn zone (e.g., too low on the screen) once the flood level has reached a certain height.
UPDATE #2: Here's my transliteration of the flood-zone Merman.
Flood Merman
timeline_index = st_floodman
The variable global.floodzone holds the height of the water on the screen, so it's a value between $FF and $30 (counting down, obviously).
switch timeline_position
{
case 0:
timeline_position += 1;
var temp;
//Can probably do without abs() below, since Belmont should drown,
//but it is possible for the water to go above Belmont's origin.
temp = median(abs(global.floodzone - Belmont.y) - $28 >> 4,0,4);
switch temp
{
case 0: vspd = -15/4; break;
case 1: vspd = -17/4; break;
case 2: vspd = -9/2; break;
case 3: vspd = -19/4; break;
case 4: vspd = -11/2; break;
}
//check for ground collision
if tile_map_read( , ) //check for collision at (x+x,y+y) w/screen wrapping
enemy_destroy();
else
{
play_sound(snd_20);
status |= MOVING | STILL;
sprite_index = spr_Merman;
image_index = 5; //jumping sprite
en_xscale_set();
}
break;
case 1:
timeline_position += 1;
spawn_splash(0,-$18);
break;
case 2:
vspd += 1/8;
if vspd<0 exit;
if tile_map_read($6,image_xscale) //($4,$10) //check collision below front foot
{
timeline_position += 1;
y &= ~$F;
move_stop();
}
else
{
if y-view_yview + $10 < global.floodzone
{
spawn_splash(0,-$18);
enemy_destroy();
}
}
break;
case 3:
image_index = 3; //ducking
status = STILL;
timeline_position += 1;
break;
case 4:
alarm[2] = 6;
timeline_position += 1;
break;
case 5:
case 13:
case 16:
alarm[2] -= 1;
if !alarm[2]
timeline_position += 1;
break;
case 6:
status &= ~STILL;
status |= MOVING;
image_index = 0;
image_speed = 1/$10;
en_xscale_set();
hspd = 7/16 * image_xscale;
timeline_position += 1;
break;
case 7:
alarm[2] = $90;
timeline_position += 1;
break;
case 8:
if tile_map_read(5,image_xscale) //check collision below ahead
{
y &= ~$F;
if tile_map_read(0,image_xscale) //check collision at knees
{
hspd *= -1;
image_xscale *= -1;
}
else
{
alarm[2] -= 1;
if !alarm[2]
{
timeline_position += 1;
status &= ~MOVING;
}
}
}
else
{
hspd *= -1;
image_xscale *= -1;
}
break;
case 9:
timeline_position += 1;
en_xscale_set();
break;
case 10:
status |= STILL;
timeline_position += 1;
break;
case 11:
image_index = 2;
timeline_position += 1;
break;
case 12:
case 15:
alarm[2] = $18;
timeline_position += 1;
break;
case 14:
timeline_position += 1;
if status & (HIDDEN | OFFSCREEN) break;
play_sound(snd_31);
spawn_fireball(8,-9);
break;
case 17:
timeline_position += 1;
vspd = -1;
break;
case 18:
timeline_position += 1;
status |= MOVING;
break;
//Note: This makes the Merman jump with mouth still open
case 19:
vspd += 1/8;
if y - view_yview + $10 < global.floodzone break;
timeline_position += 1;
spawn_splash(0,-$18);
break;
case 20:
timeline_position += 1;
play_sound(snd_21);
break;
case 21:
instance_destroy();
break;
}
Mushroom
timeline_index = st_fungus
switch timeline_position
{
case 0:
sprite_index = spr_Fungus;
image_speed = 1/$18;
image_index = 0;
status &= ~STILL;
break;
case 1:
if abs(x-Belmont.x) < $20
if abs(y-Belmont.y) < $20
exit;
timeline_position -= 1;
break;
case 2:
image_index = 3;
image_speed = 1/12;
timeline_position += 1;
break;
case 3:
alarm[2] = $2E;
timeline_position += 1;
break;
case 4:
alarm[2] -= 1;
if !alarm[2]
timeline_position += 1;
break;
case 5:
var i;
for (i=4; i>0; i-=1)
{
with spawn_new(4,x,y,obj_proj_Spore)
{
status = (other.status | MOVING);
damage_set(1,1); //Set the damage of the spore to low
switch i
{
case 4:
hspd = 5/16;
vspd = -1/4;
break;
case 3:
hspd = 5/32;
vspd = -1/2;
break;
case 2:
hspd = -5/32;
vspd = -1/2;
break;
case 1:
hspd = -5/16;
vspd = -1/4;
break;
}
}
}
timeline_position += 1;
break;
case 6:
instance_destroy();
break;
}
Floating Spore
timeline_index = st_fltspore
switch timeline_position
{
case 0:
sprite_index = spr_FloatSpore;
image_index = 0;
image_speed = 1/$18;
status &= !STILL;
status |= MOVING;
en_xscale_set();
hspd =3/8 * image_xscale;
timeline_position += 1;
break;
case 1:
alarm[03] = $FF;
vspd = -3/4;
alarm[02] = y - 1;
timeline_position += 1;
break;
case 2:
alarm[03] -= 1;
if !alarm[03]
timeline_position += 1;
move_sinewave();
break;
case 3:
en_xscale_set();
if status & OFFSCREEN
xscale *= -1;
hspd = (2 + global.hard_mode)/4 * image_xscale);
vspd = 1/4 * sign(Belmont.y - y | 1);
timeline_position += 1;
break;
case 4:
alarm[02] = $20;
timeline_position += 1;
break;
case 5:
alarm[2] -= 1;
if !alarm[2]
timeline_position += 1;
break;
case 6:
timeline_position = 0;
break;
}
Spore Pollen
timeline_index = st_spore
These are the baby spores that fungi release when they explode.
switch timeline_position
{
case 0:
sprite_index = spr_Sporeling;
image_speed = 1/12;
image_index = 0;
status &= ~STILL;
status |= MOVING;
timeline_position += 1;
break;
case 1:
alarm[2] = $28;
timeline_position += 1;
break;
case 2:
alarm[2] -= 1;
if !alarm[2]
timeline_position += 1;
break;
case 3:
hspd = irandom($FF)/$100 * sign(hspd);
vspd += irandom(7)/$100;
if tile_map_read($14,0) //check for collision with ground
instance_destroy();
else
instance_destroy();
break;
}
Owl
timeline_index = st_owl
switch timeline_position
{
case 0:
status &= ~MOVING;
status |= STILL;
timeline_position += 1;
break;
case 1:
status |= PASSTHRU;
timeline_position += 1;
break;
case 2:
sprite_index = spr_Owl;
image_speed = 0;
image_index = 2;
break;
case 3:
alarm[02] = $0F;
alarm[03] = $00;
switch irandom(7)
{
case 0: alarm[04] = $73; break;
case 1:
case 2: alarm[04] = $48; break;
case 3: alarm[04] = $65; break;
case 4: alarm[04] = $63; break;
case 5: alarm[04] = $5C; break;
case 6: alarm[04] = $8F; break;
case 7: alarm[04] = $54; break;
}
break;
case 4:
if status & OFFSCREEN
timeline_position += 1;
alarm[02] -= 1;
if !alarm[02]
{
alarm[03] ^= 1;
if alarm[03]
alarm[02] = $07;
else
alarm[02] = $28;
status ^= HIDDEN;
}
if alarm[04]
alarm[04] -= 1;
if !alarm[04]
{
status &= ~HIDDEN;
timeline_position += 1;
}
break;
case 5:
case 8:
case 23:
image_index += 1;
timeline_position += 1;
break;
case 6:
alarm[02] = 3;
timeline_position += 1;
break;
case 7:
case 10:
case 14:
case 18:
case 21:
case 30:
alarm[02] -= 1;
if !alarm[02]
timeline_position += 1;
break;
case 9:
case 13:
alarm[2] = $10;
timeline_position += 1;
break;
case 11:
status &= ~PASSTHRU;
timeline_position += 1;
break;
case 12:
image_index = 5;
timeline_position += 1;
break;
case 15:
vspd = -1;
timeline_position += 1;
case 16:
case 22:
case 32:
status |= MOVING;
timeline_position += 1;
break;
case 17:
alarm[2] = 6;
timeline_position += 1;
break;
case 19:
case 27:
status &= ~MOVING;
timeline_position += 1;
break;
case 20:
alarm[2] = 4;
timeline_position += 1;
break;
case 24:
case 33:
en_xscale_set();
if status & OFFSCREEN
image_xscale *= -1;
switch abs(x-obj_Belmont.x)>>4
{
case 0: hspd = 3/4 * image_xscale; break;
case 1: hspd = 3/2 * image_xscale; break;
case 2: hspd = 7/4 * image_xscale; break;
case 3: hspd = 2 * image_xscale; break;
case 4: hspd = 5/2 * image_xscale; break;
case 5: hspd = 11/4 * image_xscale; break;
case 6: hspd = 3 * image_xscale; break;
case 7: hspd = 7/2 * image_xscale; break;
case 8: hspd = 15/4 * image_xscale; break;
case 9: hspd = 9/2 * image_xscale; break;
}
switch abs(y-Belmont.y)>>4
{
case 0: vspd = 3/2; break;
case 1: vspd = 7/4; break;
case 2: vspd = 2; break;
case 3: vspd = 5/2; break;
case 4: vspd = 11/4; break;
case 5: vspd = 3; break;
case 6: vspd = 7/2; break;
case 7: vspd = 15/4; break;
case 8: vspd = 4; break;
case 9: vspd = 9/2; break;
}
timeline_position += 1;
break;
case 25:
case 34:
//If you consolidate the code, both Owl scripts go together.
if !(vspd<0)
{
vspd += -1/16;
}
else
{
sound_play(snd_26);
image_index = 0;
image_speed = 1/8;
image_index = 0;
status &= ~STILL;
alarm[02] = $30;
timeline_position += 1;
}
break;
case 26:
alarm[2] -= 1;
if !alarm[2]
timeline_position += 1;
else
vspd += -1/16;
break;
case 28:
en_xscale_set();
timeline_position += 1;
break;
case 29:
alarm[2] = $20;
timeline_position += 1;
break;
case 31:
image_index = 6;
status |= STILL;
timeline_position += 1;
break;
case 35:
vspd += -1/16;
if y < view_yview + $28
instance_destroy();
else
if y > view_yview+$E7
instance_destroy();
break;
}
More code to come at some point. Tendinitis is making it hard to work on my PC.
-
DISCLAIMER #2 (or is this #3?)
I just realized since a lot of these codes were written up before I started transitioning to Studio, I use the command exit at times. If you are using Studio, replace all exits in my code with return 0.
In Studio, the exit command is used to abort the current event, whereas return 0 will only abort the current script, which was what was intended originally.
-
just a quick update. Working on the bone dragon code. Decided to actually test it and realized it had some glaring errors. So now I am going back through the game code to make sure I fix those errors. So the next code treat you will all get is the Bone Dragon code from Castlevania 3.
-
Bone Dragon Head
timeline_position = st_bonedrach
Okay, this guy requires me to cover a couple details. First, this is the code from my tests. You may notice it's not quite like my other codes - I initialized the alarms, for one thing. This code uses a lot of "alarm" variables. If you can come up with some better names, go for it. I actually created a variable name for one of them - dir. This functions like you'd expect - the dragon moves irrespective of the xscale. The other variables, I didn't really know what to call them. I still use alarm[2] for a timer and alarm[3] is also a timer (of sorts). In addition, alarm[4] functions kinda as cycle counter - so like a timer. The other three alarms (9,10,and 11) are additional cycle counters. I thought about making a new array (something like cycle[n]) but decided it would be easier to just stick with the alarm array with little notes to myself. You don't need to worry about the notes, because they were only pertinent to the original RAM addresses.
Also, unrelated to my transliteration, there is a glaring issue with the original Bone Dragon code. The range of motion is about 50% greater when facing right than facing left. What this means is the bone dragon will spit fire occasionally (rarely) above its tail height when facing right. If it is facing left, it will never move up above its tail, so it will always spit fire at a height lower than its tail. What this means for level design is when the bone dragon will face left, you should place it above Belmont, and when it's facing right it should be placed level to Belmont's head or hip.
In the code, you'll see alarm[4] & $F0 | 2 and alarm[4] & $0F | $80. In both these situations, the original code was suspect because the 2 and $80 were indirect variables, not constants. The buffered values were respectively $02 $02 $02 $02 $02 $02 $02 $02 and $80 $80 $80 $80 $80 $80 $80 $80. Pretty suspect, yeah?
switch timeline_position
{
case 0:
sprite_index = spr_DracHead;
status |= STILL;
image_index = 0;
image_xscale = 1; //You'd set this when it's created (doesn't face Belmont)
dir = image_xscale;
//In GM8, alarms start at -1, so I need to remember to clear them.
alarm[2] = 0;
alarm[3] = 0;
alarm[4] = 0;
alarm[9] = 0;
alarm[10] = 0;
alarm[11] = 0;
timeline_position += 1;
break;
case 1:
if status & OFFSCREEN break;
alarm[3] = 2;
status |= MOVING;
hspd = 1 * dir;
alarm[10] = $30;
alarm[2] = 9;
timeline_position += 1;
break;
case 2:
alarm[2] -= 1;
if alarm[2] break;
if alarm[3] != 8
{
global.instance[global.index] = instance_create(x-8*image_xscale,y,obj_DracRib);
with global.instance[global.index]
{
image_xscale = other.image_xscale;
status = other.status;
damage = $10;
hp = $20;
}
global.index += 1;
alarm[3] += 1;
alarm[2] = 9;
}
else
{
hspd = 0;
status &= ~MOVING;
var i,n;
n = $10*(dir != 1) + 8;
for(i=1;i<9;i+=1)
with global.instance[i]
{
alarm[9] = 0;
alarm[2] = n;
alarm[3] = n;
alarm[4] = $82;
timeline_position = 2;
}
timeline_position += 1;
}
break;
case 3:
var i,k;
for(i=1;i<9;i+=1;)
with global.instance[i]
{
k = alarm[4] & $F;
k -= 1;
if k
alarm[4] = (alarm[4] & $F0) | k;
else
{
alarm[4] = (alarm[4] & $F0) | 2;
k = alarm[3] - alarm[2] & $FF;
if k
if k & $10
alarm[2] = alarm[2] - 1 & $1F;
else
alarm[2] = alarm[2] + 1 & $1F;
}
k = alarm[4] & $F0;
k -= $10;
if k
alarm[4] = alarm[4] & $0F | k;
else
{
alarm[4] = alarm[4] & $0F | $80;
if i!=8
alarm[3] = global.instance[i+1].alarm[2];
else
{
switch alarm[9]+$1F*(!dir)
{
case 30:
case 61: alarm[9] = 0;
case 0:
case 5:
case 6:
case 7:
case 8:
case 12:
case 18:
case 19:
case 26:
case 27:
case 28: alarm[3] = $08; break;
case 1:
case 2:
case 15:
case 20:
case 21:
case 22:
case 23: alarm[3] = $0E; break;
case 3:
case 4:
case 16:
case 17:
case 25: alarm[3] = $0A; break;
case 9: alarm[3] = $06; break;
case 10:
case 11:
case 29: alarm[3] = $04; break;
case 13:
case 14:
case 24: alarm[3] = $0C; break;
case 31:
case 36:
case 37:
case 38:
case 39:
case 43:
case 49:
case 50:
case 57:
case 58:
case 59: alarm[3] = $18; break;
case 32:
case 33:
case 46:
case 51:
case 52:
case 53:
case 54: alarm[3] = $12; break;
case 34:
case 35:
case 47:
case 48:
case 56: alarm[3] = $16; break;
case 40: alarm[3] = $1A; break;
case 41:
case 42:
case 44:
case 45:
case 55:
case 60: alarm[3] = $14; break;
}
alarm[9] += 1;
}
}
}
for(i=1;i<7;i+=1)
with global.instance[i]
{
switch alarm[2]
{
case 0:
case 16: k = 0; break;
case 1:
case 15: k = 2; break;
case 2:
case 14: k = 3; break;
case 3:
case 13: k = 5; break;
case 4:
case 12: k = 6; break;
case 5:
case 11: k = 7; break;
case 6:
case 10: k = 8; break;
case 7:
case 8:
case 9: k = 9; break;
case 17:
case 31: k = -2; break;
case 18:
case 30: k = -3; break;
case 19:
case 29: k = -5; break;
case 20:
case 28: k = -6; break;
case 21:
case 27: k = -7; break;
case 22:
case 26: k = -8; break;
case 23:
case 24:
case 25: k = -9; break;
}
global.instance[i+1].x = x + k;
switch alarm[2]
{
case 8:
case 24: k = 0; break;
case 9:
case 23: k = 2; break;
case 10:
case 22: k = 3; break;
case 11:
case 21: k = 5; break;
case 12:
case 20: k = 6; break;
case 13:
case 19: k = 7; break;
case 14:
case 18: k = 8; break;
case 15:
case 16:
case 17: k = 9; break;
case 25:
case 7: k = -2; break;
case 26:
case 6: k = -3; break;
case 27:
case 5: k = -5; break;
case 28:
case 4: k = -6; break;
case 29:
case 3: k = -7; break;
case 2: k = -8; break;
case 0:
case 1:
case 30:
case 31: k = -9; break;
}
global.instance[i+1].y = y + k;
if alarm[2] && alarm[2]<17
global.instance[i+1].image_xscale = -1;
else
global.instance[i+1].image_xscale = 1;
switch alarm[2]
{
case 0:
case 1: image_index = 2; break;
case 2:
case 3:
case 4:
case 5:
case 6: image_index = 3; break;
case 7:
case 8:
case 9:
case 10:
case 23:
case 24:
case 25:
case 26: image_index = 4; break;
case 11:
case 12:
case 13:
case 14:
case 18:
case 19:
case 20:
case 21:
case 22: image_index = 5; break;
case 15:
case 16: //image_index 2 mirrored
case 17: image_index = 6; break;
}
}
x = global.instance[7].x + 15*dir;
y = global.instance[7].y;
if alarm[10]
alarm[10] -= 1;
else
{
if status & (HIDDEN | OFFSCREEN)
{
image_index = 0;
alarm[11] = 0;
alarm[10] = $80;
}
else
if alarm[11]
{
with instance_create(x+8,y+4,obj_Fireball)
{
image_xscale = other.image_xscale;
hspd = 2 * image_xscale;
status = MOVING | STILL;
}
alarm[11] = 0;
alarm[10] = $80;
}
else
{
image_index = 1;
alarm[10] = $10;
alarm[11] += 1;
}
}
for(i=1;i<9;i+=1;)
if !global.instance[i]
{
(global.instance[1].mySpawn).alarm[1] = 2; //tell the spawner it died
for(i=8;i;i-=1)
with global.instance[i]
instance_destroy();
return 0;
}
break;
}
Bone Dragon Rib
timeline_index = st_bonedract
switch timeline_position
{
case 0:
sprite_index = spr_DracRib;
status |= STILL;
image_index = 2;
timeline_position += 1;
break;
case 1: //This is blank because, well, nothing happens.
break;
case 2: //Same code as in the head
var i;
for(i=1;i<9;i+=1)
if !global.instance[i]
{
(global.instance[1].mySpawn).alarm[1] = 2;
for(i=8;i;i-=1;)
with global.instance[i]
instance_destroy();
exit;
}
break;
}
-
Flying Eye
timeline_index = st_flyeye
switch timeline_position
{
case 0:
sprite_index = spr_FlyingEye;
image_speed = 1/$A;
image_index = 0;
status &= ~STILL; //junk line
status |= MOVING;
en_xscale_set();
hspd = 69/64;
timeline_position += 1;
break;
case 1:
alarm[2] = $20;
timeline_position += 1;
break;
case 2:
case 6:
case 9:
case 12:
alarm[2] -= 1;
if !alarm[2]
timeline_position += 1;
break;
case 3:
status |= STILL;
status &= ~MOVING;
timeline_position += 1;
break;
case 4:
case 7:
image_index += 1;
timeline_position += 1;
break;
case 5:
case 8:
alarm[2] = $10;
timeline_position += 1;
break;
case 10:
image_index -= 1;
timeline_position += 1;
break;
case 11:
alarm[2] = $4;
timeline_position += 1;
break;
case 13:
timeline_position += 1;
if status & (HIDDEN|OFFSCREEN) break;
sound_play(snd_31);
spawn_teardrop(4*image_xscale,4); //spawn a tear at (x+4,y+4)
break;
case 14:
timeline_position = 0;
break;
}
Teardrop
timeline_index = st_teardrop
switch timeline_position
{
case 0:
sprite_index = spr_Teardrop;
vspd = 3/2;
status |= STILL | MOVING;
image_speed = 0;
image_index = 0;
timeline_position += 1;
break;
case 1:
//No gravity, if I remember right
if tile_map_read($14,0) //check for collision below
instance_destroy();
else
projectile_destroy(); //just remembered, this is just a destroy-if-offscreen script
break;
}
Axe Knight
timeline_index = st_axeknight
switch timeline_position
{
case 0:
var i,k;
en_xscale_set();
status |= MOVING;
sprite_index = spr_AxeKnight;
image_speed = 1/14;
image_index = 0;
//This gets called at two different phases in the Axe Knight timeline
if (alarm[04] & $30)
{
if !(alarm[04] & $80)
{
i = (alarm[04] & $30) - $10;
alarm[04] = alarm[04] & $CF | i;
}
k = 1;
}
else
{
if !(status & (HIDDEN | OFFSCREEN))
for (i=0; i<$D; i+=1)
if !global.instance[i] //find empty spot in instances array to spawn axe
{
status |= STILL;
status &= ~MOVING;
if irandom(1)
sprite_index = spr_AxeKnight_dkat;
else
sprite_index = spr_AxeKnight_stat;
alarm[04] |= $10;
timeline_position += 1;
}
k = 1;
}
if k == 1
{
alarm[03] = $21;
en_xscale_set();
if abs(x-obj_Belmont.x) < $60
hspd = -3/2 * image_xscale;
else
hspd = 3/2* image_xscale;
if status & OFFSCREEN
hspd *= -1;
}
break;
case 1:
var i;
i = instance[alarm[04] & $0F];
if alarm[04] & $80
{
if !i
alarm[04] &= $7F;
else
if abs(x-i.x) < $04
{
alarm[04] &= $7F;
with i
instance_destroy();
}
}
switch tile_map_read(5,2) //check collision below
{
case 0:
case 4:
hspd *= -1;
break;
default:
if tile_map_read(0,2) //check collision in front of knees
hspd *= -1;
else
if tile_map_read(3,2) //check collision in front of face
hspd *= -1;
else
{
alarm[03] -= 1;
var k;
if !alarm[03]
//This gets called at two different phases in the Axe Knight timeline
if (alarm[04] & $30)
{
if !(alarm[04] & $80)
{
i = (alarm[04] & $30) - $10;
alarm[04] = alarm[04] & $CF | i;
}
k = 1;
}
else
{
if !(status & (HIDDEN | OFFSCREEN))
for (i=0; i<$D; i+=1)
if !spawn_data[i]
{
status |= STILL;
status &= ~MOVING;
if irandom(1)
image_index = 4;
else
image_index = 2;
alarm[04] |= $10;
timeline_position += 1;
}
k = 1;
}
if k == 1
{
alarm[03] = $21;
en_xscale_set();
if status & OFFSCREEN
image_xscale *= -1;
if abs(x-obj_Belmont.x) < $60
hspd = -3/8;
else
hspd = 3/8 * image_xscale;
if status & OFFSCREEN
hspd *= -1;
}
}
break;
}
break;
case 2:
alarm[02] = $0A;
timeline_position += 1;
break;
case 3:
case 6:
alarm[02] -= 1;
if !alarm[02]
timeline_position += 1;
break;
case 4:
image_index += 1;
timeline_position += 1;
break;
case 5:
alarm[2] = $08;
timeline_position += 1;
break;
case 7:
var i,k;
if image_index & 4
i = -10;
else
i = 9;
//spawn axe at (x+8,y+i) and save its instance array index
k = (spawn_axe(8,i)).myIndex | $80;
alarm[04] = alarm[04] & $70 | k;
status &= ~STILL;
timeline_position = 0;
break;
}
Axe (for axe knight)
timeline_position = st_knightaxe
switch timeline_position
{
case 0:
sprite_index = spr_Axe;
status |= MOVING;
image_speed = 1/4;
image_index = 0;
en_xscale_set;
hspd = 3/2 * image_xscale;
timeline_position += 1;
break;
case 1:
alarm[2] = $38;
timeline_position += 1;
break;
case 2:
alarm[2] -= 1;
if !alarm[2]
timeline_position += 1;
break;
case 3:
hspd *= -1;
timeline_position += 1;
break;
case 4:
timeline_index = -1;
break;
}
-
Mummy
timeline_index = st_mummy
switch timeline_position
{
case 0:
sprite_index = spr_Wrap;
image_speed = 1/$C;
status &= ~STILL;
timeline_position += 1;
break;
case 1:
alarm[2] = $3A;
timeline_position += 1;
break;
case 2:
case 10:
alarm[2] -= 1;
if !alarm[2]
timeline_position += 1;
break;
case 3:
sprite_index = st_Mummy;
image_speed = 1/$C;
image_index = 0;
status |= MOVING;
en_xscale_set();
hspd = 7/16 * image_xscale;
timeline_position += 1;
break;
case 4:
y &= ~15;
var i;
if !tile_map_read($05,1) //check for no ground ahead
i = 1;
if tile_map_read($00,1) //check for collision in front of knees
i = 1;
if i
{
hspd *= -1;
image_xscale *= -1;
}
else
{
alarm[02] -= 1;
if !alarm[02]
{
status &= ~MOVING;
timeline_position += 1;
}
}
break;
case 5:
en_xscale_set();
timeline_position += 1;
break;
case 6:
status |= STILL;
timeline_position += 1;
break;
case 7:
hspd = 0;
timeline_position += 1;
//junk state if you think about it
break;
case 8:
if !(status & (HIDDEN|OFFSCREEN))
{
sound_play(snd_31);
spawn_wrap(8,-4);
}
timeline_position += 1;
break;
case 9:
alarm[2] = $18;
timeline_position += 1;
break;
case 11:
timeline_position = 3;
break;
}
Bandage
timeline_index = st_wrap
switch timeline_position
{
case 0:
sprite_index = spr_Bandage;
status |= MOVING;
image_speed = 1/$C;
image_index = 0;
timeline_position += 1;
break;
case 1:
vspd = -1;
ystart = y;
timeline_position += 1;
break;
case 2:
move_sinewave();
break;
}
Harpy
timeline_index = st_harpy
//Compressing this code will make it unnoticably faster
switch timeline_position
{
case 0:
status &= ~MOVING;
status |= STILL;
timeline_position += 1;
//junk state
break;
case 1:
status |= MOVING;
en_xscale_set();
move_stop();
move_horz_set($0210/$100,1);
timeline_position += 1;
break;
case 2:
sprite_index = spr_Harpy;
image_index = 0;
image_speed = 1/$C;
timeline_position += 1;
break;
case 3:
if abs(x-obj_Belmont.x) < $40
timeline_position += 1;
break;
case 4:
with spawn_new(2,x+0,y+$10,obj_Fleaman)
{
image_xscale = other.image_xscale;
status = other.status|(STILL|MOVING);
damage_set($20);
timeline_index = st_fleabomb;
with other
{
image_index = 0
status &= ~MOVING;
status |= STILL;
alarm[02] = $10;
timeline_position += 1;
}
}
break;
case 5:
case 8:
alarm[2] -= 1;
if !alarm[2]
timeline_position += 1;
break;
case 6:
image_index = 2;
timeline_position += 1;
break;
case 7:
alarm[2] = $10;
timeline_position += 1;
break;
case 9:
status |= MOVING;
timeline_position += 1;
break;
case 10:
status &= ~STILL;
timeline_position += 1;
break;
case 11:
timeline_index = -1; //tells controller to ignore this unit
break;
}
Fleaman Bomb
timeline_index = st_fleabomb
switch timeline_position
{
case 0:
status |= STILL;
timeline_position += 1;
break;
case 1:
alarm[2] = $10;
timeline_position += 1;
break;
case 2:
alarm[2] -= 1;
if !alarm[2]
timeline_position += 1;
break;
case 3:
sprite_index = spr_Fleaman;
image_index = 0;
image_speed = 0;
status |= STILL;
timeline_position += 1;
break;
case 4:
status |= MOVING;
timeline_position += 1;
break;
case 5:
vspd += 5/64;
if tile_map_read($E,1) //check for collision below
{
y &= ~15;
vspd = 0;
hspd = 0;
alarm[03] = 0;
timeline_index = st_fleaman;
timeline_position += 1;
}
break;
}
Spider
timeline_index = st_spider
Comprised of three parts - the spider itself, its webbing, and the babies.
switch timeline_position
{
case 0:
status |= PASSTHRU;
WEB = (spawn_thread(0,0)).id;
sprite_index = spr_Spider
status &= ~(STILL|FLICKER);
image_speed = 1/$C;
image_index = 0;
timeline_position += 1;
break;
case 1:
with WEB
{
timeline_position = 0;
alarm[2] = other.id;
}
case 10:
move_stop();
status |= MOVING;
timeline_position += 1;
vspd = 9/8;
if (timeline_position==10)
vspd = -(vspd + 1/8);
alarm[2] = $50; //always felt this should be randomized
break;
case 2:
status &= ~PASSTHRU; //2-frame invincibility seems pointless...
timeline_position += 1;
break;
case 3:
case 11:
if y-view_yview < $30
{
status |= (PASSTHRU|HIDDEN|FLICKER);
timeline_position += 1;
y = $30+view_yview; //base of status bar
vspd = 0;
with WEB
instance_destroy();
exit;
}
case 6:
case 9:
case 15:
alarm[2] -= 1;
if !alarm[2]
timeline_position += 1;
break;
case 4:
vspd = 0; //originally in 3 but moved here
status &= ~MOVING;
timeline_position += 1;
break;
case 5:
alarm[2] = $10;
timeline_position += 1;
break;
case 7:
timeline_position += 1;
if abs(x-view_xview-$80) >= $78 exit;
if status & (HIDDEN|OFFSCREEN) break;
spawn_spiderling(0,0);
play_sound(snd_31) //play flutter sound
break;
case 8:
alarm[2] = $10;
timeline_position += 1;
break;
case 12:
timeline_position += 1;
var temp;
switch irandom(7)
{
case 0:
temp = $40;
break;
case 1:
temp = $20;
break;
case 2:
temp = $E0;
break;
case 3:
temp = $D0;
break;
case 4:
temp = $10;
break;
case 5:
temp = $F0;
break;
case 6:
temp = $50;
break;
case 7:
temp = $70;
break;
}
x = (Belmont.x - view_xview + temp & $FF) + view_xview;
break;
case 13:
timeline_position += 1;
en_xscale_set(); //Why? It's a mirrored sprite and baby was already made!
break;
case 14:
switch irandom(3)
{
case 0:
alarm[2] = $F8;
break;
case 1:
alarm[2] = $B0;
break;
case 2:
alarm[2] = $A0;
break;
case 3:
alarm[2] = $C8;
break;
}
timeline_position += 1;
break;
case 16:
timeline_position = 0;
break;
}
Spider Web
timeline_index = st_thread
Who knew a spider web had so much code!
switch timeline_position
{
case 0:
timeline_position += 1;
sprite_index = spr_Thread;
case 1:
image_index = SPIDER.y-$30>>4;
status = SPIDER.status | (STILL | PASSTHRU) & ~(MOVING);
break;
}
Spiderling
timeline_index = st_spiderling
Okay, this one's a doozy. All the data is stored in an external binary file. If you can buffer this in GM, that should speed up the code quite a bit, since file reading is kinda poor in GM. I'll include the file data below.
switch timeline_position
{
case 0:
timeline_position += 1;
sprite_index = spr_Spiderling;
image_speed = 1/$18;
break;
case 1:
timeline_position += 1;
var temp; var f;
temp[2] = abs(Belmont.y-y) & $F0;
temp[1] = abs(Belmont.x-x)>>4;
temp[0] = Belmont.x<x;
temp[0] |= (Belmont.y<y)<<1;
f = file_bin_open("babyspdr0.bin",0);
file_bin_seek(f,temp[1]+temp[2]);
temp[1] = file_bin_read_byte(f);
switch temp[0]
{
case 3:
temp[0] = $18;
case 0:
temp[1] = $08 - temp[1];
break;
case 1:
temp[0] = $10;
case 2:
temp[0] = $00;
break;
}
temp[0] += temp[1];
file_bin_close(f);
temp[3] = abs(abs($18-temp[0])-$10)+$12<<1;
f = file_bin_open("babyspdr1.bin",0);
file_bin_seek(f,temp[3]);
temp[4] = file_bin_read_byte(f)<<8;
if temp[4] & $8000
temp[4] = $10000-temp[4];
temp[4] |= file_bin_read_byte(f);
file_bin_seek(f,$36+temp[3]);
temp[5] = file_bin_read_byte(f)<<8;
if temp[5] & $8000
temp[5] = $10000-temp[5];
temp[5] |= file_bin_read_byte(f);
file_bin_close(f);
move_vert_set(temp[4]/$100,0);
move_horz_set(temp[5]/$100,0);
if temp[0] & $10
hspd *= -1;
if temp[0]+$08 & $10
vspd *= -1;
break;
case 2:
if x-view_xview < $4
instance_destroy();
else
if x-view_xview > $F7
instance_destroy();
else
if y-view_yview < $18
instance_destroy();
else
if y-view_yview > $E7
instance-destroy();
break;
}
Spiderling Data 0
filename = babyspdr.bin
04 08 08 08 08 08 08 08 08 08 08 08 08 08 08 08 00 04 06 06 07 07 07 07 07 07 07 08 08 08 08 08 00 02 04 05 06 06 06 07 07 07 07 07 07 07 07 07 00 02 03 04 05 05 06 06 06 06 07 07 07 07 07 07 00 01 02 03 04 05 05 05 06 06 06 06 06 06 07 07 00 01 02 03 03 04 04 05 05 05 06 06 06 06 06 06 00 01 02 02 03 04 04 04 05 05 05 05 06 06 06 06 00 01 01 02 03 03 04 04 04 05 05 05 05 05 06 06 00 01 01 02 02 03 03 04 04 04 05 05 05 05 05 06 00 01 01 02 02 03 03 03 04 04 04 05 05 05 05 05 00 01 01 01 02 02 03 03 03 04 04 04 04 05 05 05 00 00 01 01 02 02 03 03 03 03 04 04 04 04 05 05 00 00 01 01 02 02 02 03 03 03 04 04 04 04 04 05 00 00 01 01 02 02 02 03 03 03 03 04 04 04 04 04 00 00 01 01 01 02 02 02 03 03 03 03 04 04 04 04 00 00 01 01 01 02 02 02 02 03 03 03 03 04 04 04
Spiderling Data 1
filename = babyspdr1.bin
FF 00 FF 06 FF 14 FF 2C FF 4C FF 72 FF 9E FF CF 00 00 FE 80 FE 88 FE 9E FE C1 FE F1 FF 2B FF 6B FF B6 00 00 FE 00 FE 0B FE 27 FE 57 FE 97 FE E4 FE 3C FE 9D 00 00 00 00 00 31 00 62 00 8E 00 B4 00 D4 00 EC 00 FA 01 00 00 00 00 4A 00 93 00 D5 01 0F 01 3F 01 62 01 78 01 80 00 00 00 63 00 C4 01 1C 01 69 01 A9 01 D9 01 F5 02
So unless I'm mistaken, that should be it for the CV3 minor enemies. Next time, I'll post code from CV1 for all of the enemies, including the bosses.
-
Thisinfo has been so useful, dude! I appreciate the work you've put in. I look forward to the NES enemies, too.
-
NOTICE: The first enemy is the big bat boss from the first part of the game. This isn't just because it was the first boss, but also because its scripts (there are 4) are referenced by other bosses as well.
Vampire Bat
timeline_index = st_cvbat
switch timeline_position
{
case 0:
status |= STILL;
status &= ~MOVING;
if !(system.status & $F0) //if boss fight
exit;
alarm[2] = $78;
timeline_position += 1;
break;
case 1:
alarm[2] -= 1;
if !alarm[2]
{
sc_cvbat0();
sprite_index = spr_VampireBat;
}
break;
case 2:
if abs(Belmont.y - y) < 4
{
switch irandom(3)
{
case 0: alarm[2] = $7B; break;
case 1: alarm[2] = $20; break;
case 2: alarm[2] = $3D; break;
case 3: alarm[2] = $C7; break;
}
status &= ~MOVING;
timeline_position += 1;
}
break;
case 3:
alarm[2] -= 1;
if alarm[2]
{
if !irandom(15)
{
if abs(Belmont.y-y) < $14
{
var temp; temp = abs(Belmont.x-x);
if temp < $58
if sign(temp) == image_xscale
{
vspd = -5/2;
hspd = 0;
alarm[2] = 8;
timeline_position = 4;
}
}
}
}
else
{
var temp; temp = global.instance[0].y - y;
if temp < 0
{
with spawn_fireball(0,0) //C to D
sc_cvbat2();
alarm[2] = $18;
timeline_position = 6;
}
else
{
var temp; temp = sc_cvbat1(0,$18);
sc_cvbat3(temp);
timeline_position = 5;
}
}
break;
case 4:
alarm[2] -= 1;
if !alarm[2]
{
if global.instance[0].y < y
{
with spawn_fireball(0,0)
sc_cvbat2();
alarm[2] = $18;
timeline_position = 6;
}
else
{
var temp; temp = sc_cvbat1(0,$18);
sc_cvbat3(temp);
timeline_position = 5;
}
}
break;
case 5:
vspd += 3/128;
if vspd < 0 && y-view_yview < $70
sc_cvbat0();
else
{
if x < $10
{
x = $10;
hspd *= -1;
image_xscale *= -1;
}
else
if x > $EF
{
x = $F0;
hspd *= -1;
image_xscale *= -1;
}
}
case 6:
alarm[2] -= 1;
if !alarm[2]
sc_cvbat0();
}
Bat Subscript 0
script_id = sc_cvbat0
var temp; temp[1] = irandom(1);
temp[0] = $30;
if temp[1]
temp[0] = -temp[0];
temp[1] = temp[0] + Belmont.x - view_xview;
if temp[1] < $10 || temp[1] > $EF
temp[0] = -temp[0];
temp[1] = sc_cvbat1(temp[0],$28);
temp[0] = temp[1] & 1;
temp[1] = temp[1] >> 1;
temp[1] -= temp[1]>>2;
temp[1] /= $100;
if temp[0]
{
hspd = temp[1] * image_xscale;
vspd = 3/4;
}
else
{
vspd = temp[1];
hspd = 3/4 *image_xscale;
}
if alarm[5]
vspd *= -1;
timeline_position = 2;
Bat Subscript 1
script_id = sc_cvbat1
The value returned by this code may be ±1 the intended value due to rounding in GM. However, considering what this code is used for, it shouldn't be a noticeable difference.
en_xscale_set();
var temp;
temp[3] = Belmont.y + argument1 - y;
temp[1] = max(abs(temp[3]),1);
alarm[5] = !temp[1];
temp[3] = Belmont.x + argument0 - x;
temp[2] = max(abs(temp[3]),1);
if temp[2] < temp[1]
{
temp[0] = temp[2];
temp[1] = temp[1];
temp[3] = 1;
}
else
{
temp[0] = temp[1];
temp[1] = temp[2];
temp[3] = 0;
}
return floor(temp[0]/temp[1] * $100) << 1 | temp[3];
Bat Subscript 2
script_id = sc_cvbat2
var temp; temp[1] = sc_cvbat1(0,0);
temp[0] = temp[1] & 1;
temp[1] = temp[1] >> 1;
temp[1] += temp>>1;
temp[1] /= $100;
if temp[0]
{
hspd = temp[1] * image_xscale;
vspd = 3/2;
}
else
{
vspd = temp[1];
hspd = 3/2 * image_xscale;
}
if alarm[5]
vspd *= -1;
Bat Subscript 3
script_id = sc_cvbat3
var temp; temp[1] = argument0;
temp[0] = temp[1] & 1;
temp[1] &= ~1;
temp[1] /= $100
if temp[0]
{
hspd = temp[1] * image_xscale;
vspd = 2;
}
else
{
vspd = temp[1];
hspd = 2 * image_xscale;
}
if alarm[5]
vspd *= -1;
Medusa Bust
timeline_index = st_cvmedusa
Medusa has a flashing palette swap, like the Bone Pillars.
switch timeline_position
case 0:
status |= STILL;
status &= ~MOVING;
if !(system.status & $F0) //if boss fight
exit;
alarm[2] = $B4;
ystart = y + 4;
timeline_position += 1;
break;
case 1:
alarm[2] -= 1;
if alarm[2] exit;
//Replace tiles under bust
status &= ~STILL;
status |= MOVING;
alarm[3] = irandom(3);
sprite_index = spr_MedusaBust;
image_speed = 1/8;
vspd = 1/2;
timeline_position = 2;
break;
case 2:
if y-view_yview < $A8
{
en_xscale_set();
hspd = 15/8 * image_xscale;
alarm[3] = alarm[3] + 1 & 3;
switch other.alarm[3]
{
case 0: alarm[2] = $10; break;
case 1: alarm[2] = $80; break;
case 2: alarm[2] = $40; break;
case 3: alarm[2] = $A0; break;
}
spawn_medsnake(0,0);
timeline_position = 3;
}
break;
case 3:
alarm[2] -= 1;
if alarm[2]
{
move_sinewave(0);
if x-view_xview < $10
{
hspd *= -1;
image_xscale *= -1;
}
else
if x-view_xview > $EF
{
hspd *= -1;
image_xscale *= -1;
}
}
else
{
timeline_position = 4;
vspd = 0;
alarm[2] = $3C;
}
break;
case 4:
alarm[2] -= 1;
if !alarm[2]
{
vspd = 1/2;
timeline_position = 2;
}
break;
}
//Flash the palette
if !(image_index mod 1)
{
sprite_index = spr_MedusaFlash;
}
Medusa's Snake
timeline_index = st_cvsnake
switch timeline_position
{
case 0:
sprite_index = spr_MedusaSnake;
image_speed = 1/8;
en_xscale_set();
vspd = 0;
timeline_position = 1;
break;
case 1:
//Oddly, this used friction instead of gravity in code.
vspd += 1/8;
//Check for collision below
if tile_map_read($2E,0) //bbox_bottom 8 pixels above base
{
vspd = 0;
y &= ~7;
hspd = 3/2 * image_xscale;
alarm[2] = $18;
timeline_position = 2;
}
break;
case 2:
alarm[2] -= 1;
if alarm[2]
timeline_position = 3;
else
if !tile_map_read($2E,0) //see prev note
{
en_xscale_set();
vspd = 0;
hspd = 0;
timeline_position = 1;
}
break;
case 3:
en_xscale_set();
hspd = 3/2 * image_xscale;
alarm[2] = $18;
timeline_position = 2;
break;
}
Twin Mummies
timeline_index = st_cvmummy
Another flashing palette swap enemy. It also requires a step counter, so you can't use irandom() in place of system.alarm[0] in this code.
switch timeline_position
{
case 0:
sprite_index = spr_TwinMummy;
palette = 0;
image_speed = 1/16;
status = STILL | PASSTHRU;
if !(system.status & $F0) //if boss fight
exit;
alarm[3] = myIndex + irandom(3);
//584 = $20
//530 = $00
alarm[2] = $78;
timeline_position += 1;
break;
case 1:
alarm[2] -= 1;
if !alarm[2]
{
en_xscale_set();
hspd = 3/4 * image_xscale;
status &= ~(STILL | PASSTHRU);
status |= MOVING;
alarm[3] = alarm[3] + 1 & 3;
switch alarm[3]
{
case 0: alarm[2] = $7B; break;
case 1: alarm[2] = $41; break;
case 2: alarm[2] = $B4; break;
case 3: alarm[2] = $20; break;
}
timeline_position = 2;
}
break;
case 2:
alarm[2] -= 1;
if alarm[2]
{
if !irandom($1F)
{
en_xscale_set();
hspd = 3/4 * image_xscale;
if image_xscale != global.image_xscale
if abs(x - Belmont.x) < $38
{
hspd *= -1;
image_xscale *= -1;
}
}
if x - view_xview < 8
x = view_xview + 8;
else
if x - view_xview > $F8
x = view_xview + $F8;
}
else
{
status &= ~MOVING;
en_xscale_set();
timeline_position = 3;
alarm[2] = $28;
}
break;
case 3:
alarm[2] -= 1;
if alarm[2]
{
var temp; temp = (system.alarm[0] & 2) + 1;
if temp != palette
{
palette = temp;
if palette == 0
sprite_index = spr_TwinMummy;
else
sprite_index = spr_MummyFlash;
}
}
else
{
if palette != 0
{
palette = 0;
sprite_index = spr_TwinMummy;
}
alarm[2] = 8;
timeline_position = 4;
with spawn_twinwrap(0,8-16*irandom(1))
{
hspd = 3/2 * image_xscale;
vspd = 3/2;
play_sound(snd_twinwrap);
}
}
break;
case 4:
alarm[2] -= 1;
if !alarm[2]
{
en_xscale_set();
status |= MOVING;
alarm[3] = alarm[3] + 1 & 3;
switch alarm[3]
{
case 0: alarm[2] = $7B; break;
case 1: alarm[2] = $41; break;
case 2: alarm[2] = $B4; break;
case 3: alarm[2] = $20; break;
}
timeline_position = 2;
}
break;
}
Mummy Wrap
timeline_index = st_twinwrap
switch timeline_position
{
case 0:
status = MOVING;
sprite_index = spr_MummyWrap;
image_speed = 1/8;
move_sinewave(0);
timeline_position += 1;
break;
case 1:
move_sinewave(0);
break;
}
Frankenstein's Monster
timeline_index = st_themonster
en_xscale_set();
switch timeline_position
{
case 0:
if !(status & PASSTHRU)
{
status |= PASSTHRU;
image_speed = 1/$18;
sprite_index = spr_Frankenstein;
}
if !(system.status & $F0) //if boss fight
exit;
alarm[2] = $78;
status &= ~PASSTHRU;
timeline_position += 1;
break;
case 1:
alarm[2] -= 1;
if !alarm[2]
{
timeline_position = 2;
status |= MOVING | STILL;
hspd = 3/4 * image_xscale;
alarm[2] = $3C;
//55 = system.alarm[0] & 3;
}
break;
case 2:
alarm[2] -= 1;
if alarm[2]
Igor.x = x;
else
{
with Igor
{
timeline_position = 1;
status &= ~PASSTHRU;
status |= MOVING;
}
if abs(Belmont.x - x) < $50
dir = -image_xscale;
else
dir = image_xscale;
timeline_position = 3;
switch irandom(3)
{
case 0: alarm[2] = $3C; break;
case 1: alarm[2] = $24; break;
case 2: alarm[2] = $54; break;
case 3: alarm[2] = $18; break;
}
dir = -image_xscale;
hspd = 3/4 * dir;
}
break;
case 3:
alarm[2] -= 1;
if alarm[2]
{
if x - view_xview < $10 || x - view_xview > $EF
{
dir = - dir;
hspd = 3/4 * dir;
}
}
else
{
if abs(Belmont.x - x) < $50
dir *= -1;
hspd = 3/4 * dir;
timeline_position = 3;
switch irandom(3)
{
case 0: alarm[2] = $3C; break;
case 1: alarm[2] = $24; break;
case 2: alarm[2] = $54; break;
case 3: alarm[2] = $18; break;
}
}
}
Igor
timeline_index = st_igor
switch timeline_position
{
case 0:
if !(status & PASSTHRU)
{
status |= PASSTHRU;
sprite_index = spr_Fleaman
image_speed = 1/8;
}
en_xscale_set();
hspd = 3/2 * image_xscale;
vspd = 0;
break;
case 1:
case 2:
vspd += 1/8;
if tile_map_read(0,0) //(0,8) //check collision below
{
y &= ~7;
vspd = 0;
hspd = 0;
image_index = 1;
alarm[2] = 8;
status &= ~MOVING;
timeline_position += 1;
}
if x - view_xview < 9
x = view_xview + 9;
else
if x - view_xview > $F7
y = view_xview + $F7;
hspd *= -1;
image_xscale *= -1;
if y - view_yview < $30
{
y = view_yview + $30;
vspd = 0;
}
if timeline_position == 1
if vspd >= 0
{
image_index = 2;
timeline_position = 2;
with spawn_fireball(0,0)
{
sc_cvbat2();
}
}
break;
case 3:
alarm[2] -= 1;
if alarm[2]
{
if y - view_yview < $30
{
y = view_yview+$30;
vspd = 0;
}
}
else
{
timeline_position = 1;
switch irandom(3)
{
case 0: vspd = -3; break;
case 1: vspd = -5; break;
case 2: vspd = -4; break;
case 3: vspd = -6; break;
}
image_index = 1;
}
break;
}
Death
timeline_index = st_cvdeath
en_xscale_set();
switch timeline_position
{
case 0:
status |= STILL;
status &= ~MOVING;
if !(system.status & $F0) //if boss fight
exit;
alarm[2] = $78;
timeline_position += 1;
break;
case 1:
alarm[2] -= 1;
if alarm[2] exit;
sprite_index = spr_Reaper;
vspd = 5/2;
status |= MOVING;
status &= STILL;
timeline_position = 2;
break;
case 2:
vspd += 5/256;
sc_reaper();
break;
case 3:
if !(irandom($1F))
if abs(x-Belmont.x) < $48
{
dir = image_xscale;
vspd = 2;
hspd = 5/4 * dir;
status |= MOVING;
timeline_position = 4;
exit;
}
alarm[2] -= 1;
if alarm[2] exit;
vspd = 1+irandom(1);
dir = irandom(1) - 1 | 1;
hspd = 3/4 * dir;
status |= MOVING;
timeline_position = 4;
break;
case 4:
if x - view_xview < $10
{
x = view_xview + $10;
dir *= -1;
}
else
if x - view_xview > $F0
{
x = view_xview + $F0;
dir *= -1;
}
vspd -= 3/128;
if y - view_yview < $21
{
y = view_yview + $21;
vspd = 0;
}
else
sc_reaper();
break;
}
Death Subscript
script_id = sc_reaper
if tile_map_read(0,0) //(0,$18) //check collision below (i think)
{
var temp;
y &= ~7;
for(temp[2]=3+global.hard_mode; temp[2] > 0; temp[2]-=1)
with spawn_reapscythe(0,0) //A to D
{
temp[0] = Belmont.x - view_xview;
temp[1] = Belmont.y - view_yview;
switch temp[2]
{
case 1:
x = temp[0] + $04;
y = temp[1] + $20;
break;
case 2:
x = temp[0] + $30;
y = temp[1] + $D0;
break;
case 3:
x = temp[0] + $A0;
y = temp[1] + $18;
break;
case 4:
x = temp[0] + $20;
y = temp[1] + $B0;
break;
}
x = (x & $FF) + view_xview;
y = (y & $FF) + view_yview;
alarm[2] = $28;
status &= (MOVING | STILL);
}
switch irandom(3)
{
case 0: alarm[2] = $90; break;
case 1: alarm[2] = $5F; break;
case 2: alarm[2] = $B9; break;
case 3: alarm[2] = $54; break;
}
status &= ~MOVING;
timeline_position = 3;
}
Death's Scythe
timeline_index = st_reapscythe
Requires a step counter.
switch timeline_position
{
case 0:
sprite_index = spr_ReapScythe;
image_speed = 1/2;
alarm[2] -= 1;
if alarm[2]
{
status &= ~HIDDEN;
if system.alarm[0] & 1
status |= HIDDEN;
}
else
{
status &= ~HIDDEN;
timeline_position = 1;
}
break;
case 3:
alarm[2] -= 1;
if alarm[2] break;
case 1:
var temp; temp[1] = sc_cvbat1(0,0);
temp[0] = temp[1] & 1;
temp[1] = temp[1] >> 1;
temp[1] /= $100;
if temp[0]
{
hspd = temp[1] * image_xscale;
vspd = 14/16;
}
else
{
vspd = temp[1];
hspd = 14/16 * image_xscale;
}
if alarm[5]
vspd *= -1;
status |= MOVING;
alarm[2] = $78;
timeline_position = 2;
break;
case 2:
alarm[2] -= 1;
if !alarm[2]
{
status &= ~MOVING;
alarm[2] = $20;
timeline_position = 3;
}
break;
}
Humanoid Dracula
timeline_index = st_bigdbody
The original code was a tad hard to follow, so I'm not sure if this is correct or not. I had to read the original code time after time and time again. Dracula's head is separate from his body, but the body controls the head.
Also, Dracula's code requires a step counter as well.
if timeline_position < 14
en_xscale_set();
switch timeline_position
{
case 0:
status |= STILL;
status &= ~MOVING;
if !(system.status & $F0) //if boss fight
exit;
alarm[2] = $78;
timeline_position += 1;
break;
case 1:
alarm[2] -= 1;
if alarm[2] exit;
with instance_create(0,0,BigDHead)
sprite_index = spr_BigDHead;
sprite_index = spr_BigDBody;
status |= HIDDEN;
alarm[2] = $3C;
timeline_position = 2;
break;
case 2:
alarm[2] -= 1;
if !alarm[2]
{
status |= MOVING;
vspd = -1/2;
timeline_position = 3;
}
sc_bigdhead();
break;
case 3:
//Change $A8 to the height of the floor
if y - view_yview < $A8
{
y = view_yview + $A8;
alarm[2] = $3C;
timeline_position = 4;
}
sc_bigdhead();
break;
case 4:
alarm[2] -= 1;
if !alarm[2]
{
alarm[2] = $45;
timeline_position = 5;
}
sc_bigdhead();
break;
case 5:
alarm[2] -= 1;
if alarm[2]
{
if system.alarm[0] & 1
status |= HIDDEN;
else
status &= ~HIDDEN;
}
else
{
image_index = 1;
switch irandom(3)
{
case 0: alarm[2] = $24; break;
case 1: alarm[2] = $10; break;
case 2: alarm[2] = $32; break;
case 3: alarm[2] = $18; break;
}
status &= ~PASSTHRU;
timeline_position = 6;
}
sc_bigdhead();
break;
case 6:
alarm[2] -= 1;
if !alarm[2]
{
image_index = 2;
timeline_position = 6;
alarm[2] = $10;
}
sc_bigdhead();
break;
case 7:
alarm[2] -= 1;
if !alarm[2]
{
var temp;
for(temp[1]=0; temp[1]<3; temp[1]+=1)
with spawn_fireball(0,0) //4 to D
{
temp[0] = sc_cvbat1(0,$10-$10*temp[1]);
sc_cvbat3(temp[0]);
}
alarm[2] = $20;
timeline_position = 8;
}
sc_bigdhead();
break;
case 8:
alarm[2] -= 1;
if !alarm[2]
{
timeline_position = 9;
status |= PASSTHRU;
alarm[2] = $20;
}
sc_bigdhead();
break;
case 9:
alarm[2] -= 1;
if alarm[2]
{
if system.alarm[0] & 1
BigDHead.status &= ~HIDDEN;
else
BigDHead.status |= HIDDEN;
}
else
{
timeline_position = 10;
alarm[2] = $4F;
status |= HIDDEN;
BigDHead.status |= HIDDEN;
}
sc_bigdhead();
break;
case 10:
alarm[2] -= 1;
if !alarm[2]
{
timeline_position = 11;
alarm[2] = $20;
var temp; temp[1] = system.alarm[0];
while 1
{
switch temp[1] & 7;
{
case 0: temp[0] = $10; break;
case 1: temp[0] = $F8; break;
case 2: temp[0] = $80; break;
case 3: temp[0] = $60; break;
case 4: temp[0] = $30; break;
case 5: temp[0] = $D8; break;
case 6: temp[0] = $60; break;
case 7: temp[0] = $A8; break;
}
x = (Belmont.x - view_xview + temp[0] & $FF) + view_xview;
if x - view_xview < $14 || x - view_xview > $EB
temp[1] += 1;
else
break;
}
}
break;
case 11:
alarm[2] -= 1;
if alarm[2]
{
if system.alarm[0] & 1
{
status &= ~HIDDEN;
BigDHead.status &= ~HIDDEN;
}
else
{
status |= HIDDEN;
BigDHead.status |= HIDDEN;
}
}
else
{
switch irandom(3)
{
case 0: alarm[2] = $24; break;
case 1: alarm[2] = $10; break;
case 2: alarm[2] = $32; break;
case 3: alarm[2] = $18; break;
}
status &= ~(PASSTHRU | HIDDEN);
timeline_position = 6;
}
sc_bigdhead();
break;
case 13:
status |= HIDDEN | PASSTHRU;
with BigDHead
{
status |= PASSTHRU;
image_xscale = -other.image_xscale;
hspd = 2 * image_xscale;
vspd = -5;
}
alarm[2] = $78;
timeline_position = 14;
break;
case 14:
alarm[2] -= 1;
if !alarm[2]
{
timeline_index = st_bigdemon;
timeline_posiiton = 0;
alarm[2] = $20;
}
break;
}
Dracula's Head Subscript
script_id = sc_bigdhead
BigDHead.y = y - $1A;
BigDHead.x = x - 6 * image_xscale;
BigDHead.image_xscale = image_xscale;
BigDHead.timeline_position = timeline_position;
Demonoid Dracula
timeline_index = st_bigdemon
en_xscale_set();
switch timeline_position
{
case 0:
alarm[2] -= 1;
if alarm[2]
{
if system.alarm[0] & 1
BigDHead.status &= ~HIDDEN;
else
BigDHead.status |= HIDDEN;
}
else
{
//change music
for({var temp; temp=0;} temp < 6; temp += 1;)
with spawn_bigdflesh(0,0); //spawns 6 chunks of flesh
{
switch temp
{
case 0:
image_xscale = -1;
hspd = 45/16;
vspd = hspd;
break;
case 1:
image_xscale = -1;
hspd = 4;
vspd = 0;
break;
case 2:
image_xscale = -1;
hspd = 45/16;
vspd = -hspd;
break;
case 3:
image_xscale = 1;
hspd = -45/16;
vspd = -hspd;
break;
case 4:
image_xscale = 1;
hspd = -4;
vspd = 0;
break;
case 5:
image_xscale = 1;
hspd = -45/16;
vspd = hspd;
break;
}
}
alarm[2] = $10;
timeline_position = 1;
status |= PASSTHRU | HIDDEN;
with BigDHead
{
BigDHead.status |= HIDDEN;
sprite_index = spr_DemonHead;
}
}
break;
case 1:
alarm[2] -= 1;
if !alarm[2]
{
hp = $40;
/*
Additional code run elsewhere raises or
lowers the HP bar as so:
if system.alarm[0] & 1
global.boss_health += sign(hp - global.boss_health);
*/
BigDHead.status &= ~PASSTHRU;
status &= ~HIDDEN;
sprite_index = spr_DemonBody;
image_index = 6;
//Replace $A8 with height of floor
y = view_yview + $A8;
alarm[2] = $21;
timeline_position = 2;
sc_bigdemonhead();
}
break;
case 2:
alarm[2] -= 1;
if !alarm[2]
{
if system.alarm[0] & 1
alarm[2] = $55;
else
alarm[2] = $7B;
timeline_position = 3;
status &= ~PASSTHRU;
}
sc_bigdemonhead();
break;
case 3:
alarm[2] -= 1;
if alarm[2]
{
if !(system.alarm[0] & $F)
{
if image_xscale != Belmont.image_xscale
if io_hold & $40 //if player is attacking
{
alarm[2] = $10;
timeline_position = 4;
}
}
}
else
{
image_index = 1;
alarm[2] = $10;
timeline_position = 4;
}
sc_bigdemonhead();
break;
case 4:
alarm[2] -= 1;
if !alarm[2]
{
if system.alarm[0] & 1
vspd = -6;
else
vspd = -9/2;
hspd = 3/4 * image_xscale;
image_index = 2;
timeline_position = 5;
}
sc_bigdemonhead();
break;
case 5:
vspd += 3/16;
var temp; temp = y - view_yview;
if temp < $20
vspd = 0;
if temp < $60
image_index = 2;
else
image_index = 3;
temp = x - view_xview;
if temp < $18 || temp > $E7
hspd *= -1;
if tile_map_read(0,0) //Check collision below
{
vspd = 0;
y &= ~7;
image_index = 4;
alarm[2] = $18;
timeline_position = 6;
play_sound(snd_thwomp/*heavy landing*/);
}
sc_bigdemonhead();
break;
case 6:
alarm[2] -= 1;
if !alarm[2]
{
alarm[2] = $10;
image_index = 6;
timeline_position = 7;
}
sc_bigdemonhead();
break;
case 7:
alarm[2] -= 1;
var temp; temp = abs(x - Belmont.x);
if temp < $30 || !(temp & 3)
{
if system.alarm[0] & 1
alarm[2] = $55;
else
alarm[2] = $7B;
timeline_position = 3;
}
else
if temp & 3
{
if system.alarm[0] & 1
alarm[2] = $10
else
alarm[2] = $28;
timeline_position = 8;
}
sc_bigdemonhead();
break;
case 8:
alarm[2] -= 1;
if !alarm[2]
{
image_index = 5;
alarm[2] = $10;
timeline_position = 9;
}
sc_bigdemonhead();
break;
case 9:
alarm[2] -= 1;
if !alarm[2]
{
play_sound(snd_fwoosh/*fwooshy flames*/);
var temp;
with BigDHead
for(temp[1]=0; temp[1]<3; temp[1]+=1)
with spawn_fireball(0,0)
{
temp[0] = sc_cvbat1(0,$10-$10*temp[1]);
sc_cvbat3(temp[0]);
}
alarm[2] = $10;
timeline_position = 10;
}
sc_bigdemonhead();
break;
case 10:
alarm[2] -= 1;
if !alarm[2]
{
image_index = 6;
//Change the $A8 to the floor height
y = view_yview + $A8;
alarm[2] = $21;
status |= PASSTHRU;
timeline_position = 2;
}
sc_bigdemonhead();
break;
}
Demonoid Head Subscript
script_id = sc_bigdemonhead
BigDHead.image_xscale = image_xscale;
BigDHead.y = y - $20;
BigDHead.x = x + 8 * image_xscale;
-
Just realized I never posted the CV1 enemies other than the bosses. I'll try to get around to that this week. :o
-
That would be awesome if you could do that.
-
Dammit bro, I'm old and tired. Leave me alone!
Actually the real reason is the cable that hooks my phone up to my computer so I have internet access on the laptop has been acting up. I'm working on getting it up here. I wrote up the panther code long ago. I was shocked I hadn't posted it yet. :/
-
These are
all most CV1 enemies, but coded in the CV3 style (for compatibility with my long-overdue project). They should still run the same for the most part, as the only real changes between CV1 and CV3 codes were some complexities of state machines (CV1 wasn't very efficient) and how things like invincibility were handled.
(Yes, I'm aware I named all the duplicate enemy states XXXX2 even though these were the original CV enemies. I didn't start with CV1's code and didn't feel like renaming everything.)
CV1 Bat
timeline_index = st_flybat2
It's a pretty simple one, even for a CV3 conversion.
switch timeline_position
{
case 0:
status |= MOVING;
sprite_index = spr_Bat;
image_speed = 1/8;
y = Belmont.y + 1;
ystart += 8;
en_xscale_set();
vspd = 0;
hspd = 9/8 * image_xscale;
timeline_position += 1;
case 1:
move_sinewave(3);
break;
}
CV1 Sleeping Bat
timeline_index = st_zzzbat2
Much simpler than the CV3 sleeping bat, as it just flies straight.
switch timeline_position
{
case 0:
sprite_index = spr_SleepBat;
image_index = 0;
image_speed = 0;
status |= STILL;
timeline_position += 1;
break;
case 1: //83FF
if abs(Belmont.y-y) < $38
if abs(Belmont.x-x) < $60
{
timeline_position += 1;
en_xscale_set();
hspd = 3/2 * image_xscale;
vspd = 3/2;
image_index += 1;
status &= ~STILL;
status |= MOVING;
}
break;
case 2:
vspd -= 1/32;
if vspd<0
timeline_position += 1;
break;
case 3:
vspd = 0;
break;
}
CV1 Medusa Head
timeline_index = st_medhead2
Nearly the same as the bat, but with a larger wave amplitude.
switch timeline_position
{
case 0:
sprite_index = spr_MedHead;
image_speed = 1/8;
status |= MOVING;
en_xscale_set();
vspd = 2;
hspd = 5/4 * image_xscale;
timeline_position += 1;
break;
case 1:
move_sinewave(0);
break;
}
CV1 Panther
timeline_index = st_panther
This was the first CV1 enemy I studied and translated into CV3 code.
switch timeline_position
{
case 0:
sprite_index = spr_Panther;
image_index = 0;
image_speed = 0;
status |= STILL;
status &= ~MOVING;
timeline_position += 1;
break;
case 1:
if abs(x-Belmont.x) < $40
{
en_xscale_set();
image_index = 1;
image_speed = 1/7;
status |= MOVING;
hspd = 2 * image_xscale;
timeline_position += 1;
}
break;
case 2:
if !tile_map_read($2E) //check if no collision below
{
image_index = 1;
status |= STILL;
vspd = -3/2;
hspd = 2 * image_xscale;
timeline_position += 1;
}
break;
case 3:
vspd += 1/8;
if vspd > 0
if tile_map_read($2E) //check if collision below
{
y &= ~7;
vspd = 0;
en_xscale_set();
hspd = 2 * image_xscale;
status &= ~STILL;
image_index = 1;
timeline_position = 2;
}
break;
}
CV1 Ghoul
timeline_index = st_ghoul
As you'd expect from the Goomba of Castlevania, the code is simple.
switch timeline_position
{
case 0:
sprite_index = spr_Ghoul;
status |= MOVING;
image_index = 0;
image_speed = 1/8;
en_xscale_set();
hspd = 7/8 * image_xscale);
timeline_position += 1;
break;
case 1:
if !tile_map_read($D,0) //check for no collision below
{
hspd = 0;
vspd = 1;
timeline_position += 1;
}
else
if tile_map_read($7,0)
move_horz_rev(1);
break;
case 2:
if tile_map_read($D,0) //check for collision below
{
move_stop();
hspd = 7/8 * image_xscale;
timeline_position = 1;
}
break;
}
CV1 Merman
timeline_index = st_merman2
switch timeline_position
{
case 0:
case 6:
play_sound(snd_0E) /*splash sound*/);
for({var i; i=0;} i<4; i+=1;)
{
with spawn_watersplash(0,0)
switch i
{
case 0:
move_horz_set($30/$100,1);
move_vert_set($280/$100,1);
break;
case 1:
move_horz_set($60/$100,1);
move_vert_set($140/$100,1);
break;
case 2:
move_horz_set($60/$100,1);
move_horz_set($140/$100,1);
break;
case 3:
move_horz_set($30/$100,0);
move_horz_set($280/$100,1);
break;
}
}
if timeline_position == 6
{
instance_destroy();
break;
}
sprite_replace(myIndex,"MermanJump.gif",0,0,0,8,16);
image_speed = 0;
en_xscale_set();
status |= STILL | MOVING;
alarm[3] = 0;
timeline_position = 1;
break;
case 1:
vspd += 1/8;
if vspd < 0 break;
if y - view_yview > $C0
timeline_position = 6;
else
if tile_map_read(0,0) //check for collision below
{
if !alarm[3]
{
alarm[3] = 1;
play_sound(snd_0D) /*thud sound*/);
}
y &= ~$F;
en_xscale_set();
status &= ~STILL;
image_speed = 1/8;
alarm[2] = 8;
timeline_position = 2;
}
break;
case 2:
alarm[2] -= 1;
if !alarm[2]
{
hspd = 3/4 * image_xscale;
status |= MOVING;
switch irandom(3)
{
case 0: alarm[2] = $4F; break;
case 1: alarm[2] = $79; break;
case 2: alarm[2] = $5D; break;
case 3: alarm[2] = $90; break;
}
timeline_position = 3;
}
break;
case 3:
alarm[2] -= 1;
if alarm[2]
{
if !tile_map_read(0,0) //check for no collision below
{
timeline_position = 1;
hspd = 0;
image_index = 0;
}
}
else
{
image_index = 2;
alarm[2] = $10;
timeline_position = 4;
}
break;
case 4:
alarm[2] -= 1;
if !alarm[2]
{
with spawn_fireball(0,-8) //remember to set fireball's image_xscale upon creation
hspd = 7/4 * image_xscale;
alarm[2] = $10;
timeline_position = 5;
}
break;
case 5:
alarm[2] -= 1;
if !alarm[2]
{
en_xscale_set();
image_index = 0;
switch irandom(3)
{
case 0: alarm[2] = $4F; break;
case 1: alarm[2] = $79; break;
case 2: alarm[2] = $5D; break;
case 3: alarm[2] = $90; break;
}
timeline_position = 3;
}
}
CV1 Ghost
timeline_index = st_ghost2
Very little was edited in this, hence the odd format. Remember sc_cvbat1 is found in the Giant Bat's code in the previous post.
alarm[2] -= 1;
switch timeline_position
{
case 0:
sprite_index = spr_Ghost;
image_speed = 1/8;
timeline_position += 1;
status |= MOVING;
case 1:
if !alarm[2]
{
alarm[2] = $20;
var temp;
temp[1] = sc_cvbat1(0,0);
temp[0] = temp[1] & 1;
temp[1] = temp[1] >> 1;
temp[1] -= temp[1] >> 2;
temp[1] /= $100;
if temp[0]
{
hspd = temp[1] * image_xscale);
if alarm[5] vspd = -3/4;
else vspd = 3/4;
}
else
{
if alarm[5] vspd = -temp[1];
else vspd = temp[1];
hspd = 3/4 * image_xscale;
}
}
}
CV1 Fleaman
timeline_index = st_fleaman2
Both the lurking Fleaman and the one dropped by Harpies use the same code.
switch timeline_position
{
case 0:
if !alarm[2]
alarm[2] = $3C;
alarm[2] -= 1;
if !alarm[2]
{
sprite_index = spr_Fleaman;
image_speed = 0;
status |= STILL | MOVING;
timeline_position = 1;
}
break;
case 1:
vspd += 1/8;
if tile_map_read(0,0) //(0,8) //check for collision below
{
vspd = 0;
y &= ~7;
image_index = 0;
alarm[2] = 8;
timeline_position += 1;
}
break;
case 2:
alarm[2] -= 1;
if alarm[2] break;
if Belmont.hitTimer //If Belmont is invincible
en_xscale_set();
image_index = 1;
timeline_position = 1;
if abs(x - Belmont.x) < $40
&& image_xscale != Belmont.image_xscale
{
vspd = -7/2;
hspd = image_xscale;
}
else
{
vspd = -1;
hspd = 9/4 * image_xscale;
}
break;
//States 3 and 4 are identical to 1 and 2 respectively.
//State 5 is only used when an Eagle carries a Fleaman.
case 5:
var temp; temp = global.instance[myIndex+5];
if temp //Did a harpy drop it?
if temp.timeline_index == st_harpy2
if irandom(7)
{
x = temp.x + $10;
break;
}
else
if abs(x - Belmont.x) > $3F
{
x = temp.x + $10;
break;
}
timeline_position = 1;
vspd = 0;
hspd = 0;
status |= MOVING | STILL;
image_index = 1;
break;
}
CV1 Bone Skeleton
timeline_index = st_boneskel2
In clocktower stages, facing direction depends on which side of the cog he is standing. It's also apparent in how this enemy is coded that I've been making incompatible changes to my codes as I post.
if !(status & OFFSCREEN)
{
en_xscale_set();
if block == 13 || block == 17
{
if x - view_xview > $80
image_xscale = -1; //face left if on the right side
else
image_xscale = 1; //face right if on the left side
}
}
switch timeline_position
{
case 0:
switch irandom(3)
{
case 0: alarm[3] = 4;
case 1: alarm[3] = 2;
case 2: alarm[3] = 3;
case 3: alarm[3] = 1;
}
sprite_index = spr_BoneSkel2;
hspd = 3/2 * image_xscale;
status |= MOVING;
timeline_position = 1;
alarm[2] = 4;
break;
case 1:
alarm[2] -= 1;
if alarm[2] break;
alarm[3] -= 1;
if alarm[3]
{
if status & OFFSCREEN
hspd = abs(hspd) * image_xscale;
else
if abs(x - Belmont.x) < $48
hspd = abs(hspd) * -image_xscale;
else
hspd = abs(hspd) * image_xscale;
alarm[2] = $10;
timeline_position = 2;
}
else
{
alarm[2] = 4;
timeline_position = 3;
}
break;
case 2:
alarm[2] -= 1;
if alarm[2]
{
if tile_map_read(0,0) //($C,0) //check if collision in front
hspd *= -1;
else
if !tile_map_read(0,0) //(0,$10) //check if no collision below
{
hspd = 5/4 * image_xscale;
vspd = -5/2;
timeline_position = 5;
//no change in status - still animated during jump
}
}
else
{
alarm[2] = 4;
timeline_position = 1;
}
break;
case 3:
alarm[2] -= 1;
if alarm[2] break;
if !(status & OFFSCREEN)
{
with spawn_bone2(0,0)
{
hspd = 3/4 * other.image_xscale;
vspd = -(3.5 + irandom(1));
}
}
timeline_position = 4;
alarm[2] = 8;
break;
case 4:
alarm[2] -= 1;
if alarm[2] break;
case 6:
timeline_position = 0;
break;
case 5:
vspd += 1/8;
if tile_map_read(0,0) //(0,$10) //Check for collision below
{
y &= ~$F;
vspd = 0;
timeline_position = 6;
}
break;
}
CV1 Armor
timeline_index = st_armor2
The code may look like it's just the same thing for states 1 and 2, but it's actually different.
switch timeline_position
{
case 0:
sprite_index = spr_Knight;
status |= MOVING;
image_speed = 1/$10;
en_xscale_set();
hspd = 1/2 * image_xscale;
alarm[2] = 0;
timeline_position += 1;
break;
case 1:
if abs(Belmont.y-y) < $4
{
en_xscale_set();
hspd = 1/2 * image_xscale;
timeline_position += 1;
alarm[2] = $C0;
}
else
{
alarm[2] -= 1;
if !alarm[2]
{
hspd *= -1;
image_xscale *= -1;
alarm[2] = $C0;
}
if !tile_map_read($D,0) //check for collision below
timeline_position += 2;
else
if tile_map_read($6,image_xscale) //check for collisions in front
or tile_map_read($20,image_xscale)
{
hspd *= -1;
image_xscale *= -1;
}
}
break;
case 2:
if abs(Belmont.y - y) < $4
{
alarm[2] -= 1;
if !alarm[2]
{
en_xscale_set();
hspd = 1/2 * image_xscale;
alarm[2] = $C0;
}
if !tile_map_read($D,0) //check for collision below
timeline_position += 1;
else
if tile_map_read($6,image_xscale) //check for collisions in front
or tile_map_read($20,image_xscale)
{
hspd *= -1;
image_xscale *= -1;
}
}
else
{
timeline_position = 0;
alarm[2] = $C0;
}
break;
case 3:
vspd += 1/8;
if tile_map_read($D,0) //Check for collision below
{
y &= ~$F;
vspd = 0;
timeline_position = 1;
}
break;
}
CV1 Blood Skeleton
timeline_index = st_bloodskel2
There are two codes. The first is run independent of all others and handles state 4 when the Stopwatch is active.
//Continue state 4 if stopwatch counting down
if global.pause | 4
{
if timeline_position == 4
{
alarm[2] -= 1;
if !alarm[2]
{
alarm[2] = $54;
timeline_position = 0;
status |= PASSTHRU;
sprite_index = spr_BonePile;
}
}
}
switch timeline_position
{
case 0:
if !alarm[2]
{
alarm[2] = $3C;
status |= PASSTHRU | STILL;
sprite_index = spr_BonePile;
}
image_index = 0;
alarm[2] -= 1;
if !alarm[2]
{
timeline_position = 1;
image_speed = 1/$18;
status &= ~STILL;
alarm[3] = 1;
}
break;
case 1:
if image_index < 2 break;
sprite_index = spr_BloodSkel;
case 3:
alarm[2] -= 1;
if !alarm[2]
{
alarm[3] -= 1;
if !alarm[3]
{
en_xscale_set();
switch irandom(3) //uses secondary randomizer
{
case 0: alarm[3] = 4; break;
case 1: alarm[3] = 3; break;
case 2: alarm[3] = 5; break;
case 3: alarm[3] = 4; break;
}
}
image_index = 0;
image_speed = 1/16;
hspd = 5/8 * image_xscale;
status |= MOVING;
status &= ~(PASSTHRU | STILL);
alarm[2] = $30;
timeline_position = 2;
}
break;
case 2:
alarm[2] -= 1;
if alarm[2]
{
if tile_map_read(0,0) //($C,0) //check if collision ahead
{
hspd *= -1;
image_xscale *= -1;
}
else
if !(status & OFFSCREEN)
if !tile_map_read(0,0) //(0,$10) //check for no collision below
{
timeline_position = 5;
vspd = 0;
}
}
else
{
status |= STILL;
status &= ~MOVING;
alarm[2] = $10;
timeline_position = 3;
}
break;
//When hit by an attack, set state to 4
case 4:
if !alarm[2]
{
alarm[2] = $20;
status |= PASSTHRU;
sprite_index = spr_BonePile;
image_index 1;
image_speed = -1/$18;
}
alarm[2] -= 1;
if !alarm[2]
{
alarm[2] = $54;
status |= PASSTHRU | STILL;
timeline_position = 0;
}
break;
//The following state is only accessible when falling
case 5:
vspd += 1/8;
if tile_map_read(0,0) //(0,$10) //check for collision below
{
y &= ~7;
vspd = 0;
timeline_position = 2;
}
break;
}
CV1 Crow
timeline_index = st_crow2
This was a fun one to crack. As a couple of you may recall, I actually found a bug in the original code, which upon remedying is made the Crow's movement much smoother and more natural. It's commented, so you can choose which version you want. By default Crows are perched; setting alarm[5] when spawning them aggros them immediately.
en_xscale_set();
switch timeline_position
{
case 0:
if !alarm[5] //aggro flag
{
alarm[5] = 1;
alarm[2] = $3C;
sprite_index = spr_CrowPerch;
image_index = 0;
status |= STILL;
}
if !alarm[2]
{
sprite_index = spr_CrowFly;
alarm[4] = sign(y-Belmont.y);
switch irandom(3) //uses secondary randomizer
{
case 0: vspd = 7/4 * alarm[4]; break;
case 1: vspd = 5/2 * alarm[4]; break;
case 2: vspd = 5/4 * alarm[4]; break;
case 3: vspd = 2 * alarm[4]; break;
}
alarm[3] = image_xscale;
hspd = 3/4 * image_xscale;
status |= MOVING;
timeline_position += 1;
}
else
alarm[2] -= 1;
//Original code handled offscreen status and weapon collisions
break;
case 1:
var temp; temp = sign(vspd);
vspd += 1/32 * -alarm[4];
hspd += 1/32 * -alarm[3];
if sign(vspd) != temp
{
switch irandom(3) //uses secondary randomizer
{
case 0: alarm[2] = $18; break;
case 1: alarm[2] = $3F; break;
case 2: alarm[2] = $25; break;
case 3: alarm[2] = $79; break;
}
timeline_position = 2;
}
break;
case 2:
alarm[2] -= 1;
if alarm[2] break;
alarm[3] = image_xscale;
if abs(x - Belmont.x) < $20
{
hspd = 1/4 * alarm[3];
vspd = 0;
alarm[2] = $3D; //was random, but values are the same
timeline_position = 3;
}
else
{
alarm[4] = sign(y - Belmont.y);
switch irandom(3) //uses secondary randomizer
{
case 0: vspd = 7/4 * alarm[4]; break;
case 1: vspd = 5/2 * alarm[4]; break;
case 2: vspd = 5/4 * alarm[4]; break;
case 3: vspd = 2 * alarm[4]; break;
}
hspd = 3/4 * alarm[3];
timeline_position = 1;
}
break;
case 3:
alarm[2] -= 1;
if alarm[2]
{
if alarm[3] > 0
{
if Belmont.x - 8 < x break;
else
if x < Belmont.x + 8 break;
hspd += 1/32;
}
else hspd -= 1/32;
alarm[4] = sign(y - global.instance[0].y);
hspd = 1/2 * alarm[3];
/*
The original game code ran this line
but the correct line should have been
vspd = 1/2 * alarm[4];
*/
alarm[2] = $79;
timeline_position = 4;
}
else
{
image_index = 0;
alarm[4] = sign(y - Belmont.y);
switch irandom(3) //uses secondary randomizer
{
case 0: vspd = 7/4 * alarm[4]; break;
case 1: vspd = 5/2 * alarm[4]; break;
case 2: vspd = 5/4 * alarm[4]; break;
case 3: vspd = 2 * alarm[4]; break;
}
hspd = 3/4 * alarm[3];
timeline_position = 1;
}
break;
case 4:
alarm[2] -= 1;
if !alarm[2]
timeline_position = 0;
break;
}
CV1 Eagle
timeline_index = st_harpy2
Essentially the same as a Harpy from CV3, except a Harpy drops a Fleaman whereas the Fleaman ejects itself from the Eagle. This was either a reference to The Hobbit, or the Fleamen are prey of Eagles.
switch timeline_position
{
case 0:
en_xscale_set();
status |= MOVING;
sprite_index = spr_Eagle;
image_speed = 1/16;
global.instance[myIndex - 5] = instance_create(x+$10,y,obj_fleaman);
with global.instance[myIndex - 5]
{
timeline_position = 5;
sprite_index = spr_Fleaman;
image_speed = 1/8;
}
hspd = 7/4 * image_xscale;
alarm[2] = $30;
timeline_position = 1;
break;
case 1:
alarm[2] -= 1;
if !alarm[2]
{
alarm[2] = $10;
timeline_position = 2;
}
break;
case 2:
alarm[2] -= 1;
if !alarm[2]
{
alarm[2] = $30;
timeline_position = 1;
}
break;
}
CV1 Axe Knight
timeline_index = st_axeknight2
en_xscale_set();
switch timeline_position
{
case 0:
switch irandom(3)
{
case 0: alarm[3] = 9; break;
case 1: alarm[3] = 11; break;
case 2: alarm[3] = 10; break;
case 3: alarm[3] = 12; break;
}
if abs(x - Belmont.x) < $50
alarm[4] = -image_xscale;
else
alarm[4] = image_xscale;
hspd = 5/8 * alarm[4];
sprite_index = spr_AxeKnight;
alarm[2] = $10;
timeline_position = 1;
break;
case 1:
if status & OFFSCREEN
{
alarm[2] = 4;
timeline_position = 2;
}
else
{
alarm[2] -= 1;
if alarm[2]
{
if tile_map_read(0,0) //($C,0) //check for collision ahead
or !tile_map_read(0,0) //($04,$10) //check for pits ahead
hspd *= -1;
}
else
{
alarm[3] -= 1;
if alarm[3]
{
alarm[2] = 4;
timeline_position = 2;
status &= ~MOVING;
}
else
{
alarm[2] = $10;
timeline_position = 3;
}
}
}
break;
case 2:
alarm[2] -= 1;
if !alarm[2]
{
if abs(x - Belmont.x) < $50
alarm[4] = -image_xscale;
else
alarm[4] = image_xscale;
hspd = 5/8 * alarm[4];
alarm[2] = $10;
timeline_position = 1;
}
break;
case 3:
alarm[2] -= 1;
if !alarm[2]
{
with spawn_axe(0,0)
{
if irandom(1)
y = other.y - 12;
else
y = other.y + 8;
hspd = 5/4 * other.image_xscale;
alarm[2] = $58;
}
timeline_position = 0;
}
break;
}
-
Thanks for sharing such a great info, since I'm planning to use this in another 8bit console that uses a z80 processor, since I'm not capable of dissasembling MSX2's Vampire Killer.
-
Awesome. These will be put to good use.
-
Awesome. These will be put to good use.
I hope someday will get the skills to do it, because I was never good at programming.
-
I have the skills, I just need the time. :-\
-
So, may I ask you for help someday?? are you disassembling MSX's Vampire Killer by chance? that would be great!!
-
I've never looked at MSX assembly so I wouldn't be of much help.
Why are you interested in a disassembly of Vampire Killer?
-
That's ok, in the end I would just need to know how does all the code in this thread work, and the interest in Vampire Killer is because that would be useful for an hypothetical project since my target is a similar system (z80 3.54Mhz).
-
So while going back through my code updating my collision codes to work across versions of GM, apparently I had messed up on the whip skeletons code - I had copy-pasted code from elsewhere having misread the original code thinking it was shared with all skeletons. But whatever, i'll update that later.
While going back through it struck me that as creative as CV3's programmers where, they were a bit sloppy. I had previously noted that the Living Armor had an asymmetrical hitbox (larger on the left). It just occurred to me now that the dhuron has an extra hitbox. Most enemies have four or six hit points. Trevor Has 8 hit points. The dhuron has TEN hit points. I blame deteriorating eyesight for the oversight.
-
So the Dhuron was not supposed to be a tank?
That seems worthy of a bug fix patch.
Incidentally, I'm nearly finished with a platform engine that is designed to be generic and easily adjustable.
Right now it's powering my attempt at a Mega Man fan game, but as soon as I get a little bit more work done on it I'm going to try adjusting it for the first time.
And of course my target for this will be CV. If all goes well, I'll be using your enemy code along with my engine to build something playable.
(I'm still chasing my pipe dream of a definitive remake of CV1.)