PDA

View Full Version : Custom Bosses and You



C-Dawg
02-24-2008, 04:18 PM
Zodiac is chock-full of custom bosses; probably 20 or so at last count. I've settled on a specific custom boss code that is easy to modify and I encourage everyone to make use of it themselves. The script can be used for standard birds-eye view games just as well as sideview.

Warning, however. You will have to add code to make your enemy behave the way you want. That's inevitable. But if you follow this format, alot of things (damage, flashing, etc) will be taken care of for you.

I'll use the most recent boss, Tentaplus, as an example.



// =================================================
// Tentaplus - This script codes the Pieces Master, Tentaplus. Has
// three attack patterns: (1) loop-de-looping around the middle of the
// screen; (2) chasing player and trying to grab her in his tentacles (if
// succeeds, Tentaplus holds player stationary and drains her health to
// its own); (3) swooping over the top or bottom of screen spraying
// bubbles.
// Throughout combat, tentaplus babies will swarm around it, making the
// tentaplus impossible to hit. Periodically, squid torpedoes
// will emerge from the walls. Coordinate one to hit Tentaplus, and the
// babies will fall away, making Tentaplus temporarily vulnerable.
// INPUT:
// D0 = The number of the FFC centered on the body.
// D1 = hit points of this FFC
// D2 = number of the first of 6 FFC making up the body
// of the ship: 1 2 3
// 4 5 6
// 16x64 and makes up the final right collumn of the boss.
// D3 = number of first of 12 FFCs making up bubbles sprayed by Tentaplus.
// D4 = number of first of 3 FFCs making up torpedoes
// ==================================================

ffc script Tentaplus_CE{

void run (int this_ffc_number, int hit_points, int body_ffc_number, int bubble_ffc_number, int torpedo_ffc_number){

// ---------------------------
// GENERAL ENEMY SETUP (same for all)
// ---------------------------

ffc this_ffc = Screen->LoadFFC(this_ffc_number);
int head_location_X;
int head_location_Y;

boss_flag = true;

npc ghosted_enemy = Screen->CreateNPC(85);
ghosted_enemy->HP = hit_points;

npc ghosted_damage_enemy = Screen->CreateNPC(200);

ffc body1 = Screen->LoadFFC(body_ffc_number);
ffc body2 = Screen->LoadFFC(body_ffc_number+1);
ffc body3 = Screen->LoadFFC(body_ffc_number+2);

ffc bubble1 = Screen->LoadFFC(bubble_ffc_number); bubble1->X = -16; bubble1->Y = -16;
ffc bubble2 = Screen->LoadFFC(bubble_ffc_number+1); bubble2->X = -16; bubble2->Y = -16;
ffc bubble3 = Screen->LoadFFC(bubble_ffc_number+2); bubble3->X = -16; bubble3->Y = -16;
ffc bubble4 = Screen->LoadFFC(bubble_ffc_number+3); bubble4->X = -16; bubble4->Y = -16;
ffc bubble5 = Screen->LoadFFC(bubble_ffc_number+4); bubble5->X = -16; bubble5->Y = -16;
ffc bubble6 = Screen->LoadFFC(bubble_ffc_number+5); bubble6->X = -16; bubble6->Y = -16;
ffc bubble7 = Screen->LoadFFC(bubble_ffc_number+6); bubble7->X = -16; bubble7->Y = -16;
ffc bubble8 = Screen->LoadFFC(bubble_ffc_number+7); bubble8->X = -16; bubble8->Y = -16;
ffc bubble9 = Screen->LoadFFC(bubble_ffc_number+8); bubble9->X = -16; bubble9->Y = -16;
ffc bubble10 = Screen->LoadFFC(bubble_ffc_number+9); bubble10->X = -16; bubble10->Y = -16;
ffc bubble11 = Screen->LoadFFC(bubble_ffc_number+10); bubble11->X = -16; bubble11->Y = -16;
ffc bubble12 = Screen->LoadFFC(bubble_ffc_number+11); bubble12->X = -16; bubble12->Y = -16;

ffc torpedo1 = Screen->LoadFFC(torpedo_ffc_number); torpedo1->X = -16; torpedo1->Y = -16;
ffc torpedo2 = Screen->LoadFFC(torpedo_ffc_number+1); torpedo2->X = -16; torpedo2->Y = -16;
ffc torpedo3 = Screen->LoadFFC(torpedo_ffc_number+2); torpedo3->X = -16; torpedo3->Y = -16;

int torpedo_counter = 250; // countdown till torpedo fires

int torpedo_explosion1_counter = 0; // Keeps track of explosion timing.
int torpedo_explosion2_counter = 0;
int torpedo_explosion3_counter = 0;

int original_CSet = body1->CSet;

int prev_health = ghosted_enemy->HP; // The health of the ghosted enemy last frame

int invuln_counter = 0; // Enemy is invulnerable briefly after being damaged.

int state = 0; // State 0 = normal movement
// State 1 = reacting to damage
// State 2 = enemy is dead
int state_counter = 0;
int shot_counter = 0;
int attack_state = 0; // Start in wait mode

int wobble = 0;

int target_X;
int target_Y;

item end_boss;

// --------------------------
// Enemy Projectile Setup
// --------------------------
// We don't have arrays. We only have 32 FFCs. So to make 25+ projectiles,
// we make a bunch of enemies!

int first_projectile_enemy_number = Screen->NumNPCs() + 1;
int i;
npc current_enemy;

for(i = first_projectile_enemy_number; i < first_projectile_enemy_number + 25; i++){

current_enemy = Screen->CreateNPC(190);
current_enemy->X = -16; // Hide ze enemy off the screen
current_enemy->Y = -16;
current_enemy->WeaponDamage = 0; // Used as Vx
current_enemy->BossPal = 0; // Used as Vy
current_enemy->HP = 99999;
}

// So now there are 25 baddies off the screen waiting patiently.

while(true){

// --------------------------
// DRAW ENEMY LIFE BAR
// --------------------------

Screen->Rectangle(4,11,9,18,70,108,1,0,0,0,true,128);

if(ghosted_enemy->HP>=100){
Screen->Rectangle(4,11,(71-((62*(ghosted_enemy->HP-100)) / (hit_points-100))),17,70,101,1,0,0,0,true,128);
}
Screen->DrawTile(3,8,71,15118,1,1,6,1,0,0, 0,0,true,128);

// --------------------------
// ENEMY DAMAGE CONTROL
// --------------------------

if (invuln_counter <= 0){
ghosted_enemy->X = this_ffc->X;
ghosted_enemy->Y = this_ffc->Y;
body1->CSet = original_CSet;
body2->CSet = original_CSet;
body3->CSet = original_CSet;

}
else{
ghosted_enemy->X = -16;
ghosted_enemy->Y = -16;
invuln_counter--;
body1->CSet++;
}

// check to see if enemy has been damaged
if (prev_health > ghosted_enemy->HP){
invuln_counter = 20;
}

// if hit points are exhausted, enemy explodes

if ((ghosted_enemy->HP < 100)&&(ghosted_enemy->HP > 0)){

boss_flag = false;

ghosted_enemy->X = -16;
ghosted_enemy->Y = -16;
ghosted_enemy->HP--;
wobble++;

Screen->Circle(3,this_ffc->X,this_ffc->Y,2*wobble,109,1,0,0,0,true,128);
Screen->Circle(3,this_ffc->X,this_ffc->Y,(64*Cos(wobble)),109,1,0,0,0,true,128);

Screen->Circle(3,this_ffc->X + Rand(48) - 24,this_ffc->Y + Rand(48) - 24,
8 + Rand(8),109,1,0,0,0,true,128);

if(ghosted_enemy->HP == 50) {Game->PlaySound(84);;}

if(!((this_ffc->Data >= 3756) && (this_ffc->Data <= 3765))){

Game->PlaySound(88);
this_ffc->Data = 3756;
body1->Data = 3756;
body2->Data = 3756;
body3->Data = 3756;
}

for(i = first_projectile_enemy_number; i < first_projectile_enemy_number + 72; i++){
current_enemy = Screen->LoadNPC(i);
current_enemy->HP = 0;
}

if(ghosted_enemy->HP<=2){
end_boss = Screen->CreateItem(62);
end_boss->X = Link->X;
end_boss->Y = Link->Y;
}

}

if (ghosted_enemy->HP>100){

// --------------------------
// ENEMY MOVEMENT (varies per enemy!)
// --------------------------

// Attack State 0
// ----------------------------
// Loop-de-looping in middle of screen.
// Babies swarming, invincible.
// ----------------------------

if ( attack_state == 0){

ghosted_enemy->X = -16;
ghosted_enemy->Y = -16;

target_X = 120 + 72*Sin(wobble);
target_Y = 72 + 64*Cos(wobble);

wobble++;

body1->Data = 3856;
body1->X = this_ffc->X-16;
body1->Y = this_ffc->Y-16;
body2->Data = 3863;
body2->X = this_ffc->X;
body2->Y = this_ffc->Y+16;
body3->Data = 3867;
body3->X = this_ffc->X;
body3->Y = this_ffc->Y+32;

if(this_ffc->X < target_X){this_ffc->Vx = this_ffc->Vx+0.2;}
else{this_ffc->Vx = this_ffc->Vx-0.2;}
if(this_ffc->Y < target_Y){this_ffc->Vy = this_ffc->Vy+0.2;}
else{this_ffc->Vy = this_ffc->Vy-0.2;}

if(this_ffc->Vx>1){this_ffc->Vx=1;}
if(this_ffc->Vx<-1){this_ffc->Vx=-1;}
if(this_ffc->Vy>1){this_ffc->Vy=1;}
if(this_ffc->Vy<-1){this_ffc->Vy=-1;}

if(state_counter >= 400){
state_counter = 0;
wobble = 0;
attack_state = 1;
this_ffc->Vx = 0;
this_ffc->Vy = 0;

}else{
state_counter++;
}

// Move babies a swarm around Tentaplus

for(i = first_projectile_enemy_number; i < first_projectile_enemy_number + 25; i++){

current_enemy = Screen->LoadNPC(i);

if(current_enemy->X<this_ffc->X-32){
current_enemy->WeaponDamage = 1 + Rand(2);
}
if(current_enemy->X>this_ffc->X+32){
current_enemy->WeaponDamage = 1 + Rand(2) +10;
}

if(current_enemy->Y<this_ffc->Y-32){
current_enemy->BossPal = 1 + Rand(2);
}
if(current_enemy->Y>this_ffc->Y+32){
current_enemy->BossPal = 1 + Rand(2) +10;
}

}


}

// Attack State 1
// ----------------------------
// Chasing player in attempt to trap
// her in its tentacles.
// ----------------------------

if ( attack_state == 1){

ghosted_enemy->X = -16;
ghosted_enemy->Y = -16;

target_X = Link->X;
target_Y = Link->Y - 32;

wobble++;

body1->Data = 3892;
body1->X = this_ffc->X-16;
body1->Y = this_ffc->Y-16;
body2->Data = 0;
body2->X = this_ffc->X;
body2->Y = this_ffc->Y+16;
body3->Data = 0;
body3->X = this_ffc->X;
body3->Y = this_ffc->Y+32;

if(this_ffc->X < target_X){this_ffc->Vx = this_ffc->Vx+0.2;}
else{this_ffc->Vx = this_ffc->Vx-0.2;}
if(this_ffc->Y < target_Y){this_ffc->Vy = this_ffc->Vy+0.2;}
else{this_ffc->Vy = this_ffc->Vy-0.2;}

if(this_ffc->Vx>1){this_ffc->Vx=1;}
if(this_ffc->Vx<-1){this_ffc->Vx=-1;}
if(this_ffc->Vy>1){this_ffc->Vy=1;}
if(this_ffc->Vy<-1){this_ffc->Vy=-1;}

if((Abs(this_ffc->X-Link->X)<8)&&(this_ffc->Y<Link->Y)&&(Abs(this_ffc->Y-Link->Y)<32)){
attack_state = 2;
state_counter = 0;
wobble = 0;
}

if(state_counter >= 100){
state_counter = 0;
wobble = 0;
attack_state = 3;
this_ffc->Vx = 0;
this_ffc->Vy = 0;

}else{
state_counter++;
}

// Move babies a swarm around Tentaplus

for(i = first_projectile_enemy_number; i < first_projectile_enemy_number + 25; i++){

current_enemy = Screen->LoadNPC(i);

if(current_enemy->X<this_ffc->X-32){
current_enemy->WeaponDamage = 1 + Rand(2);
}
if(current_enemy->X>this_ffc->X+32){
current_enemy->WeaponDamage = 1 + Rand(2) +10;
}

if(current_enemy->Y<this_ffc->Y-32){
current_enemy->BossPal = 1 + Rand(2);
}
if(current_enemy->Y>this_ffc->Y+32){
current_enemy->BossPal = 1 + Rand(2) +10;
}

}
}

// Attack State 2
// ----------------------------
// Successful! Player held, her health
// decreases while Tentaplus's increases.
// ----------------------------

if ( attack_state == 2){

ghosted_enemy->X = -16;
ghosted_enemy->Y = -16;

this_ffc->Vx = 0;
this_ffc->Vy = 0;

ghosted_enemy->HP++;
if(wobble>=5){
wobble = 0;
Link->HP--;
}else{
wobble++;
}

Link->X = this_ffc->X;
Link->Y = this_ffc->Y+32;

body1->Data = 3904;
body1->X = this_ffc->X-16;
body1->Y = this_ffc->Y-16;
body2->Data = 3875;
body2->X = this_ffc->X;
body2->Y = this_ffc->Y+16;
body3->Data = 3879;
body3->X = this_ffc->X;
body3->Y = this_ffc->Y+32;

if(state_counter >= 100){
state_counter = 0;
wobble = 0;
attack_state = 0;
this_ffc->Vx = 0;
this_ffc->Vy = 0;
}else{
state_counter++;
}

// Move babies a swarm around Tentaplus

for(i = first_projectile_enemy_number; i < first_projectile_enemy_number + 25; i++){

current_enemy = Screen->LoadNPC(i);

if(current_enemy->X<this_ffc->X-32){
current_enemy->WeaponDamage = 1 + Rand(2);
}
if(current_enemy->X>this_ffc->X+32){
current_enemy->WeaponDamage = 1 + Rand(2) +10;
}

if(current_enemy->Y<this_ffc->Y-32){
current_enemy->BossPal = 1 + Rand(2);
}
if(current_enemy->Y>this_ffc->Y+32){
current_enemy->BossPal = 1 + Rand(2) +10;
}

}

}

// Attack State 3
// ----------------------------
// Swoops top or bottom of screen
// spraying bubbles.
// ----------------------------

if ( attack_state == 3){

ghosted_enemy->X = -16;
ghosted_enemy->Y = -16;

target_X = 120 + 60*Sin(3*wobble);
target_Y = 16;

wobble++;

body1->Data = 3856;
body1->X = this_ffc->X-16;
body1->Y = this_ffc->Y-16;
body2->Data = 3863;
body2->X = this_ffc->X;
body2->Y = this_ffc->Y+16;
body3->Data = 3867;
body3->X = this_ffc->X;
body3->Y = this_ffc->Y+32;

if(this_ffc->X < target_X){this_ffc->Vx = this_ffc->Vx+0.2;}
else{this_ffc->Vx = this_ffc->Vx-0.2;}
if(this_ffc->Y < target_Y){this_ffc->Vy = this_ffc->Vy+0.2;}
else{this_ffc->Vy = this_ffc->Vy-0.2;}

if(this_ffc->Vx>2){this_ffc->Vx=2;}
if(this_ffc->Vx<-2){this_ffc->Vx=-2;}
if(this_ffc->Vy>2){this_ffc->Vy=2;}
if(this_ffc->Vy<-2){this_ffc->Vy=-2;}

if((Abs(this_ffc->X-Link->X)<8)&&(this_ffc->Y<Link->Y)&&(Abs(this_ffc->Y-Link->Y)<32)){
attack_state = 2;
state_counter = 0;
wobble = 0;
this_ffc->Vx = 0;
this_ffc->Vy = 0;
}

if((state_counter==25)||(state_counter==85)){
bubble1->Data = 3883;
bubble1->CSet = original_CSet;
bubble1->X = this_ffc->X;
bubble1->Y = this_ffc->Y;
bubble1->Vx = -0.5;
bubble1->Vy = -2;

bubble7->Data = 3883;
bubble7->CSet = original_CSet;
bubble7->X = this_ffc->X;
bubble7->Y = this_ffc->Y;
bubble7->Vx = 0.5;
bubble7->Vy = -2;

Game->PlaySound(89);
}
if((state_counter==35)||(state_counter==95)){
bubble2->Data = 3883;
bubble2->CSet = original_CSet;
bubble2->X = this_ffc->X;
bubble2->Y = this_ffc->Y;
bubble2->Vx = 0.5;
bubble2->Vy = -2;

bubble8->Data = 3883;
bubble8->CSet = original_CSet;
bubble8->X = this_ffc->X;
bubble8->Y = this_ffc->Y;
bubble8->Vx = -0.5;
bubble8->Vy = -2;
Game->PlaySound(89);
}
if((state_counter==45)||(state_counter==105)){
bubble3->Data = 3883;
bubble3->CSet = original_CSet;
bubble3->X = this_ffc->X;
bubble3->Y = this_ffc->Y;
bubble3->Vx = -0.5;
bubble3->Vy = -2;

bubble9->Data = 3883;
bubble9->CSet = original_CSet;
bubble9->X = this_ffc->X;
bubble9->Y = this_ffc->Y;
bubble9->Vx = 0.5;
bubble9->Vy = -2;
Game->PlaySound(89);
}
if((state_counter==55)||(state_counter==115)){
bubble4->Data = 3883;
bubble4->CSet = original_CSet;
bubble4->X = this_ffc->X;
bubble4->Y = this_ffc->Y;
bubble4->Vx = 0.5;
bubble4->Vy = -2;

bubble10->Data = 3883;
bubble10->CSet = original_CSet;
bubble10->X = this_ffc->X;
bubble10->Y = this_ffc->Y;
bubble10->Vx = -0.5;
bubble10->Vy = -2;
Game->PlaySound(89);
}
if((state_counter==65)||(state_counter==125)){
bubble5->Data = 3883;
bubble5->CSet = original_CSet;
bubble5->X = this_ffc->X;
bubble5->Y = this_ffc->Y;
bubble5->Vx = -0.5;
bubble5->Vy = -2;

bubble11->Data = 3883;
bubble11->CSet = original_CSet;
bubble11->X = this_ffc->X;
bubble11->Y = this_ffc->Y;
bubble11->Vx = 0.5;
bubble11->Vy = -2;
Game->PlaySound(89);
}
if((state_counter==75)||(state_counter==135)){
bubble6->Data = 3883;
bubble6->CSet = original_CSet;
bubble6->X = this_ffc->X;
bubble6->Y = this_ffc->Y;
bubble6->Vx = 0.5;
bubble6->Vy = -2;

bubble12->Data = 3883;
bubble12->CSet = original_CSet;
bubble12->X = this_ffc->X;
bubble12->Y = this_ffc->Y;
bubble12->Vx = -0.5;
bubble12->Vy = -2;
Game->PlaySound(89);
}

if(state_counter >= 150){
state_counter = 0;
wobble = 0;
attack_state = 0;
this_ffc->Vx = 0;
this_ffc->Vy = 0;

}else{
state_counter++;
}

// Move babies a swarm around Tentaplus

for(i = first_projectile_enemy_number; i < first_projectile_enemy_number + 25; i++){

current_enemy = Screen->LoadNPC(i);

if(current_enemy->X<this_ffc->X-32){
current_enemy->WeaponDamage = 1 + Rand(2);
}
if(current_enemy->X>this_ffc->X+32){
current_enemy->WeaponDamage = 1 + Rand(2) +10;
}

if(current_enemy->Y<this_ffc->Y-32){
current_enemy->BossPal = 1 + Rand(2);
}
if(current_enemy->Y>this_ffc->Y+32){
current_enemy->BossPal = 1 + Rand(2) +10;
}

}

}

// Attack State 4
// ----------------------------
// Hit by torpedo! Babies scatter
// and Tentaplus is vulnerable.
// ----------------------------

if ( attack_state == 4){

ghosted_enemy->X = this_ffc->X;
ghosted_enemy->Y = this_ffc->Y;

this_ffc->Vx = 0;
this_ffc->Vy = 0;

body1->Data = 3868;
body1->X = this_ffc->X-16;
body1->Y = this_ffc->Y-16;
body2->Data = 0;
body2->X = this_ffc->X;
body2->Y = this_ffc->Y+16;
body3->Data = 0;
body3->X = this_ffc->X;
body3->Y = this_ffc->Y+32;

if(state_counter >= 200){
state_counter = 0;
wobble = 0;
attack_state = 5;
this_ffc->Vx = 0;
this_ffc->Vy = 0;

}else{
state_counter++;
}

}

// Attack State 5
// ----------------------------
// Recovering from torpedo hit;
// babies returning.
// ----------------------------

if ( attack_state == 5){

ghosted_enemy->X = this_ffc->X;
ghosted_enemy->Y = this_ffc->Y;

this_ffc->Vx = 0;
this_ffc->Vy = 0;

body1->Data = 3868;
body1->X = this_ffc->X-16;
body1->Y = this_ffc->Y-16;
body2->Data = 0;
body2->X = this_ffc->X;
body2->Y = this_ffc->Y+16;
body3->Data = 0;
body3->X = this_ffc->X;
body3->Y = this_ffc->Y+32;

if(state_counter >= 100){
state_counter = 0;
wobble = 0;
attack_state = 0;
this_ffc->Vx = 0;
this_ffc->Vy = 0;

}else{
state_counter++;
}

for(i = first_projectile_enemy_number; i < first_projectile_enemy_number + 25; i++){

current_enemy = Screen->LoadNPC(i);

if(current_enemy->X<this_ffc->X-32){
current_enemy->WeaponDamage = 1 + Rand(2);
}
if(current_enemy->X>this_ffc->X+32){
current_enemy->WeaponDamage = 1 + Rand(2) +10;
}

if(current_enemy->Y<this_ffc->Y-32){
current_enemy->BossPal = 1 + Rand(2);
}
if(current_enemy->Y>this_ffc->Y+32){
current_enemy->BossPal = 1 + Rand(2) +10;
}

}

}


CONT'D BELOW

C-Dawg
02-24-2008, 04:19 PM
// Move Babies
// ---------------------------

for(i = first_projectile_enemy_number-1; i < first_projectile_enemy_number + 25; i++){

current_enemy = Screen->LoadNPC(i);

if(current_enemy->WeaponDamage > 10){
if((current_enemy->X -(current_enemy->WeaponDamage-10))>-16){
current_enemy->X = current_enemy->X -(current_enemy->WeaponDamage-10);
}
}else{
if((current_enemy->X +(current_enemy->WeaponDamage))<256){
current_enemy->X = current_enemy->X +(current_enemy->WeaponDamage);
}
}

if(current_enemy->BossPal > 10){
if((current_enemy->Y -(current_enemy->BossPal-10))>-16){
current_enemy->Y = current_enemy->Y -(current_enemy->BossPal-10);
}
}else{
if((current_enemy->Y +(current_enemy->BossPal))<176){
current_enemy->Y = current_enemy->Y +(current_enemy->BossPal);
}
}
}

// Move Bubbles
// ---------------------------

bubble1->Vy = bubble1->Vy + 0.2;
if(bubble1->Vy > 2){bubble1->Vy = 2;}
if(bubble1->Y>260){bubble1->Y=-16;}
bubble2->Vy = bubble2->Vy + 0.2;
if(bubble2->Vy > 2){bubble2->Vy = 2;}
if(bubble2->Y>260){bubble2->Y=-16;}
bubble3->Vy = bubble3->Vy + 0.2;
if(bubble3->Vy > 2){bubble3->Vy = 2;}
if(bubble3->Y>260){bubble3->Y=-16;}
bubble4->Vy = bubble4->Vy + 0.2;
if(bubble4->Vy > 2){bubble4->Vy = 2;}
if(bubble4->Y>260){bubble4->Y=-16;}
bubble5->Vy = bubble5->Vy + 0.2;
if(bubble5->Vy > 2){bubble5->Vy = 2;}
if(bubble5->Y>260){bubble5->Y=-16;}
bubble6->Vy = bubble6->Vy + 0.2;
if(bubble6->Vy > 2){bubble6->Vy = 2;}
if(bubble6->Y>260){bubble6->Y=-16;}
bubble7->Vy = bubble7->Vy + 0.2;
if(bubble7->Vy > 2){bubble7->Vy = 2;}
if(bubble7->Y>260){bubble7->Y=-16;}
bubble8->Vy = bubble8->Vy + 0.2;
if(bubble8->Vy > 2){bubble8->Vy = 2;}
if(bubble8->Y>260){bubble8->Y=-16;}
bubble9->Vy = bubble9->Vy + 0.2;
if(bubble9->Vy > 2){bubble9->Vy = 2;}
if(bubble9->Y>260){bubble9->Y=-16;}
bubble10->Vy = bubble10->Vy + 0.2;
if(bubble10->Vy > 2){bubble10->Vy = 2;}
if(bubble10->Y>260){bubble10->Y=-16;}
bubble11->Vy = bubble11->Vy + 0.2;
if(bubble11->Vy > 2){bubble11->Vy = 2;}
if(bubble11->Y>260){bubble11->Y=-16;}
bubble12->Vy = bubble12->Vy + 0.2;
if(bubble12->Vy > 2){bubble12->Vy = 2;}
if(bubble12->Y>260){bubble12->Y=-16;}

// Move Torpedo (and handle explosion)
// ---------------------------

if(torpedo_counter <= 1){
torpedo_counter = 250;
if(torpedo1->X<0){
torpedo1->Data = 3871;
if(Rand(2)<1){
if(Rand(2)<1){
torpedo1->X=16; torpedo1->Y=32;
}else{
torpedo1->X=224; torpedo1->Y=32;
}
}else{
if(Rand(2)<1){
torpedo1->X=16; torpedo1->Y=128;
}else{
torpedo1->X=224; torpedo1->Y=128;
}
}
}else{
if(torpedo2->X<0){
torpedo2->Data = 3871;
if(Rand(2)<1){
if(Rand(2)<1){
torpedo2->X=16; torpedo2->Y=32;
}else{
torpedo2->X=224; torpedo2->Y=32;
}
}else{
if(Rand(2)<1){
torpedo2->X=16; torpedo2->Y=128;
}else{
torpedo2->X=224; torpedo2->Y=128;
}
}
}else{
if(torpedo3->X<0){
torpedo3->Data = 3871;
if(Rand(2)<1){
if(Rand(2)<1){
torpedo3->X=16; torpedo3->Y=32;
}else{
torpedo3->X=224; torpedo3->Y=32;
}
}else{
if(Rand(2)<1){
torpedo3->X=16; torpedo3->Y=128;
}else{
torpedo3->X=224; torpedo3->Y=128;
}
}
}
}
}

}else{
torpedo_counter--;
}

if((torpedo1->X>0)&&(torpedo1->Data==3871)){

if(torpedo1->X<Link->X){torpedo1->Vx = torpedo1->Vx+0.1;}
else{torpedo1->Vx = torpedo1->Vx-0.1;}
if(torpedo1->Y<Link->Y){torpedo1->Vy = torpedo1->Vy+0.1;}
else{torpedo1->Vy = torpedo1->Vy-0.1;}

if(torpedo1->Vx > 1){torpedo1->Vx=1;}
if(torpedo1->Vx < -1){torpedo1->Vx=-1;}
if(torpedo1->Vy > 1){torpedo1->Vy=1;}
if(torpedo1->Vy < -1){torpedo1->Vy=-1;}

if(((Abs(torpedo1->X-Link->X)<16)&&(Abs(torpedo1->Y-Link->Y)<16)) || ((Abs(torpedo1->X-this_ffc->X)<16)&&(Abs(torpedo1->Y-this_ffc->Y)<16)) ){

torpedo1->Data = 3887;
torpedo_explosion1_counter = 50;
Game->PlaySound(84);
torpedo1->Vx = 0;
torpedo1->Vy = 0;

if((Abs(torpedo1->X-this_ffc->X)<24)&&(Abs(torpedo1->Y-this_ffc->Y)<24)){
attack_state = 4;
state_counter = 0;
Game->PlaySound(69);
}

}
}

if(torpedo1->Data == 3887){

if(torpedo_explosion1_counter <= 0){torpedo1->Data = 0;torpedo1->X=-16;}
else{torpedo_explosion1_counter--;}

Screen->Circle(3,torpedo1->X,torpedo1->Y,torpedo_explosion1_counter,109,1,0,0,0,true,128) ;
Screen->Circle(3,torpedo1->X + Rand(48) - 24,torpedo1->Y + Rand(48) - 24,8 + Rand(8),109,1,0,0,0,true,128);

if( (Abs(torpedo1->X - Link->X) < 24) && (Abs(torpedo1->Y - Link->Y) < 24) ){
ghosted_damage_enemy->X = Link->X;
ghosted_damage_enemy->Y = Link->Y;
}
else{
ghosted_damage_enemy->X = -16;
ghosted_damage_enemy->Y = -16;
}
}

if((torpedo2->X>0)&&(torpedo2->Data==3871)){

if(torpedo2->X<Link->X){torpedo2->Vx = torpedo2->Vx+0.1;}
else{torpedo2->Vx = torpedo2->Vx-0.1;}
if(torpedo2->Y<Link->Y){torpedo2->Vy = torpedo2->Vy+0.1;}
else{torpedo2->Vy = torpedo2->Vy-0.1;}

if(torpedo2->Vx > 1){torpedo2->Vx=1;}
if(torpedo2->Vx < -1){torpedo2->Vx=-1;}
if(torpedo2->Vy > 1){torpedo2->Vy=1;}
if(torpedo2->Vy < -1){torpedo2->Vy=-1;}

if( ((Abs(torpedo2->X-Link->X)<16)&&(Abs(torpedo2->Y-Link->Y)<16)) || ((Abs(torpedo2->X-this_ffc->X)<16)&&(Abs(torpedo2->Y-this_ffc->Y)<16))){

torpedo2->Data = 3887;
torpedo_explosion2_counter = 50;
Game->PlaySound(84);
torpedo2->Vx = 0;
torpedo2->Vy = 0;

if((Abs(torpedo2->X-this_ffc->X)<24)&&(Abs(torpedo2->Y-this_ffc->Y)<24)){
attack_state = 4;
state_counter = 0;
Game->PlaySound(69);
}

}
}

if(torpedo2->Data == 3887){

if(torpedo_explosion2_counter <= 0){torpedo2->Data = 0;torpedo2->X=-16;}
else{torpedo_explosion2_counter--;}

Screen->Circle(3,torpedo2->X,torpedo2->Y,torpedo_explosion2_counter,109,1,0,0,0,true,128) ;
Screen->Circle(3,torpedo2->X + Rand(48) - 24,torpedo2->Y + Rand(48) - 24,8 + Rand(8),109,1,0,0,0,true,128);

if( (Abs(torpedo2->X - Link->X) < 24) && (Abs(torpedo2->Y - Link->Y) < 24)){
ghosted_damage_enemy->X = Link->X;
ghosted_damage_enemy->Y = Link->Y;
}
else{
ghosted_damage_enemy->X = -16;
ghosted_damage_enemy->Y = -16;
}
}

if((torpedo3->X>0)&&(torpedo3->Data==3871)){

if(torpedo3->X<Link->X){torpedo3->Vx = torpedo3->Vx+0.1;}
else{torpedo3->Vx = torpedo3->Vx-0.1;}
if(torpedo3->Y<Link->Y){torpedo3->Vy = torpedo3->Vy+0.1;}
else{torpedo3->Vy = torpedo3->Vy-0.1;}

if(torpedo3->Vx > 1){torpedo3->Vx=1;}
if(torpedo3->Vx < -1){torpedo3->Vx=-1;}
if(torpedo3->Vy > 1){torpedo3->Vy=1;}
if(torpedo3->Vy < -1){torpedo3->Vy=-1;}

if( ((Abs(torpedo3->X-Link->X)<16)&&(Abs(torpedo3->Y-Link->Y)<16)) || ((Abs(torpedo3->X-this_ffc->X)<16)&&(Abs(torpedo3->Y-this_ffc->Y)<16))){

torpedo3->Data = 3887;
torpedo_explosion3_counter = 50;
Game->PlaySound(84);
torpedo3->Vx = 0;
torpedo3->Vy = 0;

if((Abs(torpedo3->X-this_ffc->X)<24)&&(Abs(torpedo3->Y-this_ffc->Y)<24)){
attack_state = 4;
state_counter = 0;
Game->PlaySound(69);
}

}
}

if(torpedo3->Data == 3887){

if(torpedo_explosion3_counter <= 0){torpedo3->Data = 0;torpedo3->X=-16;}
else{torpedo_explosion3_counter--;}

Screen->Circle(3,torpedo3->X,torpedo3->Y,torpedo_explosion3_counter,109,1,0,0,0,true,128) ;
Screen->Circle(3,torpedo3->X + Rand(48) - 24,torpedo3->Y + Rand(48) - 24,8 + Rand(8),109,1,0,0,0,true,128);

if( (Abs(torpedo3->X - Link->X) < 24) && (Abs(torpedo3->Y - Link->Y) < 24)){
ghosted_damage_enemy->X = Link->X;
ghosted_damage_enemy->Y = Link->Y;
}
else{
ghosted_damage_enemy->X = -16;
ghosted_damage_enemy->Y = -16;
}
}

} // end of movement

// --------------------
// Necessary cleanup
// --------------------

prev_health = ghosted_enemy->HP;
Waitframe();

}// end of while loop

} // end of void run

// ===================================
// Collision detection function
// ===================================
bool canMove(int x, int y){

// x=23, y=130
// Obviously in range...
if(x<0 || x>255 || y<0 || y>175)
return false;
int mask=1111b;

// x % 16 = 7, so
// mask = 1111 & 0011 = 0011
if(x%16<8)
mask&=0011b;
else
mask&=1100b;

// y % 16 = 2, so
// mask = 0011 & 0101 = 0001
if(y%16<8)
mask&=0101b;
else
mask&=1010b;

// All but the top-right quarter of the combo is solid, so ComboS = 1011
// mask & ComboS = 0001 & 1011 = 0001
// The result wasn't 0, so return false
return ((Screen->ComboS[ComboAt(x, y)]&mask)==0);
}// end of canMove

} // end of ffc script

C-Dawg
02-24-2008, 04:20 PM
Oh criminey, that's alot of code. So let's break it down and show you what it's doing and what you need to modify to make your own custom bosses.



// =================================================
// Tentaplus - This script codes the Pieces Master, Tentaplus. Has
// three attack patterns: (1) loop-de-looping around the middle of the
// screen; (2) chasing player and trying to grab her in his tentacles (if
// succeeds, Tentaplus holds player stationary and drains her health to
// its own); (3) swooping over the top or bottom of screen spraying
// bubbles.
// Throughout combat, tentaplus babies will swarm around it, making the
// tentaplus impossible to hit. Periodically, squid torpedoes
// will emerge from the walls. Coordinate one to hit Tentaplus, and the
// babies will fall away, making Tentaplus temporarily vulnerable.
// INPUT:
// D0 = The number of the FFC centered on the body.
// D1 = hit points of this FFC
// D2 = number of the first of 3 FFC making up the body
// of the boss: one 4x4 ffc making up the body, and two 1x1 making
// tenatcles
// 16x64 and makes up the final right collumn of the boss.
// D3 = number of first of 12 FFCs making up bubbles sprayed by Tentaplus.
// D4 = number of first of 3 FFCs making up torpedoes
// ==================================================


This is the header. It's where you explain how the code will work generally, and indicate what your variables mean. I reccomend doing this first, so you have a general idea of what you're going to be coding. Deciding how many FFCs you want to use and what they'll be used for lets you go on to modify the setup section accordingly.



ffc script Tentaplus_CE{

void run (int this_ffc_number, int hit_points, int body_ffc_number, int bubble_ffc_number, int torpedo_ffc_number){

// ---------------------------
// GENERAL ENEMY SETUP (same for all)
// ---------------------------

ffc this_ffc = Screen->LoadFFC(this_ffc_number);
int head_location_X;
int head_location_Y;

boss_flag = true;

npc ghosted_enemy = Screen->CreateNPC(85);
ghosted_enemy->HP = hit_points;

npc ghosted_damage_enemy = Screen->CreateNPC(200);

ffc body1 = Screen->LoadFFC(body_ffc_number);
ffc body2 = Screen->LoadFFC(body_ffc_number+1);
ffc body3 = Screen->LoadFFC(body_ffc_number+2);

ffc bubble1 = Screen->LoadFFC(bubble_ffc_number); bubble1->X = -16; bubble1->Y = -16;
ffc bubble2 = Screen->LoadFFC(bubble_ffc_number+1); bubble2->X = -16; bubble2->Y = -16;
ffc bubble3 = Screen->LoadFFC(bubble_ffc_number+2); bubble3->X = -16; bubble3->Y = -16;
ffc bubble4 = Screen->LoadFFC(bubble_ffc_number+3); bubble4->X = -16; bubble4->Y = -16;
ffc bubble5 = Screen->LoadFFC(bubble_ffc_number+4); bubble5->X = -16; bubble5->Y = -16;
ffc bubble6 = Screen->LoadFFC(bubble_ffc_number+5); bubble6->X = -16; bubble6->Y = -16;
ffc bubble7 = Screen->LoadFFC(bubble_ffc_number+6); bubble7->X = -16; bubble7->Y = -16;
ffc bubble8 = Screen->LoadFFC(bubble_ffc_number+7); bubble8->X = -16; bubble8->Y = -16;
ffc bubble9 = Screen->LoadFFC(bubble_ffc_number+8); bubble9->X = -16; bubble9->Y = -16;
ffc bubble10 = Screen->LoadFFC(bubble_ffc_number+9); bubble10->X = -16; bubble10->Y = -16;
ffc bubble11 = Screen->LoadFFC(bubble_ffc_number+10); bubble11->X = -16; bubble11->Y = -16;
ffc bubble12 = Screen->LoadFFC(bubble_ffc_number+11); bubble12->X = -16; bubble12->Y = -16;

ffc torpedo1 = Screen->LoadFFC(torpedo_ffc_number); torpedo1->X = -16; torpedo1->Y = -16;
ffc torpedo2 = Screen->LoadFFC(torpedo_ffc_number+1); torpedo2->X = -16; torpedo2->Y = -16;
ffc torpedo3 = Screen->LoadFFC(torpedo_ffc_number+2); torpedo3->X = -16; torpedo3->Y = -16;

int torpedo_counter = 250; // countdown till torpedo fires

int torpedo_explosion1_counter = 0; // Keeps track of explosion timing.
int torpedo_explosion2_counter = 0;
int torpedo_explosion3_counter = 0;

int original_CSet = body1->CSet;

int prev_health = ghosted_enemy->HP; // The health of the ghosted enemy last frame

int invuln_counter = 0; // Enemy is invulnerable briefly after being damaged.

int state = 0; // State 0 = normal movement
// State 1 = reacting to damage
// State 2 = enemy is dead
int state_counter = 0;
int shot_counter = 0;
int attack_state = 0; // Start in wait mode

int wobble = 0;

int target_X;
int target_Y;

item end_boss;


Okay, that was all set-up code. All variables needed are instantiated, and pointers to the FFCs you need are also set up. You can modify this section right away, based on what FFCs you decided to use. In this case, for example, I'm using FFCs for 12 bubble projectiles the enemy will use. But you can set up whatever FFCs you like. I strongly reccomend using descriptive names to keep them straight.

Don't change the ghosted_enemy code, though. That's what is handling boss damage.

EDIT: Oh, and the boss_flag variable is a global variable I use to stop my custom weapons from slowing the game down when there are dozens of enemies on the screen. You might not need that. You'd need to delete it once here and once again in the enemy death section.



// --------------------------
// Enemy Projectile Setup
// --------------------------
// We don't have arrays. We only have 32 FFCs. So to make 25+ projectiles,
// we make a bunch of enemies!

int first_projectile_enemy_number = Screen->NumNPCs() + 1;
int i;
npc current_enemy;

for(i = first_projectile_enemy_number; i < first_projectile_enemy_number + 25; i++){

current_enemy = Screen->CreateNPC(190);
current_enemy->X = -16; // Hide ze enemy off the screen
current_enemy->Y = -16;
current_enemy->WeaponDamage = 0; // Used as Vx
current_enemy->BossPal = 0; // Used as Vy
current_enemy->HP = 99999;
}

// So now there are 25 baddies off the screen waiting patiently.


My most recent breakthrough- using custom enemies as additional projectiles. This gets around the 32 FFC limitation. This is optional - if you don't need dozens of bullets spraying all around, you don't need this bit. But if you do, then you'll notice I'm using WeaponDamage as if it was the enemy's Vx and BossPal as the enemy's Vy. Neither of these variables is actually used by the enemy, as it's a custom Fire-type enemy. Since I can't put negative numbers in these fields, I limit the speed of the enemies to 9 or less. If the speed is set at 10 or higher, then, I know it's actually meant to be a NEGATIVE number. That is, the second significant figure is actually a sign bit.



while(true){

// --------------------------
// DRAW ENEMY LIFE BAR
// --------------------------

Screen->Rectangle(4,11,9,18,70,108,1,0,0,0,true,128);

if(ghosted_enemy->HP>=100){
Screen->Rectangle(4,11,(71-((62*(ghosted_enemy->HP-100)) / (hit_points-100))),17,70,101,1,0,0,0,true,128);
}
Screen->DrawTile(3,8,71,15118,1,1,6,1,0,0, 0,0,true,128);


This script does exactly what it says. It draws a rectangular life bar that goes down in proportion to the hit points of ghosted_enemy (which measures the boss' hit points.) You need to change the value in drawtile (currently 15118) if you want your own custom tile to display at the bottom of the life bar. If you don't want a life bar, you can delete this entirely.



// --------------------------
// ENEMY DAMAGE CONTROL
// --------------------------

if (invuln_counter <= 0){
ghosted_enemy->X = this_ffc->X;
ghosted_enemy->Y = this_ffc->Y;
body1->CSet = original_CSet;
body2->CSet = original_CSet;
body3->CSet = original_CSet;

}
else{
ghosted_enemy->X = -16;
ghosted_enemy->Y = -16;
invuln_counter--;
body1->CSet++;
}

// check to see if enemy has been damaged
if (prev_health > ghosted_enemy->HP){
invuln_counter = 20;
}

// if hit points are exhausted, enemy explodes

if ((ghosted_enemy->HP < 100)&&(ghosted_enemy->HP > 0)){

boss_flag = false;

ghosted_enemy->X = -16;
ghosted_enemy->Y = -16;
ghosted_enemy->HP--;
wobble++;

Screen->Circle(3,this_ffc->X,this_ffc->Y,2*wobble,109,1,0,0,0,true,128);
Screen->Circle(3,this_ffc->X,this_ffc->Y,(64*Cos(wobble)),109,1,0,0,0,true,128);

Screen->Circle(3,this_ffc->X + Rand(48) - 24,this_ffc->Y + Rand(48) - 24,
8 + Rand(8),109,1,0,0,0,true,128);

if(ghosted_enemy->HP == 50) {Game->PlaySound(84);;}

if(!((this_ffc->Data >= 3756) && (this_ffc->Data <= 3765))){

Game->PlaySound(88);
this_ffc->Data = 3756;
body1->Data = 3756;
body2->Data = 3756;
body3->Data = 3756;
}

for(i = first_projectile_enemy_number; i < first_projectile_enemy_number + 72; i++){
current_enemy = Screen->LoadNPC(i);
current_enemy->HP = 0;
}

if(ghosted_enemy->HP<=2){
end_boss = Screen->CreateItem(62);
end_boss->X = Link->X;
end_boss->Y = Link->Y;
}

}



This section deals with enemy damage and final explosion. If you don't change it, the boss will flash after each hit and be briefly invulnerable. You can de-activate this behavior if you wish by changing these lines:

if (prev_health > ghosted_enemy->HP){
invuln_counter = 20;
}

so that invuln_counter isn't set so high. On the other hand, you can increase the boss's invulnerability period by increasing the value of invuln_counter.

You'll also have to add or subtract commands to change CSets for body1, body2, or however many body ffcs you're using.




if (ghosted_enemy->HP>100){

// --------------------------
// ENEMY MOVEMENT (varies per enemy!)
// --------------------------

// Attack State 0
// ----------------------------
// Loop-de-looping in middle of screen.
// Babies swarming, invincible.
// ----------------------------

if ( attack_state == 0){

ghosted_enemy->X = -16;
ghosted_enemy->Y = -16;

target_X = 120 + 72*Sin(wobble);
target_Y = 72 + 64*Cos(wobble);

wobble++;

body1->Data = 3856;
body1->X = this_ffc->X-16;
body1->Y = this_ffc->Y-16;
body2->Data = 3863;
body2->X = this_ffc->X;
body2->Y = this_ffc->Y+16;
body3->Data = 3867;
body3->X = this_ffc->X;
body3->Y = this_ffc->Y+32;

if(this_ffc->X < target_X){this_ffc->Vx = this_ffc->Vx+0.2;}
else{this_ffc->Vx = this_ffc->Vx-0.2;}
if(this_ffc->Y < target_Y){this_ffc->Vy = this_ffc->Vy+0.2;}
else{this_ffc->Vy = this_ffc->Vy-0.2;}

if(this_ffc->Vx>1){this_ffc->Vx=1;}
if(this_ffc->Vx<-1){this_ffc->Vx=-1;}
if(this_ffc->Vy>1){this_ffc->Vy=1;}
if(this_ffc->Vy<-1){this_ffc->Vy=-1;}

if(state_counter >= 400){
state_counter = 0;
wobble = 0;
attack_state = 1;
this_ffc->Vx = 0;
this_ffc->Vy = 0;

}else{
state_counter++;
}

// Move babies a swarm around Tentaplus

for(i = first_projectile_enemy_number; i < first_projectile_enemy_number + 25; i++){

current_enemy = Screen->LoadNPC(i);

if(current_enemy->X<this_ffc->X-32){
current_enemy->WeaponDamage = 1 + Rand(2);
}
if(current_enemy->X>this_ffc->X+32){
current_enemy->WeaponDamage = 1 + Rand(2) +10;
}

if(current_enemy->Y<this_ffc->Y-32){
current_enemy->BossPal = 1 + Rand(2);
}
if(current_enemy->Y>this_ffc->Y+32){
current_enemy->BossPal = 1 + Rand(2) +10;
}

}


}

// Attack State 1
// ----------------------------
// Chasing player in attempt to trap
// her in its tentacles.
// ----------------------------

if ( attack_state == 1){

ghosted_enemy->X = -16;
ghosted_enemy->Y = -16;

target_X = Link->X;
target_Y = Link->Y - 32;

wobble++;

body1->Data = 3892;
body1->X = this_ffc->X-16;
body1->Y = this_ffc->Y-16;
body2->Data = 0;
body2->X = this_ffc->X;
body2->Y = this_ffc->Y+16;
body3->Data = 0;
body3->X = this_ffc->X;
body3->Y = this_ffc->Y+32;

if(this_ffc->X < target_X){this_ffc->Vx = this_ffc->Vx+0.2;}
else{this_ffc->Vx = this_ffc->Vx-0.2;}
if(this_ffc->Y < target_Y){this_ffc->Vy = this_ffc->Vy+0.2;}
else{this_ffc->Vy = this_ffc->Vy-0.2;}

if(this_ffc->Vx>1){this_ffc->Vx=1;}
if(this_ffc->Vx<-1){this_ffc->Vx=-1;}
if(this_ffc->Vy>1){this_ffc->Vy=1;}
if(this_ffc->Vy<-1){this_ffc->Vy=-1;}

if((Abs(this_ffc->X-Link->X)<8)&&(this_ffc->Y<Link->Y)&&(Abs(this_ffc->Y-Link->Y)<32)){
attack_state = 2;
state_counter = 0;
wobble = 0;
}

if(state_counter >= 100){
state_counter = 0;
wobble = 0;
attack_state = 3;
this_ffc->Vx = 0;
this_ffc->Vy = 0;

}else{
state_counter++;
}

// Move babies a swarm around Tentaplus

for(i = first_projectile_enemy_number; i < first_projectile_enemy_number + 25; i++){

current_enemy = Screen->LoadNPC(i);

if(current_enemy->X<this_ffc->X-32){
current_enemy->WeaponDamage = 1 + Rand(2);
}
if(current_enemy->X>this_ffc->X+32){
current_enemy->WeaponDamage = 1 + Rand(2) +10;
}

if(current_enemy->Y<this_ffc->Y-32){
current_enemy->BossPal = 1 + Rand(2);
}
if(current_enemy->Y>this_ffc->Y+32){
current_enemy->BossPal = 1 + Rand(2) +10;
}

}
}

// Attack State 2
// ----------------------------
// Successful! Player held, her health
// decreases while Tentaplus's increases.
// ----------------------------

if ( attack_state == 2){

ghosted_enemy->X = -16;
ghosted_enemy->Y = -16;

this_ffc->Vx = 0;
this_ffc->Vy = 0;

ghosted_enemy->HP++;
if(wobble>=5){
wobble = 0;
Link->HP--;
}else{
wobble++;
}

Link->X = this_ffc->X;
Link->Y = this_ffc->Y+32;

body1->Data = 3904;
body1->X = this_ffc->X-16;
body1->Y = this_ffc->Y-16;
body2->Data = 3875;
body2->X = this_ffc->X;
body2->Y = this_ffc->Y+16;
body3->Data = 3879;
body3->X = this_ffc->X;
body3->Y = this_ffc->Y+32;

if(state_counter >= 100){
state_counter = 0;
wobble = 0;
attack_state = 0;
this_ffc->Vx = 0;
this_ffc->Vy = 0;
}else{
state_counter++;
}

// Move babies a swarm around Tentaplus

for(i = first_projectile_enemy_number; i < first_projectile_enemy_number + 25; i++){

current_enemy = Screen->LoadNPC(i);

if(current_enemy->X<this_ffc->X-32){
current_enemy->WeaponDamage = 1 + Rand(2);
}
if(current_enemy->X>this_ffc->X+32){
current_enemy->WeaponDamage = 1 + Rand(2) +10;
}

if(current_enemy->Y<this_ffc->Y-32){
current_enemy->BossPal = 1 + Rand(2);
}
if(current_enemy->Y>this_ffc->Y+32){
current_enemy->BossPal = 1 + Rand(2) +10;
}

}

}

// Attack State 3
// ----------------------------
// Swoops top or bottom of screen
// spraying bubbles.
// ----------------------------

if ( attack_state == 3){

ghosted_enemy->X = -16;
ghosted_enemy->Y = -16;

target_X = 120 + 60*Sin(3*wobble);
target_Y = 16;

wobble++;

body1->Data = 3856;
body1->X = this_ffc->X-16;
body1->Y = this_ffc->Y-16;
body2->Data = 3863;
body2->X = this_ffc->X;
body2->Y = this_ffc->Y+16;
body3->Data = 3867;
body3->X = this_ffc->X;
body3->Y = this_ffc->Y+32;

if(this_ffc->X < target_X){this_ffc->Vx = this_ffc->Vx+0.2;}
else{this_ffc->Vx = this_ffc->Vx-0.2;}
if(this_ffc->Y < target_Y){this_ffc->Vy = this_ffc->Vy+0.2;}
else{this_ffc->Vy = this_ffc->Vy-0.2;}

if(this_ffc->Vx>2){this_ffc->Vx=2;}
if(this_ffc->Vx<-2){this_ffc->Vx=-2;}
if(this_ffc->Vy>2){this_ffc->Vy=2;}
if(this_ffc->Vy<-2){this_ffc->Vy=-2;}

if((Abs(this_ffc->X-Link->X)<8)&&(this_ffc->Y<Link->Y)&&(Abs(this_ffc->Y-Link->Y)<32)){
attack_state = 2;
state_counter = 0;
wobble = 0;
this_ffc->Vx = 0;
this_ffc->Vy = 0;
}

if((state_counter==25)||(state_counter==85)){
bubble1->Data = 3883;
bubble1->CSet = original_CSet;
bubble1->X = this_ffc->X;
bubble1->Y = this_ffc->Y;
bubble1->Vx = -0.5;
bubble1->Vy = -2;

bubble7->Data = 3883;
bubble7->CSet = original_CSet;
bubble7->X = this_ffc->X;
bubble7->Y = this_ffc->Y;
bubble7->Vx = 0.5;
bubble7->Vy = -2;

Game->PlaySound(89);
}
if((state_counter==35)||(state_counter==95)){
bubble2->Data = 3883;
bubble2->CSet = original_CSet;
bubble2->X = this_ffc->X;
bubble2->Y = this_ffc->Y;
bubble2->Vx = 0.5;
bubble2->Vy = -2;

bubble8->Data = 3883;
bubble8->CSet = original_CSet;
bubble8->X = this_ffc->X;
bubble8->Y = this_ffc->Y;
bubble8->Vx = -0.5;
bubble8->Vy = -2;
Game->PlaySound(89);
}
if((state_counter==45)||(state_counter==105)){
bubble3->Data = 3883;
bubble3->CSet = original_CSet;
bubble3->X = this_ffc->X;
bubble3->Y = this_ffc->Y;
bubble3->Vx = -0.5;
bubble3->Vy = -2;

bubble9->Data = 3883;
bubble9->CSet = original_CSet;
bubble9->X = this_ffc->X;
bubble9->Y = this_ffc->Y;
bubble9->Vx = 0.5;
bubble9->Vy = -2;
Game->PlaySound(89);
}
if((state_counter==55)||(state_counter==115)){
bubble4->Data = 3883;
bubble4->CSet = original_CSet;
bubble4->X = this_ffc->X;
bubble4->Y = this_ffc->Y;
bubble4->Vx = 0.5;
bubble4->Vy = -2;

bubble10->Data = 3883;
bubble10->CSet = original_CSet;
bubble10->X = this_ffc->X;
bubble10->Y = this_ffc->Y;
bubble10->Vx = -0.5;
bubble10->Vy = -2;
Game->PlaySound(89);
}
if((state_counter==65)||(state_counter==125)){
bubble5->Data = 3883;
bubble5->CSet = original_CSet;
bubble5->X = this_ffc->X;
bubble5->Y = this_ffc->Y;
bubble5->Vx = -0.5;
bubble5->Vy = -2;

bubble11->Data = 3883;
bubble11->CSet = original_CSet;
bubble11->X = this_ffc->X;
bubble11->Y = this_ffc->Y;
bubble11->Vx = 0.5;
bubble11->Vy = -2;
Game->PlaySound(89);
}
if((state_counter==75)||(state_counter==135)){
bubble6->Data = 3883;
bubble6->CSet = original_CSet;
bubble6->X = this_ffc->X;
bubble6->Y = this_ffc->Y;
bubble6->Vx = 0.5;
bubble6->Vy = -2;

bubble12->Data = 3883;
bubble12->CSet = original_CSet;
bubble12->X = this_ffc->X;
bubble12->Y = this_ffc->Y;
bubble12->Vx = -0.5;
bubble12->Vy = -2;
Game->PlaySound(89);
}

if(state_counter >= 150){
state_counter = 0;
wobble = 0;
attack_state = 0;
this_ffc->Vx = 0;
this_ffc->Vy = 0;

}else{
state_counter++;
}

// Move babies a swarm around Tentaplus

for(i = first_projectile_enemy_number; i < first_projectile_enemy_number + 25; i++){

current_enemy = Screen->LoadNPC(i);

if(current_enemy->X<this_ffc->X-32){
current_enemy->WeaponDamage = 1 + Rand(2);
}
if(current_enemy->X>this_ffc->X+32){
current_enemy->WeaponDamage = 1 + Rand(2) +10;
}

if(current_enemy->Y<this_ffc->Y-32){
current_enemy->BossPal = 1 + Rand(2);
}
if(current_enemy->Y>this_ffc->Y+32){
current_enemy->BossPal = 1 + Rand(2) +10;
}

}

}

// Attack State 4
// ----------------------------
// Hit by torpedo! Babies scatter
// and Tentaplus is vulnerable.
// ----------------------------

if ( attack_state == 4){

ghosted_enemy->X = this_ffc->X;
ghosted_enemy->Y = this_ffc->Y;

this_ffc->Vx = 0;
this_ffc->Vy = 0;

body1->Data = 3868;
body1->X = this_ffc->X-16;
body1->Y = this_ffc->Y-16;
body2->Data = 0;
body2->X = this_ffc->X;
body2->Y = this_ffc->Y+16;
body3->Data = 0;
body3->X = this_ffc->X;
body3->Y = this_ffc->Y+32;

if(state_counter >= 200){
state_counter = 0;
wobble = 0;
attack_state = 5;
this_ffc->Vx = 0;
this_ffc->Vy = 0;

}else{
state_counter++;
}

}

// Attack State 5
// ----------------------------
// Recovering from torpedo hit;
// babies returning.
// ----------------------------

if ( attack_state == 5){

ghosted_enemy->X = this_ffc->X;
ghosted_enemy->Y = this_ffc->Y;

this_ffc->Vx = 0;
this_ffc->Vy = 0;

body1->Data = 3868;
body1->X = this_ffc->X-16;
body1->Y = this_ffc->Y-16;
body2->Data = 0;
body2->X = this_ffc->X;
body2->Y = this_ffc->Y+16;
body3->Data = 0;
body3->X = this_ffc->X;
body3->Y = this_ffc->Y+32;

if(state_counter >= 100){
state_counter = 0;
wobble = 0;
attack_state = 0;
this_ffc->Vx = 0;
this_ffc->Vy = 0;

}else{
state_counter++;
}

for(i = first_projectile_enemy_number; i < first_projectile_enemy_number + 25; i++){

current_enemy = Screen->LoadNPC(i);

if(current_enemy->X<this_ffc->X-32){
current_enemy->WeaponDamage = 1 + Rand(2);
}
if(current_enemy->X>this_ffc->X+32){
current_enemy->WeaponDamage = 1 + Rand(2) +10;
}

if(current_enemy->Y<this_ffc->Y-32){
current_enemy->BossPal = 1 + Rand(2);
}
if(current_enemy->Y>this_ffc->Y+32){
current_enemy->BossPal = 1 + Rand(2) +10;
}

}

}



Ah, the enemy movement section. This is truly the meat and potatoes of the custom boss code. You need to tell the enemy how to move and what to do. I like to start by organizing the boss behavior into "attack states." How you break it down is kind of arbitrary, but thinking about the attack in states will help organize your code. I start by putting in the comments you see at the start of each attack state, telling me generally how the enemy is going to act. Then, for each state, go back and enter code describing how the FFCs and any NPCs move during each state, when the state ends (does it end after a certain number of tics of state_counter? Does it end when the enemy catches the player? When the enemy is damaged?) and what state it goes to next.

Notice that for any attack_state in which you want the enemy to be vulnerable, you need to move ghosted_enemy to the boss' vulnerable point. When the boss is not vulnerable, move ghosted_enemy to -16,-16, where it will be safe from harm.

EDIT - One other note. For each attack state, the boss' graphics need to be set to match. If the boss is attacking, it might look different from when it's jumping. I've taken to just loading my combos directly (body1->Data = 1234; for instance). It might be better practice to load the combos in variables so they can be changed more easily. For now, you have to change the combo numbers assigned directly in the code.

C-Dawg
02-24-2008, 04:21 PM
// Move Babies
// ---------------------------

for(i = first_projectile_enemy_number-1; i < first_projectile_enemy_number + 25; i++){

current_enemy = Screen->LoadNPC(i);

if(current_enemy->WeaponDamage > 10){
if((current_enemy->X -(current_enemy->WeaponDamage-10))>-16){
current_enemy->X = current_enemy->X -(current_enemy->WeaponDamage-10);
}
}else{
if((current_enemy->X +(current_enemy->WeaponDamage))<256){
current_enemy->X = current_enemy->X +(current_enemy->WeaponDamage);
}
}

if(current_enemy->BossPal > 10){
if((current_enemy->Y -(current_enemy->BossPal-10))>-16){
current_enemy->Y = current_enemy->Y -(current_enemy->BossPal-10);
}
}else{
if((current_enemy->Y +(current_enemy->BossPal))<176){
current_enemy->Y = current_enemy->Y +(current_enemy->BossPal);
}
}
}

// Move Bubbles
// ---------------------------

bubble1->Vy = bubble1->Vy + 0.2;
if(bubble1->Vy > 2){bubble1->Vy = 2;}
if(bubble1->Y>260){bubble1->Y=-16;}
bubble2->Vy = bubble2->Vy + 0.2;
if(bubble2->Vy > 2){bubble2->Vy = 2;}
if(bubble2->Y>260){bubble2->Y=-16;}
bubble3->Vy = bubble3->Vy + 0.2;
if(bubble3->Vy > 2){bubble3->Vy = 2;}
if(bubble3->Y>260){bubble3->Y=-16;}
bubble4->Vy = bubble4->Vy + 0.2;
if(bubble4->Vy > 2){bubble4->Vy = 2;}
if(bubble4->Y>260){bubble4->Y=-16;}
bubble5->Vy = bubble5->Vy + 0.2;
if(bubble5->Vy > 2){bubble5->Vy = 2;}
if(bubble5->Y>260){bubble5->Y=-16;}
bubble6->Vy = bubble6->Vy + 0.2;
if(bubble6->Vy > 2){bubble6->Vy = 2;}
if(bubble6->Y>260){bubble6->Y=-16;}
bubble7->Vy = bubble7->Vy + 0.2;
if(bubble7->Vy > 2){bubble7->Vy = 2;}
if(bubble7->Y>260){bubble7->Y=-16;}
bubble8->Vy = bubble8->Vy + 0.2;
if(bubble8->Vy > 2){bubble8->Vy = 2;}
if(bubble8->Y>260){bubble8->Y=-16;}
bubble9->Vy = bubble9->Vy + 0.2;
if(bubble9->Vy > 2){bubble9->Vy = 2;}
if(bubble9->Y>260){bubble9->Y=-16;}
bubble10->Vy = bubble10->Vy + 0.2;
if(bubble10->Vy > 2){bubble10->Vy = 2;}
if(bubble10->Y>260){bubble10->Y=-16;}
bubble11->Vy = bubble11->Vy + 0.2;
if(bubble11->Vy > 2){bubble11->Vy = 2;}
if(bubble11->Y>260){bubble11->Y=-16;}
bubble12->Vy = bubble12->Vy + 0.2;
if(bubble12->Vy > 2){bubble12->Vy = 2;}
if(bubble12->Y>260){bubble12->Y=-16;}

// Move Torpedo (and handle explosion)
// ---------------------------

if(torpedo_counter <= 1){
torpedo_counter = 250;
if(torpedo1->X<0){
torpedo1->Data = 3871;
if(Rand(2)<1){
if(Rand(2)<1){
torpedo1->X=16; torpedo1->Y=32;
}else{
torpedo1->X=224; torpedo1->Y=32;
}
}else{
if(Rand(2)<1){
torpedo1->X=16; torpedo1->Y=128;
}else{
torpedo1->X=224; torpedo1->Y=128;
}
}
}else{
if(torpedo2->X<0){
torpedo2->Data = 3871;
if(Rand(2)<1){
if(Rand(2)<1){
torpedo2->X=16; torpedo2->Y=32;
}else{
torpedo2->X=224; torpedo2->Y=32;
}
}else{
if(Rand(2)<1){
torpedo2->X=16; torpedo2->Y=128;
}else{
torpedo2->X=224; torpedo2->Y=128;
}
}
}else{
if(torpedo3->X<0){
torpedo3->Data = 3871;
if(Rand(2)<1){
if(Rand(2)<1){
torpedo3->X=16; torpedo3->Y=32;
}else{
torpedo3->X=224; torpedo3->Y=32;
}
}else{
if(Rand(2)<1){
torpedo3->X=16; torpedo3->Y=128;
}else{
torpedo3->X=224; torpedo3->Y=128;
}
}
}
}
}

}else{
torpedo_counter--;
}

if((torpedo1->X>0)&&(torpedo1->Data==3871)){

if(torpedo1->X<Link->X){torpedo1->Vx = torpedo1->Vx+0.1;}
else{torpedo1->Vx = torpedo1->Vx-0.1;}
if(torpedo1->Y<Link->Y){torpedo1->Vy = torpedo1->Vy+0.1;}
else{torpedo1->Vy = torpedo1->Vy-0.1;}

if(torpedo1->Vx > 1){torpedo1->Vx=1;}
if(torpedo1->Vx < -1){torpedo1->Vx=-1;}
if(torpedo1->Vy > 1){torpedo1->Vy=1;}
if(torpedo1->Vy < -1){torpedo1->Vy=-1;}

if(((Abs(torpedo1->X-Link->X)<16)&&(Abs(torpedo1->Y-Link->Y)<16)) || ((Abs(torpedo1->X-this_ffc->X)<16)&&(Abs(torpedo1->Y-this_ffc->Y)<16)) ){

torpedo1->Data = 3887;
torpedo_explosion1_counter = 50;
Game->PlaySound(84);
torpedo1->Vx = 0;
torpedo1->Vy = 0;

if((Abs(torpedo1->X-this_ffc->X)<24)&&(Abs(torpedo1->Y-this_ffc->Y)<24)){
attack_state = 4;
state_counter = 0;
Game->PlaySound(69);
}

}
}

if(torpedo1->Data == 3887){

if(torpedo_explosion1_counter <= 0){torpedo1->Data = 0;torpedo1->X=-16;}
else{torpedo_explosion1_counter--;}

Screen->Circle(3,torpedo1->X,torpedo1->Y,torpedo_explosion1_counter,109,1,0,0,0,true,128) ;
Screen->Circle(3,torpedo1->X + Rand(48) - 24,torpedo1->Y + Rand(48) - 24,8 + Rand(8),109,1,0,0,0,true,128);

if( (Abs(torpedo1->X - Link->X) < 24) && (Abs(torpedo1->Y - Link->Y) < 24) ){
ghosted_damage_enemy->X = Link->X;
ghosted_damage_enemy->Y = Link->Y;
}
else{
ghosted_damage_enemy->X = -16;
ghosted_damage_enemy->Y = -16;
}
}

if((torpedo2->X>0)&&(torpedo2->Data==3871)){

if(torpedo2->X<Link->X){torpedo2->Vx = torpedo2->Vx+0.1;}
else{torpedo2->Vx = torpedo2->Vx-0.1;}
if(torpedo2->Y<Link->Y){torpedo2->Vy = torpedo2->Vy+0.1;}
else{torpedo2->Vy = torpedo2->Vy-0.1;}

if(torpedo2->Vx > 1){torpedo2->Vx=1;}
if(torpedo2->Vx < -1){torpedo2->Vx=-1;}
if(torpedo2->Vy > 1){torpedo2->Vy=1;}
if(torpedo2->Vy < -1){torpedo2->Vy=-1;}

if( ((Abs(torpedo2->X-Link->X)<16)&&(Abs(torpedo2->Y-Link->Y)<16)) || ((Abs(torpedo2->X-this_ffc->X)<16)&&(Abs(torpedo2->Y-this_ffc->Y)<16))){

torpedo2->Data = 3887;
torpedo_explosion2_counter = 50;
Game->PlaySound(84);
torpedo2->Vx = 0;
torpedo2->Vy = 0;

if((Abs(torpedo2->X-this_ffc->X)<24)&&(Abs(torpedo2->Y-this_ffc->Y)<24)){
attack_state = 4;
state_counter = 0;
Game->PlaySound(69);
}

}
}

if(torpedo2->Data == 3887){

if(torpedo_explosion2_counter <= 0){torpedo2->Data = 0;torpedo2->X=-16;}
else{torpedo_explosion2_counter--;}

Screen->Circle(3,torpedo2->X,torpedo2->Y,torpedo_explosion2_counter,109,1,0,0,0,true,128) ;
Screen->Circle(3,torpedo2->X + Rand(48) - 24,torpedo2->Y + Rand(48) - 24,8 + Rand(8),109,1,0,0,0,true,128);

if( (Abs(torpedo2->X - Link->X) < 24) && (Abs(torpedo2->Y - Link->Y) < 24)){
ghosted_damage_enemy->X = Link->X;
ghosted_damage_enemy->Y = Link->Y;
}
else{
ghosted_damage_enemy->X = -16;
ghosted_damage_enemy->Y = -16;
}
}

if((torpedo3->X>0)&&(torpedo3->Data==3871)){

if(torpedo3->X<Link->X){torpedo3->Vx = torpedo3->Vx+0.1;}
else{torpedo3->Vx = torpedo3->Vx-0.1;}
if(torpedo3->Y<Link->Y){torpedo3->Vy = torpedo3->Vy+0.1;}
else{torpedo3->Vy = torpedo3->Vy-0.1;}

if(torpedo3->Vx > 1){torpedo3->Vx=1;}
if(torpedo3->Vx < -1){torpedo3->Vx=-1;}
if(torpedo3->Vy > 1){torpedo3->Vy=1;}
if(torpedo3->Vy < -1){torpedo3->Vy=-1;}

if( ((Abs(torpedo3->X-Link->X)<16)&&(Abs(torpedo3->Y-Link->Y)<16)) || ((Abs(torpedo3->X-this_ffc->X)<16)&&(Abs(torpedo3->Y-this_ffc->Y)<16))){

torpedo3->Data = 3887;
torpedo_explosion3_counter = 50;
Game->PlaySound(84);
torpedo3->Vx = 0;
torpedo3->Vy = 0;

if((Abs(torpedo3->X-this_ffc->X)<24)&&(Abs(torpedo3->Y-this_ffc->Y)<24)){
attack_state = 4;
state_counter = 0;
Game->PlaySound(69);
}

}
}

if(torpedo3->Data == 3887){

if(torpedo_explosion3_counter <= 0){torpedo3->Data = 0;torpedo3->X=-16;}
else{torpedo_explosion3_counter--;}

Screen->Circle(3,torpedo3->X,torpedo3->Y,torpedo_explosion3_counter,109,1,0,0,0,true,128) ;
Screen->Circle(3,torpedo3->X + Rand(48) - 24,torpedo3->Y + Rand(48) - 24,8 + Rand(8),109,1,0,0,0,true,128);

if( (Abs(torpedo3->X - Link->X) < 24) && (Abs(torpedo3->Y - Link->Y) < 24)){
ghosted_damage_enemy->X = Link->X;
ghosted_damage_enemy->Y = Link->Y;
}
else{
ghosted_damage_enemy->X = -16;
ghosted_damage_enemy->Y = -16;
}
}

} // end of movement


Sometimes you want additional code to move seperate pieces of the boss around while the main boss is going through it's attack_states. So here, the torpedoes, bubbles, and baby tentaplus' move around on their own, independent of the state.



// --------------------
// Necessary cleanup
// --------------------

prev_health = ghosted_enemy->HP;
Waitframe();

}// end of while loop

} // end of void run

// ===================================
// Collision detection function
// ===================================
bool canMove(int x, int y){

// x=23, y=130
// Obviously in range...
if(x<0 || x>255 || y<0 || y>175)
return false;
int mask=1111b;

// x % 16 = 7, so
// mask = 1111 & 0011 = 0011
if(x%16<8)
mask&=0011b;
else
mask&=1100b;

// y % 16 = 2, so
// mask = 0011 & 0101 = 0001
if(y%16<8)
mask&=0101b;
else
mask&=1010b;

// All but the top-right quarter of the combo is solid, so ComboS = 1011
// mask & ComboS = 0001 & 1011 = 0001
// The result wasn't 0, so return false
return ((Screen->ComboS[ComboAt(x, y)]&mask)==0);
}// end of canMove

} // end of ffc script


The last bit just does some minimal cleanup and includes canMove, which I use alot for custom enemy code.

If you want to see more examples of boss behavior, check out Zodiac, unpassworded, in the Quest Announcement Forum.

ScaryBinary
02-24-2008, 09:35 PM
:eek: Wow. Awesome job. I didn't try it out yet, but just by looking at the code...that's some ingenious stuff there, and it's a great example for dweebs like me who haven't messed with custom bosses too much yet. Well done.

C-Dawg
02-25-2008, 02:34 AM
From your surprise, I take it you haven't been keeping up with Zodiac's development, eh?

ScaryBinary
02-25-2008, 07:47 AM
From your surprise, I take it you haven't been keeping up with Zodiac's development, eh?

I have to admit I'm typically off in my own little world.

I'm downloading Zodiac now! Screenshots look wicked.

bigjoe
02-25-2008, 10:11 AM
It's good to see that advanced enemies and bosses can now be completely independent of warp related trickery.

I was playing Zodiac, by the way, and noticed that you have some enemies which appear to be connected to a regular enemy, but are larger than 16x16. Can you identify which script handles that? Enemies that are larger than 16x16 will be essential in the quest that I am working on, as they fit better with LTTP Style Big Link.

C-Dawg
02-25-2008, 11:39 AM
Same way I do bosses. The only difference between normal scripted enemies of any size and bosses is: 1. life bar and 2. death animation. Normal enemies are usually less complex, but they dont have to be.

Any script in Zodiac ending is CE is a custom enemy. Some of the earlier ones like bouncer_ce are designed and already set up to be a 2x2 enemy and you don't have to do any work on the setup. You might check those out.

By the way, for those interested, getting a custom boss from drawing board to perfect functioning takes about 2 or 3 hours now. So there's no excuse for not learning to script anymore!

Russ
02-25-2008, 11:45 AM
Wow, I have to look at that script!

C-Dawg
02-25-2008, 02:31 PM
Don't get too excited. For the simple CE scripts, you still need to tell the enemy how to move around. So there's still a little scripting going on. But the graphics and size are entirely dependent on what FFCs you assign, as the script headers explain. All you have to do is assign the scripts with the right Data fields, set up your FFCs, and change the script dealing with the enemy movement only, and you're done. Right now, the following enemies have simple CE scripts:

bouncer_CE = The blue flea things from Zelda 2, appear in Scorpio.
diamond_CE = The crystals that bounce off of the walls in Gemini and Libra.
crawlie_CE = Crawls along the ground towards the player, hops up small ledges, fires Swords.

And a bunch more I'm not remembering now. I'll go post the code tonight.

Gleeok
02-26-2008, 06:23 AM
Good stuff.

As you know I have a particular sort of aversion torwards the code involving trigonometry parameters, so, could you explain this part to me? Undoubtedly the most complicated part for most people. ie: me. ;)


if((i<=36+first_projectile_enemy_number)&&(i>18+first_projectile_enemy_number)){

current_enemy->X = attack_X + (attack_radius*Cos(5+(i-first_projectile_enemy_number)*20))/2;
current_enemy->Y = attack_Y + (attack_radius*Sin(5+(i-first_projectile_enemy_number)*20))/2;

}


So which one of those changes what, and how about making each wave more than 18 bullets spaced out, or making them faster/slower, changing where the circle starts, etc.?

The_Amaster
03-26-2008, 04:08 PM
Cool! Just one question on the movement section:


body1->X = this_ffc->X-16;

Why doesn't this make the movement jerky, suddenly hopping 16 pixels at a time? Or does it?

Gleeok
03-26-2008, 07:30 PM
What it does is lock the graphics in place, so as the ffc moves, the "body" of the enemy moves with it. Although it should be pointed out that Large FFCs work just as well for giving *most* scripted bosses a body...if you're tight on ffcs. I also figured out my above post a while back. I think I missed th *20 bit. That's a really cool attack and much easier than what I was doing. You've outdone yourself again.

C-Dawg
03-29-2008, 04:14 PM
Gleeok's got it right.

The way I script enemies, only ONE FFC is actually moving around using Vx and Vy. It's this_ffc. Every other FFC making up the boss does not move around itself. Instead, it is locked into place relative to this_ffc. So if the body of the boss is made up of 6 FFCs, I figure where they need to be (one is at this_ffc->X - 16, this_ffc->Y-16, next one is at this_ffc->X, this_ffc->Y -16, et cet.)

The script you're looking at here uses three FFCs for the boss' body graphics. One large FFC for a single static picture, and two 1x1 ffcs that animate and make up the boss' tentacles. Every frame, after this_ffc moves, these body ffcs get moved into position and set to the right tile/combo.

-C

The_Amaster
03-29-2008, 07:46 PM
Okay, I gotcha. It isn't saying move the FFC -16, its saying place the FFC -16 relative to the main. Got it.