PDA

View Full Version : "Helmstar-style" Tail Whip Code



C-Dawg
10-26-2006, 01:18 PM
Hey,

I've coded up this script, which should mimic the behavior of Helmstar's tail from LttP. But it doesn't work. At runtime, the tail end stays where it was placed, one of the segments moves a little bit, and then nothing else happens. I can't tell whether this is a continuing bug where there are multiple FFCs on the screen, or a runtime error. Maybe someone else can take a look at it.

I tried to avoid problems by putting all the code in ONE FFC. So FFC 7, the spikey end of the tail, has all the code; the rest of the FFCs run no script whatsoever.



import "std.zh"

int distancesq(int x1, int x2, int y1, int y2)
{
return (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2);
}

// ===============================================
// TAIL_WHIP : This ffc will move slowly back and forth horizontally
// over four tiles, centered on it's original location. After a set amount
// of time, it will lash out towards link' s location at a high speed. It
// will pause breifly after reaching link's old location, and then return
// to it's starting point to continue wagging. Will use FFCs 1,2, 3, 4, and 5
// as tail segments, and FFC 6 as the base of the tail. Accordingly,
// the Tail_Whip FFC should be FFC 7.
// ===============================================

ffc script tail_whip{

// CONSTANTS

int wait = 60; // How long the tail waits
float pi = 3.14; // It's pi!
int delay = 10; // How much warning Link gets before
// the tail strikes

// VARIABLES

int i = 0; // Counter

int x_origin = 0;
int y_origin = 0;

int x_target = 0;
int y_target = 0;

int x; // working variables
int y;

int dist;

int state = 0; // The state of the tail_whip.
// 0 = wagging right
// 1 = wagging left
// 2 = pulling back to strike
// 3 = striking at link
// 4 = pausing after striking
// 5 = returning to wagging mode


// RUN FUNCTION
void run(){

// POINTERS

ffc segment_1 = Screen->LoadFFC(1);
ffc segment_2 = Screen->LoadFFC(2);
ffc segment_3 = Screen->LoadFFC(3);
ffc segment_4 = Screen->LoadFFC(4);
ffc segment_5 = Screen->LoadFFC(5);
ffc base = Screen->LoadFFC(6);

x_origin = this->X;
y_origin = this->Y;

while (true){

// Wagging right

if (state ==0){

if (this->X >= x_origin + 32){ state = 1; }
else{
this->Vx = Sin( ( 2 * pi) / 32) * (this->X - x_origin);
}

if (i >= wait) { i = 0; state = 2;}
else{ i = i + 1;}
}

// Wagging left

if (state ==1){

if (this->X >= x_origin - 32){ state = 0; }
else{
this->Vx = -Sin( ( 2 * pi) / 32) * (this->X - x_origin);
}

if (i >= wait) { i = 0; state = 2;}
else{ i = i + 1;}
}

// Targeting and pulling back to strike

if (state ==2){

x_target = Link->X;
y_target = Link->Y;
if ( i >= delay ){ i = 0; state = 3; }
else {
this -> Vx = 0;
this->Vy = -0.1;
}
}

if (state == 3){

if ( (this->X >= x_target - 16) && (this->X <= x_target + 16) &&
(this->Y >= y_target - 16) && (this->Y <= y_target + 16) ){

state = 4;
}
else{

dist = distancesq(x_target, this->X, y_target, this->Y);
x = this->X;
y = this->Y;
this->Vx = 2* ((x_target - x)*Abs((x_target - x))/dist);
this->Vy = 2 * ((y_target - y)*Abs((y_target - y))/dist);
}

}

if (state == 4){

if ( i >= delay/2 ) { i = 0; state = 5;}
else{ i = i++; }

}

if (state == 5){

if ( (this->X >= x_origin - 16) && (this->X <= x_origin + 16) && (this->Y >= y_origin - 16) && (this->Y <= y_origin + 16) ){

state = 0;
}
else{

dist = distancesq(x_origin, this->X, y_origin, this->Y);
x = this->X;
y = this->Y;
this->Vx = ((x_origin - x)*Abs((x_origin - x))/dist);
this->Vy = ((x_origin - y)*Abs((x_origin - y))/dist);
}
}

// After moving the tail end, next the tail segments are moved into position.
// First, put the third segment at the midpoint.

segment_3->X = ( (this->X - base->X) / 2);
segment_3->Y= ( (this->Y - base->Y) / 2);

// Next, place the other segments by finding the midpoint between 3 and the
// base, and between 3 and the tail whip, and placing two segments
// around it.

x = ( (segment_3->X - base->X) / 2);
y = ( (segment_3->Y - base->Y) / 2);

segment_1->X = ( ( x - base->X) / 2);
segment_1->Y = ( ( y - base->Y) / 2);

segment_2->X = ( ( x - segment_3->X) / 2);
segment_2->Y = ( ( y - segment_3->Y) / 2);

x = ( (segment_3->X - this->X) / 2);
y = ( (segment_3->Y - this->Y) / 2);

segment_4->X = ( ( x - segment_3->X) / 2);
segment_4->Y = ( ( y - segment_3->Y) / 2);

segment_5->X = ( ( x - this->X) / 2);
segment_5->Y = ( ( y - this->Y) / 2);

Waitframe();

} // end of while loop
} // end of void run()
} // end of ffc script

Saffith
10-28-2006, 04:29 AM
I'm afraid I don't completely understand how parts of it are supposed to work, but I fixed a couple of problems.


if (state ==0){

if (this->X >= x_origin + 32){ state = 1; }
else{
this->Vx = Sin( ( 2 * pi) / 32) * (this->X - x_origin);
}

if (i >= wait) { i = 0; state = 2;}
else{ i = i + 1;}
}
x_origin is initialized to this->X, so you're multplying that sine by 0.
I think you may want to skip the velocity and modify the position directly, something like this->X = x_origin + 32 * Sin(i). That should also eliminate the need for separate left and right motion states. But that's the part of the script where I'm least certain of your intent, so that might not be quite what you want.

If not...

if (this->X >= x_origin - 32){ state = 0; }
Make sure, at least, to change that to this->X <= x_origin - 32.


if (state ==2){

x_target = Link->X;
y_target = Link->Y;
if ( i >= delay ){ i = 0; state = 3; }
else {
this -> Vx = 0;
this->Vy = -0.1;
}
}
Don't forget to increment i.


if (state == 3){

if ( (this->X >= x_target - 16) && (this->X <= x_target + 16) &&
(this->Y >= y_target - 16) && (this->Y <= y_target + 16) ){

state = 4;
}
else{

dist = distancesq(x_target, this->X, y_target, this->Y);
x = this->X;
y = this->Y;
this->Vx = 2* ((x_target - x)*Abs((x_target - x))/dist);
this->Vy = 2 * ((y_target - y)*Abs((y_target - y))/dist);
}

}
Do you mean to be using both target-16 and target+16? The target was set to Link's top-left corner, so the end area defined in the if statement is a 2x2 tile square with Link's original position in the bottom-right.
Remember to set the velocity back to 0 after it hits.
Also, though you don't really have to set i to 0, since it was already 0 and wasn't changed, it would just make a bit more sense to set it here than at the end of state 2.


if (state == 4){

if ( i >= delay/2 ) { i = 0; state = 5;}
else{ i = i++; }

}
Not sure if you don't know or if you just mistyped it, but i = i++ is redundant. It's just i++ instead.
I doubt that would cause any problems, but it's not inconceivable.


if (state == 5){

if ( (this->X >= x_origin - 16) && (this->X <= x_origin + 16) && (this->Y >= y_origin - 16) && (this->Y <= y_origin + 16) ){

state = 0;
}
else{

dist = distancesq(x_origin, this->X, y_origin, this->Y);
x = this->X;
y = this->Y;
this->Vx = ((x_origin - x)*Abs((x_origin - x))/dist);
this->Vy = ((x_origin - y)*Abs((x_origin - y))/dist);
}
}
Again, remember to reset i and the velocities to 0.



Gah, it's after four in the morning. It doesn't seem to be right yet, but I can't keep working on it now. Hopefully I didn't screw anything up too badly, at least.

Here's the script with all the above changes, whether you wanted them or not...

import "std.zh"

int distancesq(int x1, int x2, int y1, int y2)
{
return (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2);
}

// ===============================================
// TAIL_WHIP : This ffc will move slowly back and forth horizontally
// over four tiles, centered on it's original location. After a set amount
// of time, it will lash out towards link' s location at a high speed. It
// will pause breifly after reaching link's old location, and then return
// to it's starting point to continue wagging. Will use FFCs 1,2, 3, 4, and 5
// as tail segments, and FFC 6 as the base of the tail. Accordingly,
// the Tail_Whip FFC should be FFC 7.
// ===============================================

ffc script tail_whip{

// CONSTANTS

int wait = 60; // How long the tail waits
float pi = 3.14; // It's pi!
int delay = 10; // How much warning Link gets before
// the tail strikes

// VARIABLES

int i = 0; // Counter

int x_origin = 0;
int y_origin = 0;

int x_target = 0;
int y_target = 0;

int x; // working variables
int y;

int dist;

int state = 0; // The state of the tail_whip.
// 0 = wagging right
// 1 = wagging left
// 2 = pulling back to strike
// 3 = striking at link
// 4 = pausing after striking
// 5 = returning to wagging mode


// RUN FUNCTION
void run(){

// POINTERS

ffc segment_1 = Screen->LoadFFC(1);
ffc segment_2 = Screen->LoadFFC(2);
ffc segment_3 = Screen->LoadFFC(3);
ffc segment_4 = Screen->LoadFFC(4);
ffc segment_5 = Screen->LoadFFC(5);
ffc base = Screen->LoadFFC(6);

x_origin = this->X;
y_origin = this->Y;

while (true){

// Wagging

if (state ==0){

this->X = x_origin + 32 * Sin(i);

if (i >= wait) { i = 0; state = 2;}
else{ i++;}
}

// State 1 no longer used

// Targeting and pulling back to strike

if (state ==2){

x_target = Link->X;
y_target = Link->Y;
if ( i >= delay ){ state = 3; }
else {
this -> Vx = 0;
this->Vy = -0.1;
i++;
}
}

if (state == 3){

if ( (this->X >= x_target) && (this->X <= x_target + 16) &&
(this->Y >= y_target) && (this->Y <= y_target + 16) ){

this->Vx = 0;
this->Vy = 0;
state = 4;
i = 0;
}
else{

dist = distancesq(x_target, this->X, y_target, this->Y);
x = this->X;
y = this->Y;
this->Vx = 2* ((x_target - x)*Abs((x_target - x))/dist);
this->Vy = 2 * ((y_target - y)*Abs((y_target - y))/dist);
}

}

if (state == 4){

if ( i >= delay/2 ) { i = 0; state = 5;}
else{ i++; }

}

if (state == 5){

if ( (this->X >= x_origin - 16) && (this->X <= x_origin + 16) && (this->Y >= y_origin - 16) && (this->Y <= y_origin + 16) ){

this->Vx = 0;
this->Vy = 0;
i = 0;
state = 0;
}
else{

dist = distancesq(x_origin, this->X, y_origin, this->Y);
x = this->X;
y = this->Y;
this->Vx = ((x_origin - x)*Abs((x_origin - x))/dist);
this->Vy = ((x_origin - y)*Abs((x_origin - y))/dist);
}
}

// After moving the tail end, next the tail segments are moved into position.
// First, put the third segment at the midpoint.

segment_3->X = ( (this->X - base->X) / 2);
segment_3->Y= ( (this->Y - base->Y) / 2);

// Next, place the other segments by finding the midpoint between 3 and the
// base, and between 3 and the tail whip, and placing two segments
// around it.

x = ( (segment_3->X - base->X) / 2);
y = ( (segment_3->Y - base->Y) / 2);

segment_1->X = ( ( x - base->X) / 2);
segment_1->Y = ( ( y - base->Y) / 2);

segment_2->X = ( ( x - segment_3->X) / 2);
segment_2->Y = ( ( y - segment_3->Y) / 2);

x = ( (segment_3->X - this->X) / 2);
y = ( (segment_3->Y - this->Y) / 2);

segment_4->X = ( ( x - segment_3->X) / 2);
segment_4->Y = ( ( y - segment_3->Y) / 2);

segment_5->X = ( ( x - this->X) / 2);
segment_5->Y = ( ( y - this->Y) / 2);

Waitframe();

} // end of while loop
} // end of void run()
} // end of ffc script



One interesting thing I noticed... If I change the script thusly:

ffc segment_1 = Screen->LoadFFC(2);
ffc segment_2 = Screen->LoadFFC(3);
ffc segment_3 = Screen->LoadFFC(4);
ffc segment_4 = Screen->LoadFFC(5);
ffc segment_5 = Screen->LoadFFC(6);
ffc base = Screen->LoadFFC(7);
... And attach it to FFC #1, it works quite differently, even aside from the pieces being shifted. It's closed to how it's meant to work, I think. Perhaps I'm mistaken, but I don't think it should make any difference. I believe it might indeed be a problem in the compiler or the game itself.

C-Dawg
10-28-2006, 11:30 AM
Saffith - Thanks man, you're an asset to the community. I'll check out your edits when I get back from the gym today.

And yea, I've noticed that ffc scripts tend to interact unpredictably, but changing the number of the FFC changes the behavior. Bizarre.

C-Dawg
10-30-2006, 12:52 PM
That should also eliminate the need for separate left and right motion states. But that's the part of the script where I'm least certain of your intent, so that might not be quite what you want.

The behavior I'm looking for is a smoothly wagging behavior, where the tail moves left, then right, and accellerates and decellerates during it's wag. As opposed to sharply moving left and right like it would using ATT_CHG FFCs. Your script's behavior is right on the money, even if I don't understand the mathematics behind it. What I was trying to do was use the equation for a sin wave, with amplitude 1 and period of 64, to calculate a smoothly increasing and decreasing velocity. Guess it didn't work like I'd planned.




Do you mean to be using both target-16 and target+16? The target was set to Link's top-left corner, so the end area defined in the if statement is a 2x2 tile square with Link's original position in the bottom-right.

I made the target range wide because I was concerned about the possibility of the moving FFC "overshooting" the target and never returning to the wagging behavior. Of course, the issue with such a wide range is that it won't actually hit Link if he just stands still.