PDA

View Full Version : Problems with Freeze Ray Script



C-Dawg
05-09-2008, 11:59 PM
So I'm working on a script that freezes enemies in blocks of ice. Not done with it yet (havn't added controls for enemies you DONT want to freeze, large enemy support, or health maximum) but it works pretty much as expected for now. When the enemy is hit with the beam, the enemy turns blue and freezes. A solid ice block is drawn at the nearest combo. After 300 tics, the enemy un-freezes and the ice block is replaced by whatever the combo was to begin with.

But there's a weird problem. Many enemies (not Keese, for some reason) quickly DIE while they're waiting off screen to be un-frozen. What gives? I've changed the code to stash the frozen enemies at 0,-16 so you can watch them croak. What's causing the enemies to die like that?



// ========================================
// FFC Script - Freeze Ray
// This script codes the Freeze Ray. When the
// projectile hits an enemy, the enemy is moved to -16,-16
// and a solid ice block replica of the enemy is put in the
// enemy's place. The replica is a solid block. After a few
// moments, the enemy returns.
//
// You can input a list of enemies (up to ten) that you do NOT
// want to be affected by the Freeze Ray.
//
// D0 - Whether the Freeze Ray cares about enemy health. If D0
// is zero, Freeze Ray always freezes regardless of health. If it
// is any other number, it freezes only if enemy health is that low.
//
// This script does not move the Freeze_Ray; do that on your own.
// ========================================

item script Freeze_Ray_Item{

void run (){

int beam_animation = 1743; // Combo of the beam graphic

ffc this_ffc = Screen->LoadFFC(32); // Hard-coded to use FFC 32.

if (!(this_ffc->Data == beam_animation) && (Link->MP >= 8)){
if(Link->Dir == 0) {
this_ffc->X = Link->X;
this_ffc->Y = Link->Y - 8;
this_ffc->Vy = -3;
this_ffc->Data = beam_animation;
}

if(Link->Dir == 1) {
this_ffc->X = Link->X;
this_ffc->Y = Link->Y + 8;
this_ffc->Vy = 3;
this_ffc->Data = beam_animation;

}

if(Link->Dir == 2) {
this_ffc->X = Link->X - 8;
this_ffc->Y = Link->Y;
this_ffc->Vx = -3;
this_ffc->Data = beam_animation;
}

if(Link->Dir == 3) {
this_ffc->X = Link->X + 8;
this_ffc->Y = Link->Y;
this_ffc->Vx = 3;
this_ffc->Data = beam_animation;
}

} // end of state = 0;

} // end of void run

} // end of item script

ffc script Freeze_Ray{

void run(int max_health){

// The enemies IDs that are immune to the
// Freeze Ray.

int immune_enemy1 = 0;
int immune_enemy2 = 0;
int immune_enemy3 = 0;
int immune_enemy4 = 0;
int immune_enemy5 = 0;
int immune_enemy6 = 0;
int immune_enemy7 = 0;
int immune_enemy8 = 0;
int immune_enemy9 = 0;
int immune_enemy10 = 0;

// Combo that will display below the frozen
// enemy graphic. This is what will make the
// frozen enemy SOLID, so make sure the
// combo is solid.

int ice_block = 1613;

// The ice beam only acts while it is set to a particular combo.

int trigger_combo = 1743;

// Othwerwise, this ffc waits as a null combo. (Make it invisible).

int null_combo = 1;

// Duration of the freeze, in tics.

int freeze_duration = 300;

// CSet that the frozen enemy graphic displays
// in. Choose a CSet that makes the enemy
// appear in shades of blue and white.

int ice_CSet = 13;

// These pointers keep track of which enemies
// are frozen. Maximum five.

npc frozen_enemy1;
npc frozen_enemy2;
npc frozen_enemy3;
npc frozen_enemy4;
npc frozen_enemy5;

// This array keeps track of whether each
// npc is frozen, what X and Y coordinates
// the npc was frozen at, what combo
// originally was on that space, and a countdown
// to coming back.
// INDEX:
// frozen_enemy1
// 0 - is frozen; 1,2 - coords; 3 - combo, 4 - countdown
// frozen enemy2
// 5 - is frozen; 6,7 - coords; 8 - combo, 9 - countdown
// frozen enemy3
// 10 - is frozen; 11,12 - coords; 13 - combo, 14 - countdown
// frozen enemy4
// 15 - is frozen; 16,17 - coords; 18 - combo. 19 - countdown
// frozen enemy5
// 20 - is frozen; 21,22 - coords; 23 - combo, 24 - countdown

int data[24];
int i;
for(i = 0; i <= 24; i++){
data[i] = 0;
}

// These variables handle collision checking.

int cur_npc_number = 0;
npc cur_npc;

// This variable keeps track of whether impact has been detected.

bool detect_impact = false;

while(true){

if(this->Data == trigger_combo){

this->CSet = ice_CSet;

// Detect impact and move stuff accordingly.

for ( cur_npc_number = 0; cur_npc_number <= Screen->NumNPCs(); cur_npc_number++){

cur_npc = Screen->LoadNPC(cur_npc_number);

if(
(Abs(cur_npc->X - this->X)<8) && (Abs(cur_npc->Y - this->Y)<8)

){

if(data[0] == 0){

detect_impact = true;

data[0] = 1;
data[1] = cur_npc->X;
data[2] = cur_npc->Y;
data[3] = Screen->ComboD[comboPosition(cur_npc->X, cur_npc->Y)];
data[4] = freeze_duration;

frozen_enemy1 = cur_npc;

} // end of impact on first enemy

if((data[5] == 0)&&(detect_impact == false)){

detect_impact = true;

data[5] = 1;
data[6] = cur_npc->X;
data[7] = cur_npc->Y;
data[8] = Screen->ComboD[comboPosition(cur_npc->X, cur_npc->Y)];
data[9] = freeze_duration;

frozen_enemy2 = cur_npc;

} // end of impact on second enemy

if((data[10] == 0)&&(detect_impact == false)){

detect_impact = true;

data[10] = 1;
data[11] = cur_npc->X;
data[12] = cur_npc->Y;
data[13] = Screen->ComboD[comboPosition(cur_npc->X, cur_npc->Y)];
data[14] = freeze_duration;

frozen_enemy3 = cur_npc;

} // end of impact on third enemy

if((data[15] == 0)&&(detect_impact == false)){

detect_impact = true;

data[15] = 1;
data[16] = cur_npc->X;
data[17] = cur_npc->Y;
data[18] = Screen->ComboD[comboPosition(cur_npc->X, cur_npc->Y)];
data[19] = freeze_duration;

frozen_enemy4 = cur_npc;

} // end of impact on fourth enemy

if((data[20] == 0)&&(detect_impact == false)){

detect_impact = true;

data[20] = 1;
data[21] = cur_npc->X;
data[22] = cur_npc->Y;
data[23] = Screen->ComboD[comboPosition(cur_npc->X, cur_npc->Y)];
data[24] = freeze_duration;

frozen_enemy5 = cur_npc;

} // end of impact on fifth enemy

if (detect_impact == true){

this->X = 16;
this->Y = 16;
this->Vx = 0;
this->Vy = 0;
this->Data = null_combo;
Game->PlaySound(44);
}

detect_impact = false;
}

}// end of for loop


}// end of if

// If enemies are frozen, adjust screen accordingly.

if(data[0] == 1){

Screen->ComboD[comboPosition(data[1],data[2])] = ice_block;

Screen->DrawTile(
3, // Layer
data[1], // X coordinate
data[2], // Y coordinate
frozen_enemy1->Tile, // Tile
1, // block of one tile wide
1, // block of one tile height
ice_CSet, // CSet
1, // Scale = 1
0, // no rotation
0, // no rotation
0, // no rotation angle
0, // no flip
true, // respect the transparent regions
128 // opaque
);

frozen_enemy1->X = 0;
frozen_enemy1->Y= -16;

data[4]--;

if ( data[4] <= 1 ){
frozen_enemy1->X = data[1];
frozen_enemy1->Y= data[2];
Screen->ComboD[comboPosition(data[1],data[2])] = data[3];
data[0] = 0;
data[1] = 0;
data[2] = 0;
data[3] = 0;
data[4] = 0;
}

} // end of first enemy adjustments

if(data[5] == 1){

Screen->ComboD[comboPosition(data[6],data[7])] = ice_block;

Screen->DrawTile(
3, // Layer
data[6], // X coordinate
data[7], // Y coordinate
frozen_enemy2->Tile, // Tile
1, // block of one tile wide
1, // block of one tile height
ice_CSet, // CSet
1, // Scale = 1
0, // no rotation
0, // no rotation
0, // no rotation angle
0, // no flip
true, // respect the transparent regions
128 // opaque
);

frozen_enemy2->X = 0;
frozen_enemy2->Y= -16;

data[9]--;

if ( data[9] <= 2 ){
frozen_enemy2->X = data[6];
frozen_enemy2->Y= data[7];
Screen->ComboD[comboPosition(data[6],data[7])] = data[8];
data[5] = 0;
data[6] = 0;
data[7] = 0;
data[8] = 0;
data[9] = 0;
}

} // end of second enemy adjustments

if(data[10] == 1){

Screen->ComboD[comboPosition(data[11],data[12])] = ice_block;

Screen->DrawTile(
3, // Layer
data[11], // X coordinate
data[12], // Y coordinate
frozen_enemy2->Tile, // Tile
1, // block of one tile wide
1, // block of one tile height
ice_CSet, // CSet
1, // Scale = 1
0, // no rotation
0, // no rotation
0, // no rotation angle
0, // no flip
true, // respect the transparent regions
128 // opaque
);

frozen_enemy3->X = 0;
frozen_enemy3->Y= -16;

data[14]--;

if ( data[14] <= 2 ){
frozen_enemy3->X = data[11];
frozen_enemy3->Y= data[12];
Screen->ComboD[comboPosition(data[11],data[12])] = data[13];
data[10] = 0;
data[11] = 0;
data[12] = 0;
data[13] = 0;
data[14] = 0;
}

} // end of third enemy adjustments

if(data[15] == 1){

Screen->ComboD[comboPosition(data[16],data[17])] = ice_block;

Screen->DrawTile(
3, // Layer
data[16], // X coordinate
data[17], // Y coordinate
frozen_enemy2->Tile, // Tile
1, // block of one tile wide
1, // block of one tile height
ice_CSet, // CSet
1, // Scale = 1
0, // no rotation
0, // no rotation
0, // no rotation angle
0, // no flip
true, // respect the transparent regions
128 // opaque
);

frozen_enemy4->X = 0;
frozen_enemy4->Y= -16;

data[19]--;

if ( data[19] <= 2 ){
frozen_enemy4->X = data[16];
frozen_enemy4->Y= data[17];
Screen->ComboD[comboPosition(data[16],data[17])] = data[18];
data[15] = 0;
data[16] = 0;
data[17] = 0;
data[18] = 0;
data[19] = 0;
}

} // end of fourth enemy adjustments

if(data[20] == 1){

Screen->ComboD[comboPosition(data[21],data[22])] = ice_block;

Screen->DrawTile(
3, // Layer
data[21], // X coordinate
data[22], // Y coordinate
frozen_enemy2->Tile, // Tile
1, // block of one tile wide
1, // block of one tile height
ice_CSet, // CSet
1, // Scale = 1
0, // no rotation
0, // no rotation
0, // no rotation angle
0, // no flip
true, // respect the transparent regions
128 // opaque
);

frozen_enemy5->X = 0;
frozen_enemy5->Y= -16;

data[24]--;

if ( data[24] <= 2 ){
frozen_enemy5->X = data[21];
frozen_enemy5->Y= data[22];
Screen->ComboD[comboPosition(data[21],data[22])] = data[23];
data[20] = 0;
data[21] = 0;
data[22] = 0;
data[23] = 0;
data[24] = 0;
}

} // end of fifth enemy adjustments

// Cleanup

if(!canMove(this->X, this->Y)){
this->X = 16;
this->Y = 16;
this->Vx = 0;
this->Vy = 0;
this->Data = null_combo;
}

Waitframe();

} // end of while loop

} // end of void run

// ======================================
// Combo Position finder
// Returns the Combo ID of the combo nearest
// to a given x, y coordinate.
// ======================================

int comboPosition(int x, int y){

int remainder = x % 16;
if (remainder <= 8){
x = x - remainder;
}
else{
x = x + 16 - remainder;
}

int remainder_y = y % 16;

if (remainder_y <= 8){
y = y - remainder_y;
}
else{
y = y + 16 - remainder_y;
}
return ((y & 240)+(x>>4));

} // end of comboPosition

// ===================================
// 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


EDIT: It appears that flying enemies (keese, wizzrobes, etc) don't care if they're off the screen. They're fine. But walking enemies (moblins, octrocks, etc) die as soon as they leave the screen. What's this all about? Engine behavior?

jman2050
05-10-2008, 12:41 PM
It seems to be engine behavior. I'll check it later on. As to how to get around that, I'm not entirely sure.

C-Dawg
05-10-2008, 01:54 PM
Well, one could get around it by either:

1. Correcting the engine's attempt to kill the enemy each frame; does the engine try to set health to zero? Just declare invalid? Can I correct that each frame of my script?

or

2. Killing enemies and noting their hp when hit by ice beam, then creating new enemies with equal health. This will play the "death" noise, so it's not perfect, but dooable.

I'd rather use #1 if you can tell me the mechanism by which the engine kills certain off-screen enemies.