This has been discussed in the scripting forum, but I thought I'd bring it here, too. When you place two FFCs running different scripts on the same screen, they react unpredictably. This interference can be constructive ( try putting one FFC with Saffith's followlink script and another with his Chasertrap script on the same screen!) but is usually destructive (try making one FFC with Saffith's followlink and another with a function designed to orbit that first FFC). This happens even when one of the FFCs has no script attached. (Blank FFC + FFC orbiting the blank one).
This is a real shame because getting more than one FFC running on a screen at once would be, you know, really nice.
Show me a sample quest file.
I have nowhere to upload it, unfortunetly. But if you stick the scripts together I described, you'll see the behavior I was talking about.
More details.
Among other things, I'm working on coding a LttP style lazer. This would take two FFCs; one around which the first rotates, and a second that rotates around. The rotating FFC needs to look at the X and Y coordinates of the "orbitee" around which it moves. When the rotating FFC is #2 and it looks at FFC #1, it screws up and flies off the screen. When the rotating FFC is #1 and it looks at orbitee FFC #2, however, it works as it should.
EDIT: This phenomon is not universal. Some scripts still go haywire whether they are looking at FFC #1 or #2.
You're using LoadFFC(), right? I'd bet decent money it's a problem with REFFFC not being set correctly. Could you post the intermediate ASM source?
DarkDragon wrote:You're using LoadFFC(), right? I'd bet decent money it's a problem with REFFFC not being set correctly. Could you post the intermediate ASM source?
Oh man, which one? I'll try to generate you some this evening and post it. However, no, these problems occur whether or not I'm using LoadFFC().
For instance, if the screen has Saffith's chaser_trap and _L_'s followlink FFC's, neither of which are supposed to care what the other is doing, one of them will remain stationary and the other will exhibit both following link AND chasing link behavior. (Which is actually kinda cool in that case, and I like the idea of synergizing behaviors like that. Could we include the ability to thread multiple behaviors together in a single FFC?)
In another case, I have an FFC with no script and another FFC that uses LoadFFC() to orbit the stationary FFC. This one worked once, in a test quest, but ONLY so long as I had the stationary FFC #2. It doesn't work in other test quests or if the stationary FFC is #1. Additionally, it appears that the presense of the stationary FFC may be interfering with the orbiting FFC's behavior when it is lined up with Link.
New behavior to report, same bug.
I modified the Chaser_Trap script to re-create the medusa heads from Lolo. They are invisible until Link steps in line with them, then shoot out at unavoidable speed and kill Link unless blocked by an unwalkable combo. When they are blocked, they return to the starting position. If there is just one of these Medusa traps on the screen, all is well. But I put three on a screen together, and the result was that only one of them fired, and it fired random tiles in unpredictable curves constantly for about, oh, twenty seconds, and then the game crashed. There were three FFCs on the screen, all running the Medusa_trap script.
I wish I had some web space, I'd upload this stuff and let you devs take a look at it.
What about rapidshare.de?
Update: It appears that SOME FFC scripts do get along fine. I set up one screen with three FFCs using the One-Way script, and one using the Mutliblock_Trigger script. They all worked perfectly.
The determining factor might be that none of those scripts rely on any common named variables. But I dont know if that's the key, because scripts with one scripted FFC and one unscripted FFC sometimes cause problems anyway.
Update: Further research shows that there are more scripts that can get along fine.
On one screen, I have four one-way FFCs, one Multiblock_Trigger FFC, and one Medusa Trap FFC. They all work perfectly. I hypothesize that they work well because all but one of them get their variables from the data fields of the FFCs. The medusa trap uses the same variable names as Saffith's chaser_trap. The one-way combos use the same variable names, but they are passed from the D0-D8 parts of the FFC. The multiblock trigger also gets all variables from the D0-08. I think the problem with FFCs playing together might be that when one FFC script changes a variable declared in it's script, say, X, it will also change the variable in all other FFC scripts called "X." So long as the code for that FFC script doesn't change X at all, the scripts seem to get along fine, even if they share variable names. That might be why the one-way blocks get along even though they share variable names.
I will test this when I get home. Specifically, I'll duplicate the medusa_trap code, each duplicate having multiple variable names, and see if that makes them play along nicely on a given screen.
Are the variables being changed stack-allocated or global?
Not sure, but they're declared inside the FFC script.
Here's an example. This medusa_trap code works perfectly fine if it's the only one on the screen. But place more than one FFC running this script, and POP! It stops working. Only one of the traps on the screen will fire at all, the combo set to the FFC changes, seemingly at random, it fires in a wierd curve instead of straight, etc. And it'll eventually crash Zquest if you let it fire at you long enough.
// ==================================================================
// Medusa_Trap = Waits until Link is lined up with it, then lurches
// towards him at unavoidable speed. Will severely damage (or kill)
// Link if it hits him. Will not go through solid blocks. Inspired
// by the Medusas from Adventures of Lolo.
// ==================================================================ffc script Medusa_Trap
{
void run()
{
int original_x = this->X;
int original_y = this->Y;
int sound=13; // The sound played when the trap starts moving
int state=0; // Whether or not and in which direction the trap is moving
float accel=0; // Used to track the trap's acceleration
while(true)
{
// Inactive.
// If Link is in range in any direction, and the trap can move
// in that direction, start moving.
if(state==0)
{
if(Abs(Link->X-this->X)<14 && Link->Y<this->Y && canMove(this->X, this->Y-16)) // Up
{
state=1;
this->Data++;
Game->PlaySound(sound);
}
else if(Abs(Link->X-this->X)<14 && Link->Y>this->Y && canMove(this->X, this->Y+16)) // Down
{
state=2;
this->Data++;
Game->PlaySound(sound);
}
else if(Abs(Link->Y-this->Y)<14 && Link->X<this->X && canMove(this->X-16, this->Y)) // Left
{
state=3;
this->Data++;
Game->PlaySound(sound);
}
else if(Abs(Link->Y-this->Y)<14 && Link->X>this->X && canMove(this->X+16, this->Y)) // Right
{
state=4;
this->Data++;
Game->PlaySound(sound);
}
}
// Active.
// Accelerate up to 8px/sec. Once at that speed, check at
// every tile whether it's possible to continue moving.
if(state==1) // Up
{
// Accelerate up to 1.5px/sec, then jump to 4px/sec.
// Easy way to keep the trap centered on tiles.
accel=0;
while(accel<1.5)
{
accel=accel+0.1;
this->Y=this->Y-accel;
Waitframe();
}
accel=4;
// Once at maximum speed, every time the trap's
// centered on a tile, check whether it can
// continue moving. If not, then stop and
// return to starting location.
while(state==1)
{
if(this->Y%16==0)
{
if(!canMove(this->X, this->Y-16))
{
state=0;
this->Data--;
this->X = original_x;
this->Y = original_y;
}
else
this->Y=this->Y-accel;
}
else
this->Y=this->Y-accel;
Waitframe();
}
}
else if(state==2) // Down
{
accel=0;
while(accel<1.5)
{
accel=accel+0.1;
this->Y=this->Y+accel;
Waitframe();
}
accel=4;
while(state==2)
{
if(this->Y%16==0)
{
if(!canMove(this->X, this->Y+16))
{
state=0;
this->Data--;
this->X = original_x;
this->Y = original_y;
}
else
this->Y=this->Y+accel;
}
else
this->Y=this->Y+accel;
Waitframe();
}
}
else if(state==3) // Left
{
accel=0;
while(accel<1.5)
{
accel=accel+0.1;
this->X=this->X-accel;
Waitframe();
}
accel=4;
while(state==3)
{
if(this->X%16==0)
{
if(!canMove(this->X-16, this->Y))
{
state=0;
this->Data--;
this->X = original_x;
this->Y = original_y;
}
else
this->X=this->X-accel;
}
else
this->X=this->X-accel;
Waitframe();
}
}
else if(state==4) // Right
{
accel=0;
while(accel<1.5)
{
accel=accel+0.1;
this->X=this->X+accel;
Waitframe();
}
accel=4;
while(state==4)
{
if(this->X%16==0)
{
if(!canMove(this->X+16, this->Y))
{
state=0;
this->Data--;
this->X = original_x;
this->Y = original_y;
}
else
this->X=this->X+accel;
}
else
this->X=this->X+accel;
Waitframe();
}
}
else
Waitframe();
}
}
// Check whether the trap can move into a certain tile.
// Returns true if the tile on layer 0 is within the
// range of the screen and no part of it is solid.
bool canMove(int x, int y)
{
if(x<0 || x>240 || y<0 || y>160)
return false;
return Screen->ComboS[y+(x>>4)]==0;
}
}
Another example. This code should, in theory, orbit another FFC. It does nothing, and its behavior changes depending on which FFC you use as the orbitee.
ffc script orbit_ffc { void run() {
ffc orbitee = Screen->LoadFFC(1);
for(int i=0; true; i++){
this->X = orbitee->X + 30*Sin(i);
this->Y = orbitee->Y + 30*Cos(i);
Waitframe();
}
}
}
Cross-reference to the Scripting forum discussion of the same bug: http://www.armageddongames.net//thread/94450/
C-Dawg, I can't run ZC myself at the moment, but I can offer some advice for further testing:
1. If you remove the math in orbit_ffc, is it still buggy? In particular, is the following script still buggy?
ffc script orbit_ffc { void run() {
ffc orbitee = Screen->LoadFFC(1);
while(true){
Trace(orbitee->X);
Waitframe();
}
}
}
2. What kind of scripts screw up the orbit_fcc? Is it buggy if you try to orbit a do-nothing FFC (one without an attached script)?
3. If the answer to both of the above is yes, please post a ZASM dump of the compilation, and we can try to get to the bottom of this extremely annoying bug :/
Has this been fixed? I can't reproduce it.
People are still using the clunky workaround but I'm sure I fixed this bug a long time ago. Are you certain it's still a problem? Please post an example quest.